[PATCH 4/7] regedit: Introduce a partial state machine for importing registry data
Hugh McMaster
hugh.mcmaster at outlook.com
Thu Jun 15 07:00:31 CDT 2017
Signed-off-by: Hugh McMaster <hugh.mcmaster at outlook.com>
---
programs/regedit/regproc.c | 265 ++++++++++++++++++++++++++++-----------
programs/regedit/tests/regedit.c | 16 +--
2 files changed, 201 insertions(+), 80 deletions(-)
diff --git a/programs/regedit/regproc.c b/programs/regedit/regproc.c
index ac72b94..15ad04e 100644
--- a/programs/regedit/regproc.c
+++ b/programs/regedit/regproc.c
@@ -128,6 +128,55 @@ static char* GetMultiByteStringN(const WCHAR* strW, int chars, DWORD* len)
return NULL;
}
+static WCHAR *(*get_line)(FILE *);
+
+/* parser definitions */
+enum parser_state
+{
+ HEADER, /* parsing the registry file version header */
+ PARSE_WIN31_LINE, /* parsing a Windows 3.1 registry line */
+ LINE_START, /* at the beginning of a registry line */
+ SET_VALUE, /* adding a value to the registry */
+ NB_PARSER_STATES
+};
+
+struct parser
+{
+ FILE *file; /* pointer to a registry file */
+ WCHAR two_wchars[2]; /* first two characters from the encoding check */
+ BOOL is_unicode; /* parsing Unicode or ASCII data */
+ short int reg_version; /* registry file version */
+ WCHAR *value_name; /* value name */
+ DWORD data_type; /* data type */
+ void *data; /* value data */
+ DWORD data_size; /* size of the data (in bytes) */
+ enum parser_state state; /* current parser state */
+};
+
+typedef WCHAR *(*parser_state_func)(struct parser *parser, WCHAR *pos);
+
+/* parser state machine functions */
+static WCHAR *header_state(struct parser *parser, WCHAR *pos);
+static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos);
+static WCHAR *line_start_state(struct parser *parser, WCHAR *pos);
+static WCHAR *set_value_state(struct parser *parser, WCHAR *pos);
+
+static const parser_state_func parser_funcs[NB_PARSER_STATES] =
+{
+ header_state, /* HEADER */
+ parse_win31_line_state, /* PARSE_WIN31_LINE */
+ line_start_state, /* LINE_START */
+ set_value_state, /* SET_VALUE */
+};
+
+/* set the new parser state and return the previous one */
+static inline enum parser_state set_state(struct parser *parser, enum parser_state state)
+{
+ enum parser_state ret = parser->state;
+ parser->state = state;
+ return ret;
+}
+
/******************************************************************************
* Converts a hex representation of a DWORD into a DWORD.
*/
@@ -583,44 +632,6 @@ static void processRegEntry(WCHAR* stdInput, BOOL isUnicode)
}
}
-/* version for Windows 3.1 */
-static void processRegEntry31(WCHAR *line)
-{
- int key_end = 0;
- WCHAR *value;
- int res;
-
- static WCHAR empty[] = {0};
- static WCHAR hkcr[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T'};
-
- if (strncmpW(line, hkcr, sizeof(hkcr) / sizeof(WCHAR))) return;
-
- /* get key name */
- while (line[key_end] && !isspaceW(line[key_end])) key_end++;
-
- value = line + key_end;
- while (isspaceW(value[0])) value++;
-
- if (value[0] == '=') value++;
- if (value[0] == ' ') value++; /* at most one space is skipped */
-
- line[key_end] = '\0';
- if (openKeyW(line) != ERROR_SUCCESS)
- output_message(STRING_OPEN_KEY_FAILED, line);
-
- res = RegSetValueExW(
- currentKeyHandle,
- empty,
- 0, /* Reserved */
- REG_SZ,
- (BYTE *)value,
- (strlenW(value) + 1) * sizeof(WCHAR));
- if (res != ERROR_SUCCESS)
- output_message(STRING_SETVALUE_FAILED, empty, currentKeyName);
-
- closeKey();
-}
-
enum reg_versions {
REG_VERSION_31,
REG_VERSION_40,
@@ -629,7 +640,7 @@ enum reg_versions {
REG_VERSION_INVALID
};
-static enum reg_versions parse_file_header(WCHAR *s)
+static enum reg_versions parse_file_header(const WCHAR *s)
{
static const WCHAR header_31[] = {'R','E','G','E','D','I','T',0};
static const WCHAR header_40[] = {'R','E','G','E','D','I','T','4',0};
@@ -659,6 +670,129 @@ static enum reg_versions parse_file_header(WCHAR *s)
return REG_VERSION_INVALID;
}
+/* handler for parser HEADER state */
+static WCHAR *header_state(struct parser *parser, WCHAR *pos)
+{
+ WCHAR *line, *header;
+
+ if (!(line = get_line(parser->file)))
+ return NULL;
+
+ if (!parser->is_unicode)
+ {
+ header = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(line) + 3) * sizeof(WCHAR));
+ CHECK_ENOUGH_MEMORY(header);
+ header[0] = parser->two_wchars[0];
+ header[1] = parser->two_wchars[1];
+ lstrcpyW(header + 2, line);
+ parser->reg_version = parse_file_header(header);
+ HeapFree(GetProcessHeap(), 0, header);
+ }
+ else parser->reg_version = parse_file_header(line);
+
+ switch (parser->reg_version)
+ {
+ case REG_VERSION_31:
+ set_state(parser, PARSE_WIN31_LINE);
+ break;
+ case REG_VERSION_40:
+ case REG_VERSION_50:
+ set_state(parser, LINE_START);
+ break;
+ default:
+ get_line(NULL); /* Reset static variables */
+ return NULL;
+ }
+
+ return line;
+}
+
+/* handler for parser PARSE_WIN31_LINE state */
+static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos)
+{
+ WCHAR *line, *value;
+ static WCHAR hkcr[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T'};
+ unsigned int key_end = 0;
+
+ if (!(line = get_line(parser->file)))
+ return NULL;
+
+ if (strncmpW(line, hkcr, ARRAY_SIZE(hkcr)))
+ goto invalid;
+
+ /* get key name */
+ while (line[key_end] && !isspaceW(line[key_end])) key_end++;
+
+ value = line + key_end;
+ while (*value == ' ' || *value == '\t') value++;
+
+ if (*value == '=') value++;
+ if (*value == ' ') value++; /* at most one space is skipped */
+
+ line[key_end] = 0;
+
+ closeKey();
+
+ if (openKeyW(line) != ERROR_SUCCESS)
+ {
+ output_message(STRING_OPEN_KEY_FAILED, line);
+ goto invalid;
+ }
+
+ parser->value_name = NULL;
+ parser->data_type = REG_SZ;
+ parser->data = value;
+ parser->data_size = (lstrlenW(value) + 1) * sizeof(WCHAR);
+
+ set_state(parser, SET_VALUE);
+ return value;
+
+invalid:
+ set_state(parser, PARSE_WIN31_LINE);
+ return line;
+}
+
+/* handler for parser LINE_START state */
+static WCHAR *line_start_state(struct parser *parser, WCHAR *pos)
+{
+ WCHAR *line, *p;
+
+ if (!(line = get_line(parser->file)))
+ return NULL;
+
+ for (p = line; *p; p++)
+ {
+ switch (*p)
+ {
+ case '[':
+ case '@':
+ case '"':
+ processRegEntry(p, parser->is_unicode);
+ set_state(parser, LINE_START);
+ return line;
+ case ' ':
+ case '\t':
+ break;
+ default:
+ set_state(parser, LINE_START);
+ return p;
+ }
+ }
+
+ return p;
+}
+
+/* handler for parser SET_VALUE state */
+static WCHAR *set_value_state(struct parser *parser, WCHAR *pos)
+{
+ RegSetValueExW(currentKeyHandle, parser->value_name, 0, parser->data_type,
+ parser->data, parser->data_size);
+
+ set_state(parser, PARSE_WIN31_LINE);
+
+ return pos;
+}
+
static WCHAR *get_lineA(FILE *fp)
{
static WCHAR *lineW;
@@ -716,7 +850,6 @@ static WCHAR *get_lineA(FILE *fp)
next = line;
continue;
}
- while (*line == ' ' || *line == '\t') line++;
if (*line == ';' || *line == '#')
{
line = next;
@@ -787,7 +920,6 @@ static WCHAR *get_lineW(FILE *fp)
next = line;
continue;
}
- while (*line == ' ' || *line == '\t') line++;
if (*line == ';' || *line == '#')
{
line = next;
@@ -1273,47 +1405,36 @@ BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name, DWORD format)
/******************************************************************************
* Reads contents of the specified file into the registry.
*/
-BOOL import_registry_file(FILE* reg_file)
+BOOL import_registry_file(FILE *reg_file)
{
BYTE s[2];
- BOOL is_unicode;
- WCHAR *(*get_line)(FILE *);
- WCHAR *line, *header;
- int reg_version;
+ struct parser parser;
+ WCHAR *pos;
if (!reg_file || (fread(s, 2, 1, reg_file) != 1))
return FALSE;
- is_unicode = (s[0] == 0xff && s[1] == 0xfe);
- get_line = is_unicode ? get_lineW : get_lineA;
+ parser.is_unicode = (s[0] == 0xff && s[1] == 0xfe);
+ get_line = parser.is_unicode ? get_lineW : get_lineA;
- line = get_line(reg_file);
+ parser.file = reg_file;
+ parser.two_wchars[0] = s[0];
+ parser.two_wchars[1] = s[1];
+ parser.reg_version = -1;
+ parser.value_name = NULL;
+ parser.data_type = 0;
+ parser.data = NULL;
+ parser.data_size = 0;
+ parser.state = HEADER;
- if (!is_unicode)
- {
- header = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(line) + 3) * sizeof(WCHAR));
- CHECK_ENOUGH_MEMORY(header);
- header[0] = s[0];
- header[1] = s[1];
- lstrcpyW(header + 2, line);
- reg_version = parse_file_header(header);
- HeapFree(GetProcessHeap(), 0, header);
- }
- else reg_version = parse_file_header(line);
+ pos = parser.two_wchars;
- if (reg_version == REG_VERSION_FUZZY || reg_version == REG_VERSION_INVALID)
- {
- get_line(NULL); /* Reset static variables */
- return reg_version == REG_VERSION_FUZZY;
- }
+ /* parser main loop */
+ while (pos)
+ pos = (parser_funcs[parser.state])(&parser, pos);
- while ((line = get_line(reg_file)))
- {
- if (reg_version == REG_VERSION_31)
- processRegEntry31(line);
- else
- processRegEntry(line, is_unicode);
- }
+ if (parser.reg_version == REG_VERSION_FUZZY || parser.reg_version == REG_VERSION_INVALID)
+ return parser.reg_version == REG_VERSION_FUZZY;
closeKey();
return TRUE;
diff --git a/programs/regedit/tests/regedit.c b/programs/regedit/tests/regedit.c
index c470414..32d190c 100644
--- a/programs/regedit/tests/regedit.c
+++ b/programs/regedit/tests/regedit.c
@@ -819,36 +819,36 @@ static void test_invalid_import_31(void)
/* Test character validity at the start of the line */
exec_import_str("REGEDIT\r\n"
" HKEY_CLASSES_ROOT\\" KEY_BASE " = Value1a\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
exec_import_str("REGEDIT\r\n"
" HKEY_CLASSES_ROOT\\" KEY_BASE " = Value1b\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
exec_import_str("REGEDIT\r\n"
"\tHKEY_CLASSES_ROOT\\" KEY_BASE " = Value1c\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
exec_import_str("REGEDIT\r\n"
";HKEY_CLASSES_ROOT\\" KEY_BASE " = Value2a\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
exec_import_str("REGEDIT\r\n"
"#HKEY_CLASSES_ROOT\\" KEY_BASE " = Value2b\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
/* Test case sensitivity */
exec_import_str("REGEDIT\r\n"
"hkey_classes_root\\" KEY_BASE " = Value3a\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
exec_import_str("REGEDIT\r\n"
"hKEY_CLASSES_ROOT\\" KEY_BASE " = Value3b\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
exec_import_str("REGEDIT\r\n"
"Hkey_Classes_Root\\" KEY_BASE " = Value3c\r\n");
- todo_wine verify_reg_nonexist(hkey, "");
+ verify_reg_nonexist(hkey, "");
RegCloseKey(hkey);
--
2.7.4
More information about the wine-patches
mailing list