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