os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinReg.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/*
sl@0
     2
 * tclWinReg.c --
sl@0
     3
 *
sl@0
     4
 *	This file contains the implementation of the "registry" Tcl
sl@0
     5
 *	built-in command.  This command is built as a dynamically
sl@0
     6
 *	loadable extension in a separate DLL.
sl@0
     7
 *
sl@0
     8
 * Copyright (c) 1997 by Sun Microsystems, Inc.
sl@0
     9
 * Copyright (c) 1998-1999 by Scriptics Corporation.
sl@0
    10
 *
sl@0
    11
 * See the file "license.terms" for information on usage and redistribution
sl@0
    12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    13
 *
sl@0
    14
 * RCS: @(#) $Id: tclWinReg.c,v 1.21.2.7 2007/05/15 16:08:22 dgp Exp $
sl@0
    15
 */
sl@0
    16
sl@0
    17
#include <tclPort.h>
sl@0
    18
#include <stdlib.h>
sl@0
    19
sl@0
    20
/*
sl@0
    21
 * TCL_STORAGE_CLASS is set unconditionally to DLLEXPORT because the
sl@0
    22
 * Registry_Init declaration is in the source file itself, which is only
sl@0
    23
 * accessed when we are building a library.
sl@0
    24
 */
sl@0
    25
sl@0
    26
#undef TCL_STORAGE_CLASS
sl@0
    27
#define TCL_STORAGE_CLASS DLLEXPORT
sl@0
    28
sl@0
    29
/*
sl@0
    30
 * The following macros convert between different endian ints.
sl@0
    31
 */
sl@0
    32
sl@0
    33
#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
sl@0
    34
#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
sl@0
    35
sl@0
    36
/*
sl@0
    37
 * The following flag is used in OpenKeys to indicate that the specified
sl@0
    38
 * key should be created if it doesn't currently exist.
sl@0
    39
 */
sl@0
    40
sl@0
    41
#define REG_CREATE 1
sl@0
    42
sl@0
    43
/*
sl@0
    44
 * The following tables contain the mapping from registry root names
sl@0
    45
 * to the system predefined keys.
sl@0
    46
 */
sl@0
    47
sl@0
    48
static CONST char *rootKeyNames[] = {
sl@0
    49
    "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
sl@0
    50
    "HKEY_CURRENT_USER", "HKEY_CURRENT_CONFIG",
sl@0
    51
    "HKEY_PERFORMANCE_DATA", "HKEY_DYN_DATA", NULL
sl@0
    52
};
sl@0
    53
sl@0
    54
static HKEY rootKeys[] = {
sl@0
    55
    HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT, HKEY_CURRENT_USER,
sl@0
    56
    HKEY_CURRENT_CONFIG, HKEY_PERFORMANCE_DATA, HKEY_DYN_DATA
sl@0
    57
};
sl@0
    58
sl@0
    59
/*
sl@0
    60
 * The following table maps from registry types to strings.  Note that
sl@0
    61
 * the indices for this array are the same as the constants for the
sl@0
    62
 * known registry types so we don't need a separate table to hold the
sl@0
    63
 * mapping.
sl@0
    64
 */
sl@0
    65
sl@0
    66
static CONST char *typeNames[] = {
sl@0
    67
    "none", "sz", "expand_sz", "binary", "dword",
sl@0
    68
    "dword_big_endian", "link", "multi_sz", "resource_list", NULL
sl@0
    69
};
sl@0
    70
sl@0
    71
static DWORD lastType = REG_RESOURCE_LIST;
sl@0
    72
sl@0
    73
/*
sl@0
    74
 * The following structures allow us to select between the Unicode and ASCII
sl@0
    75
 * interfaces at run time based on whether Unicode APIs are available.  The
sl@0
    76
 * Unicode APIs are preferable because they will handle characters outside
sl@0
    77
 * of the current code page.
sl@0
    78
 */
sl@0
    79
sl@0
    80
typedef struct RegWinProcs {
sl@0
    81
    int useWide;
sl@0
    82
sl@0
    83
    LONG (WINAPI *regConnectRegistryProc)(CONST TCHAR *, HKEY, PHKEY);
sl@0
    84
    LONG (WINAPI *regCreateKeyExProc)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
sl@0
    85
	    DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *, DWORD *); 
sl@0
    86
    LONG (WINAPI *regDeleteKeyProc)(HKEY, CONST TCHAR *);
sl@0
    87
    LONG (WINAPI *regDeleteValueProc)(HKEY, CONST TCHAR *);
sl@0
    88
    LONG (WINAPI *regEnumKeyProc)(HKEY, DWORD, TCHAR *, DWORD);
sl@0
    89
    LONG (WINAPI *regEnumKeyExProc)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
sl@0
    90
	    TCHAR *, DWORD *, FILETIME *);
sl@0
    91
    LONG (WINAPI *regEnumValueProc)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
sl@0
    92
	    DWORD *, BYTE *, DWORD *);
sl@0
    93
    LONG (WINAPI *regOpenKeyExProc)(HKEY, CONST TCHAR *, DWORD, REGSAM,
sl@0
    94
	    HKEY *);
sl@0
    95
    LONG (WINAPI *regQueryInfoKeyProc)(HKEY, TCHAR *, DWORD *, DWORD *,
sl@0
    96
	    DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
sl@0
    97
	    FILETIME *);
sl@0
    98
    LONG (WINAPI *regQueryValueExProc)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
sl@0
    99
	    BYTE *, DWORD *);
sl@0
   100
    LONG (WINAPI *regSetValueExProc)(HKEY, CONST TCHAR *, DWORD, DWORD,
sl@0
   101
	    CONST BYTE*, DWORD);
sl@0
   102
} RegWinProcs;
sl@0
   103
sl@0
   104
static RegWinProcs *regWinProcs;
sl@0
   105
sl@0
   106
static RegWinProcs asciiProcs = {
sl@0
   107
    0,
sl@0
   108
sl@0
   109
    (LONG (WINAPI *)(CONST TCHAR *, HKEY, PHKEY)) RegConnectRegistryA,
sl@0
   110
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
sl@0
   111
	    DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *,
sl@0
   112
	    DWORD *)) RegCreateKeyExA, 
sl@0
   113
    (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteKeyA,
sl@0
   114
    (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteValueA,
sl@0
   115
    (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD)) RegEnumKeyA,
sl@0
   116
    (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
sl@0
   117
	    TCHAR *, DWORD *, FILETIME *)) RegEnumKeyExA,
sl@0
   118
    (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
sl@0
   119
	    DWORD *, BYTE *, DWORD *)) RegEnumValueA,
sl@0
   120
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, REGSAM,
sl@0
   121
	    HKEY *)) RegOpenKeyExA,
sl@0
   122
    (LONG (WINAPI *)(HKEY, TCHAR *, DWORD *, DWORD *,
sl@0
   123
	    DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
sl@0
   124
	    FILETIME *)) RegQueryInfoKeyA,
sl@0
   125
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
sl@0
   126
	    BYTE *, DWORD *)) RegQueryValueExA,
sl@0
   127
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, DWORD,
sl@0
   128
	    CONST BYTE*, DWORD)) RegSetValueExA,
sl@0
   129
};
sl@0
   130
sl@0
   131
static RegWinProcs unicodeProcs = {
sl@0
   132
    1,
sl@0
   133
sl@0
   134
    (LONG (WINAPI *)(CONST TCHAR *, HKEY, PHKEY)) RegConnectRegistryW,
sl@0
   135
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, TCHAR *,
sl@0
   136
	    DWORD, REGSAM, SECURITY_ATTRIBUTES *, HKEY *,
sl@0
   137
	    DWORD *)) RegCreateKeyExW, 
sl@0
   138
    (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteKeyW,
sl@0
   139
    (LONG (WINAPI *)(HKEY, CONST TCHAR *)) RegDeleteValueW,
sl@0
   140
    (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD)) RegEnumKeyW,
sl@0
   141
    (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
sl@0
   142
	    TCHAR *, DWORD *, FILETIME *)) RegEnumKeyExW,
sl@0
   143
    (LONG (WINAPI *)(HKEY, DWORD, TCHAR *, DWORD *, DWORD *,
sl@0
   144
	    DWORD *, BYTE *, DWORD *)) RegEnumValueW,
sl@0
   145
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, REGSAM,
sl@0
   146
	    HKEY *)) RegOpenKeyExW,
sl@0
   147
    (LONG (WINAPI *)(HKEY, TCHAR *, DWORD *, DWORD *,
sl@0
   148
	    DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *, DWORD *,
sl@0
   149
	    FILETIME *)) RegQueryInfoKeyW,
sl@0
   150
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD *, DWORD *,
sl@0
   151
	    BYTE *, DWORD *)) RegQueryValueExW,
sl@0
   152
    (LONG (WINAPI *)(HKEY, CONST TCHAR *, DWORD, DWORD,
sl@0
   153
	    CONST BYTE*, DWORD)) RegSetValueExW,
sl@0
   154
};
sl@0
   155
sl@0
   156
sl@0
   157
/*
sl@0
   158
 * Declarations for functions defined in this file.
sl@0
   159
 */
sl@0
   160
sl@0
   161
static void		AppendSystemError(Tcl_Interp *interp, DWORD error);
sl@0
   162
static int		BroadcastValue(Tcl_Interp *interp, int objc,
sl@0
   163
			    Tcl_Obj * CONST objv[]);
sl@0
   164
static DWORD		ConvertDWORD(DWORD type, DWORD value);
sl@0
   165
static int		DeleteKey(Tcl_Interp *interp, Tcl_Obj *keyNameObj);
sl@0
   166
static int		DeleteValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   167
			    Tcl_Obj *valueNameObj);
sl@0
   168
static int		GetKeyNames(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   169
			    Tcl_Obj *patternObj);
sl@0
   170
