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