| /* | 
 |  * 	Registry Functions | 
 |  * | 
 |  * Copyright 1996 Marcus Meissner | 
 |  * Copyright 1998 Matthew Becker | 
 |  * | 
 |  * December 21, 1997 - Kevin Cozens | 
 |  * Fixed bugs in the _w95_loadreg() function. Added extra information | 
 |  * regarding the format of the Windows '95 registry files. | 
 |  * | 
 |  * NOTES | 
 |  *    When changing this file, please re-run the regtest program to ensure | 
 |  *    the conditions are handled properly. | 
 |  * | 
 |  * TODO | 
 |  *    Security access | 
 |  *    Option handling | 
 |  *    Time for RegEnumKey*, RegQueryInfoKey* | 
 |  */ | 
 |  | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 | #include <ctype.h> | 
 | #include <errno.h> | 
 | #include <sys/errno.h> | 
 | #include <sys/types.h> | 
 | #include <sys/fcntl.h> | 
 | #include <sys/stat.h> | 
 | #include <pwd.h> | 
 | #include <assert.h> | 
 | #include <time.h> | 
 | #include "windows.h" | 
 | #include "win.h" | 
 | #include "winerror.h" | 
 | #include "file.h" | 
 | #include "heap.h" | 
 | #include "debug.h" | 
 | #include "xmalloc.h" | 
 | #include "winreg.h" | 
 | #include "winversion.h" | 
 |  | 
 | static void REGISTRY_Init(void); | 
 | /* FIXME: following defines should be configured global ... */ | 
 |  | 
 | /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */ | 
 | #define WINE_PREFIX			"/.wine" | 
 | #define SAVE_USERS_DEFAULT		"/usr/local/etc/wine.userreg" | 
 | #define SAVE_LOCAL_MACHINE_DEFAULT	"/usr/local/etc/wine.systemreg" | 
 |  | 
 | /* relative in ~user/.wine/ : */ | 
 | #define SAVE_CURRENT_USER		"user.reg" | 
 | #define SAVE_LOCAL_MACHINE		"system.reg" | 
 |  | 
 | #define KEY_REGISTRY	"Software\\The WINE team\\WINE\\Registry" | 
 | #define VAL_SAVEUPDATED	"SaveOnlyUpdatedKeys" | 
 |  | 
 | /* one value of a key */ | 
 | typedef struct tagKEYVALUE | 
 | { | 
 |     LPWSTR   name;          /* name of value (UNICODE) or NULL for win31 */ | 
 |     DWORD    type;          /* type of value */ | 
 |     DWORD    len;           /* length of data in BYTEs */ | 
 |     DWORD    lastmodified;  /* time of seconds since 1.1.1970 */ | 
 |     LPBYTE   data;          /* content, may be strings, binaries, etc. */ | 
 | } KEYVALUE,*LPKEYVALUE; | 
 |  | 
 | /* a registry key */ | 
 | typedef struct tagKEYSTRUCT | 
 | { | 
 |     LPWSTR               keyname;       /* name of THIS key (UNICODE) */ | 
 |     DWORD                flags;         /* flags. */ | 
 |     LPWSTR               class; | 
 |     /* values */ | 
 |     DWORD                nrofvalues;    /* nr of values in THIS key */ | 
 |     LPKEYVALUE           values;        /* values in THIS key */ | 
 |     /* key management pointers */ | 
 |     struct tagKEYSTRUCT	*next;          /* next key on same hierarchy */ | 
 |     struct tagKEYSTRUCT	*nextsub;       /* keys that hang below THIS key */ | 
 | } KEYSTRUCT, *LPKEYSTRUCT; | 
 |  | 
 |  | 
 | static KEYSTRUCT	*key_classes_root=NULL;	/* windows 3.1 global values */ | 
 | static KEYSTRUCT	*key_current_user=NULL;	/* user specific values */ | 
 | static KEYSTRUCT	*key_local_machine=NULL;/* machine specific values */ | 
 | static KEYSTRUCT	*key_users=NULL;	/* all users? */ | 
 |  | 
 | /* dynamic, not saved */ | 
 | static KEYSTRUCT	*key_performance_data=NULL; | 
 | static KEYSTRUCT	*key_current_config=NULL; | 
 | static KEYSTRUCT	*key_dyn_data=NULL; | 
 |  | 
 | /* what valuetypes do we need to convert? */ | 
 | #define UNICONVMASK	((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ)) | 
 |  | 
 |  | 
 | static struct openhandle { | 
 | 	LPKEYSTRUCT	lpkey; | 
 | 	HKEY		hkey; | 
 | 	REGSAM		accessmask; | 
 | }  *openhandles=NULL; | 
 | static int	nrofopenhandles=0; | 
 | /* Starts after 1 because 0,1 are reserved for Win16 */ | 
 | /* Note: Should always be even, as Win95 ADVAPI32.DLL reserves odd | 
 |          HKEYs for remote registry access */ | 
 | static int	currenthandle=2; | 
 |  | 
 |  | 
 | /* | 
 |  * QUESTION | 
 |  *   Are these doing the same as HEAP_strdupAtoW and HEAP_strdupWtoA? | 
 |  *   If so, can we remove them? | 
 |  * ANSWER | 
 |  *   No, the memory handling functions are called very often in here,  | 
 |  *   just replacing them by HeapAlloc(SystemHeap,...) makes registry | 
 |  *   loading 100 times slower. -MM | 
 |  */ | 
 | static LPWSTR strdupA2W(LPCSTR src) | 
 | { | 
 |     if(src) { | 
 | 	LPWSTR dest=xmalloc(2*strlen(src)+2); | 
 | 	lstrcpyAtoW(dest,src); | 
 | 	return dest; | 
 |     } | 
 |     return NULL; | 
 | } | 
 |  | 
 | static LPWSTR strdupW(LPCWSTR a) { | 
 | 	LPWSTR	b; | 
 | 	int	len; | 
 |  | 
 |     if(a) { | 
 | 	len=sizeof(WCHAR)*(lstrlen32W(a)+1); | 
 | 	b=(LPWSTR)xmalloc(len); | 
 | 	memcpy(b,a,len); | 
 | 	return b; | 
 |     } | 
 |     return NULL; | 
 | } | 
 |  | 
 | LPWSTR strcvtA2W(LPCSTR src, int nchars) | 
 |  | 
 | { | 
 |    LPWSTR dest = xmalloc (2 * nchars + 2); | 
 |  | 
 |    lstrcpynAtoW(dest,src,nchars+1); | 
 |    dest[nchars] = 0; | 
 |    return dest; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * is_standard_hkey [Internal] | 
 |  * Determines if a hkey is a standard key | 
 |  */ | 
 | static BOOL32 is_standard_hkey( HKEY hkey ) | 
 | { | 
 |     switch(hkey) { | 
 |         case 0x00000000: | 
 |         case 0x00000001: | 
 |         case HKEY_CLASSES_ROOT: | 
 |         case HKEY_CURRENT_CONFIG: | 
 |         case HKEY_CURRENT_USER: | 
 |         case HKEY_LOCAL_MACHINE: | 
 |         case HKEY_USERS: | 
 |         case HKEY_PERFORMANCE_DATA: | 
 |         case HKEY_DYN_DATA: | 
 |             return TRUE; | 
 |         default: | 
 |             return FALSE; | 
 |     } | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * add_handle [Internal] | 
 |  */ | 
 | static void add_handle( HKEY hkey, LPKEYSTRUCT lpkey, REGSAM accessmask ) | 
 | { | 
 |     int i; | 
 |  | 
 |     TRACE(reg,"(0x%x,%p,0x%lx)\n",hkey,lpkey,accessmask); | 
 |     /* Check for duplicates */ | 
 |     for (i=0;i<nrofopenhandles;i++) { | 
 |         if (openhandles[i].lpkey==lpkey) { | 
 |             /* This is not really an error - the user is allowed to create | 
 |                two (or more) handles to the same key */ | 
 |             /*WARN(reg, "Adding key %p twice\n",lpkey);*/ | 
 |         } | 
 |         if (openhandles[i].hkey==hkey) { | 
 |             WARN(reg, "Adding handle %x twice\n",hkey); | 
 |         } | 
 |     } | 
 |     openhandles=xrealloc( openhandles,  | 
 |                           sizeof(struct openhandle)*(nrofopenhandles+1)); | 
 |  | 
 |     openhandles[i].lpkey = lpkey; | 
 |     openhandles[i].hkey = hkey; | 
 |     openhandles[i].accessmask = accessmask; | 
 |     nrofopenhandles++; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * get_handle [Internal] | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: Pointer to key | 
 |  *    Failure: NULL | 
 |  */ | 
 | static LPKEYSTRUCT get_handle( HKEY hkey ) | 
 | { | 
 |     int i; | 
 |  | 
 |     for (i=0; i<nrofopenhandles; i++) | 
 |         if (openhandles[i].hkey == hkey) | 
 |             return openhandles[i].lpkey; | 
 |     WARN(reg, "Could not find handle 0x%x\n",hkey); | 
 |     return NULL; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * remove_handle [Internal] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey [I] Handle of key to remove | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: ERROR_INVALID_HANDLE | 
 |  */ | 
 | static DWORD remove_handle( HKEY hkey ) | 
 | { | 
 |     int i; | 
 |  | 
 |     for (i=0;i<nrofopenhandles;i++) | 
 |         if (openhandles[i].hkey==hkey) | 
 |             break; | 
 |  | 
 |     if (i == nrofopenhandles) { | 
 |         WARN(reg, "Could not find handle 0x%x\n",hkey); | 
 |         return ERROR_INVALID_HANDLE; | 
 |     } | 
 |  | 
 |     memcpy( openhandles+i, | 
 |             openhandles+i+1, | 
 |             sizeof(struct openhandle)*(nrofopenhandles-i-1) | 
 |     ); | 
 |     openhandles=xrealloc(openhandles,sizeof(struct openhandle)*(nrofopenhandles-1)); | 
 |     nrofopenhandles--; | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * lookup_hkey [Internal] | 
 |  *  | 
 |  * Just as the name says. Creates the root keys on demand, so we can call the | 
 |  * Reg* functions at any time. | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: Pointer to key structure | 
 |  *    Failure: NULL | 
 |  */ | 
 | #define ADD_ROOT_KEY(xx) \ | 
 | 	xx = (LPKEYSTRUCT)xmalloc(sizeof(KEYSTRUCT));\ | 
 | 	memset(xx,'\0',sizeof(KEYSTRUCT));\ | 
 | 	xx->keyname= strdupA2W("<should_not_appear_anywhere>"); | 
 |  | 
 | static LPKEYSTRUCT lookup_hkey( HKEY hkey ) | 
 | { | 
 | 	switch (hkey) { | 
 | 	/* 0 and 1 are valid rootkeys in win16 shell.dll and are used by | 
 | 	 * some programs. Do not remove those cases. -MM | 
 | 	 */ | 
 |     	case 0x00000000: | 
 | 	case 0x00000001: | 
 | 	case HKEY_CLASSES_ROOT: { | 
 | 		if (!key_classes_root) { | 
 | 			HKEY	cl_r_hkey; | 
 |  | 
 | 			/* calls lookup_hkey recursively, TWICE */ | 
 | 			if (RegCreateKey16(HKEY_LOCAL_MACHINE,"SOFTWARE\\Classes",&cl_r_hkey)!=ERROR_SUCCESS) { | 
 | 				ERR(reg,"Could not create HKLM\\SOFTWARE\\Classes. This is impossible.\n"); | 
 | 				exit(1); | 
 | 			} | 
 | 			key_classes_root = lookup_hkey(cl_r_hkey); | 
 | 		} | 
 | 		return key_classes_root; | 
 | 	} | 
 | 	case HKEY_CURRENT_USER: | 
 | 		if (!key_current_user) { | 
 | 			HKEY	c_u_hkey; | 
 | 			struct	passwd	*pwd; | 
 |  | 
 | 			pwd=getpwuid(getuid()); | 
 | 			/* calls lookup_hkey recursively, TWICE */ | 
 | 			if (pwd && pwd->pw_name) { | 
 | 				if (RegCreateKey16(HKEY_USERS,pwd->pw_name,&c_u_hkey)!=ERROR_SUCCESS) { | 
 | 					ERR(reg,"Could not create HU\\%s. This is impossible.\n",pwd->pw_name); | 
 | 					exit(1); | 
 | 				} | 
 | 				key_current_user = lookup_hkey(c_u_hkey); | 
 | 			} else { | 
 | 				/* nothing found, use standalone */ | 
 | 				ADD_ROOT_KEY(key_current_user); | 
 | 			} | 
 | 		} | 
 | 		return key_current_user; | 
 | 	case HKEY_LOCAL_MACHINE: | 
 | 		if (!key_local_machine) { | 
 | 			ADD_ROOT_KEY(key_local_machine); | 
 | 			REGISTRY_Init(); | 
 | 		} | 
 | 		return key_local_machine; | 
 | 	case HKEY_USERS: | 
 | 		if (!key_users) { | 
 | 			ADD_ROOT_KEY(key_users); | 
 | 		} | 
 | 		return key_users; | 
 | 	case HKEY_PERFORMANCE_DATA: | 
 | 		if (!key_performance_data) { | 
 | 			ADD_ROOT_KEY(key_performance_data); | 
 | 		} | 
 | 		return key_performance_data; | 
 | 	case HKEY_DYN_DATA: | 
 | 		if (!key_dyn_data) { | 
 | 			ADD_ROOT_KEY(key_dyn_data); | 
 | 		} | 
 | 		return key_dyn_data; | 
 | 	case HKEY_CURRENT_CONFIG: | 
 | 		if (!key_current_config) { | 
 | 			ADD_ROOT_KEY(key_current_config); | 
 | 		} | 
 | 		return key_current_config; | 
 | 	default: | 
 | 		return get_handle(hkey); | 
 | 	} | 
 | 	/*NOTREACHED*/ | 
 | } | 
 | #undef ADD_ROOT_KEY | 
 | /* so we don't accidently access them ... */ | 
 | #define key_current_config NULL NULL | 
 | #define key_current_user NULL NULL | 
 | #define key_users NULL NULL | 
 | #define key_local_machine NULL NULL | 
 | #define key_classes_root NULL NULL | 
 | #define key_dyn_data NULL NULL | 
 | #define key_performance_data NULL NULL | 
 |  | 
 | /****************************************************************************** | 
 |  * split_keypath [Internal] | 
 |  * splits the unicode string 'wp' into an array of strings. | 
 |  * the array is allocated by this function.  | 
 |  * Free the array using FREE_KEY_PATH | 
 |  * | 
 |  * PARAMS | 
 |  *    wp  [I] String to split up | 
 |  *    wpv [O] Array of pointers to strings | 
 |  *    wpc [O] Number of components | 
 |  */ | 
 | static void split_keypath( LPCWSTR wp, LPWSTR **wpv, int *wpc) | 
 | { | 
 |     int	i,j,len; | 
 |     LPWSTR ws; | 
 |  | 
 |     TRACE(reg,"(%s,%p,%p)\n",debugstr_w(wp),wpv,wpc); | 
 |  | 
 |     ws	= HEAP_strdupW( SystemHeap, 0, wp ); | 
 |  | 
 |     /* We know we have at least one substring */ | 
 |     *wpc = 1; | 
 |  | 
 |     /* Replace each backslash with NULL, and increment the count */ | 
 |     for (i=0;ws[i];i++) { | 
 |         if (ws[i]=='\\') { | 
 |             ws[i]=0; | 
 |             (*wpc)++; | 
 |         } | 
 |     } | 
 |  | 
 |     len = i; | 
 |  | 
 |     /* Allocate the space for the array of pointers, leaving room for the | 
 |        NULL at the end */ | 
 |     *wpv = (LPWSTR*)HeapAlloc( SystemHeap, 0, sizeof(LPWSTR)*(*wpc+2)); | 
 |     (*wpv)[0]= ws; | 
 |  | 
 |     /* Assign each pointer to the appropriate character in the string */ | 
 |     j = 1; | 
 |     for (i=1;i<len;i++) | 
 |         if (ws[i-1]==0) { | 
 |             (*wpv)[j++]=ws+i; | 
 |             /* TRACE(reg, " Subitem %d = %s\n",j-1,debugstr_w((*wpv)[j-1])); */ | 
 |         } | 
 |  | 
 |     (*wpv)[j]=NULL; | 
 | } | 
 | #define FREE_KEY_PATH HeapFree(SystemHeap,0,wps[0]);HeapFree(SystemHeap,0,wps); | 
 |  | 
 |  | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * REGISTRY_Init [Internal] | 
 |  * Registry initialisation, allocates some default keys.  | 
 |  */ | 
 | static void REGISTRY_Init(void) { | 
 | 	HKEY	hkey; | 
 | 	char	buf[200]; | 
 |  | 
 | 	TRACE(reg,"(void)\n"); | 
 |  | 
 | 	RegCreateKey16(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey); | 
 | 	RegCloseKey(hkey); | 
 |  | 
 |         /* This was an Open, but since it is called before the real registries | 
 |            are loaded, it was changed to a Create - MTB 980507*/ | 
 | 	RegCreateKey16(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey); | 
 | 	RegSetValueEx32A(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE")); | 
 | 	RegCloseKey(hkey); | 
 |  | 
 | 	/* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion | 
 | 	 *						CurrentVersion | 
 | 	 *						CurrentBuildNumber | 
 | 	 *						CurrentType | 
 | 	 *					string	RegisteredOwner | 
 | 	 *					string	RegisteredOrganization | 
 | 	 * | 
 | 	 */ | 
 | 	/* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent | 
 | 	 * 					string	SysContact | 
 | 	 * 					string	SysLocation | 
 | 	 * 						SysServices | 
 | 	 */						 | 
 | 	if (-1!=gethostname(buf,200)) { | 
 | 		RegCreateKey16(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey); | 
 | 		RegSetValueEx16(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1); | 
 | 		RegCloseKey(hkey); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /************************ SAVE Registry Function ****************************/ | 
 |  | 
 | #define REGISTRY_SAVE_VERSION	0x00000001 | 
 |  | 
 | /* Registry saveformat: | 
 |  * If you change it, increase above number by 1, which will flush | 
 |  * old registry database files. | 
 |  *  | 
 |  * Global: | 
 |  * 	"WINE REGISTRY Version %d" | 
 |  * 	subkeys.... | 
 |  * Subkeys: | 
 |  * 	keyname | 
 |  *		valuename=lastmodified,type,data | 
 |  *		... | 
 |  *		subkeys | 
 |  *	... | 
 |  * keyname,valuename,stringdata: | 
 |  *	the usual ascii characters from 0x00-0xff (well, not 0x00) | 
 |  *	and \uXXXX as UNICODE value XXXX with XXXX>0xff | 
 |  *	( "=\\\t" escaped in \uXXXX form.) | 
 |  * type,lastmodified:  | 
 |  *	int | 
 |  *  | 
 |  * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags. | 
 |  * | 
 |  * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry] | 
 |  * SaveOnlyUpdatedKeys=yes | 
 |  */ | 
 |  | 
 | /****************************************************************************** | 
 |  * _save_check_tainted [Internal] | 
 |  */ | 
 | static int _save_check_tainted( LPKEYSTRUCT lpkey ) | 
 | { | 
 | 	int		tainted; | 
 |  | 
 | 	if (!lpkey) | 
 | 		return 0; | 
 | 	if (lpkey->flags & REG_OPTION_TAINTED) | 
 | 		tainted = 1; | 
 | 	else | 
 | 		tainted = 0; | 
 | 	while (lpkey) { | 
 | 		if (_save_check_tainted(lpkey->nextsub)) { | 
 | 			lpkey->flags |= REG_OPTION_TAINTED; | 
 | 			tainted = 1; | 
 | 		} | 
 | 		lpkey	= lpkey->next; | 
 | 	} | 
 | 	return tainted; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * _save_USTRING [Internal] | 
 |  */ | 
 | static void _save_USTRING( FILE *F, LPWSTR wstr, int escapeeq ) | 
 | { | 
 | 	LPWSTR	s; | 
 | 	int	doescape; | 
 |  | 
 | 	if (wstr==NULL) | 
 | 		return; | 
 | 	s=wstr; | 
 | 	while (*s) { | 
 | 		doescape=0; | 
 | 		if (*s>0xff) | 
 | 			doescape = 1; | 
 | 		if (*s=='\n') | 
 | 			doescape = 1; | 
 | 		if (escapeeq && *s=='=') | 
 | 			doescape = 1; | 
 | 		if (*s=='\\') | 
 |                         fputc(*s,F); /* if \\ then put it twice. */ | 
 | 		if (doescape) | 
 | 			fprintf(F,"\\u%04x",*((unsigned short*)s)); | 
 | 		else | 
 | 			fputc(*s,F); | 
 | 		s++; | 
 | 	} | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * _savesubkey [Internal] | 
 |  */ | 
 | static int _savesubkey( FILE *F, LPKEYSTRUCT lpkey, int level, int all ) | 
 | { | 
 | 	LPKEYSTRUCT	lpxkey; | 
 | 	int		i,tabs,j; | 
 |  | 
 | 	lpxkey	= lpkey; | 
 | 	while (lpxkey) { | 
 | 		if (	!(lpxkey->flags & REG_OPTION_VOLATILE) && | 
 | 			(all || (lpxkey->flags & REG_OPTION_TAINTED)) | 
 | 		) { | 
 | 			for (tabs=level;tabs--;) | 
 | 				fputc('\t',F); | 
 | 			_save_USTRING(F,lpxkey->keyname,1); | 
 | 			fputs("\n",F); | 
 | 			for (i=0;i<lpxkey->nrofvalues;i++) { | 
 | 				LPKEYVALUE	val=lpxkey->values+i; | 
 |  | 
 | 				for (tabs=level+1;tabs--;) | 
 | 					fputc('\t',F); | 
 | 				_save_USTRING(F,val->name,0); | 
 | 				fputc('=',F); | 
 | 				fprintf(F,"%ld,%ld,",val->type,val->lastmodified); | 
 | 				if ((1<<val->type) & UNICONVMASK) | 
 | 					_save_USTRING(F,(LPWSTR)val->data,0); | 
 | 				else | 
 | 					for (j=0;j<val->len;j++) | 
 | 						fprintf(F,"%02x",*((unsigned char*)val->data+j)); | 
 | 				fputs("\n",F); | 
 | 			} | 
 | 			/* descend recursively */ | 
 | 			if (!_savesubkey(F,lpxkey->nextsub,level+1,all)) | 
 | 				return 0; | 
 | 		} | 
 | 		lpxkey=lpxkey->next; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _savesubreg [Internal] | 
 |  */ | 
 | static int _savesubreg( FILE *F, LPKEYSTRUCT lpkey, int all ) | 
 | { | 
 | 	fprintf(F,"WINE REGISTRY Version %d\n",REGISTRY_SAVE_VERSION); | 
 | 	_save_check_tainted(lpkey->nextsub); | 
 | 	return _savesubkey(F,lpkey->nextsub,0,all); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _savereg [Internal] | 
 |  */ | 
 | static BOOL32 _savereg( LPKEYSTRUCT lpkey, char *fn, int all ) | 
 | { | 
 | 	FILE	*F; | 
 |  | 
 | 	F=fopen(fn,"w"); | 
 | 	if (F==NULL) { | 
 | 		WARN(reg,"Couldn't open %s for writing: %s\n", | 
 | 			fn,strerror(errno) | 
 | 		); | 
 | 		return FALSE; | 
 | 	} | 
 | 	if (!_savesubreg(F,lpkey,all)) { | 
 | 		fclose(F); | 
 | 		unlink(fn); | 
 | 		WARN(reg,"Failed to save keys, perhaps no more diskspace for %s?\n",fn); | 
 | 		return FALSE; | 
 | 	} | 
 | 	fclose(F); | 
 |         return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * SHELL_SaveRegistry [Internal] | 
 |  */ | 
 | void SHELL_SaveRegistry( void ) | 
 | { | 
 | 	char	*fn; | 
 | 	struct	passwd	*pwd; | 
 | 	char	buf[4]; | 
 | 	HKEY	hkey; | 
 | 	int	all; | 
 |  | 
 |     TRACE(reg,"(void)\n"); | 
 |  | 
 | 	all=0; | 
 | 	if (RegOpenKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS) { | 
 | 		strcpy(buf,"yes"); | 
 | 	} else { | 
 | 		DWORD len,junk,type; | 
 |  | 
 | 		len=4; | 
 | 		if (	(ERROR_SUCCESS!=RegQueryValueEx32A( | 
 | 				hkey, | 
 | 				VAL_SAVEUPDATED, | 
 | 				&junk, | 
 | 				&type, | 
 | 				buf, | 
 | 				&len | 
 | 			))|| (type!=REG_SZ) | 
 | 		) | 
 | 			strcpy(buf,"yes"); | 
 | 		RegCloseKey(hkey); | 
 | 	} | 
 | 	if (lstrcmpi32A(buf,"yes")) | 
 | 		all=1; | 
 | 	pwd=getpwuid(getuid()); | 
 | 	if (pwd!=NULL && pwd->pw_dir!=NULL) | 
 |         { | 
 |                 char *tmp; | 
 |  | 
 | 		fn=(char*)xmalloc( strlen(pwd->pw_dir) + strlen(WINE_PREFIX) + | 
 |                                    strlen(SAVE_CURRENT_USER) + 2 ); | 
 | 		strcpy(fn,pwd->pw_dir); | 
 | 		strcat(fn,WINE_PREFIX); | 
 | 		/* create the directory. don't care about errorcodes. */ | 
 | 		mkdir(fn,0755); /* drwxr-xr-x */ | 
 | 		strcat(fn,"/"SAVE_CURRENT_USER); | 
 | 		tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1); | 
 | 		strcpy(tmp,fn);strcat(tmp,".tmp"); | 
 | 		if (_savereg(lookup_hkey(HKEY_CURRENT_USER),tmp,all)) { | 
 | 			if (-1==rename(tmp,fn)) { | 
 | 				perror("rename tmp registry"); | 
 | 				unlink(tmp); | 
 | 			} | 
 | 		} | 
 | 		free(tmp); | 
 | 		free(fn); | 
 | 		fn=(char*)xmalloc(strlen(pwd->pw_dir)+strlen(WINE_PREFIX)+strlen(SAVE_LOCAL_MACHINE)+2); | 
 | 		strcpy(fn,pwd->pw_dir); | 
 | 		strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE); | 
 | 		tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1); | 
 | 		strcpy(tmp,fn);strcat(tmp,".tmp"); | 
 | 		if (_savereg(lookup_hkey(HKEY_LOCAL_MACHINE),tmp,all)) { | 
 | 			if (-1==rename(tmp,fn)) { | 
 | 				perror("rename tmp registry"); | 
 | 				unlink(tmp); | 
 | 			} | 
 | 		} | 
 | 		free(tmp); | 
 | 		free(fn); | 
 | 	} else | 
 | 		WARN(reg,"Failed to get homedirectory of UID %d.\n",getuid()); | 
 | } | 
 |  | 
 |  | 
 | /************************ LOAD Registry Function ****************************/ | 
 |  | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _find_or_add_key [Internal] | 
 |  */ | 
 | static LPKEYSTRUCT _find_or_add_key( LPKEYSTRUCT lpkey, LPWSTR keyname ) | 
 | { | 
 | 	LPKEYSTRUCT	lpxkey,*lplpkey; | 
 |  | 
 | 	if ((!keyname) || (keyname[0]==0)) { | 
 | 		free(keyname); | 
 | 		return lpkey; | 
 | 	} | 
 | 	lplpkey= &(lpkey->nextsub); | 
 | 	lpxkey	= *lplpkey; | 
 | 	while (lpxkey) { | 
 | 		if (	(lpxkey->keyname[0]==keyname[0]) &&  | 
 | 			!lstrcmpi32W(lpxkey->keyname,keyname) | 
 | 		) | 
 | 			break; | 
 | 		lplpkey	= &(lpxkey->next); | 
 | 		lpxkey	= *lplpkey; | 
 | 	} | 
 | 	if (lpxkey==NULL) { | 
 | 		*lplpkey = (LPKEYSTRUCT)xmalloc(sizeof(KEYSTRUCT)); | 
 | 		lpxkey	= *lplpkey; | 
 | 		memset(lpxkey,'\0',sizeof(KEYSTRUCT)); | 
 | 		lpxkey->keyname	= keyname; | 
 | 	} else | 
 | 		free(keyname); | 
 | 	return lpxkey; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * _find_or_add_value [Internal] | 
 |  */ | 
 | static void _find_or_add_value( LPKEYSTRUCT lpkey, LPWSTR name, DWORD type, | 
 |                                 LPBYTE data, DWORD len, DWORD lastmodified ) | 
 | { | 
 | 	LPKEYVALUE	val=NULL; | 
 | 	int		i; | 
 |  | 
 | 	if (name && !*name) {/* empty string equals default (NULL) value */ | 
 | 		free(name); | 
 | 		name = NULL; | 
 | 	} | 
 |  | 
 | 	for (i=0;i<lpkey->nrofvalues;i++) { | 
 | 		val=lpkey->values+i; | 
 | 		if (name==NULL) { | 
 | 			if (val->name==NULL) | 
 | 				break; | 
 | 		} else { | 
 | 			if (	val->name!=NULL &&  | 
 | 				val->name[0]==name[0] && | 
 | 				!lstrcmpi32W(val->name,name) | 
 | 			) | 
 | 				break; | 
 | 		} | 
 | 	} | 
 | 	if (i==lpkey->nrofvalues) { | 
 | 		lpkey->values = xrealloc( | 
 | 			lpkey->values, | 
 | 			(++lpkey->nrofvalues)*sizeof(KEYVALUE) | 
 | 		); | 
 | 		val=lpkey->values+i; | 
 | 		memset(val,'\0',sizeof(KEYVALUE)); | 
 | 		val->name = name; | 
 | 	} else { | 
 | 		if (name) | 
 | 			free(name); | 
 | 	} | 
 | 	if (val->lastmodified<lastmodified) { | 
 | 		val->lastmodified=lastmodified; | 
 | 		val->type = type; | 
 | 		val->len  = len; | 
 | 		if (val->data)  | 
 | 			free(val->data); | 
 | 		val->data = data; | 
 | 	} else | 
 | 		free(data); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _wine_read_line [Internal] | 
 |  * | 
 |  * reads a line including dynamically enlarging the readbuffer and throwing | 
 |  * away comments | 
 |  */ | 
 | static int _wine_read_line( FILE *F, char **buf, int *len ) | 
 | { | 
 | 	char	*s,*curread; | 
 | 	int	mylen,curoff; | 
 |  | 
 | 	curread	= *buf; | 
 | 	mylen	= *len; | 
 | 	**buf	= '\0'; | 
 | 	while (1) { | 
 | 		while (1) { | 
 | 			s=fgets(curread,mylen,F); | 
 | 			if (s==NULL) | 
 | 				return 0; /* EOF */ | 
 | 			if (NULL==(s=strchr(curread,'\n'))) { | 
 | 				/* buffer wasn't large enough */ | 
 | 				curoff	= strlen(*buf); | 
 | 				*buf	= xrealloc(*buf,*len*2); | 
 | 				curread	= *buf + curoff; | 
 | 				mylen	= *len;	/* we filled up the buffer and  | 
 | 						 * got new '*len' bytes to fill | 
 | 						 */ | 
 | 				*len	= *len * 2; | 
 | 			} else { | 
 | 				*s='\0'; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		/* throw away comments */ | 
 | 		if (**buf=='#' || **buf==';') { | 
 | 			curread	= *buf; | 
 | 			mylen	= *len; | 
 | 			continue; | 
 | 		} | 
 | 		if (s) 	/* got end of line */ | 
 | 			break; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _wine_read_USTRING [Internal] | 
 |  * | 
 |  * converts a char* into a UNICODE string (up to a special char) | 
 |  * and returns the position exactly after that string | 
 |  */ | 
 | static char* _wine_read_USTRING( char *buf, LPWSTR *str ) | 
 | { | 
 | 	char	*s; | 
 | 	LPWSTR	ws; | 
 |  | 
 | 	/* read up to "=" or "\0" or "\n" */ | 
 | 	s	= buf; | 
 | 	if (*s == '=') { | 
 | 		/* empty string is the win3.1 default value(NULL)*/ | 
 | 		*str	= NULL; | 
 | 		return s; | 
 | 	} | 
 | 	*str	= (LPWSTR)xmalloc(2*strlen(buf)+2); | 
 | 	ws	= *str; | 
 | 	while (*s && (*s!='\n') && (*s!='=')) { | 
 | 		if (*s!='\\') | 
 | 			*ws++=*((unsigned char*)s++); | 
 | 		else { | 
 | 			s++; | 
 | 			if (*s=='\\') { | 
 | 				*ws++='\\'; | 
 | 				s++; | 
 | 				continue; | 
 | 			} | 
 | 			if (*s!='u') { | 
 | 				WARN(reg,"Non unicode escape sequence \\%c found in |%s|\n",*s,buf); | 
 | 				*ws++='\\'; | 
 | 				*ws++=*s++; | 
 | 			} else { | 
 | 				char	xbuf[5]; | 
 | 				int	wc; | 
 |  | 
 | 				s++; | 
 | 				memcpy(xbuf,s,4);xbuf[4]='\0'; | 
 | 				if (!sscanf(xbuf,"%x",&wc)) | 
 | 					WARN(reg,"Strange escape sequence %s found in |%s|\n",xbuf,buf); | 
 | 				s+=4; | 
 | 				*ws++	=(unsigned short)wc; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	*ws	= 0; | 
 | 	ws	= *str; | 
 | 	if (*ws) | 
 | 		*str	= strdupW(*str); | 
 | 	else | 
 | 		*str	= NULL; | 
 | 	free(ws); | 
 | 	return s; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _wine_loadsubkey [Internal] | 
 |  * | 
 |  * NOTES | 
 |  *    It seems like this is returning a boolean.  Should it? | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: 1 | 
 |  *    Failure: 0 | 
 |  */ | 
 | static int _wine_loadsubkey( FILE *F, LPKEYSTRUCT lpkey, int level, char **buf, | 
 |                              int *buflen, DWORD optflag ) | 
 | { | 
 | 	LPKEYSTRUCT	lpxkey; | 
 | 	int		i; | 
 | 	char		*s; | 
 | 	LPWSTR		name; | 
 |  | 
 |     TRACE(reg,"(%p,%p,%d,%s,%d,%lx)\n", F, lpkey, level, debugstr_a(*buf), | 
 |           *buflen, optflag); | 
 |  | 
 |     lpkey->flags |= optflag; | 
 |  | 
 |     /* Good.  We already got a line here ... so parse it */ | 
 |     lpxkey = NULL; | 
 |     while (1) { | 
 |         i=0;s=*buf; | 
 |         while (*s=='\t') { | 
 |             s++; | 
 |             i++; | 
 |         } | 
 |         if (i>level) { | 
 |             if (lpxkey==NULL) { | 
 |                 WARN(reg,"Got a subhierarchy without resp. key?\n"); | 
 |                 return 0; | 
 |             } | 
 |             _wine_loadsubkey(F,lpxkey,level+1,buf,buflen,optflag); | 
 |             continue; | 
 |         } | 
 |  | 
 | 		/* let the caller handle this line */ | 
 | 		if (i<level || **buf=='\0') | 
 | 			return 1; | 
 |  | 
 | 		/* it can be: a value or a keyname. Parse the name first */ | 
 | 		s=_wine_read_USTRING(s,&name); | 
 |  | 
 | 		/* switch() default: hack to avoid gotos */ | 
 | 		switch (0) { | 
 | 		default: | 
 | 			if (*s=='\0') { | 
 | 				lpxkey=_find_or_add_key(lpkey,name); | 
 | 			} else { | 
 | 				LPBYTE		data; | 
 | 				int		len,lastmodified,type; | 
 |  | 
 | 				if (*s!='=') { | 
 | 					WARN(reg,"Unexpected character: %c\n",*s); | 
 | 					break; | 
 | 				} | 
 | 				s++; | 
 | 				if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) { | 
 | 					WARN(reg,"Haven't understood possible value in |%s|, skipping.\n",*buf); | 
 | 					break; | 
 | 				} | 
 | 				/* skip the 2 , */ | 
 | 				s=strchr(s,',');s++; | 
 | 				s=strchr(s,',');s++; | 
 | 				if ((1<<type) & UNICONVMASK) { | 
 | 					s=_wine_read_USTRING(s,(LPWSTR*)&data); | 
 | 					if (data) | 
 | 						len = lstrlen32W((LPWSTR)data)*2+2; | 
 | 					else	 | 
 | 						len = 0; | 
 | 				} else { | 
 | 					len=strlen(s)/2; | 
 | 					data = (LPBYTE)xmalloc(len+1); | 
 | 					for (i=0;i<len;i++) { | 
 | 						data[i]=0; | 
 | 						if (*s>='0' && *s<='9') | 
 | 							data[i]=(*s-'0')<<4; | 
 | 						if (*s>='a' && *s<='f') | 
 | 							data[i]=(*s-'a'+'\xa')<<4; | 
 | 						if (*s>='A' && *s<='F') | 
 | 							data[i]=(*s-'A'+'\xa')<<4; | 
 | 						s++; | 
 | 						if (*s>='0' && *s<='9') | 
 | 							data[i]|=*s-'0'; | 
 | 						if (*s>='a' && *s<='f') | 
 | 							data[i]|=*s-'a'+'\xa'; | 
 | 						if (*s>='A' && *s<='F') | 
 | 							data[i]|=*s-'A'+'\xa'; | 
 | 						s++; | 
 | 					} | 
 | 				} | 
 | 				_find_or_add_value(lpkey,name,type,data,len,lastmodified); | 
 | 			} | 
 | 		} | 
 | 		/* read the next line */ | 
 | 		if (!_wine_read_line(F,buf,buflen)) | 
 | 			return 1; | 
 |     } | 
 |     return 1; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _wine_loadsubreg [Internal] | 
 |  */ | 
 | static int _wine_loadsubreg( FILE *F, LPKEYSTRUCT lpkey, DWORD optflag ) | 
 | { | 
 | 	int	ver; | 
 | 	char	*buf; | 
 | 	int	buflen; | 
 |  | 
 | 	buf=xmalloc(10);buflen=10; | 
 | 	if (!_wine_read_line(F,&buf,&buflen)) { | 
 | 		free(buf); | 
 | 		return 0; | 
 | 	} | 
 | 	if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) { | 
 | 		free(buf); | 
 | 		return 0; | 
 | 	} | 
 | 	if (ver!=REGISTRY_SAVE_VERSION) { | 
 | 		TRACE(reg,"Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf); | 
 | 		free(buf); | 
 | 		return 0; | 
 | 	} | 
 | 	if (!_wine_read_line(F,&buf,&buflen)) { | 
 | 		free(buf); | 
 | 		return 0; | 
 | 	} | 
 | 	if (!_wine_loadsubkey(F,lpkey,0,&buf,&buflen,optflag)) { | 
 | 		free(buf); | 
 | 		return 0; | 
 | 	} | 
 | 	free(buf); | 
 | 	return 1; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _wine_loadreg [Internal] | 
 |  */ | 
 | static void _wine_loadreg( LPKEYSTRUCT lpkey, char *fn, DWORD optflag ) | 
 | { | 
 |     FILE *F; | 
 |  | 
 |     TRACE(reg,"(%p,%s,%lx)\n",lpkey,debugstr_a(fn),optflag); | 
 |  | 
 |     F = fopen(fn,"rb"); | 
 |     if (F==NULL) { | 
 |         WARN(reg,"Couldn't open %s for reading: %s\n",fn,strerror(errno) ); | 
 |         return; | 
 |     } | 
 |     if (!_wine_loadsubreg(F,lpkey,optflag)) { | 
 |         fclose(F); | 
 |         unlink(fn); | 
 |         return; | 
 |     } | 
 |     fclose(F); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _copy_registry [Internal] | 
 |  */ | 
 | static void _copy_registry( LPKEYSTRUCT from, LPKEYSTRUCT to ) | 
 | { | 
 | 	LPKEYSTRUCT	lpxkey; | 
 | 	int		j; | 
 | 	LPKEYVALUE	valfrom; | 
 |  | 
 | 	from=from->nextsub; | 
 | 	while (from) { | 
 | 		lpxkey = _find_or_add_key(to,strdupW(from->keyname)); | 
 |  | 
 | 		for (j=0;j<from->nrofvalues;j++) { | 
 | 			LPWSTR	name; | 
 | 			LPBYTE	data; | 
 |  | 
 | 			valfrom = from->values+j; | 
 | 			name=valfrom->name; | 
 | 			if (name) name=strdupW(name); | 
 | 			data=(LPBYTE)xmalloc(valfrom->len); | 
 | 			memcpy(data,valfrom->data,valfrom->len); | 
 |  | 
 | 			_find_or_add_value( | 
 | 				lpxkey, | 
 | 				name, | 
 | 				valfrom->type, | 
 | 				data, | 
 | 				valfrom->len, | 
 | 				valfrom->lastmodified | 
 | 			); | 
 | 		} | 
 | 		_copy_registry(from,lpxkey); | 
 | 		from = from->next; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /* WINDOWS 95 REGISTRY LOADER */ | 
 | /*  | 
 |  * Structure of a win95 registry database. | 
 |  * main header: | 
 |  * 0 :	"CREG"	- magic | 
 |  * 4 :	DWORD version | 
 |  * 8 :	DWORD offset_of_RGDB_part | 
 |  * 0C..0F:	? (someone fill in please) | 
 |  * 10:  WORD	number of RGDB blocks | 
 |  * 12:  WORD	? | 
 |  * 14:  WORD	always 0000? | 
 |  * 16:  WORD	always 0001? | 
 |  * 18..1F:	? (someone fill in please) | 
 |  * | 
 |  * 20: RGKN_section: | 
 |  *   header: | 
 |  * 	0 :		"RGKN"	- magic | 
 |  *      4 : DWORD	offset to first RGDB section | 
 |  *      8 : DWORD	offset to the root record | 
 |  * 	C..0x1B: 	? (fill in) | 
 |  *      0x20 ... offset_of_RGDB_part: Disk Key Entry structures | 
 |  * | 
 |  *   Disk Key Entry Structure: | 
 |  *	00: DWORD	- Free entry indicator(?) | 
 |  *	04: DWORD	- Hash = sum of bytes of keyname | 
 |  *	08: DWORD	- Root key indicator? unknown, but usually 0xFFFFFFFF on win95 systems | 
 |  *	0C: DWORD	- disk address of PreviousLevel Key. | 
 |  *	10: DWORD	- disk address of Next Sublevel Key. | 
 |  *	14: DWORD	- disk address of Next Key (on same level). | 
 |  * DKEP>18: WORD	- Nr, Low Significant part. | 
 |  *	1A: WORD	- Nr, High Significant part. | 
 |  * | 
 |  * The disk address always points to the nr part of the previous key entry  | 
 |  * of the referenced key. Don't ask me why, or even if I got this correct | 
 |  * from staring at 1kg of hexdumps. (DKEP) | 
 |  * | 
 |  * The High significant part of the structure seems to equal the number | 
 |  * of the RGDB section. The low significant part is a unique ID within | 
 |  * that RGDB section | 
 |  * | 
 |  * There are two minor corrections to the position of that structure. | 
 |  * 1. If the address is xxx014 or xxx018 it will be aligned to xxx01c AND  | 
 |  *    the DKE reread from there. | 
 |  * 2. If the address is xxxFFx it will be aligned to (xxx+1)000. | 
 |  * CPS - I have not experienced the above phenomenon in my registry files | 
 |  * | 
 |  * RGDB_section: | 
 |  * 	00:		"RGDB"	- magic | 
 |  *	04: DWORD	offset to next RGDB section | 
 |  *	08: DWORD	? | 
 |  *	0C: WORD	always 000d? | 
 |  *	0E: WORD	RGDB block number | 
 |  *	10:	DWORD	? (equals value at offset 4 - value at offset 8) | 
 |  *	14..1F:		? | 
 |  *	20.....:	disk keys | 
 |  * | 
 |  * disk key: | 
 |  * 	00: 	DWORD	nextkeyoffset	- offset to the next disk key structure | 
 |  *	08: 	WORD	nrLS		- low significant part of NR | 
 |  *	0A: 	WORD	nrHS		- high significant part of NR | 
 |  *	0C: 	DWORD	bytesused	- bytes used in this structure. | 
 |  *	10: 	WORD	name_len	- length of name in bytes. without \0 | 
 |  *	12: 	WORD	nr_of_values	- number of values. | 
 |  *	14: 	char	name[name_len]	- name string. No \0. | 
 |  *	14+name_len: disk values | 
 |  *	nextkeyoffset: ... next disk key | 
 |  * | 
 |  * disk value: | 
 |  *	00:	DWORD	type		- value type (hmm, could be WORD too) | 
 |  *	04:	DWORD			- unknown, usually 0 | 
 |  *	08:	WORD	namelen		- length of Name. 0 means name=NULL | 
 |  *	0C:	WORD	datalen		- length of Data. | 
 |  *	10:	char	name[namelen]	- name, no \0 | 
 |  *	10+namelen: BYTE	data[datalen] - data, without \0 if string | 
 |  *	10+namelen+datalen: next values or disk key | 
 |  * | 
 |  * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both | 
 |  * 0xFFFF, which means skipping over nextkeyoffset bytes (including this | 
 |  * structure) and reading another RGDB_section. | 
 |  * repeat until end of file. | 
 |  * | 
 |  * An interesting relationship exists in RGDB_section. The value at offset | 
 |  * 10 equals the value at offset 4 minus the value at offset 8. I have no | 
 |  * idea at the moment what this means.  (Kevin Cozens) | 
 |  * | 
 |  * FIXME: this description needs some serious help, yes. | 
 |  */ | 
 |  | 
 | struct	_w95keyvalue { | 
 | 	unsigned long		type; | 
 | 	unsigned short		datalen; | 
 | 	char			*name; | 
 | 	unsigned char		*data; | 
 | 	unsigned long		x1; | 
 | 	int			lastmodified; | 
 | }; | 
 |  | 
 | struct 	_w95key { | 
 | 	char			*name; | 
 | 	int			nrofvals; | 
 | 	struct	_w95keyvalue	*values; | 
 | 	struct _w95key		*prevlvl; | 
 | 	struct _w95key		*nextsub; | 
 | 	struct _w95key		*next; | 
 | }; | 
 |  | 
 |  | 
 | struct _w95_info { | 
 |   char *rgknbuffer; | 
 |   int  rgknsize; | 
 |   char *rgdbbuffer; | 
 |   int  rgdbsize; | 
 |   int  depth; | 
 |   int  lastmodified; | 
 | }; | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _w95_processKey [Internal] | 
 |  */ | 
 | static LPKEYSTRUCT _w95_processKey ( LPKEYSTRUCT lpkey,  | 
 |                                    int nrLS, int nrMS, struct _w95_info *info ) | 
 |  | 
 | { | 
 |   /* Disk Key Header structure (RGDB part) */ | 
 | 	struct	dkh { | 
 |                 unsigned long		nextkeyoff;  | 
 | 		unsigned short		nrLS; | 
 | 		unsigned short		nrMS; | 
 | 		unsigned long		bytesused; | 
 | 		unsigned short		keynamelen; | 
 | 		unsigned short		values; | 
 | 		unsigned long		xx1; | 
 | 		/* keyname */ | 
 | 		/* disk key values or nothing */ | 
 | 	}; | 
 | 	/* Disk Key Value structure */ | 
 | 	struct	dkv { | 
 | 		unsigned long		type; | 
 | 		unsigned long		x1; | 
 | 		unsigned short		valnamelen; | 
 | 		unsigned short		valdatalen; | 
 | 		/* valname, valdata */ | 
 | 	}; | 
 |  | 
 | 	 | 
 | 	struct	dkh dkh; | 
 | 	int	bytesread = 0; | 
 | 	char    *rgdbdata = info->rgdbbuffer; | 
 | 	int     nbytes = info->rgdbsize; | 
 | 	char    *curdata = rgdbdata; | 
 | 	char    *end = rgdbdata + nbytes; | 
 | 	int     off_next_rgdb; | 
 | 	char    *next = rgdbdata; | 
 | 	int     nrgdb, i; | 
 | 	LPKEYSTRUCT	lpxkey; | 
 | 	 | 
 | 	do { | 
 | 	  curdata = next; | 
 | 	  if (strncmp(curdata, "RGDB", 4)) return (NULL); | 
 | 	     | 
 | 	  memcpy(&off_next_rgdb,curdata+4,4); | 
 | 	  next = curdata + off_next_rgdb; | 
 | 	  nrgdb = (int) *((short *)curdata + 7); | 
 |  | 
 | 	} while (nrgdb != nrMS && (next < end)); | 
 |  | 
 | 	/* curdata now points to the start of the right RGDB section */ | 
 | 	curdata += 0x20; | 
 |  | 
 | #define XREAD(whereto,len) \ | 
 | 	if ((curdata + len) <end) {\ | 
 | 		memcpy(whereto,curdata,len);\ | 
 | 		curdata+=len;\ | 
 | 		bytesread+=len;\ | 
 | 	} | 
 |  | 
 | 	while (curdata < next) { | 
 | 	  struct	dkh *xdkh = (struct dkh*)curdata; | 
 |  | 
 | 	  bytesread += sizeof(dkh); /* FIXME... nextkeyoff? */ | 
 | 	  if (xdkh->nrLS == nrLS) { | 
 | 	  	memcpy(&dkh,xdkh,sizeof(dkh)); | 
 | 	  	curdata += sizeof(dkh); | 
 | 	  	break; | 
 | 	  } | 
 | 	  curdata += xdkh->nextkeyoff; | 
 | 	}; | 
 |  | 
 | 	if (dkh.nrLS != nrLS) return (NULL); | 
 |  | 
 | 	if (nrgdb != dkh.nrMS) | 
 | 	  return (NULL); | 
 |  | 
 |         assert((dkh.keynamelen<2) || curdata[0]); | 
 | 	lpxkey=_find_or_add_key(lpkey,strcvtA2W(curdata, dkh.keynamelen)); | 
 | 	curdata += dkh.keynamelen; | 
 |  | 
 | 	for (i=0;i< dkh.values; i++) { | 
 | 	  struct dkv dkv; | 
 | 	  LPBYTE data; | 
 | 	  int len; | 
 | 	  LPWSTR name; | 
 |  | 
 | 	  XREAD(&dkv,sizeof(dkv)); | 
 |  | 
 | 	  name = strcvtA2W(curdata, dkv.valnamelen); | 
 | 	  curdata += dkv.valnamelen; | 
 |  | 
 | 	  if ((1 << dkv.type) & UNICONVMASK) { | 
 | 	    data = (LPBYTE) strcvtA2W(curdata, dkv.valdatalen); | 
 | 	    len = 2*(dkv.valdatalen + 1); | 
 | 	  } else { | 
 | 	    /* I don't think we want to NULL terminate all data */ | 
 | 	    data = xmalloc(dkv.valdatalen); | 
 | 	    memcpy (data, curdata, dkv.valdatalen); | 
 | 	    len = dkv.valdatalen; | 
 | 	  } | 
 |  | 
 | 	  curdata += dkv.valdatalen; | 
 | 	   | 
 | 	  _find_or_add_value( | 
 | 			     lpxkey, | 
 | 			     name, | 
 | 			     dkv.type, | 
 | 			     data, | 
 | 			     len, | 
 | 			     info->lastmodified | 
 | 			     ); | 
 | 	} | 
 | 	return (lpxkey); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * _w95_walkrgkn [Internal] | 
 |  */ | 
 | static void _w95_walkrgkn( LPKEYSTRUCT prevkey, char *off,  | 
 |                            struct _w95_info *info ) | 
 |  | 
 | { | 
 |   /* Disk Key Entry structure (RGKN part) */ | 
 |   struct	dke { | 
 |     unsigned long		x1; | 
 |     unsigned long		x2; | 
 |     unsigned long		x3;/*usually 0xFFFFFFFF */ | 
 |     unsigned long		prevlvl; | 
 |     unsigned long		nextsub; | 
 |     unsigned long		next; | 
 |     unsigned short		nrLS; | 
 |     unsigned short		nrMS; | 
 |   } *dke = (struct dke *)off; | 
 |   LPKEYSTRUCT  lpxkey; | 
 |  | 
 |   if (dke == NULL) { | 
 |     dke = (struct dke *) ((char *)info->rgknbuffer); | 
 |   } | 
 |  | 
 |   lpxkey = _w95_processKey(prevkey, dke->nrLS, dke->nrMS, info); | 
 |   /* XXX <-- This is a hack*/ | 
 |   if (!lpxkey) { | 
 |     lpxkey = prevkey; | 
 |   } | 
 |  | 
 |   if (dke->nextsub != -1 &&  | 
 |       ((dke->nextsub - 0x20) < info->rgknsize)  | 
 |       && (dke->nextsub > 0x20)) { | 
 |      | 
 |     _w95_walkrgkn(lpxkey,  | 
 | 		  info->rgknbuffer + dke->nextsub - 0x20,  | 
 | 		  info); | 
 |   } | 
 |    | 
 |   if (dke->next != -1 &&  | 
 |       ((dke->next - 0x20) < info->rgknsize) &&  | 
 |       (dke->next > 0x20)) { | 
 |     _w95_walkrgkn(prevkey,   | 
 | 		  info->rgknbuffer + dke->next - 0x20, | 
 | 		  info); | 
 |   } | 
 |  | 
 |   return; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _w95_loadreg [Internal] | 
 |  */ | 
 | static void _w95_loadreg( char* fn, LPKEYSTRUCT lpkey ) | 
 | { | 
 | 	HFILE32		hfd; | 
 | 	char		magic[5]; | 
 | 	unsigned long	where,version,rgdbsection,end; | 
 | 	struct          _w95_info info; | 
 | 	OFSTRUCT	ofs; | 
 | 	BY_HANDLE_FILE_INFORMATION hfdinfo; | 
 |  | 
 | 	TRACE(reg,"Loading Win95 registry database '%s'\n",fn); | 
 | 	hfd=OpenFile32(fn,&ofs,OF_READ); | 
 | 	if (hfd==HFILE_ERROR32) | 
 | 		return; | 
 | 	magic[4]=0; | 
 | 	if (4!=_lread32(hfd,magic,4)) | 
 | 		return; | 
 | 	if (strcmp(magic,"CREG")) { | 
 | 		WARN(reg,"%s is not a w95 registry.\n",fn); | 
 | 		return; | 
 | 	} | 
 | 	if (4!=_lread32(hfd,&version,4)) | 
 | 		return; | 
 | 	if (4!=_lread32(hfd,&rgdbsection,4)) | 
 | 		return; | 
 | 	if (-1==_llseek32(hfd,0x20,SEEK_SET)) | 
 | 		return; | 
 | 	if (4!=_lread32(hfd,magic,4)) | 
 | 		return; | 
 | 	if (strcmp(magic,"RGKN")) { | 
 | 		WARN(reg, "second IFF header not RGKN, but %s\n", magic); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* STEP 1: Keylink structures */ | 
 | 	if (-1==_llseek32(hfd,0x40,SEEK_SET)) | 
 | 		return; | 
 | 	where	= 0x40; | 
 | 	end	= rgdbsection; | 
 |  | 
 | 	info.rgknsize = end - where; | 
 | 	info.rgknbuffer = (char*)xmalloc(info.rgknsize); | 
 | 	if (info.rgknsize != _lread32(hfd,info.rgknbuffer,info.rgknsize)) | 
 | 		return; | 
 |  | 
 | 	if (!GetFileInformationByHandle(hfd,&hfdinfo)) | 
 | 		return; | 
 |  | 
 | 	end = hfdinfo.nFileSizeLow; | 
 | 	info.lastmodified = DOSFS_FileTimeToUnixTime(&hfdinfo.ftLastWriteTime,NULL); | 
 |  | 
 | 	if (-1==_llseek32(hfd,rgdbsection,SEEK_SET)) | 
 | 		return; | 
 |  | 
 | 	info.rgdbbuffer = (char*)xmalloc(end-rgdbsection); | 
 | 	info.rgdbsize = end - rgdbsection; | 
 |  | 
 | 	if (info.rgdbsize !=_lread32(hfd,info.rgdbbuffer,info.rgdbsize)) | 
 | 		return; | 
 | 	_lclose32(hfd); | 
 |  | 
 | 	_w95_walkrgkn(lpkey, NULL, &info); | 
 |  | 
 | 	free (info.rgdbbuffer); | 
 | 	free (info.rgknbuffer); | 
 | } | 
 |  | 
 |  | 
 | /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */ | 
 |  | 
 | /* | 
 |     reghack - windows 3.11 registry data format demo program. | 
 |  | 
 |     The reg.dat file has 3 parts, a header, a table of 8-byte entries that is | 
 |     a combined hash table and tree description, and finally a text table. | 
 |  | 
 |     The header is obvious from the struct header. The taboff1 and taboff2 | 
 |     fields are always 0x20, and their usage is unknown. | 
 |  | 
 |     The 8-byte entry table has various entry types. | 
 |  | 
 |     tabent[0] is a root index. The second word has the index of the root of | 
 |             the directory. | 
 |     tabent[1..hashsize] is a hash table. The first word in the hash entry is | 
 |             the index of the key/value that has that hash. Data with the same | 
 |             hash value are on a circular list. The other three words in the | 
 |             hash entry are always zero. | 
 |     tabent[hashsize..tabcnt] is the tree structure. There are two kinds of | 
 |             entry: dirent and keyent/valent. They are identified by context. | 
 |     tabent[freeidx] is the first free entry. The first word in a free entry | 
 |             is the index of the next free entry. The last has 0 as a link. | 
 |             The other three words in the free list are probably irrelevant. | 
 |  | 
 |     Entries in text table are preceeded by a word at offset-2. This word | 
 |     has the value (2*index)+1, where index is the referring keyent/valent | 
 |     entry in the table. I have no suggestion for the 2* and the +1. | 
 |     Following the word, there are N bytes of data, as per the keyent/valent | 
 |     entry length. The offset of the keyent/valent entry is from the start | 
 |     of the text table to the first data byte. | 
 |  | 
 |     This information is not available from Microsoft. The data format is | 
 |     deduced from the reg.dat file by me. Mistakes may | 
 |     have been made. I claim no rights and give no guarantees for this program. | 
 |  | 
 |     Tor Sjøwall, tor@sn.no | 
 | */ | 
 |  | 
 | /* reg.dat header format */ | 
 | struct _w31_header { | 
 | 	char		cookie[8];	/* 'SHCC3.10' */ | 
 | 	unsigned long	taboff1;	/* offset of hash table (??) = 0x20 */ | 
 | 	unsigned long	taboff2;	/* offset of index table (??) = 0x20 */ | 
 | 	unsigned long	tabcnt;		/* number of entries in index table */ | 
 | 	unsigned long	textoff;	/* offset of text part */ | 
 | 	unsigned long	textsize;	/* byte size of text part */ | 
 | 	unsigned short	hashsize;	/* hash size */ | 
 | 	unsigned short	freeidx;	/* free index */ | 
 | }; | 
 |  | 
 | /* generic format of table entries */ | 
 | struct _w31_tabent { | 
 | 	unsigned short w0, w1, w2, w3; | 
 | }; | 
 |  | 
 | /* directory tabent: */ | 
 | struct _w31_dirent { | 
 | 	unsigned short	sibling_idx;	/* table index of sibling dirent */ | 
 | 	unsigned short	child_idx;	/* table index of child dirent */ | 
 | 	unsigned short	key_idx;	/* table index of key keyent */ | 
 | 	unsigned short	value_idx;	/* table index of value valent */ | 
 | }; | 
 |  | 
 | /* key tabent: */ | 
 | struct _w31_keyent { | 
 | 	unsigned short	hash_idx;	/* hash chain index for string */ | 
 | 	unsigned short	refcnt;		/* reference count */ | 
 | 	unsigned short	length;		/* length of string */ | 
 | 	unsigned short	string_off;	/* offset of string in text table */ | 
 | }; | 
 |  | 
 | /* value tabent: */ | 
 | struct _w31_valent { | 
 | 	unsigned short	hash_idx;	/* hash chain index for string */ | 
 | 	unsigned short	refcnt;		/* reference count */ | 
 | 	unsigned short	length;		/* length of string */ | 
 | 	unsigned short	string_off;	/* offset of string in text table */ | 
 | }; | 
 |  | 
 | /* recursive helper function to display a directory tree */ | 
 | void | 
 | __w31_dumptree(	unsigned short idx, | 
 | 		unsigned char *txt, | 
 | 		struct _w31_tabent *tab, | 
 | 		struct _w31_header *head, | 
 | 		LPKEYSTRUCT	lpkey, | 
 | 		time_t		lastmodified, | 
 | 		int		level | 
 | ) { | 
 | 	struct _w31_dirent	*dir; | 
 | 	struct _w31_keyent	*key; | 
 | 	struct _w31_valent	*val; | 
 | 	LPKEYSTRUCT		xlpkey = NULL; | 
 | 	LPWSTR			name,value; | 
 | 	static char		tail[400]; | 
 |  | 
 | 	while (idx!=0) { | 
 | 		dir=(struct _w31_dirent*)&tab[idx]; | 
 |  | 
 | 		if (dir->key_idx) { | 
 | 			key = (struct _w31_keyent*)&tab[dir->key_idx]; | 
 |  | 
 | 			memcpy(tail,&txt[key->string_off],key->length); | 
 | 			tail[key->length]='\0'; | 
 | 			/* all toplevel entries AND the entries in the  | 
 | 			 * toplevel subdirectory belong to \SOFTWARE\Classes | 
 | 			 */ | 
 | 			if (!level && !lstrcmp32A(tail,".classes")) { | 
 | 				__w31_dumptree(dir->child_idx,txt,tab,head,lpkey,lastmodified,level+1); | 
 | 				idx=dir->sibling_idx; | 
 | 				continue; | 
 | 			} | 
 | 			name=strdupA2W(tail); | 
 |  | 
 | 			xlpkey=_find_or_add_key(lpkey,name); | 
 |  | 
 | 			/* only add if leaf node or valued node */ | 
 | 			if (dir->value_idx!=0||dir->child_idx==0) { | 
 | 				if (dir->value_idx) { | 
 | 					val=(struct _w31_valent*)&tab[dir->value_idx]; | 
 | 					memcpy(tail,&txt[val->string_off],val->length); | 
 | 					tail[val->length]='\0'; | 
 | 					value=strdupA2W(tail); | 
 | 					_find_or_add_value(xlpkey,NULL,REG_SZ,(LPBYTE)value,lstrlen32W(value)*2+2,lastmodified); | 
 | 				} | 
 | 			} | 
 | 		} else { | 
 | 			TRACE(reg,"strange: no directory key name, idx=%04x\n", idx); | 
 | 		} | 
 | 		__w31_dumptree(dir->child_idx,txt,tab,head,xlpkey,lastmodified,level+1); | 
 | 		idx=dir->sibling_idx; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * _w31_loadreg [Internal] | 
 |  */ | 
 | void _w31_loadreg(void) { | 
 | 	HFILE32			hf; | 
 | 	struct _w31_header	head; | 
 | 	struct _w31_tabent	*tab; | 
 | 	unsigned char		*txt; | 
 | 	int			len; | 
 | 	OFSTRUCT		ofs; | 
 | 	BY_HANDLE_FILE_INFORMATION hfinfo; | 
 | 	time_t			lastmodified; | 
 | 	LPKEYSTRUCT		lpkey; | 
 |  | 
 | 	TRACE(reg,"(void)\n"); | 
 |  | 
 | 	hf = OpenFile32("reg.dat",&ofs,OF_READ); | 
 | 	if (hf==HFILE_ERROR32) | 
 | 		return; | 
 |  | 
 | 	/* read & dump header */ | 
 | 	if (sizeof(head)!=_lread32(hf,&head,sizeof(head))) { | 
 | 		ERR(reg, "reg.dat is too short.\n"); | 
 | 		_lclose32(hf); | 
 | 		return; | 
 | 	} | 
 | 	if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) { | 
 | 		ERR(reg, "reg.dat has bad signature.\n"); | 
 | 		_lclose32(hf); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	len = head.tabcnt * sizeof(struct _w31_tabent); | 
 | 	/* read and dump index table */ | 
 | 	tab = xmalloc(len); | 
 | 	if (len!=_lread32(hf,tab,len)) { | 
 | 		ERR(reg,"couldn't read %d bytes.\n",len);  | 
 | 		free(tab); | 
 | 		_lclose32(hf); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* read text */ | 
 | 	txt = xmalloc(head.textsize); | 
 | 	if (-1==_llseek32(hf,head.textoff,SEEK_SET)) { | 
 | 		ERR(reg,"couldn't seek to textblock.\n");  | 
 | 		free(tab); | 
 | 		free(txt); | 
 | 		_lclose32(hf); | 
 | 		return; | 
 | 	} | 
 | 	if (head.textsize!=_lread32(hf,txt,head.textsize)) { | 
 | 		ERR(reg,"textblock too short (%d instead of %ld).\n",len,head.textsize);  | 
 | 		free(tab); | 
 | 		free(txt); | 
 | 		_lclose32(hf); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (!GetFileInformationByHandle(hf,&hfinfo)) { | 
 | 		ERR(reg,"GetFileInformationByHandle failed?.\n");  | 
 | 		free(tab); | 
 | 		free(txt); | 
 | 		_lclose32(hf); | 
 | 		return; | 
 | 	} | 
 | 	lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL); | 
 | 	lpkey = lookup_hkey(HKEY_CLASSES_ROOT); | 
 | 	__w31_dumptree(tab[0].w1,txt,tab,&head,lpkey,lastmodified,0); | 
 | 	free(tab); | 
 | 	free(txt); | 
 | 	_lclose32(hf); | 
 | 	return; | 
 | } | 
 |  | 
 |  | 
 | /********************************************************************************** | 
 |  * SHELL_LoadRegistry [Internal] | 
 |  */ | 
 | void SHELL_LoadRegistry( void ) | 
 | { | 
 | 	char	*fn; | 
 | 	struct	passwd	*pwd; | 
 | 	LPKEYSTRUCT	lpkey; | 
 | 	HKEY		hkey; | 
 |  | 
 | 	TRACE(reg,"(void)\n"); | 
 |  | 
 | 	/* Load windows 3.1 entries */ | 
 | 	_w31_loadreg(); | 
 | 	/* Load windows 95 entries */ | 
 | 	_w95_loadreg("C:\\system.1st",	lookup_hkey(HKEY_LOCAL_MACHINE)); | 
 | 	_w95_loadreg("system.dat",	lookup_hkey(HKEY_LOCAL_MACHINE)); | 
 | 	_w95_loadreg("user.dat",	lookup_hkey(HKEY_USERS)); | 
 |  | 
 | 	/* the global user default is loaded under HKEY_USERS\\.Default */ | 
 | 	RegCreateKey16(HKEY_USERS,".Default",&hkey); | 
 | 	lpkey = lookup_hkey(hkey); | 
 |         if(!lpkey) | 
 |             WARN(reg,"Could not create global user default key\n"); | 
 | 	_wine_loadreg(lpkey,SAVE_USERS_DEFAULT,0); | 
 |  | 
 | 	/* HKEY_USERS\\.Default is copied to HKEY_CURRENT_USER */ | 
 | 	_copy_registry(lpkey,lookup_hkey(HKEY_CURRENT_USER)); | 
 | 	RegCloseKey(hkey); | 
 |  | 
 | 	/* the global machine defaults */ | 
 | 	_wine_loadreg(lookup_hkey(HKEY_LOCAL_MACHINE),SAVE_LOCAL_MACHINE_DEFAULT,0); | 
 |  | 
 | 	/* load the user saved registries */ | 
 |  | 
 | 	/* FIXME: use getenv("HOME") or getpwuid(getuid())->pw_dir ?? */ | 
 |  | 
 | 	pwd=getpwuid(getuid()); | 
 | 	if (pwd!=NULL && pwd->pw_dir!=NULL) { | 
 | 		fn=(char*)xmalloc(strlen(pwd->pw_dir)+strlen(WINE_PREFIX)+strlen(SAVE_CURRENT_USER)+2); | 
 | 		strcpy(fn,pwd->pw_dir); | 
 | 		strcat(fn,WINE_PREFIX"/"SAVE_CURRENT_USER); | 
 | 		_wine_loadreg(lookup_hkey(HKEY_CURRENT_USER),fn,REG_OPTION_TAINTED); | 
 | 		free(fn); | 
 | 		fn=(char*)xmalloc(strlen(pwd->pw_dir)+strlen(WINE_PREFIX)+strlen(SAVE_LOCAL_MACHINE)+2); | 
 | 		strcpy(fn,pwd->pw_dir); | 
 | 		strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE); | 
 | 		_wine_loadreg(lookup_hkey(HKEY_LOCAL_MACHINE),fn,REG_OPTION_TAINTED); | 
 | 		free(fn); | 
 | 	} else | 
 | 		WARN(reg,"Failed to get homedirectory of UID %d.\n",getuid()); | 
 | 	if (ERROR_SUCCESS==RegCreateKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)) { | 
 | 		DWORD	junk,type,len; | 
 | 		char	data[5]; | 
 |  | 
 | 		len=4; | 
 | 		if ((	RegQueryValueEx32A( | 
 | 				hkey, | 
 | 				VAL_SAVEUPDATED, | 
 | 				&junk, | 
 | 				&type, | 
 | 				data, | 
 | 				&len | 
 | 			)!=ERROR_SUCCESS) || | 
 | 			type != REG_SZ | 
 | 		) | 
 | 			RegSetValueEx32A(hkey,VAL_SAVEUPDATED,0,REG_SZ,"yes",4); | 
 | 		RegCloseKey(hkey); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /********************* API FUNCTIONS ***************************************/ | 
 | /* | 
 |  * Open Keys. | 
 |  * | 
 |  * All functions are stubs to RegOpenKeyEx32W where all the | 
 |  * magic happens.  | 
 |  * | 
 |  * Callpath: | 
 |  * RegOpenKey16 -> RegOpenKey32A -> RegOpenKeyEx32A \ | 
 |  *                                  RegOpenKey32W   -> RegOpenKeyEx32W  | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegOpenKeyEx32W [ADVAPI32.150] | 
 |  * Opens the specified key | 
 |  * | 
 |  * Unlike RegCreateKeyEx, this does not create the key if it does not exist. | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey       [I] Handle of open key | 
 |  *    lpszSubKey [I] Name of subkey to open | 
 |  *    dwReserved [I] Reserved - must be zero | 
 |  *    samDesired [I] Security access mask | 
 |  *    retkey     [O] Address of handle of open key | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | DWORD WINAPI RegOpenKeyEx32W( HKEY hkey, LPCWSTR lpszSubKey, DWORD dwReserved, | 
 |                               REGSAM samDesired, LPHKEY retkey ) | 
 | { | 
 | 	LPKEYSTRUCT	lpNextKey,lpxkey; | 
 | 	LPWSTR		*wps; | 
 | 	int		wpc,i; | 
 |  | 
 |     TRACE(reg,"(0x%x,%s,%ld,%lx,%p)\n", hkey,debugstr_w(lpszSubKey),dwReserved, | 
 |           samDesired,retkey); | 
 |  | 
 |     lpNextKey = lookup_hkey( hkey ); | 
 |     if (!lpNextKey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     if (!lpszSubKey || !*lpszSubKey) { | 
 |         /* Either NULL or pointer to empty string, so return a new handle | 
 |            to the original hkey */ | 
 |         currenthandle += 2; | 
 |         add_handle(currenthandle,lpNextKey,samDesired); | 
 |         *retkey=currenthandle; | 
 |         return ERROR_SUCCESS; | 
 |     } | 
 |  | 
 |     if (lpszSubKey[0] == '\\') { | 
 |         WARN(reg,"Subkey %s must not begin with backslash.\n",debugstr_w(lpszSubKey)); | 
 |         return ERROR_BAD_PATHNAME; | 
 |     } | 
 |  | 
 | 	split_keypath(lpszSubKey,&wps,&wpc); | 
 | 	i = 0; | 
 | 	while ((i<wpc) && (wps[i][0]=='\0')) i++; | 
 | 	lpxkey = lpNextKey; | 
 |  | 
 |     while (wps[i]) { | 
 |         lpxkey=lpNextKey->nextsub; | 
 |         while (lpxkey) { | 
 |             if (!lstrcmpi32W(wps[i],lpxkey->keyname)) { | 
 |                 break; | 
 |             } | 
 |             lpxkey=lpxkey->next; | 
 |         } | 
 |  | 
 |         if (!lpxkey) { | 
 |             TRACE(reg,"Could not find subkey %s\n",debugstr_w(wps[i])); | 
 |             FREE_KEY_PATH; | 
 |             return ERROR_FILE_NOT_FOUND; | 
 |         } | 
 |         i++; | 
 |         lpNextKey = lpxkey; | 
 |     } | 
 |  | 
 |     currenthandle += 2; | 
 |     add_handle(currenthandle,lpxkey,samDesired); | 
 |     *retkey = currenthandle; | 
 |     TRACE(reg,"  Returning %x\n", currenthandle); | 
 |     FREE_KEY_PATH; | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegOpenKeyEx32A [ADVAPI32.149] | 
 |  */ | 
 | DWORD WINAPI RegOpenKeyEx32A( HKEY hkey, LPCSTR lpszSubKey, DWORD dwReserved, | 
 |                               REGSAM samDesired, LPHKEY retkey ) | 
 | { | 
 |     LPWSTR lpszSubKeyW = strdupA2W(lpszSubKey); | 
 |     DWORD ret; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%ld,%lx,%p)\n",hkey,debugstr_a(lpszSubKey),dwReserved, | 
 |           samDesired,retkey); | 
 |     ret = RegOpenKeyEx32W( hkey, lpszSubKeyW, dwReserved, samDesired, retkey ); | 
 |     free(lpszSubKeyW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegOpenKey32W [ADVAPI32.151] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey       [I] Handle of open key | 
 |  *    lpszSubKey [I] Address of name of subkey to open | 
 |  *    retkey     [O] Address of handle of open key | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | DWORD WINAPI RegOpenKey32W( HKEY hkey, LPCWSTR lpszSubKey, LPHKEY retkey ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%p)\n",hkey,debugstr_w(lpszSubKey),retkey); | 
 |     return RegOpenKeyEx32W( hkey, lpszSubKey, 0, KEY_ALL_ACCESS, retkey ); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegOpenKey32A [ADVAPI32.148] | 
 |  */ | 
 | DWORD WINAPI RegOpenKey32A( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey ) | 
 | { | 
 |     DWORD ret; | 
 |     LPWSTR lpszSubKeyW = strdupA2W(lpszSubKey); | 
 |     TRACE(reg,"(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey); | 
 |     ret =  RegOpenKey32W( hkey, lpszSubKeyW, retkey ); | 
 |     free(lpszSubKeyW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegOpenKey16 [SHELL.1] [KERNEL.217] | 
 |  */ | 
 | DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey); | 
 |     return RegOpenKey32A( hkey, lpszSubKey, retkey ); | 
 | } | 
 |  | 
 |  | 
 | /*  | 
 |  * Create keys | 
 |  *  | 
 |  * All those functions convert their respective  | 
 |  * arguments and call RegCreateKeyExW at the end. | 
 |  * | 
 |  * We stay away from the Ex functions as long as possible because there are | 
 |  * differences in the return values | 
 |  * | 
 |  * Callpath: | 
 |  *                                      RegCreateKeyEx32A \ | 
 |  * RegCreateKey16 -> RegCreateKey32A -> RegCreateKey32W   -> RegCreateKeyEx32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegCreateKeyEx32W [ADVAPI32.131] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey         [I] Handle of an open key | 
 |  *    lpszSubKey   [I] Address of subkey name | 
 |  *    dwReserved   [I] Reserved - must be 0 | 
 |  *    lpszClass    [I] Address of class string | 
 |  *    fdwOptions   [I] Special options flag | 
 |  *    samDesired   [I] Desired security access | 
 |  *    lpSecAttribs [I] Address of key security structure | 
 |  *    retkey       [O] Address of buffer for opened handle | 
 |  *    lpDispos     [O] Receives REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY | 
 |  */ | 
 | DWORD WINAPI RegCreateKeyEx32W( HKEY hkey, LPCWSTR lpszSubKey,  | 
 |                                 DWORD dwReserved, LPWSTR lpszClass,  | 
 |                                 DWORD fdwOptions, REGSAM samDesired, | 
 |                                 LPSECURITY_ATTRIBUTES lpSecAttribs,  | 
 |                                 LPHKEY retkey, LPDWORD lpDispos ) | 
 | { | 
 | 	LPKEYSTRUCT	*lplpPrevKey,lpNextKey,lpxkey; | 
 | 	LPWSTR		*wps; | 
 | 	int		wpc,i; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%ld,%s,%lx,%lx,%p,%p,%p)\n", hkey, | 
 | 		debugstr_w(lpszSubKey), dwReserved, debugstr_w(lpszClass), | 
 | 		fdwOptions, samDesired, lpSecAttribs, retkey, lpDispos); | 
 |  | 
 |     lpNextKey = lookup_hkey(hkey); | 
 |     if (!lpNextKey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     /* Check for valid options */ | 
 |     switch(fdwOptions) { | 
 |         case REG_OPTION_NON_VOLATILE: | 
 |         case REG_OPTION_VOLATILE: | 
 |         case REG_OPTION_BACKUP_RESTORE: | 
 |             break; | 
 |         default: | 
 |             return ERROR_INVALID_PARAMETER; | 
 |     } | 
 |  | 
 |     /* Sam has to be a combination of the following */ | 
 |     if (!(samDesired &  | 
 |           (KEY_ALL_ACCESS | KEY_CREATE_LINK | KEY_CREATE_SUB_KEY |  | 
 |            KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_NOTIFY | | 
 |            KEY_QUERY_VALUE | KEY_READ | KEY_SET_VALUE | KEY_WRITE))) | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 | 	if (!lpszSubKey || !*lpszSubKey) { | 
 |                 currenthandle += 2; | 
 | 		add_handle(currenthandle,lpNextKey,samDesired); | 
 | 		*retkey=currenthandle; | 
 |                 TRACE(reg, "Returning %x\n", currenthandle); | 
 | 		lpNextKey->flags|=REG_OPTION_TAINTED; | 
 | 		return ERROR_SUCCESS; | 
 | 	} | 
 |  | 
 |     if (lpszSubKey[0] == '\\') { | 
 |         WARN(reg,"Subkey %s must not begin with backslash.\n",debugstr_w(lpszSubKey)); | 
 |         return ERROR_BAD_PATHNAME; | 
 |     } | 
 |  | 
 | 	split_keypath(lpszSubKey,&wps,&wpc); | 
 | 	i 	= 0; | 
 | 	while ((i<wpc) && (wps[i][0]=='\0')) i++; | 
 | 	lpxkey	= lpNextKey; | 
 | 	while (wps[i]) { | 
 | 		lpxkey=lpNextKey->nextsub; | 
 | 		while (lpxkey) { | 
 | 			if (!lstrcmpi32W(wps[i],lpxkey->keyname)) | 
 | 				break; | 
 | 			lpxkey=lpxkey->next; | 
 | 		} | 
 | 		if (!lpxkey) | 
 | 			break; | 
 | 		i++; | 
 | 		lpNextKey	= lpxkey; | 
 | 	} | 
 | 	if (lpxkey) { | 
 |                 currenthandle += 2; | 
 | 		add_handle(currenthandle,lpxkey,samDesired); | 
 | 		lpxkey->flags  |= REG_OPTION_TAINTED; | 
 | 		*retkey		= currenthandle; | 
 |                 TRACE(reg, "Returning %x\n", currenthandle); | 
 | 		if (lpDispos) | 
 | 			*lpDispos	= REG_OPENED_EXISTING_KEY; | 
 | 		FREE_KEY_PATH; | 
 | 		return ERROR_SUCCESS; | 
 | 	} | 
 |  | 
 | 	/* Good.  Now the hard part */ | 
 | 	while (wps[i]) { | 
 | 		lplpPrevKey	= &(lpNextKey->nextsub); | 
 | 		lpxkey		= *lplpPrevKey; | 
 | 		while (lpxkey) { | 
 | 			lplpPrevKey	= &(lpxkey->next); | 
 | 			lpxkey		= *lplpPrevKey; | 
 | 		} | 
 | 		*lplpPrevKey=malloc(sizeof(KEYSTRUCT)); | 
 | 		if (!*lplpPrevKey) { | 
 | 			FREE_KEY_PATH; | 
 |                         TRACE(reg, "Returning OUTOFMEMORY\n"); | 
 | 			return ERROR_OUTOFMEMORY; | 
 | 		} | 
 | 		memset(*lplpPrevKey,'\0',sizeof(KEYSTRUCT)); | 
 |                 TRACE(reg,"Adding %s\n", debugstr_w(wps[i])); | 
 | 		(*lplpPrevKey)->keyname	= strdupW(wps[i]); | 
 | 		(*lplpPrevKey)->next	= NULL; | 
 | 		(*lplpPrevKey)->nextsub	= NULL; | 
 | 		(*lplpPrevKey)->values	= NULL; | 
 | 		(*lplpPrevKey)->nrofvalues = 0; | 
 | 		(*lplpPrevKey)->flags 	= REG_OPTION_TAINTED; | 
 | 		if (lpszClass) | 
 | 			(*lplpPrevKey)->class = strdupW(lpszClass); | 
 | 		else | 
 | 			(*lplpPrevKey)->class = NULL; | 
 | 		lpNextKey	= *lplpPrevKey; | 
 | 		i++; | 
 | 	} | 
 |         currenthandle += 2; | 
 | 	add_handle(currenthandle,lpNextKey,samDesired); | 
 |  | 
 | 	/*FIXME: flag handling correct? */ | 
 | 	lpNextKey->flags= fdwOptions |REG_OPTION_TAINTED; | 
 | 	if (lpszClass) | 
 | 		lpNextKey->class = strdupW(lpszClass); | 
 | 	else | 
 | 		lpNextKey->class = NULL; | 
 | 	*retkey		= currenthandle; | 
 |         TRACE(reg, "Returning %x\n", currenthandle); | 
 | 	if (lpDispos) | 
 | 		*lpDispos	= REG_CREATED_NEW_KEY; | 
 | 	FREE_KEY_PATH; | 
 | 	return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegCreateKeyEx32A [ADVAPI32.130] | 
 |  */ | 
 | DWORD WINAPI RegCreateKeyEx32A( HKEY hkey, LPCSTR lpszSubKey, DWORD dwReserved, | 
 |                                 LPSTR lpszClass, DWORD fdwOptions,  | 
 |                                 REGSAM samDesired,  | 
 |                                 LPSECURITY_ATTRIBUTES lpSecAttribs,  | 
 |                                 LPHKEY retkey, LPDWORD lpDispos ) | 
 | { | 
 |     LPWSTR lpszSubKeyW, lpszClassW; | 
 |     DWORD  ret; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%ld,%s,%lx,%lx,%p,%p,%p)\n",hkey,debugstr_a(lpszSubKey), | 
 |           dwReserved,debugstr_a(lpszClass),fdwOptions,samDesired,lpSecAttribs, | 
 |           retkey,lpDispos); | 
 |  | 
 |     lpszSubKeyW = lpszSubKey?strdupA2W(lpszSubKey):NULL; | 
 |     lpszClassW = lpszClass?strdupA2W(lpszClass):NULL; | 
 |  | 
 |     ret = RegCreateKeyEx32W( hkey, lpszSubKeyW, dwReserved, lpszClassW,  | 
 |                              fdwOptions, samDesired, lpSecAttribs, retkey,  | 
 |                              lpDispos ); | 
 |  | 
 |     if(lpszSubKeyW) free(lpszSubKeyW); | 
 |     if(lpszClassW) free(lpszClassW); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegCreateKey32W [ADVAPI32.132] | 
 |  */ | 
 | DWORD WINAPI RegCreateKey32W( HKEY hkey, LPCWSTR lpszSubKey, LPHKEY retkey ) | 
 | { | 
 |     DWORD junk; | 
 |     LPKEYSTRUCT	lpNextKey; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%p)\n", hkey,debugstr_w(lpszSubKey),retkey); | 
 |  | 
 |     /* This check is here because the return value is different than the | 
 |        one from the Ex functions */ | 
 |     lpNextKey = lookup_hkey(hkey); | 
 |     if (!lpNextKey) | 
 |         return ERROR_BADKEY; | 
 |  | 
 |     return RegCreateKeyEx32W( hkey, lpszSubKey, 0, NULL,  | 
 |                               REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, | 
 |                               retkey, &junk); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegCreateKey32A [ADVAPI32.129] | 
 |  */ | 
 | DWORD WINAPI RegCreateKey32A( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey ) | 
 | { | 
 |     DWORD ret; | 
 |     LPWSTR lpszSubKeyW; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey); | 
 |     lpszSubKeyW = lpszSubKey?strdupA2W(lpszSubKey):NULL; | 
 |     ret = RegCreateKey32W( hkey, lpszSubKeyW, retkey ); | 
 |     if(lpszSubKeyW) free(lpszSubKeyW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegCreateKey16 [SHELL.2] [KERNEL.218] | 
 |  */ | 
 | DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey); | 
 |     return RegCreateKey32A( hkey, lpszSubKey, retkey ); | 
 | } | 
 |  | 
 |  | 
 | /*  | 
 |  * Query Value Functions | 
 |  * Win32 differs between keynames and valuenames.  | 
 |  * multiple values may belong to one key, the special value | 
 |  * with name NULL is the default value used by the win31 | 
 |  * compat functions. | 
 |  * | 
 |  * Callpath: | 
 |  * RegQueryValue16 -> RegQueryValue32A -> RegQueryValueEx32A \ | 
 |  *                                        RegQueryValue32W -> RegQueryValueEx32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryValueEx32W [ADVAPI32.158] | 
 |  * Retrieves type and data for a specified name associated with an open key | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey          [I]   Handle of key to query | 
 |  *    lpValueName   [I]   Name of value to query | 
 |  *    lpdwReserved  [I]   Reserved - must be NULL | 
 |  *    lpdwType      [O]   Address of buffer for value type.  If NULL, the type | 
 |  *                        is not required. | 
 |  *    lpbData       [O]   Address of data buffer.  If NULL, the actual data is | 
 |  *                        not required. | 
 |  *    lpcbData      [I/O] Address of data buffer size | 
 |  * | 
 |  * RETURNS  | 
 |  *    ERROR_SUCCESS:   Success | 
 |  *    ERROR_MORE_DATA: The specified buffer is not big enough to hold the data | 
 |  */ | 
 | DWORD WINAPI RegQueryValueEx32W( HKEY hkey, LPWSTR lpValueName, | 
 |                                  LPDWORD lpdwReserved, LPDWORD lpdwType, | 
 |                                  LPBYTE lpbData, LPDWORD lpcbData ) | 
 | { | 
 | 	LPKEYSTRUCT	lpkey; | 
 | 	int		i; | 
 |  | 
 |     TRACE(reg,"(0x%x,%s,%p,%p,%p,%ld)\n", hkey, debugstr_w(lpValueName), | 
 |           lpdwReserved, lpdwType, lpbData, lpcbData?*lpcbData:0); | 
 |  | 
 |     lpkey = lookup_hkey(hkey); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     /* Reserved must be NULL (at least for now) */ | 
 |     if (lpdwReserved) | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 |     /* An empty name string is equivalent to NULL */ | 
 |     if (lpValueName && !*lpValueName) | 
 |         lpValueName = NULL; | 
 |  | 
 | 	if (lpValueName==NULL) { | 
 |                 /* Use key's unnamed or default value, if any */ | 
 | 		for (i=0;i<lpkey->nrofvalues;i++) | 
 | 			if (lpkey->values[i].name==NULL) | 
 | 				break; | 
 | 	} else { | 
 |                 /* Search for the key name */ | 
 | 		for (i=0;i<lpkey->nrofvalues;i++) | 
 | 			if (	lpkey->values[i].name && | 
 | 				!lstrcmpi32W(lpValueName,lpkey->values[i].name) | 
 | 			) | 
 | 				break; | 
 | 	} | 
 |  | 
 | 	if (i==lpkey->nrofvalues) { | 
 |                 TRACE(reg,"Key not found\n"); | 
 | 		if (lpValueName==NULL) { | 
 |                         /* Empty keyname not found */ | 
 | 			if (lpbData) { | 
 | 				*(WCHAR*)lpbData = 0; | 
 | 				*lpcbData	= 2; | 
 | 			} | 
 | 			if (lpdwType) | 
 | 				*lpdwType	= REG_SZ; | 
 |                         TRACE(reg, "Returning an empty string\n"); | 
 | 			return ERROR_SUCCESS; | 
 | 		} | 
 | 		return ERROR_FILE_NOT_FOUND; | 
 | 	} | 
 |  | 
 |     if (lpdwType) | 
 |         *lpdwType = lpkey->values[i].type; | 
 |  | 
 |     if (lpbData==NULL) { | 
 |         /* Data is not required */ | 
 |         if (lpcbData==NULL) { | 
 |             /* And data size is not required */ | 
 |             /* So all that is returned is the type (set above) */ | 
 |             return ERROR_SUCCESS; | 
 |         } | 
 |         /* Set the size required and return success */ | 
 |         *lpcbData = lpkey->values[i].len; | 
 |         return ERROR_SUCCESS; | 
 |     } | 
 |  | 
 |     if (*lpcbData<lpkey->values[i].len) { | 
 |         /* The size was specified, but the data is too big for it */ | 
 |         /* Instead of setting it to NULL, fill in with as much as possible */ | 
 |         /* But the docs do not specify how to handle the lpbData here */ | 
 |         /* *(WCHAR*)lpbData= 0; */ | 
 |         memcpy(lpbData,lpkey->values[i].data,*lpcbData); | 
 |         *lpcbData = lpkey->values[i].len; | 
 |         return ERROR_MORE_DATA; | 
 |     } | 
 |  | 
 |     memcpy(lpbData,lpkey->values[i].data,lpkey->values[i].len); | 
 |  | 
 |     /* Extra debugging output */ | 
 |     if (lpdwType) { | 
 |         switch(*lpdwType){ | 
 |             case REG_SZ: | 
 |                 TRACE(reg," Data(sz)=%s\n",debugstr_w((LPCWSTR)lpbData)); | 
 |                 break; | 
 |             case REG_DWORD: | 
 |                 TRACE(reg," Data(dword)=%lx\n", (DWORD)*lpbData); | 
 |                 break; | 
 |             case REG_BINARY: | 
 |                 TRACE(reg," Data(binary)\n"); | 
 |                 /* Is there a way of printing this in readable form? */ | 
 |                 break; | 
 |             default: | 
 |                 TRACE(reg, "Unknown data type %ld\n", *lpdwType); | 
 |         } /* switch */ | 
 |     } /* if */ | 
 |  | 
 |     /* Set the actual size */ | 
 |     *lpcbData = lpkey->values[i].len; | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryValue32W [ADVAPI32.159] | 
 |  */ | 
 | DWORD WINAPI RegQueryValue32W( HKEY hkey, LPWSTR lpszSubKey, LPWSTR lpszData, | 
 |                                LPDWORD lpcbData ) | 
 | { | 
 | 	HKEY	xhkey; | 
 | 	DWORD	ret,lpdwType; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%p,%ld)\n",hkey,debugstr_w(lpszSubKey),lpszData, | 
 |           lpcbData?*lpcbData:0); | 
 |  | 
 |     /* Only open subkey, if we really do descend */ | 
 |     if (lpszSubKey && *lpszSubKey) { | 
 |         ret = RegOpenKey32W( hkey, lpszSubKey, &xhkey ); | 
 |         if (ret != ERROR_SUCCESS) { | 
 |             WARN(reg, "Could not open %s\n", debugstr_w(lpszSubKey)); | 
 |             return ret; | 
 |         } | 
 |     } else | 
 |         xhkey = hkey; | 
 |  | 
 |     lpdwType = REG_SZ; | 
 |     ret = RegQueryValueEx32W( xhkey, NULL, NULL, &lpdwType, (LPBYTE)lpszData, | 
 |                               lpcbData ); | 
 |     if (xhkey != hkey) | 
 |         RegCloseKey(xhkey); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryValueEx32A [ADVAPI32.157] | 
 |  */ | 
 | DWORD WINAPI RegQueryValueEx32A( HKEY hkey, LPSTR lpszValueName, | 
 |                                  LPDWORD lpdwReserved, LPDWORD lpdwType, | 
 |                                  LPBYTE lpbData, LPDWORD lpcbData ) | 
 | { | 
 | 	LPWSTR	lpszValueNameW; | 
 | 	LPBYTE	buf; | 
 | 	DWORD	ret,myxlen; | 
 | 	DWORD	*mylen; | 
 | 	DWORD	type; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%p,%p,%p,%ld)\n", hkey,debugstr_a(lpszValueName), | 
 |           lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0); | 
 |  | 
 |     lpszValueNameW = lpszValueName?strdupA2W(lpszValueName):NULL; | 
 |  | 
 |     /* Why would this be set? It is just an output */ | 
 |     if (lpdwType) | 
 |         type = *lpdwType; | 
 |  | 
 | 	if (lpbData) { | 
 | 		myxlen  = 0; | 
 | 		mylen	= &myxlen; | 
 | 		buf	= xmalloc(4); | 
 |                 /* Only get the size for now */ | 
 | 		ret = RegQueryValueEx32W( hkey, lpszValueNameW, lpdwReserved, | 
 |                                           &type, buf, mylen ); | 
 | 		free(buf); | 
 | 		if (ret==ERROR_MORE_DATA) { | 
 | 			buf	= (LPBYTE)xmalloc(*mylen); | 
 | 		} else { | 
 | 			buf	= (LPBYTE)xmalloc(2*(*lpcbData)); | 
 | 			myxlen  = 2*(*lpcbData); | 
 | 		} | 
 | 	} else { | 
 | 		/* Data is not required */ | 
 | 		buf=NULL; | 
 | 		if (lpcbData) { | 
 | 			myxlen	= *lpcbData*2; | 
 | 			mylen	= &myxlen; | 
 | 		} else | 
 | 			mylen	= NULL; | 
 | 	} | 
 |  | 
 |         /* Now get the data */ | 
 | 	ret = RegQueryValueEx32W( hkey, lpszValueNameW, lpdwReserved, &type, | 
 |                                   buf, mylen ); | 
 | 	if (lpdwType)  | 
 | 		*lpdwType=type; | 
 |  | 
 | 	if (ret==ERROR_SUCCESS) { | 
 | 		if (buf) { | 
 | 			if (UNICONVMASK & (1<<(type))) { | 
 | 				/* convert UNICODE to ASCII */ | 
 | 				lstrcpyWtoA(lpbData,(LPWSTR)buf); | 
 | 				*lpcbData	= myxlen/2; | 
 | 			} else { | 
 | 				if (myxlen>*lpcbData) | 
 | 					ret	= ERROR_MORE_DATA; | 
 | 				else | 
 | 					memcpy(lpbData,buf,myxlen); | 
 |  | 
 | 				*lpcbData	= myxlen; | 
 | 			} | 
 | 		} else { | 
 | 			if ((UNICONVMASK & (1<<(type))) && lpcbData) | 
 | 				*lpcbData	= myxlen/2; | 
 | 		} | 
 | 	} else { | 
 | 		if ((UNICONVMASK & (1<<(type))) && lpcbData) | 
 | 			*lpcbData	= myxlen/2; | 
 | 	} | 
 |  | 
 |     if(buf) free(buf); | 
 |     if(lpszValueNameW) free(lpszValueNameW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryValueEx16 [KERNEL.225] | 
 |  */ | 
 | DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPSTR lpszValueName, | 
 |                                 LPDWORD lpdwReserved, LPDWORD lpdwType, | 
 |                                 LPBYTE lpbData, LPDWORD lpcbData ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%p,%p,%p,%ld)\n",hkey,debugstr_a(lpszValueName), | 
 |           lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0); | 
 |     return RegQueryValueEx32A( hkey, lpszValueName, lpdwReserved, lpdwType, | 
 |                                lpbData, lpcbData ); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryValue32A [ADVAPI32.156] | 
 |  */ | 
 | DWORD WINAPI RegQueryValue32A( HKEY hkey, LPSTR lpszSubKey, LPSTR lpszData, | 
 |                                LPDWORD lpcbData ) | 
 | { | 
 |     HKEY xhkey; | 
 |     DWORD ret, dwType; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%p,%ld)\n",hkey,debugstr_a(lpszSubKey),lpszData, | 
 |           lpcbData?*lpcbData:0); | 
 |  | 
 |     if (lpszSubKey && *lpszSubKey) { | 
 |         ret = RegOpenKey16( hkey, lpszSubKey, &xhkey ); | 
 |         if( ret != ERROR_SUCCESS ) | 
 |             return ret; | 
 |     } else | 
 |         xhkey = hkey; | 
 |  | 
 |     dwType = REG_SZ; | 
 |     ret = RegQueryValueEx32A( xhkey, NULL,NULL, &dwType, (LPBYTE)lpszData, | 
 |                               lpcbData ); | 
 |     if( xhkey != hkey ) | 
 |         RegCloseKey( xhkey ); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryValue16 [SHELL.6] [KERNEL.224] | 
 |  * | 
 |  * NOTES | 
 |  *    Is this HACK still applicable? | 
 |  * | 
 |  * HACK | 
 |  *    The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just | 
 |  *    mask out the high 16 bit.  This (not so much incidently) hopefully fixes | 
 |  *    Aldus FH4) | 
 |  */ | 
 | DWORD WINAPI RegQueryValue16( HKEY hkey, LPSTR lpszSubKey, LPSTR lpszData, | 
 |                               LPDWORD lpcbData ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%p,%ld)\n",hkey,debugstr_a(lpszSubKey),lpszData, | 
 |           lpcbData?*lpcbData:0); | 
 |  | 
 |     if (lpcbData) | 
 |         *lpcbData &= 0xFFFF; | 
 |     return RegQueryValue32A(hkey,lpszSubKey,lpszData,lpcbData); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Setting values of Registry keys | 
 |  * | 
 |  * Callpath: | 
 |  * RegSetValue16 -> RegSetValue32A -> RegSetValueEx32A \ | 
 |  *                                    RegSetValue32W   -> RegSetValueEx32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetValueEx32W [ADVAPI32.170] | 
 |  * Sets the data and type of a value under a register key | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey          [I] Handle of key to set value for | 
 |  *    lpszValueName [I] Name of value to set | 
 |  *    dwReserved    [I] Reserved - must be zero | 
 |  *    dwType        [I] Flag for value type | 
 |  *    lpbData       [I] Address of value data | 
 |  *    cbData        [I] Size of value data | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | DWORD WINAPI RegSetValueEx32W( HKEY hkey, LPWSTR lpszValueName,  | 
 |                                DWORD dwReserved, DWORD dwType, LPBYTE lpbData, | 
 |                                DWORD cbData) | 
 | { | 
 |     LPKEYSTRUCT lpkey; | 
 |     int i; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%ld,%ld,%p,%ld)\n", hkey, debugstr_w(lpszValueName), | 
 |           dwReserved, dwType, lpbData, cbData); | 
 |  | 
 |     switch (dwType) { | 
 |         case REG_SZ: | 
 |             TRACE(reg," Data(sz)=%s\n", debugstr_w((LPCWSTR)lpbData)); | 
 |             break; | 
 |         case REG_BINARY: | 
 |             TRACE(reg," Data(binary)\n"); | 
 |             break; | 
 |         case REG_DWORD: | 
 |             TRACE(reg," Data(dword)=%lx\n", (DWORD)lpbData); | 
 |             break; | 
 |         default: | 
 |             TRACE(reg,"Unknown type: %ld\n", dwType); | 
 |     } | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 | 	lpkey->flags |= REG_OPTION_TAINTED; | 
 |  | 
 | 	if (lpszValueName==NULL) { | 
 |              /* Sets type and name for key's unnamed or default value */ | 
 | 		for (i=0;i<lpkey->nrofvalues;i++) | 
 | 			if (lpkey->values[i].name==NULL) | 
 | 				break; | 
 | 	} else { | 
 | 		for (i=0;i<lpkey->nrofvalues;i++) | 
 | 			if (	lpkey->values[i].name && | 
 | 				!lstrcmpi32W(lpszValueName,lpkey->values[i].name) | 
 | 			) | 
 | 				break; | 
 | 	} | 
 | 	if (i==lpkey->nrofvalues) { | 
 | 		lpkey->values = (LPKEYVALUE)xrealloc( | 
 | 					lpkey->values, | 
 | 					(lpkey->nrofvalues+1)*sizeof(KEYVALUE) | 
 | 				); | 
 | 		lpkey->nrofvalues++; | 
 | 		memset(lpkey->values+i,'\0',sizeof(KEYVALUE)); | 
 | 	} | 
 | 	if (lpkey->values[i].name==NULL) { | 
 | 		if (lpszValueName) | 
 | 			lpkey->values[i].name = strdupW(lpszValueName); | 
 | 		else | 
 | 			lpkey->values[i].name = NULL; | 
 | 	} | 
 | 	lpkey->values[i].len	= cbData; | 
 | 	lpkey->values[i].type	= dwType; | 
 | 	if (lpkey->values[i].data !=NULL) | 
 | 		free(lpkey->values[i].data); | 
 | 	lpkey->values[i].data	= (LPBYTE)xmalloc(cbData); | 
 | 	lpkey->values[i].lastmodified = time(NULL); | 
 | 	memcpy(lpkey->values[i].data,lpbData,cbData); | 
 | 	return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetValueEx32A [ADVAPI32.169] | 
 |  * | 
 |  */ | 
 | DWORD WINAPI RegSetValueEx32A( HKEY hkey, LPSTR lpszValueName, | 
 |                                DWORD dwReserved, DWORD dwType, LPBYTE lpbData, | 
 |                                DWORD cbData ) | 
 | { | 
 | 	LPBYTE	buf; | 
 | 	LPWSTR	lpszValueNameW; | 
 | 	DWORD	ret; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%ld,%ld,%p,%ld)\n",hkey,debugstr_a(lpszValueName), | 
 |           dwReserved,dwType,lpbData,cbData); | 
 |  | 
 | 	if ((1<<dwType) & UNICONVMASK) { | 
 | 		buf=(LPBYTE)strdupA2W(lpbData); | 
 | 		cbData=2*strlen(lpbData)+2; | 
 | 	} else | 
 | 		buf=lpbData; | 
 | 	if (lpszValueName) | 
 | 		lpszValueNameW = strdupA2W(lpszValueName); | 
 | 	else | 
 | 		lpszValueNameW = NULL; | 
 | 	ret=RegSetValueEx32W(hkey,lpszValueNameW,dwReserved,dwType,buf,cbData); | 
 | 	if (lpszValueNameW) | 
 | 		free(lpszValueNameW); | 
 | 	if (buf!=lpbData) | 
 | 		free(buf); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetValueEx16 [KERNEL.226] | 
 |  */ | 
 | DWORD WINAPI RegSetValueEx16( HKEY hkey, LPSTR lpszValueName, DWORD dwReserved, | 
 |                               DWORD dwType, LPBYTE lpbData, DWORD cbData ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%ld,%ld,%p,%ld)\n",hkey,debugstr_a(lpszValueName), | 
 |           dwReserved,dwType,lpbData,cbData); | 
 |     return RegSetValueEx32A( hkey, lpszValueName, dwReserved, dwType, lpbData, | 
 |                              cbData ); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetValue32W	[ADVAPI32.171] | 
 |  */ | 
 | DWORD WINAPI RegSetValue32W( HKEY hkey, LPCWSTR lpszSubKey, DWORD dwType, | 
 |                              LPCWSTR lpszData, DWORD cbData ) | 
 | { | 
 | 	HKEY	xhkey; | 
 | 	DWORD	ret; | 
 |  | 
 | 	TRACE(reg,"(%x,%s,%ld,%s,%ld)\n", | 
 | 		hkey,debugstr_w(lpszSubKey),dwType,debugstr_w(lpszData),cbData | 
 | 	); | 
 | 	if (lpszSubKey && *lpszSubKey) { | 
 | 		ret=RegCreateKey32W(hkey,lpszSubKey,&xhkey); | 
 | 		if (ret!=ERROR_SUCCESS) | 
 | 			return ret; | 
 | 	} else | 
 | 		xhkey=hkey; | 
 | 	if (dwType!=REG_SZ) { | 
 | 		TRACE(reg,"dwType=%ld - Changing to REG_SZ\n",dwType); | 
 | 		dwType=REG_SZ; | 
 | 	} | 
 | 	if (cbData!=2*lstrlen32W(lpszData)+2) { | 
 | 		TRACE(reg,"Len=%ld != strlen(%s)+1=%d!\n", | 
 | 			cbData,debugstr_w(lpszData),2*lstrlen32W(lpszData)+2 | 
 | 		); | 
 | 		cbData=2*lstrlen32W(lpszData)+2; | 
 | 	} | 
 | 	ret=RegSetValueEx32W(xhkey,NULL,0,dwType,(LPBYTE)lpszData,cbData); | 
 | 	if (hkey!=xhkey) | 
 | 		RegCloseKey(xhkey); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetValue32A [ADVAPI32.168] | 
 |  * | 
 |  */ | 
 | DWORD WINAPI RegSetValue32A( HKEY hkey, LPCSTR lpszSubKey, DWORD dwType, | 
 |                              LPCSTR lpszData, DWORD cbData ) | 
 | { | 
 | 	DWORD	ret; | 
 | 	HKEY	xhkey; | 
 |  | 
 | 	TRACE(reg,"(%x,%s,%ld,%s,%ld)\n",hkey,lpszSubKey,dwType,lpszData,cbData); | 
 | 	if (lpszSubKey && *lpszSubKey) { | 
 | 		ret=RegCreateKey16(hkey,lpszSubKey,&xhkey); | 
 | 		if (ret!=ERROR_SUCCESS) | 
 | 			return ret; | 
 | 	} else | 
 | 		xhkey=hkey; | 
 |  | 
 | 	if (dwType!=REG_SZ) { | 
 | 		TRACE(reg,"dwType=%ld!\n",dwType); | 
 | 		dwType=REG_SZ; | 
 | 	} | 
 | 	if (cbData!=strlen(lpszData)+1) | 
 | 		cbData=strlen(lpszData)+1; | 
 | 	ret=RegSetValueEx32A(xhkey,NULL,0,dwType,(LPBYTE)lpszData,cbData); | 
 | 	if (xhkey!=hkey) | 
 | 		RegCloseKey(xhkey); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetValue16 [KERNEL.221] [SHELL.5] | 
 |  */ | 
 | DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR lpszSubKey, DWORD	dwType, | 
 |                             LPCSTR lpszData, DWORD cbData ) | 
 | { | 
 |     TRACE(reg,"(%x,%s,%ld,%s,%ld)\n",hkey,debugstr_a(lpszSubKey),dwType, | 
 |           debugstr_a(lpszData),cbData); | 
 |     return RegSetValue32A(hkey,lpszSubKey,dwType,lpszData,cbData); | 
 | } | 
 |  | 
 |  | 
 | /*  | 
 |  * Key Enumeration | 
 |  * | 
 |  * Callpath: | 
 |  * RegEnumKey16 -> RegEnumKey32A -> RegEnumKeyEx32A \ | 
 |  *                                  RegEnumKey32W   -> RegEnumKeyEx32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumKeyEx32W [ADVAPI32.139] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey         [I] Handle to key to enumerate | 
 |  *    iSubKey      [I] Index of subkey to enumerate | 
 |  *    lpszName     [O] Buffer for subkey name | 
 |  *    lpcchName    [O] Size of subkey buffer | 
 |  *    lpdwReserved [I] Reserved | 
 |  *    lpszClass    [O] Buffer for class string | 
 |  *    lpcchClass   [O] Size of class buffer | 
 |  *    ft           [O] Time key last written to | 
 |  */ | 
 | DWORD WINAPI RegEnumKeyEx32W( HKEY hkey, DWORD iSubkey, LPWSTR lpszName, | 
 |                               LPDWORD lpcchName, LPDWORD lpdwReserved, | 
 |                               LPWSTR lpszClass, LPDWORD lpcchClass,  | 
 |                               FILETIME *ft ) | 
 | { | 
 | 	LPKEYSTRUCT	lpkey,lpxkey; | 
 |  | 
 |     TRACE(reg,"(%x,%ld,%p,%ld,%p,%p,%p,%p)\n",hkey,iSubkey,lpszName, | 
 |           *lpcchName,lpdwReserved,lpszClass,lpcchClass,ft); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 | 	if (!lpkey->nextsub) | 
 | 		return ERROR_NO_MORE_ITEMS; | 
 | 	lpxkey=lpkey->nextsub; | 
 |  | 
 |     /* Traverse the subkeys */ | 
 | 	while (iSubkey && lpxkey) { | 
 | 		iSubkey--; | 
 | 		lpxkey=lpxkey->next; | 
 | 	} | 
 |  | 
 | 	if (iSubkey || !lpxkey) | 
 | 		return ERROR_NO_MORE_ITEMS; | 
 | 	if (lstrlen32W(lpxkey->keyname)+1>*lpcchName) | 
 | 		return ERROR_MORE_DATA; | 
 | 	memcpy(lpszName,lpxkey->keyname,lstrlen32W(lpxkey->keyname)*2+2); | 
 |  | 
 |         if (*lpcchName) | 
 |             *lpcchName = lstrlen32W(lpszName); | 
 |  | 
 | 	if (lpszClass) { | 
 | 		/* FIXME: what should we write into it? */ | 
 | 		*lpszClass	= 0; | 
 | 		*lpcchClass	= 2; | 
 | 	} | 
 | 	return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumKey32W [ADVAPI32.140] | 
 |  */ | 
 | DWORD WINAPI RegEnumKey32W( HKEY hkey, DWORD iSubkey, LPWSTR lpszName,  | 
 |                             DWORD lpcchName ) | 
 | { | 
 |     FILETIME	ft; | 
 |  | 
 |     TRACE(reg,"(%x,%ld,%p,%ld)\n",hkey,iSubkey,lpszName,lpcchName); | 
 |     return RegEnumKeyEx32W(hkey,iSubkey,lpszName,&lpcchName,NULL,NULL,NULL,&ft); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumKeyEx32A [ADVAPI32.138] | 
 |  */ | 
 | DWORD WINAPI RegEnumKeyEx32A( HKEY hkey, DWORD iSubkey, LPSTR lpszName, | 
 |                               LPDWORD lpcchName, LPDWORD lpdwReserved,  | 
 |                               LPSTR lpszClass, LPDWORD lpcchClass,  | 
 |                               FILETIME *ft ) | 
 | { | 
 | 	DWORD	ret,lpcchNameW,lpcchClassW; | 
 | 	LPWSTR	lpszNameW,lpszClassW; | 
 |  | 
 |  | 
 | 	TRACE(reg,"(%x,%ld,%p,%ld,%p,%p,%p,%p)\n", | 
 | 		hkey,iSubkey,lpszName,*lpcchName,lpdwReserved,lpszClass,lpcchClass,ft | 
 | 	); | 
 | 	if (lpszName) { | 
 | 		lpszNameW	= (LPWSTR)xmalloc(*lpcchName*2); | 
 | 		lpcchNameW	= *lpcchName; | 
 | 	} else { | 
 | 		lpszNameW	= NULL; | 
 | 		lpcchNameW 	= 0; | 
 | 	} | 
 | 	if (lpszClass) { | 
 | 		lpszClassW		= (LPWSTR)xmalloc(*lpcchClass*2); | 
 | 		lpcchClassW	= *lpcchClass; | 
 | 	} else { | 
 | 		lpszClassW	=0; | 
 | 		lpcchClassW=0; | 
 | 	} | 
 | 	ret=RegEnumKeyEx32W( | 
 | 		hkey, | 
 | 		iSubkey, | 
 | 		lpszNameW, | 
 | 		&lpcchNameW, | 
 | 		lpdwReserved, | 
 | 		lpszClassW, | 
 | 		&lpcchClassW, | 
 | 		ft | 
 | 	); | 
 | 	if (ret==ERROR_SUCCESS) { | 
 | 		lstrcpyWtoA(lpszName,lpszNameW); | 
 | 		*lpcchName=strlen(lpszName); | 
 | 		if (lpszClassW) { | 
 | 			lstrcpyWtoA(lpszClass,lpszClassW); | 
 | 			*lpcchClass=strlen(lpszClass); | 
 | 		} | 
 | 	} | 
 | 	if (lpszNameW) | 
 | 		free(lpszNameW); | 
 | 	if (lpszClassW) | 
 | 		free(lpszClassW); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumKey32A [ADVAPI32.137] | 
 |  */ | 
 | DWORD WINAPI RegEnumKey32A( HKEY hkey, DWORD iSubkey, LPSTR lpszName, | 
 |                             DWORD lpcchName ) | 
 | { | 
 |     FILETIME	ft; | 
 |  | 
 |     TRACE(reg,"(%x,%ld,%p,%ld)\n",hkey,iSubkey,lpszName,lpcchName); | 
 |     return RegEnumKeyEx32A( hkey, iSubkey, lpszName, &lpcchName, NULL, NULL,  | 
 |                             NULL, &ft ); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumKey16 [SHELL.7] [KERNEL.216] | 
 |  */ | 
 | DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD iSubkey, LPSTR lpszName, | 
 |                            DWORD lpcchName ) | 
 | { | 
 |     TRACE(reg,"(%x,%ld,%p,%ld)\n",hkey,iSubkey,lpszName,lpcchName); | 
 |     return RegEnumKey32A( hkey, iSubkey, lpszName, lpcchName); | 
 | } | 
 |  | 
 |  | 
 | /*  | 
 |  * Enumerate Registry Values | 
 |  * | 
 |  * Callpath: | 
 |  * RegEnumValue16 -> RegEnumValue32A -> RegEnumValue32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumValue32W [ADVAPI32.142] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey        [I] Handle to key to query | 
 |  *    iValue      [I] Index of value to query | 
 |  *    lpszValue   [O] Value string | 
 |  *    lpcchValue  [O] Size of value buffer | 
 |  *    lpdReserved [I] Reserved | 
 |  *    lpdwType    [O] Type code | 
 |  *    lpbData     [O] Value data | 
 |  *    lpcbData    [O] Size of data buffer | 
 |  * | 
 |  * Note:  wide character functions that take and/or return "character counts" | 
 |  *  use TCHAR (that is unsigned short or char) not byte counts. | 
 |  */ | 
 | DWORD WINAPI RegEnumValue32W( HKEY hkey, DWORD iValue, LPWSTR lpszValue, | 
 |                               LPDWORD lpcchValue, LPDWORD lpdReserved, | 
 |                               LPDWORD lpdwType, LPBYTE lpbData,  | 
 |                               LPDWORD lpcbData ) | 
 | { | 
 | 	LPKEYSTRUCT	lpkey; | 
 | 	LPKEYVALUE	val; | 
 |  | 
 |     TRACE(reg,"(%x,%ld,%p,%p,%p,%p,%p,%p)\n",hkey,iValue,debugstr_w(lpszValue), | 
 |           lpcchValue,lpdReserved,lpdwType,lpbData,lpcbData); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     if (lpkey->nrofvalues <= iValue) | 
 |         return ERROR_NO_MORE_ITEMS; | 
 |  | 
 |     /* FIXME: Should this be lpkey->values + iValue * sizeof(KEYVALUE)? */ | 
 |     val = lpkey->values + iValue; | 
 |  | 
 | 	if (val->name) { | 
 | 	        if (lstrlen32W(val->name)+1>*lpcchValue) { | 
 | 			*lpcchValue = lstrlen32W(val->name)+1; | 
 | 			return ERROR_MORE_DATA; | 
 | 		} | 
 | 		memcpy(lpszValue,val->name,2*lstrlen32W(val->name)+2); | 
 | 		*lpcchValue=lstrlen32W(val->name); | 
 | 	} else { | 
 | 		*lpszValue	= 0; | 
 | 		*lpcchValue	= 0; | 
 | 	} | 
 |  | 
 |     /* Can be NULL if the type code is not required */ | 
 |     if (lpdwType) | 
 |         *lpdwType = val->type; | 
 |  | 
 | 	if (lpbData) { | 
 | 		if (val->len>*lpcbData) | 
 | 			return ERROR_MORE_DATA; | 
 | 		memcpy(lpbData,val->data,val->len); | 
 | 		*lpcbData = val->len; | 
 | 	} | 
 | 	return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumValue32A [ADVAPI32.141] | 
 |  */ | 
 | DWORD WINAPI RegEnumValue32A( HKEY hkey, DWORD iValue, LPSTR lpszValue, | 
 |                               LPDWORD lpcchValue, LPDWORD lpdReserved, | 
 |                               LPDWORD lpdwType, LPBYTE lpbData,  | 
 |                               LPDWORD lpcbData ) | 
 | { | 
 | 	LPWSTR	lpszValueW; | 
 | 	LPBYTE	lpbDataW; | 
 | 	DWORD	ret,lpcbDataW; | 
 | 	DWORD dwType; | 
 |  | 
 | 	TRACE(reg,"(%x,%ld,%p,%p,%p,%p,%p,%p)\n",hkey,iValue,lpszValue,lpcchValue, | 
 | 		lpdReserved,lpdwType,lpbData,lpcbData); | 
 |  | 
 | 	lpszValueW = (LPWSTR)xmalloc(*lpcchValue*2); | 
 | 	if (lpbData) { | 
 | 		lpbDataW = (LPBYTE)xmalloc(*lpcbData*2); | 
 | 		lpcbDataW = *lpcbData; | 
 | 	} else | 
 | 		lpbDataW = NULL; | 
 |  | 
 | 	ret = RegEnumValue32W( hkey, iValue, lpszValueW, lpcchValue,  | 
 | 				lpdReserved, &dwType, lpbDataW, &lpcbDataW ); | 
 |  | 
 | 	if (lpdwType) | 
 | 		*lpdwType = dwType; | 
 |  | 
 | 	if (ret==ERROR_SUCCESS) { | 
 | 		lstrcpyWtoA(lpszValue,lpszValueW); | 
 | 		if (lpbData) { | 
 | 			if ((1<<dwType) & UNICONVMASK) { | 
 | 				lstrcpyWtoA(lpbData,(LPWSTR)lpbDataW); | 
 | 			} else { | 
 | 				if (lpcbDataW > *lpcbData) | 
 | 					ret	= ERROR_MORE_DATA; | 
 | 				else | 
 | 					memcpy(lpbData,lpbDataW,lpcbDataW); | 
 | 			} | 
 | 			*lpcbData = lpcbDataW; | 
 | 		} | 
 | 	} | 
 |     if (lpbDataW) free(lpbDataW); | 
 |     if (lpszValueW) free(lpszValueW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegEnumValue16 [KERNEL.223] | 
 |  */ | 
 | DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD iValue, LPSTR lpszValue,  | 
 |                              LPDWORD lpcchValue, LPDWORD lpdReserved,  | 
 |                              LPDWORD lpdwType, LPBYTE lpbData,  | 
 |                              LPDWORD lpcbData ) | 
 | { | 
 |     TRACE(reg,"(%x,%ld,%p,%p,%p,%p,%p,%p)\n",hkey,iValue,lpszValue,lpcchValue, | 
 |           lpdReserved,lpdwType,lpbData,lpcbData); | 
 |     return RegEnumValue32A( hkey, iValue, lpszValue, lpcchValue, lpdReserved,  | 
 |                             lpdwType, lpbData, lpcbData ); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegCloseKey [SHELL.3] [KERNEL.220] [ADVAPI32.126] | 
 |  * Releases the handle of the specified key | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey [I] Handle of key to close | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | DWORD WINAPI RegCloseKey( HKEY hkey ) | 
 | { | 
 |     TRACE(reg,"(%x)\n",hkey); | 
 |  | 
 |     /* The standard handles are allowed to succeed, even though they are not | 
 |        closed */ | 
 |     if (is_standard_hkey(hkey)) | 
 |         return ERROR_SUCCESS; | 
 |  | 
 |     return remove_handle(hkey); | 
 | } | 
 |  | 
 |  | 
 | /*  | 
 |  * Delete registry key | 
 |  * | 
 |  * Callpath: | 
 |  * RegDeleteKey16 -> RegDeleteKey32A -> RegDeleteKey32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegDeleteKey32W [ADVAPI32.134] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey       [I] Handle to open key | 
 |  *    lpszSubKey [I] Name of subkey to delete | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | DWORD WINAPI RegDeleteKey32W( HKEY hkey, LPWSTR lpszSubKey ) | 
 | { | 
 | 	LPKEYSTRUCT	*lplpPrevKey,lpNextKey,lpxkey; | 
 | 	LPWSTR		*wps; | 
 | 	int		wpc,i; | 
 |  | 
 |     TRACE(reg,"(%x,%s)\n",hkey,debugstr_w(lpszSubKey)); | 
 |  | 
 |     lpNextKey = lookup_hkey(hkey); | 
 |     if (!lpNextKey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     /* Subkey param cannot be NULL */ | 
 |     if (!lpszSubKey || !*lpszSubKey) | 
 |         return ERROR_BADKEY; | 
 |  | 
 |     /* We need to know the previous key in the hier. */ | 
 | 	split_keypath(lpszSubKey,&wps,&wpc); | 
 | 	i 	= 0; | 
 | 	lpxkey	= lpNextKey; | 
 | 	while (i<wpc-1) { | 
 | 		lpxkey=lpNextKey->nextsub; | 
 | 		while (lpxkey) { | 
 | 			TRACE(reg, "  Scanning [%s]\n", | 
 | 				     debugstr_w(lpxkey->keyname)); | 
 | 			if (!lstrcmpi32W(wps[i],lpxkey->keyname)) | 
 | 				break; | 
 | 			lpxkey=lpxkey->next; | 
 | 		} | 
 | 		if (!lpxkey) { | 
 | 			FREE_KEY_PATH; | 
 | 			TRACE(reg, "  Not found.\n"); | 
 | 			/* not found is success */ | 
 | 			return ERROR_SUCCESS; | 
 | 		} | 
 | 		i++; | 
 | 		lpNextKey	= lpxkey; | 
 | 	} | 
 | 	lpxkey	= lpNextKey->nextsub; | 
 | 	lplpPrevKey = &(lpNextKey->nextsub); | 
 | 	while (lpxkey) { | 
 | 		TRACE(reg, "  Scanning [%s]\n", | 
 | 			     debugstr_w(lpxkey->keyname)); | 
 | 		if (!lstrcmpi32W(wps[i],lpxkey->keyname)) | 
 | 			break; | 
 | 		lplpPrevKey	= &(lpxkey->next); | 
 | 		lpxkey		= lpxkey->next; | 
 | 	} | 
 |  | 
 | 	if (!lpxkey) { | 
 | 		FREE_KEY_PATH; | 
 | 		WARN(reg , "  Not found.\n"); | 
 | 		return ERROR_FILE_NOT_FOUND; | 
 | 	} | 
 |  | 
 | 	if (lpxkey->nextsub) { | 
 | 		FREE_KEY_PATH; | 
 | 		WARN(reg , "  Not empty.\n"); | 
 | 		return ERROR_CANTWRITE; | 
 | 	} | 
 | 	*lplpPrevKey	= lpxkey->next; | 
 | 	free(lpxkey->keyname); | 
 | 	if (lpxkey->class) | 
 | 		free(lpxkey->class); | 
 | 	if (lpxkey->values) | 
 | 		free(lpxkey->values); | 
 | 	free(lpxkey); | 
 | 	FREE_KEY_PATH; | 
 | 	TRACE(reg, "  Done.\n"); | 
 | 	return	ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegDeleteKey32A [ADVAPI32.133] | 
 |  */ | 
 | DWORD WINAPI RegDeleteKey32A( HKEY hkey, LPCSTR lpszSubKey ) | 
 | { | 
 |     LPWSTR lpszSubKeyW; | 
 |     DWORD  ret; | 
 |  | 
 |     TRACE(reg,"(%x,%s)\n",hkey,debugstr_a(lpszSubKey)); | 
 |     lpszSubKeyW = lpszSubKey?strdupA2W(lpszSubKey):NULL; | 
 |     ret = RegDeleteKey32W( hkey, lpszSubKeyW ); | 
 |     if(lpszSubKeyW) free(lpszSubKeyW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegDeleteKey16 [SHELL.4] [KERNEL.219] | 
 |  */ | 
 | DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR lpszSubKey ) | 
 | { | 
 |     TRACE(reg,"(%x,%s)\n",hkey,debugstr_a(lpszSubKey)); | 
 |     return RegDeleteKey32A( hkey, lpszSubKey ); | 
 | } | 
 |  | 
 |  | 
 | /*  | 
 |  * Delete registry value | 
 |  * | 
 |  * Callpath: | 
 |  * RegDeleteValue16 -> RegDeleteValue32A -> RegDeleteValue32W | 
 |  */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegDeleteValue32W [ADVAPI32.136] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey      [I] | 
 |  *    lpszValue [I] | 
 |  * | 
 |  * RETURNS | 
 |  */ | 
 | DWORD WINAPI RegDeleteValue32W( HKEY hkey, LPWSTR lpszValue ) | 
 | { | 
 | 	DWORD		i; | 
 | 	LPKEYSTRUCT	lpkey; | 
 | 	LPKEYVALUE	val; | 
 |  | 
 |     TRACE(reg,"(%x,%s)\n",hkey,debugstr_w(lpszValue)); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 | 	if (lpszValue) { | 
 | 		for (i=0;i<lpkey->nrofvalues;i++) | 
 | 			if (	lpkey->values[i].name && | 
 | 				!lstrcmpi32W(lpkey->values[i].name,lpszValue) | 
 | 			) | 
 | 				break; | 
 | 	} else { | 
 | 		for (i=0;i<lpkey->nrofvalues;i++) | 
 | 			if (lpkey->values[i].name==NULL) | 
 | 				break; | 
 | 	} | 
 |  | 
 |     if (i == lpkey->nrofvalues) | 
 |         return ERROR_FILE_NOT_FOUND; | 
 |  | 
 | 	val	= lpkey->values+i; | 
 | 	if (val->name) free(val->name); | 
 | 	if (val->data) free(val->data); | 
 | 	memcpy(	 | 
 | 		lpkey->values+i, | 
 | 		lpkey->values+i+1, | 
 | 		sizeof(KEYVALUE)*(lpkey->nrofvalues-i-1) | 
 | 	); | 
 | 	lpkey->values	= (LPKEYVALUE)xrealloc( | 
 | 				lpkey->values, | 
 | 				(lpkey->nrofvalues-1)*sizeof(KEYVALUE) | 
 | 			); | 
 | 	lpkey->nrofvalues--; | 
 | 	return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegDeleteValue32A [ADVAPI32.135] | 
 |  */ | 
 | DWORD WINAPI RegDeleteValue32A( HKEY hkey, LPSTR lpszValue ) | 
 | { | 
 |     LPWSTR lpszValueW; | 
 |     DWORD  ret; | 
 |  | 
 |     TRACE(reg, "(%x,%s)\n",hkey,debugstr_a(lpszValue)); | 
 |     lpszValueW = lpszValue?strdupA2W(lpszValue):NULL; | 
 |     ret = RegDeleteValue32W( hkey, lpszValueW ); | 
 |     if(lpszValueW) free(lpszValueW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegDeleteValue16 [KERNEL.222] | 
 |  */ | 
 | DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR lpszValue ) | 
 | { | 
 |     TRACE(reg,"(%x,%s)\n", hkey,debugstr_a(lpszValue)); | 
 |     return RegDeleteValue32A( hkey, lpszValue ); | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegFlushKey [KERNEL.227] [ADVAPI32.143] | 
 |  * Writes key to registry | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey [I] Handle of key to write | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | DWORD WINAPI RegFlushKey( HKEY hkey ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |     BOOL32 ret; | 
 |  | 
 |     TRACE(reg, "(%x)\n", hkey); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     ERR(reg, "What is the correct filename?\n"); | 
 |  | 
 |     ret = _savereg( lpkey, "foo.bar", TRUE); | 
 |  | 
 |     if( ret ) { | 
 |         return ERROR_SUCCESS; | 
 |     } else | 
 |         return ERROR_UNKNOWN;  /* FIXME */ | 
 | } | 
 |  | 
 |  | 
 | /* FIXME: lpcchXXXX ... is this counting in WCHARS or in BYTEs ?? */ | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryInfoKey32W [ADVAPI32.153] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey                   [I] Handle to key to query | 
 |  *    lpszClass              [O] Buffer for class string | 
 |  *    lpcchClass             [O] Size of class string buffer | 
 |  *    lpdwReserved           [I] Reserved | 
 |  *    lpcSubKeys             [I] Buffer for number of subkeys | 
 |  *    lpcchMaxSubKey         [O] Buffer for longest subkey name length | 
 |  *    lpcchMaxClass          [O] Buffer for longest class string length | 
 |  *    lpcValues              [O] Buffer for number of value entries | 
 |  *    lpcchMaxValueName      [O] Buffer for longest value name length | 
 |  *    lpccbMaxValueData      [O] Buffer for longest value data length | 
 |  *    lpcbSecurityDescriptor [O] Buffer for security descriptor length | 
 |  *    ft | 
 |  * - win95 allows lpszClass to be valid and lpcchClass to be NULL  | 
 |  * - winnt returns ERROR_INVALID_PARAMETER if lpszClass is valid and | 
 |  *   lpcchClass is NULL | 
 |  * - both allow lpszClass to be NULL and lpcchClass to be NULL  | 
 |  * (it's hard to test validity, so test !NULL instead) | 
 |  */ | 
 | DWORD WINAPI RegQueryInfoKey32W( HKEY hkey, LPWSTR lpszClass,  | 
 |                                  LPDWORD lpcchClass, LPDWORD lpdwReserved, | 
 |                                  LPDWORD lpcSubKeys, LPDWORD lpcchMaxSubkey, | 
 |                                  LPDWORD lpcchMaxClass, LPDWORD lpcValues, | 
 |                                  LPDWORD lpcchMaxValueName,  | 
 |                                  LPDWORD lpccbMaxValueData,  | 
 |                                  LPDWORD lpcbSecurityDescriptor, FILETIME *ft ) | 
 | { | 
 | 	LPKEYSTRUCT	lpkey,lpxkey; | 
 | 	int		nrofkeys,maxsubkey,maxclass,maxvname,maxvdata; | 
 | 	int		i; | 
 |  | 
 | 	TRACE(reg,"(%x,%p,...)\n",hkey,lpszClass); | 
 | 	lpkey = lookup_hkey(hkey); | 
 | 	if (!lpkey) | 
 | 		return ERROR_INVALID_HANDLE; | 
 | 	if (lpszClass) { | 
 |    	        if (VERSION_GetVersion() == NT40 && lpcchClass == NULL) { | 
 | 		    return ERROR_INVALID_PARAMETER; | 
 | 		} | 
 | 		/* either lpcchClass is valid or this is win95 and lpcchClass | 
 | 		   could be invalid */ | 
 | 		if (lpkey->class) { | 
 | 		        DWORD classLen = lstrlen32W(lpkey->class); | 
 |  | 
 | 			if (lpcchClass && classLen+1>*lpcchClass) { | 
 | 				*lpcchClass=classLen+1; | 
 | 				return ERROR_MORE_DATA; | 
 | 			} | 
 | 			if (lpcchClass) | 
 | 			    *lpcchClass=classLen; | 
 | 			memcpy(lpszClass,lpkey->class, classLen*2 + 2); | 
 | 		} else { | 
 | 			*lpszClass	= 0; | 
 | 			if (lpcchClass) | 
 | 			    *lpcchClass	= 0; | 
 | 		} | 
 | 	} else { | 
 | 		if (lpcchClass) | 
 | 			*lpcchClass	= lstrlen32W(lpkey->class); | 
 | 	} | 
 | 	lpxkey=lpkey->nextsub; | 
 | 	nrofkeys=maxsubkey=maxclass=maxvname=maxvdata=0; | 
 | 	while (lpxkey) { | 
 | 		nrofkeys++; | 
 | 		if (lstrlen32W(lpxkey->keyname)>maxsubkey) | 
 | 			maxsubkey=lstrlen32W(lpxkey->keyname); | 
 | 		if (lpxkey->class && lstrlen32W(lpxkey->class)>maxclass) | 
 | 			maxclass=lstrlen32W(lpxkey->class); | 
 | 		lpxkey=lpxkey->next; | 
 | 	} | 
 | 	for (i=0;i<lpkey->nrofvalues;i++) { | 
 | 		LPKEYVALUE	val=lpkey->values+i; | 
 |  | 
 | 		if (val->name && lstrlen32W(val->name)>maxvname) | 
 | 			maxvname=lstrlen32W(val->name); | 
 | 		if (val->len>maxvdata) | 
 | 			maxvdata=val->len; | 
 | 	} | 
 | 	if (!maxclass) maxclass	= 1; | 
 | 	if (!maxvname) maxvname	= 1; | 
 | 	if (lpcValues) | 
 | 		*lpcValues	= lpkey->nrofvalues; | 
 | 	if (lpcSubKeys) | 
 | 		*lpcSubKeys	= nrofkeys; | 
 | 	if (lpcchMaxSubkey) | 
 | 		*lpcchMaxSubkey	= maxsubkey; | 
 | 	if (lpcchMaxClass) | 
 | 		*lpcchMaxClass	= maxclass; | 
 | 	if (lpcchMaxValueName) | 
 | 		*lpcchMaxValueName= maxvname; | 
 | 	if (lpccbMaxValueData) | 
 | 		*lpccbMaxValueData= maxvdata; | 
 | 	return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegQueryInfoKey32A [ADVAPI32.152] | 
 |  */ | 
 | DWORD WINAPI RegQueryInfoKey32A( HKEY hkey, LPSTR lpszClass, LPDWORD lpcchClass, | 
 |                                  LPDWORD lpdwReserved, LPDWORD lpcSubKeys, | 
 |                                  LPDWORD lpcchMaxSubkey, LPDWORD lpcchMaxClass, | 
 |                                  LPDWORD lpcValues, LPDWORD lpcchMaxValueName, | 
 |                                  LPDWORD lpccbMaxValueData,  | 
 |                                  LPDWORD lpcbSecurityDescriptor, FILETIME *ft ) | 
 | { | 
 | 	LPWSTR		lpszClassW = NULL; | 
 | 	DWORD		ret; | 
 |  | 
 | 	TRACE(reg,"(%x,%p,%p......)\n",hkey, lpszClass, lpcchClass); | 
 | 	if (lpszClass) { | 
 | 		if (lpcchClass) { | 
 | 		    lpszClassW  = (LPWSTR)xmalloc((*lpcchClass) * 2); | 
 | 		} else if (VERSION_GetVersion() == WIN95) { | 
 | 		    /* win95  allows lpcchClass to be null */ | 
 | 		    /* we don't know how big lpszClass is, would  | 
 | 		       MAX_PATHNAME_LEN be the correct default? */ | 
 | 		    lpszClassW  = (LPWSTR)xmalloc(MAX_PATHNAME_LEN*2);  | 
 | 		} | 
 |  | 
 | 	} else | 
 | 		lpszClassW  = NULL; | 
 | 	ret=RegQueryInfoKey32W( | 
 | 		hkey, | 
 | 		lpszClassW, | 
 | 		lpcchClass, | 
 | 		lpdwReserved, | 
 | 		lpcSubKeys, | 
 | 		lpcchMaxSubkey, | 
 | 		lpcchMaxClass, | 
 | 		lpcValues, | 
 | 		lpcchMaxValueName, | 
 | 		lpccbMaxValueData, | 
 | 		lpcbSecurityDescriptor, | 
 | 		ft | 
 | 	); | 
 | 	if (ret==ERROR_SUCCESS && lpszClass) | 
 | 		lstrcpyWtoA(lpszClass,lpszClassW); | 
 | 	if (lpszClassW) | 
 | 		free(lpszClassW); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegConnectRegistry32W [ADVAPI32.128] | 
 |  * | 
 |  * PARAMS | 
 |  *    lpMachineName [I] Address of name of remote computer | 
 |  *    hHey          [I] Predefined registry handle | 
 |  *    phkResult     [I] Address of buffer for remote registry handle | 
 |  */ | 
 | LONG WINAPI RegConnectRegistry32W( LPCWSTR lpMachineName, HKEY hKey,  | 
 |                                    LPHKEY phkResult ) | 
 | { | 
 |     TRACE(reg,"(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult); | 
 |  | 
 |     if (!lpMachineName || !*lpMachineName) { | 
 |         /* Use the local machine name */ | 
 |         return RegOpenKey16( hKey, "", phkResult ); | 
 |     } | 
 |  | 
 |     FIXME(reg,"Cannot connect to %s\n",debugstr_w(lpMachineName)); | 
 |     return ERROR_BAD_NETPATH; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegConnectRegistry32A [ADVAPI32.127] | 
 |  */ | 
 | LONG WINAPI RegConnectRegistry32A( LPCSTR machine, HKEY hkey, LPHKEY reskey ) | 
 | { | 
 |     DWORD ret; | 
 |     LPWSTR machineW = strdupA2W(machine); | 
 |     ret = RegConnectRegistry32W( machineW, hkey, reskey ); | 
 |     free(machineW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegGetKeySecurity [ADVAPI32.144] | 
 |  * Retrieves a copy of security descriptor protecting the registry key | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey                   [I]   Open handle of key to set | 
 |  *    SecurityInformation    [I]   Descriptor contents | 
 |  *    pSecurityDescriptor    [O]   Address of descriptor for key | 
 |  *    lpcbSecurityDescriptor [I/O] Address of size of buffer and description | 
 |  * | 
 |  * RETURNS | 
 |  *    Success: ERROR_SUCCESS | 
 |  *    Failure: Error code | 
 |  */ | 
 | LONG WINAPI RegGetKeySecurity( HKEY hkey,  | 
 |                                SECURITY_INFORMATION SecurityInformation, | 
 |                                LPSECURITY_DESCRIPTOR pSecurityDescriptor, | 
 |                                LPDWORD lpcbSecurityDescriptor ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |  | 
 |     TRACE(reg,"(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor, | 
 |           lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     /* FIXME: Check for valid SecurityInformation values */ | 
 |  | 
 |     if (*lpcbSecurityDescriptor < sizeof(*pSecurityDescriptor)) | 
 |         return ERROR_INSUFFICIENT_BUFFER; | 
 |  | 
 |     FIXME(reg, "(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation, | 
 |           pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0); | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegLoadKey32W [ADVAPI32.???] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey       [I] Handle of open key | 
 |  *    lpszSubKey [I] Address of name of subkey | 
 |  *    lpszFile   [I] Address of filename for registry information | 
 |  */ | 
 | LONG WINAPI RegLoadKey32W( HKEY hkey, LPCWSTR lpszSubKey, LPCWSTR lpszFile ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |     TRACE(reg,"(%x,%s,%s)\n",hkey,debugstr_w(lpszSubKey),debugstr_w(lpszFile)); | 
 |  | 
 |     /* Do this check before the hkey check */ | 
 |     if (!lpszSubKey || !*lpszSubKey || !lpszFile || !*lpszFile) | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     FIXME(reg,"(%x,%s,%s): stub\n",hkey,debugstr_w(lpszSubKey), | 
 |           debugstr_w(lpszFile)); | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegLoadKey32A [ADVAPI32.???] | 
 |  */ | 
 | LONG WINAPI RegLoadKey32A( HKEY hkey, LPCSTR lpszSubKey, LPCSTR lpszFile ) | 
 | { | 
 |     LONG ret; | 
 |     LPWSTR lpszSubKeyW = strdupA2W(lpszSubKey); | 
 |     LPWSTR lpszFileW = strdupA2W(lpszFile); | 
 |     ret = RegLoadKey32W( hkey, lpszSubKeyW, lpszFileW ); | 
 |     if(lpszFileW) free(lpszFileW); | 
 |     if(lpszSubKeyW) free(lpszSubKeyW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegNotifyChangeKeyValue [ADVAPI32.???] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey            [I] Handle of key to watch | 
 |  *    fWatchSubTree   [I] Flag for subkey notification | 
 |  *    fdwNotifyFilter [I] Changes to be reported | 
 |  *    hEvent          [I] Handle of signaled event | 
 |  *    fAsync          [I] Flag for asynchronous reporting | 
 |  */ | 
 | LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL32 fWatchSubTree,  | 
 |                                      DWORD fdwNotifyFilter, HANDLE32 hEvent, | 
 |                                      BOOL32 fAsync ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |     TRACE(reg,"(%x,%i,%ld,%x,%i)\n",hkey,fWatchSubTree,fdwNotifyFilter, | 
 |           hEvent,fAsync); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     FIXME(reg,"(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter, | 
 |           hEvent,fAsync); | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegUnLoadKey32W [ADVAPI32.173] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey     [I] Handle of open key | 
 |  *    lpSubKey [I] Address of name of subkey to unload | 
 |  */ | 
 | LONG WINAPI RegUnLoadKey32W( HKEY hkey, LPCWSTR lpSubKey ) | 
 | { | 
 |     FIXME(reg,"(%x,%s): stub\n",hkey, debugstr_w(lpSubKey)); | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegUnLoadKey32A [ADVAPI32.172] | 
 |  */ | 
 | LONG WINAPI RegUnLoadKey32A( HKEY hkey, LPCSTR lpSubKey ) | 
 | { | 
 |     LONG ret; | 
 |     LPWSTR lpSubKeyW = strdupA2W(lpSubKey); | 
 |     ret = RegUnLoadKey32W( hkey, lpSubKeyW ); | 
 |     if(lpSubKeyW) free(lpSubKeyW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSetKeySecurity [ADVAPI32.167] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey          [I] Open handle of key to set | 
 |  *    SecurityInfo  [I] Descriptor contents | 
 |  *    pSecurityDesc [I] Address of descriptor for key | 
 |  */ | 
 | LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo, | 
 |                                LPSECURITY_DESCRIPTOR pSecurityDesc ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |  | 
 |     TRACE(reg,"(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc); | 
 |  | 
 |     /* It seems to perform this check before the hkey check */ | 
 |     if ((SecurityInfo & OWNER_SECURITY_INFORMATION) || | 
 |         (SecurityInfo & GROUP_SECURITY_INFORMATION) || | 
 |         (SecurityInfo & DACL_SECURITY_INFORMATION) || | 
 |         (SecurityInfo & SACL_SECURITY_INFORMATION)) { | 
 |         /* Param OK */ | 
 |     } else | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 |     if (!pSecurityDesc) | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     FIXME(reg,":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc); | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSaveKey32W [ADVAPI32.166] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey   [I] Handle of key where save begins | 
 |  *    lpFile [I] Address of filename to save to | 
 |  *    sa     [I] Address of security structure | 
 |  */ | 
 | LONG WINAPI RegSaveKey32W( HKEY hkey, LPCWSTR lpFile,  | 
 |                            LPSECURITY_ATTRIBUTES sa ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |  | 
 |     TRACE(reg, "(%x,%s,%p)\n", hkey, debugstr_w(lpFile), sa); | 
 |  | 
 |     /* It appears to do this check before the hkey check */ | 
 |     if (!lpFile || !*lpFile) | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     FIXME(reg, "(%x,%s,%p): stub\n", hkey, debugstr_w(lpFile), sa); | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegSaveKey32A [ADVAPI32.165] | 
 |  */ | 
 | LONG WINAPI RegSaveKey32A( HKEY hkey, LPCSTR lpFile,  | 
 |                            LPSECURITY_ATTRIBUTES sa ) | 
 | { | 
 |     LONG ret; | 
 |     LPWSTR lpFileW = strdupA2W(lpFile); | 
 |     ret = RegSaveKey32W( hkey, lpFileW, sa ); | 
 |     free(lpFileW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegRestoreKey32W [ADVAPI32.164] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey    [I] Handle of key where restore begins | 
 |  *    lpFile  [I] Address of filename containing saved tree | 
 |  *    dwFlags [I] Optional flags | 
 |  */ | 
 | LONG WINAPI RegRestoreKey32W( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |  | 
 |     TRACE(reg, "(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags); | 
 |  | 
 |     /* It seems to do this check before the hkey check */ | 
 |     if (!lpFile || !*lpFile) | 
 |         return ERROR_INVALID_PARAMETER; | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     FIXME(reg,"(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags); | 
 |  | 
 |     /* Check for file existence */ | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegRestoreKey32A [ADVAPI32.163] | 
 |  */ | 
 | LONG WINAPI RegRestoreKey32A( HKEY hkey, LPCSTR lpFile, DWORD dwFlags ) | 
 | { | 
 |     LONG ret; | 
 |     LPWSTR lpFileW = strdupA2W(lpFile); | 
 |     ret = RegRestoreKey32W( hkey, lpFileW, dwFlags ); | 
 |     if(lpFileW) free(lpFileW); | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegReplaceKey32W [ADVAPI32.162] | 
 |  * | 
 |  * PARAMS | 
 |  *    hkey      [I] Handle of open key | 
 |  *    lpSubKey  [I] Address of name of subkey | 
 |  *    lpNewFile [I] Address of filename for file with new data | 
 |  *    lpOldFile [I] Address of filename for backup file | 
 |  */ | 
 | LONG WINAPI RegReplaceKey32W( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile, | 
 |                               LPCWSTR lpOldFile ) | 
 | { | 
 |     LPKEYSTRUCT	lpkey; | 
 |  | 
 |     TRACE(reg,"(%x,%s,%s,%s)\n",hkey,debugstr_w(lpSubKey), | 
 |           debugstr_w(lpNewFile),debugstr_w(lpOldFile)); | 
 |  | 
 |     lpkey = lookup_hkey( hkey ); | 
 |     if (!lpkey) | 
 |         return ERROR_INVALID_HANDLE; | 
 |  | 
 |     FIXME(reg, "(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),  | 
 |           debugstr_w(lpNewFile),debugstr_w(lpOldFile)); | 
 |  | 
 |     return ERROR_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * RegReplaceKey32A [ADVAPI32.161] | 
 |  */ | 
 | LONG WINAPI RegReplaceKey32A( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile, | 
 |                               LPCSTR lpOldFile ) | 
 | { | 
 |     LONG ret; | 
 |     LPWSTR lpSubKeyW = strdupA2W(lpSubKey); | 
 |     LPWSTR lpNewFileW = strdupA2W(lpNewFile); | 
 |     LPWSTR lpOldFileW = strdupA2W(lpOldFile); | 
 |     ret = RegReplaceKey32W( hkey, lpSubKeyW, lpNewFileW, lpOldFileW ); | 
 |     free(lpOldFileW); | 
 |     free(lpNewFileW); | 
 |     free(lpSubKeyW); | 
 |     return ret; | 
 | } | 
 |  |