static int		GetType(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   171
			    Tcl_Obj *valueNameObj);
sl@0
   172
static int		GetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   173
			    Tcl_Obj *valueNameObj);
sl@0
   174
static int		GetValueNames(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   175
			    Tcl_Obj *patternObj);
sl@0
   176
static int		OpenKey(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   177
			    REGSAM mode, int flags, HKEY *keyPtr);
sl@0
   178
static DWORD		OpenSubKey(char *hostName, HKEY rootKey,
sl@0
   179
			    char *keyName, REGSAM mode, int flags,
sl@0
   180
			    HKEY *keyPtr);
sl@0
   181
static int		ParseKeyName(Tcl_Interp *interp, char *name,
sl@0
   182
			    char **hostNamePtr, HKEY *rootKeyPtr,
sl@0
   183
			    char **keyNamePtr);
sl@0
   184
static DWORD		RecursiveDeleteKey(HKEY hStartKey,
sl@0
   185
			    CONST TCHAR * pKeyName);
sl@0
   186
static int		RegistryObjCmd(ClientData clientData,
sl@0
   187
			    Tcl_Interp *interp, int objc,
sl@0
   188
			    Tcl_Obj * CONST objv[]);
sl@0
   189
static int		SetValue(Tcl_Interp *interp, Tcl_Obj *keyNameObj,
sl@0
   190
			    Tcl_Obj *valueNameObj, Tcl_Obj *dataObj,
sl@0
   191
			    Tcl_Obj *typeObj);
sl@0
   192
sl@0
   193
EXTERN int Registry_Init(Tcl_Interp *interp);
sl@0
   194

sl@0
   195
/*
sl@0
   196
 *----------------------------------------------------------------------
sl@0
   197
 *
sl@0
   198
 * Registry_Init --
sl@0
   199
 *
sl@0
   200
 *	This procedure initializes the registry command.
sl@0
   201
 *
sl@0
   202
 * Results:
sl@0
   203
 *	A standard Tcl result.
sl@0
   204
 *
sl@0
   205
 * Side effects:
sl@0
   206
 *	None.
sl@0
   207
 *
sl@0
   208
 *----------------------------------------------------------------------
sl@0
   209
 */
sl@0
   210
sl@0
   211
int
sl@0
   212
Registry_Init(
sl@0
   213
    Tcl_Interp *interp)
sl@0
   214
{
sl@0
   215
    if (!Tcl_InitStubs(interp, "8.0", 0)) {
sl@0
   216
	return TCL_ERROR;
sl@0
   217
    }
sl@0
   218
sl@0
   219
    /*
sl@0
   220
     * Determine if the unicode interfaces are available and select the
sl@0
   221
     * appropriate registry function table.
sl@0
   222
     */
sl@0
   223
sl@0
   224
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
sl@0
   225
	regWinProcs = &unicodeProcs;
sl@0
   226
    } else {
sl@0
   227
	regWinProcs = &asciiProcs;
sl@0
   228
    }
sl@0
   229
sl@0
   230
    Tcl_CreateObjCommand(interp, "registry", RegistryObjCmd, NULL, NULL);
sl@0
   231
    return Tcl_PkgProvide(interp, "registry", "1.1.5");
sl@0
   232
}
sl@0
   233

sl@0
   234
/*
sl@0
   235
 *----------------------------------------------------------------------
sl@0
   236
 *
sl@0
   237
 * RegistryObjCmd --
sl@0
   238
 *
sl@0
   239
 *	This function implements the Tcl "registry" command.
sl@0
   240
 *
sl@0
   241
 * Results:
sl@0
   242
 *	A standard Tcl result.
sl@0
   243
 *
sl@0
   244
 * Side effects:
sl@0
   245
 *	None.
sl@0
   246
 *
sl@0
   247
 *----------------------------------------------------------------------
sl@0
   248
 */
sl@0
   249
sl@0
   250
static int
sl@0
   251
RegistryObjCmd(
sl@0
   252
    ClientData clientData,	/* Not used. */
sl@0
   253
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   254
    int objc,			/* Number of arguments. */
sl@0
   255
    Tcl_Obj * CONST objv[])	/* Argument values. */
sl@0
   256
{
sl@0
   257
    int index;
sl@0
   258
    char *errString;
sl@0
   259
sl@0
   260
    static CONST char *subcommands[] = {
sl@0
   261
	"broadcast", "delete", "get", "keys", "set", "type", "values",
sl@0
   262
	(char *) NULL
sl@0
   263
    };
sl@0
   264
    enum SubCmdIdx {
sl@0
   265
	BroadcastIdx, DeleteIdx, GetIdx, KeysIdx, SetIdx, TypeIdx, ValuesIdx
sl@0
   266
    };
sl@0
   267
sl@0
   268
    if (objc < 2) {
sl@0
   269
	Tcl_WrongNumArgs(interp, objc, objv, "option ?arg arg ...?");
sl@0
   270
	return TCL_ERROR;
sl@0
   271
    }
sl@0
   272
sl@0
   273
    if (Tcl_GetIndexFromObj(interp, objv[1], subcommands, "option", 0, &index)
sl@0
   274
	    != TCL_OK) {
sl@0
   275
	return TCL_ERROR;
sl@0
   276
    }
sl@0
   277
sl@0
   278
    switch (index) {
sl@0
   279
	case BroadcastIdx:		/* broadcast */
sl@0
   280
	    return BroadcastValue(interp, objc, objv);
sl@0
   281
	    break;
sl@0
   282
	case DeleteIdx:			/* delete */
sl@0
   283
	    if (objc == 3) {
sl@0
   284
		return DeleteKey(interp, objv[2]);
sl@0
   285
	    } else if (objc == 4) {
sl@0
   286
		return DeleteValue(interp, objv[2], objv[3]);
sl@0
   287
	    }
sl@0
   288
	    errString = "keyName ?valueName?";
sl@0
   289
	    break;
sl@0
   290
	case GetIdx:			/* get */
sl@0
   291
	    if (objc == 4) {
sl@0
   292
		return GetValue(interp, objv[2], objv[3]);
sl@0
   293
	    }
sl@0
   294
	    errString = "keyName valueName";
sl@0
   295
	    break;
sl@0
   296
	case KeysIdx:			/* keys */
sl@0
   297
	    if (objc == 3) {
sl@0
   298
		return GetKeyNames(interp, objv[2], NULL);
sl@0
   299
	    } else if (objc == 4) {
sl@0
   300
		return GetKeyNames(interp, objv[2], objv[3]);
sl@0
   301
	    }
sl@0
   302
	    errString = "keyName ?pattern?";
sl@0
   303
	    break;
sl@0
   304
	case SetIdx:			/* set */
sl@0
   305
	    if (objc == 3) {
sl@0
   306
		HKEY key;
sl@0
   307
sl@0
   308
		/*
sl@0
   309
		 * Create the key and then close it immediately.
sl@0
   310
		 */
sl@0
   311
sl@0
   312
		if (OpenKey(interp, objv[2], KEY_ALL_ACCESS, 1, &key)
sl@0
   313
			!= TCL_OK) {
sl@0
   314
		    return TCL_ERROR;
sl@0
   315
		}
sl@0
   316
		RegCloseKey(key);
sl@0
   317
		return TCL_OK;
sl@0
   318
	    } else if (objc == 5 || objc == 6) {
sl@0
   319
		Tcl_Obj *typeObj = (objc == 5) ? NULL : objv[5];
sl@0
   320
		return SetValue(interp, objv[2], objv[3], objv[4], typeObj);
sl@0
   321
	    }
sl@0
   322
	    errString = "keyName ?valueName data ?type??";
sl@0
   323
	    break;
sl@0
   324
	case TypeIdx:			/* type */
sl@0
   325
	    if (objc == 4) {
sl@0
   326
		return GetType(interp, objv[2], objv[3]);
sl@0
   327
	    }
sl@0
   328
	    errString = "keyName valueName";
sl@0
   329
	    break;
sl@0
   330
	case ValuesIdx:			/* values */
sl@0
   331
	    if (objc == 3) {
sl@0
   332
 		return GetValueNames(interp, objv[2], NULL);
sl@0
   333
	    } else if (objc == 4) {
sl@0
   334
 		return GetValueNames(interp, objv[2], objv[3]);
sl@0
   335
	    }
sl@0
   336
	    errString = "keyName ?pattern?";
sl@0
   337
	    break;
sl@0
   338
    }
sl@0
   339
    Tcl_WrongNumArgs(interp, 2, objv, errString);
sl@0
   340
    return TCL_ERROR;
sl@0
   341
}
sl@0
   342

sl@0
   343
/*
sl@0
   344
 *----------------------------------------------------------------------
sl@0
   345
 *
sl@0
   346
 * DeleteKey --
sl@0
   347
 *
sl@0
   348
 *	This function deletes a registry key.
sl@0
   349
 *
sl@0
   350
 * Results:
sl@0
   351
 *	A standard Tcl result.
sl@0
   352
 *
sl@0
   353
 * Side effects:
sl@0
   354
 *	None.
sl@0
   355
 *
sl@0
   356
 *----------------------------------------------------------------------
sl@0
   357
 */
sl@0
   358
sl@0
   359
static int
sl@0
   360
DeleteKey(
sl@0
   361
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   362
    Tcl_Obj *keyNameObj)	/* Name of key to delete. */
sl@0
   363
{
sl@0
   364
    char *tail, *buffer, *hostName, *keyName;
sl@0
   365
    CONST char *nativeTail;
sl@0
   366
    HKEY rootKey, subkey;
sl@0
   367
    DWORD result;
sl@0
   368
    int length;
sl@0
   369
    Tcl_Obj *resultPtr;
sl@0
   370
    Tcl_DString buf;
sl@0
   371
sl@0
   372
    /*
sl@0
   373
     * Find the parent of the key being deleted and open it.
sl@0
   374
     */
sl@0
   375
sl@0
   376
    keyName = Tcl_GetStringFromObj(keyNameObj, &length);
sl@0
   377
    buffer = ckalloc((unsigned int) length + 1);
sl@0
   378
    strcpy(buffer, keyName);
sl@0
   379
sl@0
   380
    if (ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName)
sl@0
   381
	    != TCL_OK) {
sl@0
   382
	ckfree(buffer);
sl@0
   383
	return TCL_ERROR;
sl@0
   384
    }
