[MMIXER] Fix additional data size initialization for different audio formats (#6753)
[reactos.git] / base / applications / regedit / regproc.c
index 5ca0850..f8d448e 100644 (file)
@@ -2,9 +2,9 @@
  * Registry processing routines. Routines, common for registry
  * processing frontends.
  *
- * Copyright (C) 1999 Sylvain St-Germain
- * Copyright (C) 2002 Andriy Palamarchuk
- * Copyright (C) 2008 Alexander N. Sørnes <alex@thehandofagony.com>
+ * Copyright 1999 Sylvain St-Germain
+ * Copyright 2002 Andriy Palamarchuk
+ * Copyright 2008 Alexander N. Sørnes <alex@thehandofagony.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#ifdef __REACTOS__
+#include <fcntl.h>
+#include <io.h>
 #include "regedit.h"
+#else
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <windows.h>
+#include <commctrl.h>
 
-#define REG_VAL_BUF_SIZE        4096
-
-/* maximal number of characters in hexadecimal data line,
- * including the indentation, but not including the '\' character
- */
-#define REG_FILE_HEX_LINE_LEN   (2 + 25 * 3)
-
-static const CHAR *reg_class_names[] =
-{
-    "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
-    "HKEY_CURRENT_CONFIG", "HKEY_CURRENT_USER", "HKEY_DYN_DATA"
-};
-
-#define REG_CLASS_NUMBER (COUNT_OF(reg_class_names))
-
-const WCHAR* reg_class_namesW[REG_CLASS_NUMBER] =
-{
-    L"HKEY_LOCAL_MACHINE", L"HKEY_USERS", L"HKEY_CLASSES_ROOT",
-    L"HKEY_CURRENT_CONFIG", L"HKEY_CURRENT_USER", L"HKEY_DYN_DATA"
-};
-
-static HKEY reg_class_keys[REG_CLASS_NUMBER] =
-{
-    HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
-    HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
-};
-
-/* return values */
-#define NOT_ENOUGH_MEMORY     1
-#define IO_ERROR              2
+#include "main.h"
+#endif
 
-/* processing macros */
+#define REG_VAL_BUF_SIZE        4096
 
-/* common check of memory allocation results */
-#define CHECK_ENOUGH_MEMORY(p) \
-if (!(p)) \
-{ \
-    fprintf(stderr,"%S: file %s, line %d: Not enough memory\n", \
-            getAppName(), __FILE__, __LINE__); \
-    exit(NOT_ENOUGH_MEMORY); \
-}
+static HKEY reg_class_keys[] = {
+            HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
+            HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
+        };
 
 /******************************************************************************
  * Allocates memory and converts input from multibyte to wide chars
  * Returned string must be freed by the caller
  */
-WCHAR* GetWideString(const char* strA)
+static WCHAR* GetWideString(const char* strA)
 {
     if(strA)
     {
         WCHAR* strW;
         int len = MultiByteToWideChar(CP_ACP, 0, strA, -1, NULL, 0);
 
-        strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
-        CHECK_ENOUGH_MEMORY(strW);
+        strW = malloc(len * sizeof(WCHAR));
         MultiByteToWideChar(CP_ACP, 0, strA, -1, strW, len);
         return strW;
     }
@@ -95,8 +71,7 @@ static WCHAR* GetWideStringN(const char* strA, int chars, DWORD *len)
         WCHAR* strW;
         *len = MultiByteToWideChar(CP_ACP, 0, strA, chars, NULL, 0);
 
-        strW = HeapAlloc(GetProcessHeap(), 0, *len * sizeof(WCHAR));
-        CHECK_ENOUGH_MEMORY(strW);
+        strW = malloc(*len * sizeof(WCHAR));
         MultiByteToWideChar(CP_ACP, 0, strA, chars, strW, *len);
         return strW;
     }
@@ -115,8 +90,7 @@ char* GetMultiByteString(const WCHAR* strW)
         char* strA;
         int len = WideCharToMultiByte(CP_ACP, 0, strW, -1, NULL, 0, NULL, NULL);
 
-        strA = HeapAlloc(GetProcessHeap(), 0, len);
-        CHECK_ENOUGH_MEMORY(strA);
+        strA = malloc(len);
         WideCharToMultiByte(CP_ACP, 0, strW, -1, strA, len, NULL, NULL);
         return strA;
     }
@@ -134,8 +108,7 @@ static char* GetMultiByteStringN(const WCHAR* strW, int chars, DWORD* len)
         char* strA;
         *len = WideCharToMultiByte(CP_ACP, 0, strW, chars, NULL, 0, NULL, NULL);
 
-        strA = HeapAlloc(GetProcessHeap(), 0, *len);
-        CHECK_ENOUGH_MEMORY(strA);
+        strA = malloc(*len);
         WideCharToMultiByte(CP_ACP, 0, strW, chars, strA, *len, NULL, NULL);
         return strA;
     }
@@ -143,1395 +116,1472 @@ 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 */
+    KEY_NAME,            /* parsing a key name */
+    DELETE_KEY,          /* deleting a registry key */
+    DEFAULT_VALUE_NAME,  /* parsing a default value name */
+    QUOTED_VALUE_NAME,   /* parsing a double-quoted value name */
+    DATA_START,          /* preparing for data parsing operations */
+    DELETE_VALUE,        /* deleting a registry value */
+    DATA_TYPE,           /* parsing the registry data type */
+    STRING_DATA,         /* parsing REG_SZ data */
+    DWORD_DATA,          /* parsing DWORD data */
+    HEX_DATA,            /* parsing REG_BINARY, REG_NONE, REG_EXPAND_SZ or REG_MULTI_SZ data */
+    EOL_BACKSLASH,       /* preparing to parse multiple lines of hex data */
+    HEX_MULTILINE,       /* parsing multiple lines of hex data */
+    UNKNOWN_DATA,        /* parsing an unhandled or invalid data type */
+    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 */
+    HKEY               hkey;           /* current registry key */
+    WCHAR             *key_name;       /* current key name */
+    WCHAR             *value_name;     /* value name */
+    DWORD              parse_type;     /* generic data type for parsing */
+    DWORD              data_type;      /* data type */
+    void              *data;           /* value data */
+    DWORD              data_size;      /* size of the data (in bytes) */
+    BOOL               backslash;      /* TRUE if the current line contains a backslash */
+    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 *key_name_state(struct parser *parser, WCHAR *pos);
+static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos);
+static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos);
+static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos);
+static WCHAR *data_start_state(struct parser *parser, WCHAR *pos);
+static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos);
+static WCHAR *data_type_state(struct parser *parser, WCHAR *pos);
+static WCHAR *string_data_state(struct parser *parser, WCHAR *pos);
+static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos);
+static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos);
+static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos);
+static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos);
+static WCHAR *unknown_data_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 */
+    key_name_state,            /* KEY_NAME */
+    delete_key_state,          /* DELETE_KEY */
+    default_value_name_state,  /* DEFAULT_VALUE_NAME */
+    quoted_value_name_state,   /* QUOTED_VALUE_NAME */
+    data_start_state,          /* DATA_START */
+    delete_value_state,        /* DELETE_VALUE */
+    data_type_state,           /* DATA_TYPE */
+    string_data_state,         /* STRING_DATA */
+    dword_data_state,          /* DWORD_DATA */
+    hex_data_state,            /* HEX_DATA */
+    eol_backslash_state,       /* EOL_BACKSLASH */
+    hex_multiline_state,       /* HEX_MULTILINE */
+    unknown_data_state,        /* UNKNOWN_DATA */
+    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.
  */
-static BOOL convertHexToDWord(WCHAR* str, DWORD *dw)
+static BOOL convert_hex_to_dword(WCHAR *str, DWORD *dw)
 {
-    char buf[9];
-    char dummy;
+    WCHAR *p, *end;
+    int count = 0;
+
+    while (*str == ' ' || *str == '\t') str++;
+    if (!*str) goto error;
 
-    WideCharToMultiByte(CP_ACP, 0, str, -1, buf, 9, NULL, NULL);
-    if (lstrlenW(str) > 8 || sscanf(buf, "%lx%c", dw, &dummy) != 1)
+    p = str;
+    while (iswxdigit(*p))
     {
-        fprintf(stderr,"%S: ERROR, invalid hex value\n", getAppName());
-        return FALSE;
+        count++;
+        p++;
     }
+    if (count > 8) goto error;
+
+    end = p;
+    while (*p == ' ' || *p == '\t') p++;
+    if (*p && *p != ';') goto error;
+
+    *end = 0;
+    *dw = wcstoul(str, &end, 16);
     return TRUE;
+
+error:
+    return FALSE;
 }
 
 /******************************************************************************
- * Converts a hex comma separated values list into a binary string.
+ * Converts comma-separated hex data into a binary string and modifies
+ * the input parameter to skip the concatenating backslash, if found.
+ *
+ * Returns TRUE or FALSE to indicate whether parsing was successful.
  */
-static BYTE* convertHexCSVToHex(WCHAR *str, DWORD *size)
+static BOOL convert_hex_csv_to_hex(struct parser *parser, WCHAR **str)
 {
+    size_t size;
+    BYTE *d;
     WCHAR *s;
-    BYTE *d, *data;
+
+    parser->backslash = FALSE;
 
     /* The worst case is 1 digit + 1 comma per byte */
-    *size=(lstrlenW(str)+1)/2;
-    data=HeapAlloc(GetProcessHeap(), 0, *size);
-    CHECK_ENOUGH_MEMORY(data);
-
-    s = str;
-    d = data;
-    *size=0;
-    while (*s != '\0')
+    size = ((lstrlenW(*str) + 1) / 2) + parser->data_size;
+    parser->data = realloc(parser->data, size);
+
+    s = *str;
+    d = (BYTE *)parser->data + parser->data_size;
+
+    while (*s)
     {
-        UINT wc;
         WCHAR *end;
+        unsigned long wc;
+
+        wc = wcstoul(s, &end, 16);
+        if (wc > 0xff) return FALSE;
 
-        wc = wcstoul(s,&end, 16);
-        if (end == s || wc > 0xff || (*end && *end != L','))
+        if (s == end && wc == 0)
         {
-            char* strA = GetMultiByteString(s);
-            fprintf(stderr,"%S: ERROR converting CSV hex stream. Invalid value at '%s'\n",
-                    getAppName(), strA);
-            HeapFree(GetProcessHeap(), 0, data);
-            HeapFree(GetProcessHeap(), 0, strA);
-            return NULL;
+            while (*end == ' ' || *end == '\t') end++;
+            if (*end == '\\')
+            {
+                parser->backslash = TRUE;
+                *str = end + 1;
+                return TRUE;
+            }
+            else if (*end == ';')
+                return TRUE;
+            return FALSE;
+        }
+
+        *d++ = wc;
+        parser->data_size++;
+
+        if (*end && *end != ',')
+        {
+            while (*end == ' ' || *end == '\t') end++;
+            if (*end && *end != ';') return FALSE;
+            return TRUE;
         }
-        *d++ =(BYTE)wc;
-        (*size)++;
+
         if (*end) end++;
         s = end;
     }
 
-    return data;
+    return TRUE;
 }
 
 /******************************************************************************
- * This function returns the HKEY associated with the data type encoded in the
- * value.  It modifies the input parameter (key value) in order to skip this
- * "now useless" data type information.
+ * Parses the data type of the registry value being imported and modifies
+ * the input parameter to skip the string representation of the data type.
  *
- * Note: Updated based on the algorithm used in 'server/registry.c'
+ * Returns TRUE or FALSE to indicate whether a data type was found.
  */
