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 #ifdef __REACTOS__
280             /* Up to 8 hex digits, "hex(000000002)" is invalid */
281             if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE) || end - *line > 8)
282 #else
283             if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE))
284 #endif
285                 return FALSE;
286 
287             parser->data_type = val;
288             *line = end + 2;
289         }
290         return TRUE;
291     }
292     return FALSE;
293 }
294 
295 /******************************************************************************
296  * Replaces escape sequences with their character equivalents and
297  * null-terminates the string on the first non-escaped double quote.
298  *
299  * Assigns a pointer to the remaining unparsed data in the line.
300  * Returns TRUE or FALSE to indicate whether a closing double quote was found.
301  */
302 static BOOL unescape_string(WCHAR *str, WCHAR **unparsed)
303 {
304     int str_idx = 0;            /* current character under analysis */
305     int val_idx = 0;            /* the last character of the unescaped string */
306     int len = lstrlenW(str);
307     BOOL ret;
308 
309     for (str_idx = 0; str_idx < len; str_idx++, val_idx++)
310     {
311         if (str[str_idx] == '\\')
312         {
313             str_idx++;
314             switch (str[str_idx])
315             {
316             case 'n':
317                 str[val_idx] = '\n';
318                 break;
319             case 'r':
320                 str[val_idx] = '\r';
321                 break;
322             case '0':
323                 return FALSE;
324             case '\\':
325             case '"':
326                 str[val_idx] = str[str_idx];
327                 break;
328             default:
329                 if (!str[str_idx]) return FALSE;
330                 output_message(STRING_ESCAPE_SEQUENCE, str[str_idx]);
331                 str[val_idx] = str[str_idx];
332                 break;
333             }
334         }
335         else if (str[str_idx] == '"')
336             break;
337         else
338             str[val_idx] = str[str_idx];
339     }
340 
341     ret = (str[str_idx] == '"');
342     *unparsed = str + str_idx + 1;
343     str[val_idx] = '\0';
344     return ret;
345 }
346 
347 static HKEY parse_key_name(WCHAR *key_name, WCHAR **key_path)
348 {
349     if (!key_name) return 0;
350 
351     *key_path = wcschr(key_name, '\\');
352     if (*key_path) (*key_path)++;
353 
354     return path_get_rootkey(key_name);
355 }
356 
357 static void close_key(struct parser *parser)
358 {
359     if (parser->hkey)
360     {
361         free(parser->key_name);
362         parser->key_name = NULL;
363 
364         RegCloseKey(parser->hkey);
365         parser->hkey = NULL;
366     }
367 }
368 
369 static LONG open_key(struct parser *parser, WCHAR *path)
370 {
371     HKEY key_class;
372     WCHAR *key_path;
373     LONG res;
374 
375     close_key(parser);
376 
377     /* Get the registry class */
378     if (!path || !(key_class = parse_key_name(path, &key_path)))
379         return ERROR_INVALID_PARAMETER;
380 
381     res = RegCreateKeyExW(key_class, key_path, 0, NULL, REG_OPTION_NON_VOLATILE,
382                           KEY_ALL_ACCESS|parser->sam, NULL, &parser->hkey, NULL);
383 
384     if (res == ERROR_SUCCESS)
385     {
386         parser->key_name = malloc((lstrlenW(path) + 1) * sizeof(WCHAR));
387         lstrcpyW(parser->key_name, path);
388     }
389     else
390         parser->hkey = NULL;
391 
392     return res;
393 }
394 
395 static void free_parser_data(struct parser *parser)
396 {
397     if (parser->parse_type == REG_DWORD || parser->parse_type == REG_BINARY)
398         free(parser->data);
399 
400     parser->data = NULL;
401     parser->data_size = 0;
402 }
403 
404 static void prepare_hex_string_data(struct parser *parser)
405 {
406     if (parser->data_type == REG_EXPAND_SZ || parser->data_type == REG_MULTI_SZ ||
407         parser->data_type == REG_SZ)
408     {
409         if (parser->is_unicode)
410         {
411             WCHAR *data = parser->data;
412             DWORD len = parser->data_size / sizeof(WCHAR);
413 
414             if (data[len - 1] != 0)
415             {
416                 data[len] = 0;
417                 parser->data_size += sizeof(WCHAR);
418             }
419         }
420         else
421         {
422             BYTE *data = parser->data;
423 
424             if (data[parser->data_size - 1] != 0)
425             {
426                 data[parser->data_size] = 0;
427                 parser->data_size++;
428             }
429 
430             parser->data = GetWideStringN(parser->data, parser->data_size, &parser->data_size);
431             parser->data_size *= sizeof(WCHAR);
432             free(data);
433         }
434     }
435 }
436 
437 enum reg_versions {
438     REG_VERSION_31,
439     REG_VERSION_40,
440     REG_VERSION_50,
441     REG_VERSION_FUZZY,
442     REG_VERSION_INVALID
443 };
444 
445 static enum reg_versions parse_file_header(const WCHAR *s)
446 {
447     static const WCHAR *header_31 = L"REGEDIT";
448 
449     while (*s == ' ' || *s == '\t') s++;
450 
451     if (!lstrcmpW(s, header_31))
452         return REG_VERSION_31;
453 
454     if (!lstrcmpW(s, L"REGEDIT4"))
455         return REG_VERSION_40;
456 
457     if (!lstrcmpW(s, L"Windows Registry Editor Version 5.00"))
458         return REG_VERSION_50;
459 
460     /* The Windows version accepts registry file headers beginning with "REGEDIT" and ending
461      * with other characters, as long as "REGEDIT" appears at the start of the line. For example,
462      * "REGEDIT 4", "REGEDIT9" and "REGEDIT4FOO" are all treated as valid file headers.
463      * In all such cases, however, the contents of the registry file are not imported.
464      */
465     if (!wcsncmp(s, header_31, 7)) /* "REGEDIT" without NUL */
466         return REG_VERSION_FUZZY;
467 
468     return REG_VERSION_INVALID;
469 }
470 
471 /* handler for parser HEADER state */
472 static WCHAR *header_state(struct parser *parser, WCHAR *pos)
473 {
474     WCHAR *line, *header;
475 
476     if (!(line = get_line(parser->file)))
477         return NULL;
478 
479     if (!parser->is_unicode)
480     {
481         header = malloc((lstrlenW(line) + 3) * sizeof(WCHAR));
482         header[0] = parser->two_wchars[0];
483         header[1] = parser->two_wchars[1];
484         lstrcpyW(header + 2, line);
485         parser->reg_version = parse_file_header(header);
486         free(header);
487     }
488     else parser->reg_version = parse_file_header(line);
489 
490     switch (parser->reg_version)
491     {
492     case REG_VERSION_31:
493         set_state(parser, PARSE_WIN31_LINE);
494         break;
495     case REG_VERSION_40:
496     case REG_VERSION_50:
497         set_state(parser, LINE_START);
498         break;
499     default:
500         get_line(NULL); /* Reset static variables */
501         return NULL;
502     }
503 
504     return line;
505 }
506 
507 /* handler for parser PARSE_WIN31_LINE state */
508 static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos)
509 {
510     WCHAR *line, *value;
511     unsigned int key_end = 0;
512 
513     if (!(line = get_line(parser->file)))
514         return NULL;
515 
516     if (wcsncmp(line, L"HKEY_CLASSES_ROOT", 17)) /* "HKEY_CLASSES_ROOT" without NUL */
517         return line;
518 
519     /* get key name */
520     while (line[key_end] && !iswspace(line[key_end])) key_end++;
521 
522     value = line + key_end;
523     while (*value == ' ' || *value == '\t') value++;
524 
525     if (*value == '=') value++;
526     if (*value == ' ') value++; /* at most one space is skipped */
527 
528     line[key_end] = 0;
529 
530     if (open_key(parser, line) != ERROR_SUCCESS)
531     {
532         output_message(STRING_KEY_IMPORT_FAILED, line);
533         return line;
534     }
535 
536     parser->value_name = NULL;
537     parser->data_type = REG_SZ;
538     parser->data = value;
539     parser->data_size = (lstrlenW(value) + 1) * sizeof(WCHAR);
540 
541     set_state(parser, SET_VALUE);
542     return value;
543 }
544 
545 /* handler for parser LINE_START state */
546 static WCHAR *line_start_state(struct parser *parser, WCHAR *pos)
547 {
548     WCHAR *line, *p;
549 
550     if (!(line = get_line(parser->file)))
551         return NULL;
552 
553     for (p = line; *p; p++)
554     {
555         switch (*p)
556         {
557         case '[':
558             set_state(parser, KEY_NAME);
559             return p + 1;
560         case '@':
561             set_state(parser, DEFAULT_VALUE_NAME);
562             return p;
563         case '"':
564             set_state(parser, QUOTED_VALUE_NAME);
565             return p + 1;
566         case ' ':
567         case '\t':
568             break;
569         default:
570             return p;
571         }
572     }
573 
574     return p;
575 }
576 
577 /* handler for parser KEY_NAME state */
578 static WCHAR *key_name_state(struct parser *parser, WCHAR *pos)
579 {
580     WCHAR *p = pos, *key_end;
581 
582     if (*p == ' ' || *p == '\t' || !(key_end = wcsrchr(p, ']')))
583         goto done;
584 
585     *key_end = 0;
586 
587     if (*p == '-')
588     {
589         set_state(parser, DELETE_KEY);
590         return p + 1;
591     }
592     else if (open_key(parser, p) != ERROR_SUCCESS)
593         output_message(STRING_KEY_IMPORT_FAILED, p);
594 
595 done:
596     set_state(parser, LINE_START);
597     return p;
598 }
599 
600 /* handler for parser DELETE_KEY state */
601 static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos)
602 {
603     WCHAR *p = pos;
604 
605     close_key(parser);
606 
607     if (*p == 'H' || *p == 'h')
608     {
609         HKEY root;
610         WCHAR *path;
611 
612         root = parse_key_name(p, &path);
613 
614         if (root && path && *path)
615             RegDeleteTreeW(root, path);
616     }
617 
618     set_state(parser, LINE_START);
619     return p;
620 }
621 
622 /* handler for parser DEFAULT_VALUE_NAME state */
623 static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos)
624 {
625     free(parser->value_name);
626     parser->value_name = NULL;
627 
628     set_state(parser, DATA_START);
629     return pos + 1;
630 }
631 
632 /* handler for parser QUOTED_VALUE_NAME state */
633 static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos)
634 {
635     WCHAR *val_name = pos, *p;
636 
637     free(parser->value_name);
638     parser->value_name = NULL;
639 
640     if (!unescape_string(val_name, &p))
641         goto invalid;
642 
643     /* copy the value name in case we need to parse multiple lines and the buffer is overwritten */
644     parser->value_name = malloc((lstrlenW(val_name) + 1) * sizeof(WCHAR));
645     lstrcpyW(parser->value_name, val_name);
646 
647     set_state(parser, DATA_START);
648     return p;
649 
650 invalid:
651     set_state(parser, LINE_START);
652     return val_name;
653 }
654 
655 /* handler for parser DATA_START state */
656 static WCHAR *data_start_state(struct parser *parser, WCHAR *pos)
657 {
658     WCHAR *p = pos;
659     unsigned int len;
660 
661     while (*p == ' ' || *p == '\t') p++;
662     if (*p != '=') goto invalid;
663     p++;
664     while (*p == ' ' || *p == '\t') p++;
665 
666     /* trim trailing whitespace */
667     len = lstrlenW(p);
668     while (len > 0 && (p[len - 1] == ' ' || p[len - 1] == '\t')) len--;
669     p[len] = 0;
670 
671     if (*p == '-')
672         set_state(parser, DELETE_VALUE);
673     else
674         set_state(parser, DATA_TYPE);
675     return p;
676 
677 invalid:
678     set_state(parser, LINE_START);
679     return p;
680 }
681 
682 /* handler for parser DELETE_VALUE state */
683 static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos)
684 {
685     WCHAR *p = pos + 1;
686 
687     while (*p == ' ' || *p == '\t') p++;
688     if (*p && *p != ';') goto done;
689 
690     RegDeleteValueW(parser->hkey, parser->value_name);
691 
692 done:
693     set_state(parser, LINE_START);
694     return p;
695 }
696 
697 /* handler for parser DATA_TYPE state */
698 static WCHAR *data_type_state(struct parser *parser, WCHAR *pos)
699 {
700     WCHAR *line = pos;
701 
702     if (!parse_data_type(parser, &line))
703     {
704         set_state(parser, LINE_START);
705         return line;
706     }
707 
708     switch (parser->parse_type)
709     {
710     case REG_SZ:
711         set_state(parser, STRING_DATA);
712         break;
713     case REG_DWORD:
714         set_state(parser, DWORD_DATA);
715         break;
716     case REG_BINARY: /* all hex data types, including undefined */
717         set_state(parser, HEX_DATA);
718         break;
719     default:
720         set_state(parser, UNKNOWN_DATA);
721     }
722 
723     return line;
724 }
725 
726 /* handler for parser STRING_DATA state */
727 static WCHAR *string_data_state(struct parser *parser, WCHAR *pos)
728 {
729     WCHAR *line;
730 
731     parser->data = pos;
732 
733     if (!unescape_string(parser->data, &line))
734         goto invalid;
735 
736     while (*line == ' ' || *line == '\t') line++;
737     if (*line && *line != ';') goto invalid;
738 
739     parser->data_size = (lstrlenW(parser->data) + 1) * sizeof(WCHAR);
740 
741     set_state(parser, SET_VALUE);
742     return line;
743 
744 invalid:
745     free_parser_data(parser);
746     set_state(parser, LINE_START);
747     return line;
748 }
749 
750 /* handler for parser DWORD_DATA state */
751 static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos)
752 {
753     WCHAR *line = pos;
754 
755     parser->data = malloc(sizeof(DWORD));
756 
757     if (!convert_hex_to_dword(line, parser->data))
758         goto invalid;
759 
760     parser->data_size = sizeof(DWORD);
761 
762     set_state(parser, SET_VALUE);
763     return line;
764 
765 invalid:
766     free_parser_data(parser);
767     set_state(parser, LINE_START);
768     return line;
769 }
770 
771 /* handler for parser HEX_DATA state */
772 static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos)
773 {
774     WCHAR *line = pos;
775 
776     if (!*line)
777         goto set_value;
778 
779     if (!convert_hex_csv_to_hex(parser, &line))
780         goto invalid;
781 
782     if (parser->backslash)
783     {
784         set_state(parser, EOL_BACKSLASH);
785         return line;
786     }
787 
788     prepare_hex_string_data(parser);
789 
790 set_value:
791     set_state(parser, SET_VALUE);
792     return line;
793 
794 invalid:
795     free_parser_data(parser);
796     set_state(parser, LINE_START);
797     return line;
798 }
799 
800 /* handler for parser EOL_BACKSLASH state */
801 static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos)
802 {
803     WCHAR *p = pos;
804 
805     while (*p == ' ' || *p == '\t') p++;
806     if (*p && *p != ';') goto invalid;
807 
808     set_state(parser, HEX_MULTILINE);
809     return pos;
810 
811 invalid:
812     free_parser_data(parser);
813     set_state(parser, LINE_START);
814     return p;
815 }
816 
817 /* handler for parser HEX_MULTILINE state */
818 static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos)
819 {
820     WCHAR *line;
821 
822     if (!(line = get_line(parser->file)))
823     {
824         prepare_hex_string_data(parser);
825         set_state(parser, SET_VALUE);
826         return pos;
827     }
828 
829     while (*line == ' ' || *line == '\t') line++;
830     if (!*line || *line == ';') return line;
831 
832     if (!iswxdigit(*line)) goto invalid;
833 
834     set_state(parser, HEX_DATA);
835     return line;
836 
837 invalid:
838     free_parser_data(parser);
839     set_state(parser, LINE_START);
840     return line;
841 }
842 
843 /* handler for parser UNKNOWN_DATA state */
844 static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos)
845 {
846     FIXME("Unknown registry data type [0x%x]\n", parser->data_type);
847 
848     set_state(parser, LINE_START);
849     return pos;
850 }
851 
852 /* handler for parser SET_VALUE state */
853 static WCHAR *set_value_state(struct parser *parser, WCHAR *pos)
854 {
855     RegSetValueExW(parser->hkey, parser->value_name, 0, parser->data_type,
856                    parser->data, parser->data_size);
857 
858     free_parser_data(parser);
859 
860     if (parser->reg_version == REG_VERSION_31)
861         set_state(parser, PARSE_WIN31_LINE);
862     else
863         set_state(parser, LINE_START);
864 
865     return pos;
866 }
867 
868 #define REG_VAL_BUF_SIZE  4096
869 
870 static WCHAR *get_lineA(FILE *fp)
871 {
872     static WCHAR *lineW;
873     static size_t size;
874     static char *buf, *next;
875     char *line;
876 
877     free(lineW);
878 
879     if (!fp) goto cleanup;
880 
881     if (!size)
882     {
883         size = REG_VAL_BUF_SIZE;
884         buf = malloc(size);
885         *buf = 0;
886         next = buf;
887     }
888     line = next;
889 
890     while (next)
891     {
892         char *p = strpbrk(line, "\r\n");
893         if (!p)
894         {
895             size_t len, count;
896             len = strlen(next);
897             memmove(buf, next, len + 1);
898             if (size - len < 3)
899             {
900                 size *= 2;
901                 buf = realloc(buf, size);
902             }
903             if (!(count = fread(buf + len, 1, size - len - 1, fp)))
904             {
905                 next = NULL;
906                 lineW = GetWideString(buf);
907                 return lineW;
908             }
909             buf[len + count] = 0;
910             next = buf;
911             line = buf;
912             continue;
913         }
914         next = p + 1;
915         if (*p == '\r' && *(p + 1) == '\n') next++;
916         *p = 0;
917         lineW = GetWideString(line);
918         return lineW;
919     }
920 
921 cleanup:
922     lineW = NULL;
923     free(buf);
924     size = 0;
925     return NULL;
926 }
927 
928 static WCHAR *get_lineW(FILE *fp)
929 {
930     static size_t size;
931     static WCHAR *buf, *next;
932     WCHAR *line;
933 
934     if (!fp) goto cleanup;
935 
936     if (!size)
937     {
938         size = REG_VAL_BUF_SIZE;
939         buf = malloc(size * sizeof(WCHAR));
940         *buf = 0;
941         next = buf;
942     }
943     line = next;
944 
945     while (next)
946     {
947         WCHAR *p = wcspbrk(line, L"\r\n");
948         if (!p)
949         {
950             size_t len, count;
951             len = lstrlenW(next);
952             memmove(buf, next, (len + 1) * sizeof(WCHAR));
953             if (size - len < 3)
954             {
955                 size *= 2;
956                 buf = realloc(buf, size * sizeof(WCHAR));
957             }
958             if (!(count = fread(buf + len, sizeof(WCHAR), size - len - 1, fp)))
959             {
960                 next = NULL;
961                 return buf;
962             }
963             buf[len + count] = 0;
964             next = buf;
965             line = buf;
966             continue;
967         }
968         next = p + 1;
969         if (*p == '\r' && *(p + 1) == '\n') next++;
970         *p = 0;
971         return line;
972     }
973 
974 cleanup:
975     free(buf);
976     size = 0;
977     return NULL;
978 }
979 
980 int reg_import(int argc, WCHAR *argvW[])
981 {
982     WCHAR *filename, *pos;
983     FILE *fp;
984     BYTE s[2];
985     struct parser parser;
986 
987     if (argc > 4) goto invalid;
988 
989     parser.sam = 0;
990 
991     if (argc == 4)
992     {
993         WCHAR *str = argvW[3];
994 
995         if (*str != '/' && *str != '-')
996             goto invalid;
997 
998         str++;
999 
1000         if (!lstrcmpiW(str, L"reg:32"))
1001             parser.sam = KEY_WOW64_32KEY;
1002         else if (!lstrcmpiW(str, L"reg:64"))
1003             parser.sam = KEY_WOW64_64KEY;
1004         else
1005             goto invalid;
1006     }
1007 
1008     filename = argvW[2];
1009 
1010     fp = _wfopen(filename, L"rb");
1011     if (!fp)
1012     {
1013         output_message(STRING_FILE_NOT_FOUND, filename);
1014         return 1;
1015     }
1016 
1017     if (fread(s, sizeof(WCHAR), 1, fp) != 1)
1018         goto error;
1019 
1020     parser.is_unicode = (s[0] == 0xff && s[1] == 0xfe);
1021     get_line = parser.is_unicode ? get_lineW : get_lineA;
1022 
1023     parser.file          = fp;
1024     parser.two_wchars[0] = s[0];
1025     parser.two_wchars[1] = s[1];
1026     parser.reg_version   = -1;
1027     parser.hkey          = NULL;
1028     parser.key_name      = NULL;
1029     parser.value_name    = NULL;
1030     parser.parse_type    = 0;
1031     parser.data_type     = 0;
1032     parser.data          = NULL;
1033     parser.data_size     = 0;
1034     parser.backslash     = FALSE;
1035     parser.state         = HEADER;
1036 
1037     pos = parser.two_wchars;
1038 
1039     /* parser main loop */
1040     while (pos)
1041         pos = (parser_funcs[parser.state])(&parser, pos);
1042 
1043     if (parser.reg_version == REG_VERSION_INVALID)
1044         goto error;
1045 
1046     free(parser.value_name);
1047     close_key(&parser);
1048 
1049     fclose(fp);
1050     return 0;
1051 
1052 error:
1053     fclose(fp);
1054     return 1;
1055 
1056 invalid:
1057     output_message(STRING_INVALID_SYNTAX);
1058     output_message(STRING_FUNC_HELP, _wcsupr(argvW[1]));
1059     return 1;
1060 }
1061