1 /*
2  * Copyright 2017 Hugh McMaster
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <errno.h>
20 #include <stdio.h>
21 #include "reg.h"
22 #include <wine/debug.h>
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(reg);
25 
26 static WCHAR *GetWideString(const char *strA)
27 {
28     if (strA)
29     {
30         WCHAR *strW;
31         int len = MultiByteToWideChar(CP_ACP, 0, strA, -1, NULL, 0);
32 
33         strW = malloc(len * sizeof(WCHAR));
34         MultiByteToWideChar(CP_ACP, 0, strA, -1, strW, len);
35         return strW;
36     }
37     return NULL;
38 }
39 
40 static WCHAR *GetWideStringN(const char *strA, int size, DWORD *len)
41 {
42     if (strA)
43     {
44         WCHAR *strW;
45         *len = MultiByteToWideChar(CP_ACP, 0, strA, size, NULL, 0);
46 
47         strW = malloc(*len * sizeof(WCHAR));
48         MultiByteToWideChar(CP_ACP, 0, strA, size, strW, *len);
49         return strW;
50     }
51     *len = 0;
52     return NULL;
53 }
54 
55 static WCHAR *(*get_line)(FILE *);
56 
57 /* parser definitions */
58 enum parser_state
59 {
60     HEADER,              /* parsing the registry file version header */
61     PARSE_WIN31_LINE,    /* parsing a Windows 3.1 registry line */
62     LINE_START,          /* at the beginning of a registry line */
63     KEY_NAME,            /* parsing a key name */
64     DELETE_KEY,          /* deleting a registry key */
65     DEFAULT_VALUE_NAME,  /* parsing a default value name */
66     QUOTED_VALUE_NAME,   /* parsing a double-quoted value name */
67     DATA_START,          /* preparing for data parsing operations */
68     DELETE_VALUE,        /* deleting a registry value */
69     DATA_TYPE,           /* parsing the registry data type */
70     STRING_DATA,         /* parsing REG_SZ data */
71     DWORD_DATA,          /* parsing DWORD data */
72     HEX_DATA,            /* parsing REG_BINARY, REG_NONE, REG_EXPAND_SZ or REG_MULTI_SZ data */
73     EOL_BACKSLASH,       /* preparing to parse multiple lines of hex data */
74     HEX_MULTILINE,       /* parsing multiple lines of hex data */
75     UNKNOWN_DATA,        /* parsing an unhandled or invalid data type */
76     SET_VALUE,           /* adding a value to the registry */
77     NB_PARSER_STATES
78 };
79 
80 struct parser
81 {
82     FILE              *file;           /* pointer to a registry file */
83     WCHAR              two_wchars[2];  /* first two characters from the encoding check */
84     BOOL               is_unicode;     /* parsing Unicode or ASCII data */
85     short int          reg_version;    /* registry file version */
86     REGSAM             sam;            /* 32-bit or 64-bit registry view (if set) */
87     HKEY               hkey;           /* current registry key */
88     WCHAR             *key_name;       /* current key name */
89     WCHAR             *value_name;     /* value name */
90     DWORD              parse_type;     /* generic data type for parsing */
91     DWORD              data_type;      /* data type */
92     void              *data;           /* value data */
93     DWORD              data_size;      /* size of the data (in bytes) */
94     BOOL               backslash;      /* TRUE if the current line contains a backslash */
95     enum parser_state  state;          /* current parser state */
96 };
97 
98 typedef WCHAR *(*parser_state_func)(struct parser *parser, WCHAR *pos);
99 
100 /* parser state machine functions */
101 static WCHAR *header_state(struct parser *parser, WCHAR *pos);
102 static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos);
103 static WCHAR *line_start_state(struct parser *parser, WCHAR *pos);
104 static WCHAR *key_name_state(struct parser *parser, WCHAR *pos);
105 static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos);
106 static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos);
107 static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos);
108 static WCHAR *data_start_state(struct parser *parser, WCHAR *pos);
109 static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos);
110 static WCHAR *data_type_state(struct parser *parser, WCHAR *pos);
111 static WCHAR *string_data_state(struct parser *parser, WCHAR *pos);
112 static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos);
113 static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos);
114 static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos);
115 static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos);
116 static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos);
117 static WCHAR *set_value_state(struct parser *parser, WCHAR *pos);
118 
119 static const parser_state_func parser_funcs[NB_PARSER_STATES] =
120 {
121     header_state,              /* HEADER */
122     parse_win31_line_state,    /* PARSE_WIN31_LINE */
123     line_start_state,          /* LINE_START */
124     key_name_state,            /* KEY_NAME */
125     delete_key_state,          /* DELETE_KEY */
126     default_value_name_state,  /* DEFAULT_VALUE_NAME */
127     quoted_value_name_state,   /* QUOTED_VALUE_NAME */
128     data_start_state,          /* DATA_START */
129     delete_value_state,        /* DELETE_VALUE */
130     data_type_state,           /* DATA_TYPE */
131     string_data_state,         /* STRING_DATA */
132     dword_data_state,          /* DWORD_DATA */
133     hex_data_state,            /* HEX_DATA */
134     eol_backslash_state,       /* EOL_BACKSLASH */
135     hex_multiline_state,       /* HEX_MULTILINE */
136     unknown_data_state,        /* UNKNOWN_DATA */
137     set_value_state,           /* SET_VALUE */
138 };
139 
140 /* set the new parser state and return the previous one */
141 static inline enum parser_state set_state(struct parser *parser, enum parser_state state)
142 {
143     enum parser_state ret = parser->state;
144     parser->state = state;
145     return ret;
146 }
147 
148 /******************************************************************************
149  * Converts a hex representation of a DWORD into a DWORD.
150  */
151 static BOOL convert_hex_to_dword(WCHAR *str, DWORD *dw)
152 {
153     WCHAR *p, *end;
154     int count = 0;
155 
156     while (*str == ' ' || *str == '\t') str++;
157     if (!*str) goto error;
158 
159     p = str;
160     while (iswxdigit(*p))
161     {
162         count++;
163         p++;
164     }
165     if (count > 8) goto error;
166 
167     end = p;
168     while (*p == ' ' || *p == '\t') p++;
169     if (*p && *p != ';') goto error;
170 
171     *end = 0;
172     *dw = wcstoul(str, &end, 16);
173     return TRUE;
174 
175 error:
176     return FALSE;
177 }
178 
179 /******************************************************************************
180  * Converts comma-separated hex data into a binary string and modifies
181  * the input parameter to skip the concatenating backslash, if found.
182  *
183  * Returns TRUE or FALSE to indicate whether parsing was successful.
184  */
185 static BOOL convert_hex_csv_to_hex(struct parser *parser, WCHAR **str)
186 {
187     size_t size;
188     BYTE *d;
189     WCHAR *s;
190 
191     parser->backslash = FALSE;
192 
193     /* The worst case is 1 digit + 1 comma per byte */
194     size = ((lstrlenW(*str) + 1) / 2) + parser->data_size;
195     parser->data = realloc(parser->data, size);
196 
197     s = *str;
198     d = (BYTE *)parser->data + parser->data_size;
199 
200     while (*s)
201     {
202         WCHAR *end;
203         unsigned long wc;
204 
205         wc = wcstoul(s, &end, 16);
206         if (wc > 0xff) return FALSE;
207 
208         if (s == end && wc == 0)
209         {
210             while (*end == ' ' || *end == '\t') end++;
211             if (*end == '\\')
212             {
213                 parser->backslash = TRUE;
214                 *str = end + 1;
215                 return TRUE;
216             }
217             else if (*end == ';')
218                 return TRUE;
219             return FALSE;
220         }
221 
222         *d++ = wc;
223         parser->data_size++;
224 
225         if (*end && *end != ',')
226         {
227             while (*end == ' ' || *end == '\t') end++;
228             if (*end && *end != ';') return FALSE;
229             return TRUE;
230         }
231 
232         if (*end) end++;
233         s = end;
234     }
235 
236     return TRUE;
237 }
238 
239 /******************************************************************************
240  * Parses the data type of the registry value being imported and modifies
241  * the input parameter to skip the string representation of the data type.
242  *
243  * Returns TRUE or FALSE to indicate whether a data type was found.
244  */
245 static BOOL parse_data_type(struct parser *parser, WCHAR **line)
246 {
247     struct data_type { const WCHAR *tag; int len; int type; int parse_type; };
248 
249     static const struct data_type data_types[] = {
250     /*    tag       len  type         parse type    */
251         { L"\"",     1,  REG_SZ,      REG_SZ },
252         { L"hex:",   4,  REG_BINARY,  REG_BINARY },
253         { L"dword:", 6,  REG_DWORD,   REG_DWORD },
254         { L"hex(",   4,  -1,          REG_BINARY }, /* REG_NONE, REG_EXPAND_SZ, REG_MULTI_SZ */
255         { NULL,      0,  0,           0 }
256     };
257 
258     const struct data_type *ptr;
259 
260     for (ptr = data_types; ptr->tag; ptr++)
261     {
262         if (wcsncmp(ptr->tag, *line, ptr->len))
263             continue;
264 
265         parser->parse_type = ptr->parse_type;
266         parser->data_type = ptr->parse_type;
267         *line += ptr->len;
268 
269         if (ptr->type == -1)
270         {
271             WCHAR *end;
272             DWORD val;
273 
274             if (!**line || towlower((*line)[1]) == 'x')
275                 return FALSE;
276 
277             /* "hex(xx):" is special */
278             val = wcstoul(*line, &end, 16);
279             if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE))
280                 return FALSE;
281 
282             parser->data_type = val;
283             *line = end + 2;
284         }
285         return TRUE;
286     }
287     return FALSE;
288 }
289 
290 /******************************************************************************
291  * Replaces escape sequences with their character equivalents and
292  * null-terminates the string on the first non-escaped double quote.
293  *
294  * Assigns a pointer to the remaining unparsed data in the line.
295  * Returns TRUE or FALSE to indicate whether a closing double quote was found.
296  */
297 static BOOL unescape_string(WCHAR *str, WCHAR **unparsed)
298 {
299     int str_idx = 0;            /* current character under analysis */
300     int val_idx = 0;            /* the last character of the unescaped string */
301     int len = lstrlenW(str);
302     BOOL ret;
303 
304     for (str_idx = 0; str_idx < len; str_idx++, val_idx++)
305     {
306         if (str[str_idx] == '\\')
307         {
308             str_idx++;
309             switch (str[str_idx])
310             {
311             case 'n':
312                 str[val_idx] = '\n';
313                 break;
314             case 'r':
315                 str[val_idx] = '\r';
316                 break;
317             case '0':
318                 return FALSE;
319             case '\\':
320             case '"':
321                 str[val_idx] = str[str_idx];
322                 break;
323             default:
324                 if (!str[str_idx]) return FALSE;
325                 output_message(STRING_ESCAPE_SEQUENCE, str[str_idx]);
326                 str[val_idx] = str[str_idx];
327                 break;
328             }
329         }
330         else if (str[str_idx] == '"')
331             break;
332         else
333             str[val_idx] = str[str_idx];
334     }
335 
336     ret = (str[str_idx] == '"');
337     *unparsed = str + str_idx + 1;
338     str[val_idx] = '\0';
339     return ret;
340 }
341 
342 static HKEY parse_key_name(WCHAR *key_name, WCHAR **key_path)
343 {
344     if (!key_name) return 0;
345 
346     *key_path = wcschr(key_name, '\\');
347     if (*key_path) (*key_path)++;
348 
349     return path_get_rootkey(key_name);
350 }
351 
352 static void close_key(struct parser *parser)
353 {
354     if (parser->hkey)
355     {
356         free(parser->key_name);
357         parser->key_name = NULL;
358 
359         RegCloseKey(parser->hkey);
360         parser->hkey = NULL;
361     }
362 }
363 
364 static LONG open_key(struct parser *parser, WCHAR *path)
365 {
366     HKEY key_class;
367     WCHAR *key_path;
368     LONG res;
369 
370     close_key(parser);
371 
372     /* Get the registry class */
373     if (!path || !(key_class = parse_key_name(path, &key_path)))
374         return ERROR_INVALID_PARAMETER;
375 
376     res = RegCreateKeyExW(key_class, key_path, 0, NULL, REG_OPTION_NON_VOLATILE,
377                           KEY_ALL_ACCESS|parser->sam, NULL, &parser->hkey, NULL);
378 
379     if (res == ERROR_SUCCESS)
380     {
381         parser->key_name = malloc((lstrlenW(path) + 1) * sizeof(WCHAR));
382         lstrcpyW(parser->key_name, path);
383     }
384     else
385         parser->hkey = NULL;
386 
387     return res;
388 }
389 
390 static void free_parser_data(struct parser *parser)
391 {
392     if (parser->parse_type == REG_DWORD || parser->parse_type == REG_BINARY)
393         free(parser->data);
394 
395     parser->data = NULL;
396     parser->data_size = 0;
397 }
398 
399 static void prepare_hex_string_data(struct parser *parser)
400 {
401     if (parser->data_type == REG_EXPAND_SZ || parser->data_type == REG_MULTI_SZ ||
402         parser->data_type == REG_SZ)
403     {
404         if (parser->is_unicode)
405         {
406             WCHAR *data = parser->data;
407             DWORD len = parser->data_size / sizeof(WCHAR);
408 
409             if (data[len - 1] != 0)
410             {
411                 data[len] = 0;
412                 parser->data_size += sizeof(WCHAR);
413             }
414         }
415         else
416         {
417             BYTE *data = parser->data;
418 
419             if (data[parser->data_size - 1] != 0)
420             {
421                 data[parser->data_size] = 0;
422                 parser->data_size++;
423             }
424 
425             parser->data = GetWideStringN(parser->data, parser->data_size, &parser->data_size);
426             parser->data_size *= sizeof(WCHAR);
427             free(data);
428         }
429     }
430 }
431 
432 enum reg_versions {
433     REG_VERSION_31,
434     REG_VERSION_40,
435     REG_VERSION_50,
436     REG_VERSION_FUZZY,
437     REG_VERSION_INVALID
438 };
439 
440 static enum reg_versions parse_file_header(const WCHAR *s)
441 {
442     static const WCHAR *header_31 = L"REGEDIT";
443 
444     while (*s == ' ' || *s == '\t') s++;
445 
446     if (!lstrcmpW(s, header_31))
447         return REG_VERSION_31;
448 
449     if (!lstrcmpW(s, L"REGEDIT4"))
450         return REG_VERSION_40;
451 
452     if (!lstrcmpW(s, L"Windows Registry Editor Version 5.00"))
453         return REG_VERSION_50;
454 
455     /* The Windows version accepts registry file headers beginning with "REGEDIT" and ending
456      * with other characters, as long as "REGEDIT" appears at the start of the line. For example,
457      * "REGEDIT 4", "REGEDIT9" and "REGEDIT4FOO" are all treated as valid file headers.
458      * In all such cases, however, the contents of the registry file are not imported.
459      */
460     if (!wcsncmp(s, header_31, 7)) /* "REGEDIT" without NUL */
461         return REG_VERSION_FUZZY;
462 
463     return REG_VERSION_INVALID;
464 }
465 
466 /* handler for parser HEADER state */
467 static WCHAR *header_state(struct parser *parser, WCHAR *pos)
468 {
469     WCHAR *line, *header;
470 
471     if (!(line = get_line(parser->file)))
472         return NULL;
473 
474     if (!parser->is_unicode)
475     {
476         header = malloc((lstrlenW(line) + 3) * sizeof(WCHAR));
477         header[0] = parser->two_wchars[0];
478         header[1] = parser->two_wchars[1];
479         lstrcpyW(header + 2, line);
480         parser->reg_version = parse_file_header(header);
481         free(header);
482     }
483     else parser->reg_version = parse_file_header(line);
484 
485     switch (parser->reg_version)
486     {
487     case REG_VERSION_31:
488         set_state(parser, PARSE_WIN31_LINE);
489         break;
490     case REG_VERSION_40:
491     case REG_VERSION_50:
492         set_state(parser, LINE_START);
493         break;
494     default:
495         get_line(NULL); /* Reset static variables */
496         return NULL;
497     }
498 
499     return line;
500 }
501 
502 /* handler for parser PARSE_WIN31_LINE state */
503 static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos)
504 {
505     WCHAR *line, *value;
506     unsigned int key_end = 0;
507 
508     if (!(line = get_line(parser->file)))
509         return NULL;
510 
511     if (wcsncmp(line, L"HKEY_CLASSES_ROOT", 17)) /* "HKEY_CLASSES_ROOT" without NUL */
512         return line;
513 
514     /* get key name */
515     while (line[key_end] && !iswspace(line[key_end])) key_end++;
516 
517     value = line + key_end;
518     while (*value == ' ' || *value == '\t') value++;
519 
520     if (*value == '=') value++;
521     if (*value == ' ') value++; /* at most one space is skipped */
522 
523     line[key_end] = 0;
524 
525     if (open_key(parser, line) != ERROR_SUCCESS)
526     {
527         output_message(STRING_KEY_IMPORT_FAILED, line);
528         return line;
529     }
530 
531     parser->value_name = NULL;
532     parser->data_type = REG_SZ;
533     parser->data = value;
534     parser->data_size = (lstrlenW(value) + 1) * sizeof(WCHAR);
535 
536     set_state(parser, SET_VALUE);
537     return value;
538 }
539 
540 /* handler for parser LINE_START state */
541 static WCHAR *line_start_state(struct parser *parser, WCHAR *pos)
542 {
543     WCHAR *line, *p;
544 
545     if (!(line = get_line(parser->file)))
546         return NULL;
547 
548     for (p = line; *p; p++)
549     {
550         switch (*p)
551         {
552         case '[':
553             set_state(parser, KEY_NAME);
554             return p + 1;
555         case '@':
556             set_state(parser, DEFAULT_VALUE_NAME);
557             return p;
558         case '"':
559             set_state(parser, QUOTED_VALUE_NAME);
560             return p + 1;
561         case ' ':
562         case '\t':
563             break;
564         default:
565             return p;
566         }
567     }
568 
569     return p;
570 }
571 
572 /* handler for parser KEY_NAME state */
573 static WCHAR *key_name_state(struct parser *parser, WCHAR *pos)
574 {
575     WCHAR *p = pos, *key_end;
576 
577     if (*p == ' ' || *p == '\t' || !(key_end = wcsrchr(p, ']')))
578         goto done;
579 
580     *key_end = 0;
581 
582     if (*p == '-')
583     {
584         set_state(parser, DELETE_KEY);
585         return p + 1;
586     }
587     else if (open_key(parser, p) != ERROR_SUCCESS)
588         output_message(STRING_KEY_IMPORT_FAILED, p);
589 
590 done:
591     set_state(parser, LINE_START);
592     return p;
593 }
594 
595 /* handler for parser DELETE_KEY state */
596 static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos)
597 {
598     WCHAR *p = pos;
599 
600     close_key(parser);
601 
602     if (*p == 'H' || *p == 'h')
603     {
604         HKEY root;
605         WCHAR *path;
606 
607         root = parse_key_name(p, &path);
608 
609         if (root && path && *path)
610             RegDeleteTreeW(root, path);
611     }
612 
613     set_state(parser, LINE_START);
614     return p;
615 }
616 
617 /* handler for parser DEFAULT_VALUE_NAME state */
618 static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos)
619 {
620     free(parser->value_name);
621     parser->value_name = NULL;
622 
623     set_state(parser, DATA_START);
624     return pos + 1;
625 }
626 
627 /* handler for parser QUOTED_VALUE_NAME state */
628 static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos)
629 {
630     WCHAR *val_name = pos, *p;
631 
632     free(parser->value_name);
633     parser->value_name = NULL;
634 
635     if (!unescape_string(val_name, &p))
636         goto invalid;
637 
638     /* copy the value name in case we need to parse multiple lines and the buffer is overwritten */
639     parser->value_name = malloc((lstrlenW(val_name) + 1) * sizeof(WCHAR));
640     lstrcpyW(parser->value_name, val_name);
641 
642     set_state(parser, DATA_START);
643     return p;
644 
645 invalid:
646     set_state(parser, LINE_START);
647     return val_name;
648 }
649 
650 /* handler for parser DATA_START state */
651 static WCHAR *data_start_state(struct parser *parser, WCHAR *pos)
652 {
653     WCHAR *p = pos;
654     unsigned int len;
655 
656     while (*p == ' ' || *p == '\t') p++;
657     if (*p != '=') goto invalid;
658     p++;
659     while (*p == ' ' || *p == '\t') p++;
660 
661     /* trim trailing whitespace */
662     len = lstrlenW(p);
663     while (len > 0 && (p[len - 1] == ' ' || p[len - 1] == '\t')) len--;
664     p[len] = 0;
665 
666     if (*p == '-')
667         set_state(parser, DELETE_VALUE);
668     else
669         set_state(parser, DATA_TYPE);
670     return p;
671 
672 invalid:
673     set_state(parser, LINE_START);
674     return p;
675 }
676 
677 /* handler for parser DELETE_VALUE state */
678 static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos)
679 {
680     WCHAR *p = pos + 1;
681 
682     while (*p == ' ' || *p == '\t') p++;
683     if (*p && *p != ';') goto done;
684 
685     RegDeleteValueW(parser->hkey, parser->value_name);
686 
687 done:
688     set_state(parser, LINE_START);
689     return p;
690 }
691 
692 /* handler for parser DATA_TYPE state */
693 static WCHAR *data_type_state(struct parser *parser, WCHAR *pos)
694 {
695     WCHAR *line = pos;
696 
697     if (!parse_data_type(parser, &line))
698     {
699         set_state(parser, LINE_START);
700         return line;
701     }
702 
703     switch (parser->parse_type)
704     {
705     case REG_SZ:
706         set_state(parser, STRING_DATA);
707         break;
708     case REG_DWORD:
709         set_state(parser, DWORD_DATA);
710         break;
711     case REG_BINARY: /* all hex data types, including undefined */
712         set_state(parser, HEX_DATA);
713         break;
714     default:
715         set_state(parser, UNKNOWN_DATA);
716     }
717 
718     return line;
719 }
720 
721 /* handler for parser STRING_DATA state */
722 static WCHAR *string_data_state(struct parser *parser, WCHAR *pos)
723 {
724     WCHAR *line;
725 
726     parser->data = pos;
727 
728     if (!unescape_string(parser->data, &line))
729         goto invalid;
730 
731     while (*line == ' ' || *line == '\t') line++;
732     if (*line && *line != ';') goto invalid;
733 
734     parser->data_size = (lstrlenW(parser->data) + 1) * sizeof(WCHAR);
735 
736     set_state(parser, SET_VALUE);
737     return line;
738 
739 invalid:
740     free_parser_data(parser);
741     set_state(parser, LINE_START);
742     return line;
743 }
744 
745 /* handler for parser DWORD_DATA state */
746 static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos)
747 {
748     WCHAR *line = pos;
749 
750     parser->data = malloc(sizeof(DWORD));
751 
752     if (!convert_hex_to_dword(line, parser->data))
753         goto invalid;
754 
755     parser->data_size = sizeof(DWORD);
756 
757     set_state(parser, SET_VALUE);
758     return line;
759 
760 invalid:
761     free_parser_data(parser);
762     set_state(parser, LINE_START);
763     return line;
764 }
765 
766 /* handler for parser HEX_DATA state */
767 static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos)
768 {
769     WCHAR *line = pos;
770 
771     if (!*line)
772         goto set_value;
773 
774     if (!convert_hex_csv_to_hex(parser, &line))
775         goto invalid;
776 
777     if (parser->backslash)
778     {
779         set_state(parser, EOL_BACKSLASH);
780         return line;
781     }
782 
783     prepare_hex_string_data(parser);
784 
785 set_value:
786     set_state(parser, SET_VALUE);
787     return line;
788 
789 invalid:
790     free_parser_data(parser);
791     set_state(parser, LINE_START);
792     return line;
793 }
794 
795 /* handler for parser EOL_BACKSLASH state */
796 static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos)
797 {
798     WCHAR *p = pos;
799 
800     while (*p == ' ' || *p == '\t') p++;
801     if (*p && *p != ';') goto invalid;
802 
803     set_state(parser, HEX_MULTILINE);
804     return pos;
805 
806 invalid:
807     free_parser_data(parser);
808     set_state(parser, LINE_START);
809     return p;
810 }
811 
812 /* handler for parser HEX_MULTILINE state */
813 static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos)
814 {
815     WCHAR *line;
816 
817     if (!(line = get_line(parser->file)))
818     {
819         prepare_hex_string_data(parser);
820         set_state(parser, SET_VALUE);
821         return pos;
822     }
823 
824     while (*line == ' ' || *line == '\t') line++;
825     if (!*line || *line == ';') return line;
826 
827     if (!iswxdigit(*line)) goto invalid;
828 
829     set_state(parser, HEX_DATA);
830     return line;
831 
832 invalid:
833     free_parser_data(parser);
834     set_state(parser, LINE_START);
835     return line;
836 }
837 
838 /* handler for parser UNKNOWN_DATA state */
839 static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos)
840 {
841     FIXME("Unknown registry data type [0x%x]\n", parser->data_type);
842 
843     set_state(parser, LINE_START);
844     return pos;
845 }
846 
847 /* handler for parser SET_VALUE state */
848 static WCHAR *set_value_state(struct parser *parser, WCHAR *pos)
849 {
850     RegSetValueExW(parser->hkey, parser->value_name, 0, parser->data_type,
851                    parser->data, parser->data_size);
852 
853     free_parser_data(parser);
854 
855     if (parser->reg_version == REG_VERSION_31)
856         set_state(parser, PARSE_WIN31_LINE);
857     else
858         set_state(parser, LINE_START);
859 
860     return pos;
861 }
862 
863 #define REG_VAL_BUF_SIZE  4096
864 
865 static WCHAR *get_lineA(FILE *fp)
866 {
867     static WCHAR *lineW;
868     static size_t size;
869     static char *buf, *next;
870     char *line;
871 
872     free(lineW);
873 
874     if (!fp) goto cleanup;
875 
876     if (!size)
877     {
878         size = REG_VAL_BUF_SIZE;
879         buf = malloc(size);
880         *buf = 0;
881         next = buf;
882     }
883     line = next;
884 
885     while (next)
886     {
887         char *p = strpbrk(line, "\r\n");
888         if (!p)
889         {
890             size_t len, count;
891             len = strlen(next);
892             memmove(buf, next, len + 1);
893             if (size - len < 3)
894             {
895                 size *= 2;
896                 buf = realloc(buf, size);
897             }
898             if (!(count = fread(buf + len, 1, size - len - 1, fp)))
899             {
900                 next = NULL;
901                 lineW = GetWideString(buf);
902                 return lineW;
903             }
904             buf[len + count] = 0;
905             next = buf;
906             line = buf;
907             continue;
908         }
909         next = p + 1;
910         if (*p == '\r' && *(p + 1) == '\n') next++;
911         *p = 0;
912         lineW = GetWideString(line);
913         return lineW;
914     }
915 
916 cleanup:
917     lineW = NULL;
918     free(buf);
919     size = 0;
920     return NULL;
921 }
922 
923 static WCHAR *get_lineW(FILE *fp)
924 {
925     static size_t size;
926     static WCHAR *buf, *next;
927     WCHAR *line;
928 
929     if (!fp) goto cleanup;
930 
931     if (!size)
932     {
933         size = REG_VAL_BUF_SIZE;
934         buf = malloc(size * sizeof(WCHAR));
935         *buf = 0;
936         next = buf;
937     }
938     line = next;
939 
940     while (next)
941     {
942         WCHAR *p = wcspbrk(line, L"\r\n");
943         if (!p)
944         {
945             size_t len, count;
946             len = lstrlenW(next);
947             memmove(buf, next, (len + 1) * sizeof(WCHAR));
948             if (size - len < 3)
949             {
950                 size *= 2;
951                 buf = realloc(buf, size * sizeof(WCHAR));
952             }
953             if (!(count = fread(buf + len, sizeof(WCHAR), size - len - 1, fp)))
954             {
955                 next = NULL;
956                 return buf;
957             }
958             buf[len + count] = 0;
959             next = buf;
960             line = buf;
961             continue;
962         }
963         next = p + 1;
964         if (*p == '\r' && *(p + 1) == '\n') next++;
965         *p = 0;
966         return line;
967     }
968 
969 cleanup:
970     free(buf);
971     size = 0;
972     return NULL;
973 }
974 
975 int reg_import(int argc, WCHAR *argvW[])
976 {
977     WCHAR *filename, *pos;
978     FILE *fp;
979     BYTE s[2];
980     struct parser parser;
981 
982     if (argc > 4) goto invalid;
983 
984     parser.sam = 0;
985 
986     if (argc == 4)
987     {
988         WCHAR *str = argvW[3];
989 
990         if (*str != '/' && *str != '-')
991             goto invalid;
992 
993         str++;
994 
995         if (!lstrcmpiW(str, L"reg:32"))
996             parser.sam = KEY_WOW64_32KEY;
997         else if (!lstrcmpiW(str, L"reg:64"))
998             parser.sam = KEY_WOW64_64KEY;
999         else
1000             goto invalid;
1001     }
1002 
1003     filename = argvW[2];
1004 
1005     fp = _wfopen(filename, L"rb");
1006     if (!fp)
1007     {
1008         output_message(STRING_FILE_NOT_FOUND, filename);
1009         return 1;
1010     }
1011 
1012     if (fread(s, sizeof(WCHAR), 1, fp) != 1)
1013         goto error;
1014 
1015     parser.is_unicode = (s[0] == 0xff && s[1] == 0xfe);
1016     get_line = parser.is_unicode ? get_lineW : get_lineA;
1017 
1018     parser.file          = fp;
1019     parser.two_wchars[0] = s[0];
1020     parser.two_wchars[1] = s[1];
1021     parser.reg_version   = -1;
1022     parser.hkey          = NULL;
1023     parser.key_name      = NULL;
1024     parser.value_name    = NULL;
1025     parser.parse_type    = 0;
1026     parser.data_type     = 0;
1027     parser.data          = NULL;
1028     parser.data_size     = 0;
1029     parser.backslash     = FALSE;
1030     parser.state         = HEADER;
1031 
1032     pos = parser.two_wchars;
1033 
1034     /* parser main loop */
1035     while (pos)
1036         pos = (parser_funcs[parser.state])(&parser, pos);
1037 
1038     if (parser.reg_version == REG_VERSION_INVALID)
1039         goto error;
1040 
1041     free(parser.value_name);
1042     close_key(&parser);
1043 
1044     fclose(fp);
1045     return 0;
1046 
1047 error:
1048     fclose(fp);
1049     return 1;
1050 
1051 invalid:
1052     output_message(STRING_INVALID_SYNTAX);
1053     output_message(STRING_FUNC_HELP, _wcsupr(argvW[1]));
1054     return 1;
1055 }
1056