-static DWORD getDataType(LPWSTR *lpValue, DWORD* parse_type)
+static BOOL parse_data_type(struct parser *parser, WCHAR **line)
 {
-    struct data_type
-    {
-        const WCHAR *tag;
-        int len;
-        int type;
-        int parse_type;
-    };
-
-    static const WCHAR quote[] = {'"'};
-    static const WCHAR str[] = {'s','t','r',':','"'};
-    static const WCHAR str2[] = {'s','t','r','(','2',')',':','"'};
-    static const WCHAR hex[] = {'h','e','x',':'};
-    static const WCHAR dword[] = {'d','w','o','r','d',':'};
-    static const WCHAR hexp[] = {'h','e','x','('};
-
-    static const struct data_type data_types[] = {                   /* actual type */  /* type to assume for parsing */
-        { quote,       1,   REG_SZ,              REG_SZ },
-        { str,         5,   REG_SZ,              REG_SZ },
-        { str2,        8,   REG_EXPAND_SZ,       REG_SZ },
-        { hex,         4,   REG_BINARY,          REG_BINARY },
-        { dword,       6,   REG_DWORD,           REG_DWORD },
-        { hexp,        4,   -1,                  REG_BINARY },
-        { NULL,        0,    0,                  0 }
+    struct data_type { const WCHAR *tag; int len; int type; int parse_type; };
+
+    static const struct data_type data_types[] = {
+    /*    tag       len  type         parse type    */
+        { L"\"",     1,  REG_SZ,      REG_SZ },
+        { L"hex:",   4,  REG_BINARY,  REG_BINARY },
+        { L"dword:", 6,  REG_DWORD,   REG_DWORD },
+        { L"hex(",   4,  -1,          REG_BINARY }, /* REG_NONE, REG_EXPAND_SZ, REG_MULTI_SZ */
+        { NULL,      0,  0,           0 }
     };
 
     const struct data_type *ptr;
-    int type;
 
     for (ptr = data_types; ptr->tag; ptr++)
     {
-        if (wcsncmp(ptr->tag, *lpValue, ptr->len))
+        if (wcsncmp(ptr->tag, *line, ptr->len))
             continue;
 
-        /* Found! */
-        *parse_type = ptr->parse_type;
-        type=ptr->type;
-        *lpValue+=ptr->len;
-        if (type == -1)
+        parser->parse_type = ptr->parse_type;
+        parser->data_type = ptr->parse_type;
+        *line += ptr->len;
+
+        if (ptr->type == -1)
         {
-            WCHAR* end;
+            WCHAR *end;
+            DWORD val;
+
+            if (!**line || towlower((*line)[1]) == 'x')
+                return FALSE;
 
             /* "hex(xx):" is special */
-            type = (int)wcstoul( *lpValue , &end, 16 );
-            if (**lpValue=='\0' || *end!=')' || *(end+1)!=':')
-            {
-                type=REG_NONE;
-            }
-            else
-            {
-                *lpValue = end + 2;
-            }
+            val = wcstoul(*line, &end, 16);
+#ifdef __REACTOS__
+            /* Up to 8 hex digits, "hex(000000002)" is invalid */
+            if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE) || end - *line > 8)
+#else
+            if (*end != ')' || *(end + 1) != ':' || (val == ~0u && errno == ERANGE))
+#endif
+                return FALSE;
+
+            parser->data_type = val;
+            *line = end + 2;
         }
-        return type;
+        return TRUE;
     }
-    *parse_type=REG_NONE;
-    return REG_NONE;
+    return FALSE;
 }
 
 /******************************************************************************
- * Replaces escape sequences with the characters.
+ * Replaces escape sequences with their character equivalents and
+ * null-terminates the string on the first non-escaped double quote.
+ *
+ * Assigns a pointer to the remaining unparsed data in the line.
+ * Returns TRUE or FALSE to indicate whether a closing double quote was found.
  */
