Release 970914
[wine] / misc / registry.c
1 /*
2  *      Registry Functions
3  *
4  * Copyright 1996 Marcus Meissner
5  */
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/fcntl.h>
15 #include <sys/stat.h>
16 #include <pwd.h>
17 #include <time.h>
18 #include "windows.h"
19 #include "win.h"
20 #include "winerror.h"
21 #include "file.h"
22 #include "heap.h"
23 #include "string32.h"   
24 #include "stddebug.h"
25 #include "debug.h"
26 #include "xmalloc.h"
27 #include "winreg.h"
28
29 /* FIXME: following defines should be configured global ... */
30
31 /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */
32 #define WINE_PREFIX                     "/.wine"
33 #define SAVE_USERS_DEFAULT              "/usr/local/etc/wine.userreg"
34 #define SAVE_LOCAL_MACHINE_DEFAULT      "/usr/local/etc/wine.systemreg"
35
36 /* relative in ~user/.wine/ : */
37 #define SAVE_CURRENT_USER               "user.reg"
38 #define SAVE_LOCAL_MACHINE              "system.reg"
39
40 #define KEY_REGISTRY    "Software\\The WINE team\\WINE\\Registry"
41 #define VAL_SAVEUPDATED "SaveOnlyUpdatedKeys"
42
43 /* one value of a key */
44 typedef struct tagKEYVALUE
45 {
46     LPWSTR   name;          /* name of value (UNICODE) or NULL for win31 */
47     DWORD    type;          /* type of value */
48     DWORD    len;           /* length of data */
49     DWORD    lastmodified;  /* time of seconds since 1.1.1970 */
50     LPBYTE   data;          /* content, may be strings, binaries, etc. */
51 } KEYVALUE,*LPKEYVALUE;
52
53 /* a registry key */
54 typedef struct tagKEYSTRUCT
55 {
56     LPWSTR               keyname;       /* name of THIS key (UNICODE) */
57     DWORD                flags;         /* flags. */
58     LPWSTR               class;
59     /* values */
60     DWORD                nrofvalues;    /* nr of values in THIS key */
61     LPKEYVALUE           values;        /* values in THIS key */
62     /* key management pointers */
63     struct tagKEYSTRUCT *next;          /* next key on same hierarchy */
64     struct tagKEYSTRUCT *nextsub;       /* keys that hang below THIS key */
65 } KEYSTRUCT, *LPKEYSTRUCT;
66
67
68 static KEYSTRUCT        *key_classes_root=NULL; /* windows 3.1 global values */
69 static KEYSTRUCT        *key_current_user=NULL; /* user specific values */
70 static KEYSTRUCT        *key_local_machine=NULL;/* machine specific values */
71 static KEYSTRUCT        *key_users=NULL;        /* all users? */
72
73 /* dynamic, not saved */
74 static KEYSTRUCT        *key_performance_data=NULL;
75 static KEYSTRUCT        *key_current_config=NULL;
76 static KEYSTRUCT        *key_dyn_data=NULL;
77
78 /* what valuetypes do we need to convert? */
79 #define UNICONVMASK     ((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ))
80
81 #define strdupA2W(x)    STRING32_DupAnsiToUni(x)
82 #define strdupW(x)      STRING32_strdupW(x)
83 #define strchrW(a,c)    STRING32_lstrchrW(a,c)
84
85 static struct openhandle {
86         LPKEYSTRUCT     lpkey;
87         HKEY            hkey;
88         REGSAM          accessmask;
89 }  *openhandles=NULL;
90 static int      nrofopenhandles=0;
91 static int      currenthandle=1;
92
93 static void
94 add_handle(HKEY hkey,LPKEYSTRUCT lpkey,REGSAM accessmask) {
95         int     i;
96
97         for (i=0;i<nrofopenhandles;i++) {
98                 if (openhandles[i].lpkey==lpkey) {
99                         dprintf_reg(stddeb,"add_handle:Tried to add %p twice!\n",lpkey);
100                 }
101                 if (openhandles[i].hkey==hkey) {
102                         dprintf_reg(stddeb,"add_handle:Tried to add %lx twice!\n",(LONG)hkey);
103                 }
104         }
105         openhandles=xrealloc(   openhandles,
106                                 sizeof(struct openhandle)*(nrofopenhandles+1)
107                 );
108         openhandles[i].lpkey    = lpkey;
109         openhandles[i].hkey     = hkey;
110         openhandles[i].accessmask= accessmask;
111         nrofopenhandles++;
112 }
113
114 static LPKEYSTRUCT
115 get_handle(HKEY hkey) {
116         int     i;
117
118         for (i=0;i<nrofopenhandles;i++)
119                 if (openhandles[i].hkey==hkey)
120                         return openhandles[i].lpkey;
121         dprintf_reg(stddeb,"get_handle:Didn't find handle %lx?\n",(LONG)hkey);
122         return NULL;
123 }
124
125 static void
126 remove_handle(HKEY hkey) {
127         int     i;
128
129         for (i=0;i<nrofopenhandles;i++)
130                 if (openhandles[i].hkey==hkey)
131                         break;
132         if (i==nrofopenhandles) {
133                 dprintf_reg(stddeb,"remove_handle:Didn't find handle %08x?\n",hkey);
134                 return;
135         }
136         memcpy( openhandles+i,
137                 openhandles+i+1,
138                 sizeof(struct openhandle)*(nrofopenhandles-i-1)
139         );
140         openhandles=xrealloc(openhandles,sizeof(struct openhandle)*(nrofopenhandles-1));
141         nrofopenhandles--;
142         return;
143 }
144
145
146 /* debug function, converts a unicode into a static memory area 
147  * (sub for using two static strings, in case we need them in a single call)
148  */
149 LPSTR
150 W2C(LPCWSTR x,int sub) {
151         static  LPSTR   unicodedebug[2]={NULL,NULL};
152         if (x==NULL)
153                 return "<NULL>";
154         if (sub!=0 && sub!=1)
155                 return "<W2C:bad sub>";
156         if (unicodedebug[sub]) HeapFree( SystemHeap, 0, unicodedebug[sub] );
157         unicodedebug[sub] = HEAP_strdupWtoA( SystemHeap, 0, x );
158         return unicodedebug[sub];
159 }
160
161 static LPKEYSTRUCT
162 lookup_hkey(HKEY hkey) {
163         switch (hkey) {
164         case 0x00000000:
165         case 0x00000001:
166         case HKEY_CLASSES_ROOT:
167                 return key_classes_root;
168         case HKEY_CURRENT_USER:
169                 return key_current_user;
170         case HKEY_LOCAL_MACHINE:
171                 return key_local_machine;
172         case HKEY_USERS:
173                 return key_users;
174         case HKEY_PERFORMANCE_DATA:
175                 return key_performance_data;
176         case HKEY_DYN_DATA:
177                 return key_dyn_data;
178         case HKEY_CURRENT_CONFIG:
179                 return key_current_config;
180         default:
181                 dprintf_reg(stddeb,"lookup_hkey(%lx), special key!\n",
182                         (LONG)hkey
183                 );
184                 return get_handle(hkey);
185         }
186         /*NOTREACHED*/
187 }
188
189 /* 
190  * splits the unicode string 'wp' into an array of strings.
191  * the array is allocated by this function. 
192  * the number of components will be stored in 'wpc'
193  * Free the array using FREE_KEY_PATH
194  */
195 static void
196 split_keypath(LPCWSTR wp,LPWSTR **wpv,int *wpc) {
197         int     i,j,len;
198         LPWSTR  ws;
199
200         ws      = HEAP_strdupW( SystemHeap, 0, wp );
201         *wpc    = 1;
202         for (i=0;ws[i];i++) {
203                 if (ws[i]=='\\') {
204                         ws[i]=0;
205                         (*wpc)++;
206                 }
207         }
208         len     = i;
209         *wpv    = (LPWSTR*)HeapAlloc( SystemHeap, 0, sizeof(LPWSTR)*(*wpc+2));
210         (*wpv)[0]= ws;
211         j       = 1;
212         for (i=1;i<len;i++)
213                 if (ws[i-1]==0)
214                         (*wpv)[j++]=ws+i;
215         (*wpv)[j]=NULL;
216 }
217 #define FREE_KEY_PATH HeapFree(SystemHeap,0,wps[0]);HeapFree(SystemHeap,0,wps);
218
219 /*
220  * Shell initialisation, allocates keys. 
221  */
222 void SHELL_StartupRegistry();
223 void
224 SHELL_Init() {
225         struct  passwd  *pwd;
226
227         HKEY    cl_r_hkey,c_u_hkey;
228 #define ADD_ROOT_KEY(xx) \
229         xx = (LPKEYSTRUCT)xmalloc(sizeof(KEYSTRUCT));\
230         memset(xx,'\0',sizeof(KEYSTRUCT));\
231         xx->keyname= strdupA2W("<should_not_appear_anywhere>");
232
233         ADD_ROOT_KEY(key_local_machine);
234         if (RegCreateKey16(HKEY_LOCAL_MACHINE,"\\SOFTWARE\\Classes",&cl_r_hkey)!=ERROR_SUCCESS) {
235                 fprintf(stderr,"couldn't create HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes. This is impossible.\n");
236                 exit(1);
237         }
238         key_classes_root = lookup_hkey(cl_r_hkey);
239
240         ADD_ROOT_KEY(key_users);
241
242 #if 0
243         /* FIXME: load all users and their resp. pwd->pw_dir/.wine/user.reg 
244          *        (later, when a win32 registry editing tool becomes avail.)
245          */
246         while (pwd=getpwent()) {
247                 if (pwd->pw_name == NULL)
248                         continue;
249                 RegCreateKey16(HKEY_USERS,pwd->pw_name,&c_u_hkey);
250                 RegCloseKey(c_u_hkey);
251         }
252 #endif
253         pwd=getpwuid(getuid());
254         if (pwd && pwd->pw_name) {
255                 RegCreateKey16(HKEY_USERS,pwd->pw_name,&c_u_hkey);
256                 key_current_user = lookup_hkey(c_u_hkey);
257         } else {
258                 ADD_ROOT_KEY(key_current_user);
259         }
260         ADD_ROOT_KEY(key_performance_data);
261         ADD_ROOT_KEY(key_current_config);
262         ADD_ROOT_KEY(key_dyn_data);
263 #undef ADD_ROOT_KEY
264         SHELL_StartupRegistry();
265 }
266
267
268 void
269 SHELL_StartupRegistry() {
270         HKEY    hkey,xhkey=0;
271         FILE    *F;
272         char    buf[200],cpubuf[200];
273
274         RegCreateKey16(HKEY_DYN_DATA,"\\PerfStats\\StatData",&xhkey);
275         RegCloseKey(xhkey);
276         xhkey = 0;
277         RegCreateKey16(HKEY_LOCAL_MACHINE,"\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor",&hkey);
278 #ifdef linux
279         F=fopen("/proc/cpuinfo","r");
280         if (F) {
281                 int     procnr=-1,x;
282                 while (NULL!=fgets(buf,200,F)) {
283                         if (sscanf(buf,"processor\t: %d",&x)) {
284                                 sprintf(buf,"%d",x);
285                                 if (xhkey)
286                                         RegCloseKey(xhkey);
287                                 procnr=x;
288                                 RegCreateKey16(hkey,buf,&xhkey);
289                         }
290                         if (sscanf(buf,"cpu\t\t: %s",cpubuf)) {
291                                 sprintf(buf,"CPU %s",cpubuf);
292                                 if (xhkey)
293                                         RegSetValueEx32A(xhkey,"Identifier",0,REG_SZ,buf,strlen(buf));
294                         }
295                 }
296                 fclose(F);
297         }
298         if (xhkey)
299                 RegCloseKey(xhkey);
300         RegCloseKey(hkey);
301 #else
302         /* FIXME */
303         RegCreateKey16(hkey,"0",&xhkey);
304         RegSetValueEx32A(xhkey,"Identifier",0,REG_SZ,"CPU 386",strlen("CPU 386"));
305 #endif
306         RegOpenKey16(HKEY_LOCAL_MACHINE,"\\HARDWARE\\DESCRIPTION\\System",&hkey);
307         RegSetValueEx32A(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
308         RegCloseKey(hkey);
309         /* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion
310          *                                              CurrentVersion
311          *                                              CurrentBuildNumber
312          *                                              CurrentType
313          *                                      string  RegisteredOwner
314          *                                      string  RegisteredOrganization
315          *
316          */
317         /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
318          *                                      string  SysContact
319          *                                      string  SysLocation
320          *                                              SysServices
321          */                                             
322         if (-1!=gethostname(buf,200)) {
323                 RegCreateKey16(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&xhkey);
324                 RegSetValueEx16(xhkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
325                 RegCloseKey(xhkey);
326         }
327 }
328 /************************ SAVE Registry Function ****************************/
329
330 #define REGISTRY_SAVE_VERSION   0x00000001
331
332 /* Registry saveformat:
333  * If you change it, increase above number by 1, which will flush
334  * old registry database files.
335  * 
336  * Global:
337  *      "WINE REGISTRY Version %d"
338  *      subkeys....
339  * Subkeys:
340  *      keyname
341  *              valuename=lastmodified,type,data
342  *              ...
343  *              subkeys
344  *      ...
345  * keyname,valuename,stringdata:
346  *      the usual ascii characters from 0x00-0xff (well, not 0x00)
347  *      and \uXXXX as UNICODE value XXXX with XXXX>0xff
348  *      ( "=\\\t" escaped in \uXXXX form.)
349  * type,lastmodified: 
350  *      int
351  * 
352  * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
353  *
354  * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
355  * SaveOnlyUpdatedKeys=yes
356  */
357 static int
358 _save_check_tainted(LPKEYSTRUCT lpkey) {
359         int             tainted;
360
361         if (!lpkey)
362                 return 0;
363         if (lpkey->flags & REG_OPTION_TAINTED)
364                 tainted = 1;
365         else
366                 tainted = 0;
367         while (lpkey) {
368                 if (_save_check_tainted(lpkey->nextsub)) {
369                         lpkey->flags |= REG_OPTION_TAINTED;
370                         tainted = 1;
371                 }
372                 lpkey   = lpkey->next;
373         }
374         return tainted;
375 }
376
377 static void
378 _save_USTRING(FILE *F,LPWSTR wstr,int escapeeq) {
379         LPWSTR  s;
380         int     doescape;
381
382         if (wstr==NULL)
383                 return;
384         s=wstr;
385         while (*s) {
386                 doescape=0;
387                 if (*s>0xff)
388                         doescape = 1;
389                 if (*s=='\n')
390                         doescape = 1;
391                 if (escapeeq && *s=='=')
392                         doescape = 1;
393                 if (*s=='\\')
394                         fputc(*s,F); /* if \\ then put it twice. */
395                 if (doescape)
396                         fprintf(F,"\\u%04x",*((unsigned short*)s));
397                 else
398                         fputc(*s,F);
399                 s++;
400         }
401 }
402
403 static int
404 _savesubkey(FILE *F,LPKEYSTRUCT lpkey,int level,int all) {
405         LPKEYSTRUCT     lpxkey;
406         int             i,tabs,j;
407
408         lpxkey  = lpkey;
409         while (lpxkey) {
410                 if (    !(lpxkey->flags & REG_OPTION_VOLATILE) &&
411                         (all || (lpxkey->flags & REG_OPTION_TAINTED))
412                 ) {
413                         for (tabs=level;tabs--;)
414                                 fputc('\t',F);
415                         _save_USTRING(F,lpxkey->keyname,1);
416                         fputs("\n",F);
417                         for (i=0;i<lpxkey->nrofvalues;i++) {
418                                 LPKEYVALUE      val=lpxkey->values+i;
419
420                                 for (tabs=level+1;tabs--;)
421                                         fputc('\t',F);
422                                 _save_USTRING(F,val->name,0);
423                                 fputc('=',F);
424                                 fprintf(F,"%ld,%ld,",val->type,val->lastmodified);
425                                 if ((1<<val->type) & UNICONVMASK)
426                                         _save_USTRING(F,(LPWSTR)val->data,0);
427                                 else
428                                         for (j=0;j<val->len;j++)
429                                                 fprintf(F,"%02x",*((unsigned char*)val->data+j));
430                                 fputs("\n",F);
431                         }
432                         /* descend recursively */
433                         if (!_savesubkey(F,lpxkey->nextsub,level+1,all))
434                                 return 0;
435                 }
436                 lpxkey=lpxkey->next;
437         }
438         return 1;
439 }
440
441 static int
442 _savesubreg(FILE *F,LPKEYSTRUCT lpkey,int all) {
443         fprintf(F,"WINE REGISTRY Version %d\n",REGISTRY_SAVE_VERSION);
444         _save_check_tainted(lpkey->nextsub);
445         return _savesubkey(F,lpkey->nextsub,0,all);
446 }
447
448 static BOOL32
449 _savereg(LPKEYSTRUCT lpkey,char *fn,int all) {
450         FILE    *F;
451
452         F=fopen(fn,"w");
453         if (F==NULL) {
454                 fprintf(stddeb,__FILE__":_savereg:Couldn't open %s for writing: %s\n",
455                         fn,strerror(errno)
456                 );
457                 return FALSE;
458         }
459         if (!_savesubreg(F,lpkey,all)) {
460                 fclose(F);
461                 unlink(fn);
462                 fprintf(stddeb,__FILE__":_savereg:Failed to save keys, perhaps no more diskspace for %s?\n",fn);
463                 return FALSE;
464         }
465         fclose(F);
466         return TRUE;
467 }
468
469 void
470 SHELL_SaveRegistry() {
471         char    *fn;
472         struct  passwd  *pwd;
473         char    buf[4];
474         HKEY    hkey;
475         int     all;
476
477         all=0;
478         if (RegOpenKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS) {
479                 strcpy(buf,"yes");
480         } else {
481                 DWORD len,junk,type;
482
483                 len=4;
484                 if (    (ERROR_SUCCESS!=RegQueryValueEx32A(
485                                 hkey,
486                                 VAL_SAVEUPDATED,
487                                 &junk,
488                                 &type,
489                                 buf,
490                                 &len
491                         ))|| (type!=REG_SZ)
492                 )
493                         strcpy(buf,"yes");
494                 RegCloseKey(hkey);
495         }
496         if (lstrcmpi32A(buf,"yes"))
497                 all=1;
498         pwd=getpwuid(getuid());
499         if (pwd!=NULL && pwd->pw_dir!=NULL)
500         {
501                 char *tmp;
502
503                 fn=(char*)xmalloc( strlen(pwd->pw_dir) + strlen(WINE_PREFIX) +
504                                    strlen(SAVE_CURRENT_USER) + 2 );
505                 strcpy(fn,pwd->pw_dir);
506                 strcat(fn,WINE_PREFIX);
507                 /* create the directory. don't care about errorcodes. */
508                 mkdir(fn,0755); /* drwxr-xr-x */
509                 strcat(fn,"/"SAVE_CURRENT_USER);
510                 tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1);
511                 strcpy(tmp,fn);strcat(tmp,".tmp");
512                 if (_savereg(key_current_user,tmp,all)) {
513                         if (-1==rename(tmp,fn)) {
514                                 perror("rename tmp registry");
515                                 unlink(tmp);
516                         }
517                 }
518                 free(tmp);
519                 free(fn);
520                 fn=(char*)xmalloc(strlen(pwd->pw_dir)+strlen(WINE_PREFIX)+strlen(SAVE_LOCAL_MACHINE)+2);
521                 strcpy(fn,pwd->pw_dir);
522                 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
523                 tmp = (char*)xmalloc(strlen(fn)+strlen(".tmp")+1);
524                 strcpy(tmp,fn);strcat(tmp,".tmp");
525                 if (_savereg(key_local_machine,tmp,all)) {
526                         if (-1==rename(tmp,fn)) {
527                                 perror("rename tmp registry");
528                                 unlink(tmp);
529                         }
530                 }
531                 free(tmp);
532                 free(fn);
533         } else
534                 fprintf(stderr,"SHELL_SaveRegistry:failed to get homedirectory of UID %d.\n",getuid());
535 }
536
537 /************************ LOAD Registry Function ****************************/
538
539 static LPKEYSTRUCT
540 _find_or_add_key(LPKEYSTRUCT lpkey,LPWSTR keyname) {
541         LPKEYSTRUCT     lpxkey,*lplpkey;
542
543         lplpkey= &(lpkey->nextsub);
544         lpxkey  = *lplpkey;
545         while (lpxkey) {
546                 if (!lstrcmpi32W(lpxkey->keyname,keyname))
547                         break;
548                 lplpkey = &(lpxkey->next);
549                 lpxkey  = *lplpkey;
550         }
551         if (lpxkey==NULL) {
552                 *lplpkey = (LPKEYSTRUCT)xmalloc(sizeof(KEYSTRUCT));
553                 lpxkey  = *lplpkey;
554                 memset(lpxkey,'\0',sizeof(KEYSTRUCT));
555                 lpxkey->keyname = keyname;
556         } else
557                 free(keyname);
558         return lpxkey;
559 }
560
561 static void
562 _find_or_add_value(
563         LPKEYSTRUCT lpkey,LPWSTR name,DWORD type,LPBYTE data,DWORD len,
564         DWORD lastmodified
565 ) {
566         LPKEYVALUE      val=NULL;
567         int             i;
568
569         for (i=0;i<lpkey->nrofvalues;i++) {
570                 val=lpkey->values+i;
571                 if (name==NULL) {
572                         if (val->name==NULL)
573                                 break;
574                 } else {
575                         if (    val->name!=NULL && 
576                                 !lstrcmpi32W(val->name,name)
577                         )
578                                 break;
579                 }
580         }
581         if (i==lpkey->nrofvalues) {
582                 lpkey->values = xrealloc(
583                         lpkey->values,
584                         (++lpkey->nrofvalues)*sizeof(KEYVALUE)
585                 );
586                 val=lpkey->values+i;
587                 memset(val,'\0',sizeof(KEYVALUE));
588                 val->name = name;
589         } else {
590                 if (name)
591                         free(name);
592         }
593         if (val->lastmodified<lastmodified) {
594                 val->lastmodified=lastmodified;
595                 val->type = type;
596                 val->len  = len;
597                 if (val->data) 
598                         free(val->data);
599                 val->data = data;
600         } else
601                 free(data);
602 }
603
604
605 /* reads a line including dynamically enlarging the readbuffer and throwing
606  * away comments
607  */
608 static int 
609 _wine_read_line(FILE *F,char **buf,int *len) {
610         char    *s,*curread;
611         int     mylen,curoff;
612
613         curread = *buf;
614         mylen   = *len;
615         **buf   = '\0';
616         while (1) {
617                 while (1) {
618                         s=fgets(curread,mylen,F);
619                         if (s==NULL)
620                                 return 0; /* EOF */
621                         if (NULL==(s=strchr(curread,'\n'))) {
622                                 /* buffer wasn't large enough */
623                                 curoff  = strlen(*buf);
624                                 *buf    = xrealloc(*buf,*len*2);
625                                 curread = *buf + curoff;
626                                 mylen   = *len; /* we filled up the buffer and 
627                                                  * got new '*len' bytes to fill
628                                                  */
629                                 *len    = *len * 2;
630                         } else {
631                                 *s='\0';
632                                 break;
633                         }
634                 }
635                 /* throw away comments */
636                 if (**buf=='#' || **buf==';') {
637                         curread = *buf;
638                         mylen   = *len;
639                         continue;
640                 }
641                 if (s)  /* got end of line */
642                         break;
643         }
644         return 1;
645 }
646
647 /* converts a char* into a UNICODE string (up to a special char)
648  * and returns the position exactly after that string
649  */
650 static char*
651 _wine_read_USTRING(char *buf,LPWSTR *str) {
652         char    *s;
653         LPWSTR  ws;
654
655         /* read up to "=" or "\0" or "\n" */
656         s       = buf;
657         if (*s == '=') {
658                 /* empty string is the win3.1 default value(NULL)*/
659                 *str    = NULL;
660                 return s;
661         }
662         *str    = (LPWSTR)xmalloc(2*strlen(buf)+2);
663         ws      = *str;
664         while (*s && (*s!='\n') && (*s!='=')) {
665                 if (*s!='\\')
666                         *ws++=*((unsigned char*)s++);
667                 else {
668                         s++;
669                         if (*s=='\\') {
670                                 *ws++='\\';
671                                 s++;
672                                 continue;
673                         }
674                         if (*s!='u') {
675                                 fprintf(stderr,"_wine_read_USTRING:Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
676                                 *ws++='\\';
677                                 *ws++=*s++;
678                         } else {
679                                 char    xbuf[5];
680                                 int     wc;
681
682                                 s++;
683                                 memcpy(xbuf,s,4);xbuf[4]='\0';
684                                 if (!sscanf(xbuf,"%x",&wc))
685                                         fprintf(stderr,"_wine_read_USTRING:strange escape sequence %s found in |%s|\n",xbuf,buf);
686                                 s+=4;
687                                 *ws++   =(unsigned short)wc;
688                         }
689                 }
690         }
691         *ws     = 0;
692         ws      = *str;
693         *str    = strdupW(*str);
694         free(ws);
695         return s;
696 }
697
698 static int
699 _wine_loadsubkey(
700         FILE *F,LPKEYSTRUCT lpkey,int level,char **buf,int *buflen,int optflag
701 ) {
702         LPKEYSTRUCT     lpxkey;
703         int             i;
704         char            *s;
705         LPWSTR          name;
706
707         lpkey->flags |= optflag;
708
709         /* good. we already got a line here ... so parse it */
710         lpxkey  = NULL;
711         while (1) {
712                 i=0;s=*buf;
713                 while (*s=='\t') {
714                         s++;
715                         i++;
716                 }
717                 if (i>level) {
718                         if (lpxkey==NULL) {
719                                 fprintf(stderr,"_load_subkey:Got a subhierarchy without resp. key?\n");
720                                 return 0;
721                         }
722                         _wine_loadsubkey(F,lpxkey,level+1,buf,buflen,optflag);
723                         continue;
724                 }
725                 /* let the caller handle this line */
726                 if (i<level || **buf=='\0')
727                         return 1;
728
729                 /* it can be: a value or a keyname. Parse the name first */
730                 s=_wine_read_USTRING(s,&name);
731
732                 /* switch() default: hack to avoid gotos */
733                 switch (0) {
734                 default:
735                         if (*s=='\0') {
736                                 lpxkey=_find_or_add_key(lpkey,name);
737                         } else {
738                                 LPBYTE          data;
739                                 int             len,lastmodified,type;
740
741                                 if (*s!='=') {
742                                         fprintf(stderr,"_wine_load_subkey:unexpected character: %c\n",*s);
743                                         break;
744                                 }
745                                 s++;
746                                 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
747                                         fprintf(stderr,"_wine_load_subkey: haven't understood possible value in |%s|, skipping.\n",*buf);
748                                         break;
749                                 }
750                                 /* skip the 2 , */
751                                 s=strchr(s,',');s++;
752                                 s=strchr(s,',');s++;
753                                 if ((1<<type) & UNICONVMASK) {
754                                         s=_wine_read_USTRING(s,(LPWSTR*)&data);
755                                         if (data)
756                                                 len = lstrlen32W((LPWSTR)data)*2+2;
757                                         else    
758                                                 len = 0;
759                                 } else {
760                                         len=strlen(s)/2;
761                                         data = (LPBYTE)xmalloc(len+1);
762                                         for (i=0;i<len;i++) {
763                                                 data[i]=0;
764                                                 if (*s>='0' && *s<='9')
765                                                         data[i]=(*s-'0')<<4;
766                                                 if (*s>='a' && *s<='f')
767                                                         data[i]=(*s-'a')<<4;
768                                                 if (*s>='A' && *s<='F')
769                                                         data[i]=(*s-'A')<<4;
770                                                 s++;
771                                                 if (*s>='0' && *s<='9')
772                                                         data[i]|=*s-'0';
773                                                 if (*s>='a' && *s<='f')
774                                                         data[i]|=*s-'a';
775                                                 if (*s>='A' && *s<='F')
776                                                         data[i]|=*s-'A';
777                                                 s++;
778                                         }
779                                 }
780                                 _find_or_add_value(lpkey,name,type,data,len,lastmodified);
781                         }
782                 }
783                 /* read the next line */
784                 if (!_wine_read_line(F,buf,buflen))
785                         return 1;
786         }
787         return 1;
788 }
789
790 static int
791 _wine_loadsubreg(FILE *F,LPKEYSTRUCT lpkey,int optflag) {
792         int     ver;
793         char    *buf;
794         int     buflen;
795
796         buf=xmalloc(10);buflen=10;
797         if (!_wine_read_line(F,&buf,&buflen)) {
798                 free(buf);
799                 return 0;
800         }
801         if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
802                 free(buf);
803                 return 0;
804         }
805         if (ver!=REGISTRY_SAVE_VERSION) {
806                 dprintf_reg(stddeb,__FILE__":_wine_loadsubreg:Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
807                 free(buf);
808                 return 0;
809         }
810         if (!_wine_read_line(F,&buf,&buflen)) {
811                 free(buf);
812                 return 0;
813         }
814         if (!_wine_loadsubkey(F,lpkey,0,&buf,&buflen,optflag)) {
815                 free(buf);
816                 return 0;
817         }
818         free(buf);
819         return 1;
820 }
821
822 static void
823 _wine_loadreg(LPKEYSTRUCT lpkey,char *fn,int optflag) {
824         FILE    *F;
825
826         F=fopen(fn,"rb");
827         if (F==NULL) {
828                 dprintf_reg(stddeb,__FILE__":Couldn't open %s for reading: %s\n",
829                         fn,strerror(errno)
830                 );
831                 return;
832         }
833         if (!_wine_loadsubreg(F,lpkey,optflag)) {
834                 fclose(F);
835                 unlink(fn);
836                 return;
837         }
838         fclose(F);
839 }
840
841 static void
842 _copy_registry(LPKEYSTRUCT from,LPKEYSTRUCT to) {
843         LPKEYSTRUCT     lpxkey;
844         int             j;
845         LPKEYVALUE      valfrom;
846
847         from=from->nextsub;
848         while (from) {
849                 lpxkey = _find_or_add_key(to,strdupW(from->keyname));
850
851                 for (j=0;j<from->nrofvalues;j++) {
852                         LPWSTR  name;
853                         LPBYTE  data;
854
855                         valfrom = from->values+j;
856                         name=valfrom->name;
857                         if (name) name=strdupW(name);
858                         data=(LPBYTE)malloc(valfrom->len);
859                         memcpy(data,valfrom->data,valfrom->len);
860
861                         _find_or_add_value(
862                                 lpxkey,
863                                 name,
864                                 valfrom->type,
865                                 data,
866                                 valfrom->len,
867                                 valfrom->lastmodified
868                         );
869                 }
870                 _copy_registry(from,lpxkey);
871                 from = from->next;
872         }
873 }
874
875 /* WINDOWS 95 REGISTRY LOADER */
876 /* 
877  * Structure of a win95 registry database.
878  * main header:
879  * 0 :  "CREG"  - magic
880  * 4 :  DWORD version
881  * 8 :  DWORD offset_of_RGDB_part
882  * 0C..1F:      ? (someone fill in please)
883  *
884  * 20: RGKN_section:
885  *   header:
886  *      0 :             "RGKN"  - magic
887  *      4..0x1B:        ? (fill in)
888  *      0x20 ... offset_of_RGDB_part: Disk Key Entry structures
889  *
890  *   Disk Key Entry Structure:
891  *      00: DWORD       - unknown
892  *      04: DWORD       - unknown
893  *      08: DWORD       - unknown, but usually 0xFFFFFFFF on win95 systems
894  *      0C: DWORD       - disk address of PreviousLevel Key.
895  *      10: DWORD       - disk address of Next Sublevel Key.
896  *      14: DWORD       - disk address of Next Key (on same level).
897  * DKEP>18: WORD        - Nr, Low Significant part.
898  *      1A: WORD        - Nr, High Significant part.
899  *
900  * The disk address always points to the nr part of the previous key entry 
901  * of the referenced key. Don't ask me why, or even if I got this correct
902  * from staring at 1kg of hexdumps. (DKEP)
903  *
904  * The number of the entry is the low byte of the Low Significant Part ored
905  * with 0x100 * (low byte of the High Significant part)
906  * (C expression : nr = (nrLS & 0xFF) | ((nrHS &0xFF)<<8))
907  *
908  * There are two minor corrections to the position of that structure.
909  * 1. If the address is xxx014 or xxx018 it will be aligned to xxx01c AND 
910  *    the DKE reread from there.
911  * 2. If the address is xxxFFx it will be aligned to (xxx+1)000.
912  * (FIXME: slightly better explanation needed here)
913  *
914  * RGDB_section:
915  *      00:             "RGDB"  - magic
916  *      04: DWORD       offset to next RGDB section (perhaps WORD)
917  *      08...1F:        ?
918  *      20.....:        disk keys
919  *
920  * disk key:
921  *      00:     DWORD   nextkeyoffset   - offset to the next disk key structure
922  *      08:     WORD    nrLS            - low significant part of NR
923  *      0A:     WORD    nrHS            - high significant part of NR
924  *      0C:     DWORD   bytesused       - bytes used in this structure.
925  *      10:     WORD    name_len        - length of name in bytes. without \0
926  *      12:     WORD    nr_of_values    - number of values.
927  *      14:     char    name[name_len]  - name string. No \0.
928  *      14+name_len: disk values
929  *      nextkeyoffset: ... next disk key
930  *
931  * disk value:
932  *      00:     DWORD   type            - value type (hmm, could be WORD too)
933  *      04:     DWORD                   - unknown, usually 0
934  *      08:     WORD    namelen         - length of Name. 0 means name=NULL
935  *      0C:     WORD    datalen         - length of Data.
936  *      10:     char    name[namelen]   - name, no \0
937  *      10+namelen: BYTE        data[datalen] - data, without \0 if string
938  *      10+namelen+datalen: next values or disk key
939  *
940  * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
941  * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
942  * structure) and reading another RGDB_section.
943  * repeat until end of file.
944  *
945  * FIXME: this description needs some serious help, yes.
946  */
947
948 struct  _w95keyvalue {
949         unsigned long           type;
950         unsigned short          datalen;
951         char                    *name;
952         unsigned char           *data;
953         unsigned long           x1;
954         int                     lastmodified;
955 };
956
957 struct  _w95key {
958         char                    *name;
959         int                     nrofvals;
960         struct  _w95keyvalue    *values;
961         unsigned long           dkeaddr;
962         unsigned long           x1;
963         unsigned long           x2;
964         unsigned long           x3;
965         unsigned long           xx1;
966         struct _w95key          *prevlvl;
967         struct _w95key          *nextsub;
968         struct _w95key          *next;
969 };
970
971 /* fast lookup table dkeaddr->nr */
972 struct  _w95nr2da {
973         unsigned long           dkeaddr;
974         unsigned long           nr;
975 };
976
977
978 static void
979 _w95_walk_tree(LPKEYSTRUCT lpkey,struct _w95key *key) {
980         int             i;
981         LPKEYSTRUCT     lpxkey;
982         LPWSTR          name;
983
984         while (key) {
985                 if (key->name == NULL) {
986                         fprintf(stderr,"_w95_walk_tree:Please report: key with dkeaddr %lx not loaded, skipping hierarchy\n",
987                                 key->dkeaddr
988                         );
989                         return;
990                 }
991                 lpxkey=_find_or_add_key(lpkey,strdupA2W(key->name));
992
993                 if (key->nrofvals<0) {
994                         /* shouldn't happen */
995                         fprintf(stderr,"key %s already processed!\n",key->name);
996                         key = key->next;
997                         continue;
998                 }
999                 for (i=0;i<key->nrofvals;i++) {
1000                         LPBYTE  data;
1001                         int     len;
1002
1003                         name = strdupA2W(key->values[i].name);
1004                         if (!*name) name = NULL;
1005                         free(key->values[i].name);
1006
1007                         len     = key->values[i].datalen;
1008                         data    = key->values[i].data;
1009                         if ((1<<key->values[i].type) & UNICONVMASK) {
1010                                 data = (BYTE*)strdupA2W(data);
1011                                 len  = lstrlen32W((LPWSTR)data)*2+2;
1012                                 free(key->values[i].data);
1013                         }
1014                         _find_or_add_value(
1015                                 lpxkey,
1016                                 name,
1017                                 key->values[i].type,
1018                                 data,
1019                                 len,
1020                                 key->values[i].lastmodified
1021                         );
1022                 }
1023                 if (key->values) {
1024                         free(key->values);
1025                         key->values = NULL;
1026                 }
1027                 key->nrofvals=-key->nrofvals-1;
1028                 _w95_walk_tree(lpxkey,key->nextsub);
1029                 key=key->next;
1030         }
1031 }
1032
1033 /* small helper function to adjust address offset (dkeaddrs) */
1034 static unsigned long
1035 _w95_adj_da(unsigned long dkeaddr) {
1036         if ((dkeaddr&0xFFF)<0x018) {
1037                 int     diff;
1038
1039                 diff=0x1C-(dkeaddr&0xFFF);
1040                 return dkeaddr+diff;
1041         }
1042         if (((dkeaddr+0x1C)&0xFFF)<0x1C) {
1043                 /* readjust to 0x000,
1044                  * but ONLY if we are >0x1000 already
1045                  */
1046                 if (dkeaddr & ~0xFFF)
1047                         return dkeaddr & ~0xFFF;
1048         }
1049         return dkeaddr;
1050 }
1051
1052 static int
1053 _w95dkecomp(struct _w95nr2da *a,struct _w95nr2da *b){return a->dkeaddr-b->dkeaddr;}
1054
1055 static struct _w95key*
1056 _w95dkelookup(unsigned long dkeaddr,int n,struct _w95nr2da *nr2da,struct _w95key *keys) {
1057         int     i,off;
1058
1059         if (dkeaddr == 0xFFFFFFFF)
1060                 return NULL;
1061         if (dkeaddr<0x20)
1062                 return NULL;
1063         dkeaddr=_w95_adj_da(dkeaddr+0x1c);
1064         off = (dkeaddr-0x3c)/0x1c;
1065         for (i=0;i<n;i++)
1066                 if (nr2da[(i+off)%n].dkeaddr == dkeaddr)
1067                         return keys+nr2da[(i+off)%n].nr;
1068         /* 0x3C happens often, just report unusual values */
1069         if (dkeaddr!=0x3c)
1070                 dprintf_reg(stddeb,"search hasn't found dkeaddr %lx?\n",dkeaddr);
1071         return NULL;
1072 }
1073
1074 static void
1075 _w95_loadreg(char* fn,LPKEYSTRUCT lpkey) {
1076         /* Disk Key Entry structure (RGKN part) */
1077         struct  dke {
1078                 unsigned long           x1;
1079                 unsigned long           x2;
1080                 unsigned long           x3;/*usually 0xFFFFFFFF */
1081                 unsigned long           prevlvl;
1082                 unsigned long           nextsub;
1083                 unsigned long           next;
1084                 unsigned short          nrLS;
1085                 unsigned short          nrMS;
1086         };
1087         /* Disk Key Header structure (RGDB part) */
1088         struct  dkh {
1089                 unsigned long           nextkeyoff; 
1090                 unsigned short          nrLS;
1091                 unsigned short          nrMS;
1092                 unsigned long           bytesused;
1093                 unsigned short          keynamelen;
1094                 unsigned short          values;
1095                 unsigned long           xx1;
1096                 /* keyname */
1097                 /* disk key values or nothing */
1098         };
1099         /* Disk Key Value structure */
1100         struct  dkv {
1101                 unsigned long           type;
1102                 unsigned long           x1;
1103                 unsigned short          valnamelen;
1104                 unsigned short          valdatalen;
1105                 /* valname, valdata */
1106         };
1107         struct  _w95nr2da       *nr2da;
1108
1109         HFILE32         hfd;
1110         int             lastmodified;
1111         char            magic[5];
1112         unsigned long   nr,pos,i,where,version,rgdbsection,end,off_next_rgdb;
1113         struct  _w95key *keys;
1114         int             nrofdkes;
1115         unsigned char   *data,*curdata,*nextrgdb;
1116         OFSTRUCT        ofs;
1117         BY_HANDLE_FILE_INFORMATION hfdinfo;
1118
1119         dprintf_reg(stddeb,"Loading Win95 registry database '%s'\n",fn);
1120         hfd=OpenFile32(fn,&ofs,OF_READ);
1121         if (hfd==HFILE_ERROR32)
1122                 return;
1123         magic[4]=0;
1124         if (4!=_lread32(hfd,magic,4))
1125                 return;
1126         if (strcmp(magic,"CREG")) {
1127                 fprintf(stddeb,"%s is not a w95 registry.\n",fn);
1128                 return;
1129         }
1130         if (4!=_lread32(hfd,&version,4))
1131                 return;
1132         if (4!=_lread32(hfd,&rgdbsection,4))
1133                 return;
1134         if (-1==_llseek32(hfd,0x20,SEEK_SET))
1135                 return;
1136         if (4!=_lread32(hfd,magic,4))
1137                 return;
1138         if (strcmp(magic,"RGKN")) {
1139                 dprintf_reg(stddeb,"second IFF header not RGKN, but %s\n",magic);
1140                 return;
1141         }
1142
1143         /* STEP 1: Keylink structures */
1144         if (-1==_llseek32(hfd,0x40,SEEK_SET))
1145                 return;
1146         where   = 0x40;
1147         end     = rgdbsection;
1148
1149         nrofdkes = (end-where)/sizeof(struct dke)+100;
1150         data = (char*)xmalloc(end-where);
1151         if ((end-where)!=_lread32(hfd,data,end-where))
1152                 return;
1153         curdata = data;
1154
1155         keys = (struct _w95key*)xmalloc(nrofdkes * sizeof(struct _w95key));
1156         memset(keys,'\0',nrofdkes*sizeof(struct _w95key));
1157         nr2da= (struct _w95nr2da*)xmalloc(nrofdkes * sizeof(struct _w95nr2da));
1158         memset(nr2da,'\0',nrofdkes*sizeof(struct _w95nr2da));
1159
1160         for (i=0;i<nrofdkes;i++) {
1161                 struct  dke     dke;
1162                 unsigned long   dkeaddr;
1163
1164                 pos=curdata-data+0x40;
1165                 memcpy(&dke,curdata,sizeof(dke));
1166                 curdata+=sizeof(dke);
1167                 nr = dke.nrLS + (dke.nrMS<<8);
1168                 dkeaddr=pos-4;
1169                 if ((dkeaddr&0xFFF)<0x018) {
1170                         int     diff;
1171
1172                         diff=0x1C-(dkeaddr&0xFFF);
1173                         dkeaddr+=diff;
1174                         curdata+=diff-sizeof(dke);
1175                         memcpy(&dke,curdata,sizeof(dke));
1176                         nr = dke.nrLS + (dke.nrMS<<8);
1177                         curdata+=sizeof(dke);
1178                 }
1179                 if (((dkeaddr+0x1C)&0xFFF)<0x1C) {
1180                         /* readjust to 0x000,
1181                          * but ONLY if we are >0x1000 already
1182                          */
1183                         if (dkeaddr & ~0xFFF)
1184                                 dkeaddr = dkeaddr & ~0xFFF;
1185                 }
1186                 if (nr>nrofdkes) {
1187                         /* 0xFFFFFFFF happens often, just report unusual values */
1188                         if (nr!=0xFFFFFFFF)
1189                                 dprintf_reg(stddeb,"nr %ld exceeds nrofdkes %d, skipping.\n",nr,nrofdkes);
1190                         continue;
1191                 }
1192                 if (keys[nr].dkeaddr) {
1193                         int     x;
1194
1195                         for (x=sizeof(dke);x--;)
1196                                 if (((char*)&dke)[x])
1197                                         break;
1198                         if (x==-1)
1199                                 break; /* finished reading if we got only 0 */
1200                         if (nr) {
1201                                 if (    (dke.next!=(long)keys[nr].next) ||
1202                                         (dke.nextsub!=(long)keys[nr].nextsub)   ||
1203                                         (dke.prevlvl!=(long)keys[nr].prevlvl) 
1204                                 )
1205                                         dprintf_reg(stddeb,"key doubled? nr=%ld,key->dkeaddr=%lx,dkeaddr=%lx\n",nr,keys[nr].dkeaddr,dkeaddr);
1206                         }
1207                         continue;
1208                 }
1209                 nr2da[i].nr      = nr;
1210                 nr2da[i].dkeaddr = dkeaddr;
1211
1212                 keys[nr].dkeaddr = dkeaddr;
1213                 keys[nr].x1 = dke.x1;
1214                 keys[nr].x2 = dke.x2;
1215                 keys[nr].x3 = dke.x3;
1216                 keys[nr].prevlvl= (struct _w95key*)dke.prevlvl;
1217                 keys[nr].nextsub= (struct _w95key*)dke.nextsub;
1218                 keys[nr].next   = (struct _w95key*)dke.next;
1219         }
1220         free(data);
1221
1222         qsort(nr2da,nrofdkes,sizeof(nr2da[0]),
1223               (int(*)(const void *,const void*))_w95dkecomp);
1224
1225         /* STEP 2: keydata & values */
1226         if (!GetFileInformationByHandle(hfd,&hfdinfo))
1227                 return;
1228         end = hfdinfo.nFileSizeLow;
1229         lastmodified = DOSFS_FileTimeToUnixTime(&hfdinfo.ftLastWriteTime,NULL);
1230
1231         if (-1==_llseek32(hfd,rgdbsection,SEEK_SET))
1232                 return;
1233         data = (char*)xmalloc(end-rgdbsection);
1234         if ((end-rgdbsection)!=_lread32(hfd,data,end-rgdbsection))
1235                 return;
1236         _lclose32(hfd);
1237         curdata = data;
1238         memcpy(magic,curdata,4);
1239         memcpy(&off_next_rgdb,curdata+4,4);
1240         nextrgdb = curdata+off_next_rgdb;
1241         if (strcmp(magic,"RGDB")) {
1242                 dprintf_reg(stddeb,"third IFF header not RGDB, but %s\n",magic);
1243                 return;
1244         }
1245         curdata=data+0x20;
1246         while (1) {
1247                 struct  dkh dkh;
1248                 int     bytesread;
1249                 struct  _w95key *key,xkey;
1250
1251                 bytesread = 0;
1252                 if (curdata>=nextrgdb) {
1253                         curdata = nextrgdb;
1254                         if (!strncmp(curdata,"RGDB",4)) {
1255                                 memcpy(&off_next_rgdb,curdata+4,4);
1256                                 nextrgdb = curdata+off_next_rgdb;
1257                                 curdata+=0x20;
1258                         } else {
1259                                 dprintf_reg(stddeb,"at end of RGDB section, but no next header (%x of %lx). Breaking.\n",curdata-data,end-rgdbsection);
1260                                 break;
1261                         }
1262                 }
1263 #define XREAD(whereto,len) \
1264         if ((curdata-data+len)<end) {\
1265                 memcpy(whereto,curdata,len);\
1266                 curdata+=len;\
1267                 bytesread+=len;\
1268         }
1269
1270                 XREAD(&dkh,sizeof(dkh));
1271                 nr = dkh.nrLS + (dkh.nrMS<<8);
1272                 if ((nr>nrofdkes) || (dkh.nrLS == 0xFFFF)) {
1273                         if (dkh.nrLS == 0xFFFF) {
1274                                 /* skip over key using nextkeyoff */
1275                                 curdata+=dkh.nextkeyoff-sizeof(struct dkh);
1276                                 continue;
1277                         }
1278                         dprintf_reg(stddeb,"haven't found nr %ld.\n",nr);
1279                         key = &xkey;
1280                         memset(key,'\0',sizeof(xkey));
1281                 } else {
1282                         key = keys+nr;
1283                         if (!key->dkeaddr)
1284                                 dprintf_reg(stddeb,"key with nr=%ld has no dkeaddr?\n",nr);
1285                 }
1286                 key->nrofvals   = dkh.values;
1287                 key->name       = (char*)xmalloc(dkh.keynamelen+1);
1288                 key->xx1        = dkh.xx1;
1289                 XREAD(key->name,dkh.keynamelen);
1290                 key->name[dkh.keynamelen]=0;
1291                 if (key->nrofvals) {
1292                         key->values = (struct _w95keyvalue*)xmalloc(
1293                                 sizeof(struct _w95keyvalue)*key->nrofvals
1294                         );
1295                         for (i=0;i<key->nrofvals;i++) {
1296                                 struct  dkv     dkv;
1297
1298                                 XREAD(&dkv,sizeof(dkv));
1299                                 key->values[i].type = dkv.type;
1300                                 key->values[i].name = (char*)xmalloc(
1301                                         dkv.valnamelen+1
1302                                 );
1303                                 key->values[i].datalen = dkv.valdatalen;
1304                                 key->values[i].data = (unsigned char*)xmalloc(
1305                                         dkv.valdatalen+1
1306                                 );
1307                                 key->values[i].x1   = dkv.x1;
1308                                 XREAD(key->values[i].name,dkv.valnamelen);
1309                                 XREAD(key->values[i].data,dkv.valdatalen);
1310                                 key->values[i].data[dkv.valdatalen]=0;
1311                                 key->values[i].name[dkv.valnamelen]=0;
1312                                 key->values[i].lastmodified=lastmodified;
1313                         }
1314                 }
1315                 if (bytesread != dkh.nextkeyoff) {
1316                         if (dkh.bytesused != bytesread)
1317                                 dprintf_reg(stddeb,
1318                                         "read has difference in read bytes (%d) and nextoffset (%ld) (bytesused=%ld)\n",bytesread,dkh.nextkeyoff,
1319                                         dkh.bytesused
1320                                 );
1321                         curdata += dkh.nextkeyoff-bytesread;
1322                 }
1323                 key->prevlvl    = _w95dkelookup((long)key->prevlvl,nrofdkes,nr2da,keys);
1324                 key->nextsub    = _w95dkelookup((long)key->nextsub,nrofdkes,nr2da,keys);
1325                 key->next       = _w95dkelookup((long)key->next,nrofdkes,nr2da,keys);
1326                 if (!bytesread)
1327                         break;
1328         }
1329         free(data);
1330         _w95_walk_tree(lpkey,keys);
1331         free(keys);
1332 }
1333
1334 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1335
1336 /*
1337     reghack - windows 3.11 registry data format demo program.
1338
1339     The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1340     a combined hash table and tree description, and finally a text table.
1341
1342     The header is obvious from the struct header. The taboff1 and taboff2
1343     fields are always 0x20, and their usage is unknown.
1344
1345     The 8-byte entry table has various entry types.
1346
1347     tabent[0] is a root index. The second word has the index of the root of
1348             the directory.
1349     tabent[1..hashsize] is a hash table. The first word in the hash entry is
1350             the index of the key/value that has that hash. Data with the same
1351             hash value are on a circular list. The other three words in the
1352             hash entry are always zero.
1353     tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1354             entry: dirent and keyent/valent. They are identified by context.
1355     tabent[freeidx] is the first free entry. The first word in a free entry
1356             is the index of the next free entry. The last has 0 as a link.
1357             The other three words in the free list are probably irrelevant.
1358
1359     Entries in text table are preceeded by a word at offset-2. This word
1360     has the value (2*index)+1, where index is the referring keyent/valent
1361     entry in the table. I have no suggestion for the 2* and the +1.
1362     Following the word, there are N bytes of data, as per the keyent/valent
1363     entry length. The offset of the keyent/valent entry is from the start
1364     of the text table to the first data byte.
1365
1366     This information is not available from Microsoft. The data format is
1367     deduced from the reg.dat file by me. Mistakes may
1368     have been made. I claim no rights and give no guarantees for this program.
1369
1370     Tor Sjøwall, tor@sn.no
1371 */
1372
1373 /* reg.dat header format */
1374 struct _w31_header {
1375         char            cookie[8];      /* 'SHCC3.10' */
1376         unsigned long   taboff1;        /* offset of hash table (??) = 0x20 */
1377         unsigned long   taboff2;        /* offset of index table (??) = 0x20 */
1378         unsigned long   tabcnt;         /* number of entries in index table */
1379         unsigned long   textoff;        /* offset of text part */
1380         unsigned long   textsize;       /* byte size of text part */
1381         unsigned short  hashsize;       /* hash size */
1382         unsigned short  freeidx;        /* free index */
1383 };
1384
1385 /* generic format of table entries */
1386 struct _w31_tabent {
1387         unsigned short w0, w1, w2, w3;
1388 };
1389
1390 /* directory tabent: */
1391 struct _w31_dirent {
1392         unsigned short  sibling_idx;    /* table index of sibling dirent */
1393         unsigned short  child_idx;      /* table index of child dirent */
1394         unsigned short  key_idx;        /* table index of key keyent */
1395         unsigned short  value_idx;      /* table index of value valent */
1396 };
1397
1398 /* key tabent: */
1399 struct _w31_keyent {
1400         unsigned short  hash_idx;       /* hash chain index for string */
1401         unsigned short  refcnt;         /* reference count */
1402         unsigned short  length;         /* length of string */
1403         unsigned short  string_off;     /* offset of string in text table */
1404 };
1405
1406 /* value tabent: */
1407 struct _w31_valent {
1408         unsigned short  hash_idx;       /* hash chain index for string */
1409         unsigned short  refcnt;         /* reference count */
1410         unsigned short  length;         /* length of string */
1411         unsigned short  string_off;     /* offset of string in text table */
1412 };
1413
1414 /* recursive helper function to display a directory tree */
1415 void
1416 __w31_dumptree( unsigned short idx,
1417                 unsigned char *txt,
1418                 struct _w31_tabent *tab,
1419                 struct _w31_header *head,
1420                 LPKEYSTRUCT     lpkey,
1421                 time_t          lastmodified,
1422                 int             level
1423 ) {
1424         struct _w31_dirent      *dir;
1425         struct _w31_keyent      *key;
1426         struct _w31_valent      *val;
1427         LPKEYSTRUCT             xlpkey = NULL;
1428         LPWSTR                  name,value;
1429         static char             tail[400];
1430
1431         while (idx!=0) {
1432                 dir=(struct _w31_dirent*)&tab[idx];
1433
1434                 if (dir->key_idx) {
1435                         key = (struct _w31_keyent*)&tab[dir->key_idx];
1436
1437                         memcpy(tail,&txt[key->string_off],key->length);
1438                         tail[key->length]='\0';
1439                         /* all toplevel entries AND the entries in the 
1440                          * toplevel subdirectory belong to \SOFTWARE\Classes
1441                          */
1442                         if (!level && !lstrcmp32A(tail,".classes")) {
1443                                 __w31_dumptree(dir->child_idx,txt,tab,head,lpkey,lastmodified,level+1);
1444                                 idx=dir->sibling_idx;
1445                                 continue;
1446                         }
1447                         name=STRING32_DupAnsiToUni(tail);
1448
1449                         xlpkey=_find_or_add_key(lpkey,name);
1450
1451                         /* only add if leaf node or valued node */
1452                         if (dir->value_idx!=0||dir->child_idx==0) {
1453                                 if (dir->value_idx) {
1454                                         val=(struct _w31_valent*)&tab[dir->value_idx];
1455                                         memcpy(tail,&txt[val->string_off],val->length);
1456                                         tail[val->length]='\0';
1457                                         value=STRING32_DupAnsiToUni(tail);
1458                                         _find_or_add_value(xlpkey,NULL,REG_SZ,(LPBYTE)value,lstrlen32W(value)*2+2,lastmodified);
1459                                 }
1460                         }
1461                 } else {
1462                         dprintf_reg(stddeb,"__w31_dumptree:strange: no directory key name, idx=%04x\n", idx);
1463                 }
1464                 __w31_dumptree(dir->child_idx,txt,tab,head,xlpkey,lastmodified,level+1);
1465                 idx=dir->sibling_idx;
1466         }
1467 }
1468
1469 void
1470 _w31_loadreg() {
1471         HFILE32                 hf;
1472         struct _w31_header      head;
1473         struct _w31_tabent      *tab;
1474         unsigned char           *txt;
1475         int                     len;
1476         OFSTRUCT                ofs;
1477         BY_HANDLE_FILE_INFORMATION hfinfo;
1478         time_t                  lastmodified;
1479         HKEY                    hkey;
1480         LPKEYSTRUCT             lpkey;
1481
1482         hf = OpenFile32("reg.dat",&ofs,OF_READ);
1483         if (hf==HFILE_ERROR32)
1484                 return;
1485
1486         /* read & dump header */
1487         if (sizeof(head)!=_lread32(hf,&head,sizeof(head))) {
1488                 dprintf_reg(stddeb,"_w31_loadreg:reg.dat is too short.\n");
1489                 _lclose32(hf);
1490                 return;
1491         }
1492         if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1493                 dprintf_reg(stddeb,"_w31_loadreg:reg.dat has bad signature.\n");
1494                 _lclose32(hf);
1495                 return;
1496         }
1497
1498         len = head.tabcnt * sizeof(struct _w31_tabent);
1499         /* read and dump index table */
1500         tab = xmalloc(len);
1501         if (len!=_lread32(hf,tab,len)) {
1502                 dprintf_reg(stderr,"_w31_loadreg:couldn't read %d bytes.\n",len); 
1503                 free(tab);
1504                 _lclose32(hf);
1505                 return;
1506         }
1507
1508         /* read text */
1509         txt = xmalloc(head.textsize);
1510         if (-1==_llseek32(hf,head.textoff,SEEK_SET)) {
1511                 dprintf_reg(stderr,"_w31_loadreg:couldn't seek to textblock.\n"); 
1512                 free(tab);
1513                 free(txt);
1514                 _lclose32(hf);
1515                 return;
1516         }
1517         if (head.textsize!=_lread32(hf,txt,head.textsize)) {
1518                 dprintf_reg(stderr,"_w31_loadreg:textblock too short (%d instead of %ld).\n",len,head.textsize); 
1519                 free(tab);
1520                 free(txt);
1521                 _lclose32(hf);
1522                 return;
1523         }
1524
1525         if (!GetFileInformationByHandle(hf,&hfinfo)) {
1526                 dprintf_reg(stderr,"_w31_loadreg:GetFileInformationByHandle failed?.\n"); 
1527                 free(tab);
1528                 free(txt);
1529                 _lclose32(hf);
1530                 return;
1531         }
1532         lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1533
1534         if (RegCreateKey16(HKEY_LOCAL_MACHINE,"\\SOFTWARE\\Classes",&hkey)!=ERROR_SUCCESS)
1535                 return;
1536         lpkey = lookup_hkey(hkey);
1537         __w31_dumptree(tab[0].w1,txt,tab,&head,lpkey,lastmodified,0);
1538         free(tab);
1539         free(txt);
1540         _lclose32(hf);
1541         return;
1542 }
1543
1544 void
1545 SHELL_LoadRegistry() {
1546         char    *fn;
1547         struct  passwd  *pwd;
1548         LPKEYSTRUCT     lpkey;
1549         HKEY            hkey;
1550
1551
1552         if (key_classes_root==NULL)
1553                 SHELL_Init();
1554
1555         /* Load windows 3.1 entries */
1556         _w31_loadreg();
1557         /* Load windows 95 entries */
1558         _w95_loadreg("C:\\system.1st",  key_local_machine);
1559         _w95_loadreg("system.dat",      key_local_machine);
1560         _w95_loadreg("user.dat",        key_users);
1561
1562         /* the global user default is loaded under HKEY_USERS\\.Default */
1563         RegCreateKey16(HKEY_USERS,".Default",&hkey);
1564         lpkey = lookup_hkey(hkey);
1565         _wine_loadreg(lpkey,SAVE_USERS_DEFAULT,0);
1566
1567         /* HKEY_USERS\\.Default is copied to HKEY_CURRENT_USER */
1568         _copy_registry(lpkey,key_current_user);
1569         RegCloseKey(hkey);
1570
1571         /* the global machine defaults */
1572         _wine_loadreg(key_local_machine,SAVE_LOCAL_MACHINE_DEFAULT,0);
1573
1574         /* load the user saved registries */
1575
1576         /* FIXME: use getenv("HOME") or getpwuid(getuid())->pw_dir ?? */
1577
1578         pwd=getpwuid(getuid());
1579         if (pwd!=NULL && pwd->pw_dir!=NULL) {
1580                 fn=(char*)xmalloc(strlen(pwd->pw_dir)+strlen(WINE_PREFIX)+strlen(SAVE_CURRENT_USER)+2);
1581                 strcpy(fn,pwd->pw_dir);
1582                 strcat(fn,WINE_PREFIX"/"SAVE_CURRENT_USER);
1583                 _wine_loadreg(key_current_user,fn,REG_OPTION_TAINTED);
1584                 free(fn);
1585                 fn=(char*)xmalloc(strlen(pwd->pw_dir)+strlen(WINE_PREFIX)+strlen(SAVE_LOCAL_MACHINE)+2);
1586                 strcpy(fn,pwd->pw_dir);
1587                 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1588                 _wine_loadreg(key_local_machine,fn,REG_OPTION_TAINTED);
1589                 free(fn);
1590         } else
1591                 fprintf(stderr,"SHELL_LoadRegistry:failed to get homedirectory of UID %d.\n",getuid());
1592         if (ERROR_SUCCESS==RegCreateKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)) {
1593                 DWORD   junk,type,len;
1594                 char    data[5];
1595
1596                 len=4;
1597                 if ((   RegQueryValueEx32A(
1598                                 hkey,
1599                                 VAL_SAVEUPDATED,
1600                                 &junk,
1601                                 &type,
1602                                 data,
1603                                 &len
1604                         )!=ERROR_SUCCESS) ||
1605                         type != REG_SZ
1606                 )
1607                         RegSetValueEx32A(hkey,VAL_SAVEUPDATED,0,REG_SZ,"yes",4);
1608                 RegCloseKey(hkey);
1609         }
1610 }
1611
1612
1613 /********************* API FUNCTIONS ***************************************/
1614 /*
1615  * Open Keys.
1616  *
1617  * All functions are stubs to RegOpenKeyEx32W where all the
1618  * magic happens. 
1619  *
1620  * FIXME: security,options,desiredaccess,...
1621  *
1622  * Callpath:
1623  * RegOpenKey16 -> RegOpenKey32A -> RegOpenKeyEx32A \
1624  *                                  RegOpenKey32W   -> RegOpenKeyEx32W 
1625  */
1626
1627 /* RegOpenKeyExW                [ADVAPI32.150] */
1628 DWORD WINAPI RegOpenKeyEx32W(
1629         HKEY    hkey,
1630         LPCWSTR lpszSubKey,
1631         DWORD   dwReserved,
1632         REGSAM  samDesired,
1633         LPHKEY  retkey
1634 ) {
1635         LPKEYSTRUCT     lpNextKey,lpxkey;
1636         LPWSTR          *wps;
1637         int             wpc,i;
1638         dprintf_reg(stddeb,"RegOpenKeyEx32W(%lx,%s,%ld,%lx,%p)\n",
1639                 (LONG)hkey,W2C(lpszSubKey,0),dwReserved,samDesired,retkey
1640         );
1641
1642         lpNextKey       = lookup_hkey(hkey);
1643         if (!lpNextKey)
1644                 return SHELL_ERROR_BADKEY;
1645         if (!lpszSubKey || !*lpszSubKey) {
1646                 add_handle(++currenthandle,lpNextKey,samDesired);
1647                 *retkey=currenthandle;
1648                 return SHELL_ERROR_SUCCESS;
1649         }
1650         split_keypath(lpszSubKey,&wps,&wpc);
1651         i       = 0;
1652         while ((i<wpc) && (wps[i][0]=='\0')) i++;
1653         lpxkey  = lpNextKey;
1654         while (wps[i]) {
1655                 lpxkey=lpNextKey->nextsub;
1656                 while (lpxkey) {
1657                         if (!lstrcmpi32W(wps[i],lpxkey->keyname))
1658                                 break;
1659                         lpxkey=lpxkey->next;
1660                 }
1661                 if (!lpxkey) {
1662                         FREE_KEY_PATH;
1663                         return SHELL_ERROR_BADKEY;
1664                 }
1665                 i++;
1666                 lpNextKey       = lpxkey;
1667         }
1668         add_handle(++currenthandle,lpxkey,samDesired);
1669         *retkey = currenthandle;
1670         FREE_KEY_PATH;
1671         return  SHELL_ERROR_SUCCESS;
1672 }
1673
1674 /* RegOpenKeyW                  [ADVAPI32.151] */
1675 DWORD WINAPI RegOpenKey32W(
1676         HKEY    hkey,
1677         LPCWSTR lpszSubKey,
1678         LPHKEY  retkey
1679 ) {
1680         dprintf_reg(stddeb,"RegOpenKey32W(%lx,%s,%p)\n",
1681                 (LONG)hkey,W2C(lpszSubKey,0),retkey
1682         );
1683         return RegOpenKeyEx32W(hkey,lpszSubKey,0,KEY_ALL_ACCESS,retkey);
1684 }
1685
1686
1687 /* RegOpenKeyExA                [ADVAPI32.149] */
1688 DWORD WINAPI RegOpenKeyEx32A(
1689         HKEY    hkey,
1690         LPCSTR  lpszSubKey,
1691         DWORD   dwReserved,
1692         REGSAM  samDesired,
1693         LPHKEY  retkey
1694 ) {
1695         LPWSTR  lpszSubKeyW;
1696         DWORD   ret;
1697
1698         dprintf_reg(stddeb,"RegOpenKeyEx32A(%lx,%s,%ld,%lx,%p)\n",
1699                 (LONG)hkey,lpszSubKey,dwReserved,samDesired,retkey
1700         );
1701         if (lpszSubKey)
1702                 lpszSubKeyW=strdupA2W(lpszSubKey);
1703         else
1704                 lpszSubKeyW=NULL;
1705         ret=RegOpenKeyEx32W(hkey,lpszSubKeyW,dwReserved,samDesired,retkey);
1706         if (lpszSubKeyW)
1707                 free(lpszSubKeyW);
1708         return ret;
1709 }
1710
1711 /* RegOpenKeyA                  [ADVAPI32.148] */
1712 DWORD WINAPI RegOpenKey32A(
1713         HKEY    hkey,
1714         LPCSTR  lpszSubKey,
1715         LPHKEY  retkey
1716 ) {
1717         dprintf_reg(stddeb,"RegOpenKey32A(%lx,%s,%p)\n",
1718                 (LONG)hkey,lpszSubKey,retkey
1719         );
1720         return  RegOpenKeyEx32A(hkey,lpszSubKey,0,KEY_ALL_ACCESS,retkey);
1721 }
1722
1723 /* RegOpenKey                   [SHELL.1] [KERNEL.217] */
1724 DWORD WINAPI RegOpenKey16(
1725         HKEY    hkey,
1726         LPCSTR  lpszSubKey,
1727         LPHKEY  retkey
1728 ) {
1729         dprintf_reg(stddeb,"RegOpenKey16(%lx,%s,%p)\n",
1730                 (LONG)hkey,lpszSubKey,retkey
1731         );
1732         return RegOpenKey32A(hkey,lpszSubKey,retkey);
1733 }
1734
1735 /* 
1736  * Create keys
1737  * 
1738  * All those functions convert their respective 
1739  * arguments and call RegCreateKeyExW at the end.
1740  *
1741  * FIXME: no security,no access attrib,no optionhandling yet.
1742  *
1743  * Callpath:
1744  * RegCreateKey16 -> RegCreateKey32A -> RegCreateKeyEx32A \
1745  *                                      RegCreateKey32W   -> RegCreateKeyEx32W
1746  */
1747
1748 /* RegCreateKeyExW              [ADVAPI32.131] */
1749 DWORD WINAPI RegCreateKeyEx32W(
1750         HKEY    hkey,
1751         LPCWSTR lpszSubKey,
1752         DWORD   dwReserved,
1753         LPWSTR  lpszClass,
1754         DWORD   fdwOptions,
1755         REGSAM  samDesired,
1756         LPSECURITY_ATTRIBUTES lpSecAttribs,
1757         LPHKEY  retkey,
1758         LPDWORD lpDispos
1759 ) {
1760         LPKEYSTRUCT     *lplpPrevKey,lpNextKey,lpxkey;
1761         LPWSTR          *wps;
1762         int             wpc,i;
1763
1764 /*FIXME: handle security/access/whatever */
1765         dprintf_reg(stddeb,"RegCreateKeyEx32W(%lx,%s,%ld,%s,%lx,%lx,%p,%p,%p)\n",
1766                 (LONG)hkey,
1767                 W2C(lpszSubKey,0),
1768                 dwReserved,
1769                 W2C(lpszClass,1),
1770                 fdwOptions,
1771                 samDesired,
1772                 lpSecAttribs,
1773                 retkey,
1774                 lpDispos
1775         );
1776
1777         lpNextKey       = lookup_hkey(hkey);
1778         if (!lpNextKey)
1779                 return SHELL_ERROR_BADKEY;
1780         if (!lpszSubKey || !*lpszSubKey) {
1781                 add_handle(++currenthandle,lpNextKey,samDesired);
1782                 *retkey=currenthandle;
1783                 lpNextKey->flags|=REG_OPTION_TAINTED;
1784                 return SHELL_ERROR_SUCCESS;
1785         }
1786         split_keypath(lpszSubKey,&wps,&wpc);
1787         i       = 0;
1788         while ((i<wpc) && (wps[i][0]=='\0')) i++;
1789         lpxkey  = lpNextKey;
1790         while (wps[i]) {
1791                 lpxkey=lpNextKey->nextsub;
1792                 while (lpxkey) {
1793                         if (!lstrcmpi32W(wps[i],lpxkey->keyname))
1794                                 break;
1795                         lpxkey=lpxkey->next;
1796                 }
1797                 if (!lpxkey)
1798                         break;
1799                 i++;
1800                 lpNextKey       = lpxkey;
1801         }
1802         if (lpxkey) {
1803                 add_handle(++currenthandle,lpxkey,samDesired);
1804                 lpxkey->flags  |= REG_OPTION_TAINTED;
1805                 *retkey         = currenthandle;
1806                 if (lpDispos)
1807                         *lpDispos       = REG_OPENED_EXISTING_KEY;
1808                 FREE_KEY_PATH;
1809                 return  SHELL_ERROR_SUCCESS;
1810         }
1811         /* good. now the hard part */
1812         while (wps[i]) {
1813                 lplpPrevKey     = &(lpNextKey->nextsub);
1814                 lpxkey          = *lplpPrevKey;
1815                 while (lpxkey) {
1816                         lplpPrevKey     = &(lpxkey->next);
1817                         lpxkey          = *lplpPrevKey;
1818                 }
1819                 *lplpPrevKey=malloc(sizeof(KEYSTRUCT));
1820                 if (!*lplpPrevKey) {
1821                         FREE_KEY_PATH;
1822                         return SHELL_ERROR_OUTOFMEMORY;
1823                 }
1824                 memset(*lplpPrevKey,'\0',sizeof(KEYSTRUCT));
1825                 (*lplpPrevKey)->keyname = strdupW(wps[i]);
1826                 (*lplpPrevKey)->next    = NULL;
1827                 (*lplpPrevKey)->nextsub = NULL;
1828                 (*lplpPrevKey)->values  = NULL;
1829                 (*lplpPrevKey)->nrofvalues = 0;
1830                 (*lplpPrevKey)->flags   = REG_OPTION_TAINTED;
1831                 if (lpszClass)
1832                         (*lplpPrevKey)->class = strdupW(lpszClass);
1833                 else
1834                         (*lplpPrevKey)->class = NULL;
1835                 lpNextKey       = *lplpPrevKey;
1836                 i++;
1837         }
1838         add_handle(++currenthandle,lpNextKey,samDesired);
1839
1840         /*FIXME: flag handling correct? */
1841         lpNextKey->flags= fdwOptions |REG_OPTION_TAINTED;
1842         if (lpszClass)
1843                 lpNextKey->class = strdupW(lpszClass);
1844         else
1845                 lpNextKey->class = NULL;
1846         *retkey         = currenthandle;
1847         if (lpDispos)
1848                 *lpDispos       = REG_CREATED_NEW_KEY;
1849         FREE_KEY_PATH;
1850         return SHELL_ERROR_SUCCESS;
1851 }
1852
1853 /* RegCreateKeyW                [ADVAPI32.132] */
1854 DWORD WINAPI RegCreateKey32W(
1855         HKEY    hkey,
1856         LPCWSTR lpszSubKey,
1857         LPHKEY  retkey
1858 ) {
1859         DWORD   junk,ret;
1860
1861         dprintf_reg(stddeb,"RegCreateKey32W(%lx,%s,%p)\n",
1862                 (LONG)hkey,W2C(lpszSubKey,0),retkey
1863         );
1864         ret=RegCreateKeyEx32W(
1865                 hkey,           /* key handle */
1866                 lpszSubKey,     /* subkey name */
1867                 0,              /* reserved = 0 */
1868                 NULL,           /* lpszClass? FIXME: ? */
1869                 REG_OPTION_NON_VOLATILE,        /* options */
1870                 KEY_ALL_ACCESS, /* desired access attribs */
1871                 NULL,           /* lpsecurity attributes */
1872                 retkey,         /* lpretkey */
1873                 &junk           /* disposition value */
1874         );
1875         return  ret;
1876 }
1877
1878 /* RegCreateKeyExA              [ADVAPI32.130] */
1879 DWORD WINAPI RegCreateKeyEx32A(
1880         HKEY    hkey,
1881         LPCSTR  lpszSubKey,
1882         DWORD   dwReserved,
1883         LPSTR   lpszClass,
1884         DWORD   fdwOptions,
1885         REGSAM  samDesired,
1886         LPSECURITY_ATTRIBUTES lpSecAttribs,
1887         LPHKEY  retkey,
1888         LPDWORD lpDispos
1889 ) {
1890         LPWSTR  lpszSubKeyW,lpszClassW;
1891         DWORD   ret;
1892
1893         dprintf_reg(stddeb,"RegCreateKeyEx32A(%lx,%s,%ld,%s,%lx,%lx,%p,%p,%p)\n",
1894                 (LONG)hkey,
1895                 lpszSubKey,
1896                 dwReserved,
1897                 lpszClass,
1898                 fdwOptions,
1899                 samDesired,
1900                 lpSecAttribs,
1901                 retkey,
1902                 lpDispos
1903         );
1904         if (lpszSubKey)
1905                 lpszSubKeyW=strdupA2W(lpszSubKey);
1906         else
1907                 lpszSubKeyW=NULL;
1908         if (lpszClass)
1909                 lpszClassW=strdupA2W(lpszClass);
1910         else
1911                 lpszClassW=NULL;
1912         ret=RegCreateKeyEx32W(
1913                 hkey,
1914                 lpszSubKeyW,
1915                 dwReserved,
1916                 lpszClassW,
1917                 fdwOptions,
1918                 samDesired,
1919                 lpSecAttribs,
1920                 retkey,
1921                 lpDispos
1922         );
1923         if (lpszSubKeyW)
1924                 free(lpszSubKeyW);
1925         if (lpszClassW)
1926                 free(lpszClassW);
1927         return ret;
1928 }
1929
1930 /* RegCreateKeyA                [ADVAPI32.129] */
1931 DWORD WINAPI RegCreateKey32A(
1932         HKEY    hkey,
1933         LPCSTR  lpszSubKey,
1934         LPHKEY  retkey
1935 ) {
1936         DWORD   junk;
1937
1938         dprintf_reg(stddeb,"RegCreateKey32A(%lx,%s,%p)\n",
1939                 (LONG)hkey,lpszSubKey,retkey
1940         );
1941         return  RegCreateKeyEx32A(
1942                 hkey,           /* key handle */
1943                 lpszSubKey,     /* subkey name */
1944                 0,              /* reserved = 0 */
1945                 NULL,           /* lpszClass? FIXME: ? */
1946                 REG_OPTION_NON_VOLATILE,/* options */
1947                 KEY_ALL_ACCESS, /* desired access attribs */
1948                 NULL,           /* lpsecurity attributes */
1949                 retkey,         /* lpretkey */
1950                 &junk           /* disposition value */
1951         );
1952 }
1953
1954 /* RegCreateKey                 [SHELL.2] [KERNEL.218] */
1955 DWORD WINAPI RegCreateKey16(
1956         HKEY    hkey,
1957         LPCSTR  lpszSubKey,
1958         LPHKEY  retkey
1959 ) {
1960         dprintf_reg(stddeb,"RegCreateKey16(%lx,%s,%p)\n",
1961                 (LONG)hkey,lpszSubKey,retkey
1962         );
1963         return RegCreateKey32A(hkey,lpszSubKey,retkey);
1964 }
1965
1966 /* 
1967  * Query Value Functions
1968  * Win32 differs between keynames and valuenames. 
1969  * multiple values may belong to one key, the special value
1970  * with name NULL is the default value used by the win31
1971  * compat functions.
1972  *
1973  * Callpath:
1974  * RegQueryValue16 -> RegQueryValue32A -> RegQueryValueEx32A \
1975  *                                          RegQueryValue32W -> RegQueryValueEx32W
1976  */
1977
1978 /* RegQueryValueExW             [ADVAPI32.158] */
1979 DWORD WINAPI RegQueryValueEx32W(
1980         HKEY    hkey,
1981         LPWSTR  lpszValueName,
1982         LPDWORD lpdwReserved,
1983         LPDWORD lpdwType,
1984         LPBYTE  lpbData,
1985         LPDWORD lpcbData
1986 ) {
1987         LPKEYSTRUCT     lpkey;
1988         int             i;
1989
1990         dprintf_reg(stddeb,"RegQueryValueEx32W(%x,%s,%p,%p,%p,%ld)\n",
1991                 hkey,W2C(lpszValueName,0),lpdwReserved,lpdwType,lpbData,
1992                 lpcbData?*lpcbData:0
1993         );
1994
1995         lpkey   = lookup_hkey(hkey);
1996         if (!lpkey)
1997                 return SHELL_ERROR_BADKEY;
1998         if (lpszValueName==NULL) {
1999                 for (i=0;i<lpkey->nrofvalues;i++)
2000                         if (lpkey->values[i].name==NULL)
2001                                 break;
2002         } else {
2003                 for (i=0;i<lpkey->nrofvalues;i++)
2004                         if (    lpkey->values[i].name &&
2005                                 !lstrcmpi32W(lpszValueName,lpkey->values[i].name)
2006                         )
2007                                 break;
2008         }
2009         if (i==lpkey->nrofvalues) {
2010                 if (lpszValueName==NULL) {
2011                         if (lpbData) {
2012                                 *(WCHAR*)lpbData = 0;
2013                                 *lpcbData       = 2;
2014                         }
2015                         if (lpdwType)
2016                                 *lpdwType       = REG_SZ;
2017                         return SHELL_ERROR_SUCCESS;
2018                 }
2019                 return SHELL_ERROR_BADKEY;/*FIXME: correct return? */
2020         }
2021         if (lpdwType)
2022                 *lpdwType       = lpkey->values[i].type;
2023         if (lpbData==NULL) {
2024                 if (lpcbData==NULL)
2025                         return SHELL_ERROR_SUCCESS;
2026                 *lpcbData       = lpkey->values[i].len;
2027                 return SHELL_ERROR_SUCCESS;
2028         }
2029         if (*lpcbData<lpkey->values[i].len) {
2030                 *(WCHAR*)lpbData
2031                         = 0;
2032                 *lpcbData       = lpkey->values[i].len;
2033                 return ERROR_MORE_DATA;
2034         }
2035         memcpy(lpbData,lpkey->values[i].data,lpkey->values[i].len);
2036         *lpcbData       = lpkey->values[i].len;
2037         return SHELL_ERROR_SUCCESS;
2038 }
2039
2040 /* RegQueryValueW               [ADVAPI32.159] */
2041 DWORD WINAPI RegQueryValue32W(
2042         HKEY    hkey,
2043         LPWSTR  lpszSubKey,
2044         LPWSTR  lpszData,
2045         LPDWORD lpcbData
2046 ) {
2047         HKEY    xhkey;
2048         DWORD   ret,lpdwType;
2049
2050         dprintf_reg(stddeb,"RegQueryValue32W(%x,%s,%p,%ld)\n->",
2051                 hkey,W2C(lpszSubKey,0),lpszData,
2052                 lpcbData?*lpcbData:0
2053         );
2054
2055         /* only open subkey, if we really do descend */
2056         if (lpszSubKey && *lpszSubKey) {
2057                 ret     = RegOpenKey32W(hkey,lpszSubKey,&xhkey);
2058                 if (ret!=ERROR_SUCCESS)
2059                         return ret;
2060         } else
2061                 xhkey   = hkey;
2062
2063         lpdwType        = REG_SZ;
2064         ret     = RegQueryValueEx32W(
2065                 xhkey,
2066                 NULL,           /* varname NULL -> compat */
2067                 NULL,           /* lpdwReserved, must be NULL */
2068                 &lpdwType,
2069                 (LPBYTE)lpszData,
2070                 lpcbData
2071         );
2072         if (xhkey!=hkey)
2073                 RegCloseKey(xhkey);
2074         return ret;
2075 }
2076
2077 /* RegQueryValueExA             [ADVAPI32.157] */
2078 DWORD WINAPI RegQueryValueEx32A(
2079         HKEY    hkey,
2080         LPSTR   lpszValueName,
2081         LPDWORD lpdwReserved,
2082         LPDWORD lpdwType,
2083         LPBYTE  lpbData,
2084         LPDWORD lpcbData
2085 ) {
2086         LPWSTR  lpszValueNameW;
2087         LPBYTE  buf;
2088         DWORD   ret,myxlen;
2089         DWORD   *mylen;
2090         DWORD   type;
2091
2092         dprintf_reg(stddeb,"RegQueryValueEx32A(%x,%s,%p,%p,%p,%ld)\n->",
2093                 hkey,lpszValueName,lpdwReserved,lpdwType,lpbData,
2094                 lpcbData?*lpcbData:0
2095         );
2096         if (lpszValueName)
2097                 lpszValueNameW=strdupA2W(lpszValueName);
2098         else 
2099                 lpszValueNameW=NULL;
2100
2101         if (lpdwType)
2102                 type=*lpdwType;
2103
2104         if (lpbData) {
2105                 myxlen  = 0;
2106                 mylen   = &myxlen;
2107                 buf     = xmalloc(4);
2108                 ret=RegQueryValueEx32W(
2109                         hkey,
2110                         lpszValueNameW,
2111                         lpdwReserved,
2112                         &type,
2113                         buf,
2114                         mylen
2115                 );
2116                 free(buf);
2117                 if (ret==ERROR_MORE_DATA) {
2118                         buf     = (LPBYTE)xmalloc(*mylen);
2119                 } else {
2120                         buf     = (LPBYTE)xmalloc(2*(*lpcbData));
2121                         myxlen  = 2*(*lpcbData);
2122                 }
2123         } else {
2124                 buf=NULL;
2125                 if (lpcbData) {
2126                         myxlen  = *lpcbData*2;
2127                         mylen   = &myxlen;
2128                 } else
2129                         mylen   = NULL;
2130         }
2131         ret=RegQueryValueEx32W(
2132                 hkey,
2133                 lpszValueNameW,
2134                 lpdwReserved,
2135                 &type,
2136                 buf,
2137                 mylen
2138         );
2139         if (lpdwType) 
2140                 *lpdwType=type;
2141         if (ret==ERROR_SUCCESS) {
2142                 if (buf) {
2143                         if (UNICONVMASK & (1<<(type))) {
2144                                 /* convert UNICODE to ASCII */
2145                                 lstrcpyWtoA(lpbData,(LPWSTR)buf);
2146                                 *lpcbData       = myxlen/2;
2147                         } else {
2148                                 if (myxlen>*lpcbData)
2149                                         ret     = ERROR_MORE_DATA;
2150                                 else
2151                                         memcpy(lpbData,buf,myxlen);
2152
2153                                 *lpcbData       = myxlen;
2154                         }
2155                 } else {
2156                         if ((UNICONVMASK & (1<<(type))) && lpcbData)
2157                                 *lpcbData       = myxlen/2;
2158                 }
2159         } else {
2160                 if ((UNICONVMASK & (1<<(type))) && lpcbData)
2161                         *lpcbData       = myxlen/2;
2162         }
2163         if (buf)
2164                 free(buf);
2165         return ret;
2166 }
2167
2168 /* RegQueryValueEx              [KERNEL.225] */
2169 DWORD WINAPI RegQueryValueEx16(
2170         HKEY    hkey,
2171         LPSTR   lpszValueName,
2172         LPDWORD lpdwReserved,
2173         LPDWORD lpdwType,
2174         LPBYTE  lpbData,
2175         LPDWORD lpcbData
2176 ) {
2177         dprintf_reg(stddeb,"RegQueryValueEx16(%x,%s,%p,%p,%p,%ld)\n",
2178                 hkey,lpszValueName,lpdwReserved,lpdwType,lpbData,
2179                 lpcbData?*lpcbData:0
2180         );
2181         return RegQueryValueEx32A(
2182                 hkey,
2183                 lpszValueName,
2184                 lpdwReserved,
2185                 lpdwType,
2186                 lpbData,
2187                 lpcbData
2188         );
2189 }
2190
2191 /* RegQueryValueA               [ADVAPI32.156] */
2192 DWORD WINAPI RegQueryValue32A(
2193         HKEY    hkey,
2194         LPSTR   lpszSubKey,
2195         LPSTR   lpszData,
2196         LPDWORD lpcbData
2197 ) {
2198         HKEY    xhkey;
2199         DWORD   ret,lpdwType;
2200
2201         dprintf_reg(stddeb,"RegQueryValue32A(%x,%s,%p,%ld)\n",
2202                 hkey,lpszSubKey,lpszData,
2203                 lpcbData?*lpcbData:0
2204         );
2205
2206         /* only open subkey, if we really do descend */
2207         if (lpszSubKey && *lpszSubKey) {
2208                 ret     = RegOpenKey16(hkey,lpszSubKey,&xhkey);
2209                 if (ret!=ERROR_SUCCESS)
2210                         return ret;
2211         } else
2212                 xhkey   = hkey;
2213
2214         lpdwType        = REG_SZ;
2215         ret     = RegQueryValueEx32A(
2216                 xhkey,
2217                 NULL,           /* lpszValueName NULL -> compat */
2218                 NULL,           /* lpdwReserved, must be NULL */
2219                 &lpdwType,
2220                 (LPBYTE)lpszData,
2221                 lpcbData
2222         );
2223         if (xhkey!=hkey)
2224                 RegCloseKey(xhkey);
2225         return ret;
2226 }
2227
2228 /* RegQueryValue                [SHELL.6] [KERNEL.224] */
2229 DWORD WINAPI RegQueryValue16(
2230         HKEY    hkey,
2231         LPSTR   lpszSubKey,
2232         LPSTR   lpszData,
2233         LPDWORD lpcbData
2234 ) {
2235         dprintf_reg(stddeb,"RegQueryValue16(%x,%s,%p,%ld)\n",
2236                 hkey,lpszSubKey,lpszData,lpcbData?*lpcbData:0
2237         );
2238         /* HACK: the 16bit RegQueryValue doesn't handle selectorblocks
2239          *       anyway, so we just mask out the high 16 bit.
2240          *       (this (not so much incidently;) hopefully fixes Aldus FH4)
2241          */
2242         if (lpcbData)
2243                 *lpcbData &= 0xFFFF;
2244         return RegQueryValue32A(hkey,lpszSubKey,lpszData,lpcbData);
2245 }
2246
2247 /*
2248  * Setting values of Registry keys
2249  *
2250  * Callpath:
2251  * RegSetValue16 -> RegSetValue32A -> RegSetValueEx32A \
2252  *                                    RegSetValue32W   -> RegSetValueEx32W
2253  */
2254
2255 /* RegSetValueExW               [ADVAPI32.170] */
2256 DWORD WINAPI RegSetValueEx32W(
2257         HKEY    hkey,
2258         LPWSTR  lpszValueName,
2259         DWORD   dwReserved,
2260         DWORD   dwType,
2261         LPBYTE  lpbData,
2262         DWORD   cbData
2263 ) {
2264         LPKEYSTRUCT     lpkey;
2265         int             i;
2266
2267         dprintf_reg(stddeb,"RegSetValueEx32W(%x,%s,%ld,%ld,%p,%ld)\n",
2268                 hkey,W2C(lpszValueName,0),dwReserved,dwType,lpbData,cbData
2269         );
2270         /* we no longer care about the lpbData type here... */
2271         lpkey   = lookup_hkey(hkey);
2272         if (!lpkey)
2273                 return SHELL_ERROR_BADKEY;
2274
2275         lpkey->flags |= REG_OPTION_TAINTED;
2276
2277         if (lpszValueName==NULL) {
2278                 for (i=0;i<lpkey->nrofvalues;i++)
2279                         if (lpkey->values[i].name==NULL)
2280                                 break;
2281         } else {
2282                 for (i=0;i<lpkey->nrofvalues;i++)
2283                         if (    lpkey->values[i].name &&
2284                                 !lstrcmpi32W(lpszValueName,lpkey->values[i].name)
2285                         )
2286                                 break;
2287         }
2288         if (i==lpkey->nrofvalues) {
2289                 lpkey->values = (LPKEYVALUE)xrealloc(
2290                                         lpkey->values,
2291                                         (lpkey->nrofvalues+1)*sizeof(KEYVALUE)
2292                                 );
2293                 lpkey->nrofvalues++;
2294                 memset(lpkey->values+i,'\0',sizeof(KEYVALUE));
2295         }
2296         if (lpkey->values[i].name==NULL)
2297                 if (lpszValueName)
2298                         lpkey->values[i].name = strdupW(lpszValueName);
2299                 else
2300                         lpkey->values[i].name = NULL;
2301         lpkey->values[i].len    = cbData;
2302         lpkey->values[i].type   = dwType;
2303         if (lpkey->values[i].data !=NULL)
2304                 free(lpkey->values[i].data);
2305         lpkey->values[i].data   = (LPBYTE)xmalloc(cbData);
2306         lpkey->values[i].lastmodified = time(NULL);
2307         memcpy(lpkey->values[i].data,lpbData,cbData);
2308         return SHELL_ERROR_SUCCESS;
2309 }
2310
2311 /* RegSetValueExA               [ADVAPI32.169] */
2312 DWORD WINAPI RegSetValueEx32A(
2313         HKEY    hkey,
2314         LPSTR   lpszValueName,
2315         DWORD   dwReserved,
2316         DWORD   dwType,
2317         LPBYTE  lpbData,
2318         DWORD   cbData
2319 ) {
2320         LPBYTE  buf;
2321         LPWSTR  lpszValueNameW;
2322         DWORD   ret;
2323
2324         dprintf_reg(stddeb,"RegSetValueEx32A(%x,%s,%ld,%ld,%p,%ld)\n->",
2325                 hkey,lpszValueName,dwReserved,dwType,lpbData,cbData
2326         );
2327         if ((1<<dwType) & UNICONVMASK) {
2328                 buf=(LPBYTE)strdupA2W(lpbData);
2329                 cbData=2*strlen(lpbData)+2;
2330         } else
2331                 buf=lpbData;
2332         if (lpszValueName)
2333                 lpszValueNameW = strdupA2W(lpszValueName);
2334         else
2335                 lpszValueNameW = NULL;
2336         ret=RegSetValueEx32W(hkey,lpszValueNameW,dwReserved,dwType,buf,cbData);
2337         if (lpszValueNameW)
2338                 free(lpszValueNameW);
2339         if (buf!=lpbData)
2340                 free(buf);
2341         return ret;
2342 }
2343
2344 /* RegSetValueEx                [KERNEL.226] */
2345 DWORD WINAPI RegSetValueEx16(
2346         HKEY    hkey,
2347         LPSTR   lpszValueName,
2348         DWORD   dwReserved,
2349         DWORD   dwType,
2350         LPBYTE  lpbData,
2351         DWORD   cbData
2352 ) {
2353         dprintf_reg(stddeb,"RegSetValueEx16(%x,%s,%ld,%ld,%p,%ld)\n->",
2354                 hkey,lpszValueName,dwReserved,dwType,lpbData,cbData
2355         );
2356         return RegSetValueEx32A(hkey,lpszValueName,dwReserved,dwType,lpbData,cbData);
2357 }
2358
2359 /* RegSetValueW                 [ADVAPI32.171] */
2360 DWORD WINAPI RegSetValue32W(
2361         HKEY    hkey,
2362         LPCWSTR lpszSubKey,
2363         DWORD   dwType,
2364         LPCWSTR lpszData,
2365         DWORD   cbData
2366 ) {
2367         HKEY    xhkey;
2368         DWORD   ret;
2369
2370         dprintf_reg(stddeb,"RegSetValue32W(%x,%s,%ld,%s,%ld)\n->",
2371                 hkey,W2C(lpszSubKey,0),dwType,W2C(lpszData,0),cbData
2372         );
2373         if (lpszSubKey && *lpszSubKey) {
2374                 ret=RegCreateKey32W(hkey,lpszSubKey,&xhkey);
2375                 if (ret!=ERROR_SUCCESS)
2376                         return ret;
2377         } else
2378                 xhkey=hkey;
2379         if (dwType!=REG_SZ) {
2380                 fprintf(stddeb,"RegSetValueX called with dwType=%ld!\n",dwType);
2381                 dwType=REG_SZ;
2382         }
2383         if (cbData!=2*lstrlen32W(lpszData)+2) {
2384                 dprintf_reg(stddeb,"RegSetValueX called with len=%ld != strlen(%s)+1=%d!\n",
2385                         cbData,W2C(lpszData,0),2*lstrlen32W(lpszData)+2
2386                 );
2387                 cbData=2*lstrlen32W(lpszData)+2;
2388         }
2389         ret=RegSetValueEx32W(xhkey,NULL,0,dwType,(LPBYTE)lpszData,cbData);
2390         if (hkey!=xhkey)
2391                 RegCloseKey(xhkey);
2392         return ret;
2393
2394 }
2395 /* RegSetValueA                 [ADVAPI32.168] */
2396 DWORD WINAPI RegSetValue32A(
2397         HKEY    hkey,
2398         LPCSTR  lpszSubKey,
2399         DWORD   dwType,
2400         LPCSTR  lpszData,
2401         DWORD   cbData
2402 ) {
2403         DWORD   ret;
2404         HKEY    xhkey;
2405
2406         dprintf_reg(stddeb,"RegSetValue32A(%x,%s,%ld,%s,%ld)\n->",
2407                 hkey,lpszSubKey,dwType,lpszData,cbData
2408         );
2409         if (lpszSubKey && *lpszSubKey) {
2410                 ret=RegCreateKey16(hkey,lpszSubKey,&xhkey);
2411                 if (ret!=ERROR_SUCCESS)
2412                         return ret;
2413         } else
2414                 xhkey=hkey;
2415
2416         if (dwType!=REG_SZ) {
2417                 dprintf_reg(stddeb,"RegSetValueA called with dwType=%ld!\n",dwType);
2418                 dwType=REG_SZ;
2419         }
2420         if (cbData!=strlen(lpszData)+1)
2421                 cbData=strlen(lpszData)+1;
2422         ret=RegSetValueEx32A(xhkey,NULL,0,dwType,(LPBYTE)lpszData,cbData);
2423         if (xhkey!=hkey)
2424                 RegCloseKey(xhkey);
2425         return ret;
2426 }
2427
2428 /* RegSetValue                  [KERNEL.221] [SHELL.5] */
2429 DWORD WINAPI RegSetValue16(
2430         HKEY    hkey,
2431         LPCSTR  lpszSubKey,
2432         DWORD   dwType,
2433         LPCSTR  lpszData,
2434         DWORD   cbData
2435 ) {
2436         DWORD   ret;
2437         dprintf_reg(stddeb,"RegSetValue16(%x,%s,%ld,%s,%ld)\n->",
2438                 hkey,lpszSubKey,dwType,lpszData,cbData
2439         );
2440         ret=RegSetValue32A(hkey,lpszSubKey,dwType,lpszData,cbData);
2441         return ret;
2442 }
2443
2444 /* 
2445  * Key Enumeration
2446  *
2447  * Callpath:
2448  * RegEnumKey16 -> RegEnumKey32A -> RegEnumKeyEx32A \
2449  *                              RegEnumKey32W   -> RegEnumKeyEx32W
2450  */
2451
2452 /* RegEnumKeyExW                [ADVAPI32.139] */
2453 DWORD WINAPI RegEnumKeyEx32W(
2454         HKEY    hkey,
2455         DWORD   iSubkey,
2456         LPWSTR  lpszName,
2457         LPDWORD lpcchName,
2458         LPDWORD lpdwReserved,
2459         LPWSTR  lpszClass,
2460         LPDWORD lpcchClass,
2461         FILETIME        *ft
2462 ) {
2463         LPKEYSTRUCT     lpkey,lpxkey;
2464
2465         dprintf_reg(stddeb,"RegEnumKeyEx32W(%x,%ld,%p,%ld,%p,%p,%p,%p)\n",
2466                 hkey,iSubkey,lpszName,*lpcchName,lpdwReserved,lpszClass,lpcchClass,ft
2467         );
2468         lpkey=lookup_hkey(hkey);
2469         if (!lpkey)
2470                 return SHELL_ERROR_BADKEY;
2471         if (!lpkey->nextsub)
2472                 return ERROR_NO_MORE_ITEMS;
2473         lpxkey=lpkey->nextsub;
2474         while (iSubkey && lpxkey) {
2475                 iSubkey--;
2476                 lpxkey=lpxkey->next;
2477         }
2478         if (iSubkey || !lpxkey)
2479                 return ERROR_NO_MORE_ITEMS;
2480         if (2*lstrlen32W(lpxkey->keyname)+2>*lpcchName)
2481                 return ERROR_MORE_DATA;
2482         memcpy(lpszName,lpxkey->keyname,lstrlen32W(lpxkey->keyname)*2+2);
2483         if (lpszClass) {
2484                 /* what should we write into it? */
2485                 *lpszClass              = 0;
2486                 *lpcchClass     = 2;
2487         }
2488         return ERROR_SUCCESS;
2489
2490 }
2491
2492 /* RegEnumKeyW                  [ADVAPI32.140] */
2493 DWORD WINAPI RegEnumKey32W(
2494         HKEY    hkey,
2495         DWORD   iSubkey,
2496         LPWSTR  lpszName,
2497         DWORD   lpcchName
2498 ) {
2499         FILETIME        ft;
2500
2501         dprintf_reg(stddeb,"RegEnumKey32W(%x,%ld,%p,%ld)\n->",
2502                 hkey,iSubkey,lpszName,lpcchName
2503         );
2504         return RegEnumKeyEx32W(hkey,iSubkey,lpszName,&lpcchName,NULL,NULL,NULL,&ft);
2505 }
2506 /* RegEnumKeyExA                [ADVAPI32.138] */
2507 DWORD WINAPI RegEnumKeyEx32A(
2508         HKEY    hkey,
2509         DWORD   iSubkey,
2510         LPSTR   lpszName,
2511         LPDWORD lpcchName,
2512         LPDWORD lpdwReserved,
2513         LPSTR   lpszClass,
2514         LPDWORD lpcchClass,
2515         FILETIME        *ft
2516 ) {
2517         DWORD   ret,lpcchNameW,lpcchClassW;
2518         LPWSTR  lpszNameW,lpszClassW;
2519
2520
2521         dprintf_reg(stddeb,"RegEnumKeyEx32A(%x,%ld,%p,%ld,%p,%p,%p,%p)\n->",
2522                 hkey,iSubkey,lpszName,*lpcchName,lpdwReserved,lpszClass,lpcchClass,ft
2523         );
2524         if (lpszName) {
2525                 lpszNameW       = (LPWSTR)xmalloc(*lpcchName*2);
2526                 lpcchNameW      = *lpcchName*2;
2527         } else {
2528                 lpszNameW       = NULL;
2529                 lpcchNameW      = 0;
2530         }
2531         if (lpszClass) {
2532                 lpszClassW              = (LPWSTR)xmalloc(*lpcchClass*2);
2533                 lpcchClassW     = *lpcchClass*2;
2534         } else {
2535                 lpszClassW      =0;
2536                 lpcchClassW=0;
2537         }
2538         ret=RegEnumKeyEx32W(
2539                 hkey,
2540                 iSubkey,
2541                 lpszNameW,
2542                 &lpcchNameW,
2543                 lpdwReserved,
2544                 lpszClassW,
2545                 &lpcchClassW,
2546                 ft
2547         );
2548         if (ret==ERROR_SUCCESS) {
2549                 lstrcpyWtoA(lpszName,lpszNameW);
2550                 *lpcchName=strlen(lpszName);
2551                 if (lpszClassW) {
2552                         lstrcpyWtoA(lpszClass,lpszClassW);
2553                         *lpcchClass=strlen(lpszClass);
2554                 }
2555         }
2556         if (lpszNameW)
2557                 free(lpszNameW);
2558         if (lpszClassW)
2559                 free(lpszClassW);
2560         return ret;
2561 }
2562
2563 /* RegEnumKeyA                  [ADVAPI32.137] */
2564 DWORD WINAPI RegEnumKey32A(
2565         HKEY    hkey,
2566         DWORD   iSubkey,
2567         LPSTR   lpszName,
2568         DWORD   lpcchName
2569 ) {
2570         FILETIME        ft;
2571
2572         dprintf_reg(stddeb,"RegEnumKey32A(%x,%ld,%p,%ld)\n->",
2573                 hkey,iSubkey,lpszName,lpcchName
2574         );
2575         return  RegEnumKeyEx32A(
2576                 hkey,
2577                 iSubkey,
2578                 lpszName,
2579                 &lpcchName,
2580                 NULL,
2581                 NULL,
2582                 NULL,
2583                 &ft
2584         );
2585 }
2586
2587 /* RegEnumKey                   [SHELL.7] [KERNEL.216] */
2588 DWORD WINAPI RegEnumKey16(
2589         HKEY    hkey,
2590         DWORD   iSubkey,
2591         LPSTR   lpszName,
2592         DWORD   lpcchName
2593 ) {
2594         dprintf_reg(stddeb,"RegEnumKey16(%x,%ld,%p,%ld)\n->",
2595                 hkey,iSubkey,lpszName,lpcchName
2596         );
2597         return RegEnumKey32A(hkey,iSubkey,lpszName,lpcchName);
2598 }
2599
2600 /* 
2601  * Enumerate Registry Values
2602  *
2603  * Callpath:
2604  * RegEnumValue16 -> RegEnumValue32A -> RegEnumValue32W
2605  */
2606
2607 /* RegEnumValueW                [ADVAPI32.142] */
2608 DWORD WINAPI RegEnumValue32W(
2609         HKEY    hkey,
2610         DWORD   iValue,
2611         LPWSTR  lpszValue,
2612         LPDWORD lpcchValue,
2613         LPDWORD lpdReserved,
2614         LPDWORD lpdwType,
2615         LPBYTE  lpbData,
2616         LPDWORD lpcbData
2617 ) {
2618         LPKEYSTRUCT     lpkey;
2619         LPKEYVALUE      val;
2620
2621         dprintf_reg(stddeb,"RegEnumValue32W(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
2622                 hkey,iValue,lpszValue,lpcchValue,lpdReserved,lpdwType,lpbData,lpcbData
2623         );
2624         lpkey = lookup_hkey(hkey);
2625         if (!lpkey)
2626                 return SHELL_ERROR_BADKEY;
2627         if (lpkey->nrofvalues<=iValue)
2628                 return ERROR_NO_MORE_ITEMS;
2629         val     = lpkey->values+iValue;
2630
2631         if (val->name) {
2632                 if (lstrlen32W(val->name)*2+2>*lpcchValue) {
2633                         *lpcchValue = lstrlen32W(val->name)*2+2;
2634                         return ERROR_MORE_DATA;
2635                 }
2636                 memcpy(lpszValue,val->name,2*lstrlen32W(val->name)+2);
2637                 *lpcchValue=lstrlen32W(val->name)*2+2;
2638         } else {
2639                 /* how to handle NULL value? */
2640                 *lpszValue      = 0;
2641                 *lpcchValue     = 2;
2642         }
2643         *lpdwType=val->type;
2644         if (lpbData) {
2645                 if (val->len>*lpcbData)
2646                         return ERROR_MORE_DATA;
2647                 memcpy(lpbData,val->data,val->len);
2648                 *lpcbData = val->len;
2649         }
2650         return SHELL_ERROR_SUCCESS;
2651 }
2652
2653 /* RegEnumValueA                [ADVAPI32.141] */
2654 DWORD WINAPI RegEnumValue32A(
2655         HKEY    hkey,
2656         DWORD   iValue,
2657         LPSTR   lpszValue,
2658         LPDWORD lpcchValue,
2659         LPDWORD lpdReserved,
2660         LPDWORD lpdwType,
2661         LPBYTE  lpbData,
2662         LPDWORD lpcbData
2663 ) {
2664         LPWSTR  lpszValueW;
2665         LPBYTE  lpbDataW;
2666         DWORD   ret,lpcbDataW;
2667
2668         dprintf_reg(stddeb,"RegEnumValue32A(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
2669                 hkey,iValue,lpszValue,lpcchValue,lpdReserved,lpdwType,lpbData,lpcbData
2670         );
2671
2672         lpszValueW = (LPWSTR)xmalloc(*lpcchValue*2);
2673         if (lpbData) {
2674                 lpbDataW = (LPBYTE)xmalloc(*lpcbData*2);
2675                 lpcbDataW = *lpcbData*2;
2676         } else
2677                 lpbDataW = NULL;
2678         ret=RegEnumValue32W(
2679                 hkey,
2680                 iValue,
2681                 lpszValueW,
2682                 lpcchValue,
2683                 lpdReserved,
2684                 lpdwType,
2685                 lpbDataW,
2686                 &lpcbDataW
2687         );
2688
2689         if (ret==ERROR_SUCCESS) {
2690                 lstrcpyWtoA(lpszValue,lpszValueW);
2691                 if (lpbData) {
2692                         if ((1<<*lpdwType) & UNICONVMASK) {
2693                                 lstrcpyWtoA(lpbData,(LPWSTR)lpbDataW);
2694                         } else {
2695                                 if (lpcbDataW > *lpcbData)
2696                                         ret     = ERROR_MORE_DATA;
2697                                 else
2698                                         memcpy(lpbData,lpbDataW,lpcbDataW);
2699                         }
2700                         *lpcbData = lpcbDataW;
2701                 }
2702         }
2703         if (lpbDataW)
2704                 free(lpbDataW);
2705         if (lpszValueW)
2706                 free(lpszValueW);
2707         return ret;
2708 }
2709
2710 /* RegEnumValue                 [KERNEL.223] */
2711 DWORD WINAPI RegEnumValue16(
2712         HKEY    hkey,
2713         DWORD   iValue,
2714         LPSTR   lpszValue,
2715         LPDWORD lpcchValue,
2716         LPDWORD lpdReserved,
2717         LPDWORD lpdwType,
2718         LPBYTE  lpbData,
2719         LPDWORD lpcbData
2720 ) {
2721         dprintf_reg(stddeb,"RegEnumValue(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
2722                 hkey,iValue,lpszValue,lpcchValue,lpdReserved,lpdwType,lpbData,lpcbData
2723         );
2724         return RegEnumValue32A(
2725                 hkey,
2726                 iValue,
2727                 lpszValue,
2728                 lpcchValue,
2729                 lpdReserved,
2730                 lpdwType,
2731                 lpbData,
2732                 lpcbData
2733         );
2734 }
2735
2736 /* 
2737  *  Close registry key
2738  */
2739 /* RegCloseKey                  [SHELL.3] [KERNEL.220] [ADVAPI32.126] */
2740 DWORD WINAPI RegCloseKey(HKEY hkey) {
2741         dprintf_reg(stddeb,"RegCloseKey(%x)\n",hkey);
2742         remove_handle(hkey);
2743         return ERROR_SUCCESS;
2744 }
2745 /* 
2746  * Delete registry key
2747  *
2748  * Callpath:
2749  * RegDeleteKey16 -> RegDeleteKey32A -> RegDeleteKey32W
2750  */
2751 /* RegDeleteKeyW                [ADVAPI32.134] */
2752 DWORD WINAPI RegDeleteKey32W(HKEY hkey,LPWSTR lpszSubKey) {
2753         LPKEYSTRUCT     *lplpPrevKey,lpNextKey,lpxkey;
2754         LPWSTR          *wps;
2755         int             wpc,i;
2756
2757         dprintf_reg(stddeb,"RegDeleteKey32W(%x,%s)\n",
2758                 hkey,W2C(lpszSubKey,0)
2759         );
2760         lpNextKey       = lookup_hkey(hkey);
2761         if (!lpNextKey)
2762                 return SHELL_ERROR_BADKEY;
2763         /* we need to know the previous key in the hier. */
2764         if (!lpszSubKey || !*lpszSubKey)
2765                 return SHELL_ERROR_BADKEY;
2766         split_keypath(lpszSubKey,&wps,&wpc);
2767         i       = 0;
2768         lpxkey  = lpNextKey;
2769         while (i<wpc-1) {
2770                 lpxkey=lpNextKey->nextsub;
2771                 while (lpxkey) {
2772                         if (!lstrcmpi32W(wps[i],lpxkey->keyname))
2773                                 break;
2774                         lpxkey=lpxkey->next;
2775                 }
2776                 if (!lpxkey) {
2777                         FREE_KEY_PATH;
2778                         /* not found is success */
2779                         return SHELL_ERROR_SUCCESS;
2780                 }
2781                 i++;
2782                 lpNextKey       = lpxkey;
2783         }
2784         lpxkey  = lpNextKey->nextsub;
2785         lplpPrevKey = &(lpNextKey->nextsub);
2786         while (lpxkey) {
2787                 if (!lstrcmpi32W(wps[i],lpxkey->keyname))
2788                         break;
2789                 lplpPrevKey     = &(lpxkey->next);
2790                 lpxkey          = lpxkey->next;
2791         }
2792         if (!lpxkey)
2793                 return SHELL_ERROR_SUCCESS;
2794         if (lpxkey->nextsub)
2795                 return SHELL_ERROR_CANTWRITE;
2796         *lplpPrevKey    = lpxkey->next;
2797         free(lpxkey->keyname);
2798         if (lpxkey->class)
2799                 free(lpxkey->class);
2800         if (lpxkey->values)
2801                 free(lpxkey->values);
2802         free(lpxkey);
2803         FREE_KEY_PATH;
2804         return  SHELL_ERROR_SUCCESS;
2805 }
2806
2807 /* RegDeleteKeyA                [ADVAPI32.133] */
2808 DWORD WINAPI RegDeleteKey32A(HKEY hkey,LPCSTR lpszSubKey) {
2809         LPWSTR  lpszSubKeyW;
2810         DWORD   ret;
2811
2812         dprintf_reg(stddeb,"RegDeleteKey32A(%x,%s)\n",
2813                 hkey,lpszSubKey
2814         );
2815         lpszSubKeyW=HEAP_strdupAtoW(GetProcessHeap(),0,lpszSubKey);
2816         ret=RegDeleteKey32W(hkey,lpszSubKeyW);
2817         HeapFree(GetProcessHeap(),0,lpszSubKeyW);
2818         return ret;
2819 }
2820
2821 /* RegDeleteKey                 [SHELL.4] [KERNEL.219] */
2822 DWORD WINAPI RegDeleteKey16(HKEY hkey,LPCSTR lpszSubKey) {
2823         dprintf_reg(stddeb,"RegDeleteKey16(%x,%s)\n",
2824                 hkey,lpszSubKey
2825         );
2826         return RegDeleteKey32A(hkey,lpszSubKey);
2827 }
2828
2829 /* 
2830  * Delete registry value
2831  *
2832  * Callpath:
2833  * RegDeleteValue16 -> RegDeleteValue32A -> RegDeleteValue32W
2834  */
2835 /* RegDeleteValueW              [ADVAPI32.136] */
2836 DWORD WINAPI RegDeleteValue32W(HKEY hkey,LPWSTR lpszValue)
2837 {
2838         DWORD           i;
2839         LPKEYSTRUCT     lpkey;
2840         LPKEYVALUE      val;
2841
2842         dprintf_reg(stddeb,"RegDeleteValue32W(%x,%s)\n",
2843                 hkey,W2C(lpszValue,0)
2844         );
2845         lpkey=lookup_hkey(hkey);
2846         if (!lpkey)
2847                 return SHELL_ERROR_BADKEY;
2848         if (lpszValue) {
2849                 for (i=0;i<lpkey->nrofvalues;i++)
2850                         if (    lpkey->values[i].name &&
2851                                 !lstrcmpi32W(lpkey->values[i].name,lpszValue)
2852                         )
2853                                 break;
2854         } else {
2855                 for (i=0;i<lpkey->nrofvalues;i++)
2856                         if (lpkey->values[i].name==NULL)
2857                                 break;
2858         }
2859         if (i==lpkey->nrofvalues)
2860                 return SHELL_ERROR_BADKEY;/*FIXME: correct errorcode? */
2861         val     = lpkey->values+i;
2862         if (val->name) free(val->name);
2863         if (val->data) free(val->data);
2864         memcpy( 
2865                 lpkey->values+i,
2866                 lpkey->values+i+1,
2867                 sizeof(KEYVALUE)*(lpkey->nrofvalues-i-1)
2868         );
2869         lpkey->values   = (LPKEYVALUE)xrealloc(
2870                                 lpkey->values,
2871                                 (lpkey->nrofvalues-1)*sizeof(KEYVALUE)
2872                         );
2873         lpkey->nrofvalues--;
2874         return SHELL_ERROR_SUCCESS;
2875 }
2876
2877 /* RegDeleteValueA              [ADVAPI32.135] */
2878 DWORD WINAPI RegDeleteValue32A(HKEY hkey,LPSTR lpszValue)
2879 {
2880         LPWSTR  lpszValueW;
2881         DWORD   ret;
2882
2883         dprintf_reg( stddeb, "RegDeleteValue32A(%x,%s)\n", hkey,lpszValue );
2884         lpszValueW=HEAP_strdupAtoW(GetProcessHeap(),0,lpszValue);
2885         ret=RegDeleteValue32W(hkey,lpszValueW);
2886         HeapFree(GetProcessHeap(),0,lpszValueW);
2887         return ret;
2888 }
2889
2890 /* RegDeleteValue               [KERNEL.222] */
2891 DWORD WINAPI RegDeleteValue16(HKEY hkey,LPSTR lpszValue)
2892 {
2893         dprintf_reg( stddeb,"RegDeleteValue16(%x,%s)\n", hkey,lpszValue );
2894         return RegDeleteValue32A(hkey,lpszValue);
2895 }
2896
2897 /* RegFlushKey                  [ADVAPI32.143] [KERNEL.227] */
2898 DWORD WINAPI RegFlushKey(HKEY hkey)
2899 {
2900         dprintf_reg(stddeb,"RegFlushKey(%x), STUB.\n",hkey);
2901         return SHELL_ERROR_SUCCESS;
2902 }
2903
2904 /* FIXME: lpcchXXXX ... is this counting in WCHARS or in BYTEs ?? */
2905
2906 /* RegQueryInfoKeyW             [ADVAPI32.153] */
2907 DWORD WINAPI RegQueryInfoKey32W(
2908         HKEY    hkey,
2909         LPWSTR  lpszClass,
2910         LPDWORD lpcchClass,
2911         LPDWORD lpdwReserved,
2912         LPDWORD lpcSubKeys,
2913         LPDWORD lpcchMaxSubkey,
2914         LPDWORD lpcchMaxClass,
2915         LPDWORD lpcValues,
2916         LPDWORD lpcchMaxValueName,
2917         LPDWORD lpccbMaxValueData,
2918         LPDWORD lpcbSecurityDescriptor,
2919         FILETIME        *ft
2920 ) {
2921         LPKEYSTRUCT     lpkey,lpxkey;
2922         int             nrofkeys,maxsubkey,maxclass,maxvalues,maxvname,maxvdata;
2923         int             i;
2924
2925         dprintf_reg(stddeb,"RegQueryInfoKey32W(%x,......)\n",hkey);
2926         lpkey=lookup_hkey(hkey);
2927         if (!lpkey)
2928                 return SHELL_ERROR_BADKEY;
2929         if (lpszClass) {
2930                 if (lpkey->class) {
2931                         if (lstrlen32W(lpkey->class)*2+2>*lpcchClass) {
2932                                 *lpcchClass=lstrlen32W(lpkey->class)*2;
2933                                 return ERROR_MORE_DATA;
2934                         }
2935                         *lpcchClass=lstrlen32W(lpkey->class)*2;
2936                         memcpy(lpszClass,lpkey->class,lstrlen32W(lpkey->class));
2937                 } else {
2938                         *lpszClass      = 0;
2939                         *lpcchClass     = 0;
2940                 }
2941         } else {
2942                 if (lpcchClass)
2943                         *lpcchClass     = lstrlen32W(lpkey->class)*2;
2944         }
2945         lpxkey=lpkey->nextsub;
2946         nrofkeys=maxsubkey=maxclass=maxvalues=maxvname=maxvdata=0;
2947         while (lpxkey) {
2948                 nrofkeys++;
2949                 if (lstrlen32W(lpxkey->keyname)>maxsubkey)
2950                         maxsubkey=lstrlen32W(lpxkey->keyname);
2951                 if (lpxkey->class && lstrlen32W(lpxkey->class)>maxclass)
2952                         maxclass=lstrlen32W(lpxkey->class);
2953                 if (lpxkey->nrofvalues>maxvalues)
2954                         maxvalues=lpxkey->nrofvalues;
2955                 for (i=0;i<lpxkey->nrofvalues;i++) {
2956                         LPKEYVALUE      val=lpxkey->values+i;
2957
2958                         if (val->name && lstrlen32W(val->name)>maxvname)
2959                                 maxvname=lstrlen32W(val->name);
2960                         if (val->len>maxvdata)
2961                                 maxvdata=val->len;
2962                 }
2963                 lpxkey=lpxkey->next;
2964         }
2965         if (!maxclass) maxclass = 1;
2966         if (!maxvname) maxvname = 1;
2967         if (lpcSubKeys)
2968                 *lpcSubKeys     = nrofkeys;
2969         if (lpcchMaxSubkey)
2970                 *lpcchMaxSubkey = maxsubkey*2;
2971         if (lpcchMaxClass)
2972                 *lpcchMaxClass  = maxclass*2;
2973         if (lpcValues)
2974                 *lpcValues      = maxvalues;
2975         if (lpcchMaxValueName)
2976                 *lpcchMaxValueName= maxvname;
2977         if (lpccbMaxValueData)
2978                 *lpccbMaxValueData= maxvdata;
2979         return SHELL_ERROR_SUCCESS;
2980 }
2981
2982 /* RegQueryInfoKeyA             [ADVAPI32.152] */
2983 DWORD WINAPI RegQueryInfoKey32A(
2984         HKEY    hkey,
2985         LPSTR   lpszClass,
2986         LPDWORD lpcchClass,
2987         LPDWORD lpdwReserved,
2988         LPDWORD lpcSubKeys,
2989         LPDWORD lpcchMaxSubkey,
2990         LPDWORD lpcchMaxClass,
2991         LPDWORD lpcValues,
2992         LPDWORD lpcchMaxValueName,
2993         LPDWORD lpccbMaxValueData,
2994         LPDWORD lpcbSecurityDescriptor,
2995         FILETIME        *ft
2996 ) {
2997         LPWSTR          lpszClassW;
2998         DWORD           ret;
2999
3000         dprintf_reg(stddeb,"RegQueryInfoKey32A(%x,......)\n",hkey);
3001         if (lpszClass) {
3002                 *lpcchClass*= 2;
3003                 lpszClassW  = (LPWSTR)xmalloc(*lpcchClass);
3004
3005         } else
3006                 lpszClassW  = NULL;
3007         ret=RegQueryInfoKey32W(
3008                 hkey,
3009                 lpszClassW,
3010                 lpcchClass,
3011                 lpdwReserved,
3012                 lpcSubKeys,
3013                 lpcchMaxSubkey,
3014                 lpcchMaxClass,
3015                 lpcValues,
3016                 lpcchMaxValueName,
3017                 lpccbMaxValueData,
3018                 lpcbSecurityDescriptor,
3019                 ft
3020         );
3021         if (ret==ERROR_SUCCESS && lpszClass)
3022                 lstrcpyWtoA(lpszClass,lpszClassW);
3023         if (lpcchClass)
3024                 *lpcchClass/=2;
3025         if (lpcchMaxSubkey)
3026                 *lpcchMaxSubkey/=2;
3027         if (lpcchMaxClass)
3028                 *lpcchMaxClass/=2;
3029         if (lpcchMaxValueName)
3030                 *lpcchMaxValueName/=2;
3031         if (lpszClassW)
3032                 free(lpszClassW);
3033         return ret;
3034 }
3035 /* RegConnectRegistryA          [ADVAPI32.127] */
3036 DWORD WINAPI RegConnectRegistry32A(LPCSTR machine,HKEY hkey,LPHKEY reskey)
3037 {
3038         fprintf(stderr,"RegConnectRegistry32A(%s,%08x,%p), STUB.\n",
3039                 machine,hkey,reskey
3040         );
3041         return ERROR_FILE_NOT_FOUND; /* FIXME */
3042 }