regedit: Introduce a partial state machine for importing registry data.

Signed-off-by: Hugh McMaster <hugh.mcmaster@outlook.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hugh McMaster 2017-06-15 12:00:31 +00:00 committed by Alexandre Julliard
parent b1e48fb469
commit ec87800856
2 changed files with 201 additions and 80 deletions

View File

@ -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;

View File

@ -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);