sl@0
   385
sl@0
   386
    resultPtr = Tcl_GetObjResult(interp);
sl@0
   387
    if (*keyName == '\0') {
sl@0
   388
	Tcl_AppendToObj(resultPtr, "bad key: cannot delete root keys", -1);
sl@0
   389
	ckfree(buffer);
sl@0
   390
	return TCL_ERROR;
sl@0
   391
    }
sl@0
   392
sl@0
   393
    tail = strrchr(keyName, '\\');
sl@0
   394
    if (tail) {
sl@0
   395
	*tail++ = '\0';
sl@0
   396
    } else {
sl@0
   397
	tail = keyName;
sl@0
   398
	keyName = NULL;
sl@0
   399
    }
sl@0
   400
sl@0
   401
    result = OpenSubKey(hostName, rootKey, keyName,
sl@0
   402
	    KEY_ENUMERATE_SUB_KEYS | DELETE, 0, &subkey);
sl@0
   403
    if (result != ERROR_SUCCESS) {
sl@0
   404
	ckfree(buffer);
sl@0
   405
	if (result == ERROR_FILE_NOT_FOUND) {
sl@0
   406
	    return TCL_OK;
sl@0
   407
	} else {
sl@0
   408
	    Tcl_AppendToObj(resultPtr, "unable to delete key: ", -1);
sl@0
   409
	    AppendSystemError(interp, result);
sl@0
   410
	    return TCL_ERROR;
sl@0
   411
	}
sl@0
   412
    }
sl@0
   413
sl@0
   414
    /*
sl@0
   415
     * Now we recursively delete the key and everything below it.
sl@0
   416
     */
sl@0
   417
sl@0
   418
    nativeTail = Tcl_WinUtfToTChar(tail, -1, &buf);
sl@0
   419
    result = RecursiveDeleteKey(subkey, nativeTail);
sl@0
   420
    Tcl_DStringFree(&buf);
sl@0
   421
sl@0
   422
    if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
sl@0
   423
	Tcl_AppendToObj(resultPtr, "unable to delete key: ", -1);
sl@0
   424
	AppendSystemError(interp, result);
sl@0
   425
	result = TCL_ERROR;
sl@0
   426
    } else {
sl@0
   427
	result = TCL_OK;
sl@0
   428
    }
sl@0
   429
sl@0
   430
    RegCloseKey(subkey);
sl@0
   431
    ckfree(buffer);
sl@0
   432
    return result;
sl@0
   433
}
sl@0
   434

sl@0
   435
/*
sl@0
   436
 *----------------------------------------------------------------------
sl@0
   437
 *
sl@0
   438
 * DeleteValue --
sl@0
   439
 *
sl@0
   440
 *	This function deletes a value from a registry key.
sl@0
   441
 *
sl@0
   442
 * Results:
sl@0
   443
 *	A standard Tcl result.
sl@0
   444
 *
sl@0
   445
 * Side effects:
sl@0
   446
 *	None.
sl@0
   447
 *
sl@0
   448
 *----------------------------------------------------------------------
sl@0
   449
 */
sl@0
   450
sl@0
   451
static int
sl@0
   452
DeleteValue(
sl@0
   453
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   454
    Tcl_Obj *keyNameObj,	/* Name of key. */
sl@0
   455
    Tcl_Obj *valueNameObj)	/* Name of value to delete. */
sl@0
   456
{
sl@0
   457
    HKEY key;
sl@0
   458
    char *valueName;
sl@0
   459
    int length;
sl@0
   460
    DWORD result;
sl@0
   461
    Tcl_Obj *resultPtr;
sl@0
   462
    Tcl_DString ds;
sl@0
   463
sl@0
   464
    /*
sl@0
   465
     * Attempt to open the key for deletion.
sl@0
   466
     */
sl@0
   467
sl@0
   468
    if (OpenKey(interp, keyNameObj, KEY_SET_VALUE, 0, &key)
sl@0
   469
	    != TCL_OK) {
sl@0
   470
	return TCL_ERROR;
sl@0
   471
    }
sl@0
   472
sl@0
   473
    resultPtr = Tcl_GetObjResult(interp);
sl@0
   474
    valueName = Tcl_GetStringFromObj(valueNameObj, &length);
sl@0
   475
    Tcl_WinUtfToTChar(valueName, length, &ds);
sl@0
   476
    result = (*regWinProcs->regDeleteValueProc)(key, Tcl_DStringValue(&ds));
sl@0
   477
    Tcl_DStringFree(&ds);
sl@0
   478
    if (result != ERROR_SUCCESS) {
sl@0
   479
	Tcl_AppendStringsToObj(resultPtr, "unable to delete value \"",
sl@0
   480
		Tcl_GetString(valueNameObj), "\" from key \"",
sl@0
   481
		Tcl_GetString(keyNameObj), "\": ", NULL);
sl@0
   482
	AppendSystemError(interp, result);
sl@0
   483
	result = TCL_ERROR;
sl@0
   484
    } else {
sl@0
   485
	result = TCL_OK;
sl@0
   486
    }
sl@0
   487
    RegCloseKey(key);
sl@0
   488
    return result;
sl@0
   489
}
sl@0
   490

sl@0
   491
/*
sl@0
   492
 *----------------------------------------------------------------------
sl@0
   493
 *
sl@0
   494
 * GetKeyNames --
sl@0
   495
 *
sl@0
   496
 *	This function enumerates the subkeys of a given key.  If the
sl@0
   497
 *	optional pattern is supplied, then only keys that match the
sl@0
   498
 *	pattern will be returned.
sl@0
   499
 *
sl@0
   500
 * Results:
sl@0
   501
 *	Returns the list of subkeys in the result object of the
sl@0
   502
 *	interpreter, or an error message on failure.
sl@0
   503
 *
sl@0
   504
 * Side effects:
sl@0
   505
 *	None.
sl@0
   506
 *
sl@0
   507
 *----------------------------------------------------------------------
sl@0
   508
 */
sl@0
   509
sl@0
   510
static int
sl@0
   511
GetKeyNames(
sl@0
   512
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   513
    Tcl_Obj *keyNameObj,	/* Key to enumerate. */
sl@0
   514
    Tcl_Obj *patternObj)	/* Optional match pattern. */
sl@0
   515
{
sl@0
   516
    char *pattern;		/* Pattern being matched against subkeys */
sl@0
   517
    HKEY key;			/* Handle to the key being examined */
sl@0
   518
    DWORD subKeyCount;		/* Number of subkeys to list */
sl@0
   519
    DWORD maxSubKeyLen;		/* Maximum string length of any subkey */
sl@0
   520
    char *buffer;		/* Buffer to hold the subkey name */
sl@0
   521
    DWORD bufSize;		/* Size of the buffer */
sl@0
   522
    DWORD index;		/* Position of the current subkey */
sl@0
   523
    char *name;			/* Subkey name */
sl@0
   524
    Tcl_Obj *resultPtr;		/* List of subkeys being accumulated */
sl@0
   525
    int result = TCL_OK;	/* Return value from this command */
sl@0
   526
    Tcl_DString ds;		/* Buffer to translate subkey name to UTF-8 */
sl@0
   527
sl@0
   528
    if (patternObj) {
sl@0
   529
	pattern = Tcl_GetString(patternObj);
sl@0
   530
    } else {
sl@0
   531
	pattern = NULL;
sl@0
   532
    }
sl@0
   533
sl@0
   534
    /* Attempt to open the key for enumeration. */
sl@0
   535
sl@0
   536
    if (OpenKey(interp, keyNameObj,
sl@0
   537
		KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
sl@0
   538
		0, &key) != TCL_OK) {
sl@0
   539
	return TCL_ERROR;
sl@0
   540
    }
sl@0
   541
sl@0
   542
    /* 
sl@0
   543
     * Determine how big a buffer is needed for enumerating subkeys, and
sl@0
   544
     * how many subkeys there are
sl@0
   545
     */
sl@0
   546
sl@0
   547
    result = (*regWinProcs->regQueryInfoKeyProc)
sl@0
   548
	(key, NULL, NULL, NULL, &subKeyCount, &maxSubKeyLen, NULL, NULL, 
sl@0
   549
	 NULL, NULL, NULL, NULL);
sl@0
   550
    if (result != ERROR_SUCCESS) {
sl@0
   551
	Tcl_SetObjResult(interp, Tcl_NewObj());
sl@0
   552
	Tcl_AppendResult(interp, "unable to query key \"", 
sl@0
   553
			 Tcl_GetString(keyNameObj), "\": ", NULL);
sl@0
   554
	AppendSystemError(interp, result);
sl@0
   555
	RegCloseKey(key);
sl@0
   556
	return TCL_ERROR;
sl@0
   557
    }
sl@0
   558
    if (regWinProcs->useWide) {
sl@0
   559
	buffer = ckalloc((maxSubKeyLen+1) * sizeof(WCHAR));
sl@0
   560
    } else {
sl@0
   561
	buffer = ckalloc(maxSubKeyLen+1);
sl@0
   562
    }
sl@0
   563
sl@0
   564
    /* Enumerate the subkeys */
sl@0
   565
sl@0
   566
    resultPtr = Tcl_NewObj();
sl@0
   567
    for (index = 0; index < subKeyCount; ++index) {
sl@0
   568
	bufSize = maxSubKeyLen+1;
sl@0
   569
	result = (*regWinProcs->regEnumKeyExProc)
sl@0
   570
	    (key, index, buffer, &bufSize, NULL, NULL, NULL, NULL);
sl@0
   571
	if (result != ERROR_SUCCESS) {
sl@0
   572
	    Tcl_SetObjResult(interp, Tcl_NewObj());
sl@0
   573
	    Tcl_AppendResult(interp,
sl@0
   574
			     "unable to enumerate subkeys of \"",
sl@0
   575
			     Tcl_GetString(keyNameObj),
sl@0
   576
			     "\": ", NULL);
sl@0
   577
	    AppendSystemError(interp, result);
sl@0
   578
	    result = TCL_ERROR;
sl@0
   579
	    break;
sl@0
   580
	}
sl@0
   581
	if (regWinProcs->useWide) {
sl@0
   582
	    Tcl_WinTCharToUtf((TCHAR *) buffer, bufSize * sizeof(WCHAR), &ds);
sl@0
   583
	} else {
sl@0
   584
	    Tcl_WinTCharToUtf((TCHAR *) buffer, bufSize, &ds);
sl@0
   585
	}
sl@0
   586
	name = Tcl_DStringValue(&ds);
sl@0
   587
	if (pattern && !Tcl_StringMatch(name, pattern)) {
sl@0
   588
	    Tcl_DStringFree(&ds);
sl@0
   589
	    continue;
sl@0
   590
	}
sl@0
   591
	result = Tcl_ListObjAppendElement(interp, resultPtr,
sl@0
   592
		Tcl_NewStringObj(name, Tcl_DStringLength(&ds)));
sl@0
   593
	Tcl_DStringFree(&ds);
sl@0
   594
	if (result != TCL_OK) {
sl@0
   595
	    break;
sl@0
   596
	}
sl@0
   597
    }
sl@0
   598
    if (result == TCL_OK) {
sl@0
   599
	Tcl_SetObjResult(interp, resultPtr);
sl@0
   600
    }
sl@0
   601
sl@0
   602
    ckfree(buffer);
sl@0
   603
    RegCloseKey(key);
sl@0
   604
    return result;
sl@0
   605
}
sl@0
   606