-static void REGPROC_unescape_string(WCHAR* str)
+static BOOL REGPROC_unescape_string(WCHAR *str, WCHAR **unparsed)
 {
     int str_idx = 0;            /* current character under analysis */
     int val_idx = 0;            /* the last character of the unescaped string */
     int len = lstrlenW(str);
-    for (str_idx = 0; str_idx < len; str_idx++, val_idx++)
-    {
-        if (str[str_idx] == '\\')
-        {
+    BOOL ret;
+
+    for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
+        if (str[str_idx] == '\\') {
             str_idx++;
-            switch (str[str_idx])
-            {
+            switch (str[str_idx]) {
             case 'n':
                 str[val_idx] = '\n';
                 break;
+            case 'r':
+                str[val_idx] = '\r';
+                break;
+            case '0':
+                return FALSE;
             case '\\':
             case '"':
                 str[val_idx] = str[str_idx];
                 break;
             default:
-                fprintf(stderr,"Warning! Unrecognized escape sequence: \\%C'\n",
-                str[str_idx]);
+                if (!str[str_idx]) return FALSE;
+                output_message(STRING_ESCAPE_SEQUENCE, str[str_idx]);
                 str[val_idx] = str[str_idx];
                 break;
             }
-        }
-        else
-        {
+        } else if (str[str_idx] == '"') {
+            break;
+        } else {
             str[val_idx] = str[str_idx];
         }
     }
+
+    ret = (str[str_idx] == '"');
+    *unparsed = str + str_idx + 1;
     str[val_idx] = '\0';
+    return ret;
 }
 
-static BOOL parseKeyName(LPWSTR lpKeyName, HKEY *hKey, LPWSTR *lpKeyPath)
+static HKEY parse_key_name(WCHAR *key_name, WCHAR **key_path)
 {
-    WCHAR* lpSlash = NULL;
-    unsigned int i, len;
+    unsigned int i;
 
-    if (lpKeyName == NULL)
-        return FALSE;
-
-    for(i = 0; *(lpKeyName + i) != 0; i++)
-    {
-        if(*(lpKeyName+i) == '\\')
-        {
-            lpSlash = lpKeyName + i;
-            break;
-        }
-    }
+    if (!key_name) return 0;
 
-    if (lpSlash)
-    {
-        len = lpSlash-lpKeyName;
-    }
-    else
-    {
-        len = lstrlenW(lpKeyName);
-        lpSlash = lpKeyName+len;
-    }
-    *hKey = NULL;
+    *key_path = wcschr(key_name, '\\');
+    if (*key_path) (*key_path)++;
 
-    for (i = 0; i < REG_CLASS_NUMBER; i++)
+    for (i = 0; i < ARRAY_SIZE(reg_class_keys); i++)
     {
-        if (CompareStringW(LOCALE_USER_DEFAULT, 0, lpKeyName, len, reg_class_namesW[i], len) == CSTR_EQUAL &&
-        len == lstrlenW(reg_class_namesW[i]))
+        int len = lstrlenW(reg_class_namesW[i]);
+#ifdef __REACTOS__
+        if (!_wcsnicmp(key_name, reg_class_namesW[i], len) &&
+#else
+        if (!wcsnicmp(key_name, reg_class_namesW[i], len) &&
+#endif
+           (key_name[len] == 0 || key_name[len] == '\\'))
         {
-            *hKey = reg_class_keys[i];
-            break;
+            return reg_class_keys[i];
         }
     }
 
-    if (*hKey == NULL)
-        return FALSE;
-
-    if (*lpSlash != '\0')
-        lpSlash++;
-    *lpKeyPath = lpSlash;
-    return TRUE;
+    return 0;
 }
 
-/* Globals used by the setValue() & co */
-static LPSTR currentKeyName;
-static HKEY  currentKeyHandle = NULL;
-
-/******************************************************************************
- * Sets the value with name val_name to the data in val_data for the currently
- * opened key.
- *
- * Parameters:
- * val_name - name of the registry value
- * val_data - registry value data
- */
-static LONG setValue(WCHAR* val_name, WCHAR* val_data, BOOL is_unicode)
+static void close_key(struct parser *parser)
 {
-    LONG res;
-    DWORD  dwDataType, dwParseType;
-    LPBYTE lpbData;
-    DWORD  dwData, dwLen;
-    WCHAR del[] = {'-',0};
-
-    if ( (val_name == NULL) || (val_data == NULL) )
-        return ERROR_INVALID_PARAMETER;
-
-    if (lstrcmpW(val_data, del) == 0)
-    {
-        res=RegDeleteValueW(currentKeyHandle,val_name);
-        return (res == ERROR_FILE_NOT_FOUND ? ERROR_SUCCESS : res);
-    }
-
-    /* Get the data type stored into the value field */
-    dwDataType = getDataType(&val_data, &dwParseType);
-
-    if (dwParseType == REG_SZ)          /* no conversion for string */
-    {
-        REGPROC_unescape_string(val_data);
-        /* Compute dwLen after REGPROC_unescape_string because it may
-         * have changed the string length and we don't want to store
-         * the extra garbage in the registry.
-         */
-        dwLen = lstrlenW(val_data);
-        if (dwLen>0 && val_data[dwLen-1]=='"')
-        {
-            dwLen--;
-            val_data[dwLen]='\0';
-        }
-        lpbData = (BYTE*) val_data;
-        dwLen++;  /* include terminating null */
-        dwLen = dwLen * sizeof(WCHAR); /* size is in bytes */
-    }
-    else if (dwParseType == REG_DWORD)  /* Convert the dword types */
-    {
-        if (!convertHexToDWord(val_data, &dwData))
-            return ERROR_INVALID_DATA;
-        lpbData = (BYTE*)&dwData;
-        dwLen = sizeof(dwData);
-    }
-    else if (dwParseType == REG_BINARY) /* Convert the binary data */
+    if (parser->hkey)
     {
-        lpbData = convertHexCSVToHex(val_data, &dwLen);
-        if (!lpbData)
-            return ERROR_INVALID_DATA;
+        free(parser->key_name);
+        parser->key_name = NULL;
 
-        if((dwDataType == REG_MULTI_SZ || dwDataType == REG_EXPAND_SZ) && !is_unicode)
-        {
-            LPBYTE tmp = lpbData;
-            lpbData = (LPBYTE)GetWideStringN((char*)lpbData, dwLen, &dwLen);
-            dwLen *= sizeof(WCHAR);
-            HeapFree(GetProcessHeap(), 0, tmp);
-        }
-    }
-    else                                /* unknown format */
-    {
-        fprintf(stderr,"%S: ERROR, unknown data format\n", getAppName());
-        return ERROR_INVALID_DATA;
+        RegCloseKey(parser->hkey);
+        parser->hkey = NULL;
     }
-
-    res = RegSetValueExW(
-        currentKeyHandle,
-        val_name,
-        0,                  /* Reserved */
-        dwDataType,
-        lpbData,
-        dwLen);
-    if (dwParseType == REG_BINARY)
-        HeapFree(GetProcessHeap(), 0, lpbData);
-    return res;
 }
 
 /******************************************************************************
- * A helper function for processRegEntry() that opens the current key.
- * That key must be closed by calling closeKey().
+ * Opens the registry key given by the input path.
+ * This key must be closed by calling close_key().
  */
-static LONG openKeyW(WCHAR* stdInput)
+static LONG open_key(struct parser *parser, WCHAR *path)
 {
-    HKEY keyClass;
-    WCHAR* keyPath;
-    DWORD dwDisp;
+    HKEY key_class;
+    WCHAR *key_path;
     LONG res;
 
-    /* Sanity checks */
-    if (stdInput == NULL)
-        return ERROR_INVALID_PARAMETER;
+    close_key(parser);
 
     /* Get the registry class */
-    if (!parseKeyName(stdInput, &keyClass, &keyPath))
+    if (!path || !(key_class = parse_key_name(path, &key_path)))
         return ERROR_INVALID_PARAMETER;
 
-    res = RegCreateKeyExW(keyClass,                 /* Class     */
-                          keyPath,                  /* Sub Key   */
-                          0,                        /* MUST BE 0 */
-                          NULL,                     /* object type */
-                          REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
-                          KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
-                          NULL,                     /* security attribute */
-                          &currentKeyHandle,        /* result */
-                          &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
-                                                                    REG_OPENED_EXISTING_KEY */
+    res = RegCreateKeyExW(key_class, key_path, 0, NULL, REG_OPTION_NON_VOLATILE,
+                          KEY_ALL_ACCESS, NULL, &parser->hkey, NULL);
 
     if (res == ERROR_SUCCESS)
-        currentKeyName = GetMultiByteString(stdInput);
+    {
+        parser->key_name = malloc((lstrlenW(path) + 1) * sizeof(WCHAR));
+        lstrcpyW(parser->key_name, path);
+    }
     else
-        currentKeyHandle = NULL;
+        parser->hkey = NULL;
 
     return res;
-
 }
 
-/******************************************************************************
- * Close the currently opened key.
- */
-static void closeKey(void)
+static void free_parser_data(struct parser *parser)
 {
-    if (currentKeyHandle)
-    {
-        HeapFree(GetProcessHeap(), 0, currentKeyName);
-        RegCloseKey(currentKeyHandle);
-        currentKeyHandle = NULL;
-    }
+    if (parser->parse_type == REG_DWORD || parser->parse_type == REG_BINARY)
+        free(parser->data);
+
+    parser->data = NULL;
+    parser->data_size = 0;
 }
 
-/******************************************************************************
- * This function is a wrapper for the setValue function.  It prepares the
- * land and cleans the area once completed.
- * Note: this function modifies the line parameter.
- *
- * line - registry file unwrapped line. Should have the registry value name and
- *      complete registry value data.
- */
-static void processSetValue(WCHAR* line, BOOL is_unicode)
+static void prepare_hex_string_data(struct parser *parser)
 {
-    WCHAR* val_name;                   /* registry value name   */
-    WCHAR* val_data;                   /* registry value data   */
-    int line_idx = 0;                 /* current character under analysis */
-    LONG res;
-
-    /* get value name */
-    while ( iswspace(line[line_idx]) ) line_idx++;
-    if (line[line_idx] == '@' && line[line_idx + 1] == '=')
+    if (parser->data_type == REG_EXPAND_SZ || parser->data_type == REG_MULTI_SZ ||
+        parser->data_type == REG_SZ)
     {
-        line[line_idx] = '\0';
-        val_name = line;
-        line_idx++;
-    }
-    else if (line[line_idx] == '\"')
-    {
-        line_idx++;
-        val_name = line + line_idx;
-        while (TRUE)
+        if (parser->is_unicode)
         {
-            if (line[line_idx] == '\\')   /* skip escaped character */
-            {
-                line_idx += 2;
-            }
-            else
+            WCHAR *data = parser->data;
+            DWORD len = parser->data_size / sizeof(WCHAR);
+
+            if (data[len - 1] != 0)
             {
-                if (line[line_idx] == '\"')
-                {
-                    line[line_idx] = '\0';
-                    line_idx++;
-                    break;
-                }
-                else
-                {
-                    line_idx++;
-                }
+                data[len] = 0;
+                parser->data_size += sizeof(WCHAR);
             }
         }
-        while ( iswspace(line[line_idx]) ) line_idx++;
-        if (line[line_idx] != '=')
+        else
         {
-            char* lineA;
-            line[line_idx] = '\"';
-            lineA = GetMultiByteString(line);
-            fprintf(stderr,"Warning! unrecognized line:\n%s\n", lineA);
-            HeapFree(GetProcessHeap(), 0, lineA);
-            return;
-        }
+            BYTE *data = parser->data;
 
-    }
-    else
-    {
-        char* lineA = GetMultiByteString(line);
-        fprintf(stderr,"Warning! unrecognized line:\n%s\n", lineA);
-        HeapFree(GetProcessHeap(), 0, lineA);
-        return;
-    }
-    line_idx++;                   /* skip the '=' character */
-
-    while ( iswspace(line[line_idx]) ) line_idx++;
-    val_data = line + line_idx;
-    /* trim trailing blanks */
-    line_idx = lstrlenW(val_data);
-    while (line_idx > 0 && iswspace(val_data[line_idx-1])) line_idx--;
-    val_data[line_idx] = '\0';
-
-    REGPROC_unescape_string(val_name);
-    res = setValue(val_name, val_data, is_unicode);
-    if ( res != ERROR_SUCCESS )
-    {
-        char* val_nameA = GetMultiByteString(val_name);
-        char* val_dataA = GetMultiByteString(val_data);
-        fprintf(stderr,"%S: ERROR Key %s not created. Value: %s, Data: %s\n",
-        getAppName(),
-        currentKeyName,
-        val_nameA,
-        val_dataA);
-        HeapFree(GetProcessHeap(), 0, val_nameA);
-        HeapFree(GetProcessHeap(), 0, val_dataA);
+            if (data[parser->data_size - 1] != 0)
+            {
+                data[parser->data_size] = 0;
+                parser->data_size++;
+            }
+
+            parser->data = GetWideStringN(parser->data, parser->data_size, &parser->data_size);
+            parser->data_size *= sizeof(WCHAR);
+            free(data);
+        }
     }
 }
 
-/******************************************************************************
- * This function receives the currently read entry and performs the
- * corresponding action.
- * isUnicode affects parsing of REG_MULTI_SZ values
- */
-static void processRegEntry(WCHAR* stdInput, BOOL isUnicode)
+enum reg_versions {
+    REG_VERSION_31,
+    REG_VERSION_40,
+    REG_VERSION_50,
+    REG_VERSION_FUZZY,
+    REG_VERSION_INVALID
+};
+
+static enum reg_versions parse_file_header(const WCHAR *s)
 {
-    /*
-     * We encountered the end of the file, make sure we
-     * close the opened key and exit
+    static const WCHAR header_31[] = L"REGEDIT";
+
+    while (*s == ' ' || *s == '\t') s++;
+
+    if (!lstrcmpW(s, header_31))
+        return REG_VERSION_31;
+
+    if (!lstrcmpW(s, L"REGEDIT4"))
+        return REG_VERSION_40;
+
+    if (!lstrcmpW(s, L"Windows Registry Editor Version 5.00"))
+        return REG_VERSION_50;
+
+    /* The Windows version accepts registry file headers beginning with "REGEDIT" and ending
+     * with other characters, as long as "REGEDIT" appears at the start of the line. For example,
+     * "REGEDIT 4", "REGEDIT9" and "REGEDIT4FOO" are all treated as valid file headers.
+     * In all such cases, however, the contents of the registry file are not imported.
      */
-    if (stdInput == NULL)
-    {
-        closeKey();
-        return;
-    }
+    if (!wcsncmp(s, header_31, 7)) /* "REGEDIT" without NUL */
+        return REG_VERSION_FUZZY;
 
-    if ( stdInput[0] == L'[')           /* We are reading a new key */
-    {
-        WCHAR* keyEnd;
-        closeKey();                     /* Close the previous key */
+    return REG_VERSION_INVALID;
+}
 
-        /* Get rid of the square brackets */
-        stdInput++;
-        keyEnd = wcsrchr(stdInput, L']');
-        if (keyEnd)
-            *keyEnd='\0';
+/* handler for parser HEADER state */
+static WCHAR *header_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *line, *header;
 
-        /* delete the key if we encounter '-' at the start of reg key */
-        if ( stdInput[0] == '-')
-        {
-            delete_registry_key(stdInput + 1);
-        }
-        else if ( openKeyW(stdInput) != ERROR_SUCCESS )
-        {
-            char* stdInputA = GetMultiByteString(stdInput);
-            fprintf(stderr,"%S: setValue failed to open key %s\n",
-            getAppName(), stdInputA);
-            HeapFree(GetProcessHeap(), 0, stdInputA);
-        }
-    }
-    else if( currentKeyHandle &&
-    (( stdInput[0] == '@') || /* reading a default @=data pair */
-    ( stdInput[0] == '\"'))) /* reading a new value=data pair */
+    if (!(line = get_line(parser->file)))
+        return NULL;
+
+    if (!parser->is_unicode)
     {
-        processSetValue(stdInput, isUnicode);
+        header = malloc((lstrlenW(line) + 3) * sizeof(WCHAR));
+        header[0] = parser->two_wchars[0];
+        header[1] = parser->two_wchars[1];
+        lstrcpyW(header + 2, line);
+        parser->reg_version = parse_file_header(header);
+        free(header);
     }
-    else
+    else parser->reg_version = parse_file_header(line);
+
+    switch (parser->reg_version)
     {
-        /* Since we are assuming that the file format is valid we must be
-         * reading a blank line which indicates the end of this key processing
-         */
-        closeKey();
+    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;
 }
 
-/******************************************************************************
- * Processes a registry file.
- * Correctly processes comments (in # form), line continuation.
- *
- * Parameters:
- *   in - input stream to read from
- */
-static void processRegLinesA(FILE *in)
+/* handler for parser PARSE_WIN31_LINE state */
+static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos)
 {
-    LPSTR line           = NULL;  /* line read from input stream */
-    ULONG lineSize       = REG_VAL_BUF_SIZE;
+    WCHAR *line, *value;
+    static WCHAR hkcr[] = L"HKEY_CLASSES_ROOT";
+    unsigned int key_end = 0;
 
-    line = HeapAlloc(GetProcessHeap(), 0, lineSize);
-    CHECK_ENOUGH_MEMORY(line);
+    if (!(line = get_line(parser->file)))
+        return NULL;
 
-    while (!feof(in))
-    {
-        LPSTR s; /* The pointer into line for where the current fgets should read */
-        LPSTR check;
-        WCHAR* lineW;
-        s = line;
+    if (wcsncmp(line, hkcr, lstrlenW(hkcr)))
+        return line;
 
-        for (;;)
-        {
-            size_t size_remaining;
-            int size_to_get;
-            char *s_eol; /* various local uses */
-
-            /* Do we need to expand the buffer ? */
-            assert (s >= line && s <= line + lineSize);
-            size_remaining = lineSize - (s-line);
-            if (size_remaining < 2) /* room for 1 character and the \0 */
-            {
-                char *new_buffer;
-                size_t new_size = lineSize + REG_VAL_BUF_SIZE;
-                if (new_size > lineSize) /* no arithmetic overflow */
-                    new_buffer = HeapReAlloc (GetProcessHeap(), 0, line, new_size);
-                else
-                    new_buffer = NULL;
-                CHECK_ENOUGH_MEMORY(new_buffer);
-                line = new_buffer;
-                s = line + lineSize - size_remaining;
-                lineSize = new_size;
-                size_remaining = lineSize - (s-line);
-            }
+    /* get key name */
+    while (line[key_end] && !iswspace(line[key_end])) key_end++;
 
-            /* Get as much as possible into the buffer, terminated either by
-             * eof, error, eol or getting the maximum amount.  Abort on error.
-             */
-            size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
+    value = line + key_end;
+    while (*value == ' ' || *value == '\t') value++;
 
-            check = fgets (s, size_to_get, in);
+    if (*value == '=') value++;
+    if (*value == ' ') value++; /* at most one space is skipped */
 
-            if (check == NULL)
-            {
-                if (ferror(in))
-                {
-                    perror ("While reading input");
-                    exit (IO_ERROR);
-                }
-                else
-                {
-                    assert (feof(in));
-                    *s = '\0';
-                    /* It is not clear to me from the definition that the
-                     * contents of the buffer are well defined on detecting
-                     * an eof without managing to read anything.
-                     */
-                }
-            }
+    line[key_end] = 0;
 
-            /* If we didn't read the eol nor the eof go around for the rest */
-            s_eol = strchr (s, '\n');
-            if (!feof (in) && !s_eol)
-            {
-                s = strchr (s, '\0');
-                /* It should be s + size_to_get - 1 but this is safer */
-                continue;
-            }
+    if (open_key(parser, line) != ERROR_SUCCESS)
+    {
+        output_message(STRING_OPEN_KEY_FAILED, line);
+        return line;
+    }
 
-            /* If it is a comment line then discard it and go around again */
-            if (line [0] == '#')
-            {
-                s = line;
-                continue;
-            }
+    parser->value_name = NULL;
+    parser->data_type = REG_SZ;
+    parser->data = value;
+    parser->data_size = (lstrlenW(value) + 1) * sizeof(WCHAR);
 
-            /* Remove any line feed.  Leave s_eol on the \0 */
-            if (s_eol)
-            {
-                *s_eol = '\0';
-                if (s_eol > line && *(s_eol-1) == '\r')
-                    *--s_eol = '\0';
-            }
-            else
-                s_eol = strchr (s, '\0');
+    set_state(parser, SET_VALUE);
+    return value;
+}
 
-            /* If there is a concatenating \\ then go around again */
-            if (s_eol > line && *(s_eol-1) == '\\')
-            {
-                int c;
-                s = s_eol-1;
-
-                do
-                {
-                    c = fgetc(in);
-                }
-                while(c == ' ' || c == '\t');
-
-                if(c == EOF)
-                {
-                    fprintf(stderr,"%S: ERROR - invalid continuation.\n",
-                    getAppName());
-                }
-                else
-                {
-                    *s = c;
-                    s++;
-                }
-                continue;
-            }
+/* handler for parser LINE_START state */
+static WCHAR *line_start_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *line, *p;
 
-            lineW = GetWideString(line);
+    if (!(line = get_line(parser->file)))
+        return NULL;
 
-            break; /* That is the full virtual line */
+    for (p = line; *p; p++)
+    {
+        switch (*p)
+        {
+        case '[':
+            set_state(parser, KEY_NAME);
+            return p + 1;
+        case '@':
+            set_state(parser, DEFAULT_VALUE_NAME);
+            return p;
+        case '"':
+            set_state(parser, QUOTED_VALUE_NAME);
+            return p + 1;
+        case ' ':
+        case '\t':
+            break;
+        default:
+            return p;
         }
-
-        processRegEntry(lineW, FALSE);
-        HeapFree(GetProcessHeap(), 0, lineW);
     }
-    processRegEntry(NULL, FALSE);
 
-    HeapFree(GetProcessHeap(), 0, line);
+    return p;
 }
 
-static void processRegLinesW(FILE *in)
+/* handler for parser KEY_NAME state */
+static WCHAR *key_name_state(struct parser *parser, WCHAR *pos)
 {
-    WCHAR* buf           = NULL;  /* line read from input stream */
-    ULONG lineSize       = REG_VAL_BUF_SIZE;
-    size_t CharsInBuf = -1;
-
-    WCHAR* s; /* The pointer into buf for where the current fgets should read */
-    WCHAR* line; /* The start of the current line */
+    WCHAR *p = pos, *key_end;
 
-    buf = HeapAlloc(GetProcessHeap(), 0, lineSize * sizeof(WCHAR));
-    CHECK_ENOUGH_MEMORY(buf);
+    if (*p == ' ' || *p == '\t' || !(key_end = wcsrchr(p, ']')))
+        goto done;
 
-    s = buf;
-    line = buf;
+    *key_end = 0;
 
-    while(!feof(in))
+    if (*p == '-')
     {
-        size_t size_remaining;
-        int size_to_get;
-        WCHAR *s_eol = NULL; /* various local uses */
-
-        /* Do we need to expand the buffer ? */
-        assert (s >= buf && s <= buf + lineSize);
-        size_remaining = lineSize - (s-buf);
-        if (size_remaining < 2) /* room for 1 character and the \0 */
-        {
-            WCHAR *new_buffer;
-            size_t new_size = lineSize + (REG_VAL_BUF_SIZE / sizeof(WCHAR));
-            if (new_size > lineSize) /* no arithmetic overflow */
-                new_buffer = HeapReAlloc (GetProcessHeap(), 0, buf, new_size * sizeof(WCHAR));
-            else
-                new_buffer = NULL;
-            CHECK_ENOUGH_MEMORY(new_buffer);
-            buf = new_buffer;
-            line = buf;
-            s = buf + lineSize - size_remaining;
-            lineSize = new_size;
-            size_remaining = lineSize - (s-buf);
-        }
+        set_state(parser, DELETE_KEY);
+        return p + 1;
+    }
+    else if (open_key(parser, p) != ERROR_SUCCESS)
+        output_message(STRING_OPEN_KEY_FAILED, p);
 
-        /* Get as much as possible into the buffer, terminated either by
-        * eof, error or getting the maximum amount.  Abort on error.
-        */
-        size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
+done:
+    set_state(parser, LINE_START);
+    return p;
+}
 
-        CharsInBuf = fread(s, sizeof(WCHAR), size_to_get - 1, in);
-        s[CharsInBuf] = 0;
+/* handler for parser DELETE_KEY state */
+static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *p = pos;
 
-        if (CharsInBuf == 0)
-        {
-            if (ferror(in))
-            {
-                perror ("While reading input");
-                exit (IO_ERROR);
-            }
-            else
-            {
-                assert (feof(in));
-                *s = '\0';
-                /* It is not clear to me from the definition that the
-                * contents of the buffer are well defined on detecting
-                * an eof without managing to read anything.
-                */
-            }
-        }
+    close_key(parser);
 
-        /* If we didn't read the eol nor the eof go around for the rest */
-        while(1)
-        {
-            s_eol = wcschr(line, '\n');
+    if (*p == 'H' || *p == 'h')
+        delete_registry_key(p);
 
-            if(!s_eol)
-            {
-                /* Move the stub of the line to the start of the buffer so
-                 * we get the maximum space to read into, and so we don't
-                 * have to recalculate 'line' if the buffer expands */
-                MoveMemory(buf, line, (lstrlenW(line) + 1) * sizeof(WCHAR));
-                line = buf;
-                s = wcschr(line, '\0');
-                break;
-            }
+    set_state(parser, LINE_START);
+    return p;
+}
 
-            /* If it is a comment line then discard it and go around again */
-            if (*line == '#')
-            {
-                line = s_eol + 1;
-                continue;
-            }
+/* handler for parser DEFAULT_VALUE_NAME state */
+static WCHAR *default_value_name_state(struct parser *parser, WCHAR *pos)
+{
+    free(parser->value_name);
+    parser->value_name = NULL;
 
-            /* If there is a concatenating \\ then go around again */
-            if ((*(s_eol-1) == '\\') ||
-            (*(s_eol-1) == '\r' && *(s_eol-2) == '\\'))
-            {
-                WCHAR* NextLine = s_eol;
+    set_state(parser, DATA_START);
+    return pos + 1;
+}
 
-                while(*(NextLine+1) == ' ' || *(NextLine+1) == '\t')
-                    NextLine++;
+/* handler for parser QUOTED_VALUE_NAME state */
+static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *val_name = pos, *p;
 
-                NextLine++;
+    free(parser->value_name);
+    parser->value_name = NULL;
 
-                if(*(s_eol-1) == '\r')
-                    s_eol--;
+    if (!REGPROC_unescape_string(val_name, &p))
+        goto invalid;
 
-                MoveMemory(s_eol - 1, NextLine, (CharsInBuf - (NextLine - s) + 1)*sizeof(WCHAR));
-                CharsInBuf -= NextLine - s_eol + 1;
-                s_eol = 0;
-                continue;
-            }
+    /* copy the value name in case we need to parse multiple lines and the buffer is overwritten */
+    parser->value_name = malloc((lstrlenW(val_name) + 1) * sizeof(WCHAR));
+    lstrcpyW(parser->value_name, val_name);
 
-            /* Remove any line feed.  Leave s_eol on the \0 */
-            if (s_eol)
-            {
-                *s_eol = '\0';
-                if (s_eol > buf && *(s_eol-1) == '\r')
-                    *(s_eol-1) = '\0';
-            }
+    set_state(parser, DATA_START);
+    return p;
 
-            if(!s_eol)
-                break;
+invalid:
+    set_state(parser, LINE_START);
+    return val_name;
+}
 
-            processRegEntry(line, TRUE);
-            line = s_eol + 1;
-            s_eol = 0;
-            continue; /* That is the full virtual line */
-        }
-    }
+/* handler for parser DATA_START state */
+static WCHAR *data_start_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *p = pos;
+    unsigned int len;
+
+    while (*p == ' ' || *p == '\t') p++;
+    if (*p != '=') goto done;
+    p++;
+    while (*p == ' ' || *p == '\t') p++;
 
