regedit: Fix the Italian translation.
[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         dwLen++;  /* include terminating null */
278     }
279     else if (dwParseType == REG_DWORD)  /* Convert the dword types */
280     {
281         if (!convertHexToDWord(val_data, &dwData))
282             return ERROR_INVALID_DATA;
283         lpbData = (BYTE*)&dwData;
284         dwLen = sizeof(dwData);
285     }
286     else if (dwParseType == REG_BINARY) /* Convert the binary data */
287     {
288         lpbData = convertHexCSVToHex(val_data, &dwLen);
289         if (!lpbData)
290             return ERROR_INVALID_DATA;
291     }
292     else                                /* unknown format */
293     {
294         fprintf(stderr,"%s: ERROR, unknown data format\n", getAppName());
295         return ERROR_INVALID_DATA;
296     }
297
298     res = RegSetValueEx(
299                currentKeyHandle,
300                val_name,
301                0,                  /* Reserved */
302                dwDataType,
303                lpbData,
304                dwLen);
305     if (dwParseType == REG_BINARY)
306         HeapFree(GetProcessHeap(), 0, lpbData);
307     return res;
308 }
309
310 /******************************************************************************
311  * A helper function for processRegEntry() that opens the current key.
312  * That key must be closed by calling closeKey().
313  */
314 static LONG openKey(LPSTR stdInput)
315 {
316     HKEY keyClass;
317     LPSTR keyPath;
318     DWORD dwDisp;
319     LONG res;
320
321     /* Sanity checks */
322     if (stdInput == NULL)
323         return ERROR_INVALID_PARAMETER;
324
325     /* Get the registry class */
326     if (!parseKeyName(stdInput, &keyClass, &keyPath))
327         return ERROR_INVALID_PARAMETER;
328
329     res = RegCreateKeyEx(
330                keyClass,                 /* Class     */
331                keyPath,                  /* Sub Key   */
332                0,                        /* MUST BE 0 */
333                NULL,                     /* object type */
334                REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
335                KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
336                NULL,                     /* security attribute */
337                &currentKeyHandle,        /* result */
338                &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
339                                                         REG_OPENED_EXISTING_KEY */
340
341     if (res == ERROR_SUCCESS)
342     {
343         currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(stdInput)+1);
344         CHECK_ENOUGH_MEMORY(currentKeyName);
345         strcpy(currentKeyName, stdInput);
346     }
347     else
348     {
349         currentKeyHandle = NULL;
350     }
351
352     return res;
353
354 }
355
356 /******************************************************************************
357  * Close the currently opened key.
358  */
359 static void closeKey(void)
360 {
361     if (currentKeyHandle)
362     {
363         HeapFree(GetProcessHeap(), 0, currentKeyName);
364         RegCloseKey(currentKeyHandle);
365         currentKeyHandle = NULL;
366     }
367 }
368
369 /******************************************************************************
370  * This function is a wrapper for the setValue function.  It prepares the
371  * land and cleans the area once completed.
372  * Note: this function modifies the line parameter.
373  *
374  * line - registry file unwrapped line. Should have the registry value name and
375  *      complete registry value data.
376  */
377 static void processSetValue(LPSTR line)
378 {
379     LPSTR val_name;                   /* registry value name   */
380     LPSTR val_data;                   /* registry value data   */
381
382     int line_idx = 0;                 /* current character under analysis */
383     LONG res;
384
385     /* get value name */
386     if (line[line_idx] == '@' && line[line_idx + 1] == '=') {
387         line[line_idx] = '\0';
388         val_name = line;
389         line_idx++;
390     } else if (line[line_idx] == '\"') {
391         line_idx++;
392         val_name = line + line_idx;
393         while (TRUE) {
394             if (line[line_idx] == '\\')   /* skip escaped character */
395             {
396                 line_idx += 2;
397             } else {
398                 if (line[line_idx] == '\"') {
399                     line[line_idx] = '\0';
400                     line_idx++;
401                     break;
402                 } else {
403                     line_idx++;
404                 }
405             }
406         }
407         if (line[line_idx] != '=') {
408             line[line_idx] = '\"';
409             fprintf(stderr,"Warning! unrecognized line:\n%s\n", line);
410             return;
411         }
412
413     } else {
414         fprintf(stderr,"Warning! unrecognized line:\n%s\n", line);
415         return;
416     }
417     line_idx++;                   /* skip the '=' character */
418     val_data = line + line_idx;
419
420     REGPROC_unescape_string(val_name);
421     res = setValue(val_name, val_data);
422     if ( res != ERROR_SUCCESS )
423         fprintf(stderr,"%s: ERROR Key %s not created. Value: %s, Data: %s\n",
424                 getAppName(),
425                 currentKeyName,
426                 val_name,
427                 val_data);
428 }
429
430 /******************************************************************************
431  * This function receives the currently read entry and performs the
432  * corresponding action.
433  */
434 static void processRegEntry(LPSTR stdInput)
435 {
436     /*
437      * We encountered the end of the file, make sure we
438      * close the opened key and exit
439      */
440     if (stdInput == NULL) {
441         closeKey();
442         return;
443     }
444
445     if      ( stdInput[0] == '[')      /* We are reading a new key */
446     {
447         LPSTR keyEnd;
448         closeKey();                    /* Close the previous key */
449
450         /* Get rid of the square brackets */
451         stdInput++;
452         keyEnd = strrchr(stdInput, ']');
453         if (keyEnd)
454             *keyEnd='\0';
455
456         /* delete the key if we encounter '-' at the start of reg key */
457         if ( stdInput[0] == '-')
458             delete_registry_key(stdInput+1);
459         else if ( openKey(stdInput) != ERROR_SUCCESS )
460             fprintf(stderr,"%s: setValue failed to open key %s\n",
461                     getAppName(), stdInput);
462     } else if( currentKeyHandle &&
463                (( stdInput[0] == '@') || /* reading a default @=data pair */
464                 ( stdInput[0] == '\"'))) /* reading a new value=data pair */
465     {
466         processSetValue(stdInput);
467     } else
468     {
469         /* Since we are assuming that the file format is valid we must be
470          * reading a blank line which indicates the end of this key processing
471          */
472         closeKey();
473     }
474 }
475
476 /******************************************************************************
477  * Processes a registry file.
478  * Correctly processes comments (in # form), line continuation.
479  *
480  * Parameters:
481  *   in - input stream to read from
482  */
483 void processRegLines(FILE *in)
484 {
485     LPSTR line           = NULL;  /* line read from input stream */
486     ULONG lineSize       = REG_VAL_BUF_SIZE;
487     BOOL  unicode_check  = TRUE;
488
489     line = HeapAlloc(GetProcessHeap(), 0, lineSize);
490     CHECK_ENOUGH_MEMORY(line);
491
492     while (!feof(in)) {
493         LPSTR s; /* The pointer into line for where the current fgets should read */
494         LPSTR check;
495         s = line;
496         for (;;) {
497             size_t size_remaining;
498             int size_to_get;
499             char *s_eol; /* various local uses */
500
501             /* Do we need to expand the buffer ? */
502             assert (s >= line && s <= line + lineSize);
503             size_remaining = lineSize - (s-line);
504             if (size_remaining < 2) /* room for 1 character and the \0 */
505             {
506                 char *new_buffer;
507                 size_t new_size = lineSize + REG_VAL_BUF_SIZE;
508                 if (new_size > lineSize) /* no arithmetic overflow */
509                     new_buffer = HeapReAlloc (GetProcessHeap(), 0, line, new_size);
510                 else
511                     new_buffer = NULL;
512                 CHECK_ENOUGH_MEMORY(new_buffer);
513                 line = new_buffer;
514                 s = line + lineSize - size_remaining;
515                 lineSize = new_size;
516                 size_remaining = lineSize - (s-line);
517             }
518
519             /* Get as much as possible into the buffer, terminated either by
520              * eof, error, eol or getting the maximum amount.  Abort on error.
521              */
522             size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
523
524             if (unicode_check)
525             {
526                 if (fread( s, 2, 1, in) == 1)
527                 {
528                     if ((BYTE)s[0] == 0xff && (BYTE)s[1] == 0xfe)
529                     {
530                         printf("Trying to import from a unicode file: this isn't supported yet.\n"
531                                "Please use export as Win 9x/NT4 files from native regedit\n");
532                         HeapFree(GetProcessHeap(), 0, line);
533                         return;
534                     }
535                     else
536                     {
537                         unicode_check = FALSE;
538                         check = fgets (&s[2], size_to_get-2, in);
539                     }
540                 }
541                 else
542                 {
543                     unicode_check = FALSE;
544                     check = NULL;
545                 }
546             }
547             else
548                 check = fgets (s, size_to_get, in);
549
550             if (check == NULL) {
551                 if (ferror(in)) {
552                     perror ("While reading input");
553                     exit (IO_ERROR);
554                 } else {
555                     assert (feof(in));
556                     *s = '\0';
557                     /* It is not clear to me from the definition that the
558                      * contents of the buffer are well defined on detecting
559                      * an eof without managing to read anything.
560                      */
561                 }
562             }
563
564             /* If we didn't read the eol nor the eof go around for the rest */
565             s_eol = strchr (s, '\n');
566             if (!feof (in) && !s_eol) {
567                 s = strchr (s, '\0');
568                 /* It should be s + size_to_get - 1 but this is safer */
569                 continue;
570             }
571
572             /* If it is a comment line then discard it and go around again */
573             if (line [0] == '#') {
574                 s = line;
575                 continue;
576             }
577
578             /* Remove any line feed.  Leave s_eol on the \0 */
579             if (s_eol) {
580                 *s_eol = '\0';
581                 if (s_eol > line && *(s_eol-1) == '\r')
582                     *--s_eol = '\0';
583             } else
584                 s_eol = strchr (s, '\0');
585
586             /* If there is a concatenating \\ then go around again */
587             if (s_eol > line && *(s_eol-1) == '\\') {
588                 int c;
589                 s = s_eol-1;
590                 /* The following error protection could be made more self-
591                  * correcting but I thought it not worth trying.
592                  */
593                 if ((c = fgetc (in)) == EOF || c != ' ' ||
594                         (c = fgetc (in)) == EOF || c != ' ')
595                     fprintf(stderr,"%s: ERROR - invalid continuation.\n",
596                             getAppName());
597                 continue;
598             }
599
600             break; /* That is the full virtual line */
601         }
602
603         processRegEntry(line);
604     }
605     processRegEntry(NULL);
606
607     HeapFree(GetProcessHeap(), 0, line);
608 }
609
610 /****************************************************************************
611  * REGPROC_print_error
612  *
613  * Print the message for GetLastError
614  */
615
616 static void REGPROC_print_error(void)
617 {
618     LPVOID lpMsgBuf;
619     DWORD error_code;
620     int status;
621
622     error_code = GetLastError ();
623     status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
624                            NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
625     if (!status) {
626         fprintf(stderr,"%s: Cannot display message for error %d, status %d\n",
627                 getAppName(), error_code, GetLastError());
628         exit(1);
629     }
630     puts(lpMsgBuf);
631     LocalFree((HLOCAL)lpMsgBuf);
632     exit(1);
633 }
634
635 /******************************************************************************
636  * Checks whether the buffer has enough room for the string or required size.
637  * Resizes the buffer if necessary.
638  *
639  * Parameters:
640  * buffer - pointer to a buffer for string
641  * len - current length of the buffer in characters.
642  * required_len - length of the string to place to the buffer in characters.
643  *   The length does not include the terminating null character.
644  */
645 static void REGPROC_resize_char_buffer(CHAR **buffer, DWORD *len, DWORD required_len)
646 {
647     required_len++;
648     if (required_len > *len) {
649         *len = required_len;
650         if (!*buffer)
651             *buffer = HeapAlloc(GetProcessHeap(), 0, *len * sizeof(**buffer));
652         else
653             *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *len * sizeof(**buffer));
654         CHECK_ENOUGH_MEMORY(*buffer);
655     }
656 }
657
658 /******************************************************************************
659  * Prints string str to file
660  */
661 static void REGPROC_export_string(FILE *file, CHAR *str)
662 {
663     size_t len = strlen(str);
664     size_t i;
665
666     /* escaping characters */
667     for (i = 0; i < len; i++) {
668         CHAR c = str[i];
669         switch (c) {
670         case '\\':
671             fputs("\\\\", file);
672             break;
673         case '\"':
674             fputs("\\\"", file);
675             break;
676         case '\n':
677             fputs("\\\n", file);
678             break;
679         default:
680             fputc(c, file);
681             break;
682         }
683     }
684 }
685
686 /******************************************************************************
687  * Writes contents of the registry key to the specified file stream.
688  *
689  * Parameters:
690  * file - writable file stream to export registry branch to.
691  * key - registry branch to export.
692  * reg_key_name_buf - name of the key with registry class.
693  *      Is resized if necessary.
694  * reg_key_name_len - length of the buffer for the registry class in characters.
695  * val_name_buf - buffer for storing value name.
696  *      Is resized if necessary.
697  * val_name_len - length of the buffer for storing value names in characters.
698  * val_buf - buffer for storing values while extracting.
699  *      Is resized if necessary.
700  * val_size - size of the buffer for storing values in bytes.
701  */
702 static void export_hkey(FILE *file, HKEY key,
703                  CHAR **reg_key_name_buf, DWORD *reg_key_name_len,
704                  CHAR **val_name_buf, DWORD *val_name_len,
705                  BYTE **val_buf, DWORD *val_size)
706 {
707     DWORD max_sub_key_len;
708     DWORD max_val_name_len;
709     DWORD max_val_size;
710     DWORD curr_len;
711     DWORD i;
712     BOOL more_data;
713     LONG ret;
714
715     /* get size information and resize the buffers if necessary */
716     if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL,
717                         &max_sub_key_len, NULL,
718                         NULL, &max_val_name_len, &max_val_size, NULL, NULL
719                        ) != ERROR_SUCCESS) {
720         REGPROC_print_error();
721     }
722     curr_len = strlen(*reg_key_name_buf);
723     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len,
724                                max_sub_key_len + curr_len + 1);
725     REGPROC_resize_char_buffer(val_name_buf, val_name_len,
726                                max_val_name_len);
727     if (max_val_size > *val_size) {
728         *val_size = max_val_size;
729         if (!*val_buf) *val_buf = HeapAlloc(GetProcessHeap(), 0, *val_size);
730         else *val_buf = HeapReAlloc(GetProcessHeap(), 0, *val_buf, *val_size);
731         CHECK_ENOUGH_MEMORY(val_buf);
732     }
733
734     /* output data for the current key */
735     fputs("\n[", file);
736     fputs(*reg_key_name_buf, file);
737     fputs("]\n", file);
738     /* print all the values */
739     i = 0;
740     more_data = TRUE;
741     while(more_data) {
742         DWORD value_type;
743         DWORD val_name_len1 = *val_name_len;
744         DWORD val_size1 = *val_size;
745         ret = RegEnumValue(key, i, *val_name_buf, &val_name_len1, NULL,
746                            &value_type, *val_buf, &val_size1);
747         if (ret != ERROR_SUCCESS) {
748             more_data = FALSE;
749             if (ret != ERROR_NO_MORE_ITEMS) {
750                 REGPROC_print_error();
751             }
752         } else {
753             i++;
754
755             if ((*val_name_buf)[0]) {
756                 fputs("\"", file);
757                 REGPROC_export_string(file, *val_name_buf);
758                 fputs("\"=", file);
759             } else {
760                 fputs("@=", file);
761             }
762
763             switch (value_type) {
764             case REG_SZ:
765             case REG_EXPAND_SZ:
766                 fputs("\"", file);
767                 if (val_size1) REGPROC_export_string(file, (char*) *val_buf);
768                 fputs("\"\n", file);
769                 break;
770
771             case REG_DWORD:
772                 fprintf(file, "dword:%08x\n", *((DWORD *)*val_buf));
773                 break;
774
775             default:
776                 fprintf(stderr,"%s: warning - unsupported registry format '%d', "
777                         "treat as binary\n",
778                         getAppName(), value_type);
779                 fprintf(stderr,"key name: \"%s\"\n", *reg_key_name_buf);
780                 fprintf(stderr,"value name:\"%s\"\n\n", *val_name_buf);
781                 /* falls through */
782             case REG_MULTI_SZ:
783                 /* falls through */
784             case REG_BINARY: {
785                     DWORD i1;
786                     const CHAR *hex_prefix;
787                     CHAR buf[20];
788                     int cur_pos;
789
790                     if (value_type == REG_BINARY) {
791                         hex_prefix = "hex:";
792                     } else {
793                         hex_prefix = buf;
794                         sprintf(buf, "hex(%d):", value_type);
795                     }
796
797                     /* position of where the next character will be printed */
798                     /* NOTE: yes, strlen("hex:") is used even for hex(x): */
799                     cur_pos = strlen("\"\"=") + strlen("hex:") +
800                               strlen(*val_name_buf);
801
802                     fputs(hex_prefix, file);
803                     for (i1 = 0; i1 < val_size1; i1++) {
804                         fprintf(file, "%02x", (unsigned int)(*val_buf)[i1]);
805                         if (i1 + 1 < val_size1) {
806                             fputs(",", file);
807                         }
808                         cur_pos += 3;
809
810                         /* wrap the line */
811                         if (cur_pos > REG_FILE_HEX_LINE_LEN) {
812                             fputs("\\\n  ", file);
813                             cur_pos = 2;
814                         }
815                     }
816                     fputs("\n", file);
817                     break;
818                 }
819             }
820         }
821     }
822
823     i = 0;
824     more_data = TRUE;
825     (*reg_key_name_buf)[curr_len] = '\\';
826     while(more_data) {
827         DWORD buf_len = *reg_key_name_len - curr_len;
828
829         ret = RegEnumKeyEx(key, i, *reg_key_name_buf + curr_len + 1, &buf_len,
830                            NULL, NULL, NULL, NULL);
831         if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
832             more_data = FALSE;
833             if (ret != ERROR_NO_MORE_ITEMS) {
834                 REGPROC_print_error();
835             }
836         } else {
837             HKEY subkey;
838
839             i++;
840             if (RegOpenKey(key, *reg_key_name_buf + curr_len + 1,
841                            &subkey) == ERROR_SUCCESS) {
842                 export_hkey(file, subkey, reg_key_name_buf, reg_key_name_len,
843                             val_name_buf, val_name_len, val_buf, val_size);
844                 RegCloseKey(subkey);
845             } else {
846                 REGPROC_print_error();
847             }
848         }
849     }
850     (*reg_key_name_buf)[curr_len] = '\0';
851 }
852
853 /******************************************************************************
854  * Open file for export.
855  */
856 static FILE *REGPROC_open_export_file(CHAR *file_name)
857 {
858     FILE *file;
859
860     if (strcmp(file_name,"-")==0)
861         file=stdout;
862     else
863     {
864         file = fopen(file_name, "w");
865         if (!file) {
866             perror("");
867             fprintf(stderr,"%s: Can't open file \"%s\"\n", getAppName(), file_name);
868             exit(1);
869         }
870     }
871     fputs("REGEDIT4\n", file);
872     return file;
873 }
874
875 /******************************************************************************
876  * Writes contents of the registry key to the specified file stream.
877  *
878  * Parameters:
879  * file_name - name of a file to export registry branch to.
880  * reg_key_name - registry branch to export. The whole registry is exported if
881  *      reg_key_name is NULL or contains an empty string.
882  */
883 BOOL export_registry_key(CHAR *file_name, CHAR *reg_key_name)
884 {
885     CHAR *reg_key_name_buf;
886     CHAR *val_name_buf;
887     BYTE *val_buf;
888     DWORD reg_key_name_len = KEY_MAX_LEN;
889     DWORD val_name_len = KEY_MAX_LEN;
890     DWORD val_size = REG_VAL_BUF_SIZE;
891     FILE *file = NULL;
892
893     reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0,
894                                  reg_key_name_len  * sizeof(*reg_key_name_buf));
895     val_name_buf = HeapAlloc(GetProcessHeap(), 0,
896                              val_name_len * sizeof(*val_name_buf));
897     val_buf = HeapAlloc(GetProcessHeap(), 0, val_size);
898     CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf);
899
900     if (reg_key_name && reg_key_name[0]) {
901         HKEY reg_key_class;
902         CHAR *branch_name;
903         HKEY key;
904
905         REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_len,
906                                    strlen(reg_key_name));
907         strcpy(reg_key_name_buf, reg_key_name);
908
909         /* open the specified key */
910         if (!parseKeyName(reg_key_name, &reg_key_class, &branch_name)) {
911             fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
912                     getAppName(), reg_key_name);
913             exit(1);
914         }
915         if (!branch_name[0]) {
916             /* no branch - registry class is specified */
917             file = REGPROC_open_export_file(file_name);
918             export_hkey(file, reg_key_class,
919                         &reg_key_name_buf, &reg_key_name_len,
920                         &val_name_buf, &val_name_len,
921                         &val_buf, &val_size);
922         } else if (RegOpenKey(reg_key_class, branch_name, &key) == ERROR_SUCCESS) {
923             file = REGPROC_open_export_file(file_name);
924             export_hkey(file, key,
925                         &reg_key_name_buf, &reg_key_name_len,
926                         &val_name_buf, &val_name_len,
927                         &val_buf, &val_size);
928             RegCloseKey(key);
929         } else {
930             fprintf(stderr,"%s: Can't export. Registry key '%s' does not exist!\n",
931                     getAppName(), reg_key_name);
932             REGPROC_print_error();
933         }
934     } else {
935         unsigned int i;
936
937         /* export all registry classes */
938         file = REGPROC_open_export_file(file_name);
939         for (i = 0; i < REG_CLASS_NUMBER; i++) {
940             /* do not export HKEY_CLASSES_ROOT */
941             if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
942                     reg_class_keys[i] != HKEY_CURRENT_USER &&
943                     reg_class_keys[i] != HKEY_CURRENT_CONFIG &&
944                     reg_class_keys[i] != HKEY_DYN_DATA) {
945                 strcpy(reg_key_name_buf, reg_class_names[i]);
946                 export_hkey(file, reg_class_keys[i],
947                             &reg_key_name_buf, &reg_key_name_len,
948                             &val_name_buf, &val_name_len,
949                             &val_buf, &val_size);
950             }
951         }
952     }
953
954     if (file) {
955         fclose(file);
956     }
957     HeapFree(GetProcessHeap(), 0, reg_key_name);
958     HeapFree(GetProcessHeap(), 0, val_buf);
959     return TRUE;
960 }
961
962 /******************************************************************************
963  * Reads contents of the specified file into the registry.
964  */
965 BOOL import_registry_file(LPTSTR filename)
966 {
967     FILE* reg_file = fopen(filename, "r");
968
969     if (reg_file) {
970         processRegLines(reg_file);
971         return TRUE;
972     }
973     return FALSE;
974 }
975
976 /******************************************************************************
977  * Removes the registry key with all subkeys. Parses full key name.
978  *
979  * Parameters:
980  * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
981  *      empty, points to register key class, does not exist.
982  */
983 void delete_registry_key(CHAR *reg_key_name)
984 {
985     CHAR *key_name;
986     HKEY key_class;
987
988     if (!reg_key_name || !reg_key_name[0])
989         return;
990
991     if (!parseKeyName(reg_key_name, &key_class, &key_name)) {
992         fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
993                 getAppName(), reg_key_name);
994         exit(1);
995     }
996     if (!*key_name) {
997         fprintf(stderr,"%s: Can't delete registry class '%s'\n",
998                 getAppName(), reg_key_name);
999         exit(1);
1000     }
1001
1002     RegDeleteTreeA(key_class, key_name);
1003 }