xref: /reactos/base/applications/regedit/regproc.c (revision e8c7597b)
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  * Copyright 2008 Alexander N. Sørnes <alex@thehandofagony.com>
8  * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
9  */
10 
11 #ifdef __REACTOS__
12 #include <fcntl.h>
13 #include <io.h>
14 #include "regedit.h"
15 #else
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <io.h>
19 #include <windows.h>
20 #include <commctrl.h>
21 
22 #include "main.h"
23 #endif
24 
25 #define REG_VAL_BUF_SIZE        4096
26 
27 static HKEY reg_class_keys[] = {
28             HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
29             HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
30         };
31 
32 /******************************************************************************
33  * Allocates memory and converts input from multibyte to wide chars
34  * Returned string must be freed by the caller
35  */
36 static WCHAR* GetWideString(const char* strA)
37 {
38     if(strA)
39     {
40         WCHAR* strW;
41         int len = MultiByteToWideChar(CP_ACP, 0, strA, -1, NULL, 0);
42 
43         strW = malloc(len * sizeof(WCHAR));
44         MultiByteToWideChar(CP_ACP, 0, strA, -1, strW, len);
45         return strW;
46     }
47     return NULL;
48 }
49 
50 /******************************************************************************
51  * Allocates memory and converts input from multibyte to wide chars
52  * Returned string must be freed by the caller
53  */
54 static WCHAR* GetWideStringN(const char* strA, int chars, DWORD *len)
55 {
56     if(strA)
57     {
58         WCHAR* strW;
59         *len = MultiByteToWideChar(CP_ACP, 0, strA, chars, NULL, 0);
60 
61         strW = malloc(*len * sizeof(WCHAR));
62         MultiByteToWideChar(CP_ACP, 0, strA, chars, strW, *len);
63         return strW;
64     }
65     *len = 0;
66     return NULL;
67 }
68 
69 /******************************************************************************
70  * Allocates memory and converts input from wide chars to multibyte
71  * Returned string must be freed by the caller
72  */
73 char* GetMultiByteString(const WCHAR* strW)
74 {
75     if(strW)
76     {
77         char* strA;
78         int len = WideCharToMultiByte(CP_ACP, 0, strW, -1, NULL, 0, NULL, NULL);
79 
80         strA = malloc(len);
81         WideCharToMultiByte(CP_ACP, 0, strW, -1, strA, len, NULL, NULL);
82         return strA;
83     }
84     return NULL;
85 }
86 
87 /******************************************************************************
88  * Allocates memory and converts input from wide chars to multibyte
89  * Returned string must be freed by the caller
90  */
91 static char* GetMultiByteStringN(const WCHAR* strW, int chars, DWORD* len)
92 {
93     if(strW)
94     {
95         char* strA;
96         *len = WideCharToMultiByte(CP_ACP, 0, strW, chars, NULL, 0, NULL, NULL);
97 
98         strA = malloc(*len);
99         WideCharToMultiByte(CP_ACP, 0, strW, chars, strA, *len, NULL, NULL);
100         return strA;
101     }
102     *len = 0;
103     return NULL;
104 }
105 
106 static WCHAR *(*get_line)(FILE *);
107 
108 /* parser definitions */
109 enum parser_state
110 {
111     HEADER,              /* parsing the registry file version header */
112     PARSE_WIN31_LINE,    /* parsing a Windows 3.1 registry line */
113     LINE_START,          /* at the beginning of a registry line */
114     KEY_NAME,            /* parsing a key name */
115     DELETE_KEY,          /* deleting a registry key */
116     DEFAULT_VALUE_NAME,  /* parsing a default value name */
117     QUOTED_VALUE_NAME,   /* parsing a double-quoted value name */
118     DATA_START,          /* preparing for data parsing operations */
119     DELETE_VALUE,        /* deleting a registry value */
120     DATA_TYPE,           /* parsing the registry data type */
121     STRING_DATA,         /* parsing REG_SZ data */
122     DWORD_DATA,          /* parsing DWORD data */
123     HEX_DATA,            /* parsing REG_BINARY, REG_NONE, REG_EXPAND_SZ or REG_MULTI_SZ data */
124     EOL_BACKSLASH,       /* preparing to parse multiple lines of hex data */
125     HEX_MULTILINE,       /* parsing multiple lines of hex data */
126     UNKNOWN_DATA,        /* parsing an unhandled or invalid data type */
127     SET_VALUE,           /* adding a value to the registry */
128     NB_PARSER_STATES
129 };
130 
131 struct parser
132 {
133     FILE              *file;           /* pointer to a registry file */
134     WCHAR              two_wchars[2];  /* first two characters from the encoding check */
135     BOOL               is_unicode;     /* parsing Unicode or ASCII data */
136     short int          reg_version;    /* registry file version */
137     HKEY               hkey;           /* current registry key */
138     WCHAR             *key_name;       /* current key name */
139     WCHAR             *value_name;     /* value name */
140     DWORD              parse_type;     /* generic data type for parsing */
141     DWORD              data_type;      /* data type */
142     void              *data;           /* value data */
143     DWORD              data_size;      /* size of the data (in bytes) */
144     BOOL               backslash;      /* TRUE if the current line contains a backslash */
145     enum parser_state  state;          /* current parser state */
146 };
147 
148 typedef WCHAR *(*parser_state_func)(struct parser *parser, WCHAR *pos);
149 
150 /* parser state machine functions */
151 static WCHAR *header_state(struct parser *parser, WCHAR *pos);
152 static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos);
153 static WCHAR *line_start_state(struct parser *parser, WCHAR *pos);
154 static WCHAR *key_name_state(struct parser *parser, WCHAR *pos);
155 static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos);
156 static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos);
157 static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos);
158 static WCHAR *data_start_state(struct parser *parser, WCHAR *pos);
159 static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos);
160 static WCHAR *data_type_state(struct parser *parser, WCHAR *pos);
161 static WCHAR *string_data_state(struct parser *parser, WCHAR *pos);
162 static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos);
163 static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos);
164 static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos);
165 static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos);
166 static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos);
167 static WCHAR *set_value_state(struct parser *parser, WCHAR *pos);
168 
169 static const parser_state_func parser_funcs[NB_PARSER_STATES] =
170 {
171     header_state,              /* HEADER */
172     parse_win31_line_state,    /* PARSE_WIN31_LINE */
173     line_start_state,          /* LINE_START */
174     key_name_state,            /* KEY_NAME */
175     delete_key_state,          /* DELETE_KEY */
176     default_value_name_state,  /* DEFAULT_VALUE_NAME */
177     quoted_value_name_state,   /* QUOTED_VALUE_NAME */
178     data_start_state,          /* DATA_START */
179     delete_value_state,        /* DELETE_VALUE */
180     data_type_state,           /* DATA_TYPE */
181     string_data_state,         /* STRING_DATA */
182     dword_data_state,          /* DWORD_DATA */
183     hex_data_state,            /* HEX_DATA */
184     eol_backslash_state,       /* EOL_BACKSLASH */
185     hex_multiline_state,       /* HEX_MULTILINE */
186     unknown_data_state,        /* UNKNOWN_DATA */
187     set_value_state,           /* SET_VALUE */
188 };
189 
190 /* set the new parser state and return the previous one */
191 static inline enum parser_state set_state(struct parser *parser, enum parser_state state)
192 {
193     enum parser_state ret = parser->state;
194     parser->state = state;
195     return ret;
196 }
197 
198 /******************************************************************************
199  * Converts a hex representation of a DWORD into a DWORD.
200  */
201 static BOOL convert_hex_to_dword(WCHAR *str, DWORD *dw)
202 {
203     WCHAR *p, *end;
204     int count = 0;
205 
206     while (*str == ' ' || *str == '\t') str++;
207     if (!*str) goto error;
208 
209     p = str;
210     while (iswxdigit(*p))
211     {
212         count++;
213         p++;
214     }
215     if (count > 8) goto error;
216 
217     end = p;
218     while (*p == ' ' || *p == '\t') p++;
219     if (*p && *p != ';') goto error;
220 
221     *end = 0;
222     *dw = wcstoul(str, &end, 16);
223     return TRUE;
224 
225 error:
226     return FALSE;
227 }
228 
229 /******************************************************************************
230  * Converts comma-separated hex data into a binary string and modifies
231  * the input parameter to skip the concatenating backslash, if found.
232  *
233  * Returns TRUE or FALSE to indicate whether parsing was successful.
234  */
235 static BOOL convert_hex_csv_to_hex(struct parser *parser, WCHAR **str)
236 {
237     size_t size;
238     BYTE *d;
239     WCHAR *s;
240 
241     parser->backslash = FALSE;
242 
243     /* The worst case is 1 digit + 1 comma per byte */
244     size = ((lstrlenW(*str) + 1) / 2) + parser->data_size;
245     parser->data = realloc(parser->data, size);
246 
247     s = *str;
248     d = (BYTE *)parser->data + parser->data_size;
249 
250     while (*s)
251     {
252         WCHAR *end;
253         unsigned long wc;
254 
255         wc = wcstoul(s, &end, 16);
256         if (wc > 0xff) return FALSE;
257 
258         if (s == end && wc == 0)
259         {
260             while (*end == ' ' || *end == '\t') end++;
261             if (*end == '\\')
262             {
263                 parser->backslash = TRUE;
264                 *str = end + 1;
265                 return TRUE;
266             }
267             else if (*end == ';')
268                 return TRUE;
269             return FALSE;
270         }
271 
272         *d++ = wc;
273         parser->data_size++;
274 
275         if (*end && *end != ',')
276         {
277             while (*end == ' ' || *end == '\t') end++;
278             if (*end && *end != ';') return FALSE;
279             return TRUE;
280         }
281 
282         if (*end) end++;
283         s = end;
284     }
285 
286     return TRUE;
287 }
288 
289 /******************************************************************************
290  * Parses the data type of the registry value being imported and modifies
291  * the input parameter to skip the string representation of the data type.
292  *
293  * Returns TRUE or FALSE to indicate whether a data type was found.
294  */
295 static BOOL parse_data_type(struct parser *parser, WCHAR **line)
296 {
297     struct data_type { const WCHAR *tag; int len; int type; int parse_type; };
298 
299     static const struct data_type data_types[] = {
300     /*    tag       len  type         parse type    */
301         { L"\"",     1,  REG_SZ,      REG_SZ },
302         { L"hex:",   4,  REG_BINARY,  REG_BINARY },
303         { L"dword:", 6,  REG_DWORD,   REG_DWORD },
304         { L"hex(",   4,  -1,          REG_BINARY }, /* REG_NONE, REG_EXPAND_SZ, REG_MULTI_SZ */
305         { NULL,      0,  0,           0 }
306     };
307 
308     const struct data_type *ptr;
309 
310     for (ptr = data_types; ptr->tag; ptr++)
311     {
312         if (wcsncmp(ptr->tag, *line, ptr->len))
313             continue;
314 
315         parser->parse_type = ptr->parse_type;
316         parser->data_type = ptr->parse_type;
317         *line += ptr->len;
318 
319         if (ptr->type == -1)
320         {
321             WCHAR *end;
322             DWORD val;
323 
324             if (!**line || towlower((*line)[1]) == 'x')
325                 return FALSE;
326 
327             /* "hex(xx):" is special */
328             val = wcstoul(*line, &end, 16);
329 #ifdef __REACTOS__
330             /* Up to 8 hex digits, "hex(000000002)" is invalid */
331             if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE) || end - *line > 8)
332 #else
333             if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE))
334 #endif
335                 return FALSE;
336 
337             parser->data_type = val;
338             *line = end + 2;
339         }
340         return TRUE;
341     }
342     return FALSE;
343 }
344 
345 /******************************************************************************
346  * Replaces escape sequences with their character equivalents and
347  * null-terminates the string on the first non-escaped double quote.
348  *
349  * Assigns a pointer to the remaining unparsed data in the line.
350  * Returns TRUE or FALSE to indicate whether a closing double quote was found.
351  */
352 static BOOL REGPROC_unescape_string(WCHAR *str, WCHAR **unparsed)
353 {
354     int str_idx = 0;            /* current character under analysis */
355     int val_idx = 0;            /* the last character of the unescaped string */
356     int len = lstrlenW(str);
357     BOOL ret;
358 
359     for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
360         if (str[str_idx] == '\\') {
361             str_idx++;
362             switch (str[str_idx]) {
363             case 'n':
364                 str[val_idx] = '\n';
365                 break;
366             case 'r':
367                 str[val_idx] = '\r';
368                 break;
369             case '0':
370                 return FALSE;
371             case '\\':
372             case '"':
373                 str[val_idx] = str[str_idx];
374                 break;
375             default:
376                 if (!str[str_idx]) return FALSE;
377                 output_message(STRING_ESCAPE_SEQUENCE, str[str_idx]);
378                 str[val_idx] = str[str_idx];
379                 break;
380             }
381         } else if (str[str_idx] == '"') {
382             break;
383         } else {
384             str[val_idx] = str[str_idx];
385         }
386     }
387 
388     ret = (str[str_idx] == '"');
389     *unparsed = str + str_idx + 1;
390     str[val_idx] = '\0';
391     return ret;
392 }
393 
394 static HKEY parse_key_name(WCHAR *key_name, WCHAR **key_path)
395 {
396     unsigned int i;
397 
398     if (!key_name) return 0;
399 
400     *key_path = wcschr(key_name, '\\');
401     if (*key_path) (*key_path)++;
402 
403     for (i = 0; i < ARRAY_SIZE(reg_class_keys); i++)
404     {
405         int len = lstrlenW(reg_class_namesW[i]);
406 #ifdef __REACTOS__
407         if (!_wcsnicmp(key_name, reg_class_namesW[i], len) &&
408 #else
409         if (!wcsnicmp(key_name, reg_class_namesW[i], len) &&
410 #endif
411            (key_name[len] == 0 || key_name[len] == '\\'))
412         {
413             return reg_class_keys[i];
414         }
415     }
416 
417     return 0;
418 }
419 
420 static void close_key(struct parser *parser)
421 {
422     if (parser->hkey)
423     {
424         free(parser->key_name);
425         parser->key_name = NULL;
426 
427         RegCloseKey(parser->hkey);
428         parser->hkey = NULL;
429     }
430 }
431 
432 /******************************************************************************
433  * Opens the registry key given by the input path.
434  * This key must be closed by calling close_key().
435  */
436 static LONG open_key(struct parser *parser, WCHAR *path)
437 {
438     HKEY key_class;
439     WCHAR *key_path;
440     LONG res;
441 
442     close_key(parser);
443 
444     /* Get the registry class */
445     if (!path || !(key_class = parse_key_name(path, &key_path)))
446         return ERROR_INVALID_PARAMETER;
447 
448     res = RegCreateKeyExW(key_class, key_path, 0, NULL, REG_OPTION_NON_VOLATILE,
449                           KEY_ALL_ACCESS, NULL, &parser->hkey, NULL);
450 
451     if (res == ERROR_SUCCESS)
452     {
453         parser->key_name = malloc((lstrlenW(path) + 1) * sizeof(WCHAR));
454         lstrcpyW(parser->key_name, path);
455     }
456     else
457         parser->hkey = NULL;
458 
459     return res;
460 }
461 
462 static void free_parser_data(struct parser *parser)
463 {
464     if (parser->parse_type == REG_DWORD || parser->parse_type == REG_BINARY)
465         free(parser->data);
466 
467     parser->data = NULL;
468     parser->data_size = 0;
469 }
470 
471 static void prepare_hex_string_data(struct parser *parser)
472 {
473     if (parser->data_type == REG_EXPAND_SZ || parser->data_type == REG_MULTI_SZ ||
474         parser->data_type == REG_SZ)
475     {
476         if (parser->is_unicode)
477         {
478             WCHAR *data = parser->data;
479             DWORD len = parser->data_size / sizeof(WCHAR);
480 
481             if (data[len - 1] != 0)
482             {
483                 data[len] = 0;
484                 parser->data_size += sizeof(WCHAR);
485             }
486         }
487         else
488         {
489             BYTE *data = parser->data;
490 
491             if (data[parser->data_size - 1] != 0)
492             {
493                 data[parser->data_size] = 0;
494                 parser->data_size++;
495             }
496 
497             parser->data = GetWideStringN(parser->data, parser->data_size, &parser->data_size);
498             parser->data_size *= sizeof(WCHAR);
499             free(data);
500         }
501     }
502 }
503 
504 enum reg_versions {
505     REG_VERSION_31,
506     REG_VERSION_40,
507     REG_VERSION_50,
508     REG_VERSION_FUZZY,
509     REG_VERSION_INVALID
510 };
511 
512 static enum reg_versions parse_file_header(const WCHAR *s)
513 {
514     static const WCHAR header_31[] = L"REGEDIT";
515 
516     while (*s == ' ' || *s == '\t') s++;
517 
518     if (!lstrcmpW(s, header_31))
519         return REG_VERSION_31;
520 
521     if (!lstrcmpW(s, L"REGEDIT4"))
522         return REG_VERSION_40;
523 
524     if (!lstrcmpW(s, L"Windows Registry Editor Version 5.00"))
525         return REG_VERSION_50;
526 
527     /* The Windows version accepts registry file headers beginning with "REGEDIT" and ending
528      * with other characters, as long as "REGEDIT" appears at the start of the line. For example,
529      * "REGEDIT 4", "REGEDIT9" and "REGEDIT4FOO" are all treated as valid file headers.
530      * In all such cases, however, the contents of the registry file are not imported.
531      */
532     if (!wcsncmp(s, header_31, 7)) /* "REGEDIT" without NUL */
533         return REG_VERSION_FUZZY;
534 
535     return REG_VERSION_INVALID;
536 }
537 
538 /* handler for parser HEADER state */
539 static WCHAR *header_state(struct parser *parser, WCHAR *pos)
540 {
541     WCHAR *line, *header;
542 
543     if (!(line = get_line(parser->file)))
544         return NULL;
545 
546     if (!parser->is_unicode)
547     {
548         header = malloc((lstrlenW(line) + 3) * sizeof(WCHAR));
549         header[0] = parser->two_wchars[0];
550         header[1] = parser->two_wchars[1];
551         lstrcpyW(header + 2, line);
552         parser->reg_version = parse_file_header(header);
553         free(header);
554     }
555     else parser->reg_version = parse_file_header(line);
556 
557     switch (parser->reg_version)
558     {
559     case REG_VERSION_31:
560         set_state(parser, PARSE_WIN31_LINE);
561         break;
562     case REG_VERSION_40:
563     case REG_VERSION_50:
564         set_state(parser, LINE_START);
565         break;
566     default:
567         get_line(NULL); /* Reset static variables */
568         return NULL;
569     }
570 
571     return line;
572 }
573 
574 /* handler for parser PARSE_WIN31_LINE state */
575 static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos)
576 {
577     WCHAR *line, *value;
578     static WCHAR hkcr[] = L"HKEY_CLASSES_ROOT";
579     unsigned int key_end = 0;
580 
581     if (!(line = get_line(parser->file)))
582         return NULL;
583 
584     if (wcsncmp(line, hkcr, lstrlenW(hkcr)))
585         return line;
586 
587     /* get key name */
588     while (line[key_end] && !iswspace(line[key_end])) key_end++;
589 
590     value = line + key_end;
591     while (*value == ' ' || *value == '\t') value++;
592 
593     if (*value == '=') value++;
594     if (*value == ' ') value++; /* at most one space is skipped */
595 
596     line[key_end] = 0;
597 
598     if (open_key(parser, line) != ERROR_SUCCESS)
599     {
600         output_message(STRING_OPEN_KEY_FAILED, line);
601         return line;
602     }
603 
604     parser->value_name = NULL;
605     parser->data_type = REG_SZ;
606     parser->data = value;
607     parser->data_size = (lstrlenW(value) + 1) * sizeof(WCHAR);
608 
609     set_state(parser, SET_VALUE);
610     return value;
611 }
612 
613 /* handler for parser LINE_START state */
614 static WCHAR *line_start_state(struct parser *parser, WCHAR *pos)
615 {
616     WCHAR *line, *p;
617 
618     if (!(line = get_line(parser->file)))
619         return NULL;
620 
621     for (p = line; *p; p++)
622     {
623         switch (*p)
624         {
625         case '[':
626             set_state(parser, KEY_NAME);
627             return p + 1;
628         case '@':
629             set_state(parser, DEFAULT_VALUE_NAME);
630             return p;
631         case '"':
632             set_state(parser, QUOTED_VALUE_NAME);
633             return p + 1;
634         case ' ':
635         case '\t':
636             break;
637         default:
638             return p;
639         }
640     }
641 
642     return p;
643 }
644 
645 /* handler for parser KEY_NAME state */
646 static WCHAR *key_name_state(struct parser *parser, WCHAR *pos)
647 {
648     WCHAR *p = pos, *key_end;
649 
650     if (*p == ' ' || *p == '\t' || !(key_end = wcsrchr(p, ']')))
651         goto done;
652 
653     *key_end = 0;
654 
655     if (*p == '-')
656     {
657         set_state(parser, DELETE_KEY);
658         return p + 1;
659     }
660     else if (open_key(parser, p) != ERROR_SUCCESS)
661         output_message(STRING_OPEN_KEY_FAILED, p);
662 
663 done:
664     set_state(parser, LINE_START);
665     return p;
666 }
667 
668 /* handler for parser DELETE_KEY state */
669 static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos)
670 {
671     WCHAR *p = pos;
672 
673     close_key(parser);
674 
675     if (*p == 'H' || *p == 'h')
676         delete_registry_key(p);
677 
678     set_state(parser, LINE_START);
679     return p;
680 }
681 
682 /* handler for parser DEFAULT_VALUE_NAME state */
683 static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos)
684 {
685     free(parser->value_name);
686     parser->value_name = NULL;
687 
688     set_state(parser, DATA_START);
689     return pos + 1;
690 }
691 
692 /* handler for parser QUOTED_VALUE_NAME state */
693 static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos)
694 {
695     WCHAR *val_name = pos, *p;
696 
697     free(parser->value_name);
698     parser->value_name = NULL;
699 
700     if (!REGPROC_unescape_string(val_name, &p))
701         goto invalid;
702 
703     /* copy the value name in case we need to parse multiple lines and the buffer is overwritten */
704     parser->value_name = malloc((lstrlenW(val_name) + 1) * sizeof(WCHAR));
705     lstrcpyW(parser->value_name, val_name);
706 
707     set_state(parser, DATA_START);
708     return p;
709 
710 invalid:
711     set_state(parser, LINE_START);
712     return val_name;
713 }
714 
715 /* handler for parser DATA_START state */
716 static WCHAR *data_start_state(struct parser *parser, WCHAR *pos)
717 {
718     WCHAR *p = pos;
719     unsigned int len;
720 
721     while (*p == ' ' || *p == '\t') p++;
722     if (*p != '=') goto done;
723     p++;
724     while (*p == ' ' || *p == '\t') p++;
725 
726     /* trim trailing whitespace */
727     len = lstrlenW(p);
728     while (len > 0 && (p[len - 1] == ' ' || p[len - 1] == '\t')) len--;
729     p[len] = 0;
730 
731     if (*p == '-')
732         set_state(parser, DELETE_VALUE);
733     else
734         set_state(parser, DATA_TYPE);
735     return p;
736 
737 done:
738     set_state(parser, LINE_START);
739     return p;
740 }
741 
742 /* handler for parser DELETE_VALUE state */
743 static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos)
744 {
745     WCHAR *p = pos + 1;
746 
747     while (*p == ' ' || *p == '\t') p++;
748     if (*p && *p != ';') goto done;
749 
750     RegDeleteValueW(parser->hkey, parser->value_name);
751 
752 done:
753     set_state(parser, LINE_START);
754     return p;
755 }
756 
757 /* handler for parser DATA_TYPE state */
758 static WCHAR *data_type_state(struct parser *parser, WCHAR *pos)
759 {
760     WCHAR *line = pos;
761 
762     if (!parse_data_type(parser, &line))
763     {
764         set_state(parser, LINE_START);
765         return line;
766     }
767 
768     switch (parser->parse_type)
769     {
770     case REG_SZ:
771         set_state(parser, STRING_DATA);
772         break;
773     case REG_DWORD:
774         set_state(parser, DWORD_DATA);
775         break;
776     case REG_BINARY: /* all hex data types, including undefined */
777         set_state(parser, HEX_DATA);
778         break;
779     default:
780         set_state(parser, UNKNOWN_DATA);
781     }
782 
783     return line;
784 }
785 
786 /* handler for parser STRING_DATA state */
787 static WCHAR *string_data_state(struct parser *parser, WCHAR *pos)
788 {
789     WCHAR *line;
790 
791     parser->data = pos;
792 
793     if (!REGPROC_unescape_string(parser->data, &line))
794         goto invalid;
795 
796     while (*line == ' ' || *line == '\t') line++;
797     if (*line && *line != ';') goto invalid;
798 
799     parser->data_size = (lstrlenW(parser->data) + 1) * sizeof(WCHAR);
800 
801     set_state(parser, SET_VALUE);
802     return line;
803 
804 invalid:
805     free_parser_data(parser);
806     set_state(parser, LINE_START);
807     return line;
808 }
809 
810 /* handler for parser DWORD_DATA state */
811 static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos)
812 {
813     WCHAR *line = pos;
814 
815     parser->data = malloc(sizeof(DWORD));
816 
817     if (!convert_hex_to_dword(line, parser->data))
818         goto invalid;
819 
820     parser->data_size = sizeof(DWORD);
821 
822     set_state(parser, SET_VALUE);
823     return line;
824 
825 invalid:
826     free_parser_data(parser);
827     set_state(parser, LINE_START);
828     return line;
829 }
830 
831 /* handler for parser HEX_DATA state */
832 static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos)
833 {
834     WCHAR *line = pos;
835 
836     if (!*line)
837         goto set_value;
838 
839     if (!convert_hex_csv_to_hex(parser, &line))
840         goto invalid;
841 
842     if (parser->backslash)
843     {
844         set_state(parser, EOL_BACKSLASH);
845         return line;
846     }
847 
848     prepare_hex_string_data(parser);
849 
850 set_value:
851     set_state(parser, SET_VALUE);
852     return line;
853 
854 invalid:
855     free_parser_data(parser);
856     set_state(parser, LINE_START);
857     return line;
858 }
859 
860 /* handler for parser EOL_BACKSLASH state */
861 static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos)
862 {
863     WCHAR *p = pos;
864 
865     while (*p == ' ' || *p == '\t') p++;
866     if (*p && *p != ';') goto invalid;
867 
868     set_state(parser, HEX_MULTILINE);
869     return pos;
870 
871 invalid:
872     free_parser_data(parser);
873     set_state(parser, LINE_START);
874     return p;
875 }
876 
877 /* handler for parser HEX_MULTILINE state */
878 static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos)
879 {
880     WCHAR *line;
881 
882     if (!(line = get_line(parser->file)))
883     {
884         prepare_hex_string_data(parser);
885         set_state(parser, SET_VALUE);
886         return pos;
887     }
888 
889     while (*line == ' ' || *line == '\t') line++;
890     if (!*line || *line == ';') return line;
891 
892     if (!iswxdigit(*line)) goto invalid;
893 
894     set_state(parser, HEX_DATA);
895     return line;
896 
897 invalid:
898     free_parser_data(parser);
899     set_state(parser, LINE_START);
900     return line;
901 }
902 
903 /* handler for parser UNKNOWN_DATA state */
904 static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos)
905 {
906     output_message(STRING_UNKNOWN_DATA_FORMAT, parser->data_type);
907 
908     set_state(parser, LINE_START);
909     return pos;
910 }
911 
912 /* handler for parser SET_VALUE state */
913 static WCHAR *set_value_state(struct parser *parser, WCHAR *pos)
914 {
915     RegSetValueExW(parser->hkey, parser->value_name, 0, parser->data_type,
916                    parser->data, parser->data_size);
917 
918     free_parser_data(parser);
919 
920     if (parser->reg_version == REG_VERSION_31)
921         set_state(parser, PARSE_WIN31_LINE);
922     else
923         set_state(parser, LINE_START);
924 
925     return pos;
926 }
927 
928 static WCHAR *get_lineA(FILE *fp)
929 {
930     static WCHAR *lineW;
931     static size_t size;
932     static char *buf, *next;
933     char *line;
934 
935     free(lineW);
936 
937     if (!fp) goto cleanup;
938 
939     if (!size)
940     {
941         size = REG_VAL_BUF_SIZE;
942         buf = malloc(size);
943         *buf = 0;
944         next = buf;
945     }
946     line = next;
947 
948     while (next)
949     {
950         char *p = strpbrk(line, "\r\n");
951         if (!p)
952         {
953             size_t len, count;
954             len = strlen(next);
955             memmove(buf, next, len + 1);
956             if (size - len < 3)
957             {
958                 size *= 2;
959                 buf = realloc(buf, size);
960             }
961             if (!(count = fread(buf + len, 1, size - len - 1, fp)))
962             {
963                 next = NULL;
964                 lineW = GetWideString(buf);
965                 return lineW;
966             }
967             buf[len + count] = 0;
968             next = buf;
969             line = buf;
970             continue;
971         }
972         next = p + 1;
973         if (*p == '\r' && *(p + 1) == '\n') next++;
974         *p = 0;
975         lineW = GetWideString(line);
976         return lineW;
977     }
978 
979 cleanup:
980     lineW = NULL;
981     if (size) free(buf);
982     size = 0;
983     return NULL;
984 }
985 
986 static WCHAR *get_lineW(FILE *fp)
987 {
988     static size_t size;
989     static WCHAR *buf, *next;
990     WCHAR *line;
991 
992     if (!fp) goto cleanup;
993 
994     if (!size)
995     {
996         size = REG_VAL_BUF_SIZE;
997         buf = malloc(size * sizeof(WCHAR));
998         *buf = 0;
999         next = buf;
1000     }
1001     line = next;
1002 
1003     while (next)
1004     {
1005         WCHAR *p = wcspbrk(line, L"\r\n");
1006         if (!p)
1007         {
1008             size_t len, count;
1009             len = lstrlenW(next);
1010             memmove(buf, next, (len + 1) * sizeof(WCHAR));
1011             if (size - len < 3)
1012             {
1013                 size *= 2;
1014                 buf = realloc(buf, size * sizeof(WCHAR));
1015             }
1016             if (!(count = fread(buf + len, sizeof(WCHAR), size - len - 1, fp)))
1017             {
1018                 next = NULL;
1019                 return buf;
1020             }
1021             buf[len + count] = 0;
1022             next = buf;
1023             line = buf;
1024             continue;
1025         }
1026         next = p + 1;
1027         if (*p == '\r' && *(p + 1) == '\n') next++;
1028         *p = 0;
1029         return line;
1030     }
1031 
1032 cleanup:
1033     if (size) free(buf);
1034     size = 0;
1035     return NULL;
1036 }
1037 
1038 /******************************************************************************
1039  * Reads contents of the specified file into the registry.
1040  */
1041 BOOL import_registry_file(FILE *reg_file)
1042 {
1043     BYTE s[2];
1044     struct parser parser;
1045     WCHAR *pos;
1046 
1047     if (!reg_file || (fread(s, 2, 1, reg_file) != 1))
1048         return FALSE;
1049 
1050     parser.is_unicode = (s[0] == 0xff && s[1] == 0xfe);
1051     get_line = parser.is_unicode ? get_lineW : get_lineA;
1052 
1053     parser.file          = reg_file;
1054     parser.two_wchars[0] = s[0];
1055     parser.two_wchars[1] = s[1];
1056     parser.reg_version   = -1;
1057     parser.hkey          = NULL;
1058     parser.key_name      = NULL;
1059     parser.value_name    = NULL;
1060     parser.parse_type    = 0;
1061     parser.data_type     = 0;
1062     parser.data          = NULL;
1063     parser.data_size     = 0;
1064     parser.backslash     = FALSE;
1065     parser.state         = HEADER;
1066 
1067     pos = parser.two_wchars;
1068 
1069     /* parser main loop */
1070     while (pos)
1071         pos = (parser_funcs[parser.state])(&parser, pos);
1072 
1073     if (parser.reg_version == REG_VERSION_FUZZY || parser.reg_version == REG_VERSION_INVALID)
1074         return parser.reg_version == REG_VERSION_FUZZY;
1075 
1076     free(parser.value_name);
1077     close_key(&parser);
1078 
1079     return TRUE;
1080 }
1081 
1082 /******************************************************************************
1083  * Removes the registry key with all subkeys. Parses full key name.
1084  *
1085  * Parameters:
1086  * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
1087  *      empty, points to register key class, does not exist.
1088  */
1089 void delete_registry_key(WCHAR *reg_key_name)
1090 {
1091     WCHAR *key_name = NULL;
1092     HKEY key_class;
1093 
1094     if (!reg_key_name || !reg_key_name[0])
1095         return;
1096 
1097     if (!(key_class = parse_key_name(reg_key_name, &key_name)))
1098     {
1099         if (key_name) *(key_name - 1) = 0;
1100 #ifdef __REACTOS__
1101         output_message(STRING_INVALID_SYSTEM_KEY, reg_key_name);
1102         return;
1103 #else
1104         error_exit(STRING_INVALID_SYSTEM_KEY, reg_key_name);
1105 #endif
1106     }
1107 
1108     if (!key_name || !*key_name)
1109 #ifdef __REACTOS__
1110     {
1111         output_message(STRING_DELETE_FAILED, reg_key_name);
1112         return;
1113     }
1114 #else
1115         error_exit(STRING_DELETE_FAILED, reg_key_name);
1116 #endif
1117 
1118 #ifdef __REACTOS__
1119     SHDeleteKey(key_class, key_name);
1120 #else
1121     RegDeleteTreeW(key_class, key_name);
1122 #endif
1123 }
1124 
1125 static void REGPROC_write_line(FILE *fp, const WCHAR *str, BOOL unicode)
1126 {
1127     if (unicode)
1128         fwrite(str, sizeof(WCHAR), lstrlenW(str), fp);
1129     else
1130     {
1131         char *strA = GetMultiByteString(str);
1132         fputs(strA, fp);
1133         free(strA);
1134     }
1135 }
1136 
1137 static WCHAR *REGPROC_escape_string(WCHAR *str, size_t str_len, size_t *line_len)
1138 {
1139     size_t i, escape_count, pos;
1140     WCHAR *buf;
1141 
1142     for (i = 0, escape_count = 0; i < str_len; i++)
1143     {
1144         WCHAR c = str[i];
1145 
1146         if (!c) break;
1147 
1148         if (c == '\r' || c == '\n' || c == '\\' || c == '"')
1149             escape_count++;
1150     }
1151 
1152     buf = malloc((str_len + escape_count + 1) * sizeof(WCHAR));
1153 
1154     for (i = 0, pos = 0; i < str_len; i++, pos++)
1155     {
1156         WCHAR c = str[i];
1157 
1158         if (!c) break;
1159 
1160         switch (c)
1161         {
1162         case '\r':
1163             buf[pos++] = '\\';
1164             buf[pos] = 'r';
1165             break;
1166         case '\n':
1167             buf[pos++] = '\\';
1168             buf[pos] = 'n';
1169             break;
1170         case '\\':
1171             buf[pos++] = '\\';
1172             buf[pos] = '\\';
1173             break;
1174         case '"':
1175             buf[pos++] = '\\';
1176             buf[pos] = '"';
1177             break;
1178         default:
1179             buf[pos] = c;
1180         }
1181     }
1182 
1183     buf[pos] = 0;
1184     *line_len = pos;
1185     return buf;
1186 }
1187 
1188 static size_t export_value_name(FILE *fp, WCHAR *name, size_t len, BOOL unicode)
1189 {
1190     static const WCHAR default_name[] = L"@=";
1191     size_t line_len;
1192 
1193     if (name && *name)
1194     {
1195         WCHAR *str = REGPROC_escape_string(name, len, &line_len);
1196         WCHAR *buf = malloc((line_len + 4) * sizeof(WCHAR));
1197 #ifdef __REACTOS__
1198         StringCchPrintfW(buf, line_len + 4, L"\"%s\"=", str);
1199         line_len = wcslen(buf);
1200 #else
1201         line_len = swprintf(buf, line_len + 4, L"\"%s\"=", str);
1202 #endif
1203         REGPROC_write_line(fp, buf, unicode);
1204         free(buf);
1205         free(str);
1206     }
1207     else
1208     {
1209         line_len = lstrlenW(default_name);
1210         REGPROC_write_line(fp, default_name, unicode);
1211     }
1212 
1213     return line_len;
1214 }
1215 
1216 static void export_string_data(WCHAR **buf, WCHAR *data, size_t size)
1217 {
1218     size_t len = 0, line_len;
1219     WCHAR *str;
1220 
1221     if (size)
1222         len = size / sizeof(WCHAR) - 1;
1223     str = REGPROC_escape_string(data, len, &line_len);
1224     *buf = malloc((line_len + 3) * sizeof(WCHAR));
1225 #ifdef __REACTOS__
1226     StringCchPrintfW(*buf, line_len + 3, L"\"%s\"", str);
1227 #else
1228     swprintf(*buf, line_len + 3, L"\"%s\"", str);
1229 #endif
1230     free(str);
1231 }
1232 
1233 static void export_dword_data(WCHAR **buf, DWORD *data)
1234 {
1235     *buf = malloc(15 * sizeof(WCHAR));
1236 #ifdef __REACTOS__
1237     StringCchPrintfW(*buf, 15, L"dword:%08x", *data);
1238 #else
1239     swprintf(*buf, 15, L"dword:%08x", *data);
1240 #endif
1241 }
1242 
1243 static size_t export_hex_data_type(FILE *fp, DWORD type, BOOL unicode)
1244 {
1245     static const WCHAR hex[] = L"hex:";
1246     size_t line_len;
1247 
1248     if (type == REG_BINARY)
1249     {
1250         line_len = lstrlenW(hex);
1251         REGPROC_write_line(fp, hex, unicode);
1252     }
1253     else
1254     {
1255         WCHAR *buf = malloc(15 * sizeof(WCHAR));
1256 #ifdef __REACTOS__
1257         StringCchPrintfW(buf, 15, L"hex(%x):", type);
1258         line_len = wcslen(buf);
1259 #else
1260         line_len = swprintf(buf, 15, L"hex(%x):", type);
1261 #endif
1262         REGPROC_write_line(fp, buf, unicode);
1263         free(buf);
1264     }
1265 
1266     return line_len;
1267 }
1268 
1269 #define MAX_HEX_CHARS 77
1270 
1271 static void export_hex_data(FILE *fp, WCHAR **buf, DWORD type, DWORD line_len,
1272                             void *data, DWORD size, BOOL unicode)
1273 {
1274     size_t num_commas, i, pos;
1275 
1276     line_len += export_hex_data_type(fp, type, unicode);
1277 
1278     if (!size) return;
1279 
1280     if (!unicode && (type == REG_EXPAND_SZ || type == REG_MULTI_SZ))
1281         data = GetMultiByteStringN(data, size / sizeof(WCHAR), &size);
1282 
1283     num_commas = size - 1;
1284     *buf = malloc(size * 3 * sizeof(WCHAR));
1285 
1286     for (i = 0, pos = 0; i < size; i++)
1287     {
1288 #ifdef __REACTOS__
1289         StringCchPrintfW(*buf + pos, 3, L"%02x", ((BYTE *)data)[i]);
1290         pos += wcslen(*buf + pos);
1291 #else
1292         pos += swprintf(*buf + pos, 3, L"%02x", ((BYTE *)data)[i]);
1293 #endif
1294         if (i == num_commas) break;
1295         (*buf)[pos++] = ',';
1296         (*buf)[pos] = 0;
1297         line_len += 3;
1298 
1299         if (line_len >= MAX_HEX_CHARS)
1300         {
1301             REGPROC_write_line(fp, *buf, unicode);
1302             REGPROC_write_line(fp, L"\\\r\n  ", unicode);
1303             line_len = 2;
1304             pos = 0;
1305         }
1306     }
1307 }
1308 
1309 static void export_newline(FILE *fp, BOOL unicode)
1310 {
1311     REGPROC_write_line(fp, L"\r\n", unicode);
1312 }
1313 
1314 static void export_data(FILE *fp, WCHAR *value_name, DWORD value_len, DWORD type,
1315                         void *data, size_t size, BOOL unicode)
1316 {
1317     WCHAR *buf = NULL;
1318     size_t line_len = export_value_name(fp, value_name, value_len, unicode);
1319 
1320     switch (type)
1321     {
1322     case REG_SZ:
1323         export_string_data(&buf, data, size);
1324         break;
1325     case REG_DWORD:
1326         if (size)
1327         {
1328             export_dword_data(&buf, data);
1329             break;
1330         }
1331         /* fall through */
1332     case REG_NONE:
1333     case REG_EXPAND_SZ:
1334     case REG_BINARY:
1335     case REG_MULTI_SZ:
1336     default:
1337         export_hex_data(fp, &buf, type, line_len, data, size, unicode);
1338         break;
1339     }
1340 
1341     if (size || type == REG_SZ)
1342     {
1343         REGPROC_write_line(fp, buf, unicode);
1344         free(buf);
1345     }
1346 
1347     export_newline(fp, unicode);
1348 }
1349 
1350 static WCHAR *build_subkey_path(WCHAR *path, DWORD path_len, WCHAR *subkey_name, DWORD subkey_len)
1351 {
1352     WCHAR *subkey_path;
1353 
1354     subkey_path = malloc((path_len + subkey_len + 2) * sizeof(WCHAR));
1355 #ifdef __REACTOS__
1356     StringCchPrintfW(subkey_path, path_len + subkey_len + 2, L"%s\\%s", path, subkey_name);
1357 #else
1358     swprintf(subkey_path, path_len + subkey_len + 2, L"%s\\%s", path, subkey_name);
1359 #endif
1360 
1361     return subkey_path;
1362 }
1363 
1364 static void export_key_name(FILE *fp, WCHAR *name, BOOL unicode)
1365 {
1366     WCHAR *buf;
1367 
1368     buf = malloc((lstrlenW(name) + 7) * sizeof(WCHAR));
1369 #ifdef __REACTOS__
1370     StringCchPrintfW(buf, lstrlenW(name) + 7, L"\r\n[%s]\r\n", name);
1371 #else
1372     swprintf(buf, lstrlenW(name) + 7, L"\r\n[%s]\r\n", name);
1373 #endif
1374     REGPROC_write_line(fp, buf, unicode);
1375     free(buf);
1376 }
1377 
1378 #define MAX_SUBKEY_LEN   257
1379 
1380 static void export_registry_data(FILE *fp, HKEY key, WCHAR *path, BOOL unicode)
1381 {
1382     LONG rc;
1383     DWORD max_value_len = 256, value_len;
1384     DWORD max_data_bytes = 2048, data_size;
1385     DWORD subkey_len;
1386     DWORD i, type, path_len;
1387     WCHAR *value_name, *subkey_name, *subkey_path;
1388     BYTE *data;
1389     HKEY subkey;
1390 
1391     export_key_name(fp, path, unicode);
1392 
1393     value_name = malloc(max_value_len * sizeof(WCHAR));
1394     data = malloc(max_data_bytes);
1395 
1396     i = 0;
1397     for (;;)
1398     {
1399         value_len = max_value_len;
1400         data_size = max_data_bytes;
1401         rc = RegEnumValueW(key, i, value_name, &value_len, NULL, &type, data, &data_size);
1402         if (rc == ERROR_SUCCESS)
1403         {
1404             export_data(fp, value_name, value_len, type, data, data_size, unicode);
1405             i++;
1406         }
1407         else if (rc == ERROR_MORE_DATA)
1408         {
1409             if (data_size > max_data_bytes)
1410             {
1411                 max_data_bytes = data_size;
1412                 data = realloc(data, max_data_bytes);
1413             }
1414             else
1415             {
1416                 max_value_len *= 2;
1417                 value_name = realloc(value_name, max_value_len * sizeof(WCHAR));
1418             }
1419         }
1420         else break;
1421     }
1422 
1423     free(data);
1424     free(value_name);
1425 
1426     subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR));
1427 
1428     path_len = lstrlenW(path);
1429 
1430     i = 0;
1431     for (;;)
1432     {
1433         subkey_len = MAX_SUBKEY_LEN;
1434         rc = RegEnumKeyExW(key, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL);
1435         if (rc == ERROR_SUCCESS)
1436         {
1437             subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len);
1438             if (!RegOpenKeyExW(key, subkey_name, 0, KEY_READ, &subkey))
1439             {
1440                 export_registry_data(fp, subkey, subkey_path, unicode);
1441                 RegCloseKey(subkey);
1442             }
1443             free(subkey_path);
1444             i++;
1445         }
1446         else break;
1447     }
1448 
1449     free(subkey_name);
1450 }
1451 
1452 static FILE *REGPROC_open_export_file(WCHAR *file_name, BOOL unicode)
1453 {
1454     FILE *file;
1455 
1456     if (!lstrcmpW(file_name, L"-"))
1457     {
1458         file = stdout;
1459         _setmode(_fileno(file), _O_BINARY);
1460     }
1461     else
1462     {
1463         file = _wfopen(file_name, L"wb");
1464         if (!file)
1465         {
1466             _wperror(L"regedit");
1467 #ifdef __REACTOS__
1468             output_message(STRING_CANNOT_OPEN_FILE, file_name);
1469             return NULL;
1470 #else
1471             error_exit(STRING_CANNOT_OPEN_FILE, file_name);
1472 #endif
1473         }
1474     }
1475 
1476     if (unicode)
1477     {
1478         static const BYTE bom[] = {0xff,0xfe};
1479         static const WCHAR header[] = L"Windows Registry Editor Version 5.00\r\n";
1480 
1481         fwrite(bom, sizeof(BYTE), ARRAY_SIZE(bom), file);
1482         fwrite(header, sizeof(WCHAR), lstrlenW(header), file);
1483     }
1484     else
1485         fputs("REGEDIT4\r\n", file);
1486 
1487     return file;
1488 }
1489 
1490 static HKEY open_export_key(HKEY key_class, WCHAR *subkey, WCHAR *path)
1491 {
1492     HKEY key;
1493 
1494     if (!RegOpenKeyExW(key_class, subkey, 0, KEY_READ, &key))
1495         return key;
1496 
1497     output_message(STRING_OPEN_KEY_FAILED, path);
1498     return NULL;
1499 }
1500 
1501 static BOOL export_key(WCHAR *file_name, WCHAR *path, BOOL unicode)
1502 {
1503     HKEY key_class, key;
1504     WCHAR *subkey;
1505     FILE *fp;
1506 
1507     if (!(key_class = parse_key_name(path, &subkey)))
1508     {
1509         if (subkey) *(subkey - 1) = 0;
1510         output_message(STRING_INVALID_SYSTEM_KEY, path);
1511         return FALSE;
1512     }
1513 
1514     if (!(key = open_export_key(key_class, subkey, path)))
1515         return FALSE;
1516 
1517     fp = REGPROC_open_export_file(file_name, unicode);
1518 #ifdef __REACTOS__
1519     if (!fp)
1520         return TRUE; /* Error message is already displayed */
1521 #endif
1522     export_registry_data(fp, key, path, unicode);
1523     export_newline(fp, unicode);
1524     fclose(fp);
1525 
1526     RegCloseKey(key);
1527     return TRUE;
1528 }
1529 
1530 static BOOL export_all(WCHAR *file_name, WCHAR *path, BOOL unicode)
1531 {
1532     FILE *fp;
1533     int i;
1534     HKEY classes[] = {HKEY_LOCAL_MACHINE, HKEY_USERS}, key;
1535     WCHAR *class_name;
1536 
1537     fp = REGPROC_open_export_file(file_name, unicode);
1538 #ifdef __REACTOS__
1539     if (!fp)
1540         return TRUE; /* Error message is already displayed */
1541 #endif
1542 
1543     for (i = 0; i < ARRAY_SIZE(classes); i++)
1544     {
1545         if (!(key = open_export_key(classes[i], NULL, path)))
1546         {
1547             fclose(fp);
1548             return FALSE;
1549         }
1550 
1551         class_name = malloc((lstrlenW(reg_class_namesW[i]) + 1) * sizeof(WCHAR));
1552         lstrcpyW(class_name, reg_class_namesW[i]);
1553 
1554         export_registry_data(fp, classes[i], class_name, unicode);
1555 
1556         free(class_name);
1557         RegCloseKey(key);
1558     }
1559 
1560     export_newline(fp, unicode);
1561     fclose(fp);
1562 
1563     return TRUE;
1564 }
1565 
1566 BOOL export_registry_key(WCHAR *file_name, WCHAR *path, DWORD format)
1567 {
1568     BOOL unicode = (format == REG_FORMAT_5);
1569 
1570     if (path && *path)
1571         return export_key(file_name, path, unicode);
1572     else
1573         return export_all(file_name, path, unicode);
1574 }
1575