regedit: Merge getRegClass() and getRegKeyName() to form parseKeyName().
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 /* maximal number of characters in hexadecimal data line,
34    not including '\' character */
35 #define REG_FILE_HEX_LINE_LEN   76
36
37 static const CHAR *reg_class_names[] = {
38                                      "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
39                                      "HKEY_CURRENT_CONFIG", "HKEY_CURRENT_USER", "HKEY_DYN_DATA"
40                                  };
41
42 #define REG_CLASS_NUMBER (sizeof(reg_class_names) / sizeof(reg_class_names[0]))
43
44 static HKEY reg_class_keys[REG_CLASS_NUMBER] = {
45             HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
46             HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
47         };
48
49 /* return values */
50 #define NOT_ENOUGH_MEMORY     1
51 #define IO_ERROR              2
52
53 /* processing macros */
54
55 /* common check of memory allocation results */
56 #define CHECK_ENOUGH_MEMORY(p) \
57 if (!(p)) \
58 { \
59     fprintf(stderr,"%s: file %s, line %d: Not enough memory\n", \
60             getAppName(), __FILE__, __LINE__); \
61     exit(NOT_ENOUGH_MEMORY); \
62 }
63
64 /******************************************************************************
65  * Converts a hex representation of a DWORD into a DWORD.
66  */
67 static BOOL convertHexToDWord(char* str, DWORD *dw)
68 {
69     char dummy;
70     if (strlen(str) > 8 || sscanf(str, "%x%c", dw, &dummy) != 1) {
71         fprintf(stderr,"%s: ERROR, invalid hex value\n", getAppName());
72         return FALSE;
73     }
74     return TRUE;
75 }
76
77 /******************************************************************************
78  * Converts a hex comma separated values list into a binary string.
79  */
80 static BYTE* convertHexCSVToHex(char *str, DWORD *size)
81 {
82     char *s;
83     BYTE *d, *data;
84
85     /* The worst case is 1 digit + 1 comma per byte */
86     *size=(strlen(str)+1)/2;
87     data=HeapAlloc(GetProcessHeap(), 0, *size);
88     CHECK_ENOUGH_MEMORY(data);
89
90     s = str;
91     d = data;
92     *size=0;
93     while (*s != '\0') {
94         UINT wc;
95         char dummy;
96
97         if (s[1] != ',' && s[1] != '\0' && s[2] != ',' && s[2] != '\0') {
98             fprintf(stderr,"%s: ERROR converting CSV hex stream. Invalid sequence at '%s'\n",
99                     getAppName(), s);
100             HeapFree(GetProcessHeap(), 0, data);
101             return NULL;
102         }
103         if (sscanf(s, "%x%c", &wc, &dummy) < 1 || dummy != ',') {
104             fprintf(stderr,"%s: ERROR converting CSV hex stream. Invalid value at '%s'\n",
105                     getAppName(), s);
106             HeapFree(GetProcessHeap(), 0, data);
107             return NULL;
108         }
109         *d++ =(BYTE)wc;
110         (*size)++;
111
112         s+=(wc < 0x10 ? 2 : 3);
113     }
114
115     return data;
116 }
117
118 /******************************************************************************
119  * This function returns the HKEY associated with the data type encoded in the
120  * value.  It modifies the input parameter (key value) in order to skip this
121  * "now useless" data type information.
122  *
123  * Note: Updated based on the algorithm used in 'server/registry.c'
124  */
125 static DWORD getDataType(LPSTR *lpValue, DWORD* parse_type)
126 {
127     struct data_type { const char *tag; int len; int type; int parse_type; };
128
129     static const struct data_type data_types[] = {                   /* actual type */  /* type to assume for parsing */
130                 { "\"",        1,   REG_SZ,              REG_SZ },
131                 { "str:\"",    5,   REG_SZ,              REG_SZ },
132                 { "str(2):\"", 8,   REG_EXPAND_SZ,       REG_SZ },
133                 { "hex:",      4,   REG_BINARY,          REG_BINARY },
134                 { "dword:",    6,   REG_DWORD,           REG_DWORD },
135                 { "hex(",      4,   -1,                  REG_BINARY },
136                 { NULL,        0,    0,                  0 }
137             };
138
139     const struct data_type *ptr;
140     int type;
141
142     for (ptr = data_types; ptr->tag; ptr++) {
143         if (memcmp( ptr->tag, *lpValue, ptr->len ))
144             continue;
145
146         /* Found! */
147         *parse_type = ptr->parse_type;
148         type=ptr->type;
149         *lpValue+=ptr->len;
150         if (type == -1) {
151             char* end;
152             /* "hex(xx):" is special */
153             type = (int)strtoul( *lpValue , &end, 16 );
154             if (**lpValue=='\0' || *end!=')' || *(end+1)!=':') {
155                 type=REG_NONE;
156             } else {
157                 *lpValue=end+2;
158             }
159         }
160         return type;
161     }
162     *parse_type=REG_NONE;
163     return REG_NONE;
164 }
165
166 /******************************************************************************
167  * Replaces escape sequences with the characters.
168  */
169 static void REGPROC_unescape_string(LPSTR str)
170 {
171     int str_idx = 0;            /* current character under analysis */
172     int val_idx = 0;            /* the last character of the unescaped string */
173     int len = strlen(str);
174     for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
175         if (str[str_idx] == '\\') {
176             str_idx++;
177             switch (str[str_idx]) {
178             case 'n':
179                 str[val_idx] = '\n';
180                 break;
181             case '\\':
182             case '"':
183                 str[val_idx] = str[str_idx];
184                 break;
185             default:
186                 fprintf(stderr,"Warning! Unrecognized escape sequence: \\%c'\n",
187                         str[str_idx]);
188                 str[val_idx] = str[str_idx];
189                 break;
190             }
191         } else {
192             str[val_idx] = str[str_idx];
193         }
194     }
195     str[val_idx] = '\0';
196 }
197
198 /******************************************************************************
199  * Parses HKEY_SOME_ROOT\some\key\path to get the root key handle and
200  * extract the key path (what comes after the first '\').
201  */
202 static BOOL parseKeyName(LPSTR lpKeyName, HKEY *hKey, LPSTR *lpKeyPath)
203 {
204     LPSTR lpSlash;
205     unsigned int i, len;
206
207     if (lpKeyName == NULL)
208         return FALSE;
209
210     lpSlash = strchr(lpKeyName, '\\');
211     if (lpSlash)
212     {
213         len = lpSlash-lpKeyName;
214     }
215     else
216     {
217         len = strlen(lpKeyName);
218         lpSlash = lpKeyName+len;
219     }
220     *hKey = NULL;
221     for (i = 0; i < REG_CLASS_NUMBER; i++) {
222         if (strncmp(lpKeyName, reg_class_names[i], len) == 0 &&
223             len == strlen(reg_class_names[i])) {
224             *hKey = reg_class_keys[i];
225             break;
226         }
227     }
228     if (*hKey == NULL)
229         return FALSE;
230
231     if (*lpSlash != '\0')
232         lpSlash++;
233     *lpKeyPath = lpSlash;
234     return TRUE;
235 }
236
237 /* Globals used by the setValue() & co */
238 static LPSTR currentKeyName;
239 static HKEY  currentKeyHandle = NULL;
240
241 /******************************************************************************
242  * Sets the value with name val_name to the data in val_data for the currently
243  * opened key.
244  *
245  * Parameters:
246  * val_name - name of the registry value
247  * val_data - registry value data
248  */
249 static LONG setValue(LPSTR val_name, LPSTR val_data)
250 {
251     LONG res;
252     DWORD  dwDataType, dwParseType;
253     LPBYTE lpbData;
254     DWORD  dwData, dwLen;
255
256     if ( (val_name == NULL) || (val_data == NULL) )
257         return ERROR_INVALID_PARAMETER;
258
259     if (strcmp(val_data, "-") == 0)
260     {
261         res=RegDeleteValue(currentKeyHandle,val_name);
262         return (res == ERROR_FILE_NOT_FOUND ? ERROR_SUCCESS : res);
263     }
264
265     /* Get the data type stored into the value field */
266     dwDataType = getDataType(&val_data, &dwParseType);
267
268     if (dwParseType == REG_SZ)          /* no conversion for string */
269     {
270         REGPROC_unescape_string(val_data);
271         /* Compute dwLen after REGPROC_unescape_string because it may
272          * have changed the string length and we don't want to store
273          * the extra garbage in the registry.
274          */
275         dwLen = strlen(val_data);
276         if (dwLen>0 && val_data[dwLen-1]=='"')
277         {
278             dwLen--;
279             val_data[dwLen]='\0';
280         }
281         lpbData = (BYTE*) val_data;
282     }
283     else if (dwParseType == REG_DWORD)  /* Convert the dword types */
284     {
285         if (!convertHexToDWord(val_data, &dwData))
286             return ERROR_INVALID_DATA;
287         lpbData = (BYTE*)&dwData;
288         dwLen = sizeof(dwData);
289     }
290     else if (dwParseType == REG_BINARY) /* Convert the binary data */
291     {
292         lpbData = convertHexCSVToHex(val_data, &dwLen);
293         if (!lpbData)
294             return ERROR_INVALID_DATA;
295     }
296     else                                /* unknown format */
297     {
298         fprintf(stderr,"%s: ERROR, unknown data format\n", getAppName());
299         return ERROR_INVALID_DATA;
300     }
301
302     res = RegSetValueEx(
303                currentKeyHandle,
304                val_name,
305                0,                  /* Reserved */
306                dwDataType,
307                lpbData,
308                dwLen);
309     if (dwParseType == REG_BINARY)
310         HeapFree(GetProcessHeap(), 0, lpbData);
311     return res;
312 }
313
314 /******************************************************************************
315  * A helper function for processRegEntry() that opens the current key.
316  * That key must be closed by calling closeKey().
317  */
318 static LONG openKey(LPSTR stdInput)
319 {
320     HKEY keyClass;
321     LPSTR keyPath;
322     DWORD dwDisp;
323     LONG res;
324
325     /* Sanity checks */
326     if (stdInput == NULL)
327         return ERROR_INVALID_PARAMETER;
328
329     /* Get the registry class */
330     if (!parseKeyName(stdInput, &keyClass, &keyPath))
331         return ERROR_INVALID_PARAMETER;
332
333     res = RegCreateKeyEx(
334                keyClass,                 /* Class     */
335                keyPath,                  /* Sub Key   */
336                0,                        /* MUST BE 0 */
337                NULL,                     /* object type */
338                REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
339                KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
340                NULL,                     /* security attribute */
341                &currentKeyHandle,        /* result */
342                &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
343                                                         REG_OPENED_EXISTING_KEY */
344
345     if (res == ERROR_SUCCESS)
346     {
347         currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(stdInput)+1);
348         CHECK_ENOUGH_MEMORY(currentKeyName);
349         strcpy(currentKeyName, stdInput);
350     }
351     else
352     {
353         currentKeyHandle = NULL;
354     }
355
356     return res;
357
358 }
359
360 /******************************************************************************
361  * Close the currently opened key.
362  */
363 static void closeKey(void)
364 {
365     if (currentKeyHandle)
366     {
367         HeapFree(GetProcessHeap(), 0, currentKeyName);
368         RegCloseKey(currentKeyHandle);
369         currentKeyHandle = NULL;
370     }
371 }
372
373 /******************************************************************************
374  * This function is a wrapper for the setValue function.  It prepares the
375  * land and clean the area once completed.
376  * Note: this function modifies the line parameter.
377  *
378  * line - registry file unwrapped line. Should have the registry value name and
379  *      complete registry value data.
380  */
381 static void processSetValue(LPSTR line)
382 {
383     LPSTR val_name;                   /* registry value name   */
384     LPSTR val_data;                   /* registry value data   */
385
386     int line_idx = 0;                 /* current character under analysis */
387     LONG res;
388
389     /* get value name */
390     if (line[line_idx] == '@' && line[line_idx + 1] == '=') {
391         line[line_idx] = '\0';
392         val_name = line;
393         line_idx++;
394     } else if (line[line_idx] == '\"') {
395         line_idx++;
396         val_name = line + line_idx;
397         while (TRUE) {
398             if (line[line_idx] == '\\')   /* skip escaped character */
399             {
400                 line_idx += 2;
401             } else {
402                 if (line[line_idx] == '\"') {
403                     line[line_idx] = '\0';
404                     line_idx++;
405                     break;
406                 } else {
407                     line_idx++;
408                 }
409             }
410         }
411         if (line[line_idx] != '=') {
412             line[line_idx] = '\"';
413             fprintf(stderr,"Warning! unrecognized line:\n%s\n", line);
414             return;
415         }
416
417     } else {
418         fprintf(stderr,"Warning! unrecognized line:\n%s\n", line);
419         return;
420     }
421     line_idx++;                   /* skip the '=' character */
422     val_data = line + line_idx;
423
424     REGPROC_unescape_string(val_name);
425     res = setValue(val_name, val_data);
426     if ( res != ERROR_SUCCESS )
427         fprintf(stderr,"%s: ERROR Key %s not created. Value: %s, Data: %s\n",
428                 getAppName(),
429                 currentKeyName,
430                 val_name,
431                 val_data);
432 }
433
434 /******************************************************************************
435  * This function receives the currently read entry and performs the
436  * corresponding action.
437  */
438 static void processRegEntry(LPSTR stdInput)
439 {
440     /*
441      * We encountered the end of the file, make sure we
442      * close the opened key and exit
443      */
444     if (stdInput == NULL) {
445         closeKey();
446         return;
447     }
448
449     if      ( stdInput[0] == '[')      /* We are reading a new key */
450     {
451         LPSTR keyEnd;
452         closeKey();                    /* Close the previous key */
453
454         /* Get rid of the square brackets */
455         stdInput++;
456         keyEnd = strrchr(stdInput, ']');
457         if (keyEnd)
458             *keyEnd='\0';
459
460         /* delete the key if we encounter '-' at the start of reg key */
461         if ( stdInput[0] == '-')
462             delete_registry_key(stdInput+1);
463         else if ( openKey(stdInput) != ERROR_SUCCESS )
464             fprintf(stderr,"%s: setValue failed to open key %s\n",
465                     getAppName(), stdInput);
466     } else if( currentKeyHandle &&
467                (( stdInput[0] == '@') || /* reading a default @=data pair */
468                 ( stdInput[0] == '\"'))) /* reading a new value=data pair */
469     {
470         processSetValue(stdInput);
471     } else
472     {
473         /* Since we are assuming that the file format is valid we must be
474          * reading a blank line which indicates the end of this key processing
475          */
476         closeKey();
477     }
478 }
479
480 /******************************************************************************
481  * Processes a registry file.
482  * Correctly processes comments (in # form), line continuation.
483  *
484  * Parameters:
485  *   in - input stream to read from
486  */
487 void processRegLines(FILE *in)
488 {
489     LPSTR line           = NULL;  /* line read from input stream */
490     ULONG lineSize       = REG_VAL_BUF_SIZE;
491
492     line = HeapAlloc(GetProcessHeap(), 0, lineSize);
493     CHECK_ENOUGH_MEMORY(line);
494
495     while (!feof(in)) {
496         LPSTR s; /* The pointer into line for where the current fgets should read */
497         s = line;
498         for (;;) {
499             size_t size_remaining;
500             int size_to_get;
501             char *s_eol; /* various local uses */
502
503             /* Do we need to expand the buffer ? */
504             assert (s >= line && s <= line + lineSize);
505             size_remaining = lineSize - (s-line);
506             if (size_remaining < 2) /* room for 1 character and the \0 */
507             {
508                 char *new_buffer;
509                 size_t new_size = lineSize + REG_VAL_BUF_SIZE;
510                 if (new_size > lineSize) /* no arithmetic overflow */
511                     new_buffer = HeapReAlloc (GetProcessHeap(), 0, line, new_size);
512                 else
513                     new_buffer = NULL;
514                 CHECK_ENOUGH_MEMORY(new_buffer);
515                 line = new_buffer;
516                 s = line + lineSize - size_remaining;
517                 lineSize = new_size;
518                 size_remaining = lineSize - (s-line);
519             }
520
521             /* Get as much as possible into the buffer, terminated either by
522              * eof, error, eol or getting the maximum amount.  Abort on error.
523              */
524             size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
525             if (NULL == fgets (s, size_to_get, in)) {
526                 if (ferror(in)) {
527                     perror ("While reading input");
528                     exit (IO_ERROR);
529                 } else {
530                     assert (feof(in));
531                     *s = '\0';
532                     /* It is not clear to me from the definition that the
533                      * contents of the buffer are well defined on detecting
534                      * an eof without managing to read anything.
535                      */
536                 }
537             }
538
539             /* If we didn't read the eol nor the eof go around for the rest */
540             s_eol = strchr (s, '\n');
541             if (!feof (in) && !s_eol) {
542                 s = strchr (s, '\0');
543                 /* It should be s + size_to_get - 1 but this is safer */
544                 continue;
545             }
546
547             /* If it is a comment line then discard it and go around again */
548             if (line [0] == '#') {
549                 s = line;
550                 continue;
551             }
552
553             /* Remove any line feed.  Leave s_eol on the \0 */
554             if (s_eol) {
555                 *s_eol = '\0';
556                 if (s_eol > line && *(s_eol-1) == '\r')
557                     *--s_eol = '\0';
558             } else
559                 s_eol = strchr (s, '\0');
560
561             /* If there is a concatenating \\ then go around again */
562             if (s_eol > line && *(s_eol-1) == '\\') {
563                 int c;
564                 s = s_eol-1;
565                 /* The following error protection could be made more self-
566                  * correcting but I thought it not worth trying.
567                  */
568                 if ((c = fgetc (in)) == EOF || c != ' ' ||
569                         (c = fgetc (in)) == EOF || c != ' ')
570                     fprintf(stderr,"%s: ERROR - invalid continuation.\n",
571                             getAppName());
572                 continue;
573             }
574
575             break; /* That is the full virtual line */
576         }
577
578         processRegEntry(line);
579     }
580     processRegEntry(NULL);
581
582     HeapFree(GetProcessHeap(), 0, line);
583 }
584
585 /****************************************************************************
586  * REGPROC_print_error
587  *
588  * Print the message for GetLastError
589  */
590
591 static void REGPROC_print_error(void)
592 {
593     LPVOID lpMsgBuf;
594     DWORD error_code;
595     int status;
596
597     error_code = GetLastError ();
598     status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
599                            NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
600     if (!status) {
601         fprintf(stderr,"%s: Cannot display message for error %d, status %d\n",
602                 getAppName(), error_code, GetLastError());
603         exit(1);
604     }
605     puts(lpMsgBuf);
606     LocalFree((HLOCAL)lpMsgBuf);
607     exit(1);
608 }
609
610 /******************************************************************************
611  * Checks whether the buffer has enough room for the string or required size.
612  * Resizes the buffer if necessary.
613  *
614  * Parameters:
615  * buffer - pointer to a buffer for string
616  * len - current length of the buffer in characters.
617  * required_len - length of the string to place to the buffer in characters.
618  *   The length does not include the terminating null character.
619  */
620 static void REGPROC_resize_char_buffer(CHAR **buffer, DWORD *len, DWORD required_len)
621 {
622     required_len++;
623     if (required_len > *len) {
624         *len = required_len;
625         if (!*buffer)
626             *buffer = HeapAlloc(GetProcessHeap(), 0, *len * sizeof(**buffer));
627         else
628             *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *len * sizeof(**buffer));
629         CHECK_ENOUGH_MEMORY(*buffer);
630     }
631 }
632
633 /******************************************************************************
634  * Prints string str to file
635  */
636 static void REGPROC_export_string(FILE *file, CHAR *str)
637 {
638     size_t len = strlen(str);
639     size_t i;
640
641     /* escaping characters */
642     for (i = 0; i < len; i++) {
643         CHAR c = str[i];
644         switch (c) {
645         case '\\':
646             fputs("\\\\", file);
647             break;
648         case '\"':
649             fputs("\\\"", file);
650             break;
651         case '\n':
652             fputs("\\\n", file);
653             break;
654         default:
655             fputc(c, file);
656             break;
657         }
658     }
659 }
660
661 /******************************************************************************
662  * Writes contents of the registry key to the specified file stream.
663  *
664  * Parameters:
665  * file - writable file stream to export registry branch to.
666  * key - registry branch to export.
667  * reg_key_name_buf - name of the key with registry class.
668  *      Is resized if necessary.
669  * reg_key_name_len - length of the buffer for the registry class in characters.
670  * val_name_buf - buffer for storing value name.
671  *      Is resized if necessary.
672  * val_name_len - length of the buffer for storing value names in characters.
673  * val_buf - buffer for storing values while extracting.
674  *      Is resized if necessary.
675  * val_size - size of the buffer for storing values in bytes.
676  */
677 static void export_hkey(FILE *file, HKEY key,
678                  CHAR **reg_key_name_buf, DWORD *reg_key_name_len,
679                  CHAR **val_name_buf, DWORD *val_name_len,
680                  BYTE **val_buf, DWORD *val_size)
681 {
682     DWORD max_sub_key_len;
683     DWORD max_val_name_len;
684     DWORD max_val_size;
685     DWORD curr_len;
686     DWORD i;
687     BOOL more_data;
688     LONG ret;
689
690     /* get size information and resize the buffers if necessary */
691     if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL,
692                         &max_sub_key_len, NULL,
693                         NULL, &max_val_name_len, &max_val_size, NULL, NULL
694                        ) != ERROR_SUCCESS) {
695         REGPROC_print_error();
696     }
697     curr_len = strlen(*reg_key_name_buf);
698     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len,
699                                max_sub_key_len + curr_len + 1);
700     REGPROC_resize_char_buffer(val_name_buf, val_name_len,
701                                max_val_name_len);
702     if (max_val_size > *val_size) {
703         *val_size = max_val_size;
704         if (!*val_buf) *val_buf = HeapAlloc(GetProcessHeap(), 0, *val_size);
705         else *val_buf = HeapReAlloc(GetProcessHeap(), 0, *val_buf, *val_size);
706         CHECK_ENOUGH_MEMORY(val_buf);
707     }
708
709     /* output data for the current key */
710     fputs("\n[", file);
711     fputs(*reg_key_name_buf, file);
712     fputs("]\n", file);
713     /* print all the values */
714     i = 0;
715     more_data = TRUE;
716     while(more_data) {
717         DWORD value_type;
718         DWORD val_name_len1 = *val_name_len;
719         DWORD val_size1 = *val_size;
720         ret = RegEnumValue(key, i, *val_name_buf, &val_name_len1, NULL,
721                            &value_type, *val_buf, &val_size1);
722         if (ret != ERROR_SUCCESS) {
723             more_data = FALSE;
724             if (ret != ERROR_NO_MORE_ITEMS) {
725                 REGPROC_print_error();
726             }
727         } else {
728             i++;
729
730             if ((*val_name_buf)[0]) {
731                 fputs("\"", file);
732                 REGPROC_export_string(file, *val_name_buf);
733                 fputs("\"=", file);
734             } else {
735                 fputs("@=", file);
736             }
737
738             switch (value_type) {
739             case REG_SZ:
740             case REG_EXPAND_SZ:
741                 fputs("\"", file);
742                 REGPROC_export_string(file, (char*) *val_buf);
743                 fputs("\"\n", file);
744                 break;
745
746             case REG_DWORD:
747                 fprintf(file, "dword:%08x\n", *((DWORD *)*val_buf));
748                 break;
749
750             default:
751                 fprintf(stderr,"%s: warning - unsupported registry format '%d', "
752                         "treat as binary\n",
753                         getAppName(), value_type);
754                 fprintf(stderr,"key name: \"%s\"\n", *reg_key_name_buf);
755                 fprintf(stderr,"value name:\"%s\"\n\n", *val_name_buf);
756                 /* falls through */
757             case REG_MULTI_SZ:
758                 /* falls through */
759             case REG_BINARY: {
760                     DWORD i1;
761                     const CHAR *hex_prefix;
762                     CHAR buf[20];
763                     int cur_pos;
764
765                     if (value_type == REG_BINARY) {
766                         hex_prefix = "hex:";
767                     } else {
768                         hex_prefix = buf;
769                         sprintf(buf, "hex(%d):", value_type);
770                     }
771
772                     /* position of where the next character will be printed */
773                     /* NOTE: yes, strlen("hex:") is used even for hex(x): */
774                     cur_pos = strlen("\"\"=") + strlen("hex:") +
775                               strlen(*val_name_buf);
776
777                     fputs(hex_prefix, file);
778                     for (i1 = 0; i1 < val_size1; i1++) {
779                         fprintf(file, "%02x", (unsigned int)(*val_buf)[i1]);
780                         if (i1 + 1 < val_size1) {
781                             fputs(",", file);
782                         }
783                         cur_pos += 3;
784
785                         /* wrap the line */
786                         if (cur_pos > REG_FILE_HEX_LINE_LEN) {
787                             fputs("\\\n  ", file);
788                             cur_pos = 2;
789                         }
790                     }
791                     fputs("\n", file);
792                     break;
793                 }
794             }
795         }
796     }
797
798     i = 0;
799     more_data = TRUE;
800     (*reg_key_name_buf)[curr_len] = '\\';
801     while(more_data) {
802         DWORD buf_len = *reg_key_name_len - curr_len;
803
804         ret = RegEnumKeyEx(key, i, *reg_key_name_buf + curr_len + 1, &buf_len,
805                            NULL, NULL, NULL, NULL);
806         if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
807             more_data = FALSE;
808             if (ret != ERROR_NO_MORE_ITEMS) {
809                 REGPROC_print_error();
810             }
811         } else {
812             HKEY subkey;
813
814             i++;
815             if (RegOpenKey(key, *reg_key_name_buf + curr_len + 1,
816                            &subkey) == ERROR_SUCCESS) {
817                 export_hkey(file, subkey, reg_key_name_buf, reg_key_name_len,
818                             val_name_buf, val_name_len, val_buf, val_size);
819                 RegCloseKey(subkey);
820             } else {
821                 REGPROC_print_error();
822             }
823         }
824     }
825     (*reg_key_name_buf)[curr_len] = '\0';
826 }
827
828 /******************************************************************************
829  * Open file for export.
830  */
831 static FILE *REGPROC_open_export_file(CHAR *file_name)
832 {
833     FILE *file = fopen(file_name, "w");
834     if (!file) {
835         perror("");
836         fprintf(stderr,"%s: Can't open file \"%s\"\n", getAppName(), file_name);
837         exit(1);
838     }
839     fputs("REGEDIT4\n", file);
840     return file;
841 }
842
843 /******************************************************************************
844  * Writes contents of the registry key to the specified file stream.
845  *
846  * Parameters:
847  * file_name - name of a file to export registry branch to.
848  * reg_key_name - registry branch to export. The whole registry is exported if
849  *      reg_key_name is NULL or contains an empty string.
850  */
851 BOOL export_registry_key(CHAR *file_name, CHAR *reg_key_name)
852 {
853     CHAR *reg_key_name_buf;
854     CHAR *val_name_buf;
855     BYTE *val_buf;
856     DWORD reg_key_name_len = KEY_MAX_LEN;
857     DWORD val_name_len = KEY_MAX_LEN;
858     DWORD val_size = REG_VAL_BUF_SIZE;
859     FILE *file = NULL;
860
861     reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0,
862                                  reg_key_name_len  * sizeof(*reg_key_name_buf));
863     val_name_buf = HeapAlloc(GetProcessHeap(), 0,
864                              val_name_len * sizeof(*val_name_buf));
865     val_buf = HeapAlloc(GetProcessHeap(), 0, val_size);
866     CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf);
867
868     if (reg_key_name && reg_key_name[0]) {
869         HKEY reg_key_class;
870         CHAR *branch_name;
871         HKEY key;
872
873         REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_len,
874                                    strlen(reg_key_name));
875         strcpy(reg_key_name_buf, reg_key_name);
876
877         /* open the specified key */
878         if (!parseKeyName(reg_key_name, &reg_key_class, &branch_name)) {
879             fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
880                     getAppName(), reg_key_name);
881             exit(1);
882         }
883         if (!branch_name[0]) {
884             /* no branch - registry class is specified */
885             file = REGPROC_open_export_file(file_name);
886             export_hkey(file, reg_key_class,
887                         &reg_key_name_buf, &reg_key_name_len,
888                         &val_name_buf, &val_name_len,
889                         &val_buf, &val_size);
890         } else if (RegOpenKey(reg_key_class, branch_name, &key) == ERROR_SUCCESS) {
891             file = REGPROC_open_export_file(file_name);
892             export_hkey(file, key,
893                         &reg_key_name_buf, &reg_key_name_len,
894                         &val_name_buf, &val_name_len,
895                         &val_buf, &val_size);
896             RegCloseKey(key);
897         } else {
898             fprintf(stderr,"%s: Can't export. Registry key '%s' does not exist!\n",
899                     getAppName(), reg_key_name);
900             REGPROC_print_error();
901         }
902     } else {
903         unsigned int i;
904
905         /* export all registry classes */
906         file = REGPROC_open_export_file(file_name);
907         for (i = 0; i < REG_CLASS_NUMBER; i++) {
908             /* do not export HKEY_CLASSES_ROOT */
909             if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
910                     reg_class_keys[i] != HKEY_CURRENT_USER &&
911                     reg_class_keys[i] != HKEY_CURRENT_CONFIG &&
912                     reg_class_keys[i] != HKEY_DYN_DATA) {
913                 strcpy(reg_key_name_buf, reg_class_names[i]);
914                 export_hkey(file, reg_class_keys[i],
915                             &reg_key_name_buf, &reg_key_name_len,
916                             &val_name_buf, &val_name_len,
917                             &val_buf, &val_size);
918             }
919         }
920     }
921
922     if (file) {
923         fclose(file);
924     }
925     HeapFree(GetProcessHeap(), 0, reg_key_name);
926     HeapFree(GetProcessHeap(), 0, val_buf);
927     return TRUE;
928 }
929
930 /******************************************************************************
931  * Reads contents of the specified file into the registry.
932  */
933 BOOL import_registry_file(LPTSTR filename)
934 {
935     FILE* reg_file = fopen(filename, "r");
936
937     if (reg_file) {
938         processRegLines(reg_file);
939         return TRUE;
940     }
941     return FALSE;
942 }
943
944 /******************************************************************************
945  * Recursive function which removes the registry key with all subkeys.
946  */
947 static void delete_branch(HKEY key,
948                    CHAR **reg_key_name_buf, DWORD *reg_key_name_len)
949 {
950     HKEY branch_key;
951     DWORD max_sub_key_len;
952     DWORD subkeys;
953     DWORD curr_len;
954     LONG ret;
955     long int i;
956
957     if (RegOpenKey(key, *reg_key_name_buf, &branch_key) != ERROR_SUCCESS) {
958         REGPROC_print_error();
959     }
960
961     /* get size information and resize the buffers if necessary */
962     if (RegQueryInfoKey(branch_key, NULL, NULL, NULL,
963                         &subkeys, &max_sub_key_len,
964                         NULL, NULL, NULL, NULL, NULL, NULL
965                        ) != ERROR_SUCCESS) {
966         REGPROC_print_error();
967     }
968     curr_len = strlen(*reg_key_name_buf);
969     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len,
970                                max_sub_key_len + curr_len + 1);
971
972     (*reg_key_name_buf)[curr_len] = '\\';
973     for (i = subkeys - 1; i >= 0; i--) {
974         DWORD buf_len = *reg_key_name_len - curr_len;
975
976         ret = RegEnumKeyEx(branch_key, i, *reg_key_name_buf + curr_len + 1,
977                            &buf_len, NULL, NULL, NULL, NULL);
978         if (ret != ERROR_SUCCESS &&
979                 ret != ERROR_MORE_DATA &&
980                 ret != ERROR_NO_MORE_ITEMS) {
981             REGPROC_print_error();
982         } else {
983             delete_branch(key, reg_key_name_buf, reg_key_name_len);
984         }
985     }
986     (*reg_key_name_buf)[curr_len] = '\0';
987     RegCloseKey(branch_key);
988     RegDeleteKey(key, *reg_key_name_buf);
989 }
990
991 /******************************************************************************
992  * Removes the registry key with all subkeys. Parses full key name.
993  *
994  * Parameters:
995  * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
996  *      empty, points to register key class, does not exist.
997  */
998 void delete_registry_key(CHAR *reg_key_name)
999 {
1000     CHAR *key_name;
1001     HKEY key_class;
1002     HKEY branch_key;
1003
1004     if (!reg_key_name || !reg_key_name[0])
1005         return;
1006
1007     if (!parseKeyName(reg_key_name, &key_class, &key_name)) {
1008         fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
1009                 getAppName(), reg_key_name);
1010         exit(1);
1011     }
1012     if (!*key_name) {
1013         fprintf(stderr,"%s: Can't delete registry class '%s'\n",
1014                 getAppName(), reg_key_name);
1015         exit(1);
1016     }
1017
1018     /* open the specified key to make sure it exists */
1019     if (RegOpenKey(key_class, key_name, &branch_key) == ERROR_SUCCESS) {
1020         CHAR *branch_name;
1021         DWORD branch_name_len;
1022         RegCloseKey(branch_key);
1023
1024         /* Copy the key name to a new buffer that delete_branch() can
1025          * reallocate as needed
1026          */
1027         branch_name_len = strlen(key_name);
1028         branch_name = HeapAlloc(GetProcessHeap(), 0, branch_name_len+1);
1029         CHECK_ENOUGH_MEMORY(branch_name);
1030         strcpy(branch_name, key_name);
1031         delete_branch(key_class, &branch_name, &branch_name_len);
1032         HeapFree(GetProcessHeap(), 0, branch_name);
1033     }
1034 }