Attachment "getkeynames.c" to
ticket [3362446fff]
added by
apnadkarni
2011-07-11 15:31:17.
static int
GetKeyNames(
Tcl_Interp *interp, /* Current interpreter. */
Tcl_Obj *keyNameObj, /* Key to enumerate. */
Tcl_Obj *patternObj, /* Optional match pattern. */
REGSAM mode) /* Mode flags to pass. */
{
const char *pattern; /* Pattern being matched against subkeys */
HKEY key; /* Handle to the key being examined */
DWORD index; /* Position of the current subkey */
Tcl_Obj *resultPtr; /* List of subkeys being accumulated */
int result;
if (patternObj) {
pattern = Tcl_GetString(patternObj);
} else {
pattern = NULL;
}
/*
* Attempt to open the key for enumeration.
*/
mode |= KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS;
if (OpenKey(interp, keyNameObj, mode, 0, &key) != TCL_OK) {
return TCL_ERROR;
}
/*
* NOTE: the original code used RegQueryInfoKey to get the key count
* and max subkey len. There are several issues with this, including
* a race condition (others might modify counts and/or lengths),
* as well as a mysterious bug (seemingly in RegQueryInfoKey) where
* a subkey is included in the return count but its length is not
* accounted for - observed in a running system through a debugger
*
* The modified code below makes use of the fact that a key is
* at most 256 bytes long and buffer is simply allocated on stack. It then
* iterates until "eof" (ERROR_NO_MORE_ITEMS). So we do not need
* RegQueryInfoKey or memory allocs/frees.
*/
/* resultPtr is ALWAYS used, both on success, as well as failure */
resultPtr = Tcl_NewObj();
/* Loop until we break out on eof or error */
for (result = ERROR_SUCCESS, index = 0; ; ++index) {
/*
* Microsoft specifies the max key length as 255. Plus 1 for
* terminating \0. However, it turns out you can actually write 256
* chars to a key (even though you are not supposed to) and get it
* back so we make the buffer 256+1 bytes
*/
TCHAR subkeyName[257];
DWORD bufSize; /* Size of the buffer */
Tcl_DString ds;
/* Must init bufSize on each iteration */
bufSize = sizeof(subkeyName)/sizeof(subkeyName[0]);
result = RegEnumKeyEx(key, index, subkeyName, &bufSize,
NULL, NULL, NULL, NULL);
if (result != ERROR_SUCCESS)
break;
/* Got subkey, store it after pattern match check (if there is one) */
Tcl_WinTCharToUtf(subkeyName, bufSize * sizeof(TCHAR), &ds);
if (pattern == NULL
|| Tcl_StringMatch(Tcl_DStringValue(&ds), pattern)) {
Tcl_ListObjAppendElement(interp, resultPtr,
Tcl_NewStringObj(Tcl_DStringValue(&ds),
Tcl_DStringLength(&ds)));
}
Tcl_DStringFree(&ds);
}
RegCloseKey(key);
/*
* On loop termination, result contains a Windows error code.
* ERROR_NO_MORE_ITEMS means eof - i.e. success retrieving all
* anything else is a genuine error
*/
if (result == ERROR_NO_MORE_ITEMS) {
/* SUCCESS RETURN - All done with subkeys */
Tcl_SetObjResult(interp, resultPtr);
result = TCL_OK;
} else {
/* ERROR RETURN */
Tcl_SetObjLength(resultPtr, 0); /* Discard collected data */
Tcl_AppendStringsToObj(resultPtr,
"unable to enumerate subkeys of \"",
Tcl_GetString(keyNameObj), "\": ", NULL);
Tcl_SetObjResult(interp, resultPtr);
AppendSystemError(interp, result);
result = TCL_ERROR;
}
return result;
}