#include <tclPort.h>
#include <stdlib.h>
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT
#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
#define REG_CREATE 1
static CONST char *rootKeyNames[] = {
"HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
"HKEY_CURRENT_USER", "HKEY_CURRENT_CONFIG",
"HKEY_PERFORMANCE_DATA", "HKEY_DYN_DATA", NULL
};
static HKEY rootKeys[] = {
HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT, HKEY_CURRENT_USER,
HKEY_CURRENT_CONFIG, HKEY_PERFORMANCE_DATA, HKEY_DYN_DATA
};
static CONST char *typeNames[] = {
"none", "sz", "expand_sz", "binary", "dword",
"dword_big_endian", "link", "multi_sz", "resource_list", NULL
};
static DWORD lastType = REG_RESOURCE_LIST;
typedef struct RegWinProcs {
int useWide;
LONG (WINAPI *regConnectRegistryProc)(CONST TCHAR *, HKEY, PHKEY);
LONG (WINAPI *regCreateKeyExProc)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *, DWORD *);
LONG (WINAPI *regDeleteKeyProc)(HKEY, CONST TCHAR *);
LONG (WINAPI *regDeleteValueProc)(HKEY, CONST TCHAR *);
LONG (WINAPI *regEnumKeyProc)(HKEY, DWORD, TCHAR *, DWORD);
LONG (WINAPI *regEnumKeyExProc)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
TCHAR *, DWORD *, FILETIME *);
LONG (WINAPI *regEnumValueProc)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
DWORD *, BYTE *, DWORD *);
LONG (WINAPI *regOpenKeyExProc)(HKEY, CONST TCHAR *, DWORD, REGSAM,
HKEY *);
LONG (WINAPI *regQueryInfoKeyProc)(HKEY, TCHAR *, DWORD *, DWORD *,
DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
FILETIME *);
LONG (WINAPI *regQueryValueExProc)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
BYTE *, DWORD *);
LONG (WINAPI *regSetValueExProc)(HKEY, CONST TCHAR *, DWORD, DWORD,
CONST BYTE*, DWORD);
} RegWinProcs;
static RegWinProcs *regWinProcs;
static RegWinProcs asciiProcs = {
0,
(LONG (WINAPI *)(CONST TCHAR *, HKEY, PHKEY)) RegConnectRegistryA,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *,
DWORD *)) RegCreateKeyExA,
(LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteKeyA,
(LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteValueA,
(LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD)) RegEnumKeyA,
(LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
TCHAR *, DWORD *, FILETIME *)) RegEnumKeyExA,
(LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
DWORD *, BYTE *, DWORD *)) RegEnumValueA,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, REGSAM,
HKEY *)) RegOpenKeyExA,
(LONG (WINAPI *)(HKEY, TCHAR *, DWORD *, DWORD *,
DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
FILETIME *)) RegQueryInfoKeyA,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
BYTE *, DWORD *)) RegQueryValueExA,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, DWORD,
CONST BYTE*, DWORD)) RegSetValueExA,
};
static RegWinProcs unicodeProcs = {
1,
(LONG (WINAPI *)(CONST TCHAR *, HKEY, PHKEY)) RegConnectRegistryW,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *,
DWORD *)) RegCreateKeyExW,
(LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteKeyW,
(LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteValueW,
(LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD)) RegEnumKeyW,
(LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
TCHAR *, DWORD *, FILETIME *)) RegEnumKeyExW,
(LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
DWORD *, BYTE *, DWORD *)) RegEnumValueW,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, REGSAM,
HKEY *)) RegOpenKeyExW,
(LONG (WINAPI *)(HKEY, TCHAR *, DWORD *, DWORD *,
DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
FILETIME *)) RegQueryInfoKeyW,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
BYTE *, DWORD *)) RegQueryValueExW,
(LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, DWORD,
CONST BYTE*, DWORD)) RegSetValueExW,
};
static void AppendSystemError(Tcl_Interp *interp, DWORD error);
static int BroadcastValue(Tcl_Interp *interp, int objc,
Tcl_Obj * CONST objv[]);
static DWORD ConvertDWORD(DWORD type, DWORD value);
static int DeleteKey(Tcl_Interp *interp, Tcl_Obj *keyNameObj);
static int DeleteValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj);
static int GetKeyNames(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *patternObj);
static int GetType(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj);
static int GetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj);
static int GetValueNames(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *patternObj);
static int OpenKey(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
REGSAM mode, int flags, HKEY *keyPtr);
static DWORD OpenSubKey(char *hostName, HKEY rootKey,
char *keyName, REGSAM mode, int flags,
HKEY *keyPtr);
static int ParseKeyName(Tcl_Interp *interp, char *name,
char **hostNamePtr, HKEY *rootKeyPtr,
char **keyNamePtr);
static DWORD RecursiveDeleteKey(HKEY hStartKey,
CONST TCHAR * pKeyName);
static int RegistryObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj * CONST objv[]);
static int SetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj, Tcl_Obj *dataObj,
Tcl_Obj *typeObj);
EXTERN int Registry_Init(Tcl_Interp *interp);
int
Registry_Init(
Tcl_Interp *interp)
{
if (!Tcl_InitStubs(interp, "8.0", 0)) {
return TCL_ERROR;
}
if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
regWinProcs = &unicodeProcs;
} else {
regWinProcs = &asciiProcs;
}
Tcl_CreateObjCommand(interp, "registry", RegistryObjCmd, NULL, NULL);
return Tcl_PkgProvide(interp, "registry", "1.0");
}
static int
RegistryObjCmd(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj * CONST objv[])
{
int index;
char *errString;
static CONST char *subcommands[] = {
"broadcast", "delete", "get", "keys", "set", "type", "values",
(char *) NULL
};
enum SubCmdIdx {
BroadcastIdx, DeleteIdx, GetIdx, KeysIdx, SetIdx, TypeIdx, ValuesIdx
};
if (objc < 2) {
Tcl_WrongNumArgs(interp, objc, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], subcommands, "option", 0, &index)
!= TCL_OK) {
return TCL_ERROR;
}
switch (index) {
case BroadcastIdx:
return BroadcastValue(interp, objc, objv);
break;
case DeleteIdx:
if (objc == 3) {
return DeleteKey(interp, objv[2]);
} else if (objc == 4) {
return DeleteValue(interp, objv[2], objv[3]);
}
errString = "keyName ?valueName?";
break;
case GetIdx:
if (objc == 4) {
return GetValue(interp, objv[2], objv[3]);
}
errString = "keyName valueName";
break;
case KeysIdx:
if (objc == 3) {
return GetKeyNames(interp, objv[2], NULL);
} else if (objc == 4) {
return GetKeyNames(interp, objv[2], objv[3]);
}
errString = "keyName ?pattern?";
break;
case SetIdx:
if (objc == 3) {
HKEY key;
if (OpenKey(interp, objv[2], KEY_ALL_ACCESS, 1, &key)
!= TCL_OK) {
return TCL_ERROR;
}
RegCloseKey(key);
return TCL_OK;
} else if (objc == 5 || objc == 6) {
Tcl_Obj *typeObj = (objc == 5) ? NULL : objv[5];
return SetValue(interp, objv[2], objv[3], objv[4], typeObj);
}
errString = "keyName ?valueName data ?type??";
break;
case TypeIdx:
if (objc == 4) {
return GetType(interp, objv[2], objv[3]);
}
errString = "keyName valueName";
break;
case ValuesIdx:
if (objc == 3) {
return GetValueNames(interp, objv[2], NULL);
} else if (objc == 4) {
return GetValueNames(interp, objv[2], objv[3]);
}
errString = "keyName ?pattern?";
break;
}
Tcl_WrongNumArgs(interp, 2, objv, errString);
return TCL_ERROR;
}
static int
DeleteKey(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj)
{
char *tail, *buffer, *hostName, *keyName;
CONST char *nativeTail;
HKEY rootKey, subkey;
DWORD result;
int length;
Tcl_Obj *resultPtr;
Tcl_DString buf;
keyName = Tcl_GetStringFromObj(keyNameObj, &length);
buffer = ckalloc((unsigned int) length + 1);
strcpy(buffer, keyName);
if (ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName)
!= TCL_OK) {
ckfree(buffer);
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
if (*keyName == '\0') {
Tcl_AppendToObj(resultPtr, "bad key: cannot delete root keys", -1);
ckfree(buffer);
return TCL_ERROR;
}
tail = strrchr(keyName, '\\');
if (tail) {
*tail++ = '\0';
} else {
tail = keyName;
keyName = NULL;
}
result = OpenSubKey(hostName, rootKey, keyName,
KEY_ENUMERATE_SUB_KEYS | DELETE, 0, &subkey);
if (result != ERROR_SUCCESS) {
ckfree(buffer);
if (result == ERROR_FILE_NOT_FOUND) {
return TCL_OK;
} else {
Tcl_AppendToObj(resultPtr, "unable to delete key: ", -1);
AppendSystemError(interp, result);
return TCL_ERROR;
}
}
nativeTail = Tcl_WinUtfToTChar(tail, -1, &buf);
result = RecursiveDeleteKey(subkey, nativeTail);
Tcl_DStringFree(&buf);
if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
Tcl_AppendToObj(resultPtr, "unable to delete key: ", -1);
AppendSystemError(interp, result);
result = TCL_ERROR;
} else {
result = TCL_OK;
}
RegCloseKey(subkey);
ckfree(buffer);
return result;
}
static int
DeleteValue(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj)
{
HKEY key;
char *valueName;
int length;
DWORD result;
Tcl_Obj *resultPtr;
Tcl_DString ds;
if (OpenKey(interp, keyNameObj, KEY_SET_VALUE, 0, &key)
!= TCL_OK) {
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
valueName = Tcl_GetStringFromObj(valueNameObj, &length);
Tcl_WinUtfToTChar(valueName, length, &ds);
result = (*regWinProcs->regDeleteValueProc)(key, Tcl_DStringValue(&ds));
Tcl_DStringFree(&ds);
if (result != ERROR_SUCCESS) {
Tcl_AppendStringsToObj(resultPtr, "unable to delete value \"",
Tcl_GetString(valueNameObj), "\" from key \"",
Tcl_GetString(keyNameObj), "\": ", NULL);
AppendSystemError(interp, result);
result = TCL_ERROR;
} else {
result = TCL_OK;
}
RegCloseKey(key);
return result;
}
static int
GetKeyNames(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
Tcl_Obj *patternObj)
{
HKEY key;
DWORD index;
char buffer[MAX_PATH+1], *pattern, *name;
Tcl_Obj *resultPtr;
int result = TCL_OK;
Tcl_DString ds;
if (OpenKey(interp, keyNameObj, KEY_ENUMERATE_SUB_KEYS, 0, &key)
!= TCL_OK) {
return TCL_ERROR;
}
if (patternObj) {
pattern = Tcl_GetString(patternObj);
} else {
pattern = NULL;
}
resultPtr = Tcl_GetObjResult(interp);
for (index = 0; (*regWinProcs->regEnumKeyProc)(key, index, buffer,
MAX_PATH+1) == ERROR_SUCCESS; index++) {
Tcl_WinTCharToUtf((TCHAR *) buffer, -1, &ds);
name = Tcl_DStringValue(&ds);
if (pattern && !Tcl_StringMatch(name, pattern)) {
Tcl_DStringFree(&ds);
continue;
}
result = Tcl_ListObjAppendElement(interp, resultPtr,
Tcl_NewStringObj(name, Tcl_DStringLength(&ds)));
Tcl_DStringFree(&ds);
if (result != TCL_OK) {
break;
}
}
RegCloseKey(key);
return result;
}
static int
GetType(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj)
{
HKEY key;
Tcl_Obj *resultPtr;
DWORD result;
DWORD type;
Tcl_DString ds;
char *valueName;
CONST char *nativeValue;
int length;
if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
!= TCL_OK) {
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
valueName = Tcl_GetStringFromObj(valueNameObj, &length);
nativeValue = Tcl_WinUtfToTChar(valueName, length, &ds);
result = (*regWinProcs->regQueryValueExProc)(key, nativeValue, NULL, &type,
NULL, NULL);
Tcl_DStringFree(&ds);
RegCloseKey(key);
if (result != ERROR_SUCCESS) {
Tcl_AppendStringsToObj(resultPtr, "unable to get type of value \"",
Tcl_GetString(valueNameObj), "\" from key \"",
Tcl_GetString(keyNameObj), "\": ", NULL);
AppendSystemError(interp, result);
return TCL_ERROR;
}
if (type > lastType || type < 0) {
Tcl_SetIntObj(resultPtr, (int) type);
} else {
Tcl_SetStringObj(resultPtr, typeNames[type], -1);
}
return TCL_OK;
}
static int
GetValue(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj)
{
HKEY key;
char *valueName;
CONST char *nativeValue;
DWORD result, length, type;
Tcl_Obj *resultPtr;
Tcl_DString data, buf;
int nameLen;
if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
!= TCL_OK) {
return TCL_ERROR;
}
Tcl_DStringInit(&data);
length = TCL_DSTRING_STATIC_SIZE - 1;
Tcl_DStringSetLength(&data, (int) length);
resultPtr = Tcl_GetObjResult(interp);
valueName = Tcl_GetStringFromObj(valueNameObj, &nameLen);
nativeValue = Tcl_WinUtfToTChar(valueName, nameLen, &buf);
result = (*regWinProcs->regQueryValueExProc)(key, nativeValue, NULL, &type,
(BYTE *) Tcl_DStringValue(&data), &length);
while (result == ERROR_MORE_DATA) {
length *= 2;
Tcl_DStringSetLength(&data, (int) length);
result = (*regWinProcs->regQueryValueExProc)(key, (char *) nativeValue,
NULL, &type, (BYTE *) Tcl_DStringValue(&data), &length);
}
Tcl_DStringFree(&buf);
RegCloseKey(key);
if (result != ERROR_SUCCESS) {
Tcl_AppendStringsToObj(resultPtr, "unable to get value \"",
Tcl_GetString(valueNameObj), "\" from key \"",
Tcl_GetString(keyNameObj), "\": ", NULL);
AppendSystemError(interp, result);
Tcl_DStringFree(&data);
return TCL_ERROR;
}
if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
Tcl_SetIntObj(resultPtr, (int) ConvertDWORD(type,
*((DWORD*) Tcl_DStringValue(&data))));
} else if (type == REG_MULTI_SZ) {
char *p = Tcl_DStringValue(&data);
char *end = Tcl_DStringValue(&data) + length;
while (p < end && ((regWinProcs->useWide)
? *((Tcl_UniChar *)p) : *p) != 0) {
Tcl_WinTCharToUtf((TCHAR *) p, -1, &buf);
Tcl_ListObjAppendElement(interp, resultPtr,
Tcl_NewStringObj(Tcl_DStringValue(&buf),
Tcl_DStringLength(&buf)));
if (regWinProcs->useWide) {
while (*((Tcl_UniChar *)p)++ != 0) {}
} else {
while (*p++ != '\0') {}
}
Tcl_DStringFree(&buf);
}
} else if ((type == REG_SZ) || (type == REG_EXPAND_SZ)) {
Tcl_WinTCharToUtf((TCHAR *) Tcl_DStringValue(&data), -1, &buf);
Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&buf),
Tcl_DStringLength(&buf));
Tcl_DStringFree(&buf);
} else {
Tcl_SetByteArrayObj(resultPtr, Tcl_DStringValue(&data), (int) length);
}
Tcl_DStringFree(&data);
return result;
}
static int
GetValueNames(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
Tcl_Obj *patternObj)
{
HKEY key;
Tcl_Obj *resultPtr;
DWORD index, size, maxSize, result;
Tcl_DString buffer, ds;
char *pattern, *name;
if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
!= TCL_OK) {
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
result = (*regWinProcs->regQueryInfoKeyProc)(key, NULL, NULL, NULL, NULL,
NULL, NULL, &index, &maxSize, NULL, NULL, NULL);
if (result != ERROR_SUCCESS) {
Tcl_AppendStringsToObj(resultPtr, "unable to query key \"",
Tcl_GetString(keyNameObj), "\": ", NULL);
AppendSystemError(interp, result);
RegCloseKey(key);
result = TCL_ERROR;
goto done;
}
maxSize++;
Tcl_DStringInit(&buffer);
Tcl_DStringSetLength(&buffer,
(int) ((regWinProcs->useWide) ? maxSize*2 : maxSize));
index = 0;
result = TCL_OK;
if (patternObj) {
pattern = Tcl_GetString(patternObj);
} else {
pattern = NULL;
}
size = maxSize;
while ((*regWinProcs->regEnumValueProc)(key, index,
Tcl_DStringValue(&buffer), &size, NULL, NULL, NULL, NULL)
== ERROR_SUCCESS) {
if (regWinProcs->useWide) {
size *= 2;
}
Tcl_WinTCharToUtf((TCHAR *) Tcl_DStringValue(&buffer), (int) size, &ds);
name = Tcl_DStringValue(&ds);
if (!pattern || Tcl_StringMatch(name, pattern)) {
result = Tcl_ListObjAppendElement(interp, resultPtr,
Tcl_NewStringObj(name, Tcl_DStringLength(&ds)));
if (result != TCL_OK) {
Tcl_DStringFree(&ds);
break;
}
}
Tcl_DStringFree(&ds);
index++;
size = maxSize;
}
Tcl_DStringFree(&buffer);
done:
RegCloseKey(key);
return result;
}
static int
OpenKey(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
REGSAM mode,
int flags,
HKEY *keyPtr)
{
char *keyName, *buffer, *hostName;
int length;
HKEY rootKey;
DWORD result;
keyName = Tcl_GetStringFromObj(keyNameObj, &length);
buffer = ckalloc((unsigned int) length + 1);
strcpy(buffer, keyName);
result = ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName);
if (result == TCL_OK) {
result = OpenSubKey(hostName, rootKey, keyName, mode, flags, keyPtr);
if (result != ERROR_SUCCESS) {
Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
Tcl_AppendToObj(resultPtr, "unable to open key: ", -1);
AppendSystemError(interp, result);
result = TCL_ERROR;
} else {
result = TCL_OK;
}
}
ckfree(buffer);
return result;
}
static DWORD
OpenSubKey(
char *hostName,
HKEY rootKey,
char *keyName,
REGSAM mode,
int flags,
HKEY *keyPtr)
{
DWORD result;
Tcl_DString buf;
if (hostName) {
hostName = (char *) Tcl_WinUtfToTChar(hostName, -1, &buf);
result = (*regWinProcs->regConnectRegistryProc)(hostName, rootKey,
&rootKey);
Tcl_DStringFree(&buf);
if (result != ERROR_SUCCESS) {
return result;
}
}
keyName = (char *) Tcl_WinUtfToTChar(keyName, -1, &buf);
if (flags & REG_CREATE) {
DWORD create;
result = (*regWinProcs->regCreateKeyExProc)(rootKey, keyName, 0, "",
REG_OPTION_NON_VOLATILE, mode, NULL, keyPtr, &create);
} else {
if (rootKey == HKEY_PERFORMANCE_DATA) {
*keyPtr = HKEY_PERFORMANCE_DATA;
result = ERROR_SUCCESS;
} else {
result = (*regWinProcs->regOpenKeyExProc)(rootKey, keyName, 0,
mode, keyPtr);
}
}
Tcl_DStringFree(&buf);
if (hostName) {
RegCloseKey(rootKey);
}
return result;
}
static int
ParseKeyName(
Tcl_Interp *interp,
char *name,
char **hostNamePtr,
HKEY *rootKeyPtr,
char **keyNamePtr)
{
char *rootName;
int result, index;
Tcl_Obj *rootObj, *resultPtr = Tcl_GetObjResult(interp);
*hostNamePtr = *keyNamePtr = rootName = NULL;
if (name[0] == '\\') {
if (name[1] == '\\') {
*hostNamePtr = name;
for (rootName = name+2; *rootName != '\0'; rootName++) {
if (*rootName == '\\') {
*rootName++ = '\0';
break;
}
}
}
} else {
rootName = name;
}
if (!rootName) {
Tcl_AppendStringsToObj(resultPtr, "bad key \"", name,
"\": must start with a valid root", NULL);
return TCL_ERROR;
}
for (*keyNamePtr = rootName; **keyNamePtr != '\0'; (*keyNamePtr)++) {
if (**keyNamePtr == '\\') {
**keyNamePtr = '\0';
(*keyNamePtr)++;
break;
}
}
rootObj = Tcl_NewStringObj(rootName, -1);
result = Tcl_GetIndexFromObj(interp, rootObj, rootKeyNames, "root name",
TCL_EXACT, &index);
Tcl_DecrRefCount(rootObj);
if (result != TCL_OK) {
return TCL_ERROR;
}
*rootKeyPtr = rootKeys[index];
return TCL_OK;
}
static DWORD
RecursiveDeleteKey(
HKEY startKey,
CONST char *keyName)
{
DWORD result, size, maxSize;
Tcl_DString subkey;
HKEY hKey;
if (!keyName || *keyName == '\0') {
return ERROR_BADKEY;
}
result = (*regWinProcs->regOpenKeyExProc)(startKey, keyName, 0,
KEY_ENUMERATE_SUB_KEYS | DELETE | KEY_QUERY_VALUE, &hKey);
if (result != ERROR_SUCCESS) {
return result;
}
result = (*regWinProcs->regQueryInfoKeyProc)(hKey, NULL, NULL, NULL, NULL,
&maxSize, NULL, NULL, NULL, NULL, NULL, NULL);
maxSize++;
if (result != ERROR_SUCCESS) {
return result;
}
Tcl_DStringInit(&subkey);
Tcl_DStringSetLength(&subkey,
(int) ((regWinProcs->useWide) ? maxSize * 2 : maxSize));
while (result == ERROR_SUCCESS) {
size = maxSize;
result=(*regWinProcs->regEnumKeyExProc)(hKey, 0,
Tcl_DStringValue(&subkey), &size, NULL, NULL, NULL, NULL);
if (result == ERROR_NO_MORE_ITEMS) {
result = (*regWinProcs->regDeleteKeyProc)(startKey, keyName);
break;
} else if (result == ERROR_SUCCESS) {
result = RecursiveDeleteKey(hKey, Tcl_DStringValue(&subkey));
}
}
Tcl_DStringFree(&subkey);
RegCloseKey(hKey);
return result;
}
static int
SetValue(
Tcl_Interp *interp,
Tcl_Obj *keyNameObj,
Tcl_Obj *valueNameObj,
Tcl_Obj *dataObj,
Tcl_Obj *typeObj)
{
DWORD type, result;
HKEY key;
int length;
char *valueName;
Tcl_Obj *resultPtr;
Tcl_DString nameBuf;
if (typeObj == NULL) {
type = REG_SZ;
} else if (Tcl_GetIndexFromObj(interp, typeObj, typeNames, "type",
0, (int *) &type) != TCL_OK) {
if (Tcl_GetIntFromObj(NULL, typeObj, (int*) &type) != TCL_OK) {
return TCL_ERROR;
}
Tcl_ResetResult(interp);
}
if (OpenKey(interp, keyNameObj, KEY_ALL_ACCESS, 1, &key) != TCL_OK) {
return TCL_ERROR;
}
valueName = Tcl_GetStringFromObj(valueNameObj, &length);
valueName = (char *) Tcl_WinUtfToTChar(valueName, length, &nameBuf);
resultPtr = Tcl_GetObjResult(interp);
if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
DWORD value;
if (Tcl_GetIntFromObj(interp, dataObj, (int*) &value) != TCL_OK) {
RegCloseKey(key);
Tcl_DStringFree(&nameBuf);
return TCL_ERROR;
}
value = ConvertDWORD(type, value);
result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
(BYTE*) &value, sizeof(DWORD));
} else if (type == REG_MULTI_SZ) {
Tcl_DString data, buf;
int objc, i;
Tcl_Obj **objv;
if (Tcl_ListObjGetElements(interp, dataObj, &objc, &objv) != TCL_OK) {
RegCloseKey(key);
Tcl_DStringFree(&nameBuf);
return TCL_ERROR;
}
Tcl_DStringInit(&data);
for (i = 0; i < objc; i++) {
Tcl_DStringAppend(&data, Tcl_GetString(objv[i]), -1);
Tcl_DStringSetLength(&data, Tcl_DStringLength(&data)+1);
}
Tcl_WinUtfToTChar(Tcl_DStringValue(&data), Tcl_DStringLength(&data)+1,
&buf);
result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
(BYTE *) Tcl_DStringValue(&buf),
(DWORD) Tcl_DStringLength(&buf));
Tcl_DStringFree(&data);
Tcl_DStringFree(&buf);
} else if (type == REG_SZ || type == REG_EXPAND_SZ) {
Tcl_DString buf;
char *data = Tcl_GetStringFromObj(dataObj, &length);
data = (char *) Tcl_WinUtfToTChar(data, length, &buf);
if (regWinProcs->useWide) {
Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf)+1);
}
length = Tcl_DStringLength(&buf) + 1;
result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
(BYTE*)data, (DWORD) length);
Tcl_DStringFree(&buf);
} else {
char *data;
data = Tcl_GetByteArrayFromObj(dataObj, &length);
result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
(BYTE *)data, (DWORD) length);
}
Tcl_DStringFree(&nameBuf);
RegCloseKey(key);
if (result != ERROR_SUCCESS) {
Tcl_AppendToObj(resultPtr, "unable to set value: ", -1);
AppendSystemError(interp, result);
return TCL_ERROR;
}
return TCL_OK;
}
static int
BroadcastValue(
Tcl_Interp *interp,
int objc,
Tcl_Obj * CONST objv[])
{
DWORD result, sendResult;
UINT timeout = 3000;
int len;
char *str;
Tcl_Obj *objPtr;
if ((objc != 3) && (objc != 5)) {
Tcl_WrongNumArgs(interp, 2, objv, "keyName ?-timeout millisecs?");
return TCL_ERROR;
}
if (objc > 3) {
str = Tcl_GetStringFromObj(objv[3], &len);
if ((len < 2) || (*str != '-') || strncmp(str, "-timeout", len)) {
Tcl_WrongNumArgs(interp, 2, objv, "keyName ?-timeout millisecs?");
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[4], (int *) &timeout) != TCL_OK) {
return TCL_ERROR;
}
}
str = Tcl_GetStringFromObj(objv[2], &len);
if (len = 0) {
str = NULL;
}
result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE,
(WPARAM) 0, (LPARAM) str, SMTO_ABORTIFHUNG, timeout, &sendResult);
objPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewIntObj(result));
Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewIntObj(sendResult));
Tcl_SetObjResult(interp, objPtr);
return TCL_OK;
}
static void
AppendSystemError(
Tcl_Interp *interp,
DWORD error)
{
int length;
WCHAR *wMsgPtr;
char *msg;
char id[TCL_INTEGER_SPACE], msgBuf[24 + TCL_INTEGER_SPACE];
Tcl_DString ds;
Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (WCHAR *) &wMsgPtr,
0, NULL);
if (length == 0) {
char *msgPtr;
length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &msgPtr,
0, NULL);
if (length > 0) {
wMsgPtr = (WCHAR *) LocalAlloc(LPTR, (length + 1) * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, msgPtr, length + 1, wMsgPtr,
length + 1);
LocalFree(msgPtr);
}
}
if (length == 0) {
if (error == ERROR_CALL_NOT_IMPLEMENTED) {
msg = "function not supported under Win32s";
} else {
sprintf(msgBuf, "unknown error: %ld", error);
msg = msgBuf;
}
} else {
Tcl_Encoding encoding;
encoding = Tcl_GetEncoding(NULL, "unicode");
Tcl_ExternalToUtfDString(encoding, (char *) wMsgPtr, -1, &ds);
Tcl_FreeEncoding(encoding);
LocalFree(wMsgPtr);
msg = Tcl_DStringValue(&ds);
length = Tcl_DStringLength(&ds);
if (msg[length-1] == '\n') {
msg[--length] = 0;
}
if (msg[length-1] == '\r') {
msg[--length] = 0;
}
}
sprintf(id, "%ld", error);
Tcl_SetErrorCode(interp, "WINDOWS", id, msg, (char *) NULL);
Tcl_AppendToObj(resultPtr, msg, length);
if (length != 0) {
Tcl_DStringFree(&ds);
}
}
static DWORD
ConvertDWORD(
DWORD type,
DWORD value)
{
DWORD order = 1;
DWORD localType;
localType = (*((char*)(&order)) == 1) ? REG_DWORD : REG_DWORD_BIG_ENDIAN;
return (type != localType) ? SWAPLONG(value) : value;
}