sl@0
   607
/*
sl@0
   608
 *----------------------------------------------------------------------
sl@0
   609
 *
sl@0
   610
 * GetType --
sl@0
   611
 *
sl@0
   612
 *	This function gets the type of a given registry value and
sl@0
   613
 *	places it in the interpreter result.
sl@0
   614
 *
sl@0
   615
 * Results:
sl@0
   616
 *	Returns a normal Tcl result.
sl@0
   617
 *
sl@0
   618
 * Side effects:
sl@0
   619
 *	None.
sl@0
   620
 *
sl@0
   621
 *----------------------------------------------------------------------
sl@0
   622
 */
sl@0
   623
sl@0
   624
static int
sl@0
   625
GetType(
sl@0
   626
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   627
    Tcl_Obj *keyNameObj,	/* Name of key. */
sl@0
   628
    Tcl_Obj *valueNameObj)	/* Name of value to get. */
sl@0
   629
{
sl@0
   630
    HKEY key;
sl@0
   631
    Tcl_Obj *resultPtr;
sl@0
   632
    DWORD result;
sl@0
   633
    DWORD type;
sl@0
   634
    Tcl_DString ds;
sl@0
   635
    char *valueName;
sl@0
   636
    CONST char *nativeValue;
sl@0
   637
    int length;
sl@0
   638
sl@0
   639
    /*
sl@0
   640
     * Attempt to open the key for reading.
sl@0
   641
     */
sl@0
   642
sl@0
   643
    if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
sl@0
   644
	    != TCL_OK) {
sl@0
   645
	return TCL_ERROR;
sl@0
   646
    }
sl@0
   647
sl@0
   648
    /*
sl@0
   649
     * Get the type of the value.
sl@0
   650
     */
sl@0
   651
sl@0
   652
    resultPtr = Tcl_GetObjResult(interp);
sl@0
   653
sl@0
   654
    valueName = Tcl_GetStringFromObj(valueNameObj, &length);
sl@0
   655
    nativeValue = Tcl_WinUtfToTChar(valueName, length, &ds);
sl@0
   656
    result = (*regWinProcs->regQueryValueExProc)(key, nativeValue, NULL, &type,
sl@0
   657
	    NULL, NULL);
sl@0
   658
    Tcl_DStringFree(&ds);
sl@0
   659
    RegCloseKey(key);
sl@0
   660
sl@0
   661
    if (result != ERROR_SUCCESS) {
sl@0
   662
	Tcl_AppendStringsToObj(resultPtr, "unable to get type of value \"",
sl@0
   663
		Tcl_GetString(valueNameObj), "\" from key \"",
sl@0
   664
		Tcl_GetString(keyNameObj), "\": ", NULL);
sl@0
   665
	AppendSystemError(interp, result);
sl@0
   666
	return TCL_ERROR;
sl@0
   667
    }
sl@0
   668
sl@0
   669
    /*
sl@0
   670
     * Set the type into the result.  Watch out for unknown types.
sl@0
   671
     * If we don't know about the type, just use the numeric value.
sl@0
   672
     */
sl@0
   673
sl@0
   674
    if (type > lastType || type < 0) {
sl@0
   675
	Tcl_SetIntObj(resultPtr, (int) type);
sl@0
   676
    } else {
sl@0
   677
	Tcl_SetStringObj(resultPtr, typeNames[type], -1);
sl@0
   678
    }
sl@0
   679
    return TCL_OK;
sl@0
   680
}
sl@0
   681

sl@0
   682
/*
sl@0
   683
 *----------------------------------------------------------------------
sl@0
   684
 *
sl@0
   685
 * GetValue --
sl@0
   686
 *
sl@0
   687
 *	This function gets the contents of a registry value and places
sl@0
   688
 *	a list containing the data and the type in the interpreter
sl@0
   689
 *	result.
sl@0
   690
 *
sl@0
   691
 * Results:
sl@0
   692
 *	Returns a normal Tcl result.
sl@0
   693
 *
sl@0
   694
 * Side effects:
sl@0
   695
 *	None.
sl@0
   696
 *
sl@0
   697
 *----------------------------------------------------------------------
sl@0
   698
 */
sl@0
   699
sl@0
   700
static int
sl@0
   701
GetValue(
sl@0
   702
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   703
    Tcl_Obj *keyNameObj,	/* Name of key. */
sl@0
   704
    Tcl_Obj *valueNameObj)	/* Name of value to get. */
sl@0
   705
{
sl@0
   706
    HKEY key;
sl@0
   707
    char *valueName;
sl@0
   708
    CONST char *nativeValue;
sl@0
   709
    DWORD result, length, type;
sl@0
   710
    Tcl_Obj *resultPtr;
sl@0
   711
    Tcl_DString data, buf;
sl@0
   712
    int nameLen;
sl@0
   713
sl@0
   714
    /*
sl@0
   715
     * Attempt to open the key for reading.
sl@0
   716
     */
sl@0
   717
sl@0
   718
    if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
sl@0
   719
	    != TCL_OK) {
sl@0
   720
	return TCL_ERROR;
sl@0
   721
    }
sl@0
   722
sl@0
   723
    /*
sl@0
   724
     * Initialize a Dstring to maximum statically allocated size
sl@0
   725
     * we could get one more byte by avoiding Tcl_DStringSetLength()
sl@0
   726
     * and just setting length to TCL_DSTRING_STATIC_SIZE, but this
sl@0
   727
     * should be safer if the implementation of Dstrings changes.
sl@0
   728
     *
sl@0
   729
     * This allows short values to be read from the registy in one call.
sl@0
   730
     * Longer values need a second call with an expanded DString.
sl@0
   731
     */
sl@0
   732
sl@0
   733
    Tcl_DStringInit(&data);
sl@0
   734
    length = TCL_DSTRING_STATIC_SIZE - 1;
sl@0
   735
    Tcl_DStringSetLength(&data, (int) length);
sl@0
   736
sl@0
   737
    resultPtr = Tcl_GetObjResult(interp);
sl@0
   738
sl@0
   739
    valueName = Tcl_GetStringFromObj(valueNameObj, &nameLen);
sl@0
   740
    nativeValue = Tcl_WinUtfToTChar(valueName, nameLen, &buf);
sl@0
   741
sl@0
   742
    result = (*regWinProcs->regQueryValueExProc)(key, nativeValue, NULL, &type,
sl@0
   743
	    (BYTE *) Tcl_DStringValue(&data), &length);
sl@0
   744
    while (result == ERROR_MORE_DATA) {
sl@0
   745
	/*
sl@0
   746
	 * The Windows docs say that in this error case, we just need
sl@0
   747
	 * to expand our buffer and request more data.
sl@0
   748
	 * Required for HKEY_PERFORMANCE_DATA
sl@0
   749
	 */
sl@0
   750
	length *= 2;
sl@0
   751
        Tcl_DStringSetLength(&data, (int) length);
sl@0
   752
        result = (*regWinProcs->regQueryValueExProc)(key, (char *) nativeValue,
sl@0
   753
		NULL, &type, (BYTE *) Tcl_DStringValue(&data), &length);
sl@0
   754
    }
sl@0
   755
    Tcl_DStringFree(&buf);
sl@0
   756
    RegCloseKey(key);
sl@0
   757
    if (result != ERROR_SUCCESS) {
sl@0
   758
	Tcl_AppendStringsToObj(resultPtr, "unable to get value \"",
sl@0
   759
		Tcl_GetString(valueNameObj), "\" from key \"",
sl@0
   760
		Tcl_GetString(keyNameObj), "\": ", NULL);
sl@0
   761
	AppendSystemError(interp, result);
sl@0
   762
	Tcl_DStringFree(&data);
sl@0
   763
	return TCL_ERROR;
sl@0
   764
    }
sl@0
   765