-    processRegEntry(NULL, TRUE);
+    /* trim trailing whitespace */
+    len = lstrlenW(p);
+    while (len > 0 && (p[len - 1] == ' ' || p[len - 1] == '\t')) len--;
+    p[len] = 0;
+
+    if (*p == '-')
+        set_state(parser, DELETE_VALUE);
+    else
+        set_state(parser, DATA_TYPE);
+    return p;
 
-    HeapFree(GetProcessHeap(), 0, buf);
+done:
+    set_state(parser, LINE_START);
+    return p;
 }
 
-/****************************************************************************
- * REGPROC_print_error
- *
- * Print the message for GetLastError
- */
+/* handler for parser DELETE_VALUE state */
+static WCHAR *delete_value_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *p = pos + 1;
+
+    while (*p == ' ' || *p == '\t') p++;
+    if (*p && *p != ';') goto done;
 
-static void REGPROC_print_error(void)
+    RegDeleteValueW(parser->hkey, parser->value_name);
+
+done:
+    set_state(parser, LINE_START);
+    return p;
+}
+
+/* handler for parser DATA_TYPE state */
+static WCHAR *data_type_state(struct parser *parser, WCHAR *pos)
 {
-    LPVOID lpMsgBuf;
-    DWORD error_code;
-    int status;
-
-    error_code = GetLastError ();
-    status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-    NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
-    if (!status)
+    WCHAR *line = pos;
+
+    if (!parse_data_type(parser, &line))
     {
-        fprintf(stderr,"%S: Cannot display message for error %ld, status %ld\n",
-        getAppName(), error_code, GetLastError());
-        exit(1);
+        set_state(parser, LINE_START);
+        return line;
     }
-    puts(lpMsgBuf);
-    LocalFree(lpMsgBuf);
-    exit(1);
-}
 
