small range fixes for OSS driver
[wine] / misc / registry.c
1 /*
2  *      Registry Functions
3  *
4  * Copyright 1996 Marcus Meissner
5  * Copyright 1998 Matthew Becker
6  * Copyright 1999 Sylvain St-Germain
7  *
8  * December 21, 1997 - Kevin Cozens
9  * Fixed bugs in the _w95_loadreg() function. Added extra information
10  * regarding the format of the Windows '95 registry files.
11  *
12  * NOTES
13  *    When changing this file, please re-run the regtest program to ensure
14  *    the conditions are handled properly.
15  *
16  * TODO
17  *    Security access
18  *    Option handling
19  *    Time for RegEnumKey*, RegQueryInfoKey*
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <sys/errno.h>
28 #include <sys/types.h>
29 #include <sys/fcntl.h>
30 #include <sys/stat.h>
31 #include <assert.h>
32 #include <time.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wine/winbase16.h"
36 #include "wine/winestring.h"
37 #include "winerror.h"
38 #include "file.h"
39 #include "heap.h"
40 #include "debugtools.h"
41 #include "xmalloc.h"
42 #include "options.h"
43 #include "winreg.h"
44 #include "winversion.h"
45
46 DECLARE_DEBUG_CHANNEL(reg)
47 DECLARE_DEBUG_CHANNEL(string)
48
49 static void REGISTRY_Init(void);
50 /* FIXME: following defines should be configured global ... */
51
52 /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */
53 #define WINE_PREFIX                 "/.wine"
54 #define SAVE_USERS_DEFAULT          "/usr/local/etc/wine.userreg"
55 #define SAVE_LOCAL_MACHINE_DEFAULT  "/usr/local/etc/wine.systemreg"
56
57 /* relative in ~user/.wine/ : */
58 #define SAVE_CURRENT_USER           "user.reg"
59 #define SAVE_LOCAL_USERS_DEFAULT    "wine.userreg"
60 #define SAVE_LOCAL_MACHINE          "system.reg"
61
62 #define KEY_REGISTRY                "Software\\The WINE team\\WINE\\Registry"
63 #define VAL_SAVEUPDATED             "SaveOnlyUpdatedKeys"
64
65 /* one value of a key */
66 typedef struct tagKEYVALUE
67 {
68     LPWSTR   name;          /* name of value (UNICODE) or NULL for win31 */
69     DWORD    type;          /* type of value */
70     DWORD    len;           /* length of data in BYTEs */
71     DWORD    lastmodified;  /* time of seconds since 1.1.1970 */
72     LPBYTE   data;          /* content, may be strings, binaries, etc. */
73 } KEYVALUE,*LPKEYVALUE;
74
75 /* a registry key */
76 typedef struct tagKEYSTRUCT
77 {
78     LPWSTR               keyname;       /* name of THIS key (UNICODE) */
79     DWORD                flags;         /* flags. */
80     LPWSTR               class;
81     /* values */
82     DWORD                nrofvalues;    /* nr of values in THIS key */
83     LPKEYVALUE           values;        /* values in THIS key */
84     /* key management pointers */
85     struct tagKEYSTRUCT *next;          /* next key on same hierarchy */
86     struct tagKEYSTRUCT *nextsub;       /* keys that hang below THIS key */
87 } KEYSTRUCT, *LPKEYSTRUCT;
88
89
90 static KEYSTRUCT        *key_classes_root=NULL; /* windows 3.1 global values */
91 static KEYSTRUCT        *key_current_user=NULL; /* user specific values */
92 static KEYSTRUCT        *key_local_machine=NULL;/* machine specific values */
93 static KEYSTRUCT        *key_users=NULL;        /* all users? */
94
95 /* dynamic, not saved */
96 static KEYSTRUCT        *key_performance_data=NULL;
97 static KEYSTRUCT        *key_current_config=NULL;
98 static KEYSTRUCT        *key_dyn_data=NULL;
99
100 /* what valuetypes do we need to convert? */
101 #define UNICONVMASK     ((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ))
102
103
104 static struct openhandle {
105         LPKEYSTRUCT     lpkey;
106         HKEY            hkey;
107         REGSAM          accessmask;
108 }  *openhandles=NULL;
109 static int      nrofopenhandles=0;
110 /* Starts after 1 because 0,1 are reserved for Win16 */
111 /* Note: Should always be even, as Win95 ADVAPI32.DLL reserves odd
112          HKEYs for remote registry access */
113 static int      currenthandle=2;
114
115
116 /*
117  * QUESTION
118  *   Are these doing the same as HEAP_strdupAtoW and HEAP_strdupWtoA?
119  *   If so, can we remove them?
120  * ANSWER
121  *   No, the memory handling functions are called very often in here, 
122  *   just replacing them by HeapAlloc(SystemHeap,...) makes registry
123  *   loading 100 times slower. -MM
124  */
125 static LPWSTR strdupA2W(LPCSTR src)
126 {
127     if(src) {
128         LPWSTR dest=xmalloc(2*strlen(src)+2);
129         lstrcpyAtoW(dest,src);
130         return dest;
131     }
132     return NULL;
133 }
134
135 static LPWSTR strdupW(LPCWSTR a) {
136         LPWSTR  b;
137         int     len;
138
139     if(a) {
140         len=sizeof(WCHAR)*(lstrlenW(a)+1);
141         b=(LPWSTR)xmalloc(len);
142         memcpy(b,a,len);
143         return b;
144     }
145     return NULL;
146 }
147
148 LPWSTR strcvtA2W(LPCSTR src, int nchars)
149
150 {
151    LPWSTR dest = xmalloc (2 * nchars + 2);
152
153    lstrcpynAtoW(dest,src,nchars+1);
154    dest[nchars] = 0;
155    return dest;
156 }
157 /*
158  * we need to convert A to W with '\0' in strings (MULTI_SZ) 
159  */
160
161 static LPWSTR  lmemcpynAtoW( LPWSTR dst, LPCSTR src, INT n )
162 {       LPWSTR p = dst;
163
164         TRACE_(reg)("\"%s\" %i\n",src, n);
165
166         while (n-- > 0) *p++ = (WCHAR)(unsigned char)*src++;
167
168         return dst;
169 }
170 static LPSTR lmemcpynWtoA( LPSTR dst, LPCWSTR src, INT n )
171 {       LPSTR p = dst;
172
173         TRACE_(string)("L\"%s\" %i\n",debugstr_w(src), n);
174
175         while (n-- > 0) *p++ = (CHAR)*src++;
176
177         return dst;
178 }
179
180 static void debug_print_value (LPBYTE lpbData, LPKEYVALUE key)
181 {
182   if (TRACE_ON(reg) && lpbData) 
183   { 
184     switch(key->type)
185     { 
186       case HEX_REG_EXPAND_SZ:
187       case HEX_REG_SZ:
188       case REG_SZ:
189         TRACE_(reg)(" Value %s, Data(sz)=%s\n", 
190           debugstr_w(key->name), 
191           debugstr_w((LPCWSTR)lpbData));
192         break;
193
194       case HEX_REG_DWORD:
195       case REG_DWORD:
196         TRACE_(reg)(" Value %s, Data(dword)=0x%08lx\n",
197           debugstr_w(key->name), 
198           (DWORD)*lpbData);
199         break;
200     
201       case HEX_REG_MULTI_SZ:
202       case REG_MULTI_SZ:
203       { 
204         int i;
205         LPCWSTR ptr = (LPCWSTR)lpbData;
206         for (i=0;ptr[0];i++)
207         { 
208           TRACE_(reg)(" Value %s, MULTI_SZ(%i=%s)\n", 
209             debugstr_w(key->name), 
210             i, 
211             debugstr_w(ptr));
212
213           ptr += lstrlenW(ptr)+1;
214         }
215       }
216       break;
217
218       case HEX_REG_NONE:
219       case HEX_REG_BINARY:
220       case HEX_REG_LINK:
221       case HEX_REG_RESOURCE_LIST:
222       case HEX_REG_FULL_RESOURCE_DESCRIPTOR:
223       case REG_NONE:
224       case REG_LINK:
225       case REG_RESOURCE_LIST:
226       case REG_FULL_RESOURCE_DESCRIPTOR:
227       case REG_BINARY:
228       { 
229         char szTemp[100];      /* 3*32 + 3 + 1 */
230         int i;
231         for ( i = 0; i < key->len ; i++)      
232         { 
233           sprintf (&(szTemp[i*3]),"%02x ", lpbData[i]);
234           if (i>=31)
235           { 
236             sprintf (&(szTemp[i*3+3]),"...");
237             break;
238           }
239         }
240         TRACE_(reg)(" Value %s, Data(raw)=(%s)\n", 
241           debugstr_w(key->name), 
242           szTemp);                  
243       }
244       break;
245     
246       default:
247         FIXME_(reg)(" Value %s, Unknown data type %ld\n", 
248           debugstr_w(key->name), 
249           key->type);
250     } /* switch */
251   } /* if */
252 }
253
254
255 /******************************************************************************
256  * is_standard_hkey [Internal]
257  * Determines if a hkey is a standard key
258  */
259 static BOOL is_standard_hkey( HKEY hkey )
260 {
261     switch(hkey) {
262         case 0x00000000:
263         case 0x00000001:
264         case HKEY_CLASSES_ROOT:
265         case HKEY_CURRENT_CONFIG:
266         case HKEY_CURRENT_USER:
267         case HKEY_LOCAL_MACHINE:
268         case HKEY_USERS:
269         case HKEY_PERFORMANCE_DATA:
270         case HKEY_DYN_DATA:
271             return TRUE;
272         default:
273             return FALSE;
274     }
275 }
276
277 /******************************************************************************
278  * add_handle [Internal]
279  */
280 static void add_handle( HKEY hkey, LPKEYSTRUCT lpkey, REGSAM accessmask )
281 {
282     int i;
283
284     TRACE_(reg)("(0x%x,%p,0x%lx)\n",hkey,lpkey,accessmask);
285     /* Check for duplicates */
286     for (i=0;i<nrofopenhandles;i++) {
287         if (openhandles[i].lpkey==lpkey) {
288             /* This is not really an error - the user is allowed to create
289                two (or more) handles to the same key */
290             /*WARN(reg, "Adding key %p twice\n",lpkey);*/
291         }
292         if (openhandles[i].hkey==hkey) {
293             WARN_(reg)("Adding handle %x twice\n",hkey);
294         }
295     }
296     openhandles=xrealloc( openhandles, 
297                           sizeof(struct openhandle)*(nrofopenhandles+1));
298
299     openhandles[i].lpkey = lpkey;
300     openhandles[i].hkey = hkey;
301     openhandles[i].accessmask = accessmask;
302     nrofopenhandles++;
303 }
304
305
306 /******************************************************************************
307  * get_handle [Internal]
308  *
309  * RETURNS
310  *    Success: Pointer to key
311  *    Failure: NULL
312  */
313 static LPKEYSTRUCT get_handle( HKEY hkey )
314 {
315     int i;
316
317     for (i=0; i<nrofopenhandles; i++)
318         if (openhandles[i].hkey == hkey)
319             return openhandles[i].lpkey;
320     WARN_(reg)("Could not find handle 0x%x\n",hkey);
321     return NULL;
322 }
323
324
325 /******************************************************************************
326  * remove_handle [Internal]
327  *
328  * PARAMS
329  *    hkey [I] Handle of key to remove
330  *
331  * RETURNS
332  *    Success: ERROR_SUCCESS
333  *    Failure: ERROR_INVALID_HANDLE
334  */
335 static DWORD remove_handle( HKEY hkey )
336 {
337     int i;
338
339     for (i=0;i<nrofopenhandles;i++)
340         if (openhandles[i].hkey==hkey)
341             break;
342
343     if (i == nrofopenhandles) {
344         WARN_(reg)("Could not find handle 0x%x\n",hkey);
345         return ERROR_INVALID_HANDLE;
346     }
347
348     memcpy( openhandles+i,
349             openhandles+i+1,
350             sizeof(struct openhandle)*(nrofopenhandles-i-1)
351     );
352     openhandles=xrealloc(openhandles,sizeof(struct openhandle)*(nrofopenhandles-1));
353     nrofopenhandles--;
354     return ERROR_SUCCESS;
355 }
356
357 /******************************************************************************
358  * lookup_hkey [Internal]
359  * 
360  * Just as the name says. Creates the root keys on demand, so we can call the
361  * Reg* functions at any time.
362  *
363  * RETURNS
364  *    Success: Pointer to key structure
365  *    Failure: NULL
366  */
367 #define ADD_ROOT_KEY(xx) \
368         xx = (LPKEYSTRUCT)xmalloc(sizeof(KEYSTRUCT));\
369         memset(xx,'\0',sizeof(KEYSTRUCT));\
370         xx->keyname= strdupA2W("<should_not_appear_anywhere>");
371
372 static LPKEYSTRUCT lookup_hkey( HKEY hkey )
373 {
374   switch (hkey) {
375   /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
376    * some programs. Do not remove those cases. -MM
377    */
378         case 0x00000000:
379   case 0x00000001:
380   case HKEY_CLASSES_ROOT: 
381   {
382     if (!key_classes_root) 
383     {
384       HKEY      cl_r_hkey;
385
386       /* calls lookup_hkey recursively, TWICE */
387       if ( RegCreateKey16(
388             HKEY_LOCAL_MACHINE,
389             "SOFTWARE\\Classes",
390             &cl_r_hkey) != ERROR_SUCCESS) 
391       {
392         ERR_(reg)("Could not create HKLM\\SOFTWARE\\Classes. This is impossible.\n");
393         exit(1);
394       }
395
396       key_classes_root = lookup_hkey(cl_r_hkey);
397     }
398     return key_classes_root;
399   }
400
401   case HKEY_CURRENT_USER:
402     if (!key_current_user) {
403       ADD_ROOT_KEY(key_current_user);
404     }
405     return key_current_user;
406
407   case HKEY_LOCAL_MACHINE:
408     if (!key_local_machine) {
409       ADD_ROOT_KEY(key_local_machine);
410       REGISTRY_Init();
411     }
412     return key_local_machine;
413
414   case HKEY_USERS:
415     if (!key_users) {
416       ADD_ROOT_KEY(key_users);
417     }
418     return key_users;
419
420   case HKEY_PERFORMANCE_DATA:
421     if (!key_performance_data) {
422       ADD_ROOT_KEY(key_performance_data);
423     }
424     return key_performance_data;
425
426   case HKEY_DYN_DATA:
427     if (!key_dyn_data) {
428       ADD_ROOT_KEY(key_dyn_data);
429     }
430     return key_dyn_data;
431
432   case HKEY_CURRENT_CONFIG:
433     if (!key_current_config) {
434       ADD_ROOT_KEY(key_current_config);
435     }
436     return key_current_config;
437
438   default:
439     return get_handle(hkey);
440
441   }
442   /*NOTREACHED*/
443 }
444 #undef ADD_ROOT_KEY
445 /* so we don't accidently access them ... */
446 #define key_current_config NULL NULL
447 #define key_current_user NULL NULL
448 #define key_users NULL NULL
449 #define key_local_machine NULL NULL
450 #define key_classes_root NULL NULL
451 #define key_dyn_data NULL NULL
452 #define key_performance_data NULL NULL
453
454 /******************************************************************************
455  * split_keypath [Internal]
456  * splits the unicode string 'wp' into an array of strings.
457  * the array is allocated by this function. 
458  * Free the array using FREE_KEY_PATH
459  *
460  * PARAMS
461  *    wp  [I] String to split up
462  *    wpv [O] Array of pointers to strings
463  *    wpc [O] Number of components
464  */
465 static void split_keypath( LPCWSTR wp, LPWSTR **wpv, int *wpc)
466 {
467     int i,j,len;
468     LPWSTR ws;
469
470     TRACE_(reg)("(%s,%p,%p)\n",debugstr_w(wp),wpv,wpc);
471
472     ws  = HEAP_strdupW( SystemHeap, 0, wp );
473
474     /* We know we have at least one substring */
475     *wpc = 1;
476
477     /* Replace each backslash with NULL, and increment the count */
478     for (i=0;ws[i];i++) {
479         if (ws[i]=='\\') {
480             ws[i]=0;
481             (*wpc)++;
482         }
483     }
484
485     len = i;
486
487     /* Allocate the space for the array of pointers, leaving room for the
488        NULL at the end */
489     *wpv = (LPWSTR*)HeapAlloc( SystemHeap, 0, sizeof(LPWSTR)*(*wpc+2));
490     (*wpv)[0]= ws;
491
492     /* Assign each pointer to the appropriate character in the string */
493     j = 1;
494     for (i=1;i<len;i++)
495         if (ws[i-1]==0) {
496             (*wpv)[j++]=ws+i;
497             /* TRACE(reg, " Subitem %d = %s\n",j-1,debugstr_w((*wpv)[j-1])); */
498         }
499
500     (*wpv)[j]=NULL;
501 }
502 #define FREE_KEY_PATH HeapFree(SystemHeap,0,wps[0]);HeapFree(SystemHeap,0,wps);
503
504
505
506
507 /******************************************************************************
508  * REGISTRY_Init [Internal]
509  * Registry initialisation, allocates some default keys. 
510  */
511 static void REGISTRY_Init(void) {
512         HKEY    hkey;
513         char    buf[200];
514
515         TRACE_(reg)("(void)\n");
516
517         RegCreateKey16(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
518         RegCloseKey(hkey);
519
520         /* This was an Open, but since it is called before the real registries
521            are loaded, it was changed to a Create - MTB 980507*/
522         RegCreateKey16(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
523         RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
524         RegCloseKey(hkey);
525
526         /* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion
527          *                                              CurrentVersion
528          *                                              CurrentBuildNumber
529          *                                              CurrentType
530          *                                      string  RegisteredOwner
531          *                                      string  RegisteredOrganization
532          *
533          */
534         /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
535          *                                      string  SysContact
536          *                                      string  SysLocation
537          *                                              SysServices
538          */                                             
539         if (-1!=gethostname(buf,200)) {
540                 RegCreateKey16(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
541                 RegSetValueEx16(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
542                 RegCloseKey(hkey);
543         }
544 }
545
546
547 /************************ SAVE Registry Function ****************************/
548
549 #define REGISTRY_SAVE_VERSION   0x00000001
550
551 /* Registry saveformat:
552  * If you change it, increase above number by 1, which will flush
553  * old registry database files.
554  * 
555  * Global:
556  *      "WINE REGISTRY Version %d"
557  *      subkeys....
558  * Subkeys:
559  *      keyname
560  *              valuename=lastmodified,type,data
561  *              ...
562  *              subkeys
563  *      ...
564  * keyname,valuename,stringdata:
565  *      the usual ascii characters from 0x00-0xff (well, not 0x00)
566  *      and \uXXXX as UNICODE value XXXX with XXXX>0xff
567  *      ( "=\\\t" escaped in \uXXXX form.)
568  * type,lastmodified: 
569  *      int
570  * 
571  * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
572  *
573  * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
574  * SaveOnlyUpdatedKeys=yes
575  */
576
577 /******************************************************************************
578  * _save_check_tainted [Internal]
579  */
580 static int _save_check_tainted( LPKEYSTRUCT lpkey )
581 {
582         int             tainted;
583
584         if (!lpkey)
585                 return 0;
586         if (lpkey->flags & REG_OPTION_TAINTED)
587                 tainted = 1;
588         else
589                 tainted = 0;
590         while (lpkey) {
591                 if (_save_check_tainted(lpkey->nextsub)) {
592                         lpkey->flags |= REG_OPTION_TAINTED;
593                         tainted = 1;
594                 }
595                 lpkey   = lpkey->next;
596         }
597         return tainted;
598 }
599
600 /******************************************************************************
601  * _save_USTRING [Internal]
602  */
603 static void _save_USTRING( FILE *F, LPWSTR wstr, int escapeeq )
604 {
605         LPWSTR  s;
606         int     doescape;
607
608         if (wstr==NULL)
609                 return;
610         s=wstr;
611         while (*s) {
612                 doescape=0;
613                 if (*s>0xff)
614                         doescape = 1;
615                 if (*s=='\n')
616                         doescape = 1;
617                 if (escapeeq && *s=='=')
618                         doescape = 1;
619                 if (*s=='\\')
620                         fputc(*s,F); /* if \\ then put it twice. */
621                 if (doescape)
622                         fprintf(F,"\\u%04x",*((unsigned short*)s));
623                 else
624                         fputc(*s,F);
625                 s++;
626         }
627 }
628
629 /******************************************************************************
630  * _savesubkey [Internal]
631  *
632  * NOTES
633  *  REG_MULTI_SZ is handled as binary (like in win95) (js)
634  */
635 static int _savesubkey( FILE *F, LPKEYSTRUCT lpkey, int level, int all )
636 {
637         LPKEYSTRUCT     lpxkey;
638         int             i,tabs,j;
639
640         lpxkey  = lpkey;
641         while (lpxkey) {
642                 if (    !(lpxkey->flags & REG_OPTION_VOLATILE) &&
643                         (all || (lpxkey->flags & REG_OPTION_TAINTED))
644                 ) {
645                         for (tabs=level;tabs--;)
646                                 fputc('\t',F);
647                         _save_USTRING(F,lpxkey->keyname,1);
648                         fputs("\n",F);
649                         for (i=0;i<lpxkey->nrofvalues;i++) {
650                                 LPKEYVALUE      val=lpxkey->values+i;
651
652                                 for (tabs=level+1;tabs--;)
653                                         fputc('\t',F);
654                                 _save_USTRING(F,val->name,0);
655                                 fputc('=',F);
656                                 fprintf(F,"%ld,%ld,",val->type,val->lastmodified);
657                                 if ( val->type == REG_SZ || val->type == REG_EXPAND_SZ )
658                                         _save_USTRING(F,(LPWSTR)val->data,0);
659                                 else
660                                         for (j=0;j<val->len;j++)
661                                                 fprintf(F,"%02x",*((unsigned char*)val->data+j));
662                                 fputs("\n",F);
663                         }
664                         /* descend recursively */
665                         if (!_savesubkey(F,lpxkey->nextsub,level+1,all))
666                                 return 0;
667                 }
668                 lpxkey=lpxkey->next;
669         }
670         return 1;
671 }
672
673
674 /******************************************************************************
675  * _savesubreg [Internal]
676  */
677 static int _savesubreg( FILE *F, LPKEYSTRUCT lpkey, int all )
678 {
679         fprintf(F,"WINE REGISTRY Version %d\n",REGISTRY_SAVE_VERSION);
680         _save_check_tainted(lpkey->nextsub);
681         return _savesubkey(F,lpkey->nextsub,0,all);
682 }
683
684
685 /******************************************************************************
686  * _savereg [Internal]
687  */
688 static BOOL _savereg( LPKEYSTRUCT lpkey, char *fn, int all )
689 {
690         FILE    *F;
691
692         F=fopen(fn,"w");
693         if (F==NULL) {
694                 WARN_(reg)("Couldn't open %s for writing: %s\n",
695                         fn,strerror(errno)
696                 );
697                 return FALSE;
698         }
699         if (!_savesubreg(F,lpkey,all)) {
700                 fclose(F);
701                 unlink(fn);
702                 WARN_(reg)("Failed to save keys, perhaps no more diskspace for %s?\n",fn);
703                 return FALSE;
704         }
705         fclose(F);
706         return TRUE;
707 }
708
709
710 /******************************************************************************
711  * SHELL_SaveRegistry [Internal]
712  */
713 void SHELL_SaveRegistry( void )
714 {
715     char   *fn, *home, *tmp;
716     char   buf[4];
717     HKEY   hkey;
718     int    all;
719     int    usedCfgUser = 0;
720     int    usedCfgLM   = 0;
721
722     TRACE_(reg)("(void)\n");
723
724     all=0;
725     if (RegOpenKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS) 
726     {
727         strcpy(buf,"yes");
728     } 
729     else 
730     {
731         DWORD len,junk,type;
732
733         len=4;
734         if ((ERROR_SUCCESS!=RegQueryValueExA( hkey,
735                                               VAL_SAVEUPDATED,
736                                               &junk,
737                                               &type,
738                                               buf,
739                                               &len)) || (type!=REG_SZ))
740         {
741             strcpy(buf,"yes");
742         }
743         RegCloseKey(hkey);
744     }
745
746     if (lstrcmpiA(buf,"yes")) all=1;
747
748     if (!(home = getenv( "HOME" )))
749     {
750         WARN_(reg)("Failed to get homedirectory of UID %d.\n",getuid());
751         return;
752     }
753     /* 
754      * Save HKEY_CURRENT_USER 
755      * Try first saving according to the defined location in .winerc
756      */
757     fn = xmalloc( MAX_PATHNAME_LEN ); 
758     if (PROFILE_GetWineIniString ( "Registry", "UserFileName", "", fn, MAX_PATHNAME_LEN - 1)) 
759     {
760         _savereg(lookup_hkey(HKEY_CURRENT_USER),fn,all);
761         usedCfgUser = 1;
762     }
763     free (fn);
764
765     if (usedCfgUser != 1)
766     {
767         fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
768                            strlen(SAVE_CURRENT_USER) + 2 );
769         strcpy(fn,home);
770         strcat(fn,WINE_PREFIX);
771   
772         /* create the directory. don't care about errorcodes. */
773         mkdir(fn,0755); /* drwxr-xr-x */
774         strcat(fn,"/"SAVE_CURRENT_USER);
775
776         tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1);
777         strcpy(tmp,fn);
778         strcat(tmp,".tmp");
779   
780         if (_savereg(lookup_hkey(HKEY_CURRENT_USER),tmp,all)) {
781             if (-1==rename(tmp,fn)) {
782                 perror("rename tmp registry");
783                 unlink(tmp);
784             }
785         }
786         free(tmp);
787         free(fn);
788     }
789
790     /* 
791      * Save HKEY_LOCAL_MACHINE
792      * Try first saving according to the defined location in .winerc
793      */
794     fn = xmalloc ( MAX_PATHNAME_LEN);
795     if (PROFILE_GetWineIniString ( "Registry", "LocalMachineFileName", "", fn, 
796                                    MAX_PATHNAME_LEN - 1))
797     {
798         _savereg(lookup_hkey(HKEY_LOCAL_MACHINE), fn, all);
799         usedCfgLM = 1;
800     }
801     free (fn);
802
803     if ( usedCfgLM != 1)
804     {
805         fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
806         strcpy(fn,home);
807         strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
808
809         tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1);
810         strcpy(tmp,fn);
811         strcat(tmp,".tmp");
812
813         if (_savereg(lookup_hkey(HKEY_LOCAL_MACHINE),tmp,all)) {
814             if (-1==rename(tmp,fn)) {
815                 perror("rename tmp registry");
816                 unlink(tmp);
817             }
818         }
819         free(tmp);
820         free(fn);
821     }
822
823     /* 
824      * Save HKEY_USERS
825      */
826     fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
827
828     strcpy(fn,home);
829     strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
830
831     tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1);
832     strcpy(tmp,fn);strcat(tmp,".tmp");
833     if ( _savereg(lookup_hkey(HKEY_USERS),tmp,FALSE)) {
834         if (-1==rename(tmp,fn)) {
835             perror("rename tmp registry");
836             unlink(tmp);
837         }
838     }
839     free(tmp);
840     free(fn);
841 }
842
843
844 /************************ LOAD Registry Function ****************************/
845
846
847
848 /******************************************************************************
849  * _find_or_add_key [Internal]
850  */
851 static LPKEYSTRUCT _find_or_add_key( LPKEYSTRUCT lpkey, LPWSTR keyname )
852 {
853         LPKEYSTRUCT     lpxkey,*lplpkey;
854
855         if ((!keyname) || (keyname[0]==0)) {
856                 free(keyname);
857                 return lpkey;
858         }
859         lplpkey= &(lpkey->nextsub);
860         lpxkey  = *lplpkey;
861         while (lpxkey) {
862                 if (    (lpxkey->keyname[0]==keyname[0]) && 
863                         !lstrcmpiW(lpxkey->keyname,keyname)
864                 )
865                         break;
866                 lplpkey = &(lpxkey->next);
867                 lpxkey  = *lplpkey;
868         }
869         if (lpxkey==NULL) {
870                 *lplpkey = (LPKEYSTRUCT)xmalloc(sizeof(KEYSTRUCT));
871                 lpxkey  = *lplpkey;
872                 memset(lpxkey,'\0',sizeof(KEYSTRUCT));
873                 lpxkey->keyname = keyname;
874         } else
875                 free(keyname);
876         return lpxkey;
877 }
878
879 /******************************************************************************
880  * _find_or_add_value [Internal]
881  */
882 static void _find_or_add_value( LPKEYSTRUCT lpkey, LPWSTR name, DWORD type,
883                                 LPBYTE data, DWORD len, DWORD lastmodified )
884 {
885         LPKEYVALUE      val=NULL;
886         int             i;
887
888         if (name && !*name) {/* empty string equals default (NULL) value */
889                 free(name);
890                 name = NULL;
891         }
892
893         for (i=0;i<lpkey->nrofvalues;i++) {
894                 val=lpkey->values+i;
895                 if (name==NULL) {
896                         if (val->name==NULL)
897                                 break;
898                 } else {
899                         if (    val->name!=NULL && 
900                                 val->name[0]==name[0] &&
901                                 !lstrcmpiW(val->name,name)
902                         )
903                                 break;
904                 }
905         }
906         if (i==lpkey->nrofvalues) {
907                 lpkey->values = xrealloc(
908                         lpkey->values,
909                         (++lpkey->nrofvalues)*sizeof(KEYVALUE)
910                 );
911                 val=lpkey->values+i;
912                 memset(val,'\0',sizeof(KEYVALUE));
913                 val->name = name;
914         } else {
915                 if (name)
916                         free(name);
917         }
918         if (val->lastmodified<lastmodified) {
919                 val->lastmodified=lastmodified;
920                 val->type = type;
921
922                 if ((type == REG_SZ || type == REG_EXPAND_SZ) && !data){
923
924                     data=xmalloc(sizeof(WCHAR));
925                     memset(data,0,sizeof(WCHAR));
926                     len =sizeof(WCHAR);
927                 }
928
929                 val->len  = len;
930                 if (val->data) 
931                         free(val->data);
932                 val->data = data;
933         } else
934                 free(data);
935 }
936
937
938 /******************************************************************************
939  * _wine_read_line [Internal]
940  *
941  * reads a line including dynamically enlarging the readbuffer and throwing
942  * away comments
943  */
944 static int _wine_read_line( FILE *F, char **buf, int *len )
945 {
946         char    *s,*curread;
947         int     mylen,curoff;
948
949         curread = *buf;
950         mylen   = *len;
951         **buf   = '\0';
952         while (1) {
953                 while (1) {
954                         s=fgets(curread,mylen,F);
955                         if (s==NULL)
956                                 return 0; /* EOF */
957                         if (NULL==(s=strchr(curread,'\n'))) {
958                                 /* buffer wasn't large enough */
959                                 curoff  = strlen(*buf);
960                                 *buf    = xrealloc(*buf,*len*2);
961                                 curread = *buf + curoff;
962                                 mylen   = *len; /* we filled up the buffer and 
963                                                  * got new '*len' bytes to fill
964                                                  */
965                                 *len    = *len * 2;
966                         } else {
967                                 *s='\0';
968                                 break;
969                         }
970                 }
971                 /* throw away comments */
972                 if (**buf=='#' || **buf==';') {
973                         curread = *buf;
974                         mylen   = *len;
975                         continue;
976                 }
977                 if (s)  /* got end of line */
978                         break;
979         }
980         return 1;
981 }
982
983
984 /******************************************************************************
985  * _wine_read_USTRING [Internal]
986  *
987  * converts a char* into a UNICODE string (up to a special char)
988  * and returns the position exactly after that string
989  */
990 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
991 {
992         char    *s;
993         LPWSTR  ws;
994
995         /* read up to "=" or "\0" or "\n" */
996         s       = buf;
997         if (*s == '=') {
998                 /* empty string is the win3.1 default value(NULL)*/
999                 *str    = NULL;
1000                 return s;
1001         }
1002         *str    = (LPWSTR)xmalloc(2*strlen(buf)+2);
1003         ws      = *str;
1004         while (*s && (*s!='\n') && (*s!='=')) {
1005                 if (*s!='\\')
1006                         *ws++=*((unsigned char*)s++);
1007                 else {
1008                         s++;
1009                         if (!*s) {
1010                                 /* Dangling \ ... may only happen if a registry
1011                                  * write was short. FIXME: What do to?
1012                                  */
1013                                  break;
1014                         }
1015                         if (*s=='\\') {
1016                                 *ws++='\\';
1017                                 s++;
1018                                 continue;
1019                         }
1020                         if (*s!='u') {
1021                                 WARN_(reg)("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
1022                                 *ws++='\\';
1023                                 *ws++=*s++;
1024                         } else {
1025                                 char    xbuf[5];
1026                                 int     wc;
1027
1028                                 s++;
1029                                 memcpy(xbuf,s,4);xbuf[4]='\0';
1030                                 if (!sscanf(xbuf,"%x",&wc))
1031                                         WARN_(reg)("Strange escape sequence %s found in |%s|\n",xbuf,buf);
1032                                 s+=4;
1033                                 *ws++   =(unsigned short)wc;
1034                         }
1035                 }
1036         }
1037         *ws     = 0;
1038         ws      = *str;
1039         if (*ws)
1040                 *str    = strdupW(*str);
1041         else
1042                 *str    = NULL;
1043         free(ws);
1044         return s;
1045 }
1046
1047
1048 /******************************************************************************
1049  * _wine_loadsubkey [Internal]
1050  *
1051  * NOTES
1052  *    It seems like this is returning a boolean.  Should it?
1053  *
1054  * RETURNS
1055  *    Success: 1
1056  *    Failure: 0
1057  */
1058 static int _wine_loadsubkey( FILE *F, LPKEYSTRUCT lpkey, int level, char **buf,
1059                              int *buflen, DWORD optflag )
1060 {
1061         LPKEYSTRUCT     lpxkey;
1062         int             i;
1063         char            *s;
1064         LPWSTR          name;
1065
1066     TRACE_(reg)("(%p,%p,%d,%s,%d,%lx)\n", F, lpkey, level, debugstr_a(*buf),
1067           *buflen, optflag);
1068
1069     lpkey->flags |= optflag;
1070
1071     /* Good.  We already got a line here ... so parse it */
1072     lpxkey = NULL;
1073     while (1) {
1074         i=0;s=*buf;
1075         while (*s=='\t') {
1076             s++;
1077             i++;
1078         }
1079         if (i>level) {
1080             if (lpxkey==NULL) {
1081                 WARN_(reg)("Got a subhierarchy without resp. key?\n");
1082                 return 0;
1083             }
1084             _wine_loadsubkey(F,lpxkey,level+1,buf,buflen,optflag);
1085             continue;
1086         }
1087
1088                 /* let the caller handle this line */
1089                 if (i<level || **buf=='\0')
1090                         return 1;
1091
1092                 /* it can be: a value or a keyname. Parse the name first */
1093                 s=_wine_read_USTRING(s,&name);
1094
1095                 /* switch() default: hack to avoid gotos */
1096                 switch (0) {
1097                 default:
1098                         if (*s=='\0') {
1099                                 lpxkey=_find_or_add_key(lpkey,name);
1100                         } else {
1101                                 LPBYTE          data;
1102                                 int             len,lastmodified,type;
1103
1104                                 if (*s!='=') {
1105                                         WARN_(reg)("Unexpected character: %c\n",*s);
1106                                         break;
1107                                 }
1108                                 s++;
1109                                 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
1110                                         WARN_(reg)("Haven't understood possible value in |%s|, skipping.\n",*buf);
1111                                         break;
1112                                 }
1113                                 /* skip the 2 , */
1114                                 s=strchr(s,',');s++;
1115                                 s=strchr(s,',');s++;
1116                                 if (type == REG_SZ || type == REG_EXPAND_SZ) {
1117                                         s=_wine_read_USTRING(s,(LPWSTR*)&data);
1118                                         if (data)
1119                                                 len = lstrlenW((LPWSTR)data)*2+2;
1120                                         else    
1121                                                 len = 0;
1122                                 } else {
1123                                         len=strlen(s)/2;
1124                                         data = (LPBYTE)xmalloc(len+1);
1125                                         for (i=0;i<len;i++) {
1126                                                 data[i]=0;
1127                                                 if (*s>='0' && *s<='9')
1128                                                         data[i]=(*s-'0')<<4;
1129                                                 if (*s>='a' && *s<='f')
1130                                                         data[i]=(*s-'a'+'\xa')<<4;
1131                                                 if (*s>='A' && *s<='F')
1132                                                         data[i]=(*s-'A'+'\xa')<<4;
1133                                                 s++;
1134                                                 if (*s>='0' && *s<='9')
1135                                                         data[i]|=*s-'0';
1136                                                 if (*s>='a' && *s<='f')
1137                                                         data[i]|=*s-'a'+'\xa';
1138                                                 if (*s>='A' && *s<='F')
1139                                                         data[i]|=*s-'A'+'\xa';
1140                                                 s++;
1141                                         }
1142                                 }
1143                                 _find_or_add_value(lpkey,name,type,data,len,lastmodified);
1144                         }
1145                 }
1146                 /* read the next line */
1147                 if (!_wine_read_line(F,buf,buflen))
1148                         return 1;
1149     }
1150     return 1;
1151 }
1152
1153
1154 /******************************************************************************
1155  * _wine_loadsubreg [Internal]
1156  */
1157 static int _wine_loadsubreg( FILE *F, LPKEYSTRUCT lpkey, DWORD optflag )
1158 {
1159         int     ver;
1160         char    *buf;
1161         int     buflen;
1162
1163         buf=xmalloc(10);buflen=10;
1164         if (!_wine_read_line(F,&buf,&buflen)) {
1165                 free(buf);
1166                 return 0;
1167         }
1168         if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
1169                 free(buf);
1170                 return 0;
1171         }
1172         if (ver!=REGISTRY_SAVE_VERSION) {
1173                 TRACE_(reg)("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
1174                 free(buf);
1175                 return 0;
1176         }
1177         if (!_wine_read_line(F,&buf,&buflen)) {
1178                 free(buf);
1179                 return 0;
1180         }
1181         if (!_wine_loadsubkey(F,lpkey,0,&buf,&buflen,optflag)) {
1182                 free(buf);
1183                 return 0;
1184         }
1185         free(buf);
1186         return 1;
1187 }
1188
1189
1190 /******************************************************************************
1191  * _wine_loadreg [Internal]
1192  */
1193 static void _wine_loadreg( LPKEYSTRUCT lpkey, char *fn, DWORD optflag )
1194 {
1195     FILE *F;
1196
1197     TRACE_(reg)("(%p,%s,%lx)\n",lpkey,debugstr_a(fn),optflag);
1198
1199     F = fopen(fn,"rb");
1200     if (F==NULL) {
1201         WARN_(reg)("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
1202         return;
1203     }
1204     if (!_wine_loadsubreg(F,lpkey,optflag)) {
1205         fclose(F);
1206         unlink(fn);
1207         return;
1208     }
1209     fclose(F);
1210 }
1211
1212 /******************************************************************************
1213  * _flush_registry [Internal]
1214  * 
1215  * This function allow to flush section of the internal registry.  It is mainly
1216  * implements to fix a problem with the global HKU and the local HKU.
1217  * Those two files are read to build the HKU\.Default branch to finaly copy
1218  * this branch onto HKCU hive, once this is done, if we keep the HKU hive as is, 
1219  * all the global HKU are saved onto the user's personal version of HKU hive.
1220  * which is bad...
1221  */
1222
1223  /* Forward declaration of recusive agent */
1224 static void _flush_reg(LPKEYSTRUCT from);
1225
1226 static void _flush_registry( LPKEYSTRUCT from )
1227 {
1228   /* make sure we have something... */
1229   if (from == NULL)
1230     return;
1231
1232   /* Launch the recusive agent on sub branches */
1233   _flush_reg( from->nextsub );
1234   _flush_reg( from->next );
1235
1236   /* Initialize pointers */
1237   from->nextsub = NULL;
1238   from->next    = NULL;
1239 }
1240 static void _flush_reg( LPKEYSTRUCT from )
1241 {
1242         int     j;
1243
1244   /* make sure we have something... */
1245   if (from == NULL)
1246     return;
1247
1248   /* 
1249    * do the same for the child keys 
1250    */
1251   if (from->nextsub != NULL)
1252     _flush_reg(from->nextsub);
1253
1254   /* 
1255    * do the same for the sibling keys 
1256    */
1257   if (from->next != NULL)
1258     _flush_reg(from->next);
1259
1260   /*
1261    * iterate through this key's values and delete them
1262    */
1263   for (j=0;j<from->nrofvalues;j++) 
1264   {
1265     free( (from->values+j)->name);
1266     free( (from->values+j)->data);
1267   }
1268
1269   /*
1270    * free the structure
1271    */
1272   if ( from != NULL )
1273     free(from);
1274 }
1275
1276
1277 /******************************************************************************
1278  * _copy_registry [Internal]
1279  */
1280 static void _copy_registry( LPKEYSTRUCT from, LPKEYSTRUCT to )
1281 {
1282         LPKEYSTRUCT     lpxkey;
1283         int             j;
1284         LPKEYVALUE      valfrom;
1285
1286         from=from->nextsub;
1287         while (from) {
1288                 lpxkey = _find_or_add_key(to,strdupW(from->keyname));
1289
1290                 for (j=0;j<from->nrofvalues;j++) {
1291                         LPWSTR  name;
1292                         LPBYTE  data;
1293
1294                         valfrom = from->values+j;
1295                         name=valfrom->name;
1296                         if (name) name=strdupW(name);
1297                         data=(LPBYTE)xmalloc(valfrom->len);
1298                         memcpy(data,valfrom->data,valfrom->len);
1299
1300                         _find_or_add_value(
1301                                 lpxkey,
1302                                 name,
1303                                 valfrom->type,
1304                                 data,
1305                                 valfrom->len,
1306                                 valfrom->lastmodified
1307                         );
1308                 }
1309                 _copy_registry(from,lpxkey);
1310                 from = from->next;
1311         }
1312 }
1313
1314
1315 /* WINDOWS 95 REGISTRY LOADER */
1316 /* 
1317  * Structure of a win95 registry database.
1318  * main header:
1319  * 0 :  "CREG"  - magic
1320  * 4 :  DWORD version
1321  * 8 :  DWORD offset_of_RGDB_part
1322  * 0C..0F:      ? (someone fill in please)
1323  * 10:  WORD    number of RGDB blocks
1324  * 12:  WORD    ?
1325  * 14:  WORD    always 0000?
1326  * 16:  WORD    always 0001?
1327  * 18..1F:      ? (someone fill in please)
1328  *
1329  * 20: RGKN_section:
1330  *   header:
1331  *      0 :             "RGKN"  - magic
1332  *      4 : DWORD       offset to first RGDB section
1333  *      8 : DWORD       offset to the root record
1334  *      C..0x1B:        ? (fill in)
1335  *      0x20 ... offset_of_RGDB_part: Disk Key Entry structures
1336  *
1337  *   Disk Key Entry Structure:
1338  *      00: DWORD       - Free entry indicator(?)
1339  *      04: DWORD       - Hash = sum of bytes of keyname
1340  *      08: DWORD       - Root key indicator? unknown, but usually 0xFFFFFFFF on win95 systems
1341  *      0C: DWORD       - disk address of PreviousLevel Key.
1342  *      10: DWORD       - disk address of Next Sublevel Key.
1343  *      14: DWORD       - disk address of Next Key (on same level).
1344  * DKEP>18: WORD        - Nr, Low Significant part.
1345  *      1A: WORD        - Nr, High Significant part.
1346  *
1347  * The disk address always points to the nr part of the previous key entry 
1348  * of the referenced key. Don't ask me why, or even if I got this correct
1349  * from staring at 1kg of hexdumps. (DKEP)
1350  *
1351  * The High significant part of the structure seems to equal the number
1352  * of the RGDB section. The low significant part is a unique ID within
1353  * that RGDB section
1354  *
1355  * There are two minor corrections to the position of that structure.
1356  * 1. If the address is xxx014 or xxx018 it will be aligned to xxx01c AND 
1357  *    the DKE reread from there.
1358  * 2. If the address is xxxFFx it will be aligned to (xxx+1)000.
1359  * CPS - I have not experienced the above phenomenon in my registry files
1360  *
1361  * RGDB_section:
1362  *      00:             "RGDB"  - magic
1363  *      04: DWORD       offset to next RGDB section
1364  *      08: DWORD       ?
1365  *      0C: WORD        always 000d?
1366  *      0E: WORD        RGDB block number
1367  *      10:     DWORD   ? (equals value at offset 4 - value at offset 8)
1368  *      14..1F:         ?
1369  *      20.....:        disk keys
1370  *
1371  * disk key:
1372  *      00:     DWORD   nextkeyoffset   - offset to the next disk key structure
1373  *      08:     WORD    nrLS            - low significant part of NR
1374  *      0A:     WORD    nrHS            - high significant part of NR
1375  *      0C:     DWORD   bytesused       - bytes used in this structure.
1376  *      10:     WORD    name_len        - length of name in bytes. without \0
1377  *      12:     WORD    nr_of_values    - number of values.
1378  *      14:     char    name[name_len]  - name string. No \0.
1379  *      14+name_len: disk values
1380  *      nextkeyoffset: ... next disk key
1381  *
1382  * disk value:
1383  *      00:     DWORD   type            - value type (hmm, could be WORD too)
1384  *      04:     DWORD                   - unknown, usually 0
1385  *      08:     WORD    namelen         - length of Name. 0 means name=NULL
1386  *      0C:     WORD    datalen         - length of Data.
1387  *      10:     char    name[namelen]   - name, no \0
1388  *      10+namelen: BYTE        data[datalen] - data, without \0 if string
1389  *      10+namelen+datalen: next values or disk key
1390  *
1391  * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
1392  * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
1393  * structure) and reading another RGDB_section.
1394  * repeat until end of file.
1395  *
1396  * An interesting relationship exists in RGDB_section. The value at offset
1397  * 10 equals the value at offset 4 minus the value at offset 8. I have no
1398  * idea at the moment what this means.  (Kevin Cozens)
1399  *
1400  * FIXME: this description needs some serious help, yes.
1401  */
1402
1403 struct  _w95keyvalue {
1404         unsigned long           type;
1405         unsigned short          datalen;
1406         char                    *name;
1407         unsigned char           *data;
1408         unsigned long           x1;
1409         int                     lastmodified;
1410 };
1411
1412 struct  _w95key {
1413         char                    *name;
1414         int                     nrofvals;
1415         struct  _w95keyvalue    *values;
1416         struct _w95key          *prevlvl;
1417         struct _w95key          *nextsub;
1418         struct _w95key          *next;
1419 };
1420
1421
1422 struct _w95_info {
1423   char *rgknbuffer;
1424   int  rgknsize;
1425   char *rgdbbuffer;
1426   int  rgdbsize;
1427   int  depth;
1428   int  lastmodified;
1429 };
1430
1431
1432 /******************************************************************************
1433  * _w95_processKey [Internal]
1434  */
1435 static LPKEYSTRUCT _w95_processKey ( LPKEYSTRUCT lpkey, 
1436                                    int nrLS, int nrMS, struct _w95_info *info )
1437
1438 {
1439   /* Disk Key Header structure (RGDB part) */
1440         struct  dkh {
1441                 unsigned long           nextkeyoff; 
1442                 unsigned short          nrLS;
1443                 unsigned short          nrMS;
1444                 unsigned long           bytesused;
1445                 unsigned short          keynamelen;
1446                 unsigned short          values;
1447                 unsigned long           xx1;
1448                 /* keyname */
1449                 /* disk key values or nothing */
1450         };
1451         /* Disk Key Value structure */
1452         struct  dkv {
1453                 unsigned long           type;
1454                 unsigned long           x1;
1455                 unsigned short          valnamelen;
1456                 unsigned short          valdatalen;
1457                 /* valname, valdata */
1458         };
1459
1460         
1461         struct  dkh dkh;
1462         int     bytesread = 0;
1463         char    *rgdbdata = info->rgdbbuffer;
1464         int     nbytes = info->rgdbsize;
1465         char    *curdata = rgdbdata;
1466         char    *end = rgdbdata + nbytes;
1467         int     off_next_rgdb;
1468         char    *next = rgdbdata;
1469         int     nrgdb, i;
1470         LPKEYSTRUCT     lpxkey;
1471         
1472         do {
1473           curdata = next;
1474           if (strncmp(curdata, "RGDB", 4)) return (NULL);
1475             
1476           memcpy(&off_next_rgdb,curdata+4,4);
1477           next = curdata + off_next_rgdb;
1478           nrgdb = (int) *((short *)curdata + 7);
1479
1480         } while (nrgdb != nrMS && (next < end));
1481
1482         /* curdata now points to the start of the right RGDB section */
1483         curdata += 0x20;
1484
1485 #define XREAD(whereto,len) \
1486         if ((curdata + len) <= end) {\
1487                 memcpy(whereto,curdata,len);\
1488                 curdata+=len;\
1489                 bytesread+=len;\
1490         }
1491
1492         while (curdata < next) {
1493           struct        dkh *xdkh = (struct dkh*)curdata;
1494
1495           bytesread += sizeof(dkh); /* FIXME... nextkeyoff? */
1496           if (xdkh->nrLS == nrLS) {
1497                 memcpy(&dkh,xdkh,sizeof(dkh));
1498                 curdata += sizeof(dkh);
1499                 break;
1500           }
1501           curdata += xdkh->nextkeyoff;
1502         };
1503
1504         if (dkh.nrLS != nrLS) return (NULL);
1505
1506         if (nrgdb != dkh.nrMS)
1507           return (NULL);
1508
1509         assert((dkh.keynamelen<2) || curdata[0]);
1510         lpxkey=_find_or_add_key(lpkey,strcvtA2W(curdata, dkh.keynamelen));
1511         curdata += dkh.keynamelen;
1512
1513         for (i=0;i< dkh.values; i++) {
1514           struct dkv dkv;
1515           LPBYTE data;
1516           int len;
1517           LPWSTR name;
1518
1519           XREAD(&dkv,sizeof(dkv));
1520
1521           name = strcvtA2W(curdata, dkv.valnamelen);
1522           curdata += dkv.valnamelen;
1523
1524           if ((1 << dkv.type) & UNICONVMASK) {
1525             data = (LPBYTE) strcvtA2W(curdata, dkv.valdatalen);
1526             len = 2*(dkv.valdatalen + 1);
1527           } else {
1528             /* I don't think we want to NULL terminate all data */
1529             data = xmalloc(dkv.valdatalen);
1530             memcpy (data, curdata, dkv.valdatalen);
1531             len = dkv.valdatalen;
1532           }
1533
1534           curdata += dkv.valdatalen;
1535           
1536           _find_or_add_value(
1537                              lpxkey,
1538                              name,
1539                              dkv.type,
1540                              data,
1541                              len,
1542                              info->lastmodified
1543                              );
1544         }
1545         return (lpxkey);
1546 }
1547
1548 /******************************************************************************
1549  * _w95_walkrgkn [Internal]
1550  */
1551 static void _w95_walkrgkn( LPKEYSTRUCT prevkey, char *off, 
1552                            struct _w95_info *info )
1553
1554 {
1555   /* Disk Key Entry structure (RGKN part) */
1556   struct        dke {
1557     unsigned long               x1;
1558     unsigned long               x2;
1559     unsigned long               x3;/*usually 0xFFFFFFFF */
1560     unsigned long               prevlvl;
1561     unsigned long               nextsub;
1562     unsigned long               next;
1563     unsigned short              nrLS;
1564     unsigned short              nrMS;
1565   } *dke = (struct dke *)off;
1566   LPKEYSTRUCT  lpxkey;
1567
1568   if (dke == NULL) {
1569     dke = (struct dke *) ((char *)info->rgknbuffer);
1570   }
1571
1572   lpxkey = _w95_processKey(prevkey, dke->nrLS, dke->nrMS, info);
1573   /* XXX <-- This is a hack*/
1574   if (!lpxkey) {
1575     lpxkey = prevkey;
1576   }
1577
1578   if (dke->nextsub != -1 && 
1579       ((dke->nextsub - 0x20) < info->rgknsize) 
1580       && (dke->nextsub > 0x20)) {
1581     
1582     _w95_walkrgkn(lpxkey, 
1583                   info->rgknbuffer + dke->nextsub - 0x20, 
1584                   info);
1585   }
1586   
1587   if (dke->next != -1 && 
1588       ((dke->next - 0x20) < info->rgknsize) && 
1589       (dke->next > 0x20)) {
1590     _w95_walkrgkn(prevkey,  
1591                   info->rgknbuffer + dke->next - 0x20,
1592                   info);
1593   }
1594
1595   return;
1596 }
1597
1598
1599 /******************************************************************************
1600  * _w95_loadreg [Internal]
1601  */
1602 static void _w95_loadreg( char* fn, LPKEYSTRUCT lpkey )
1603 {
1604         HFILE           hfd;
1605         char            magic[5];
1606         unsigned long   where,version,rgdbsection,end;
1607         struct          _w95_info info;
1608         OFSTRUCT        ofs;
1609         BY_HANDLE_FILE_INFORMATION hfdinfo;
1610
1611         TRACE_(reg)("Loading Win95 registry database '%s'\n",fn);
1612         hfd=OpenFile(fn,&ofs,OF_READ);
1613         if (hfd==HFILE_ERROR)
1614                 return;
1615         magic[4]=0;
1616         if (4!=_lread(hfd,magic,4))
1617                 return;
1618         if (strcmp(magic,"CREG")) {
1619                 WARN_(reg)("%s is not a w95 registry.\n",fn);
1620                 return;
1621         }
1622         if (4!=_lread(hfd,&version,4))
1623                 return;
1624         if (4!=_lread(hfd,&rgdbsection,4))
1625                 return;
1626         if (-1==_llseek(hfd,0x20,SEEK_SET))
1627                 return;
1628         if (4!=_lread(hfd,magic,4))
1629                 return;
1630         if (strcmp(magic,"RGKN")) {
1631                 WARN_(reg)("second IFF header not RGKN, but %s\n", magic);
1632                 return;
1633         }
1634
1635         /* STEP 1: Keylink structures */
1636         if (-1==_llseek(hfd,0x40,SEEK_SET))
1637                 return;
1638         where   = 0x40;
1639         end     = rgdbsection;
1640
1641         info.rgknsize = end - where;
1642         info.rgknbuffer = (char*)xmalloc(info.rgknsize);
1643         if (info.rgknsize != _lread(hfd,info.rgknbuffer,info.rgknsize))
1644                 return;
1645
1646         if (!GetFileInformationByHandle(hfd,&hfdinfo))
1647                 return;
1648
1649         end = hfdinfo.nFileSizeLow;
1650         info.lastmodified = DOSFS_FileTimeToUnixTime(&hfdinfo.ftLastWriteTime,NULL);
1651
1652         if (-1==_llseek(hfd,rgdbsection,SEEK_SET))
1653                 return;
1654
1655         info.rgdbbuffer = (char*)xmalloc(end-rgdbsection);
1656         info.rgdbsize = end - rgdbsection;
1657
1658         if (info.rgdbsize !=_lread(hfd,info.rgdbbuffer,info.rgdbsize))
1659                 return;
1660         _lclose(hfd);
1661
1662         _w95_walkrgkn(lpkey, NULL, &info);
1663
1664         free (info.rgdbbuffer);
1665         free (info.rgknbuffer);
1666 }
1667
1668
1669 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1670
1671 /*
1672     reghack - windows 3.11 registry data format demo program.
1673
1674     The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1675     a combined hash table and tree description, and finally a text table.
1676
1677     The header is obvious from the struct header. The taboff1 and taboff2
1678     fields are always 0x20, and their usage is unknown.
1679
1680     The 8-byte entry table has various entry types.
1681
1682     tabent[0] is a root index. The second word has the index of the root of
1683             the directory.
1684     tabent[1..hashsize] is a hash table. The first word in the hash entry is
1685             the index of the key/value that has that hash. Data with the same
1686             hash value are on a circular list. The other three words in the
1687             hash entry are always zero.
1688     tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1689             entry: dirent and keyent/valent. They are identified by context.
1690     tabent[freeidx] is the first free entry. The first word in a free entry
1691             is the index of the next free entry. The last has 0 as a link.
1692             The other three words in the free list are probably irrelevant.
1693
1694     Entries in text table are preceeded by a word at offset-2. This word
1695     has the value (2*index)+1, where index is the referring keyent/valent
1696     entry in the table. I have no suggestion for the 2* and the +1.
1697     Following the word, there are N bytes of data, as per the keyent/valent
1698     entry length. The offset of the keyent/valent entry is from the start
1699     of the text table to the first data byte.
1700
1701     This information is not available from Microsoft. The data format is
1702     deduced from the reg.dat file by me. Mistakes may
1703     have been made. I claim no rights and give no guarantees for this program.
1704
1705     Tor Sjøwall, tor@sn.no
1706 */
1707
1708 /* reg.dat header format */
1709 struct _w31_header {
1710         char            cookie[8];      /* 'SHCC3.10' */
1711         unsigned long   taboff1;        /* offset of hash table (??) = 0x20 */
1712         unsigned long   taboff2;        /* offset of index table (??) = 0x20 */
1713         unsigned long   tabcnt;         /* number of entries in index table */
1714         unsigned long   textoff;        /* offset of text part */
1715         unsigned long   textsize;       /* byte size of text part */
1716         unsigned short  hashsize;       /* hash size */
1717         unsigned short  freeidx;        /* free index */
1718 };
1719
1720 /* generic format of table entries */
1721 struct _w31_tabent {
1722         unsigned short w0, w1, w2, w3;
1723 };
1724
1725 /* directory tabent: */
1726 struct _w31_dirent {
1727         unsigned short  sibling_idx;    /* table index of sibling dirent */
1728         unsigned short  child_idx;      /* table index of child dirent */
1729         unsigned short  key_idx;        /* table index of key keyent */
1730         unsigned short  value_idx;      /* table index of value valent */
1731 };
1732
1733 /* key tabent: */
1734 struct _w31_keyent {
1735         unsigned short  hash_idx;       /* hash chain index for string */
1736         unsigned short  refcnt;         /* reference count */
1737         unsigned short  length;         /* length of string */
1738         unsigned short  string_off;     /* offset of string in text table */
1739 };
1740
1741 /* value tabent: */
1742 struct _w31_valent {
1743         unsigned short  hash_idx;       /* hash chain index for string */
1744         unsigned short  refcnt;         /* reference count */
1745         unsigned short  length;         /* length of string */
1746         unsigned short  string_off;     /* offset of string in text table */
1747 };
1748
1749 /* recursive helper function to display a directory tree */
1750 void
1751 __w31_dumptree( unsigned short idx,
1752                 unsigned char *txt,
1753                 struct _w31_tabent *tab,
1754                 struct _w31_header *head,
1755                 LPKEYSTRUCT     lpkey,
1756                 time_t          lastmodified,
1757                 int             level
1758 ) {
1759         struct _w31_dirent      *dir;
1760         struct _w31_keyent      *key;
1761         struct _w31_valent      *val;
1762         LPKEYSTRUCT             xlpkey = NULL;
1763         LPWSTR                  name,value;
1764         static char             tail[400];
1765
1766         while (idx!=0) {
1767                 dir=(struct _w31_dirent*)&tab[idx];
1768
1769                 if (dir->key_idx) {
1770                         key = (struct _w31_keyent*)&tab[dir->key_idx];
1771
1772                         memcpy(tail,&txt[key->string_off],key->length);
1773                         tail[key->length]='\0';
1774                         /* all toplevel entries AND the entries in the 
1775                          * toplevel subdirectory belong to \SOFTWARE\Classes
1776                          */
1777                         if (!level && !lstrcmpA(tail,".classes")) {
1778                                 __w31_dumptree(dir->child_idx,txt,tab,head,lpkey,lastmodified,level+1);
1779                                 idx=dir->sibling_idx;
1780                                 continue;
1781                         }
1782                         name=strdupA2W(tail);
1783
1784                         xlpkey=_find_or_add_key(lpkey,name);
1785
1786                         /* only add if leaf node or valued node */
1787                         if (dir->value_idx!=0||dir->child_idx==0) {
1788                                 if (dir->value_idx) {
1789                                         val=(struct _w31_valent*)&tab[dir->value_idx];
1790                                         memcpy(tail,&txt[val->string_off],val->length);
1791                                         tail[val->length]='\0';
1792                                         value=strdupA2W(tail);
1793                                         _find_or_add_value(xlpkey,NULL,REG_SZ,(LPBYTE)value,lstrlenW(value)*2+2,lastmodified);
1794                                 }
1795                         }
1796                 } else {
1797                         TRACE_(reg)("strange: no directory key name, idx=%04x\n", idx);
1798                 }
1799                 __w31_dumptree(dir->child_idx,txt,tab,head,xlpkey,lastmodified,level+1);
1800                 idx=dir->sibling_idx;
1801         }
1802 }
1803
1804
1805 /******************************************************************************
1806  * _w31_loadreg [Internal]
1807  */
1808 void _w31_loadreg(void) {
1809         HFILE                   hf;
1810         struct _w31_header      head;
1811         struct _w31_tabent      *tab;
1812         unsigned char           *txt;
1813         int                     len;
1814         OFSTRUCT                ofs;
1815         BY_HANDLE_FILE_INFORMATION hfinfo;
1816         time_t                  lastmodified;
1817         LPKEYSTRUCT             lpkey;
1818
1819         TRACE_(reg)("(void)\n");
1820
1821         hf = OpenFile("reg.dat",&ofs,OF_READ);
1822         if (hf==HFILE_ERROR)
1823                 return;
1824
1825         /* read & dump header */
1826         if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1827                 ERR_(reg)("reg.dat is too short.\n");
1828                 _lclose(hf);
1829                 return;
1830         }
1831         if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1832                 ERR_(reg)("reg.dat has bad signature.\n");
1833                 _lclose(hf);
1834                 return;
1835         }
1836
1837         len = head.tabcnt * sizeof(struct _w31_tabent);
1838         /* read and dump index table */
1839         tab = xmalloc(len);
1840         if (len!=_lread(hf,tab,len)) {
1841                 ERR_(reg)("couldn't read %d bytes.\n",len); 
1842                 free(tab);
1843                 _lclose(hf);
1844                 return;
1845         }
1846
1847         /* read text */
1848         txt = xmalloc(head.textsize);
1849         if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1850                 ERR_(reg)("couldn't seek to textblock.\n"); 
1851                 free(tab);
1852                 free(txt);
1853                 _lclose(hf);
1854                 return;
1855         }
1856         if (head.textsize!=_lread(hf,txt,head.textsize)) {
1857                 ERR_(reg)("textblock too short (%d instead of %ld).\n",len,head.textsize); 
1858                 free(tab);
1859                 free(txt);
1860                 _lclose(hf);
1861                 return;
1862         }
1863
1864         if (!GetFileInformationByHandle(hf,&hfinfo)) {
1865                 ERR_(reg)("GetFileInformationByHandle failed?.\n"); 
1866                 free(tab);
1867                 free(txt);
1868                 _lclose(hf);
1869                 return;
1870         }
1871         lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1872         lpkey = lookup_hkey(HKEY_CLASSES_ROOT);
1873         __w31_dumptree(tab[0].w1,txt,tab,&head,lpkey,lastmodified,0);
1874         free(tab);
1875         free(txt);
1876         _lclose(hf);
1877         return;
1878 }
1879
1880
1881 /**********************************************************************************
1882  * SHELL_LoadRegistry [Internal]
1883  */
1884 void SHELL_LoadRegistry( void )
1885 {
1886   char        *fn, *home;
1887   LPKEYSTRUCT   lpkey, HKCU, HKU, HKLM;
1888   HKEY              hkey;
1889
1890   TRACE_(reg)("(void)\n");
1891
1892   HKCU = lookup_hkey(HKEY_CURRENT_USER);
1893   HKU  = lookup_hkey(HKEY_USERS);
1894   HKLM = lookup_hkey(HKEY_LOCAL_MACHINE);
1895
1896   /* Load windows 3.1 entries */
1897   _w31_loadreg();
1898   /* Load windows 95 entries */
1899   _w95_loadreg("C:\\system.1st",        HKLM);
1900   _w95_loadreg("system.dat",    HKLM);
1901   _w95_loadreg("user.dat",      HKU);
1902
1903   /* 
1904    * Load the global HKU hive directly from /usr/local/etc
1905    */ 
1906   _wine_loadreg( HKU, SAVE_USERS_DEFAULT, 0);
1907
1908   /* 
1909    * Load the global machine defaults directly form /usr/local/etc
1910    */
1911   _wine_loadreg( HKLM, SAVE_LOCAL_MACHINE_DEFAULT, 0);
1912
1913   /*
1914    * Load the user saved registries 
1915    */
1916   if ((home = getenv( "HOME" )))
1917   {
1918     /* 
1919      * Load user's personal versions of global HKU/.Default keys
1920      */
1921     fn=(char*)xmalloc(
1922                 strlen(home)+
1923                 strlen(WINE_PREFIX)+
1924                 strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1925
1926     strcpy(fn, home);
1927     strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1928     _wine_loadreg(HKU, fn, REG_OPTION_TAINTED); 
1929     free(fn);
1930
1931     /* 
1932      * Load HKCU, attempt to get the registry location from the config 
1933      * file first, if exist, load and keep going.
1934      */      
1935     fn = xmalloc( MAX_PATHNAME_LEN ); 
1936     if ( PROFILE_GetWineIniString(
1937           "Registry", 
1938           "UserFileName", 
1939           "", 
1940           fn, 
1941           MAX_PATHNAME_LEN - 1)) 
1942     {
1943       _wine_loadreg(HKCU,fn,0);
1944     }
1945         free (fn);
1946
1947     fn=(char*)xmalloc(
1948                 strlen(home)+
1949                 strlen(WINE_PREFIX)+
1950                 strlen(SAVE_CURRENT_USER)+2);
1951
1952     strcpy(fn, home);
1953     strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1954     _wine_loadreg(HKCU, fn, REG_OPTION_TAINTED);
1955     free(fn);
1956
1957     /* 
1958      * Load HKLM, attempt to get the registry location from the config 
1959      * file first, if exist, load and keep going.
1960      */      
1961     fn = xmalloc ( MAX_PATHNAME_LEN);
1962     if ( PROFILE_GetWineIniString(
1963           "Registry", 
1964           "LocalMachineFileName", 
1965           "", 
1966           fn, 
1967           MAX_PATHNAME_LEN - 1))
1968     {
1969         _wine_loadreg(HKLM, fn, 0);
1970     }
1971     free(fn);
1972
1973     fn=(char*)xmalloc(
1974                 strlen(home)+
1975                 strlen(WINE_PREFIX)+
1976                 strlen(SAVE_LOCAL_MACHINE)+2);
1977
1978     strcpy(fn,home);
1979     strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1980     _wine_loadreg(HKLM, fn, REG_OPTION_TAINTED);
1981     free(fn);
1982   }
1983   else
1984   {
1985     WARN_(reg)("Failed to get homedirectory of UID %d.\n",getuid());
1986   }
1987
1988   /* 
1989    * Obtain the handle of the HKU\.Default key.
1990    * in order to copy HKU\.Default\* onto HKEY_CURRENT_USER 
1991    */
1992   RegCreateKey16(HKEY_USERS,".Default",&hkey);
1993   lpkey = lookup_hkey(hkey);
1994   if(!lpkey)
1995      WARN_(reg)("Could not create global user default key\n");
1996   else
1997     _copy_registry(lpkey, HKCU );
1998
1999   RegCloseKey(hkey);
2000
2001   /* 
2002    * Since HKU is built from the global HKU and the local user HKU file we must
2003    * flush the HKU tree we have built at this point otherwise the part brought
2004    * in from the global HKU is saved into the local HKU.  To avoid this 
2005    * useless dupplication of HKU keys we reread the local HKU key.
2006    */
2007
2008   /* Allways flush the HKU hive and reload it only with user's personal HKU */
2009   _flush_registry(HKU); 
2010
2011   /* Reload user's local HKU hive */
2012   if (home)
2013   {
2014       fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX)
2015                          + strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
2016       
2017       strcpy(fn,home);
2018       strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
2019
2020       _wine_loadreg( HKU, fn, REG_OPTION_TAINTED);
2021
2022       free(fn);
2023   }
2024
2025   /* 
2026    * Make sure the update mode is there
2027    */
2028   if (ERROR_SUCCESS==RegCreateKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)) 
2029   {
2030     DWORD       junk,type,len;
2031     char        data[5];
2032
2033     len=4;
2034     if ((       RegQueryValueExA(
2035             hkey,
2036             VAL_SAVEUPDATED,
2037             &junk,
2038             &type,
2039             data,
2040             &len) != ERROR_SUCCESS) || (type != REG_SZ))
2041     {
2042       RegSetValueExA(hkey,VAL_SAVEUPDATED,0,REG_SZ,"yes",4);
2043     }
2044
2045     RegCloseKey(hkey);
2046   }
2047 }
2048
2049
2050 /********************* API FUNCTIONS ***************************************/
2051 /*
2052  * Open Keys.
2053  *
2054  * All functions are stubs to RegOpenKeyEx32W where all the
2055  * magic happens. 
2056  *
2057  * Callpath:
2058  * RegOpenKey16 -> RegOpenKey32A -> RegOpenKeyEx32A \
2059  *                                  RegOpenKey32W   -> RegOpenKeyEx32W 
2060  */
2061
2062
2063 /******************************************************************************
2064  * RegOpenKeyEx32W [ADVAPI32.150]
2065  * Opens the specified key
2066  *
2067  * Unlike RegCreateKeyEx, this does not create the key if it does not exist.
2068  *
2069  * PARAMS
2070  *    hkey       [I] Handle of open key
2071  *    lpszSubKey [I] Name of subkey to open
2072  *    dwReserved [I] Reserved - must be zero
2073  *    samDesired [I] Security access mask
2074  *    retkey     [O] Address of handle of open key
2075  *
2076  * RETURNS
2077  *    Success: ERROR_SUCCESS
2078  *    Failure: Error code
2079  */
2080 DWORD WINAPI RegOpenKeyExW( HKEY hkey, LPCWSTR lpszSubKey, DWORD dwReserved,
2081                               REGSAM samDesired, LPHKEY retkey )
2082 {
2083         LPKEYSTRUCT     lpNextKey,lpxkey;
2084         LPWSTR          *wps;
2085         int             wpc,i;
2086
2087     TRACE_(reg)("(0x%x,%s,%ld,%lx,%p)\n", hkey,debugstr_w(lpszSubKey),dwReserved,
2088           samDesired,retkey);
2089
2090     lpNextKey = lookup_hkey( hkey );
2091     if (!lpNextKey)
2092         return ERROR_INVALID_HANDLE;
2093
2094     if (!lpszSubKey || !*lpszSubKey) {
2095         /* Either NULL or pointer to empty string, so return a new handle
2096            to the original hkey */
2097         currenthandle += 2;
2098         add_handle(currenthandle,lpNextKey,samDesired);
2099         *retkey=currenthandle;
2100         return ERROR_SUCCESS;
2101     }
2102
2103     if (lpszSubKey[0] == '\\') {
2104         WARN_(reg)("Subkey %s must not begin with backslash.\n",debugstr_w(lpszSubKey));
2105         return ERROR_BAD_PATHNAME;
2106     }
2107
2108         split_keypath(lpszSubKey,&wps,&wpc);
2109         i = 0;
2110         while ((i<wpc) && (wps[i][0]=='\0')) i++;
2111         lpxkey = lpNextKey;
2112
2113     while (wps[i]) {
2114         lpxkey=lpNextKey->nextsub;
2115         while (lpxkey) {
2116             if (!lstrcmpiW(wps[i],lpxkey->keyname)) {
2117                 break;
2118             }
2119             lpxkey=lpxkey->next;
2120         }
2121
2122         if (!lpxkey) {
2123             TRACE_(reg)("Could not find subkey %s\n",debugstr_w(wps[i]));
2124             FREE_KEY_PATH;
2125             return ERROR_FILE_NOT_FOUND;
2126         }
2127         i++;
2128         lpNextKey = lpxkey;
2129     }
2130
2131     currenthandle += 2;
2132     add_handle(currenthandle,lpxkey,samDesired);
2133     *retkey = currenthandle;
2134     TRACE_(reg)("  Returning %x\n", currenthandle);
2135     FREE_KEY_PATH;
2136     return ERROR_SUCCESS;
2137 }
2138
2139
2140 /******************************************************************************
2141  * RegOpenKeyEx32A [ADVAPI32.149]
2142  */
2143 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR lpszSubKey, DWORD dwReserved,
2144                               REGSAM samDesired, LPHKEY retkey )
2145 {
2146     LPWSTR lpszSubKeyW = strdupA2W(lpszSubKey);
2147     DWORD ret;
2148
2149     TRACE_(reg)("(%x,%s,%ld,%lx,%p)\n",hkey,debugstr_a(lpszSubKey),dwReserved,
2150           samDesired,retkey);
2151     ret = RegOpenKeyExW( hkey, lpszSubKeyW, dwReserved, samDesired, retkey );
2152     free(lpszSubKeyW);
2153     return ret;
2154 }
2155
2156
2157 /******************************************************************************
2158  * RegOpenKey32W [ADVAPI32.151]
2159  *
2160  * PARAMS
2161  *    hkey       [I] Handle of open key
2162  *    lpszSubKey [I] Address of name of subkey to open
2163  *    retkey     [O] Address of handle of open key
2164  *
2165  * RETURNS
2166  *    Success: ERROR_SUCCESS
2167  *    Failure: Error code
2168  */
2169 DWORD WINAPI RegOpenKeyW( HKEY hkey, LPCWSTR lpszSubKey, LPHKEY retkey )
2170 {
2171     TRACE_(reg)("(%x,%s,%p)\n",hkey,debugstr_w(lpszSubKey),retkey);
2172     return RegOpenKeyExW( hkey, lpszSubKey, 0, KEY_ALL_ACCESS, retkey );
2173 }
2174
2175
2176 /******************************************************************************
2177  * RegOpenKey32A [ADVAPI32.148]
2178  */
2179 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey )
2180 {
2181     DWORD ret;
2182     LPWSTR lpszSubKeyW = strdupA2W(lpszSubKey);
2183     TRACE_(reg)("(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey);
2184     ret =  RegOpenKeyW( hkey, lpszSubKeyW, retkey );
2185     free(lpszSubKeyW);
2186     return ret;
2187 }
2188
2189
2190 /******************************************************************************
2191  * RegOpenKey16 [SHELL.1] [KERNEL.217]
2192  */
2193 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey )
2194 {
2195     TRACE_(reg)("(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey);
2196     return RegOpenKeyA( hkey, lpszSubKey, retkey );
2197 }
2198
2199
2200 /* 
2201  * Create keys
2202  * 
2203  * All those functions convert their respective 
2204  * arguments and call RegCreateKeyExW at the end.
2205  *
2206  * We stay away from the Ex functions as long as possible because there are
2207  * differences in the return values
2208  *
2209  * Callpath:
2210  *                                      RegCreateKeyEx32A \
2211  * RegCreateKey16 -> RegCreateKey32A -> RegCreateKey32W   -> RegCreateKeyEx32W
2212  */
2213
2214
2215 /******************************************************************************
2216  * RegCreateKeyEx32W [ADVAPI32.131]
2217  *
2218  * PARAMS
2219  *    hkey         [I] Handle of an open key
2220  *    lpszSubKey   [I] Address of subkey name
2221  *    dwReserved   [I] Reserved - must be 0
2222  *    lpszClass    [I] Address of class string
2223  *    fdwOptions   [I] Special options flag
2224  *    samDesired   [I] Desired security access
2225  *    lpSecAttribs [I] Address of key security structure
2226  *    retkey       [O] Address of buffer for opened handle
2227  *    lpDispos     [O] Receives REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY
2228  */
2229 DWORD WINAPI RegCreateKeyExW( HKEY hkey, LPCWSTR lpszSubKey, 
2230                                 DWORD dwReserved, LPWSTR lpszClass, 
2231                                 DWORD fdwOptions, REGSAM samDesired,
2232                                 LPSECURITY_ATTRIBUTES lpSecAttribs, 
2233                                 LPHKEY retkey, LPDWORD lpDispos )
2234 {
2235         LPKEYSTRUCT     *lplpPrevKey,lpNextKey,lpxkey;
2236         LPWSTR          *wps;
2237         int             wpc,i;
2238
2239     TRACE_(reg)("(%x,%s,%ld,%s,%lx,%lx,%p,%p,%p)\n", hkey,
2240                 debugstr_w(lpszSubKey), dwReserved, debugstr_w(lpszClass),
2241                 fdwOptions, samDesired, lpSecAttribs, retkey, lpDispos);
2242
2243     lpNextKey = lookup_hkey(hkey);
2244     if (!lpNextKey)
2245         return ERROR_INVALID_HANDLE;
2246
2247     /* Check for valid options */
2248     switch(fdwOptions) {
2249         case REG_OPTION_NON_VOLATILE:
2250         case REG_OPTION_VOLATILE:
2251         case REG_OPTION_BACKUP_RESTORE:
2252             break;
2253         default:
2254             return ERROR_INVALID_PARAMETER;
2255     }
2256
2257     /* Sam has to be a combination of the following */
2258     if (!(samDesired & 
2259           (KEY_ALL_ACCESS | KEY_CREATE_LINK | KEY_CREATE_SUB_KEY | 
2260            KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_NOTIFY |
2261            KEY_QUERY_VALUE | KEY_READ | KEY_SET_VALUE | KEY_WRITE)))
2262         return ERROR_INVALID_PARAMETER;
2263
2264         if (!lpszSubKey || !*lpszSubKey) {
2265                 currenthandle += 2;
2266                 add_handle(currenthandle,lpNextKey,samDesired);
2267                 *retkey=currenthandle;
2268                 TRACE_(reg)("Returning %x\n", currenthandle);
2269                 lpNextKey->flags|=REG_OPTION_TAINTED;
2270                 return ERROR_SUCCESS;
2271         }
2272
2273     if (lpszSubKey[0] == '\\') {
2274         WARN_(reg)("Subkey %s must not begin with backslash.\n",debugstr_w(lpszSubKey));
2275         return ERROR_BAD_PATHNAME;
2276     }
2277
2278         split_keypath(lpszSubKey,&wps,&wpc);
2279         i       = 0;
2280         while ((i<wpc) && (wps[i][0]=='\0')) i++;
2281         lpxkey  = lpNextKey;
2282         while (wps[i]) {
2283                 lpxkey=lpNextKey->nextsub;
2284                 while (lpxkey) {
2285                         if (!lstrcmpiW(wps[i],lpxkey->keyname))
2286                                 break;
2287                         lpxkey=lpxkey->next;
2288                 }
2289                 if (!lpxkey)
2290                         break;
2291                 i++;
2292                 lpNextKey       = lpxkey;
2293         }
2294         if (lpxkey) {
2295                 currenthandle += 2;
2296                 add_handle(currenthandle,lpxkey,samDesired);
2297                 lpxkey->flags  |= REG_OPTION_TAINTED;
2298                 *retkey         = currenthandle;
2299                 TRACE_(reg)("Returning %x\n", currenthandle);
2300                 if (lpDispos)
2301                         *lpDispos       = REG_OPENED_EXISTING_KEY;
2302                 FREE_KEY_PATH;
2303                 return ERROR_SUCCESS;
2304         }
2305
2306         /* Good.  Now the hard part */
2307         while (wps[i]) {
2308                 lplpPrevKey     = &(lpNextKey->nextsub);
2309                 lpxkey          = *lplpPrevKey;
2310                 while (lpxkey) {
2311                         lplpPrevKey     = &(lpxkey->next);
2312                         lpxkey          = *lplpPrevKey;
2313                 }
2314                 *lplpPrevKey=malloc(sizeof(KEYSTRUCT));
2315                 if (!*lplpPrevKey) {
2316                         FREE_KEY_PATH;
2317                         TRACE_(reg)("Returning OUTOFMEMORY\n");
2318                         return ERROR_OUTOFMEMORY;
2319                 }
2320                 memset(*lplpPrevKey,'\0',sizeof(KEYSTRUCT));
2321                 TRACE_(reg)("Adding %s\n", debugstr_w(wps[i]));
2322                 (*lplpPrevKey)->keyname = strdupW(wps[i]);
2323                 (*lplpPrevKey)->next    = NULL;
2324                 (*lplpPrevKey)->nextsub = NULL;
2325                 (*lplpPrevKey)->values  = NULL;
2326                 (*lplpPrevKey)->nrofvalues = 0;
2327                 (*lplpPrevKey)->flags   = REG_OPTION_TAINTED;
2328                 if (lpszClass)
2329                         (*lplpPrevKey)->class = strdupW(lpszClass);
2330                 else
2331                         (*lplpPrevKey)->class = NULL;
2332                 lpNextKey       = *lplpPrevKey;
2333                 i++;
2334         }
2335         currenthandle += 2;
2336         add_handle(currenthandle,lpNextKey,samDesired);
2337
2338         /*FIXME: flag handling correct? */
2339         lpNextKey->flags= fdwOptions |REG_OPTION_TAINTED;
2340         if (lpszClass)
2341                 lpNextKey->class = strdupW(lpszClass);
2342         else
2343                 lpNextKey->class = NULL;
2344         *retkey         = currenthandle;
2345         TRACE_(reg)("Returning %x\n", currenthandle);
2346         if (lpDispos)
2347                 *lpDispos       = REG_CREATED_NEW_KEY;
2348         FREE_KEY_PATH;
2349         return ERROR_SUCCESS;
2350 }
2351
2352
2353 /******************************************************************************
2354  * RegCreateKeyEx32A [ADVAPI32.130]
2355  */
2356 DWORD WINAPI RegCreateKeyExA( HKEY hkey, LPCSTR lpszSubKey, DWORD dwReserved,
2357                                 LPSTR lpszClass, DWORD fdwOptions, 
2358                                 REGSAM samDesired, 
2359                                 LPSECURITY_ATTRIBUTES lpSecAttribs, 
2360                                 LPHKEY retkey, LPDWORD lpDispos )
2361 {
2362     LPWSTR lpszSubKeyW, lpszClassW;
2363     DWORD  ret;
2364
2365     TRACE_(reg)("(%x,%s,%ld,%s,%lx,%lx,%p,%p,%p)\n",hkey,debugstr_a(lpszSubKey),
2366           dwReserved,debugstr_a(lpszClass),fdwOptions,samDesired,lpSecAttribs,
2367           retkey,lpDispos);
2368
2369     lpszSubKeyW = lpszSubKey?strdupA2W(lpszSubKey):NULL;
2370     lpszClassW = lpszClass?strdupA2W(lpszClass):NULL;
2371
2372     ret = RegCreateKeyExW( hkey, lpszSubKeyW, dwReserved, lpszClassW, 
2373                              fdwOptions, samDesired, lpSecAttribs, retkey, 
2374                              lpDispos );
2375
2376     if(lpszSubKeyW) free(lpszSubKeyW);
2377     if(lpszClassW) free(lpszClassW);
2378
2379     return ret;
2380 }
2381
2382
2383 /******************************************************************************
2384  * RegCreateKey32W [ADVAPI32.132]
2385  */
2386 DWORD WINAPI RegCreateKeyW( HKEY hkey, LPCWSTR lpszSubKey, LPHKEY retkey )
2387 {
2388     DWORD junk;
2389     LPKEYSTRUCT lpNextKey;
2390
2391     TRACE_(reg)("(%x,%s,%p)\n", hkey,debugstr_w(lpszSubKey),retkey);
2392
2393     /* This check is here because the return value is different than the
2394        one from the Ex functions */
2395     lpNextKey = lookup_hkey(hkey);
2396     if (!lpNextKey)
2397         return ERROR_BADKEY;
2398
2399     return RegCreateKeyExW( hkey, lpszSubKey, 0, NULL, 
2400                               REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
2401                               retkey, &junk);
2402 }
2403
2404
2405 /******************************************************************************
2406  * RegCreateKey32A [ADVAPI32.129]
2407  */
2408 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey )
2409 {
2410     DWORD ret;
2411     LPWSTR lpszSubKeyW;
2412
2413     TRACE_(reg)("(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey);
2414     lpszSubKeyW = lpszSubKey?strdupA2W(lpszSubKey):NULL;
2415     ret = RegCreateKeyW( hkey, lpszSubKeyW, retkey );
2416     if(lpszSubKeyW) free(lpszSubKeyW);
2417     return ret;
2418 }
2419
2420
2421 /******************************************************************************
2422  * RegCreateKey16 [SHELL.2] [KERNEL.218]
2423  */
2424 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR lpszSubKey, LPHKEY retkey )
2425 {
2426     TRACE_(reg)("(%x,%s,%p)\n",hkey,debugstr_a(lpszSubKey),retkey);
2427     return RegCreateKeyA( hkey, lpszSubKey, retkey );
2428 }
2429
2430
2431 /* 
2432  * Query Value Functions
2433  * Win32 differs between keynames and valuenames. 
2434  * multiple values may belong to one key, the special value
2435  * with name NULL is the default value used by the win31
2436  * compat functions.
2437  *
2438  * Callpath:
2439  * RegQueryValue16 -> RegQueryValue32A -> RegQueryValueEx32A \
2440  *                                        RegQueryValue32W -> RegQueryValueEx32W
2441  */
2442
2443
2444 /******************************************************************************
2445  * RegQueryValueEx32W [ADVAPI32.158]
2446  * Retrieves type and data for a specified name associated with an open key
2447  *
2448  * PARAMS
2449  *    hkey          [I]   Handle of key to query
2450  *    lpValueName   [I]   Name of value to query
2451  *    lpdwReserved  [I]   Reserved - must be NULL
2452  *    lpdwType      [O]   Address of buffer for value type.  If NULL, the type
2453  *                        is not required.
2454  *    lpbData       [O]   Address of data buffer.  If NULL, the actual data is
2455  *                        not required.
2456  *    lpcbData      [I/O] Address of data buffer size
2457  *
2458  * RETURNS 
2459  *    ERROR_SUCCESS:   Success
2460  *    ERROR_MORE_DATA: !!! if the specified buffer is not big enough to hold the data
2461  *                     buffer is left untouched. The MS-documentation is wrong (js) !!!
2462  */
2463 DWORD WINAPI RegQueryValueExW( HKEY hkey, LPWSTR lpValueName,
2464                                  LPDWORD lpdwReserved, LPDWORD lpdwType,
2465                                  LPBYTE lpbData, LPDWORD lpcbData )
2466 {
2467         LPKEYSTRUCT     lpkey;
2468         int             i;
2469         DWORD           ret;
2470
2471         TRACE_(reg)("(0x%x,%s,%p,%p,%p,%p=%ld)\n", hkey, debugstr_w(lpValueName),
2472           lpdwReserved, lpdwType, lpbData, lpcbData, lpcbData?*lpcbData:0);
2473
2474         lpkey = lookup_hkey(hkey);
2475
2476         if (!lpkey)
2477           return ERROR_INVALID_HANDLE;
2478
2479         if ((lpbData && ! lpcbData) || lpdwReserved)
2480           return ERROR_INVALID_PARAMETER;
2481
2482         /* An empty name string is equivalent to NULL */
2483         if (lpValueName && !*lpValueName)
2484           lpValueName = NULL;
2485
2486         if (lpValueName==NULL) 
2487         { /* Use key's unnamed or default value, if any */
2488           for (i=0;i<lpkey->nrofvalues;i++)
2489             if (lpkey->values[i].name==NULL)
2490               break;
2491         } 
2492         else 
2493         { /* Search for the key name */
2494           for (i=0;i<lpkey->nrofvalues;i++)
2495             if ( lpkey->values[i].name && !lstrcmpiW(lpValueName,lpkey->values[i].name))
2496               break;
2497         }
2498
2499         if (i==lpkey->nrofvalues) 
2500         { TRACE_(reg)(" Key not found\n");
2501           if (lpValueName==NULL) 
2502           { /* Empty keyname not found */
2503             if (lpbData) 
2504             { *(WCHAR*)lpbData = 0;
2505               *lpcbData = 2;
2506             }
2507             if (lpdwType)
2508               *lpdwType = REG_SZ;
2509             TRACE_(reg)(" Returning an empty string\n");
2510             return ERROR_SUCCESS;
2511           }
2512           return ERROR_FILE_NOT_FOUND;
2513         }
2514
2515         ret = ERROR_SUCCESS;
2516
2517         if (lpdwType)                                   /* type required ?*/
2518           *lpdwType = lpkey->values[i].type;
2519
2520         if (lpbData)                                    /* data required ?*/
2521         { if (*lpcbData >= lpkey->values[i].len)        /* buffer large enought ?*/
2522             memcpy(lpbData,lpkey->values[i].data,lpkey->values[i].len);
2523           else
2524             ret = ERROR_MORE_DATA;
2525         }
2526
2527         if (lpcbData)                                   /* size required ?*/
2528         { *lpcbData = lpkey->values[i].len;
2529         }
2530
2531         debug_print_value ( lpbData, &lpkey->values[i]);
2532
2533         TRACE_(reg)(" (ret=%lx, type=%lx, len=%ld)\n", ret, lpdwType?*lpdwType:0,lpcbData?*lpcbData:0);
2534
2535         return ret;
2536 }
2537
2538
2539 /******************************************************************************
2540  * RegQueryValue32W [ADVAPI32.159]
2541  */
2542 DWORD WINAPI RegQueryValueW( HKEY hkey, LPCWSTR lpszSubKey, LPWSTR lpszData,
2543                                LPLONG lpcbData )
2544 {
2545         HKEY    xhkey;
2546         DWORD   ret,lpdwType;
2547
2548     TRACE_(reg)("(%x,%s,%p,%ld)\n",hkey,debugstr_w(lpszSubKey),lpszData,
2549           lpcbData?*lpcbData:0);
2550
2551     /* Only open subkey, if we really do descend */
2552     if (lpszSubKey && *lpszSubKey) {
2553         ret = RegOpenKeyW( hkey, lpszSubKey, &xhkey );
2554         if (ret != ERROR_SUCCESS) {
2555             WARN_(reg)("Could not open %s\n", debugstr_w(lpszSubKey));
2556             return ret;
2557         }
2558     } else
2559         xhkey = hkey;
2560
2561     lpdwType = REG_SZ;
2562     ret = RegQueryValueExW( xhkey, NULL, NULL, &lpdwType, (LPBYTE)lpszData,
2563                               lpcbData );
2564     if (xhkey != hkey)
2565         RegCloseKey(xhkey);
2566     return ret;
2567 }
2568
2569
2570 /******************************************************************************
2571  * RegQueryValueEx32A [ADVAPI32.157]
2572  *
2573  * NOTES:
2574  * the documantation is wrong: if the buffer is to small it remains untouched 
2575  *
2576  * FIXME: check returnvalue (len) for an empty key
2577  */
2578 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPSTR lpszValueName,
2579                                  LPDWORD lpdwReserved, LPDWORD lpdwType,
2580                                  LPBYTE lpbData, LPDWORD lpcbData )
2581 {
2582         LPWSTR  lpszValueNameW;
2583         LPBYTE  mybuf = NULL;
2584         DWORD   ret, mytype, mylen = 0;
2585
2586         TRACE_(reg)("(%x,%s,%p,%p,%p,%p=%ld)\n", hkey,debugstr_a(lpszValueName),
2587           lpdwReserved,lpdwType,lpbData,lpcbData,lpcbData?*lpcbData:0);
2588
2589         if (!lpcbData && lpbData)                       /* buffer without size is illegal */
2590         {  return ERROR_INVALID_PARAMETER;
2591         }
2592
2593         lpszValueNameW = lpszValueName ? strdupA2W(lpszValueName) : NULL;       
2594         
2595         /* get just the type first */
2596         ret = RegQueryValueExW( hkey, lpszValueNameW, lpdwReserved, &mytype, NULL, NULL );
2597         
2598         if ( ret != ERROR_SUCCESS )                     /* failed ?? */
2599         { if(lpszValueNameW) free(lpszValueNameW);
2600           return ret;
2601         }
2602         
2603         if (lpcbData)                                   /* at least length requested? */
2604         { if (UNICONVMASK & (1<<(mytype)))              /* string requested? */
2605           { if (lpbData )                               /* value requested? */
2606             { mylen = 2*( *lpcbData );
2607               mybuf = (LPBYTE)xmalloc( mylen );
2608             }
2609
2610             ret = RegQueryValueExW( hkey, lpszValueNameW, lpdwReserved, lpdwType, mybuf, &mylen );
2611
2612             if (ret == ERROR_SUCCESS )
2613             { if ( lpbData )
2614               { lmemcpynWtoA(lpbData, (LPWSTR)mybuf, mylen/2);
2615               }
2616             }
2617
2618             *lpcbData = mylen/2;                        /* size is in byte! */
2619           }
2620           else                                          /* no strings, call it straight */
2621           { ret = RegQueryValueExW( hkey, lpszValueNameW, lpdwReserved, lpdwType, lpbData, lpcbData );
2622           }
2623         }
2624         
2625         if (lpdwType)                                   /* type when requested */
2626         { *lpdwType = mytype;
2627         }
2628
2629         TRACE_(reg)(" (ret=%lx,type=%lx, len=%ld)\n", ret,mytype,lpcbData?*lpcbData:0);
2630         
2631         if(mybuf) free(mybuf);
2632         if(lpszValueNameW) free(lpszValueNameW);
2633         return ret;
2634 }
2635
2636
2637 /******************************************************************************
2638  * RegQueryValueEx16 [KERNEL.225]
2639  */
2640 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPSTR lpszValueName,
2641                                 LPDWORD lpdwReserved, LPDWORD lpdwType,
2642                                 LPBYTE lpbData, LPDWORD lpcbData )
2643 {
2644     TRACE_(reg)("(%x,%s,%p,%p,%p,%ld)\n",hkey,debugstr_a(lpszValueName),
2645           lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0);
2646     return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType,
2647                                lpbData, lpcbData );
2648 }
2649
2650
2651 /******************************************************************************
2652  * RegQueryValue32A [ADVAPI32.156]
2653  */
2654 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR lpszSubKey, LPSTR lpszData,
2655                                LPLONG lpcbData )
2656 {
2657     HKEY xhkey;
2658     DWORD ret, dwType;
2659
2660     TRACE_(reg)("(%x,%s,%p,%ld)\n",hkey,debugstr_a(lpszSubKey),lpszData,
2661           lpcbData?*lpcbData:0);
2662
2663     if (lpszSubKey && *lpszSubKey) {
2664         ret = RegOpenKey16( hkey, lpszSubKey, &xhkey );
2665         if( ret != ERROR_SUCCESS )
2666             return ret;
2667     } else
2668         xhkey = hkey;
2669
2670     dwType = REG_SZ;
2671     ret = RegQueryValueExA( xhkey, NULL,NULL, &dwType, (LPBYTE)lpszData,
2672                               lpcbData );
2673     if( xhkey != hkey )
2674         RegCloseKey( xhkey );
2675     return ret;
2676 }
2677
2678
2679 /******************************************************************************
2680  * RegQueryValue16 [SHELL.6] [KERNEL.224]
2681  *
2682  * NOTES
2683  *    Is this HACK still applicable?
2684  *
2685  * HACK
2686  *    The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
2687  *    mask out the high 16 bit.  This (not so much incidently) hopefully fixes
2688  *    Aldus FH4)
2689  */
2690 DWORD WINAPI RegQueryValue16( HKEY hkey, LPSTR lpszSubKey, LPSTR lpszData,
2691                               LPDWORD lpcbData )
2692 {
2693     TRACE_(reg)("(%x,%s,%p,%ld)\n",hkey,debugstr_a(lpszSubKey),lpszData,
2694           lpcbData?*lpcbData:0);
2695
2696     if (lpcbData)
2697         *lpcbData &= 0xFFFF;
2698     return RegQueryValueA(hkey,lpszSubKey,lpszData,lpcbData);
2699 }
2700
2701
2702 /*
2703  * Setting values of Registry keys
2704  *
2705  * Callpath:
2706  * RegSetValue16 -> RegSetValue32A -> RegSetValueEx32A \
2707  *                                    RegSetValue32W   -> RegSetValueEx32W
2708  */
2709
2710
2711 /******************************************************************************
2712  * RegSetValueEx32W [ADVAPI32.170]
2713  * Sets the data and type of a value under a register key
2714  *
2715  * PARAMS
2716  *    hkey          [I] Handle of key to set value for
2717  *    lpszValueName [I] Name of value to set
2718  *    dwReserved    [I] Reserved - must be zero
2719  *    dwType        [I] Flag for value type
2720  *    lpbData       [I] Address of value data
2721  *    cbData        [I] Size of value data
2722  *
2723  * RETURNS
2724  *    Success: ERROR_SUCCESS
2725  *    Failure: Error code
2726  *
2727  * NOTES
2728  *   win95 does not care about cbData for REG_SZ and finds out the len by itself (js) 
2729  */
2730 DWORD WINAPI RegSetValueExW( HKEY hkey, LPWSTR lpszValueName, 
2731                                DWORD dwReserved, DWORD dwType, LPBYTE lpbData,
2732                                DWORD cbData)
2733 {
2734         LPKEYSTRUCT lpkey;
2735         int i;
2736
2737         TRACE_(reg)("(%x,%s,%ld,%ld,%p,%ld)\n", hkey, debugstr_w(lpszValueName),
2738           dwReserved, dwType, lpbData, cbData);
2739
2740         lpkey = lookup_hkey( hkey );
2741
2742         if (!lpkey)
2743           return ERROR_INVALID_HANDLE;
2744
2745         lpkey->flags |= REG_OPTION_TAINTED;
2746
2747         if (lpszValueName==NULL) {
2748              /* Sets type and name for key's unnamed or default value */
2749                 for (i=0;i<lpkey->nrofvalues;i++)
2750                         if (lpkey->values[i].name==NULL)
2751                                 break;
2752         } else {
2753                 for (i=0;i<lpkey->nrofvalues;i++)
2754                         if (    lpkey->values[i].name &&
2755                                 !lstrcmpiW(lpszValueName,lpkey->values[i].name)
2756                         )
2757                                 break;
2758         }
2759         if (i==lpkey->nrofvalues) {
2760                 lpkey->values = (LPKEYVALUE)xrealloc(
2761                                         lpkey->values,
2762                                         (lpkey->nrofvalues+1)*sizeof(KEYVALUE)
2763                                 );
2764                 lpkey->nrofvalues++;
2765                 memset(lpkey->values+i,'\0',sizeof(KEYVALUE));
2766         }
2767         if (lpkey->values[i].name==NULL) {
2768                 if (lpszValueName)
2769                         lpkey->values[i].name = strdupW(lpszValueName);
2770                 else
2771                         lpkey->values[i].name = NULL;
2772         }
2773
2774         if (dwType == REG_SZ)
2775           cbData = 2 * (lstrlenW ((LPCWSTR)lpbData) + 1);
2776           
2777         lpkey->values[i].len    = cbData;
2778         lpkey->values[i].type   = dwType;
2779         if (lpkey->values[i].data !=NULL)
2780                 free(lpkey->values[i].data);
2781         lpkey->values[i].data   = (LPBYTE)xmalloc(cbData);
2782         lpkey->values[i].lastmodified = time(NULL);
2783         memcpy(lpkey->values[i].data,lpbData,cbData);
2784         return ERROR_SUCCESS;
2785 }
2786
2787
2788 /******************************************************************************
2789  * RegSetValueEx32A [ADVAPI32.169]
2790  *
2791  * NOTES
2792  *   win95 does not care about cbData for REG_SZ and finds out the len by itself (js) 
2793  */
2794 DWORD WINAPI RegSetValueExA( HKEY hkey, LPSTR lpszValueName,
2795                                DWORD dwReserved, DWORD dwType, LPBYTE lpbData,
2796                                DWORD cbData )
2797 {
2798         LPBYTE  buf;
2799         LPWSTR  lpszValueNameW;
2800         DWORD   ret;
2801
2802         if (!lpbData)
2803                 return (ERROR_INVALID_PARAMETER);
2804                 
2805         TRACE_(reg)("(%x,%s,%ld,%ld,%p,%ld)\n",hkey,debugstr_a(lpszValueName),
2806           dwReserved,dwType,lpbData,cbData);
2807
2808         if ((1<<dwType) & UNICONVMASK) 
2809         { if (dwType == REG_SZ)
2810             cbData = strlen ((LPCSTR)lpbData)+1;
2811
2812           buf = (LPBYTE)xmalloc( cbData *2 );
2813           lmemcpynAtoW ((LPVOID)buf, lpbData, cbData );
2814           cbData=2*cbData;
2815         } 
2816         else
2817           buf=lpbData;
2818
2819         if (lpszValueName)
2820           lpszValueNameW = strdupA2W(lpszValueName);
2821         else
2822           lpszValueNameW = NULL;
2823
2824         ret=RegSetValueExW(hkey,lpszValueNameW,dwReserved,dwType,buf,cbData);
2825
2826         if (lpszValueNameW)
2827           free(lpszValueNameW);
2828
2829         if (buf!=lpbData)
2830           free(buf);
2831
2832         return ret;
2833 }
2834
2835
2836 /******************************************************************************
2837  * RegSetValueEx16 [KERNEL.226]
2838  */
2839 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPSTR lpszValueName, DWORD dwReserved,
2840                               DWORD dwType, LPBYTE lpbData, DWORD cbData )
2841 {
2842     TRACE_(reg)("(%x,%s,%ld,%ld,%p,%ld)\n",hkey,debugstr_a(lpszValueName),
2843           dwReserved,dwType,lpbData,cbData);
2844     return RegSetValueExA( hkey, lpszValueName, dwReserved, dwType, lpbData,
2845                              cbData );
2846 }
2847
2848
2849 /******************************************************************************
2850  * RegSetValue32W       [ADVAPI32.171]
2851  */
2852 DWORD WINAPI RegSetValueW( HKEY hkey, LPCWSTR lpszSubKey, DWORD dwType,
2853                              LPCWSTR lpszData, DWORD cbData )
2854 {
2855         HKEY    xhkey;
2856         DWORD   ret;
2857
2858         TRACE_(reg)("(%x,%s,%ld,%s,%ld)\n",
2859                 hkey,debugstr_w(lpszSubKey),dwType,debugstr_w(lpszData),cbData
2860         );
2861         if (lpszSubKey && *lpszSubKey) {
2862                 ret=RegCreateKeyW(hkey,lpszSubKey,&xhkey);
2863                 if (ret!=ERROR_SUCCESS)
2864                         return ret;
2865         } else
2866                 xhkey=hkey;
2867         if (dwType!=REG_SZ) {
2868                 TRACE_(reg)("dwType=%ld - Changing to REG_SZ\n",dwType);
2869                 dwType=REG_SZ;
2870         }
2871         if (cbData!=2*lstrlenW(lpszData)+2) {
2872                 TRACE_(reg)("Len=%ld != strlen(%s)+1=%d!\n",
2873                         cbData,debugstr_w(lpszData),2*lstrlenW(lpszData)+2
2874                 );
2875                 cbData=2*lstrlenW(lpszData)+2;
2876         }
2877         ret=RegSetValueExW(xhkey,NULL,0,dwType,(LPBYTE)lpszData,cbData);
2878         if (hkey!=xhkey)
2879                 RegCloseKey(xhkey);
2880         return ret;
2881 }
2882
2883
2884 /******************************************************************************
2885  * RegSetValue32A [ADVAPI32.168]
2886  *
2887  */
2888 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR lpszSubKey, DWORD dwType,
2889                              LPCSTR lpszData, DWORD cbData )
2890 {
2891         DWORD   ret;
2892         HKEY    xhkey;
2893
2894         TRACE_(reg)("(%x,%s,%ld,%s,%ld)\n",hkey,lpszSubKey,dwType,lpszData,cbData);
2895         if (lpszSubKey && *lpszSubKey) {
2896                 ret=RegCreateKey16(hkey,lpszSubKey,&xhkey);
2897                 if (ret!=ERROR_SUCCESS)
2898                         return ret;
2899         } else
2900                 xhkey=hkey;
2901
2902         if (dwType!=REG_SZ) {
2903                 TRACE_(reg)("dwType=%ld!\n",dwType);
2904                 dwType=REG_SZ;
2905         }
2906         if (cbData!=strlen(lpszData)+1)
2907                 cbData=strlen(lpszData)+1;
2908         ret=RegSetValueExA(xhkey,NULL,0,dwType,(LPBYTE)lpszData,cbData);
2909         if (xhkey!=hkey)
2910                 RegCloseKey(xhkey);
2911         return ret;
2912 }
2913
2914
2915 /******************************************************************************
2916  * RegSetValue16 [KERNEL.221] [SHELL.5]
2917  */
2918 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR lpszSubKey, DWORD dwType,
2919                             LPCSTR lpszData, DWORD cbData )
2920 {
2921     TRACE_(reg)("(%x,%s,%ld,%s,%ld)\n",hkey,debugstr_a(lpszSubKey),dwType,
2922           debugstr_a(lpszData),cbData);
2923     return RegSetValueA(hkey,lpszSubKey,dwType,lpszData,cbData);
2924 }
2925
2926
2927 /* 
2928  * Key Enumeration
2929  *
2930  * Callpath:
2931  * RegEnumKey16 -> RegEnumKey32A -> RegEnumKeyEx32A \
2932  *                                  RegEnumKey32W   -> RegEnumKeyEx32W
2933  */
2934
2935
2936 /******************************************************************************
2937  * RegEnumKeyEx32W [ADVAPI32.139]
2938  *
2939  * PARAMS
2940  *    hkey         [I] Handle to key to enumerate
2941  *    iSubKey      [I] Index of subkey to enumerate
2942  *    lpszName     [O] Buffer for subkey name
2943  *    lpcchName    [O] Size of subkey buffer
2944  *    lpdwReserved [I] Reserved
2945  *    lpszClass    [O] Buffer for class string
2946  *    lpcchClass   [O] Size of class buffer
2947  *    ft           [O] Time key last written to
2948  */
2949 DWORD WINAPI RegEnumKeyExW( HKEY hkey, DWORD iSubkey, LPWSTR lpszName,
2950                               LPDWORD lpcchName, LPDWORD lpdwReserved,
2951                               LPWSTR lpszClass, LPDWORD lpcchClass, 
2952                               FILETIME *ft )
2953 {
2954         LPKEYSTRUCT     lpkey,lpxkey;
2955
2956     TRACE_(reg)("(%x,%ld,%p,%ld,%p,%p,%p,%p)\n",hkey,iSubkey,lpszName,
2957           *lpcchName,lpdwReserved,lpszClass,lpcchClass,ft);
2958
2959     lpkey = lookup_hkey( hkey );
2960     if (!lpkey)
2961         return ERROR_INVALID_HANDLE;
2962
2963         if (!lpkey->nextsub)
2964                 return ERROR_NO_MORE_ITEMS;
2965         lpxkey=lpkey->nextsub;
2966
2967     /* Traverse the subkeys */
2968         while (iSubkey && lpxkey) {
2969                 iSubkey--;
2970                 lpxkey=lpxkey->next;
2971         }
2972
2973         if (iSubkey || !lpxkey)
2974                 return ERROR_NO_MORE_ITEMS;
2975         if (lstrlenW(lpxkey->keyname)+1>*lpcchName)
2976                 return ERROR_MORE_DATA;
2977         memcpy(lpszName,lpxkey->keyname,lstrlenW(lpxkey->keyname)*2+2);
2978
2979         if (*lpcchName)
2980             *lpcchName = lstrlenW(lpszName);
2981
2982         if (lpszClass) {
2983                 /* FIXME: what should we write into it? */
2984                 *lpszClass      = 0;
2985                 *lpcchClass     = 2;
2986         }
2987         return ERROR_SUCCESS;
2988 }
2989
2990
2991 /******************************************************************************
2992  * RegEnumKey32W [ADVAPI32.140]
2993  */
2994 DWORD WINAPI RegEnumKeyW( HKEY hkey, DWORD iSubkey, LPWSTR lpszName, 
2995                             DWORD lpcchName )
2996 {
2997     FILETIME    ft;
2998
2999     TRACE_(reg)("(%x,%ld,%p,%ld)\n",hkey,iSubkey,lpszName,lpcchName);
3000     return RegEnumKeyExW(hkey,iSubkey,lpszName,&lpcchName,NULL,NULL,NULL,&ft);
3001 }
3002
3003
3004 /******************************************************************************
3005  * RegEnumKeyEx32A [ADVAPI32.138]
3006  */
3007 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD iSubkey, LPSTR lpszName,
3008                               LPDWORD lpcchName, LPDWORD lpdwReserved, 
3009                               LPSTR lpszClass, LPDWORD lpcchClass, 
3010                               FILETIME *ft )
3011 {
3012         DWORD   ret,lpcchNameW,lpcchClassW;
3013         LPWSTR  lpszNameW,lpszClassW;
3014
3015
3016         TRACE_(reg)("(%x,%ld,%p,%ld,%p,%p,%p,%p)\n",
3017                 hkey,iSubkey,lpszName,*lpcchName,lpdwReserved,lpszClass,lpcchClass,ft
3018         );
3019         if (lpszName) {
3020                 lpszNameW       = (LPWSTR)xmalloc(*lpcchName*2);
3021                 lpcchNameW      = *lpcchName;
3022         } else {
3023                 lpszNameW       = NULL;
3024                 lpcchNameW      = 0;
3025         }
3026         if (lpszClass) {
3027                 lpszClassW              = (LPWSTR)xmalloc(*lpcchClass*2);
3028                 lpcchClassW     = *lpcchClass;
3029         } else {
3030                 lpszClassW      =0;
3031                 lpcchClassW=0;
3032         }
3033         ret=RegEnumKeyExW(
3034                 hkey,
3035                 iSubkey,
3036                 lpszNameW,
3037                 &lpcchNameW,
3038                 lpdwReserved,
3039                 lpszClassW,
3040                 &lpcchClassW,
3041                 ft
3042         );
3043         if (ret==ERROR_SUCCESS) {
3044                 lstrcpyWtoA(lpszName,lpszNameW);
3045                 *lpcchName=strlen(lpszName);
3046                 if (lpszClassW) {
3047                         lstrcpyWtoA(lpszClass,lpszClassW);
3048                         *lpcchClass=strlen(lpszClass);
3049                 }
3050         }
3051         if (lpszNameW)
3052                 free(lpszNameW);
3053         if (lpszClassW)
3054                 free(lpszClassW);
3055         return ret;
3056 }
3057
3058
3059 /******************************************************************************
3060  * RegEnumKey32A [ADVAPI32.137]
3061  */
3062 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD iSubkey, LPSTR lpszName,
3063                             DWORD lpcchName )
3064 {
3065     FILETIME    ft;
3066
3067     TRACE_(reg)("(%x,%ld,%p,%ld)\n",hkey,iSubkey,lpszName,lpcchName);
3068     return RegEnumKeyExA( hkey, iSubkey, lpszName, &lpcchName, NULL, NULL, 
3069                             NULL, &ft );
3070 }
3071
3072
3073 /******************************************************************************
3074  * RegEnumKey16 [SHELL.7] [KERNEL.216]
3075  */
3076 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD iSubkey, LPSTR lpszName,
3077                            DWORD lpcchName )
3078 {
3079     TRACE_(reg)("(%x,%ld,%p,%ld)\n",hkey,iSubkey,lpszName,lpcchName);
3080     return RegEnumKeyA( hkey, iSubkey, lpszName, lpcchName);
3081 }
3082
3083
3084 /* 
3085  * Enumerate Registry Values
3086  *
3087  * Callpath:
3088  * RegEnumValue16 -> RegEnumValue32A -> RegEnumValue32W
3089  */
3090
3091
3092 /******************************************************************************
3093  * RegEnumValue32W [ADVAPI32.142]
3094  *
3095  * PARAMS
3096  *    hkey        [I] Handle to key to query
3097  *    iValue      [I] Index of value to query
3098  *    lpszValue   [O] Value string
3099  *    lpcchValue  [I/O] Size of value buffer (in wchars)
3100  *    lpdReserved [I] Reserved
3101  *    lpdwType    [O] Type code
3102  *    lpbData     [O] Value data
3103  *    lpcbData    [I/O] Size of data buffer (in bytes)
3104  *
3105  * Note:  wide character functions that take and/or return "character counts"
3106  *  use TCHAR (that is unsigned short or char) not byte counts.
3107  */
3108 DWORD WINAPI RegEnumValueW( HKEY hkey, DWORD iValue, LPWSTR lpszValue,
3109                               LPDWORD lpcchValue, LPDWORD lpdReserved,
3110                               LPDWORD lpdwType, LPBYTE lpbData, 
3111                               LPDWORD lpcbData )
3112 {
3113         LPKEYSTRUCT     lpkey;
3114         LPKEYVALUE      val;
3115
3116         TRACE_(reg)("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",hkey,iValue,debugstr_w(lpszValue),
3117           lpcchValue,lpdReserved,lpdwType,lpbData,lpcbData);
3118
3119         lpkey = lookup_hkey( hkey );
3120         
3121         if (!lpcbData && lpbData)
3122                 return ERROR_INVALID_PARAMETER;
3123                 
3124         if (!lpkey)
3125                 return ERROR_INVALID_HANDLE;
3126
3127         if (lpkey->nrofvalues <= iValue)
3128                 return ERROR_NO_MORE_ITEMS;
3129
3130         val = &(lpkey->values[iValue]);
3131
3132         if (val->name) {
3133                 if (lstrlenW(val->name)+1>*lpcchValue) {
3134                         *lpcchValue = lstrlenW(val->name)+1;
3135                         return ERROR_MORE_DATA;
3136                 }
3137                 memcpy(lpszValue,val->name,2 * (lstrlenW(val->name)+1) );
3138                 *lpcchValue=lstrlenW(val->name);
3139         } else {
3140                 *lpszValue      = 0;
3141                 *lpcchValue     = 0;
3142         }
3143
3144         /* Can be NULL if the type code is not required */
3145         if (lpdwType)
3146                 *lpdwType = val->type;
3147
3148         if (lpbData) {
3149                 if (val->len>*lpcbData)
3150                         return ERROR_MORE_DATA;
3151                 memcpy(lpbData,val->data,val->len);
3152                 *lpcbData = val->len;
3153         }
3154
3155         debug_print_value ( val->data, val );
3156         return ERROR_SUCCESS;
3157 }
3158
3159
3160 /******************************************************************************
3161  * RegEnumValue32A [ADVAPI32.141]
3162  */
3163 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD iValue, LPSTR lpszValue,
3164                               LPDWORD lpcchValue, LPDWORD lpdReserved,
3165                               LPDWORD lpdwType, LPBYTE lpbData, 
3166                               LPDWORD lpcbData )
3167 {
3168         LPWSTR  lpszValueW;
3169         LPBYTE  lpbDataW;
3170         DWORD   ret,lpcbDataW;
3171         DWORD dwType;
3172
3173         TRACE_(reg)("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",hkey,iValue,lpszValue,lpcchValue,
3174                 lpdReserved,lpdwType,lpbData,lpcbData);
3175
3176         lpszValueW = (LPWSTR)xmalloc(*lpcchValue*2);
3177         if (lpbData) {
3178                 lpbDataW = (LPBYTE)xmalloc(*lpcbData*2);
3179                 lpcbDataW = *lpcbData;
3180         } else
3181                 lpbDataW = NULL;
3182
3183         ret = RegEnumValueW( hkey, iValue, lpszValueW, lpcchValue, 
3184                                 lpdReserved, &dwType, lpbDataW, &lpcbDataW );
3185
3186         if (lpdwType)
3187                 *lpdwType = dwType;
3188
3189         if (ret==ERROR_SUCCESS) {
3190                 lstrcpyWtoA(lpszValue,lpszValueW);
3191                 if (lpbData) {
3192                         if ((1<<dwType) & UNICONVMASK) {
3193                                 lstrcpyWtoA(lpbData,(LPWSTR)lpbDataW);
3194                         } else {
3195                                 if (lpcbDataW > *lpcbData)
3196                                         ret     = ERROR_MORE_DATA;
3197                                 else
3198                                         memcpy(lpbData,lpbDataW,lpcbDataW);
3199                         }
3200                         *lpcbData = lpcbDataW;
3201                 }
3202         }
3203     if (lpbDataW) free(lpbDataW);
3204     if (lpszValueW) free(lpszValueW);
3205     return ret;
3206 }
3207
3208
3209 /******************************************************************************
3210  * RegEnumValue16 [KERNEL.223]
3211  */
3212 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD iValue, LPSTR lpszValue, 
3213                              LPDWORD lpcchValue, LPDWORD lpdReserved, 
3214                              LPDWORD lpdwType, LPBYTE lpbData, 
3215                              LPDWORD lpcbData )
3216 {
3217     TRACE_(reg)("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",hkey,iValue,lpszValue,lpcchValue,
3218           lpdReserved,lpdwType,lpbData,lpcbData);
3219     return RegEnumValueA( hkey, iValue, lpszValue, lpcchValue, lpdReserved, 
3220                             lpdwType, lpbData, lpcbData );
3221 }
3222
3223
3224 /******************************************************************************
3225  * RegCloseKey [SHELL.3] [KERNEL.220] [ADVAPI32.126]
3226  * Releases the handle of the specified key
3227  *
3228  * PARAMS
3229  *    hkey [I] Handle of key to close
3230  *
3231  * RETURNS
3232  *    Success: ERROR_SUCCESS
3233  *    Failure: Error code
3234  */
3235 DWORD WINAPI RegCloseKey( HKEY hkey )
3236 {
3237     TRACE_(reg)("(%x)\n",hkey);
3238
3239     /* The standard handles are allowed to succeed, even though they are not
3240        closed */
3241     if (is_standard_hkey(hkey))
3242         return ERROR_SUCCESS;
3243
3244     return remove_handle(hkey);
3245 }
3246
3247
3248 /* 
3249  * Delete registry key
3250  *
3251  * Callpath:
3252  * RegDeleteKey16 -> RegDeleteKey32A -> RegDeleteKey32W
3253  */
3254
3255
3256 /******************************************************************************
3257  * RegDeleteKey32W [ADVAPI32.134]
3258  *
3259  * PARAMS
3260  *    hkey       [I] Handle to open key
3261  *    lpszSubKey [I] Name of subkey to delete
3262  *
3263  * RETURNS
3264  *    Success: ERROR_SUCCESS
3265  *    Failure: Error code
3266  */
3267 DWORD WINAPI RegDeleteKeyW( HKEY hkey, LPWSTR lpszSubKey )
3268 {
3269         LPKEYSTRUCT     *lplpPrevKey,lpNextKey,lpxkey;
3270         LPWSTR          *wps;
3271         int             wpc,i;
3272
3273     TRACE_(reg)("(%x,%s)\n",hkey,debugstr_w(lpszSubKey));
3274
3275     lpNextKey = lookup_hkey(hkey);
3276     if (!lpNextKey)
3277         return ERROR_INVALID_HANDLE;
3278
3279     /* Subkey param cannot be NULL */
3280     if (!lpszSubKey || !*lpszSubKey)
3281         return ERROR_BADKEY;
3282
3283     /* We need to know the previous key in the hier. */
3284         split_keypath(lpszSubKey,&wps,&wpc);
3285         i       = 0;
3286         lpxkey  = lpNextKey;
3287         while (i<wpc-1) {
3288                 lpxkey=lpNextKey->nextsub;
3289                 while (lpxkey) {
3290                         TRACE_(reg)("  Scanning [%s]\n",
3291                                      debugstr_w(lpxkey->keyname));
3292                         if (!lstrcmpiW(wps[i],lpxkey->keyname))
3293                                 break;
3294                         lpxkey=lpxkey->next;
3295                 }
3296                 if (!lpxkey) {
3297                         FREE_KEY_PATH;
3298                         TRACE_(reg)("  Not found.\n");
3299                         /* not found is success */
3300                         return ERROR_SUCCESS;
3301                 }
3302                 i++;
3303                 lpNextKey       = lpxkey;
3304         }
3305         lpxkey  = lpNextKey->nextsub;
3306         lplpPrevKey = &(lpNextKey->nextsub);
3307         while (lpxkey) {
3308                 TRACE_(reg)("  Scanning [%s]\n",
3309                              debugstr_w(lpxkey->keyname));
3310                 if (!lstrcmpiW(wps[i],lpxkey->keyname))
3311                         break;
3312                 lplpPrevKey     = &(lpxkey->next);
3313                 lpxkey          = lpxkey->next;
3314         }
3315
3316         if (!lpxkey) {
3317                 FREE_KEY_PATH;
3318                 WARN_(reg)("  Not found.\n");
3319                 return ERROR_FILE_NOT_FOUND;
3320         }
3321
3322         if (lpxkey->nextsub) {
3323                 FREE_KEY_PATH;
3324                 WARN_(reg)("  Not empty.\n");
3325                 return ERROR_CANTWRITE;
3326         }
3327         *lplpPrevKey    = lpxkey->next;
3328         free(lpxkey->keyname);
3329         if (lpxkey->class)
3330                 free(lpxkey->class);
3331         if (lpxkey->values)
3332                 free(lpxkey->values);
3333         free(lpxkey);
3334         FREE_KEY_PATH;
3335         TRACE_(reg)("  Done.\n");
3336         return  ERROR_SUCCESS;
3337 }
3338
3339
3340 /******************************************************************************
3341  * RegDeleteKey32A [ADVAPI32.133]
3342  */
3343 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR lpszSubKey )
3344 {
3345     LPWSTR lpszSubKeyW;
3346     DWORD  ret;
3347
3348     TRACE_(reg)("(%x,%s)\n",hkey,debugstr_a(lpszSubKey));
3349     lpszSubKeyW = lpszSubKey?strdupA2W(lpszSubKey):NULL;
3350     ret = RegDeleteKeyW( hkey, lpszSubKeyW );
3351     if(lpszSubKeyW) free(lpszSubKeyW);
3352     return ret;
3353 }
3354
3355
3356 /******************************************************************************
3357  * RegDeleteKey16 [SHELL.4] [KERNEL.219]
3358  */
3359 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR lpszSubKey )
3360 {
3361     TRACE_(reg)("(%x,%s)\n",hkey,debugstr_a(lpszSubKey));
3362     return RegDeleteKeyA( hkey, lpszSubKey );
3363 }
3364
3365
3366 /* 
3367  * Delete registry value
3368  *
3369  * Callpath:
3370  * RegDeleteValue16 -> RegDeleteValue32A -> RegDeleteValue32W
3371  */
3372
3373
3374 /******************************************************************************
3375  * RegDeleteValue32W [ADVAPI32.136]
3376  *
3377  * PARAMS
3378  *    hkey      [I]
3379  *    lpszValue [I]
3380  *
3381  * RETURNS
3382  */
3383 DWORD WINAPI RegDeleteValueW( HKEY hkey, LPWSTR lpszValue )
3384 {
3385         DWORD           i;
3386         LPKEYSTRUCT     lpkey;
3387         LPKEYVALUE      val;
3388
3389     TRACE_(reg)("(%x,%s)\n",hkey,debugstr_w(lpszValue));
3390
3391     lpkey = lookup_hkey( hkey );
3392     if (!lpkey)
3393         return ERROR_INVALID_HANDLE;
3394
3395         if (lpszValue) {
3396                 for (i=0;i<lpkey->nrofvalues;i++)
3397                         if (    lpkey->values[i].name &&
3398                                 !lstrcmpiW(lpkey->values[i].name,lpszValue)
3399                         )
3400                                 break;
3401         } else {
3402                 for (i=0;i<lpkey->nrofvalues;i++)
3403                         if (lpkey->values[i].name==NULL)
3404                                 break;
3405         }
3406
3407     if (i == lpkey->nrofvalues)
3408         return ERROR_FILE_NOT_FOUND;
3409
3410         val     = lpkey->values+i;
3411         if (val->name) free(val->name);
3412         if (val->data) free(val->data);
3413         memcpy( 
3414                 lpkey->values+i,
3415                 lpkey->values+i+1,
3416                 sizeof(KEYVALUE)*(lpkey->nrofvalues-i-1)
3417         );
3418         lpkey->values   = (LPKEYVALUE)xrealloc(
3419                                 lpkey->values,
3420                                 (lpkey->nrofvalues-1)*sizeof(KEYVALUE)
3421                         );
3422         lpkey->nrofvalues--;
3423         return ERROR_SUCCESS;
3424 }
3425
3426
3427 /******************************************************************************
3428  * RegDeleteValue32A [ADVAPI32.135]
3429  */
3430 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPSTR lpszValue )
3431 {
3432     LPWSTR lpszValueW;
3433     DWORD  ret;
3434
3435     TRACE_(reg)("(%x,%s)\n",hkey,debugstr_a(lpszValue));
3436     lpszValueW = lpszValue?strdupA2W(lpszValue):NULL;
3437     ret = RegDeleteValueW( hkey, lpszValueW );
3438     if(lpszValueW) free(lpszValueW);
3439     return ret;
3440 }
3441
3442
3443 /******************************************************************************
3444  * RegDeleteValue16 [KERNEL.222]
3445  */
3446 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR lpszValue )
3447 {
3448     TRACE_(reg)("(%x,%s)\n", hkey,debugstr_a(lpszValue));
3449     return RegDeleteValueA( hkey, lpszValue );
3450 }
3451
3452
3453 /******************************************************************************
3454  * RegFlushKey [KERNEL.227] [ADVAPI32.143]
3455  * Writes key to registry
3456  *
3457  * PARAMS
3458  *    hkey [I] Handle of key to write
3459  *
3460  * RETURNS
3461  *    Success: ERROR_SUCCESS
3462  *    Failure: Error code
3463  */
3464 DWORD WINAPI RegFlushKey( HKEY hkey )
3465 {
3466     LPKEYSTRUCT lpkey;
3467     BOOL ret;
3468
3469     TRACE_(reg)("(%x)\n", hkey);
3470
3471     lpkey = lookup_hkey( hkey );
3472     if (!lpkey)
3473         return ERROR_BADKEY;
3474
3475     ERR_(reg)("What is the correct filename?\n");
3476
3477     ret = _savereg( lpkey, "foo.bar", TRUE);
3478
3479     if( ret ) {
3480         return ERROR_SUCCESS;
3481     } else
3482         return ERROR_UNKNOWN;  /* FIXME */
3483 }
3484
3485
3486 /* FIXME: lpcchXXXX ... is this counting in WCHARS or in BYTEs ?? */
3487
3488
3489 /******************************************************************************
3490  * RegQueryInfoKey32W [ADVAPI32.153]
3491  *
3492  * PARAMS
3493  *    hkey                   [I] Handle to key to query
3494  *    lpszClass              [O] Buffer for class string
3495  *    lpcchClass             [O] Size of class string buffer
3496  *    lpdwReserved           [I] Reserved
3497  *    lpcSubKeys             [I] Buffer for number of subkeys
3498  *    lpcchMaxSubKey         [O] Buffer for longest subkey name length
3499  *    lpcchMaxClass          [O] Buffer for longest class string length
3500  *    lpcValues              [O] Buffer for number of value entries
3501  *    lpcchMaxValueName      [O] Buffer for longest value name length
3502  *    lpccbMaxValueData      [O] Buffer for longest value data length
3503  *    lpcbSecurityDescriptor [O] Buffer for security descriptor length
3504  *    ft
3505  * - win95 allows lpszClass to be valid and lpcchClass to be NULL 
3506  * - winnt returns ERROR_INVALID_PARAMETER if lpszClass is valid and
3507  *   lpcchClass is NULL
3508  * - both allow lpszClass to be NULL and lpcchClass to be NULL 
3509  * (it's hard to test validity, so test !NULL instead)
3510  */
3511 DWORD WINAPI RegQueryInfoKeyW( HKEY hkey, LPWSTR lpszClass, 
3512                                  LPDWORD lpcchClass, LPDWORD lpdwReserved,
3513                                  LPDWORD lpcSubKeys, LPDWORD lpcchMaxSubkey,
3514                                  LPDWORD lpcchMaxClass, LPDWORD lpcValues,
3515                                  LPDWORD lpcchMaxValueName, 
3516                                  LPDWORD lpccbMaxValueData, 
3517                                  LPDWORD lpcbSecurityDescriptor, FILETIME *ft )
3518 {
3519         LPKEYSTRUCT     lpkey,lpxkey;
3520         int             nrofkeys,maxsubkey,maxclass,maxvname,maxvdata;
3521         int             i;
3522
3523         TRACE_(reg)("(%x,%p,...)\n",hkey,lpszClass);
3524         lpkey = lookup_hkey(hkey);
3525         if (!lpkey)
3526                 return ERROR_INVALID_HANDLE;
3527         if (lpszClass) {
3528                 if (VERSION_GetVersion() == NT40 && lpcchClass == NULL) {
3529                     return ERROR_INVALID_PARAMETER;
3530                 }
3531                 /* either lpcchClass is valid or this is win95 and lpcchClass
3532                    could be invalid */
3533                 if (lpkey->class) {
3534                         DWORD classLen = lstrlenW(lpkey->class);
3535
3536                         if (lpcchClass && classLen+1>*lpcchClass) {
3537                                 *lpcchClass=classLen+1;
3538                                 return ERROR_MORE_DATA;
3539                         }
3540                         if (lpcchClass)
3541                             *lpcchClass=classLen;
3542                         memcpy(lpszClass,lpkey->class, classLen*2 + 2);
3543                 } else {
3544                         *lpszClass      = 0;
3545                         if (lpcchClass)
3546                             *lpcchClass = 0;
3547                 }
3548         } else {
3549                 if (lpcchClass)
3550                         *lpcchClass     = lstrlenW(lpkey->class);
3551         }
3552         lpxkey=lpkey->nextsub;
3553         nrofkeys=maxsubkey=maxclass=maxvname=maxvdata=0;
3554         while (lpxkey) {
3555                 nrofkeys++;
3556                 if (lstrlenW(lpxkey->keyname)>maxsubkey)
3557                         maxsubkey=lstrlenW(lpxkey->keyname);
3558                 if (lpxkey->class && lstrlenW(lpxkey->class)>maxclass)
3559                         maxclass=lstrlenW(lpxkey->class);
3560                 lpxkey=lpxkey->next;
3561         }
3562         for (i=0;i<lpkey->nrofvalues;i++) {
3563                 LPKEYVALUE      val=lpkey->values+i;
3564
3565                 if (val->name && lstrlenW(val->name)>maxvname)
3566                         maxvname=lstrlenW(val->name);
3567                 if (val->len>maxvdata)
3568                         maxvdata=val->len;
3569         }
3570         if (!maxclass) maxclass = 1;
3571         if (!maxvname) maxvname = 1;
3572         if (lpcValues)
3573                 *lpcValues      = lpkey->nrofvalues;
3574         if (lpcSubKeys)
3575                 *lpcSubKeys     = nrofkeys;
3576         if (lpcchMaxSubkey)
3577                 *lpcchMaxSubkey = maxsubkey;
3578         if (lpcchMaxClass)
3579                 *lpcchMaxClass  = maxclass;
3580         if (lpcchMaxValueName)
3581                 *lpcchMaxValueName= maxvname;
3582         if (lpccbMaxValueData)
3583                 *lpccbMaxValueData= maxvdata;
3584         return ERROR_SUCCESS;
3585 }
3586
3587
3588 /******************************************************************************
3589  * RegQueryInfoKey32A [ADVAPI32.152]
3590  */
3591 DWORD WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR lpszClass, LPDWORD lpcchClass,
3592                                  LPDWORD lpdwReserved, LPDWORD lpcSubKeys,
3593                                  LPDWORD lpcchMaxSubkey, LPDWORD lpcchMaxClass,
3594                                  LPDWORD lpcValues, LPDWORD lpcchMaxValueName,
3595                                  LPDWORD lpccbMaxValueData, 
3596                                  LPDWORD lpcbSecurityDescriptor, FILETIME *ft )
3597 {
3598         LPWSTR          lpszClassW = NULL;
3599         DWORD           ret;
3600
3601         TRACE_(reg)("(%x,%p,%p......)\n",hkey, lpszClass, lpcchClass);
3602         if (lpszClass) {
3603                 if (lpcchClass) {
3604                     lpszClassW  = (LPWSTR)xmalloc((*lpcchClass) * 2);
3605                 } else if (VERSION_GetVersion() == WIN95) {
3606                     /* win95  allows lpcchClass to be null */
3607                     /* we don't know how big lpszClass is, would 
3608                        MAX_PATHNAME_LEN be the correct default? */
3609                     lpszClassW  = (LPWSTR)xmalloc(MAX_PATHNAME_LEN*2); 
3610                 }
3611
3612         } else
3613                 lpszClassW  = NULL;
3614         ret=RegQueryInfoKeyW(
3615                 hkey,
3616                 lpszClassW,
3617                 lpcchClass,
3618                 lpdwReserved,
3619                 lpcSubKeys,
3620                 lpcchMaxSubkey,
3621                 lpcchMaxClass,
3622                 lpcValues,
3623                 lpcchMaxValueName,
3624                 lpccbMaxValueData,
3625                 lpcbSecurityDescriptor,
3626                 ft
3627         );
3628         if (ret==ERROR_SUCCESS && lpszClass)
3629                 lstrcpyWtoA(lpszClass,lpszClassW);
3630         if (lpszClassW)
3631                 free(lpszClassW);
3632         return ret;
3633 }
3634
3635
3636 /******************************************************************************
3637  * RegConnectRegistry32W [ADVAPI32.128]
3638  *
3639  * PARAMS
3640  *    lpMachineName [I] Address of name of remote computer
3641  *    hHey          [I] Predefined registry handle
3642  *    phkResult     [I] Address of buffer for remote registry handle
3643  */
3644 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey, 
3645                                    LPHKEY phkResult )
3646 {
3647     TRACE_(reg)("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
3648
3649     if (!lpMachineName || !*lpMachineName) {
3650         /* Use the local machine name */
3651         return RegOpenKey16( hKey, "", phkResult );
3652     }
3653
3654     FIXME_(reg)("Cannot connect to %s\n",debugstr_w(lpMachineName));
3655     return ERROR_BAD_NETPATH;
3656 }
3657
3658
3659 /******************************************************************************
3660  * RegConnectRegistry32A [ADVAPI32.127]
3661  */
3662 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
3663 {
3664     DWORD ret;
3665     LPWSTR machineW = strdupA2W(machine);
3666     ret = RegConnectRegistryW( machineW, hkey, reskey );
3667     free(machineW);
3668     return ret;
3669 }
3670
3671
3672 /******************************************************************************
3673  * RegGetKeySecurity [ADVAPI32.144]
3674  * Retrieves a copy of security descriptor protecting the registry key
3675  *
3676  * PARAMS
3677  *    hkey                   [I]   Open handle of key to set
3678  *    SecurityInformation    [I]   Descriptor contents
3679  *    pSecurityDescriptor    [O]   Address of descriptor for key
3680  *    lpcbSecurityDescriptor [I/O] Address of size of buffer and description
3681  *
3682  * RETURNS
3683  *    Success: ERROR_SUCCESS
3684  *    Failure: Error code
3685  */
3686 LONG WINAPI RegGetKeySecurity( HKEY hkey, 
3687                                SECURITY_INFORMATION SecurityInformation,
3688                                PSECURITY_DESCRIPTOR pSecurityDescriptor,
3689                                LPDWORD lpcbSecurityDescriptor )
3690 {
3691     LPKEYSTRUCT lpkey;
3692
3693     TRACE_(reg)("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
3694           lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
3695
3696     lpkey = lookup_hkey( hkey );
3697     if (!lpkey)
3698         return ERROR_INVALID_HANDLE;
3699
3700     /* FIXME: Check for valid SecurityInformation values */
3701
3702     if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
3703         return ERROR_INSUFFICIENT_BUFFER;
3704
3705     FIXME_(reg)("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
3706           pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
3707
3708     return ERROR_SUCCESS;
3709 }
3710
3711
3712 /******************************************************************************
3713  * RegLoadKey32W [ADVAPI32.???]
3714  *
3715  * PARAMS
3716  *    hkey       [I] Handle of open key
3717  *    lpszSubKey [I] Address of name of subkey
3718  *    lpszFile   [I] Address of filename for registry information
3719  */
3720 LONG WINAPI RegLoadKeyW( HKEY hkey, LPCWSTR lpszSubKey, LPCWSTR lpszFile )
3721 {
3722     LPKEYSTRUCT lpkey;
3723     TRACE_(reg)("(%x,%s,%s)\n",hkey,debugstr_w(lpszSubKey),debugstr_w(lpszFile));
3724
3725     /* Do this check before the hkey check */
3726     if (!lpszSubKey || !*lpszSubKey || !lpszFile || !*lpszFile)
3727         return ERROR_INVALID_PARAMETER;
3728
3729     lpkey = lookup_hkey( hkey );
3730     if (!lpkey)
3731         return ERROR_INVALID_HANDLE;
3732
3733     FIXME_(reg)("(%x,%s,%s): stub\n",hkey,debugstr_w(lpszSubKey),
3734           debugstr_w(lpszFile));
3735
3736     return ERROR_SUCCESS;
3737 }
3738
3739
3740 /******************************************************************************
3741  * RegLoadKey32A [ADVAPI32.???]
3742  */
3743 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR lpszSubKey, LPCSTR lpszFile )
3744 {
3745     LONG ret;
3746     LPWSTR lpszSubKeyW = strdupA2W(lpszSubKey);
3747     LPWSTR lpszFileW = strdupA2W(lpszFile);
3748     ret = RegLoadKeyW( hkey, lpszSubKeyW, lpszFileW );
3749     if(lpszFileW) free(lpszFileW);
3750     if(lpszSubKeyW) free(lpszSubKeyW);
3751     return ret;
3752 }
3753
3754
3755 /******************************************************************************
3756  * RegNotifyChangeKeyValue [ADVAPI32.???]
3757  *
3758  * PARAMS
3759  *    hkey            [I] Handle of key to watch
3760  *    fWatchSubTree   [I] Flag for subkey notification
3761  *    fdwNotifyFilter [I] Changes to be reported
3762  *    hEvent          [I] Handle of signaled event
3763  *    fAsync          [I] Flag for asynchronous reporting
3764  */
3765 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree, 
3766                                      DWORD fdwNotifyFilter, HANDLE hEvent,
3767                                      BOOL fAsync )
3768 {
3769     LPKEYSTRUCT lpkey;
3770     TRACE_(reg)("(%x,%i,%ld,%x,%i)\n",hkey,fWatchSubTree,fdwNotifyFilter,
3771           hEvent,fAsync);
3772
3773     lpkey = lookup_hkey( hkey );
3774     if (!lpkey)
3775         return ERROR_INVALID_HANDLE;
3776
3777     FIXME_(reg)("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
3778           hEvent,fAsync);
3779
3780     return ERROR_SUCCESS;
3781 }
3782
3783
3784 /******************************************************************************
3785  * RegUnLoadKey32W [ADVAPI32.173]
3786  *
3787  * PARAMS
3788  *    hkey     [I] Handle of open key
3789  *    lpSubKey [I] Address of name of subkey to unload
3790  */
3791 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
3792 {
3793     FIXME_(reg)("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
3794     return ERROR_SUCCESS;
3795 }
3796
3797
3798 /******************************************************************************
3799  * RegUnLoadKey32A [ADVAPI32.172]
3800  */
3801 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
3802 {
3803     LONG ret;
3804     LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
3805     ret = RegUnLoadKeyW( hkey, lpSubKeyW );
3806     if(lpSubKeyW) free(lpSubKeyW);
3807     return ret;
3808 }
3809
3810
3811 /******************************************************************************
3812  * RegSetKeySecurity [ADVAPI32.167]
3813  *
3814  * PARAMS
3815  *    hkey          [I] Open handle of key to set
3816  *    SecurityInfo  [I] Descriptor contents
3817  *    pSecurityDesc [I] Address of descriptor for key
3818  */
3819 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
3820                                PSECURITY_DESCRIPTOR pSecurityDesc )
3821 {
3822     LPKEYSTRUCT lpkey;
3823
3824     TRACE_(reg)("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
3825
3826     /* It seems to perform this check before the hkey check */
3827     if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
3828         (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
3829         (SecurityInfo & DACL_SECURITY_INFORMATION) ||
3830         (SecurityInfo & SACL_SECURITY_INFORMATION)) {
3831         /* Param OK */
3832     } else
3833         return ERROR_INVALID_PARAMETER;
3834
3835     if (!pSecurityDesc)
3836         return ERROR_INVALID_PARAMETER;
3837
3838     lpkey = lookup_hkey( hkey );
3839     if (!lpkey)
3840         return ERROR_INVALID_HANDLE;
3841
3842     FIXME_(reg)(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
3843
3844     return ERROR_SUCCESS;
3845 }
3846
3847
3848 /******************************************************************************
3849  * RegSaveKey32W [ADVAPI32.166]
3850  *
3851  * PARAMS
3852  *    hkey   [I] Handle of key where save begins
3853  *    lpFile [I] Address of filename to save to
3854  *    sa     [I] Address of security structure
3855  */
3856 LONG WINAPI RegSaveKeyW( HKEY hkey, LPCWSTR lpFile, 
3857                            LPSECURITY_ATTRIBUTES sa )
3858 {
3859     LPKEYSTRUCT lpkey;
3860
3861     TRACE_(reg)("(%x,%s,%p)\n", hkey, debugstr_w(lpFile), sa);
3862
3863     /* It appears to do this check before the hkey check */
3864     if (!lpFile || !*lpFile)
3865         return ERROR_INVALID_PARAMETER;
3866
3867     lpkey = lookup_hkey( hkey );
3868     if (!lpkey)
3869         return ERROR_INVALID_HANDLE;
3870
3871     FIXME_(reg)("(%x,%s,%p): stub\n", hkey, debugstr_w(lpFile), sa);
3872
3873     return ERROR_SUCCESS;
3874 }
3875
3876
3877 /******************************************************************************
3878  * RegSaveKey32A [ADVAPI32.165]
3879  */
3880 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR lpFile, 
3881                            LPSECURITY_ATTRIBUTES sa )
3882 {
3883     LONG ret;
3884     LPWSTR lpFileW = strdupA2W(lpFile);
3885     ret = RegSaveKeyW( hkey, lpFileW, sa );
3886     free(lpFileW);
3887     return ret;
3888 }
3889
3890
3891 /******************************************************************************
3892  * RegRestoreKey32W [ADVAPI32.164]
3893  *
3894  * PARAMS
3895  *    hkey    [I] Handle of key where restore begins
3896  *    lpFile  [I] Address of filename containing saved tree
3897  *    dwFlags [I] Optional flags
3898  */
3899 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
3900 {
3901     LPKEYSTRUCT lpkey;
3902
3903     TRACE_(reg)("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
3904
3905     /* It seems to do this check before the hkey check */
3906     if (!lpFile || !*lpFile)
3907         return ERROR_INVALID_PARAMETER;
3908
3909     lpkey = lookup_hkey( hkey );
3910     if (!lpkey)
3911         return ERROR_INVALID_HANDLE;
3912
3913     FIXME_(reg)("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
3914
3915     /* Check for file existence */
3916
3917     return ERROR_SUCCESS;
3918 }
3919
3920
3921 /******************************************************************************
3922  * RegRestoreKey32A [ADVAPI32.163]
3923  */
3924 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
3925 {
3926     LONG ret;
3927     LPWSTR lpFileW = strdupA2W(lpFile);
3928     ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
3929     if(lpFileW) free(lpFileW);
3930     return ret;
3931 }
3932
3933
3934 /******************************************************************************
3935  * RegReplaceKey32W [ADVAPI32.162]
3936  *
3937  * PARAMS
3938  *    hkey      [I] Handle of open key
3939  *    lpSubKey  [I] Address of name of subkey
3940  *    lpNewFile [I] Address of filename for file with new data
3941  *    lpOldFile [I] Address of filename for backup file
3942  */
3943 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
3944                               LPCWSTR lpOldFile )
3945 {
3946     LPKEYSTRUCT lpkey;
3947
3948     TRACE_(reg)("(%x,%s,%s,%s)\n",hkey,debugstr_w(lpSubKey),
3949           debugstr_w(lpNewFile),debugstr_w(lpOldFile));
3950
3951     lpkey = lookup_hkey( hkey );
3952     if (!lpkey)
3953         return ERROR_INVALID_HANDLE;
3954
3955     FIXME_(reg)("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey), 
3956           debugstr_w(lpNewFile),debugstr_w(lpOldFile));
3957
3958     return ERROR_SUCCESS;
3959 }
3960
3961
3962 /******************************************************************************
3963  * RegReplaceKey32A [ADVAPI32.161]
3964  */
3965 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
3966                               LPCSTR lpOldFile )
3967 {
3968     LONG ret;
3969     LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
3970     LPWSTR lpNewFileW = strdupA2W(lpNewFile);
3971     LPWSTR lpOldFileW = strdupA2W(lpOldFile);
3972     ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
3973     free(lpOldFileW);
3974     free(lpNewFileW);
3975     free(lpSubKeyW);
3976     return ret;
3977 }
3978