sl@0
   766
    /*
sl@0
   767
     * If the data is a 32-bit quantity, store it as an integer object.  If it
sl@0
   768
     * is a multi-string, store it as a list of strings.  For null-terminated
sl@0
   769
     * strings, append up the to first null.  Otherwise, store it as a binary
sl@0
   770
     * string.
sl@0
   771
     */
sl@0
   772
sl@0
   773
    if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
sl@0
   774
	Tcl_SetIntObj(resultPtr, (int) ConvertDWORD(type,
sl@0
   775
		*((DWORD*) Tcl_DStringValue(&data))));
sl@0
   776
    } else if (type == REG_MULTI_SZ) {
sl@0
   777
	char *p = Tcl_DStringValue(&data);
sl@0
   778
	char *end = Tcl_DStringValue(&data) + length;
sl@0
   779
sl@0
   780
	/*
sl@0
   781
	 * Multistrings are stored as an array of null-terminated strings,
sl@0
   782
	 * terminated by two null characters.  Also do a bounds check in
sl@0
   783
	 * case we get bogus data.
sl@0
   784
	 */
sl@0
   785
 
sl@0
   786
	while (p < end 	&& ((regWinProcs->useWide) 
sl@0
   787
		? *((Tcl_UniChar *)p) : *p) != 0) {
sl@0
   788
	    Tcl_WinTCharToUtf((TCHAR *) p, -1, &buf);
sl@0
   789
	    Tcl_ListObjAppendElement(interp, resultPtr,
sl@0
   790
		    Tcl_NewStringObj(Tcl_DStringValue(&buf),
sl@0
   791
			    Tcl_DStringLength(&buf)));
sl@0
   792
	    if (regWinProcs->useWide) {
sl@0
   793
		while (*((Tcl_UniChar *)p)++ != 0) {}
sl@0
   794
	    } else {
sl@0
   795
		while (*p++ != '\0') {}
sl@0
   796
	    }
sl@0
   797
	    Tcl_DStringFree(&buf);
sl@0
   798
	}
sl@0
   799
    } else if ((type == REG_SZ) || (type == REG_EXPAND_SZ)) {
sl@0
   800
	Tcl_WinTCharToUtf((TCHAR *) Tcl_DStringValue(&data), -1, &buf);
sl@0
   801
	Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&buf),
sl@0
   802
		Tcl_DStringLength(&buf));
sl@0
   803
	Tcl_DStringFree(&buf);
sl@0
   804
    } else {
sl@0
   805
	/*
sl@0
   806
	 * Save binary data as a byte array.
sl@0
   807
	 */
sl@0
   808
sl@0
   809
	Tcl_SetByteArrayObj(resultPtr, Tcl_DStringValue(&data), (int) length);
sl@0
   810
    }
sl@0
   811
    Tcl_DStringFree(&data);
sl@0
   812
    return result;
sl@0
   813
}
sl@0
   814

sl@0
   815
/*
sl@0
   816
 *----------------------------------------------------------------------
sl@0
   817
 *
sl@0
   818
 * GetValueNames --
sl@0
   819
 *
sl@0
   820
 *	This function enumerates the values of the a given key.  If
sl@0
   821
 *	the optional pattern is supplied, then only value names that
sl@0
   822
 *	match the pattern will be returned.
sl@0
   823
 *
sl@0
   824
 * Results:
sl@0
   825
 *	Returns the list of value names in the result object of the
sl@0
   826
 *	interpreter, or an error message on failure.
sl@0
   827
 *
sl@0
   828
 * Side effects:
sl@0
   829
 *	None.
sl@0
   830
 *
sl@0
   831
 *----------------------------------------------------------------------
sl@0
   832
 */
sl@0
   833
sl@0
   834
static int
sl@0
   835
GetValueNames(
sl@0
   836
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   837
    Tcl_Obj *keyNameObj,	/* Key to enumerate. */
sl@0
   838
    Tcl_Obj *patternObj)	/* Optional match pattern. */
sl@0
   839
{
sl@0
   840
    HKEY key;
sl@0
   841
    Tcl_Obj *resultPtr;
sl@0
   842
    DWORD index, size, maxSize, result;
sl@0
   843
    Tcl_DString buffer, ds;
sl@0
   844
    char *pattern, *name;
sl@0
   845
sl@0
   846
    /*
sl@0
   847
     * Attempt to open the key for enumeration.
sl@0
   848
     */
sl@0
   849
sl@0
   850
    if (OpenKey(interp, keyNameObj, KEY_QUERY_VALUE, 0, &key)
sl@0
   851
	    != TCL_OK) {
sl@0
   852
	return TCL_ERROR;
sl@0
   853
    }
sl@0
   854
sl@0
   855
    resultPtr = Tcl_GetObjResult(interp);
sl@0
   856
sl@0
   857
    /*
sl@0
   858
     * Query the key to determine the appropriate buffer size to hold the
sl@0
   859
     * largest value name plus the terminating null.
sl@0
   860
     */
sl@0
   861
sl@0
   862
    result = (*regWinProcs->regQueryInfoKeyProc)(key, NULL, NULL, NULL, NULL,
sl@0
   863
	    NULL, NULL, &index, &maxSize, NULL, NULL, NULL);
sl@0
   864
    if (result != ERROR_SUCCESS) {
sl@0
   865
	Tcl_AppendStringsToObj(resultPtr, "unable to query key \"",
sl@0
   866
		Tcl_GetString(keyNameObj), "\": ", NULL);
sl@0
   867
	AppendSystemError(interp, result);
sl@0
   868
	RegCloseKey(key);
sl@0
   869
	result = TCL_ERROR;
sl@0
   870
	goto done;
sl@0
   871
    }
sl@0
   872
    maxSize++;
sl@0
   873
sl@0
   874
sl@0
   875
    Tcl_DStringInit(&buffer);
sl@0
   876
    Tcl_DStringSetLength(&buffer,
sl@0
   877
	    (int) ((regWinProcs->useWide) ? maxSize*2 : maxSize));
sl@0
   878
    index = 0;
sl@0
   879
    result = TCL_OK;
sl@0
   880
sl@0
   881
    if (patternObj) {
sl@0
   882
	pattern = Tcl_GetString(patternObj);
sl@0
   883
    } else {
sl@0
   884
	pattern = NULL;
sl@0
   885
    }
sl@0
   886
sl@0
   887
    /*
sl@0
   888
     * Enumerate the values under the given subkey until we get an error,
sl@0
   889
     * indicating the end of the list.  Note that we need to reset size
sl@0
   890
     * after each iteration because RegEnumValue smashes the old value.
sl@0
   891
     */
sl@0
   892
sl@0
   893
    size = maxSize;
sl@0
   894
    while ((*regWinProcs->regEnumValueProc)(key, index,
sl@0
   895
	    Tcl_DStringValue(&buffer), &size, NULL, NULL, NULL, NULL)
sl@0
   896
	    == ERROR_SUCCESS) {
sl@0
   897
sl@0
   898
	if (regWinProcs->useWide) {
sl@0
   899
	    size *= 2;
sl@0
   900
	}
sl@0
   901
sl@0
   902
	Tcl_WinTCharToUtf((TCHAR *) Tcl_DStringValue(&buffer), (int) size, &ds);
sl@0
   903
	name = Tcl_DStringValue(&ds);
sl@0
   904
	if (!pattern || Tcl_StringMatch(name, pattern)) {
sl@0
   905
	    result = Tcl_ListObjAppendElement(interp, resultPtr,
sl@0
   906
		    Tcl_NewStringObj(name, Tcl_DStringLength(&ds)));
sl@0
   907
	    if (result != TCL_OK) {
sl@0
   908
		Tcl_DStringFree(&ds);
sl@0
   909
		break;
sl@0
   910
	    }
sl@0
   911
	}
sl@0
   912
	Tcl_DStringFree(&ds);
sl@0
   913
sl@0
   914
	index++;
sl@0
   915
	size = maxSize;
sl@0
   916
    }
sl@0
   917
    Tcl_DStringFree(&buffer);
sl@0
   918
sl@0
   919
    done:
sl@0
   920
    RegCloseKey(key);
sl@0
   921
    return result;
sl@0
   922
}
sl@0
   923

sl@0
   924
/*
sl@0
   925
 *----------------------------------------------------------------------
sl@0
   926
 *
sl@0
   927
 * OpenKey --
sl@0
   928
 *
sl@0
   929
 *	This function opens the specified key.  This function is a
sl@0
   930
 *	simple wrapper around ParseKeyName and OpenSubKey.
sl@0
   931
 *
sl@0
   932
 * Results:
sl@0
   933
 *	Returns the opened key in the keyPtr argument and a Tcl
sl@0
   934
 *	result code.
sl@0
   935
 *
sl@0
   936
 * Side effects:
sl@0
   937
 *	None.
sl@0
   938
 *
sl@0
   939
 *----------------------------------------------------------------------
sl@0
   940
 */
sl@0
   941
sl@0
   942
static int
sl@0
   943
OpenKey(
sl@0
   944
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
   945
    Tcl_Obj *keyNameObj,	/* Key to open. */
sl@0
   946
    REGSAM mode,		/* Access mode. */
sl@0
   947
    int flags,			/* 0 or REG_CREATE. */
sl@0
   948
    HKEY *keyPtr)		/* Returned HKEY. */
