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