-/******************************************************************************
- * Checks whether the buffer has enough room for the string or required size.
- * Resizes the buffer if necessary.
- *
- * Parameters:
- * buffer - pointer to a buffer for string
- * len - current length of the buffer in characters.
- * required_len - length of the string to place to the buffer in characters.
- *   The length does not include the terminating null character.
- */
-static void REGPROC_resize_char_buffer(WCHAR **buffer, DWORD *len, DWORD required_len)
-{
-    required_len++;
-    if (required_len > *len)
+    switch (parser->parse_type)
     {
-        *len = required_len;
-        if (!*buffer)
-            *buffer = HeapAlloc(GetProcessHeap(), 0, *len * sizeof(**buffer));
-        else
-            *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *len * sizeof(**buffer));
-        CHECK_ENOUGH_MEMORY(*buffer);
+    case REG_SZ:
+        set_state(parser, STRING_DATA);
+        break;
+    case REG_DWORD:
+        set_state(parser, DWORD_DATA);
+        break;
+    case REG_BINARY: /* all hex data types, including undefined */
+        set_state(parser, HEX_DATA);
+        break;
+    default:
+        set_state(parser, UNKNOWN_DATA);
     }
+
+    return line;
 }
 
-/******************************************************************************
- * Same as REGPROC_resize_char_buffer() but on a regular buffer.
- *
- * Parameters:
- * buffer - pointer to a buffer
- * len - current size of the buffer in bytes
- * required_size - size of the data to place in the buffer in bytes
- */
-static void REGPROC_resize_binary_buffer(BYTE **buffer, DWORD *size, DWORD required_size)
+/* handler for parser STRING_DATA state */
+static WCHAR *string_data_state(struct parser *parser, WCHAR *pos)
 {
-    if (required_size > *size)
+    WCHAR *line;
+
+    parser->data = pos;
+
+    if (!REGPROC_unescape_string(parser->data, &line))
+        goto invalid;
+
+    while (*line == ' ' || *line == '\t') line++;
+    if (*line && *line != ';') goto invalid;
+
+    parser->data_size = (lstrlenW(parser->data) + 1) * sizeof(WCHAR);
+
+    set_state(parser, SET_VALUE);
+    return line;
+
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return line;
+}
+
+/* handler for parser DWORD_DATA state */
+static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *line = pos;
+
+    parser->data = malloc(sizeof(DWORD));
+
+    if (!convert_hex_to_dword(line, parser->data))
+        goto invalid;
+
+    parser->data_size = sizeof(DWORD);
+
+    set_state(parser, SET_VALUE);
+    return line;
+
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return line;
+}
+
+/* handler for parser HEX_DATA state */
+static WCHAR *hex_data_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *line = pos;
+
+    if (!*line)
+        goto set_value;
+
+    if (!convert_hex_csv_to_hex(parser, &line))
+        goto invalid;
+
+    if (parser->backslash)
     {
-        *size = required_size;
-        if (!*buffer)
-            *buffer = HeapAlloc(GetProcessHeap(), 0, *size);
-        else
-            *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *size);
-        CHECK_ENOUGH_MEMORY(*buffer);
+        set_state(parser, EOL_BACKSLASH);
+        return line;
     }
+
+    prepare_hex_string_data(parser);
+
+set_value:
+    set_state(parser, SET_VALUE);
+    return line;
+
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return line;
 }
 
-/******************************************************************************
- * Prints string str to file
- */
-static void REGPROC_export_string(WCHAR **line_buf, DWORD *line_buf_size, DWORD *line_len, WCHAR *str, DWORD str_len)
+/* handler for parser EOL_BACKSLASH state */
+static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos)
 {
-    DWORD i, pos;
-    DWORD extra = 0;
+    WCHAR *p = pos;
 
-    REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + 10);
+    while (*p == ' ' || *p == '\t') p++;
+    if (*p && *p != ';') goto invalid;
 
-    /* escaping characters */
-    pos = *line_len;
-    for (i = 0; i < str_len; i++)
-    {
-        WCHAR c = str[i];
-        switch (c)
-        {
-        case '\n':
-            extra++;
-            REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + extra);
-            (*line_buf)[pos++] = '\\';
-            (*line_buf)[pos++] = 'n';
-            break;
+    set_state(parser, HEX_MULTILINE);
+    return pos;
 
-        case '\\':
-        case '"':
-            extra++;
-            REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + extra);
-            (*line_buf)[pos++] = '\\';
-            /* Fall through */
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return p;
+}
 
-        default:
-            (*line_buf)[pos++] = c;
-            break;
-        }
+/* handler for parser HEX_MULTILINE state */
+static WCHAR *hex_multiline_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *line;
+
+    if (!(line = get_line(parser->file)))
+    {
+        prepare_hex_string_data(parser);
+        set_state(parser, SET_VALUE);
+        return pos;
     }
-    (*line_buf)[pos] = '\0';
-    *line_len = pos;
+
+    while (*line == ' ' || *line == '\t') line++;
+    if (!*line || *line == ';') return line;
+
+    if (!iswxdigit(*line)) goto invalid;
+
+    set_state(parser, HEX_DATA);
+    return line;
+
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return line;
 }
 
-static void REGPROC_export_binary(WCHAR **line_buf, DWORD *line_buf_size, DWORD *line_len, DWORD type, BYTE *value, DWORD value_size, BOOL unicode)
+/* handler for parser UNKNOWN_DATA state */
+static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos)
 {
-    DWORD hex_pos, data_pos;
-    const WCHAR *hex_prefix;
-    const WCHAR hex[] = {'h','e','x',':',0};
-    WCHAR hex_buf[17];
-    const WCHAR concat[] = {'\\','\n',' ',' ',0};
-    DWORD concat_prefix, concat_len;
-    const WCHAR newline[] = {'\n',0};
-    CHAR* value_multibyte = NULL;
+    output_message(STRING_UNKNOWN_DATA_FORMAT, parser->data_type);
 
-    if (type == REG_BINARY)
+    set_state(parser, LINE_START);
+    return pos;
+}
+
+/* handler for parser SET_VALUE state */
+static WCHAR *set_value_state(struct parser *parser, WCHAR *pos)
+{
+    RegSetValueExW(parser->hkey, parser->value_name, 0, parser->data_type,
+                   parser->data, parser->data_size);
+
+    free_parser_data(parser);
+
+    if (parser->reg_version == REG_VERSION_31)
+        set_state(parser, PARSE_WIN31_LINE);
+    else
+        set_state(parser, LINE_START);
+
+    return pos;
+}
+
+static WCHAR *get_lineA(FILE *fp)
+{
+    static WCHAR *lineW;
+    static size_t size;
+    static char *buf, *next;
+    char *line;
+
+    free(lineW);
+
+    if (!fp) goto cleanup;
+
+    if (!size)
     {
-        hex_prefix = hex;
+        size = REG_VAL_BUF_SIZE;
+        buf = malloc(size);
+        *buf = 0;
+        next = buf;
     }
-    else
+    line = next;
+
+    while (next)
     {
-        const WCHAR hex_format[] = {'h','e','x','(','%','u',')',':',0};
-        hex_prefix = hex_buf;
-        wsprintfW(hex_buf, hex_format, type);
-        if ((type == REG_SZ || type == REG_EXPAND_SZ || type == REG_MULTI_SZ) && !unicode)
+        char *p = strpbrk(line, "\r\n");
+        if (!p)
         {
-            value_multibyte = GetMultiByteStringN((WCHAR*)value, value_size / sizeof(WCHAR), &value_size);
-            value = (BYTE*)value_multibyte;
+            size_t len, count;
+            len = strlen(next);
+            memmove(buf, next, len + 1);
+            if (size - len < 3)
+            {
+                size *= 2;
+                buf = realloc(buf, size);
+            }
+            if (!(count = fread(buf + len, 1, size - len - 1, fp)))
+            {
+                next = NULL;
+                lineW = GetWideString(buf);
+                return lineW;
+            }
+            buf[len + count] = 0;
+            next = buf;
+            line = buf;
+            continue;
         }
+        next = p + 1;
+        if (*p == '\r' && *(p + 1) == '\n') next++;
+        *p = 0;
+        lineW = GetWideString(line);
+        return lineW;
     }
 
-    concat_len = lstrlenW(concat);
-    concat_prefix = 2;
-
-    hex_pos = *line_len;
-    *line_len += lstrlenW(hex_prefix);
-    data_pos = *line_len;
-    *line_len += value_size * 3;
-    /* - The 2 spaces that concat places at the start of the
-     *   line effectively reduce the space available for data.
-     * - If the value name and hex prefix are very long
-     *   ( > REG_FILE_HEX_LINE_LEN) then we may overestimate
-     *   the needed number of lines by one. But that's ok.
-     * - The trailing linefeed takes the place of a comma so
-     *   it's accounted for already.
-     */
-    *line_len += *line_len / (REG_FILE_HEX_LINE_LEN - concat_prefix) * concat_len;
-    REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len);
-    lstrcpyW(*line_buf + hex_pos, hex_prefix);
-    if (value_size)
-    {
-        const WCHAR format[] = {'%','0','2','x',0};
-        DWORD i, column;
+cleanup:
+    lineW = NULL;
+    if (size) free(buf);
+    size = 0;
+    return NULL;
+}
 