sl@0
   949
{
sl@0
   950
    char *keyName, *buffer, *hostName;
sl@0
   951
    int length;
sl@0
   952
    HKEY rootKey;
sl@0
   953
    DWORD result;
sl@0
   954
sl@0
   955
    keyName = Tcl_GetStringFromObj(keyNameObj, &length);
sl@0
   956
    buffer = ckalloc((unsigned int) length + 1);
sl@0
   957
    strcpy(buffer, keyName);
sl@0
   958
sl@0
   959
    result = ParseKeyName(interp, buffer, &hostName, &rootKey, &keyName);
sl@0
   960
    if (result == TCL_OK) {
sl@0
   961
	result = OpenSubKey(hostName, rootKey, keyName, mode, flags, keyPtr);
sl@0
   962
	if (result != ERROR_SUCCESS) {
sl@0
   963
	    Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
sl@0
   964
	    Tcl_AppendToObj(resultPtr, "unable to open key: ", -1);
sl@0
   965
	    AppendSystemError(interp, result);
sl@0
   966
	    result = TCL_ERROR;
sl@0
   967
	} else {
sl@0
   968
	    result = TCL_OK;
sl@0
   969
	}
sl@0
   970
    }
sl@0
   971
sl@0
   972
    ckfree(buffer);
sl@0
   973
    return result;
sl@0
   974
}
sl@0
   975

sl@0
   976
/*
sl@0
   977
 *----------------------------------------------------------------------
sl@0
   978
 *
sl@0
   979
 * OpenSubKey --
sl@0
   980
 *
sl@0
   981
 *	This function opens a given subkey of a root key on the
sl@0
   982
 *	specified host.
sl@0
   983
 *
sl@0
   984
 * Results:
sl@0
   985
 *	Returns the opened key in the keyPtr and a Windows error code
sl@0
   986
 *	as the return value.
sl@0
   987
 *
sl@0
   988
 * Side effects:
sl@0
   989
 *	None.
sl@0
   990
 *
sl@0
   991
 *----------------------------------------------------------------------
sl@0
   992
 */
sl@0
   993
sl@0
   994
static DWORD
sl@0
   995
OpenSubKey(
sl@0
   996
    char *hostName,		/* Host to access, or NULL for local. */
sl@0
   997
    HKEY rootKey,		/* Root registry key. */
sl@0
   998
    char *keyName,		/* Subkey name. */
sl@0
   999
    REGSAM mode,		/* Access mode. */
sl@0
  1000
    int flags,			/* 0 or REG_CREATE. */
sl@0
  1001
    HKEY *keyPtr)		/* Returned HKEY. */
sl@0
  1002
{
sl@0
  1003
    DWORD result;
sl@0
  1004
    Tcl_DString buf;
sl@0
  1005
sl@0
  1006
    /*
sl@0
  1007
     * Attempt to open the root key on a remote host if necessary.
sl@0
  1008
     */
sl@0
  1009
sl@0
  1010
    if (hostName) {
sl@0
  1011
	hostName = (char *) Tcl_WinUtfToTChar(hostName, -1, &buf);
sl@0
  1012
	result = (*regWinProcs->regConnectRegistryProc)(hostName, rootKey,
sl@0
  1013
		&rootKey);
sl@0
  1014
	Tcl_DStringFree(&buf);
sl@0
  1015
	if (result != ERROR_SUCCESS) {
sl@0
  1016
	    return result;
sl@0
  1017
	}
sl@0
  1018
    }
sl@0
  1019
sl@0
  1020
    /*
sl@0
  1021
     * Now open the specified key with the requested permissions.  Note
sl@0
  1022
     * that this key must be closed by the caller.
sl@0
  1023
     */
sl@0
  1024
sl@0
  1025
    keyName = (char *) Tcl_WinUtfToTChar(keyName, -1, &buf);
sl@0
  1026
    if (flags & REG_CREATE) {
sl@0
  1027
	DWORD create;
sl@0
  1028
	result = (*regWinProcs->regCreateKeyExProc)(rootKey, keyName, 0, NULL,
sl@0
  1029
		REG_OPTION_NON_VOLATILE, mode, NULL, keyPtr, &create);
sl@0
  1030
    } else {
sl@0
  1031
	if (rootKey == HKEY_PERFORMANCE_DATA) {
sl@0
  1032
	    /*
sl@0
  1033
	     * Here we fudge it for this special root key.
sl@0
  1034
	     * See MSDN for more info on HKEY_PERFORMANCE_DATA and
sl@0
  1035
	     * the peculiarities surrounding it
sl@0
  1036
	     */
sl@0
  1037
	    *keyPtr = HKEY_PERFORMANCE_DATA;
sl@0
  1038
	    result = ERROR_SUCCESS;
sl@0
  1039
	} else {
sl@0
  1040
	    result = (*regWinProcs->regOpenKeyExProc)(rootKey, keyName, 0,
sl@0
  1041
		    mode, keyPtr);
sl@0
  1042
	}
sl@0
  1043
    }
sl@0
  1044
    Tcl_DStringFree(&buf);
sl@0
  1045
sl@0
  1046
    /*
sl@0
  1047
     * Be sure to close the root key since we are done with it now.
sl@0
  1048
     */
sl@0
  1049
sl@0
  1050
    if (hostName) {
sl@0
  1051
	RegCloseKey(rootKey);
sl@0
  1052
    }
sl@0
  1053
    return result;
sl@0
  1054
}
sl@0
  1055

sl@0
  1056
/*
sl@0
  1057
 *----------------------------------------------------------------------
sl@0
  1058
 *
sl@0
  1059
 * ParseKeyName --
sl@0
  1060
 *
sl@0
  1061
 *	This function parses a key name into the host, root, and subkey
sl@0
  1062
 *	parts.
sl@0
  1063
 *
sl@0
  1064
 * Results:
sl@0
  1065
 *	The pointers to the start of the host and subkey names are
sl@0
  1066
 *	returned in the hostNamePtr and keyNamePtr variables.  The
sl@0
  1067
 *	specified root HKEY is returned in rootKeyPtr.  Returns
sl@0
  1068
 *	a standard Tcl result.
sl@0
  1069
 *
sl@0
  1070
 *
sl@0
  1071
 * Side effects:
sl@0
  1072
 *	Modifies the name string by inserting nulls.
sl@0
  1073
 *
sl@0
  1074
 *----------------------------------------------------------------------
sl@0
  1075
 */
sl@0
  1076
sl@0
  1077
static int
sl@0
  1078
ParseKeyName(
sl@0
  1079
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
  1080
    char *name,
sl@0
  1081
    char **hostNamePtr,
sl@0
  1082
    HKEY *rootKeyPtr,
sl@0
  1083
    char **keyNamePtr)
sl@0
  1084
{
sl@0
  1085
    char *rootName;
sl@0
  1086
    int result, index;
sl@0
  1087
    Tcl_Obj *rootObj, *resultPtr = Tcl_GetObjResult(interp);
sl@0
  1088
sl@0
  1089
    /*
sl@0
  1090
     * Split the key into host and root portions.
sl@0
  1091
     */
sl@0
  1092
sl@0
  1093
    *hostNamePtr = *keyNamePtr = rootName = NULL;
sl@0
  1094
    if (name[0] == '\\') {
sl@0
  1095
	if (name[1] == '\\') {
sl@0
  1096
	    *hostNamePtr = name;
sl@0
  1097
	    for (rootName = name+2; *rootName != '\0'; rootName++) {
sl@0
  1098
		if (*rootName == '\\') {
sl@0
  1099
		    *rootName++ = '\0';
sl@0
  1100
		    break;
sl@0
  1101
		}
sl@0
  1102
	    }
sl@0
  1103
	}
sl@0
  1104
    } else {
sl@0
  1105
	rootName = name;
sl@0
  1106
    }
sl@0
  1107
    if (!rootName) {
sl@0
  1108
	Tcl_AppendStringsToObj(resultPtr, "bad key \"", name,
sl@0
  1109
		"\": must start with a valid root", NULL);
sl@0
  1110
	return TCL_ERROR;
sl@0
  1111
    }
sl@0
  1112
sl@0
  1113
    /*
sl@0
  1114
     * Split the root into root and subkey portions.
sl@0
  1115
     */
sl@0
  1116
sl@0
  1117
    for (*keyNamePtr = rootName; **keyNamePtr != '\0'; (*keyNamePtr)++) {
sl@0
  1118
	if (**keyNamePtr == '\\') {
sl@0
  1119
	    **keyNamePtr = '\0';
sl@0
  1120
	    (*keyNamePtr)++;
sl@0
  1121
	    break;
sl@0
  1122
	}
sl@0
  1123
    }
sl@0
  1124
sl@0
  1125
    /*
sl@0
  1126
     * Look for a matching root name.
sl@0
  1127
     */
sl@0
  1128
sl@0
  1129
    rootObj = Tcl_NewStringObj(rootName, -1);
sl@0
  1130
    result = Tcl_GetIndexFromObj(interp, rootObj, rootKeyNames, "root name",
sl@0
  1131
	    TCL_EXACT, &index);
sl@0
  1132
    Tcl_DecrRefCount(rootObj);
sl@0
  1133
    if (result != TCL_OK) {
sl@0
  1134
	return TCL_ERROR;
sl@0
  1135
    }
sl@0
  1136
    *rootKeyPtr = rootKeys[index];
sl@0
  1137
    return TCL_OK;
sl@0
  1138
}
sl@0
  1139

sl@0
  1140
/*
sl@0
  1141
 *----------------------------------------------------------------------
sl@0
  1142
 *
sl@0
  1143
 * RecursiveDeleteKey --
sl@0
  1144
 *
sl@0
  1145
 *	This function recursively deletes all the keys below a starting
sl@0
  1146
 *	key.  Although Windows 95 does this automatically, we still need
sl@0
  1147
 *	to do this for Windows NT.
sl@0
  1148
 *
sl@0
  1149
 * Results:
sl@0
  1150
 *	Returns a Windows error code.
sl@0
  1151
 *
sl@0
  1152
 * Side effects:
sl@0
  1153
 *	Deletes all of the keys and values below the given key.
sl@0
  1154
 *
sl@0
  1155
 *----------------------------------------------------------------------
sl@0
  1156
 */
sl@0
  1157
sl@0
  1158
static DWORD
sl@0
  1159
