Moved almost all remaining process, thread, fiber and exception
[wine] / programs / regedit / regproc.c
1 /*
2  * Registry processing routines. Routines, common for registry
3  * processing frontends.
4  *
5  * Copyright 1999 Sylvain St-Germain
6  * Copyright 2002 Andriy Palamarchuk
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <limits.h>
24 #include <stdio.h>
25 #include <windows.h>
26 #include <winnt.h>
27 #include <winreg.h>
28 #include <assert.h>
29 #include "regproc.h"
30
31 #define REG_VAL_BUF_SIZE        4096
32
33 /* Delimiters used to parse the "value" to query queryValue*/
34 #define QUERY_VALUE_MAX_ARGS  1
35
36 /* maximal number of characters in hexadecimal data line,
37    not including '\' character */
38 #define REG_FILE_HEX_LINE_LEN   76
39
40 /* Globals used by the api setValue, queryValue */
41 static LPSTR currentKeyName   = NULL;
42 static HKEY  currentKeyClass  = 0;
43 static HKEY  currentKeyHandle = 0;
44 static BOOL  bTheKeyIsOpen    = FALSE;
45
46 static CHAR *app_name = "UNKNOWN";
47
48 static CHAR *reg_class_names[] = {
49     "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
50     "HKEY_CURRENT_CONFIG", "HKEY_CURRENT_USER"
51 };
52
53 #define REG_CLASS_NUMBER (sizeof(reg_class_names) / sizeof(reg_class_names[0]))
54
55 static HKEY reg_class_keys[REG_CLASS_NUMBER] = {
56     HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
57     HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER
58 };
59
60 /* return values */
61 #define NOT_ENOUGH_MEMORY     1
62 #define IO_ERROR              2
63
64 /* processing macros */
65
66 /* common check of memory allocation results */
67 #define CHECK_ENOUGH_MEMORY(p) \
68     if (!(p)) \
69     { \
70         printf("%s: file %s, line %d: Not enough memory", \
71                getAppName(), __FILE__, __LINE__); \
72         exit(NOT_ENOUGH_MEMORY); \
73     }
74
75 /******************************************************************************
76  * This is a replacement for strsep which is not portable (missing on Solaris).
77  */
78 #if 0
79 /* DISABLED */
80 char* getToken(char** str, const char* delims)
81 {
82     char* token;
83
84     if (*str==NULL) {
85         /* No more tokens */
86         return NULL;
87     }
88
89     token=*str;
90     while (**str!='\0') {
91         if (strchr(delims,**str)!=NULL) {
92             **str='\0';
93             (*str)++;
94             return token;
95         }
96         (*str)++;
97     }
98     /* There is no other token */
99     *str=NULL;
100     return token;
101 }
102 #endif
103
104 /******************************************************************************
105  * Copies file name from command line string to the buffer.
106  * Rewinds the command line string pointer to the next non-space character
107  * after the file name.
108  * Buffer contains an empty string if no filename was found;
109  *
110  * params:
111  * command_line - command line current position pointer
112  *      where *s[0] is the first symbol of the file name.
113  * file_name - buffer to write the file name to.
114  */
115 void get_file_name(CHAR **command_line, CHAR *file_name)
116 {
117     CHAR *s = *command_line;
118     int pos = 0;                /* position of pointer "s" in *command_line */
119     file_name[0] = 0;
120
121     if (!s[0])
122     {
123         return;
124     }
125
126     if (s[0] == '"')
127     {
128         s++;
129         (*command_line)++;
130         while(s[0] != '"')
131         {
132             if (!s[0])
133             {
134                 printf("%s: Unexpected end of file name!\n", getAppName());
135                 exit(1);
136             }
137             s++;
138             pos++;
139         }
140     } else {
141         while(s[0] && !isspace(s[0]))
142         {
143             s++;
144             pos++;
145         }
146     }
147     memcpy(file_name, *command_line, pos * sizeof((*command_line)[0]));
148     /* remove the last backslash */
149     if (file_name[pos - 1] == '\\')
150     {
151         file_name[pos - 1] = '\0';
152     } else {
153         file_name[pos] = '\0';
154     }
155
156     if (s[0])
157     {
158         s++;
159         pos++;
160     }
161     while(s[0] && isspace(s[0]))
162     {
163         s++;
164         pos++;
165     }
166     (*command_line) += pos;
167 }
168
169
170 /******************************************************************************
171  * Converts a hex representation of a DWORD into a DWORD.
172  */
173 DWORD convertHexToDWord(char *str, BYTE *buf)
174 {
175   DWORD dw;
176   char xbuf[9];
177
178   memcpy(xbuf,str,8);
179   xbuf[8]='\0';
180   sscanf(xbuf,"%08lx",&dw);
181   memcpy(buf,&dw,sizeof(DWORD));
182   return sizeof(DWORD);
183 }
184
185 /******************************************************************************
186  * Converts a hex buffer into a hex comma separated values
187  */
188 char* convertHexToHexCSV(BYTE *buf, ULONG bufLen)
189 {
190   char* str;
191   char* ptrStr;
192   BYTE* ptrBuf;
193
194   ULONG current = 0;
195
196   str    = HeapAlloc(GetProcessHeap(), 0, (bufLen+1)*2);
197   memset(str, 0, (bufLen+1)*2);
198   ptrStr = str;  /* Pointer to result  */
199   ptrBuf = buf;  /* Pointer to current */
200
201   while (current < bufLen)
202   {
203     BYTE bCur = ptrBuf[current++];
204     char res[3];
205
206     sprintf(res, "%02x", (unsigned int)*&bCur);
207     strcat(str, res);
208     strcat(str, ",");
209   }
210
211   /* Get rid of the last comma */
212   str[strlen(str)-1] = '\0';
213   return str;
214 }
215
216 /******************************************************************************
217  * Converts a hex buffer into a DWORD string
218  */
219 char* convertHexToDWORDStr(BYTE *buf, ULONG bufLen)
220 {
221   char* str;
222   DWORD dw;
223
224   if ( bufLen != sizeof(DWORD) ) return NULL;
225
226   str = HeapAlloc(GetProcessHeap(), 0, (bufLen*2)+1);
227
228   memcpy(&dw,buf,sizeof(DWORD));
229   sprintf(str, "%08lx", dw);
230
231   /* Get rid of the last comma */
232   return str;
233 }
234
235 /******************************************************************************
236  * Converts a hex comma separated values list into a hex list.
237  * The Hex input string must be in exactly the correct form.
238  */
239 DWORD convertHexCSVToHex(char *str, BYTE *buf, ULONG bufLen)
240 {
241   char *s = str;  /* Pointer to current */
242   char *b = buf;  /* Pointer to result  */
243
244   ULONG strLen    = strlen(str);
245   ULONG strPos    = 0;
246   DWORD byteCount = 0;
247
248   memset(buf, 0, bufLen);
249
250   /*
251    * warn the user if we are here with a string longer than 2 bytes that does
252    * not contains ",".  It is more likely because the data is invalid.
253    */
254   if ( ( strLen > 2) && ( strchr(str, ',') == NULL) )
255     printf("%s: WARNING converting CSV hex stream with no comma, "
256            "input data seems invalid.\n", getAppName());
257   if (strLen > 3*bufLen)
258     printf ("%s: ERROR converting CSV hex stream.  Too long\n", getAppName());
259
260   while (strPos < strLen)
261   {
262     char xbuf[3];
263     UINT wc;
264
265     memcpy(xbuf,s,2); xbuf[2]='\0';
266     sscanf(xbuf,"%02x",&wc);
267     if (byteCount < bufLen)
268       *b++ =(unsigned char)wc;
269
270     s+=3;
271     strPos+=3;
272     byteCount++;
273   }
274
275   return byteCount;
276 }
277
278 /******************************************************************************
279  * This function returns the HKEY associated with the data type encoded in the
280  * value.  It modifies the input parameter (key value) in order to skip this
281  * "now useless" data type information.
282  *
283  * Note: Updated based on the algorithm used in 'server/registry.c'
284  */
285 DWORD getDataType(LPSTR *lpValue, DWORD* parse_type)
286 {
287     struct data_type { const char *tag; int len; int type; int parse_type; };
288
289     static const struct data_type data_types[] =
290     {                   /* actual type */  /* type to assume for parsing */
291         { "\"",        1,   REG_SZ,              REG_SZ },
292         { "str:\"",    5,   REG_SZ,              REG_SZ },
293         { "str(2):\"", 8,   REG_EXPAND_SZ,       REG_SZ },
294         { "hex:",      4,   REG_BINARY,          REG_BINARY },
295         { "dword:",    6,   REG_DWORD,           REG_DWORD },
296         { "hex(",      4,   -1,                  REG_BINARY },
297         { NULL,        0,    0,                  0 }
298     };
299
300     const struct data_type *ptr;
301     int type;
302
303     for (ptr = data_types; ptr->tag; ptr++)
304     {
305         if (memcmp( ptr->tag, *lpValue, ptr->len ))
306             continue;
307
308         /* Found! */
309         *parse_type = ptr->parse_type;
310         type=ptr->type;
311         *lpValue+=ptr->len;
312         if (type == -1) {
313             char* end;
314             /* "hex(xx):" is special */
315             type = (int)strtoul( *lpValue , &end, 16 );
316             if (**lpValue=='\0' || *end!=')' || *(end+1)!=':') {
317                 type=REG_NONE;
318             } else {
319                 *lpValue=end+2;
320             }
321         }
322         return type;
323     }
324     return (**lpValue=='\0'?REG_SZ:REG_NONE);
325 }
326
327 /******************************************************************************
328  * Returns an allocated buffer with a cleaned copy (removed the surrounding
329  * dbl quotes) of the passed value.
330  */
331 LPSTR getArg( LPSTR arg)
332 {
333   LPSTR tmp = NULL;
334   ULONG len;
335
336   if (arg == NULL)
337     return NULL;
338
339   /*
340    * Get rid of surrounding quotes
341    */
342   len = strlen(arg);
343
344   if( arg[len-1] == '\"' ) arg[len-1] = '\0';
345   if( arg[0]     == '\"' ) arg++;
346
347   tmp = HeapAlloc(GetProcessHeap(), 0, strlen(arg)+1);
348   strcpy(tmp, arg);
349
350   return tmp;
351 }
352
353 /******************************************************************************
354  * Replaces escape sequences with the characters.
355  */
356 void REGPROC_unescape_string(LPSTR str)
357 {
358     int str_idx = 0;            /* current character under analysis */
359     int val_idx = 0;            /* the last character of the unescaped string */
360     int len = strlen(str);
361     for (str_idx = 0; str_idx < len; str_idx++, val_idx++)
362     {
363         if (str[str_idx] == '\\')
364         {
365             str_idx++;
366             switch (str[str_idx])
367             {
368             case 'n':
369                 str[val_idx] = '\n';
370                 break;
371             case '\\':
372             case '"':
373                 str[val_idx] = str[str_idx];
374                 break;
375             default:
376                 printf("Warning! Unrecognized escape sequence: \\%c'\n",
377                        str[str_idx]);
378                 str[val_idx] = str[str_idx];
379                 break;
380             }
381         } else {
382             str[val_idx] = str[str_idx];
383         }
384     }
385     str[val_idx] = '\0';
386 }
387
388 /******************************************************************************
389  * Sets the value with name val_name to the data in val_data for the currently
390  * opened key.
391  *
392  * Parameters:
393  * val_name - name of the registry value
394  * val_data - registry value data
395  */
396 HRESULT setValue(LPSTR val_name, LPSTR val_data)
397 {
398   HRESULT hRes;
399   DWORD   dwDataType, dwParseType;
400   LPBYTE lpbData;
401   BYTE   convert[KEY_MAX_LEN];
402   BYTE *bBigBuffer = 0;
403   DWORD  dwLen;
404
405   if ( (val_name == NULL) || (val_data == NULL) )
406     return ERROR_INVALID_PARAMETER;
407
408   /* Get the data type stored into the value field */
409   dwDataType = getDataType(&val_data, &dwParseType);
410
411   if ( dwParseType == REG_SZ)        /* no conversion for string */
412   {
413     dwLen = strlen(val_data);
414     if (dwLen>0 && val_data[dwLen-1]=='"')
415     {
416       dwLen--;
417       val_data[dwLen]='\0';
418     }
419     dwLen++;
420     REGPROC_unescape_string(val_data);
421     lpbData = val_data;
422   }
423   else if (dwParseType == REG_DWORD)  /* Convert the dword types */
424   {
425     dwLen   = convertHexToDWord(val_data, convert);
426     lpbData = convert;
427   }
428   else                               /* Convert the hexadecimal types */
429   {
430     int b_len = strlen (val_data)+2/3;
431     if (b_len > KEY_MAX_LEN)
432     {
433       bBigBuffer = HeapAlloc (GetProcessHeap(), 0, b_len);
434       CHECK_ENOUGH_MEMORY(bBigBuffer);
435       dwLen = convertHexCSVToHex(val_data, bBigBuffer, b_len);
436       lpbData = bBigBuffer;
437     }
438     else
439     {
440       dwLen   = convertHexCSVToHex(val_data, convert, KEY_MAX_LEN);
441       lpbData = convert;
442     }
443   }
444
445   hRes = RegSetValueEx(
446           currentKeyHandle,
447           val_name,
448           0,                  /* Reserved */
449           dwDataType,
450           lpbData,
451           dwLen);
452
453   if (bBigBuffer)
454       HeapFree (GetProcessHeap(), 0, bBigBuffer);
455   return hRes;
456 }
457
458
459 /******************************************************************************
460  * Open the key
461  */
462 HRESULT openKey( LPSTR stdInput)
463 {
464   DWORD   dwDisp;
465   HRESULT hRes;
466
467   /* Sanity checks */
468   if (stdInput == NULL)
469     return ERROR_INVALID_PARAMETER;
470
471   /* Get the registry class */
472   currentKeyClass = getRegClass(stdInput); /* Sets global variable */
473   if (currentKeyClass == (HKEY)ERROR_INVALID_PARAMETER)
474     return (HRESULT)ERROR_INVALID_PARAMETER;
475
476   /* Get the key name */
477   currentKeyName = getRegKeyName(stdInput); /* Sets global variable */
478   if (currentKeyName == NULL)
479     return ERROR_INVALID_PARAMETER;
480
481   hRes = RegCreateKeyEx(
482           currentKeyClass,          /* Class     */
483           currentKeyName,           /* Sub Key   */
484           0,                        /* MUST BE 0 */
485           NULL,                     /* object type */
486           REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
487           KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
488           NULL,                     /* security attribute */
489           &currentKeyHandle,        /* result */
490           &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
491                                                     REG_OPENED_EXISTING_KEY */
492
493   if (hRes == ERROR_SUCCESS)
494     bTheKeyIsOpen = TRUE;
495
496   return hRes;
497
498 }
499
500 /******************************************************************************
501  * Extracts from [HKEY\some\key\path] or HKEY\some\key\path types of line
502  * the key name (what starts after the first '\')
503  */
504 LPSTR getRegKeyName(LPSTR lpLine)
505 {
506   LPSTR keyNameBeg;
507   char  lpLineCopy[KEY_MAX_LEN];
508
509   if (lpLine == NULL)
510     return NULL;
511
512   strcpy(lpLineCopy, lpLine);
513
514   keyNameBeg = strchr(lpLineCopy, '\\');    /* The key name start by '\' */
515   if (keyNameBeg)
516   {
517       LPSTR keyNameEnd;
518
519       keyNameBeg++;                             /* is not part of the name */
520       keyNameEnd = strchr(lpLineCopy, ']');
521       if (keyNameEnd)
522       {
523           *keyNameEnd = '\0';               /* remove ']' from the key name */
524       }
525   } else {
526       keyNameBeg = lpLineCopy + strlen(lpLineCopy); /* branch - empty string */
527   }
528   currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(keyNameBeg) + 1);
529   CHECK_ENOUGH_MEMORY(currentKeyName);
530   strcpy(currentKeyName, keyNameBeg);
531    return currentKeyName;
532 }
533
534 /******************************************************************************
535  * Extracts from [HKEY\some\key\path] or HKEY\some\key\path types of line
536  * the key class (what ends before the first '\')
537  */
538 HKEY getRegClass(LPSTR lpClass)
539 {
540   LPSTR classNameEnd;
541   LPSTR classNameBeg;
542   int i;
543
544   char  lpClassCopy[KEY_MAX_LEN];
545
546   if (lpClass == NULL)
547     return (HKEY)ERROR_INVALID_PARAMETER;
548
549   strncpy(lpClassCopy, lpClass, KEY_MAX_LEN);
550
551   classNameEnd  = strchr(lpClassCopy, '\\');    /* The class name ends by '\' */
552   if (!classNameEnd)                            /* or the whole string */
553   {
554       classNameEnd = lpClassCopy + strlen(lpClassCopy);
555       if (classNameEnd[-1] == ']')
556       {
557           classNameEnd--;
558       }
559   }
560   *classNameEnd = '\0';                       /* Isolate the class name */
561   if (lpClassCopy[0] == '[')
562   {
563       classNameBeg = lpClassCopy + 1;
564   } else {
565       classNameBeg = lpClassCopy;
566   }
567
568   for (i = 0; i < REG_CLASS_NUMBER; i++)
569   {
570       if (!strcmp(classNameBeg, reg_class_names[i]))
571       {
572           return reg_class_keys[i];
573       }
574   }
575   return (HKEY)ERROR_INVALID_PARAMETER;
576 }
577
578 /******************************************************************************
579  * Close the currently opened key.
580  */
581 void closeKey()
582 {
583   RegCloseKey(currentKeyHandle);
584
585   HeapFree(GetProcessHeap(), 0, currentKeyName); /* Allocated by getKeyName */
586
587   bTheKeyIsOpen    = FALSE;
588
589   currentKeyName   = NULL;
590   currentKeyClass  = 0;
591   currentKeyHandle = 0;
592 }
593
594 /******************************************************************************
595  * This function is the main entry point to the setValue type of action.  It
596  * receives the currently read line and dispatch the work depending on the
597  * context.
598  */
599 void doSetValue(LPSTR stdInput)
600 {
601   /*
602    * We encoutered the end of the file, make sure we
603    * close the opened key and exit
604    */
605   if (stdInput == NULL)
606   {
607     if (bTheKeyIsOpen != FALSE)
608       closeKey();
609
610     return;
611   }
612
613   if      ( stdInput[0] == '[')      /* We are reading a new key */
614   {
615     if ( bTheKeyIsOpen != FALSE )
616       closeKey();                    /* Close the previous key before */
617
618     if ( openKey(stdInput) != ERROR_SUCCESS )
619       printf("%s: setValue failed to open key %s\n", getAppName(), stdInput);
620   }
621   else if( ( bTheKeyIsOpen ) &&
622            (( stdInput[0] == '@') || /* reading a default @=data pair */
623             ( stdInput[0] == '\"'))) /* reading a new value=data pair */
624   {
625     processSetValue(stdInput);
626   }
627   else                               /* since we are assuming that the */
628   {                                  /* file format is valid we must   */
629     if ( bTheKeyIsOpen )             /* be reading a blank line which  */
630       closeKey();                    /* indicate end of this key processing */
631   }
632 }
633
634 /******************************************************************************
635  * This funtion is the main entry point to the queryValue type of action.  It
636  * receives the currently read line and dispatch the work depending on the
637  * context.
638  */
639 void doQueryValue(LPSTR stdInput) {
640   /*
641    * We encoutered the end of the file, make sure we
642    * close the opened key and exit
643    */
644   if (stdInput == NULL)
645   {
646     if (bTheKeyIsOpen != FALSE)
647       closeKey();
648
649     return;
650   }
651
652   if      ( stdInput[0] == '[')      /* We are reading a new key */
653   {
654     if ( bTheKeyIsOpen != FALSE )
655       closeKey();                    /* Close the previous key before */
656
657     if ( openKey(stdInput) != ERROR_SUCCESS )
658       printf("%s: queryValue failed to open key %s\n", getAppName(), stdInput);
659   }
660   else if( ( bTheKeyIsOpen ) &&
661            (( stdInput[0] == '@') || /* reading a default @=data pair */
662             ( stdInput[0] == '\"'))) /* reading a new value=data pair */
663   {
664     processQueryValue(stdInput);
665   }
666   else                               /* since we are assuming that the */
667   {                                  /* file format is valid we must   */
668     if ( bTheKeyIsOpen )             /* be reading a blank line which  */
669       closeKey();                    /* indicate end of this key processing */
670   }
671 }
672
673 /******************************************************************************
674  * This funtion is the main entry point to the deletetValue type of action.  It
675  * receives the currently read line and dispatch the work depending on the
676  * context.
677  */
678 void doDeleteValue(LPSTR line) {
679   printf ("%s: deleteValue not yet implemented\n", getAppName());
680 }
681
682 /******************************************************************************
683  * This funtion is the main entry point to the deleteKey type of action.  It
684  * receives the currently read line and dispatch the work depending on the
685  * context.
686  */
687 void doDeleteKey(LPSTR line)   {
688   printf ("%s: deleteKey not yet implemented\n", getAppName());
689 }
690
691 /******************************************************************************
692  * This funtion is the main entry point to the createKey type of action.  It
693  * receives the currently read line and dispatch the work depending on the
694  * context.
695  */
696 void doCreateKey(LPSTR line)   {
697   printf ("%s: createKey not yet implemented\n", getAppName());
698 }
699
700 /******************************************************************************
701  * This function is a wrapper for the setValue function.  It prepares the
702  * land and clean the area once completed.
703  * Note: this function modifies the line parameter.
704  *
705  * line - registry file unwrapped line. Should have the registry value name and
706  *      complete registry value data.
707  */
708 void processSetValue(LPSTR line)
709 {
710   LPSTR val_name;                   /* registry value name   */
711   LPSTR val_data;                   /* registry value data   */
712
713   int line_idx = 0;                 /* current character under analysis */
714   HRESULT hRes = 0;
715
716   /* get value name */
717   if (line[line_idx] == '@' && line[line_idx + 1] == '=')
718   {
719       line[line_idx] = '\0';
720       val_name = line;
721       line_idx++;
722   }
723   else if (line[line_idx] == '\"')
724   {
725       line_idx++;
726       val_name = line + line_idx;
727       while (TRUE)
728       {
729           if (line[line_idx] == '\\')   /* skip escaped character */
730           {
731               line_idx += 2;
732           } else {
733               if (line[line_idx] == '\"')
734               {
735                   line[line_idx] = '\0';
736                   line_idx++;
737                   break;
738               } else {
739                   line_idx++;
740               }
741           }
742       }
743       if (line[line_idx] != '=')
744       {
745           line[line_idx] = '\"';
746           printf("Warning! unrecognized line:\n%s\n", line);
747           return;
748       }
749
750   }
751   else
752   {
753       printf("Warning! unrecognized line:\n%s\n", line);
754       return;
755   }
756   line_idx++;                   /* skip the '=' character */
757   val_data = line + line_idx;
758
759   REGPROC_unescape_string(val_name);
760   hRes = setValue(val_name, val_data);
761   if ( hRes != ERROR_SUCCESS )
762     printf("%s: ERROR Key %s not created. Value: %s, Data: %s\n",
763            getAppName(),
764            currentKeyName,
765            val_name,
766            val_data);
767 }
768
769 /******************************************************************************
770  * This function is a wrapper for the queryValue function.  It prepares the
771  * land and clean the area once completed.
772  */
773 void processQueryValue(LPSTR cmdline)
774 {
775   printf("ERROR!!! - temporary disabled");
776   exit(1);
777 #if 0
778   LPSTR   argv[QUERY_VALUE_MAX_ARGS];/* args storage    */
779   LPSTR   token      = NULL;         /* current token analized */
780   ULONG   argCounter = 0;            /* counter of args */
781   INT     counter;
782   HRESULT hRes       = 0;
783   LPSTR   keyValue   = NULL;
784   LPSTR   lpsRes     = NULL;
785
786   /*
787    * Init storage and parse the line
788    */
789   for (counter=0; counter<QUERY_VALUE_MAX_ARGS; counter++)
790     argv[counter]=NULL;
791
792   while( (token = getToken(&cmdline, queryValueDelim[argCounter])) != NULL )
793   {
794     argv[argCounter++] = getArg(token);
795
796     if (argCounter == QUERY_VALUE_MAX_ARGS)
797       break;  /* Stop processing args no matter what */
798   }
799
800   /* The value we look for is the first token on the line */
801   if ( argv[0] == NULL )
802     return; /* SHOULD NOT HAPPEN */
803   else
804     keyValue = argv[0];
805
806   if( (keyValue[0] == '@') && (strlen(keyValue) == 1) )
807   {
808     LONG  lLen  = KEY_MAX_LEN;
809     CHAR*  lpsData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
810     /*
811      * We need to query the key default value
812      */
813     hRes = RegQueryValue(
814              currentKeyHandle,
815              currentKeyName,
816              (LPBYTE)lpsData,
817              &lLen);
818
819     if (hRes==ERROR_MORE_DATA) {
820         lpsData=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpsData,lLen);
821         hRes = RegQueryValue(currentKeyHandle,currentKeyName,(LPBYTE)lpsData,&lLen);
822     }
823
824     if (hRes == ERROR_SUCCESS)
825     {
826       lpsRes = HeapAlloc( GetProcessHeap(), 0, lLen);
827       strncpy(lpsRes, lpsData, lLen);
828       lpsRes[lLen-1]='\0';
829     }
830   }
831   else
832   {
833     DWORD  dwLen  = KEY_MAX_LEN;
834     BYTE*  lpbData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
835     DWORD  dwType;
836     /*
837      * We need to query a specific value for the key
838      */
839     hRes = RegQueryValueEx(
840              currentKeyHandle,
841              keyValue,
842              0,
843              &dwType,
844              (LPBYTE)lpbData,
845              &dwLen);
846
847     if (hRes==ERROR_MORE_DATA) {
848         lpbData=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpbData,dwLen);
849         hRes = RegQueryValueEx(currentKeyHandle,keyValue,NULL,&dwType,(LPBYTE)lpbData,&dwLen);
850     }
851
852     if (hRes == ERROR_SUCCESS)
853     {
854       /*
855        * Convert the returned data to a displayable format
856        */
857       switch ( dwType )
858       {
859         case REG_SZ:
860         case REG_EXPAND_SZ:
861         {
862           lpsRes = HeapAlloc( GetProcessHeap(), 0, dwLen);
863           strncpy(lpsRes, lpbData, dwLen);
864           lpsRes[dwLen-1]='\0';
865           break;
866         }
867         case REG_DWORD:
868         {
869           lpsRes = convertHexToDWORDStr(lpbData, dwLen);
870           break;
871         }
872         default:
873         {
874           lpsRes = convertHexToHexCSV(lpbData, dwLen);
875           break;
876         }
877       }
878     }
879
880     HeapFree(GetProcessHeap(), 0, lpbData);
881   }
882
883
884   if ( hRes == ERROR_SUCCESS )
885     printf(
886       "%s: Value \"%s\" = \"%s\" in key [%s]\n",
887       getAppName(),
888       keyValue,
889       lpsRes,
890       currentKeyName);
891
892   else
893     printf("%s: ERROR Value \"%s\" not found for key \"%s\".\n",
894       getAppName(),
895       keyValue,
896       currentKeyName);
897
898   /*
899    * Do some cleanup
900    */
901   for (counter=0; counter<argCounter; counter++)
902     if (argv[counter] != NULL)
903       HeapFree(GetProcessHeap(), 0, argv[counter]);
904
905   if (lpsRes != NULL)
906     HeapFree(GetProcessHeap(), 0, lpsRes);
907 #endif
908 }
909
910 /******************************************************************************
911  * Calls command for each line of a registry file.
912  * Correctly processes comments (in # form), line continuation.
913  *
914  * Parameters:
915  *   in - input stream to read from
916  *   command - command to be called for each line
917  */
918 void processRegLines(FILE *in, CommandAPI command)
919 {
920     LPSTR line           = NULL;  /* line read from input stream */
921     ULONG lineSize       = REG_VAL_BUF_SIZE;
922
923     line = HeapAlloc(GetProcessHeap(), 0, lineSize);
924     CHECK_ENOUGH_MEMORY(line);
925
926     while (!feof(in))
927     {
928         LPSTR s; /* The pointer into line for where the current fgets should read */
929         s = line;
930         for (;;)
931         {
932             size_t size_remaining;
933             int size_to_get;
934             char *s_eol; /* various local uses */
935
936             /* Do we need to expand the buffer ? */
937             assert (s >= line && s <= line + lineSize);
938             size_remaining = lineSize - (s-line);
939             if (size_remaining < 2) /* room for 1 character and the \0 */
940             {
941                 char *new_buffer;
942                 size_t new_size = lineSize + REG_VAL_BUF_SIZE;
943                 if (new_size > lineSize) /* no arithmetic overflow */
944                     new_buffer = HeapReAlloc (GetProcessHeap(), 0, line, new_size);
945                 else
946                     new_buffer = NULL;
947                 CHECK_ENOUGH_MEMORY(new_buffer);
948                 line = new_buffer;
949                 s = line + lineSize - size_remaining;
950                 lineSize = new_size;
951                 size_remaining = lineSize - (s-line);
952             }
953
954             /* Get as much as possible into the buffer, terminated either by
955              * eof, error, eol or getting the maximum amount.  Abort on error.
956              */
957             size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
958             if (NULL == fgets (s, size_to_get, in))
959             {
960                 if (ferror(in))
961                 {
962                     perror ("While reading input");
963                     exit (IO_ERROR);
964                 }
965                 else
966                 {
967                     assert (feof(in));
968                     *s = '\0';
969                     /* It is not clear to me from the definition that the
970                      * contents of the buffer are well defined on detecting
971                      * an eof without managing to read anything.
972                      */
973                 }
974             }
975
976             /* If we didn't read the eol nor the eof go around for the rest */
977             s_eol = strchr (s, '\n');
978             if (!feof (in) && !s_eol)
979             {
980                 s = strchr (s, '\0');
981                 /* It should be s + size_to_get - 1 but this is safer */
982                 continue;
983             }
984
985             /* If it is a comment line then discard it and go around again */
986             if (line [0] == '#')
987             {
988                 s = line;
989                 continue;
990             }
991
992             /* Remove any line feed.  Leave s_eol on the \0 */
993             if (s_eol)
994             {
995                 *s_eol = '\0';
996                 if (s_eol > line && *(s_eol-1) == '\r')
997                     *--s_eol = '\0';
998             }
999             else
1000                 s_eol = strchr (s, '\0');
1001
1002             /* If there is a concatenating \\ then go around again */
1003             if (s_eol > line && *(s_eol-1) == '\\')
1004             {
1005                 int c;
1006                 s = s_eol-1;
1007                 /* The following error protection could be made more self-
1008                  * correcting but I thought it not worth trying.
1009                  */
1010                 if ((c = fgetc (in)) == EOF || c != ' ' ||
1011                     (c = fgetc (in)) == EOF || c != ' ')
1012                     printf ("%s: ERROR - invalid continuation.\n", getAppName());
1013                 continue;
1014             }
1015
1016             break; /* That is the full virtual line */
1017         }
1018
1019         command(line);
1020     }
1021     command(NULL);
1022
1023     HeapFree(GetProcessHeap(), 0, line);
1024 }
1025
1026 /******************************************************************************
1027  * This funtion is the main entry point to the registerDLL action.  It
1028  * receives the currently read line, then loads and registers the requested DLLs
1029  */
1030 void doRegisterDLL(LPSTR stdInput) {
1031   HMODULE theLib = 0;
1032   UINT retVal = 0;
1033
1034   /* Check for valid input */
1035   if (stdInput == NULL)
1036     return;
1037
1038   /* Load and register the library, then free it */
1039   theLib = LoadLibrary(stdInput);
1040   if (theLib)
1041   {
1042     FARPROC lpfnDLLRegProc = GetProcAddress(theLib, "DllRegisterServer");
1043     if (lpfnDLLRegProc)
1044       retVal = (*lpfnDLLRegProc)();
1045     else
1046       printf("%s: Couldn't find DllRegisterServer proc in '%s'.\n",
1047              getAppName(), stdInput);
1048
1049     if (retVal != S_OK)
1050       printf("%s: DLLRegisterServer error 0x%x in '%s'.\n",
1051              getAppName(), retVal, stdInput);
1052
1053     FreeLibrary(theLib);
1054   }
1055   else
1056   {
1057     printf("%s: Could not load DLL '%s'.\n", getAppName(), stdInput);
1058   }
1059 }
1060
1061 /******************************************************************************
1062  * This funtion is the main entry point to the unregisterDLL action.  It
1063  * receives the currently read line, then loads and unregisters the requested DLLs
1064  */
1065 void doUnregisterDLL(LPSTR stdInput) {
1066   HMODULE theLib = 0;
1067   UINT retVal = 0;
1068
1069   /* Check for valid input */
1070   if (stdInput == NULL)
1071     return;
1072
1073   /* Load and unregister the library, then free it */
1074   theLib = LoadLibrary(stdInput);
1075   if (theLib)
1076   {
1077     FARPROC lpfnDLLRegProc = GetProcAddress(theLib, "DllUnregisterServer");
1078     if (lpfnDLLRegProc)
1079       retVal = (*lpfnDLLRegProc)();
1080     else
1081       printf("%s: Couldn't find DllUnregisterServer proc in '%s'.\n",
1082              getAppName(), stdInput);
1083
1084     if (retVal != S_OK)
1085       printf("%s: DLLUnregisterServer error 0x%x in '%s'.\n",
1086              getAppName(), retVal, stdInput);
1087
1088     FreeLibrary(theLib);
1089   }
1090   else
1091   {
1092     printf("%s: Could not load DLL '%s'.\n", getAppName(), stdInput);
1093   }
1094 }
1095
1096 /****************************************************************************
1097  * REGPROC_print_error
1098  *
1099  * Print the message for GetLastError
1100  */
1101
1102 void REGPROC_print_error() {
1103     LPVOID lpMsgBuf;
1104     DWORD error_code;
1105     int status;
1106
1107     error_code = GetLastError ();
1108     status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1109                            NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
1110     if (!status) {
1111         printf("%s: Cannot display message for error %ld, status %ld\n",
1112                getAppName(), error_code, GetLastError());
1113         exit(1);
1114     }
1115     puts(lpMsgBuf);
1116     LocalFree((HLOCAL)lpMsgBuf);
1117     exit(1);
1118 }
1119
1120 /******************************************************************************
1121  * Checks whether the buffer has enough room for the string or required size.
1122  * Resizes the buffer if necessary.
1123  *
1124  * Parameters:
1125  * buffer - pointer to a buffer for string
1126  * len - current length of the buffer in characters.
1127  * required_len - length of the string to place to the buffer in characters.
1128  *   The length does not include the terminating null character.
1129  */
1130 void REGPROC_resize_char_buffer(CHAR **buffer, DWORD *len, DWORD required_len)
1131 {
1132     required_len++;
1133     if (required_len > *len)
1134     {
1135         *len = required_len;
1136         *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer,
1137                               *len * sizeof(**buffer));
1138         CHECK_ENOUGH_MEMORY(*buffer);
1139     }
1140 }
1141
1142 /******************************************************************************
1143  * Prints string str to file
1144  */
1145 void REGPROC_export_string(FILE *file, CHAR *str)
1146 {
1147     size_t len = strlen(str);
1148     size_t i;
1149
1150     /* escaping characters */
1151     for (i = 0; i < len; i++)
1152     {
1153         CHAR c = str[i];
1154         switch (c)
1155         {
1156         case '\\':
1157             fputs("\\\\", file);
1158             break;
1159         case '\"':
1160             fputs("\\\"", file);
1161             break;
1162         case '\n':
1163             fputs("\\\n", file);
1164             break;
1165         default:
1166             fputc(c, file);
1167             break;
1168         }
1169     }
1170 }
1171
1172 /******************************************************************************
1173  * Writes contents of the registry key to the specified file stream.
1174  *
1175  * Parameters:
1176  * file - writable file stream to export registry branch to.
1177  * key - registry branch to export.
1178  * reg_key_name_buf - name of the key with registry class.
1179  *      Is resized if necessary.
1180  * reg_key_name_len - length of the buffer for the registry class in characters.
1181  * val_name_buf - buffer for storing value name.
1182  *      Is resized if necessary.
1183  * val_name_len - length of the buffer for storing value names in characters.
1184  * val_buf - buffer for storing values while extracting.
1185  *      Is resized if necessary.
1186  * val_size - size of the buffer for storing values in bytes.
1187  */
1188 void export_hkey(FILE *file, HKEY key,
1189                  CHAR **reg_key_name_buf, DWORD *reg_key_name_len,
1190                  CHAR **val_name_buf, DWORD *val_name_len,
1191                  BYTE **val_buf, DWORD *val_size)
1192 {
1193     DWORD max_sub_key_len;
1194     DWORD max_val_name_len;
1195     DWORD max_val_size;
1196     DWORD curr_len;
1197     DWORD i;
1198     BOOL more_data;
1199     LONG ret;
1200
1201     /* get size information and resize the buffers if necessary */
1202     if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL,
1203                         &max_sub_key_len, NULL,
1204                         NULL, &max_val_name_len, &max_val_size, NULL, NULL
1205         ) != ERROR_SUCCESS)
1206     {
1207         REGPROC_print_error();
1208     }
1209     curr_len = strlen(*reg_key_name_buf);
1210     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len,
1211                                max_sub_key_len + curr_len + 1);
1212     REGPROC_resize_char_buffer(val_name_buf, val_name_len,
1213                                max_val_name_len);
1214     if (max_val_size > *val_size)
1215     {
1216         *val_size = max_val_size;
1217         *val_buf = HeapReAlloc(GetProcessHeap(), 0, *val_buf, *val_size);
1218         CHECK_ENOUGH_MEMORY(val_buf);
1219     }
1220
1221     /* output data for the current key */
1222     fputs("\n[", file);
1223     fputs(*reg_key_name_buf, file);
1224     fputs("]\n", file);
1225     /* print all the values */
1226     i = 0;
1227     more_data = TRUE;
1228     while(more_data)
1229     {
1230         DWORD value_type;
1231         DWORD val_name_len1 = *val_name_len;
1232         DWORD val_size1 = *val_size;
1233         ret = RegEnumValue(key, i, *val_name_buf, &val_name_len1, NULL,
1234                            &value_type, *val_buf, &val_size1);
1235         if (ret != ERROR_SUCCESS)
1236         {
1237             more_data = FALSE;
1238             if (ret != ERROR_NO_MORE_ITEMS)
1239             {
1240                 REGPROC_print_error();
1241             }
1242         } else {
1243             i++;
1244
1245             if ((*val_name_buf)[0])
1246             {
1247                 fputs("\"", file);
1248                 REGPROC_export_string(file, *val_name_buf);
1249                 fputs("\"=", file);
1250             } else {
1251                 fputs("@=", file);
1252             }
1253
1254             switch (value_type)
1255             {
1256             case REG_SZ:
1257             case REG_EXPAND_SZ:
1258                 fputs("\"", file);
1259                 REGPROC_export_string(file, *val_buf);
1260                 fputs("\"\n", file);
1261                 break;
1262
1263             case REG_DWORD:
1264                 fprintf(file, "dword:%08lx\n", *((DWORD *)*val_buf));
1265                 break;
1266
1267             default:
1268                 printf("%s: warning - unsupported registry format '%ld', "
1269                        "treat as binary\n",
1270                        getAppName(), value_type);
1271                 printf("key name: \"%s\"\n", *reg_key_name_buf);
1272                 printf("value name:\"%s\"\n\n", *val_name_buf);
1273                 /* falls through */
1274             case REG_MULTI_SZ:
1275                 /* falls through */
1276             case REG_BINARY:
1277             {
1278                 DWORD i1;
1279                 CHAR *hex_prefix;
1280                 CHAR buf[20];
1281                 int cur_pos;
1282
1283                 if (value_type == REG_BINARY)
1284                 {
1285                     hex_prefix = "hex:";
1286                 } else {
1287                     hex_prefix = buf;
1288                     sprintf(buf, "hex(%ld):", value_type);
1289                 }
1290
1291                 /* position of where the next character will be printed */
1292                 /* NOTE: yes, strlen("hex:") is used even for hex(x): */
1293                 cur_pos = strlen("\"\"=") + strlen("hex:") +
1294                     strlen(*val_name_buf);
1295
1296                 fputs(hex_prefix, file);
1297                 for (i1 = 0; i1 < val_size1; i1++)
1298                 {
1299                     fprintf(file, "%02x", (unsigned int)(*val_buf)[i1]);
1300                     if (i1 + 1 < val_size1)
1301                     {
1302                         fputs(",", file);
1303                     }
1304                     cur_pos += 3;
1305
1306                     /* wrap the line */
1307                     if (cur_pos > REG_FILE_HEX_LINE_LEN)
1308                     {
1309                         fputs("\\\n  ", file);
1310                         cur_pos = 2;
1311                     }
1312                 }
1313                 fputs("\n", file);
1314                 break;
1315             }
1316             }
1317         }
1318     }
1319
1320     i = 0;
1321     more_data = TRUE;
1322     (*reg_key_name_buf)[curr_len] = '\\';
1323     while(more_data)
1324     {
1325         DWORD buf_len = *reg_key_name_len - curr_len;
1326
1327         ret = RegEnumKeyEx(key, i, *reg_key_name_buf + curr_len + 1, &buf_len,
1328                            NULL, NULL, NULL, NULL);
1329         if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)
1330         {
1331             more_data = FALSE;
1332             if (ret != ERROR_NO_MORE_ITEMS)
1333             {
1334                 REGPROC_print_error();
1335             }
1336         } else {
1337             HKEY subkey;
1338
1339             i++;
1340             if (RegOpenKey(key, *reg_key_name_buf + curr_len + 1,
1341                            &subkey) == ERROR_SUCCESS)
1342             {
1343                 export_hkey(file, subkey, reg_key_name_buf, reg_key_name_len,
1344                             val_name_buf, val_name_len, val_buf, val_size);
1345                 RegCloseKey(subkey);
1346             } else {
1347                 REGPROC_print_error();
1348             }
1349         }
1350     }
1351     (*reg_key_name_buf)[curr_len] = '\0';
1352 }
1353
1354 /******************************************************************************
1355  * Open file for export.
1356  */
1357 FILE *REGPROC_open_export_file(CHAR *file_name)
1358 {
1359     FILE *file = fopen(file_name, "w");
1360     if (!file)
1361     {
1362         perror("");
1363         printf("%s: Can't open file \"%s\"\n", getAppName(), file_name);
1364         exit(1);
1365     }
1366     fputs("REGEDIT4\n", file);
1367     return file;
1368 }
1369
1370 /******************************************************************************
1371  * Writes contents of the registry key to the specified file stream.
1372  *
1373  * Parameters:
1374  * file_name - name of a file to export registry branch to.
1375  * reg_key_name - registry branch to export. The whole registry is exported if
1376  *      reg_key_name is NULL or contains an empty string.
1377  */
1378 BOOL export_registry_key(CHAR *file_name, CHAR *reg_key_name)
1379 {
1380     HKEY reg_key_class;
1381
1382     CHAR *reg_key_name_buf;
1383     CHAR *val_name_buf;
1384     BYTE *val_buf;
1385     DWORD reg_key_name_len = KEY_MAX_LEN;
1386     DWORD val_name_len = KEY_MAX_LEN;
1387     DWORD val_size = REG_VAL_BUF_SIZE;
1388     FILE *file = NULL;
1389
1390     reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0,
1391                                  reg_key_name_len  * sizeof(*reg_key_name_buf));
1392     val_name_buf = HeapAlloc(GetProcessHeap(), 0,
1393                              val_name_len * sizeof(*val_name_buf));
1394     val_buf = HeapAlloc(GetProcessHeap(), 0, val_size);
1395     CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf);
1396
1397     if (reg_key_name && reg_key_name[0])
1398     {
1399         CHAR *branch_name;
1400         HKEY key;
1401
1402         REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_len,
1403                                    strlen(reg_key_name));
1404         strcpy(reg_key_name_buf, reg_key_name);
1405
1406         /* open the specified key */
1407         reg_key_class = getRegClass(reg_key_name);
1408         if (reg_key_class == (HKEY)ERROR_INVALID_PARAMETER)
1409         {
1410             printf("%s: Incorrect registry class specification in '%s'\n",
1411                    getAppName(), reg_key_name);
1412             exit(1);
1413         }
1414         branch_name = getRegKeyName(reg_key_name);
1415         CHECK_ENOUGH_MEMORY(branch_name);
1416         if (!branch_name[0])
1417         {
1418             /* no branch - registry class is specified */
1419             file = REGPROC_open_export_file(file_name);
1420             export_hkey(file, reg_key_class,
1421                         &reg_key_name_buf, &reg_key_name_len,
1422                         &val_name_buf, &val_name_len,
1423                         &val_buf, &val_size);
1424         }
1425         else if (RegOpenKey(reg_key_class, branch_name, &key) == ERROR_SUCCESS)
1426         {
1427             file = REGPROC_open_export_file(file_name);
1428             export_hkey(file, key,
1429                         &reg_key_name_buf, &reg_key_name_len,
1430                         &val_name_buf, &val_name_len,
1431                         &val_buf, &val_size);
1432             RegCloseKey(key);
1433         } else {
1434             printf("%s: Can't export. Registry key '%s' does not exist!\n",
1435                    getAppName(), reg_key_name);
1436             REGPROC_print_error();
1437         }
1438         HeapFree(GetProcessHeap(), 0, branch_name);
1439     } else {
1440         int i;
1441
1442         /* export all registry classes */
1443         file = REGPROC_open_export_file(file_name);
1444         for (i = 0; i < REG_CLASS_NUMBER; i++)
1445         {
1446             /* do not export HKEY_CLASSES_ROOT */
1447             if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
1448                 reg_class_keys[i] != HKEY_CURRENT_USER &&
1449                 reg_class_keys[i] != HKEY_CURRENT_CONFIG)
1450             {
1451                 strcpy(reg_key_name_buf, reg_class_names[i]);
1452                 export_hkey(file, reg_class_keys[i],
1453                             &reg_key_name_buf, &reg_key_name_len,
1454                             &val_name_buf, &val_name_len,
1455                             &val_buf, &val_size);
1456             }
1457         }
1458     }
1459
1460     if (file)
1461     {
1462         fclose(file);
1463     }
1464     HeapFree(GetProcessHeap(), 0, reg_key_name);
1465     HeapFree(GetProcessHeap(), 0, val_buf);
1466     return TRUE;
1467 }
1468
1469 /******************************************************************************
1470  * Reads contents of the specified file into the registry.
1471  */
1472 BOOL import_registry_file(LPTSTR filename)
1473 {
1474     FILE* reg_file = fopen(filename, "r");
1475
1476     if (reg_file) {
1477         processRegLines(reg_file, doSetValue);
1478         return TRUE;
1479     }
1480     return FALSE;
1481 }
1482
1483 /******************************************************************************
1484  * Recursive function which removes the registry key with all subkeys.
1485  */
1486 void delete_branch(HKEY key,
1487                    CHAR **reg_key_name_buf, DWORD *reg_key_name_len)
1488 {
1489     HKEY branch_key;
1490     DWORD max_sub_key_len;
1491     DWORD subkeys;
1492     DWORD curr_len;
1493     LONG ret;
1494     long int i;
1495
1496     if (RegOpenKey(key, *reg_key_name_buf, &branch_key) != ERROR_SUCCESS)
1497     {
1498         REGPROC_print_error();
1499     }
1500
1501     /* get size information and resize the buffers if necessary */
1502     if (RegQueryInfoKey(branch_key, NULL, NULL, NULL,
1503                         &subkeys, &max_sub_key_len,
1504                         NULL, NULL, NULL, NULL, NULL, NULL
1505         ) != ERROR_SUCCESS)
1506     {
1507         REGPROC_print_error();
1508     }
1509     curr_len = strlen(*reg_key_name_buf);
1510     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len,
1511                                max_sub_key_len + curr_len + 1);
1512
1513     (*reg_key_name_buf)[curr_len] = '\\';
1514     for (i = subkeys - 1; i >= 0; i--)
1515     {
1516         DWORD buf_len = *reg_key_name_len - curr_len;
1517
1518         ret = RegEnumKeyEx(branch_key, i, *reg_key_name_buf + curr_len + 1,
1519                            &buf_len, NULL, NULL, NULL, NULL);
1520         if (ret != ERROR_SUCCESS &&
1521             ret != ERROR_MORE_DATA &&
1522             ret != ERROR_NO_MORE_ITEMS)
1523         {
1524             REGPROC_print_error();
1525         } else {
1526             delete_branch(key, reg_key_name_buf, reg_key_name_len);
1527         }
1528     }
1529     (*reg_key_name_buf)[curr_len] = '\0';
1530     RegCloseKey(branch_key);
1531     RegDeleteKey(key, *reg_key_name_buf);
1532 }
1533
1534 /******************************************************************************
1535  * Removes the registry key with all subkeys. Parses full key name.
1536  *
1537  * Parameters:
1538  * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
1539  *      empty, points to register key class, does not exist.
1540  */
1541 void delete_registry_key(CHAR *reg_key_name)
1542 {
1543     CHAR *branch_name;
1544     DWORD branch_name_len;
1545     HKEY reg_key_class;
1546     HKEY branch_key;
1547
1548     if (!reg_key_name || !reg_key_name[0])
1549         return;
1550     /* open the specified key */
1551     reg_key_class = getRegClass(reg_key_name);
1552     if (reg_key_class == (HKEY)ERROR_INVALID_PARAMETER)
1553     {
1554         printf("%s: Incorrect registry class specification in '%s'\n",
1555                getAppName(), reg_key_name);
1556         exit(1);
1557     }
1558     branch_name = getRegKeyName(reg_key_name);
1559     CHECK_ENOUGH_MEMORY(branch_name);
1560     branch_name_len = strlen(branch_name);
1561     if (!branch_name[0])
1562     {
1563         printf("%s: Can't delete registry class '%s'\n",
1564                getAppName(), reg_key_name);
1565         exit(1);
1566     }
1567     if (RegOpenKey(reg_key_class, branch_name, &branch_key) == ERROR_SUCCESS)
1568     {
1569         /* check whether the key exists */
1570         RegCloseKey(branch_key);
1571         delete_branch(reg_key_class, &branch_name, &branch_name_len);
1572     }
1573     HeapFree(GetProcessHeap(), 0, branch_name);
1574 }
1575
1576 /******************************************************************************
1577  * Sets the application name. Then application name is used in the error
1578  * reporting.
1579  */
1580 void setAppName(CHAR *name)
1581 {
1582     app_name = name;
1583 }
1584
1585 CHAR *getAppName()
1586 {
1587     return app_name;
1588 }