-        column = data_pos; /* no line wrap yet */
-        i = 0;
-        while (1)
-        {
-            wsprintfW(*line_buf + data_pos, format, (unsigned int)value[i]);
-            data_pos += 2;
-            if (++i == value_size)
-                break;
+static WCHAR *get_lineW(FILE *fp)
+{
+    static size_t size;
+    static WCHAR *buf, *next;
+    WCHAR *line;
 
-            (*line_buf)[data_pos++] = ',';
-            column += 3;
+    if (!fp) goto cleanup;
 
-            /* wrap the line */
-            if (column >= REG_FILE_HEX_LINE_LEN)
+    if (!size)
+    {
+        size = REG_VAL_BUF_SIZE;
+        buf = malloc(size * sizeof(WCHAR));
+        *buf = 0;
+        next = buf;
+    }
+    line = next;
+
+    while (next)
+    {
+        WCHAR *p = wcspbrk(line, L"\r\n");
+        if (!p)
+        {
+            size_t len, count;
+            len = lstrlenW(next);
+            memmove(buf, next, (len + 1) * sizeof(WCHAR));
+            if (size - len < 3)
+            {
+                size *= 2;
+                buf = realloc(buf, size * sizeof(WCHAR));
+            }
+            if (!(count = fread(buf + len, sizeof(WCHAR), size - len - 1, fp)))
             {
-                lstrcpyW(*line_buf + data_pos, concat);
-                data_pos += concat_len;
-                column = concat_prefix;
+                next = NULL;
+                return buf;
             }
+            buf[len + count] = 0;
+            next = buf;
+            line = buf;
+            continue;
         }
+        next = p + 1;
+        if (*p == '\r' && *(p + 1) == '\n') next++;
+        *p = 0;
+        return line;
     }
-    lstrcpyW(*line_buf + data_pos, newline);
-    HeapFree(GetProcessHeap(), 0, value_multibyte);
+
+cleanup:
+    if (size) free(buf);
+    size = 0;
+    return NULL;
 }
 
 /******************************************************************************
- * Writes the given line to a file, in multi-byte or wide characters
+ * Reads contents of the specified file into the registry.
  */
-static void REGPROC_write_line(FILE *file, const WCHAR* str, BOOL unicode)
+BOOL import_registry_file(FILE *reg_file)
+{
+    BYTE s[2];
+    struct parser parser;
+    WCHAR *pos;
+
+    if (!reg_file || (fread(s, 2, 1, reg_file) != 1))
+        return FALSE;
+
+    parser.is_unicode = (s[0] == 0xff && s[1] == 0xfe);
+    get_line = parser.is_unicode ? get_lineW : get_lineA;
+
+    parser.file          = reg_file;
+    parser.two_wchars[0] = s[0];
+    parser.two_wchars[1] = s[1];
+    parser.reg_version   = -1;
+    parser.hkey          = NULL;
+    parser.key_name      = NULL;
+    parser.value_name    = NULL;
+    parser.parse_type    = 0;
+    parser.data_type     = 0;
+    parser.data          = NULL;
+    parser.data_size     = 0;
+    parser.backslash     = FALSE;
+    parser.state         = HEADER;
+
+    pos = parser.two_wchars;
+
+    /* parser main loop */
+    while (pos)
+        pos = (parser_funcs[parser.state])(&parser, pos);
+
+    if (parser.reg_version == REG_VERSION_FUZZY || parser.reg_version == REG_VERSION_INVALID)
+        return parser.reg_version == REG_VERSION_FUZZY;
+
+    free(parser.value_name);
+    close_key(&parser);
+
+    return TRUE;
+}
+
+/******************************************************************************
+ * Removes the registry key with all subkeys. Parses full key name.
+ *
+ * Parameters:
+ * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
+ *      empty, points to register key class, does not exist.
+ */
+void delete_registry_key(WCHAR *reg_key_name)
+{
+    WCHAR *key_name = NULL;
+    HKEY key_class;
+
+    if (!reg_key_name || !reg_key_name[0])
+        return;
+
+    if (!(key_class = parse_key_name(reg_key_name, &key_name)))
+    {
+        if (key_name) *(key_name - 1) = 0;
+#ifdef __REACTOS__
+        output_message(STRING_INVALID_SYSTEM_KEY, reg_key_name);
+        return;
+#else
+        error_exit(STRING_INVALID_SYSTEM_KEY, reg_key_name);
+#endif
+    }
+
+    if (!key_name || !*key_name)
+#ifdef __REACTOS__
+    {
+        output_message(STRING_DELETE_FAILED, reg_key_name);
+        return;
+    }
+#else
+        error_exit(STRING_DELETE_FAILED, reg_key_name);
+#endif
+
+#ifdef __REACTOS__
+    SHDeleteKey(key_class, key_name);
+#else
+    RegDeleteTreeW(key_class, key_name);
+#endif
+}
+
+static void REGPROC_write_line(FILE *fp, const WCHAR *str, BOOL unicode)
 {
-    int i;
     if (unicode)
+        fwrite(str, sizeof(WCHAR), lstrlenW(str), fp);
+    else
     {
-        for(i = 0; str[i]; i++)
+        char *strA = GetMultiByteString(str);
+        fputs(strA, fp);
+        free(strA);
+    }
+}
+
+static WCHAR *REGPROC_escape_string(WCHAR *str, size_t str_len, size_t *line_len)
+{
+    size_t i, escape_count, pos;
+    WCHAR *buf;
+
+    for (i = 0, escape_count = 0; i < str_len; i++)
+    {
+        WCHAR c = str[i];
+
+        if (!c) break;
+
+        if (c == '\r' || c == '\n' || c == '\\' || c == '"')
+            escape_count++;
+    }
+
+    buf = malloc((str_len + escape_count + 1) * sizeof(WCHAR));
+
+    for (i = 0, pos = 0; i < str_len; i++, pos++)
+    {
+        WCHAR c = str[i];
+
+        if (!c) break;
+
+        switch (c)
         {
-            if (str[i] == L'\n')
-                fputwc(L'\r', file);
-            fputwc(str[i], file);
+        case '\r':
+            buf[pos++] = '\\';
+            buf[pos] = 'r';
+            break;
+        case '\n':
+            buf[pos++] = '\\';
+            buf[pos] = 'n';
+            break;
+        case '\\':
+            buf[pos++] = '\\';
+            buf[pos] = '\\';
+            break;
+        case '"':
+            buf[pos++] = '\\';
+            buf[pos] = '"';
+            break;
+        default:
+            buf[pos] = c;
         }
     }
+
+    buf[pos] = 0;
+    *line_len = pos;
+    return buf;
+}
+
+static size_t export_value_name(FILE *fp, WCHAR *name, size_t len, BOOL unicode)
+{
+    static const WCHAR default_name[] = L"@=";
+    size_t line_len;
+
+    if (name && *name)
+    {
+        WCHAR *str = REGPROC_escape_string(name, len, &line_len);
+        WCHAR *buf = malloc((line_len + 4) * sizeof(WCHAR));
+#ifdef __REACTOS__
+        StringCchPrintfW(buf, line_len + 4, L"\"%s\"=", str);
+        line_len = wcslen(buf);
+#else
+        line_len = swprintf(buf, line_len + 4, L"\"%s\"=", str);
+#endif
+        REGPROC_write_line(fp, buf, unicode);
+        free(buf);
+        free(str);
+    }
     else
     {
-        char* strA = GetMultiByteString(str);
-        fputs(strA, file);
-        HeapFree(GetProcessHeap(), 0, strA);
+        line_len = lstrlenW(default_name);
+        REGPROC_write_line(fp, default_name, unicode);
     }
+
+    return line_len;
 }
 
-/******************************************************************************
- * Writes contents of the registry key to the specified file stream.
- *
- * Parameters:
- * file - writable file stream to export registry branch to.
- * key - registry branch to export.
- * reg_key_name_buf - name of the key with registry class.
- *      Is resized if necessary.
- * reg_key_name_size - length of the buffer for the registry class in characters.
- * val_name_buf - buffer for storing value name.
- *      Is resized if necessary.
- * val_name_size - length of the buffer for storing value names in characters.
- * val_buf - buffer for storing values while extracting.
- *      Is resized if necessary.
- * val_size - size of the buffer for storing values in bytes.
- */
-static void export_hkey(FILE *file, HKEY key,
-WCHAR **reg_key_name_buf, DWORD *reg_key_name_size,
-WCHAR **val_name_buf, DWORD *val_name_size,
-BYTE **val_buf, DWORD *val_size,
-WCHAR **line_buf, DWORD *line_buf_size,
-BOOL unicode)
+static void export_string_data(WCHAR **buf, WCHAR *data, size_t size)
+{
+    size_t len = 0, line_len;
+    WCHAR *str;
+
+    if (size)
+        len = size / sizeof(WCHAR) - 1;
+    str = REGPROC_escape_string(data, len, &line_len);
+    *buf = malloc((line_len + 3) * sizeof(WCHAR));
+#ifdef __REACTOS__
+    StringCchPrintfW(*buf, line_len + 3, L"\"%s\"", str);
+#else
+    swprintf(*buf, line_len + 3, L"\"%s\"", str);
+#endif
+    free(str);
+}
+
+static void export_dword_data(WCHAR **buf, DWORD *data)
+{
+    *buf = malloc(15 * sizeof(WCHAR));
+#ifdef __REACTOS__
+    StringCchPrintfW(*buf, 15, L"dword:%08x", *data);
+#else
+    swprintf(*buf, 15, L"dword:%08x", *data);
+#endif
+}
+
+static size_t export_hex_data_type(FILE *fp, DWORD type, BOOL unicode)
 {
-    DWORD max_sub_key_len;
-    DWORD max_val_name_len;
-    DWORD max_val_size;
-    DWORD curr_len;
-    DWORD i;
-    BOOL more_data;
-    LONG ret;
-    WCHAR key_format[] = {'\n','[','%','s',']','\n',0};
-
-    /* get size information and resize the buffers if necessary */
-    if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL,
-    &max_sub_key_len, NULL,
-    NULL, &max_val_name_len, &max_val_size, NULL, NULL
-                        ) != ERROR_SUCCESS)
+    static const WCHAR hex[] = L"hex:";
+    size_t line_len;
+
+    if (type == REG_BINARY)
     {
-        REGPROC_print_error();
+        line_len = lstrlenW(hex);
+        REGPROC_write_line(fp, hex, unicode);
     }