RecursiveDeleteKey(
sl@0
  1160
    HKEY startKey,		/* Parent of key to be deleted. */
sl@0
  1161
    CONST char *keyName)	/* Name of key to be deleted in external
sl@0
  1162
				 * encoding, not UTF. */
sl@0
  1163
{
sl@0
  1164
    DWORD result, size, maxSize;
sl@0
  1165
    Tcl_DString subkey;
sl@0
  1166
    HKEY hKey;
sl@0
  1167
sl@0
  1168
    /*
sl@0
  1169
     * Do not allow NULL or empty key name.
sl@0
  1170
     */
sl@0
  1171
sl@0
  1172
    if (!keyName || *keyName == '\0') {
sl@0
  1173
	return ERROR_BADKEY;
sl@0
  1174
    }
sl@0
  1175
sl@0
  1176
    result = (*regWinProcs->regOpenKeyExProc)(startKey, keyName, 0,
sl@0
  1177
	    KEY_ENUMERATE_SUB_KEYS | DELETE | KEY_QUERY_VALUE, &hKey);
sl@0
  1178
    if (result != ERROR_SUCCESS) {
sl@0
  1179
	return result;
sl@0
  1180
    }
sl@0
  1181
    result = (*regWinProcs->regQueryInfoKeyProc)(hKey, NULL, NULL, NULL, NULL,
sl@0
  1182
	    &maxSize, NULL, NULL, NULL, NULL, NULL, NULL);
sl@0
  1183
    maxSize++;
sl@0
  1184
    if (result != ERROR_SUCCESS) {
sl@0
  1185
	return result;
sl@0
  1186
    }
sl@0
  1187
sl@0
  1188
    Tcl_DStringInit(&subkey);
sl@0
  1189
    Tcl_DStringSetLength(&subkey,
sl@0
  1190
	    (int) ((regWinProcs->useWide) ? maxSize * 2 : maxSize));
sl@0
  1191
sl@0
  1192
    while (result == ERROR_SUCCESS) {
sl@0
  1193
	/*
sl@0
  1194
	 * Always get index 0 because key deletion changes ordering.
sl@0
  1195
	 */
sl@0
  1196
sl@0
  1197
	size = maxSize;
sl@0
  1198
	result=(*regWinProcs->regEnumKeyExProc)(hKey, 0,
sl@0
  1199
		Tcl_DStringValue(&subkey), &size, NULL, NULL, NULL, NULL);
sl@0
  1200
	if (result == ERROR_NO_MORE_ITEMS) {
sl@0
  1201
	    result = (*regWinProcs->regDeleteKeyProc)(startKey, keyName);
sl@0
  1202
	    break;
sl@0
  1203
	} else if (result == ERROR_SUCCESS) {
sl@0
  1204
	    result = RecursiveDeleteKey(hKey, Tcl_DStringValue(&subkey));
sl@0
  1205
	}
sl@0
  1206
    }
sl@0
  1207
    Tcl_DStringFree(&subkey);
sl@0
  1208
    RegCloseKey(hKey);
sl@0
  1209
    return result;
sl@0
  1210
}
sl@0
  1211

sl@0
  1212
/*
sl@0
  1213
 *----------------------------------------------------------------------
sl@0
  1214
 *
sl@0
  1215
 * SetValue --
sl@0
  1216
 *
sl@0
  1217
 *	This function sets the contents of a registry value.  If
sl@0
  1218
 *	the key or value does not exist, it will be created.  If it
sl@0
  1219
 *	does exist, then the data and type will be replaced.
sl@0
  1220
 *
sl@0
  1221
 * Results:
sl@0
  1222
 *	Returns a normal Tcl result.
sl@0
  1223
 *
sl@0
  1224
 * Side effects:
sl@0
  1225
 *	May create new keys or values.
sl@0
  1226
 *
sl@0
  1227
 *----------------------------------------------------------------------
sl@0
  1228
 */
sl@0
  1229
sl@0
  1230
static int
sl@0
  1231
SetValue(
sl@0
  1232
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
  1233
    Tcl_Obj *keyNameObj,	/* Name of key. */
sl@0
  1234
    Tcl_Obj *valueNameObj,	/* Name of value to set. */
sl@0
  1235
    Tcl_Obj *dataObj,		/* Data to be written. */
sl@0
  1236
    Tcl_Obj *typeObj)		/* Type of data to be written. */
sl@0
  1237
{
sl@0
  1238
    DWORD type, result;
sl@0
  1239
    HKEY key;
sl@0
  1240
    int length;
sl@0
  1241
    char *valueName;
sl@0
  1242
    Tcl_Obj *resultPtr;
sl@0
  1243
    Tcl_DString nameBuf;
sl@0
  1244
sl@0
  1245
    if (typeObj == NULL) {
sl@0
  1246
	type = REG_SZ;
sl@0
  1247
    } else if (Tcl_GetIndexFromObj(interp, typeObj, typeNames, "type",
sl@0
  1248
	    0, (int *) &type) != TCL_OK) {
sl@0
  1249
	if (Tcl_GetIntFromObj(NULL, typeObj, (int*) &type) != TCL_OK) {
sl@0
  1250
	    return TCL_ERROR;
sl@0
  1251
	}
sl@0
  1252
	Tcl_ResetResult(interp);
sl@0
  1253
    }
sl@0
  1254
    if (OpenKey(interp, keyNameObj, KEY_ALL_ACCESS, 1, &key) != TCL_OK) {
sl@0
  1255
	return TCL_ERROR;
sl@0
  1256
    }
sl@0
  1257
sl@0
  1258
    valueName = Tcl_GetStringFromObj(valueNameObj, &length);
sl@0
  1259
    valueName = (char *) Tcl_WinUtfToTChar(valueName, length, &nameBuf);
sl@0
  1260
    resultPtr = Tcl_GetObjResult(interp);
sl@0
  1261
sl@0
  1262
    if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
sl@0
  1263
	DWORD value;
sl@0
  1264
	if (Tcl_GetIntFromObj(interp, dataObj, (int*) &value) != TCL_OK) {
sl@0
  1265
	    RegCloseKey(key);
sl@0
  1266
	    Tcl_DStringFree(&nameBuf);
sl@0
  1267
	    return TCL_ERROR;
sl@0
  1268
	}
sl@0
  1269
sl@0
  1270
	value = ConvertDWORD(type, value);
sl@0
  1271
	result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
sl@0
  1272
		(BYTE*) &value, sizeof(DWORD));
sl@0
  1273
    } else if (type == REG_MULTI_SZ) {
sl@0
  1274
	Tcl_DString data, buf;
sl@0
  1275
	int objc, i;
sl@0
  1276
	Tcl_Obj **objv;
sl@0
  1277
sl@0
  1278
	if (Tcl_ListObjGetElements(interp, dataObj, &objc, &objv) != TCL_OK) {
sl@0
  1279
	    RegCloseKey(key);
sl@0
  1280
	    Tcl_DStringFree(&nameBuf);
sl@0
  1281
	    return TCL_ERROR;
sl@0
  1282
	}
sl@0
  1283
sl@0
  1284
	/*
sl@0
  1285
	 * Append the elements as null terminated strings.  Note that
sl@0
  1286
	 * we must not assume the length of the string in case there are
sl@0
  1287
	 * embedded nulls, which aren't allowed in REG_MULTI_SZ values.
sl@0
  1288
	 */
sl@0
  1289
sl@0
  1290
	Tcl_DStringInit(&data);
sl@0
  1291
	for (i = 0; i < objc; i++) {
sl@0
  1292
	    Tcl_DStringAppend(&data, Tcl_GetString(objv[i]), -1);
sl@0
  1293
sl@0
  1294
	    /*
sl@0
  1295
	     * Add a null character to separate this value from the next.
sl@0
  1296
	     * We accomplish this by growing the string by one byte.  Since the
sl@0
  1297
	     * DString always tacks on an extra null byte, the new byte will
sl@0
  1298
	     * already be set to null.
sl@0
  1299
	     */
sl@0
  1300
sl@0
  1301
	    Tcl_DStringSetLength(&data, Tcl_DStringLength(&data)+1);
sl@0
  1302
	}
sl@0
  1303
sl@0
  1304
	Tcl_WinUtfToTChar(Tcl_DStringValue(&data), Tcl_DStringLength(&data)+1,
sl@0
  1305
		&buf);
sl@0
  1306
	result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
sl@0
  1307
		(BYTE *) Tcl_DStringValue(&buf),
sl@0
  1308
		(DWORD) Tcl_DStringLength(&buf));
sl@0
  1309
	Tcl_DStringFree(&data);
sl@0
  1310
	Tcl_DStringFree(&buf);
sl@0
  1311
    } else if (type == REG_SZ || type == REG_EXPAND_SZ) {
sl@0
  1312
	Tcl_DString buf;
sl@0
  1313
	char *data = Tcl_GetStringFromObj(dataObj, &length);
sl@0
  1314
sl@0
  1315
	data = (char *) Tcl_WinUtfToTChar(data, length, &buf);
sl@0
  1316
sl@0
  1317
	/*
sl@0
  1318
	 * Include the null in the length, padding if needed for Unicode.
sl@0
  1319
	 */
sl@0
  1320
sl@0
  1321
	if (regWinProcs->useWide) {
sl@0
  1322
	    Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf)+1);
sl@0
  1323
	}
sl@0
  1324
	length = Tcl_DStringLength(&buf) + 1;
sl@0
  1325
sl@0
  1326
	result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
sl@0
  1327
		(BYTE*)data, (DWORD) length);
sl@0
  1328
	Tcl_DStringFree(&buf);
sl@0
  1329
    } else {
sl@0
  1330
	char *data;
sl@0
  1331
sl@0
  1332
	/*
sl@0
  1333
	 * Store binary data in the registry.
sl@0
  1334
	 */
sl@0
  1335
sl@0
  1336
	data = Tcl_GetByteArrayFromObj(dataObj, &length);
sl@0
  1337
	result = (*regWinProcs->regSetValueExProc)(key, valueName, 0, type,
sl@0
  1338
		(BYTE *)data, (DWORD) length);
sl@0
  1339
    }
sl@0
  1340
    Tcl_DStringFree(&nameBuf);
sl@0
  1341
    RegCloseKey(key);
sl@0
  1342
    if (result != ERROR_SUCCESS) {
sl@0
  1343
	Tcl_AppendToObj(resultPtr, "unable to set value: ", -1);
sl@0
  1344
	AppendSystemError(interp, result);
sl@0
  1345
	return TCL_ERROR;
sl@0
  1346
    }
sl@0
  1347
    return TCL_OK;
sl@0
  1348
}
sl@0
  1349

sl@0
  1350
/*
sl@0
  1351
 *----------------------------------------------------------------------
sl@0
  1352
 *
sl@0
  1353
 * BroadcastValue --
sl@0
  1354
 *
sl@0
  1355
 *	This function broadcasts a WM_SETTINGCHANGE message to indicate
sl@0
  1356
 *	to other programs that we have changed the contents of a registry
sl@0
  1357
 *	value.
sl@0
  1358
 *
sl@0
  1359
 * Results:
sl@0
  1360
 *	Returns a normal Tcl result.
sl@0
  1361
 *
sl@0
  1362
 * Side effects:
sl@0
  1363
 *	Will cause other programs to reload their system settings.
sl@0
  1364
 *
sl@0
  1365
 *----------------------------------------------------------------------
sl@0
  1366
 */
sl@0
  1367
sl@0
  1368
static int
sl@0
  1369
BroadcastValue(
sl@0
  1370
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
  1371
    int objc,			/* Number of arguments. */
sl@0
  1372
    Tcl_Obj * CONST objv[])	/* Argument values. */
sl@0
  1373
{
sl@0
  1374
    LRESULT result, sendResult;
sl@0
  1375
    UINT timeout = 3000;
sl@0
  1376
    int len;
sl@0
  1377
    char *str;
sl@0
  1378
    Tcl_Obj *objPtr;
sl@0
  1379
sl@0
  1380
    if ((objc != 3) && (objc != 5)) {
sl@0
  1381
	Tcl_WrongNumArgs(interp, 2, objv, "keyName ?-timeout millisecs?");
sl@0
  1382
	return TCL_ERROR;
sl@0
  1383
    }
sl@0
  1384
sl@0
  1385
    if (objc > 3) {
sl@0
  1386
	str = Tcl_GetStringFromObj(objv[3], &len);
sl@0
  1387
	if ((len < 2) || (*str != '-') || strncmp(str, "-timeout", (size_t) len)) {
sl@0
  1388
	    Tcl_WrongNumArgs(interp, 2, objv, "keyName ?-timeout millisecs?");
sl@0
  1389
	    return TCL_ERROR;
sl@0
  1390
	}
sl@0
  1391
	if (Tcl_GetIntFromObj(interp, objv[4], (int *) &timeout) != TCL_OK) {
sl@0
  1392
	    return TCL_ERROR;
sl@0
  1393
	}
sl@0
  1394
    }
sl@0
  1395
sl@0
  1396
    str = Tcl_GetStringFromObj(objv[2], &len);
sl@0
  1397
    if (len == 0) {
sl@0
  1398
	str = NULL;
sl@0
  1399
    }
sl@0
  1400
sl@0
  1401
    /*
sl@0
  1402
     * Use the ignore the result.
sl@0
  1403
     */
sl@0
  1404
    result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE,
sl@0
  1405
	    (WPARAM) 0, (LPARAM) str, SMTO_ABORTIFHUNG, timeout, &sendResult);
sl@0
  1406
sl@0
  1407
    objPtr = Tcl_NewObj();
sl@0
  1408
    Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewLongObj((long) result));
sl@0
  1409
    Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewLongObj((long) sendResult));
sl@0
  1410
    Tcl_SetObjResult(interp, objPtr);
sl@0
  1411
sl@0
  1412
    return TCL_OK;
sl@0
  1413
}
sl@0
  1414

sl@0
  1415
/*
sl@0
  1416
 *----------------------------------------------------------------------
sl@0
  1417
 *
sl@0
  1418
 * AppendSystemError --
sl@0
  1419
 *
sl@0
  1420
 *	This routine formats a Windows system error message and places
sl@0
  1421
 *	it into the interpreter result.
sl@0
  1422
 *
sl@0
  1423
 * Results:
sl@0
  1424
 *	None.
sl@0
  1425
 *
sl@0
  1426
 * Side effects:
sl@0
  1427
 *	None.
sl@0
  1428
 *
sl@0
  1429
 *----------------------------------------------------------------------
sl@0
  1430
 */
sl@0
  1431
sl@0
  1432
static void
sl@0
  1433
AppendSystemError(
sl@0
  1434
    Tcl_Interp *interp,		/* Current interpreter. */
sl@0
  1435
    DWORD error)		/* Result code from error. */
sl@0
  1436
{
sl@0
  1437
    int length;
sl@0
  1438
    WCHAR *wMsgPtr;
sl@0
  1439
    char *msg;
sl@0
  1440
    char id[TCL_INTEGER_SPACE], msgBuf[24 + TCL_INTEGER_SPACE];
sl@0
  1441
    Tcl_DString ds;
sl@0
  1442
    Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
sl@0
  1443
sl@0
  1444
    length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
sl@0
  1445
	    | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error,
sl@0
  1446
	    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (WCHAR *) &wMsgPtr,
sl@0
  1447
	    0, NULL);
sl@0
  1448
    if (length == 0) {
sl@0
  1449
	char *msgPtr;
sl@0
  1450
sl@0
  1451
	length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
sl@0
  1452
		| FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error,
sl@0
  1453
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &msgPtr,
sl@0
  1454
		0, NULL);
sl@0
  1455
	if (length > 0) {
sl@0
  1456
	    wMsgPtr = (WCHAR *) LocalAlloc(LPTR, (length + 1) * sizeof(WCHAR));
sl@0
  1457
	    MultiByteToWideChar(CP_ACP, 0, msgPtr, length + 1, wMsgPtr,
sl@0
  1458
		    length + 1);
sl@0
  1459
	    LocalFree(msgPtr);
sl@0
  1460
	}
sl@0
  1461
    }
sl@0
  1462
    if (length == 0) {
sl@0
  1463
	if (error == ERROR_CALL_NOT_IMPLEMENTED) {
sl@0
  1464
	    msg = "function not supported under Win32s";
sl@0
  1465
	} else {
sl@0
  1466
	    sprintf(msgBuf, "unknown error: %ld", error);
sl@0
  1467
	    msg = msgBuf;
sl@0
  1468
	}
sl@0
  1469
    } else {
sl@0
  1470
	Tcl_Encoding encoding;
sl@0
  1471
sl@0
  1472
	encoding = Tcl_GetEncoding(NULL, "unicode");
sl@0
  1473
	Tcl_ExternalToUtfDString(encoding, (char *) wMsgPtr, -1, &ds);
sl@0
  1474
	Tcl_FreeEncoding(encoding);
sl@0
  1475
	LocalFree(wMsgPtr);
sl@0
  1476
sl@0
  1477
	msg = Tcl_DStringValue(&ds);
sl@0
  1478
	length = Tcl_DStringLength(&ds);
sl@0
  1479
sl@0
  1480
	/*
sl@0
  1481
	 * Trim the trailing CR/LF from the system message.
sl@0
  1482
	 */
sl@0
  1483
	if (msg[length-1] == '\n') {
sl@0
  1484
	    msg[--length] = 0;
sl@0
  1485
	}
sl@0
  1486
	if (msg[length-1] == '\r') {
sl@0
  1487
	    msg[--length] = 0;
sl@0
  1488
	}
sl@0
  1489
    }
sl@0
  1490
sl@0
  1491
    sprintf(id, "%ld", error);
sl@0
  1492
    Tcl_SetErrorCode(interp, "WINDOWS", id, msg, (char *) NULL);
sl@0
  1493
    Tcl_AppendToObj(resultPtr, msg, length);
sl@0
  1494
sl@0
  1495
    if (length != 0) {
sl@0
  1496
	Tcl_DStringFree(&ds);
sl@0
  1497
    }
sl@0
  1498
}
sl@0
  1499

sl@0
  1500
/*
sl@0
  1501
 *----------------------------------------------------------------------
sl@0
  1502
 *
sl@0
  1503
 * ConvertDWORD --
sl@0
  1504
 *
sl@0
  1505
 *	This function determines whether a DWORD needs to be byte
sl@0
  1506
 *	swapped, and returns the appropriately swapped value.
sl@0
  1507
 *
sl@0
  1508
 * Results:
sl@0
  1509
 *	Returns a converted DWORD.
sl@0
  1510
 *
sl@0
  1511
 * Side effects:
sl@0
  1512
 *	None.
sl@0
  1513
 *
sl@0
  1514
 *----------------------------------------------------------------------
sl@0
  1515
 */
sl@0
  1516
sl@0
  1517
static DWORD
sl@0
  1518
ConvertDWORD(
sl@0
  1519
    DWORD type,			/* Either REG_DWORD or REG_DWORD_BIG_ENDIAN */
sl@0
  1520
    DWORD value)		/* The value to be converted. */
sl@0
  1521
{
sl@0
  1522
    DWORD order = 1;
sl@0
  1523
    DWORD localType;
sl@0
  1524
sl@0
  1525
    /*
sl@0
  1526
     * Check to see if the low bit is in the first byte.
sl@0
  1527
     */
sl@0
  1528
sl@0
  1529
    localType = (*((char*)(&order)) == 1) ? REG_DWORD : REG_DWORD_BIG_ENDIAN;
sl@0
  1530
    return (type != localType) ? SWAPLONG(value) : value;
sl@0
  1531
}