-    curr_len = lstrlenW(*reg_key_name_buf);
-    REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_size,
-    max_sub_key_len + curr_len + 1);
-    REGPROC_resize_char_buffer(val_name_buf, val_name_size,
-    max_val_name_len);
-    REGPROC_resize_binary_buffer(val_buf, val_size, max_val_size);
-    REGPROC_resize_char_buffer(line_buf, line_buf_size, lstrlenW(*reg_key_name_buf) + 4);
-    /* output data for the current key */
-    wsprintfW(*line_buf, key_format, *reg_key_name_buf);
-    REGPROC_write_line(file, *line_buf, unicode);
-
-    /* print all the values */
-    i = 0;
-    more_data = TRUE;
-    while(more_data)
+    else
+    {
+        WCHAR *buf = malloc(15 * sizeof(WCHAR));
+#ifdef __REACTOS__
+        StringCchPrintfW(buf, 15, L"hex(%x):", type);
+        line_len = wcslen(buf);
+#else
+        line_len = swprintf(buf, 15, L"hex(%x):", type);
+#endif
+        REGPROC_write_line(fp, buf, unicode);
+        free(buf);
+    }
+
+    return line_len;
+}
+
+#define MAX_HEX_CHARS 77
+
+static void export_hex_data(FILE *fp, WCHAR **buf, DWORD type, DWORD line_len,
+                            void *data, DWORD size, BOOL unicode)
+{
+    size_t num_commas, i, pos;
+
+    line_len += export_hex_data_type(fp, type, unicode);
+
+    if (!size) return;
+
+    if (!unicode && (type == REG_EXPAND_SZ || type == REG_MULTI_SZ))
+        data = GetMultiByteStringN(data, size / sizeof(WCHAR), &size);
+
+    num_commas = size - 1;
+    *buf = malloc(size * 3 * sizeof(WCHAR));
+
+    for (i = 0, pos = 0; i < size; i++)
     {
-        DWORD value_type;
-        DWORD val_name_size1 = *val_name_size;
-        DWORD val_size1 = *val_size;
-        ret = RegEnumValueW(key, i, *val_name_buf, &val_name_size1, NULL,
-        &value_type, *val_buf, &val_size1);
-        if (ret == ERROR_MORE_DATA)
+#ifdef __REACTOS__
+        StringCchPrintfW(*buf + pos, 3, L"%02x", ((BYTE *)data)[i]);
+        pos += wcslen(*buf + pos);
+#else
+        pos += swprintf(*buf + pos, 3, L"%02x", ((BYTE *)data)[i]);
+#endif
+        if (i == num_commas) break;
+        (*buf)[pos++] = ',';
+        (*buf)[pos] = 0;
+        line_len += 3;
+
+        if (line_len >= MAX_HEX_CHARS)
         {
-            /* Increase the size of the buffers and retry */
-            REGPROC_resize_char_buffer(val_name_buf, val_name_size, val_name_size1);
-            REGPROC_resize_binary_buffer(val_buf, val_size, val_size1);
+            REGPROC_write_line(fp, *buf, unicode);
+            REGPROC_write_line(fp, L"\\\r\n  ", unicode);
+            line_len = 2;
+            pos = 0;
         }
-        else if (ret != ERROR_SUCCESS)
+    }
+}
+
+static void export_newline(FILE *fp, BOOL unicode)
+{
+    REGPROC_write_line(fp, L"\r\n", unicode);
+}
+
+static void export_data(FILE *fp, WCHAR *value_name, DWORD value_len, DWORD type,
+                        void *data, size_t size, BOOL unicode)
+{
+    WCHAR *buf = NULL;
+    size_t line_len = export_value_name(fp, value_name, value_len, unicode);
+
+    switch (type)
+    {
+    case REG_SZ:
+        export_string_data(&buf, data, size);
+        break;
+    case REG_DWORD:
+        if (size)
         {
-            more_data = FALSE;
-            if (ret != ERROR_NO_MORE_ITEMS)
-            {
-                REGPROC_print_error();
-            }
+            export_dword_data(&buf, data);
+            break;
         }
-        else
-        {
-            DWORD line_len;
-            i++;
+        /* fall through */
+    case REG_NONE:
+    case REG_EXPAND_SZ:
+    case REG_BINARY:
+    case REG_MULTI_SZ:
+    default:
+        export_hex_data(fp, &buf, type, line_len, data, size, unicode);
+        break;
+    }
 
-            if ((*val_name_buf)[0])
-            {
-                const WCHAR val_start[] = {'"','%','s','"','=',0};
+    if (size || type == REG_SZ)
+    {
+        REGPROC_write_line(fp, buf, unicode);
+        free(buf);
+    }
 
-                line_len = 0;
-                REGPROC_export_string(line_buf, line_buf_size, &line_len, *val_name_buf, lstrlenW(*val_name_buf));
-                REGPROC_resize_char_buffer(val_name_buf, val_name_size, lstrlenW(*line_buf) + 1);
-                lstrcpyW(*val_name_buf, *line_buf);
+    export_newline(fp, unicode);
+}
 
-                line_len = 3 + lstrlenW(*val_name_buf);
-                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len);
-                wsprintfW(*line_buf, val_start, *val_name_buf);
-            }
-            else
-            {
-                const WCHAR std_val[] = {'@','=',0};
-                line_len = 2;
-                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len);
-                lstrcpyW(*line_buf, std_val);
-            }
+static WCHAR *build_subkey_path(WCHAR *path, DWORD path_len, WCHAR *subkey_name, DWORD subkey_len)
+{
+    WCHAR *subkey_path;
 
-            switch (value_type)
-            {
-            case REG_SZ:
-            {
-                WCHAR* wstr = (WCHAR*)*val_buf;
-
-                if (val_size1 < sizeof(WCHAR) || val_size1 % sizeof(WCHAR) ||
-                wstr[val_size1 / sizeof(WCHAR) - 1])
-                {
-                    REGPROC_export_binary(line_buf, line_buf_size, &line_len, value_type, *val_buf, val_size1, unicode);
-                }
-                else
-                {
-                    const WCHAR start[] = {'"',0};
-                    const WCHAR end[] = {'"','\n',0};
-                    DWORD len;
-
-                    len = lstrlenW(start);
-                    REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + len);
-                    lstrcpyW(*line_buf + line_len, start);
-                    line_len += len;
-
-                    /* At this point we know wstr is '\0'-terminated
-                     * so we can substract 1 from the size
-                     */
-                    REGPROC_export_string(line_buf, line_buf_size, &line_len, wstr, val_size1 / sizeof(WCHAR) - 1);
-
-                    REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + lstrlenW(end));
-                    lstrcpyW(*line_buf + line_len, end);
-                }
-                break;
-            }
+    subkey_path = malloc((path_len + subkey_len + 2) * sizeof(WCHAR));
+#ifdef __REACTOS__
+    StringCchPrintfW(subkey_path, path_len + subkey_len + 2, L"%s\\%s", path, subkey_name);
+#else
+    swprintf(subkey_path, path_len + subkey_len + 2, L"%s\\%s", path, subkey_name);
+#endif
 
-            case REG_DWORD:
-            {
-                WCHAR format[] = {'d','w','o','r','d',':','%','0','8','x','\n',0};
+    return subkey_path;
+}
 
-                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + 15);
-                wsprintfW(*line_buf + line_len, format, *((DWORD *)*val_buf));
-                break;
-            }
+static void export_key_name(FILE *fp, WCHAR *name, BOOL unicode)
+{
+    WCHAR *buf;
+
+    buf = malloc((lstrlenW(name) + 7) * sizeof(WCHAR));
+#ifdef __REACTOS__
+    StringCchPrintfW(buf, lstrlenW(name) + 7, L"\r\n[%s]\r\n", name);
+#else
+    swprintf(buf, lstrlenW(name) + 7, L"\r\n[%s]\r\n", name);
+#endif
+    REGPROC_write_line(fp, buf, unicode);
+    free(buf);
+}
 
-            default:
-            {
-                char* key_nameA = GetMultiByteString(*reg_key_name_buf);
-                char* value_nameA = GetMultiByteString(*val_name_buf);
-                fprintf(stderr,"%S: warning - unsupported registry format '%ld', "
-                "treat as binary\n",
-                getAppName(), value_type);
-                fprintf(stderr,"key name: \"%s\"\n", key_nameA);
-                fprintf(stderr,"value name:\"%s\"\n\n", value_nameA);
-                HeapFree(GetProcessHeap(), 0, key_nameA);
-                HeapFree(GetProcessHeap(), 0, value_nameA);
-            }
-            /* falls through */
-            case REG_EXPAND_SZ:
-            case REG_MULTI_SZ:
-                /* falls through */
-            case REG_BINARY:
-                REGPROC_export_binary(line_buf, line_buf_size, &line_len, value_type, *val_buf, val_size1, unicode);
-            }
-            REGPROC_write_line(file, *line_buf, unicode);
-        }
-    }
+#define MAX_SUBKEY_LEN   257
+
+static void export_registry_data(FILE *fp, HKEY key, WCHAR *path, BOOL unicode)
+{
+    LONG rc;
+    DWORD max_value_len = 256, value_len;
+    DWORD max_data_bytes = 2048, data_size;
+    DWORD subkey_len;
+    DWORD i, type, path_len;
+    WCHAR *value_name, *subkey_name, *subkey_path;
+    BYTE *data;
+    HKEY subkey;
+
+    export_key_name(fp, path, unicode);
+
+    value_name = malloc(max_value_len * sizeof(WCHAR));
+    data = malloc(max_data_bytes);
 
     i = 0;
-    more_data = TRUE;
-    (*reg_key_name_buf)[curr_len] = '\\';
-    while(more_data)
+    for (;;)
     {
-        DWORD buf_size = *reg_key_name_size - curr_len - 1;
-
-        ret = RegEnumKeyExW(key, i, *reg_key_name_buf + curr_len + 1, &buf_size,
-        NULL, NULL, NULL, NULL);
-        if (ret == ERROR_MORE_DATA)
+        value_len = max_value_len;
+        data_size = max_data_bytes;
+        rc = RegEnumValueW(key, i, value_name, &value_len, NULL, &type, data, &data_size);
+        if (rc == ERROR_SUCCESS)
         {
-            /* Increase the size of the buffer and retry */
-            REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_size, curr_len + 1 + buf_size);
+            export_data(fp, value_name, value_len, type, data, data_size, unicode);
+            i++;
         }
-        else if (ret != ERROR_SUCCESS)
+        else if (rc == ERROR_MORE_DATA)
         {
-            more_data = FALSE;
-            if (ret != ERROR_NO_MORE_ITEMS)
+            if (data_size > max_data_bytes)
             {
-                REGPROC_print_error();
+                max_data_bytes = data_size;
+                data = realloc(data, max_data_bytes);
+            }
+            else
+            {
+                max_value_len *= 2;
+                value_name = realloc(value_name, max_value_len * sizeof(WCHAR));
             }
         }
-        else
-        {
-            HKEY subkey;
+        else break;
+    }
 
-            i++;
-            if (RegOpenKeyW(key, *reg_key_name_buf + curr_len + 1,
-            &subkey) == ERROR_SUCCESS)
+    free(data);
+    free(value_name);
+
+    subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR));
+
+    path_len = lstrlenW(path);
+
+    i = 0;
+    for (;;)
+    {
+        subkey_len = MAX_SUBKEY_LEN;
+        rc = RegEnumKeyExW(key, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL);
+        if (rc == ERROR_SUCCESS)
+        {
+            subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len);
+            if (!RegOpenKeyExW(key, subkey_name, 0, KEY_READ, &subkey))
             {
-                export_hkey(file, subkey, reg_key_name_buf, reg_key_name_size,
-                val_name_buf, val_name_size, val_buf, val_size,
-                line_buf, line_buf_size, unicode);
+                export_registry_data(fp, subkey, subkey_path, unicode);
                 RegCloseKey(subkey);
             }
-            else
-            {
-                REGPROC_print_error();
-            }
+            free(subkey_path);
+            i++;
         }
+        else break;
     }
-    (*reg_key_name_buf)[curr_len] = '\0';
+
+    free(subkey_name);
 }
 
-/******************************************************************************
- * Open file for export.
- */
 static FILE *REGPROC_open_export_file(WCHAR *file_name, BOOL unicode)
 {
     FILE *file;
-    WCHAR dash = '-';
 
-    if (wcsncmp(file_name, &dash, 1) == 0)
+    if (!lstrcmpW(file_name, L"-"))
+    {
         file = stdout;
+        _setmode(_fileno(file), _O_BINARY);
+    }
     else
     {
-        if (unicode)
-            file = _wfopen(file_name, L"wb");
-        else
-            file = _wfopen(file_name, L"w");
+        file = _wfopen(file_name, L"wb");
         if (!file)
         {
-            CHAR* file_nameA = GetMultiByteString(file_name);
-            perror("");
-            fprintf(stderr,"%S: Can't open file \"%s\"\n", getAppName(), file_nameA);
-            HeapFree(GetProcessHeap(), 0, file_nameA);
-            exit(1);
+            _wperror(L"regedit");
+#ifdef __REACTOS__
+            output_message(STRING_CANNOT_OPEN_FILE, file_name);
+            return NULL;
+#else
+            error_exit(STRING_CANNOT_OPEN_FILE, file_name);
+#endif
         }
     }
+
     if (unicode)
     {
-        const BYTE unicode_seq[] = {0xff,0xfe};
-        const WCHAR header[] = L"Windows Registry Editor Version 5.00\r\n";
-        fwrite(unicode_seq, sizeof(BYTE), COUNT_OF(unicode_seq), file);
+        static const BYTE bom[] = {0xff,0xfe};
+        static const WCHAR header[] = L"Windows Registry Editor Version 5.00\r\n";
+
+        fwrite(bom, sizeof(BYTE), ARRAY_SIZE(bom), file);
         fwrite(header, sizeof(WCHAR), lstrlenW(header), file);
     }
     else
-    {
-        fputs("REGEDIT4\n", file);
-    }
+        fputs("REGEDIT4\r\n", file);
 
     return file;
 }
 
-/******************************************************************************
- * Writes contents of the registry key to the specified file stream.
- *
- * Parameters:
- * file_name - name of a file to export registry branch to.
- * reg_key_name - registry branch to export. The whole registry is exported if
- *      reg_key_name is NULL or contains an empty string.
- */
-BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name, DWORD format)
+static HKEY open_export_key(HKEY key_class, WCHAR *subkey, WCHAR *path)
 {
-    WCHAR *reg_key_name_buf;
-    WCHAR *val_name_buf;
-    BYTE *val_buf;
-    WCHAR *line_buf;
-    DWORD reg_key_name_size = KEY_MAX_LEN;
-    DWORD val_name_size = KEY_MAX_LEN;
-    DWORD val_size = REG_VAL_BUF_SIZE;
-    DWORD line_buf_size = KEY_MAX_LEN + REG_VAL_BUF_SIZE;
-    FILE *file = NULL;
-    BOOL unicode = (format == REG_FORMAT_5);
+    HKEY key;
 
-    reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0,
-    reg_key_name_size  * sizeof(*reg_key_name_buf));
-    val_name_buf = HeapAlloc(GetProcessHeap(), 0,
-    val_name_size * sizeof(*val_name_buf));
-    val_buf = HeapAlloc(GetProcessHeap(), 0, val_size);
-    line_buf = HeapAlloc(GetProcessHeap(), 0, line_buf_size * sizeof(*line_buf));
-    CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf && line_buf);
+    if (!RegOpenKeyExW(key_class, subkey, 0, KEY_READ, &key))
+        return key;
 
-    if (reg_key_name && reg_key_name[0])
-    {
-        HKEY reg_key_class;
-        WCHAR *branch_name = NULL;
-        HKEY key;
+    output_message(STRING_OPEN_KEY_FAILED, path);
+    return NULL;
+}
 
-        REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_size,
-        lstrlenW(reg_key_name));
-        lstrcpyW(reg_key_name_buf, reg_key_name);
+static BOOL export_key(WCHAR *file_name, WCHAR *path, BOOL unicode)
+{
+    HKEY key_class, key;
+    WCHAR *subkey;
+    FILE *fp;
 
-        /* open the specified key */
-        if (!parseKeyName(reg_key_name, &reg_key_class, &branch_name))
-        {
-            CHAR* key_nameA = GetMultiByteString(reg_key_name);
-            fprintf(stderr,"%S: Incorrect registry class specification in '%s'\n",
-            getAppName(), key_nameA);
-            HeapFree(GetProcessHeap(), 0, key_nameA);
-            exit(1);
-        }
-        if (!branch_name[0])
-        {
-            /* no branch - registry class is specified */
-            file = REGPROC_open_export_file(file_name, unicode);
-            export_hkey(file, reg_key_class,
-            &reg_key_name_buf, &reg_key_name_size,
-            &val_name_buf, &val_name_size,
-            &val_buf, &val_size, &line_buf,
-            &line_buf_size, unicode);
-        }
-        else if (RegOpenKeyW(reg_key_class, branch_name, &key) == ERROR_SUCCESS)
-        {
-            file = REGPROC_open_export_file(file_name, unicode);
-            export_hkey(file, key,
-            &reg_key_name_buf, &reg_key_name_size,
-            &val_name_buf, &val_name_size,
-            &val_buf, &val_size, &line_buf,
-            &line_buf_size, unicode);
-            RegCloseKey(key);
-        }
-        else
-        {
-            CHAR* key_nameA = GetMultiByteString(reg_key_name);
-            fprintf(stderr,"%S: Can't export. Registry key '%s' does not exist!\n",
-            getAppName(), key_nameA);
-            HeapFree(GetProcessHeap(), 0, key_nameA);
-            REGPROC_print_error();
-        }
-    }
-    else
+    if (!(key_class = parse_key_name(path, &subkey)))
     {
-        unsigned int i;
-
-        /* export all registry classes */
-        file = REGPROC_open_export_file(file_name, unicode);
-        for (i = 0; i < REG_CLASS_NUMBER; i++)
-        {
-            /* do not export HKEY_CLASSES_ROOT */
-            if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
-            reg_class_keys[i] != HKEY_CURRENT_USER &&
-            reg_class_keys[i] != HKEY_CURRENT_CONFIG &&
-            reg_class_keys[i] != HKEY_DYN_DATA)
-            {
-                lstrcpyW(reg_key_name_buf, reg_class_namesW[i]);
-                export_hkey(file, reg_class_keys[i],
-                &reg_key_name_buf, &reg_key_name_size,
-                &val_name_buf, &val_name_size,
-                &val_buf, &val_size, &line_buf,
-                &line_buf_size, unicode);
-            }
-        }
+        if (subkey) *(subkey - 1) = 0;
+        output_message(STRING_INVALID_SYSTEM_KEY, path);
+        return FALSE;
     }
 
-    if (file)
-    {
-        fclose(file);
-    }
-    HeapFree(GetProcessHeap(), 0, reg_key_name);
-    HeapFree(GetProcessHeap(), 0, val_name_buf);
-    HeapFree(GetProcessHeap(), 0, val_buf);
-    HeapFree(GetProcessHeap(), 0, line_buf);
+    if (!(key = open_export_key(key_class, subkey, path)))
+        return FALSE;
+
+    fp = REGPROC_open_export_file(file_name, unicode);
+#ifdef __REACTOS__
+    if (!fp)
+        return TRUE; /* Error message is already displayed */
+#endif
+    export_registry_data(fp, key, path, unicode);
+    export_newline(fp, unicode);
+    fclose(fp);
+
+    RegCloseKey(key);
     return TRUE;
 }
 
-/******************************************************************************
- * Reads contents of the specified file into the registry.
- */
-BOOL import_registry_file(FILE* reg_file)
+static BOOL export_all(WCHAR *file_name, WCHAR *path, BOOL unicode)
 {
-    if (reg_file)
+    FILE *fp;
+    int i;
+    HKEY classes[] = {HKEY_LOCAL_MACHINE, HKEY_USERS}, key;
+    WCHAR *class_name;
+
+    fp = REGPROC_open_export_file(file_name, unicode);
+#ifdef __REACTOS__
+    if (!fp)
+        return TRUE; /* Error message is already displayed */
+#endif
+
+    for (i = 0; i < ARRAY_SIZE(classes); i++)
     {
-        BYTE s[2];
-        if (fread( s, 2, 1, reg_file) == 1)
+        if (!(key = open_export_key(classes[i], NULL, path)))
         {
-            if (s[0] == 0xff && s[1] == 0xfe)
-            {
-                processRegLinesW(reg_file);
-            }
-            else
-            {
-                fseek(reg_file, 0, SEEK_SET);
-                processRegLinesA(reg_file);
-            }
+            fclose(fp);
+            return FALSE;
         }
-        return TRUE;
-    }
-    return FALSE;
-}
 
-/******************************************************************************
- * Removes the registry key with all subkeys. Parses full key name.
- *
- * Parameters:
- * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
- *      empty, points to register key class, does not exist.
- */
-void delete_registry_key(WCHAR *reg_key_name)
-{
-    WCHAR *key_name = NULL;
-    HKEY key_class;
+        class_name = malloc((lstrlenW(reg_class_namesW[i]) + 1) * sizeof(WCHAR));
+        lstrcpyW(class_name, reg_class_namesW[i]);
 
-    if (!reg_key_name || !reg_key_name[0])
-        return;
+        export_registry_data(fp, classes[i], class_name, unicode);
 
-    if (!parseKeyName(reg_key_name, &key_class, &key_name))
-    {
-        char* reg_key_nameA = GetMultiByteString(reg_key_name);
-        fprintf(stderr,"%S: Incorrect registry class specification in '%s'\n",
-        getAppName(), reg_key_nameA);
-        HeapFree(GetProcessHeap(), 0, reg_key_nameA);
-        exit(1);
-    }
-    if (!*key_name)
-    {
-        char* reg_key_nameA = GetMultiByteString(reg_key_name);
-        fprintf(stderr,"%S: Can't delete registry class '%s'\n",
-        getAppName(), reg_key_nameA);
-        HeapFree(GetProcessHeap(), 0, reg_key_nameA);
-        exit(1);
+        free(class_name);
+        RegCloseKey(key);
     }
 
-    SHDeleteKey(key_class, key_name);
+    export_newline(fp, unicode);
+    fclose(fp);
+
+    return TRUE;
+}
+
+BOOL export_registry_key(WCHAR *file_name, WCHAR *path, DWORD format)
+{
+    BOOL unicode = (format == REG_FORMAT_5);
+
+    if (path && *path)
+        return export_key(file_name, path, unicode);
+    else
+        return export_all(file_name, path, unicode);
 }