[MMIXER] Fix additional data size initialization for different audio formats (#6753)
[reactos.git] / base / applications / regedit / regproc.c
index 8d09bb3..f8d448e 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#ifdef __REACTOS__
+#include <fcntl.h>
+#include <io.h>
 #include "regedit.h"
-
-#include <assert.h>
+#else
+#include <errno.h>
 #include <fcntl.h>
 #include <io.h>
-#include <wine/unicode.h>
-
-#define REG_VAL_BUF_SIZE        4096
+#include <windows.h>
+#include <commctrl.h>
 
-/* 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)
+#include "main.h"
+#endif
 
-const WCHAR* reg_class_namesW[] =
-{
-    L"HKEY_LOCAL_MACHINE", L"HKEY_USERS", L"HKEY_CLASSES_ROOT",
-    L"HKEY_CURRENT_CONFIG", L"HKEY_CURRENT_USER", L"HKEY_DYN_DATA"
-};
+#define REG_VAL_BUF_SIZE        4096
 
 static HKEY reg_class_keys[] = {
             HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
             HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
         };
 
-#define REG_CLASS_NUMBER (sizeof(reg_class_keys) / sizeof(reg_class_keys[0]))
-
-/* return values */
-#define NOT_ENOUGH_MEMORY     1
-#define IO_ERROR              2
-
-/* processing macros */
-
-/* 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); \
-}
-
 /******************************************************************************
  * 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;
     }
@@ -93,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;
     }
@@ -113,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;
     }
@@ -132,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;
     }
@@ -141,124 +116,259 @@ 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;
 
-    WideCharToMultiByte(CP_ACP, 0, str, -1, buf, 9, NULL, NULL);
-    if (lstrlenW(str) > 8 || sscanf(buf, "%lx%c", dw, &dummy) != 1) {
-        fprintf(stderr,"%S: ERROR, invalid hex value\n", getAppName());
-        return FALSE;
+    while (*str == ' ' || *str == '\t') str++;
+    if (!*str) goto error;
+
+    p = str;
+    while (iswxdigit(*p))
+    {
+        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') {
-        UINT wc;
+    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)
+    {
         WCHAR *end;
+        unsigned long wc;
 
-        wc = strtoulW(s,&end,16);
-        if (end == s || wc > 0xff || (*end && *end != ',')) {
-            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;
+        wc = wcstoul(s, &end, 16);
+        if (wc > 0xff) return FALSE;
+
+        if (s == end && wc == 0)
+        {
+            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 }
-            };
+    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 (strncmpW( ptr->tag, *lpValue, ptr->len ))
+    for (ptr = data_types; ptr->tag; ptr++)
+    {
+        if (wcsncmp(ptr->tag, *line, ptr->len))
             continue;
 
-        /* Found! */
-        *parse_type = ptr->parse_type;
-        type=ptr->type;
-        *lpValue+=ptr->len;
-        if (type == -1) {
-            WCHAR* end;
+        parser->parse_type = ptr->parse_type;
+        parser->data_type = ptr->parse_type;
+        *line += ptr->len;
+
+        if (ptr->type == -1)
+        {
+            WCHAR *end;
+            DWORD val;
+
+            if (!**line || towlower((*line)[1]) == 'x')
+                return FALSE;
 
             /* "hex(xx):" is special */
-            type = (int)strtoulW( *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);
+    BOOL ret;
+
     for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
         if (str[str_idx] == '\\') {
             str_idx++;
@@ -266,1185 +376,1212 @@ static void REGPROC_unescape_string(WCHAR* str)
             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 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;
+    if (!key_name) return 0;
 
-    for(i = 0; *(lpKeyName+i) != 0; i++)
+    *key_path = wcschr(key_name, '\\');
+    if (*key_path) (*key_path)++;
+
+    for (i = 0; i < ARRAY_SIZE(reg_class_keys); i++)
     {
-        if(*(lpKeyName+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] == '\\'))
         {
-            lpSlash = lpKeyName+i;
-            break;
+            return reg_class_keys[i];
         }
     }
 
-    if (lpSlash)
-    {
-        len = lpSlash-lpKeyName;
-    }
-    else
+    return 0;
+}
+
+static void close_key(struct parser *parser)
+{
+    if (parser->hkey)
     {
-        len = lstrlenW(lpKeyName);
-        lpSlash = lpKeyName+len;
-    }
-    *hKey = NULL;
+        free(parser->key_name);
+        parser->key_name = NULL;
 
-    for (i = 0; i < REG_CLASS_NUMBER; i++) {
-        if (CompareStringW(LOCALE_USER_DEFAULT, 0, lpKeyName, len, reg_class_namesW[i], len) == CSTR_EQUAL &&
-            len == lstrlenW(reg_class_namesW[i])) {
-            *hKey = reg_class_keys[i];
-            break;
-        }
+        RegCloseKey(parser->hkey);
+        parser->hkey = NULL;
     }
-
-    if (*hKey == NULL)
-        return FALSE;
-
-
-    if (*lpSlash != '\0')
-        lpSlash++;
-    *lpKeyPath = lpSlash;
-    return TRUE;
 }
 
-/* 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
+ * Opens the registry key given by the input path.
+ * This key must be closed by calling close_key().
  */
-static LONG setValue(WCHAR* val_name, WCHAR* val_data, BOOL is_unicode)
+static LONG open_key(struct parser *parser, WCHAR *path)
 {
+    HKEY key_class;
+    WCHAR *key_path;
     LONG res;
-    DWORD  dwDataType, dwParseType;
-    LPBYTE lpbData;
-    DWORD  dwData, dwLen;
-    WCHAR del[] = {'-',0};
 
-    if ( (val_name == NULL) || (val_data == NULL) )
+    close_key(parser);
+
+    /* Get the registry class */
+    if (!path || !(key_class = parse_key_name(path, &key_path)))
         return ERROR_INVALID_PARAMETER;
 
-    if (lstrcmpW(val_data, del) == 0)
+    res = RegCreateKeyExW(key_class, key_path, 0, NULL, REG_OPTION_NON_VOLATILE,
+                          KEY_ALL_ACCESS, NULL, &parser->hkey, NULL);
+
+    if (res == ERROR_SUCCESS)
     {
-        res=RegDeleteValueW(currentKeyHandle,val_name);
-        return (res == ERROR_FILE_NOT_FOUND ? ERROR_SUCCESS : res);
+        parser->key_name = malloc((lstrlenW(path) + 1) * sizeof(WCHAR));
+        lstrcpyW(parser->key_name, path);
     }
+    else
+        parser->hkey = NULL;
+
+    return res;
+}
 
-    /* Get the data type stored into the value field */
-    dwDataType = getDataType(&val_data, &dwParseType);
+static void free_parser_data(struct parser *parser)
+{
+    if (parser->parse_type == REG_DWORD || parser->parse_type == REG_BINARY)
+        free(parser->data);
+
+    parser->data = NULL;
+    parser->data_size = 0;
+}
 
-    if (dwParseType == REG_SZ)          /* no conversion for string */
+static void prepare_hex_string_data(struct parser *parser)
+{
+    if (parser->data_type == REG_EXPAND_SZ || parser->data_type == REG_MULTI_SZ ||
+        parser->data_type == REG_SZ)
     {
-        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(val_data[dwLen-1] != '"')
-            return ERROR_INVALID_DATA;
-        if (dwLen>0 && val_data[dwLen-1]=='"')
+        if (parser->is_unicode)
         {
-            dwLen--;
-            val_data[dwLen]='\0';
+            WCHAR *data = parser->data;
+            DWORD len = parser->data_size / sizeof(WCHAR);
+
+            if (data[len - 1] != 0)
+            {
+                data[len] = 0;
+                parser->data_size += sizeof(WCHAR);
+            }
+        }
+        else
+        {
+            BYTE *data = parser->data;
+
+            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);
         }
-        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 */
+}
+
+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)
+{
+    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 (!wcsncmp(s, header_31, 7)) /* "REGEDIT" without NUL */
+        return REG_VERSION_FUZZY;
+
+    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)
     {
-        if (!convertHexToDWord(val_data, &dwData))
-            return ERROR_INVALID_DATA;
-        lpbData = (BYTE*)&dwData;
-        dwLen = sizeof(dwData);
+        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 if (dwParseType == REG_BINARY) /* Convert the binary data */
-    {
-        lpbData = convertHexCSVToHex(val_data, &dwLen);
-        if (!lpbData)
-            return ERROR_INVALID_DATA;
+    else parser->reg_version = parse_file_header(line);
 
-        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 */
+    switch (parser->reg_version)
     {
-        fprintf(stderr,"%S: ERROR, unknown data format\n", getAppName());
-        return ERROR_INVALID_DATA;
+    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;
     }
 
-    res = RegSetValueExW(
-               currentKeyHandle,
-               val_name,
-               0,                  /* Reserved */
-               dwDataType,
-               lpbData,
-               dwLen);
-    if (dwParseType == REG_BINARY)
-        HeapFree(GetProcessHeap(), 0, lpbData);
-    return res;
+    return line;
 }
 
-/******************************************************************************
- * A helper function for processRegEntry() that opens the current key.
- * That key must be closed by calling closeKey().
- */
-static LONG openKeyW(WCHAR* stdInput)
+/* handler for parser PARSE_WIN31_LINE state */
+static WCHAR *parse_win31_line_state(struct parser *parser, WCHAR *pos)
 {
-    HKEY keyClass;
-    WCHAR* keyPath;
-    DWORD dwDisp;
-    LONG res;
+    WCHAR *line, *value;
+    static WCHAR hkcr[] = L"HKEY_CLASSES_ROOT";
+    unsigned int key_end = 0;
 
-    /* Sanity checks */
-    if (stdInput == NULL)
-        return ERROR_INVALID_PARAMETER;
+    if (!(line = get_line(parser->file)))
+        return NULL;
 
-    /* Get the registry class */
-    if (!parseKeyName(stdInput, &keyClass, &keyPath))
-        return ERROR_INVALID_PARAMETER;
+    if (wcsncmp(line, hkcr, lstrlenW(hkcr)))
+        return line;
 
-    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 */
+    /* get key name */
+    while (line[key_end] && !iswspace(line[key_end])) key_end++;
 
-    if (res == ERROR_SUCCESS)
-        currentKeyName = GetMultiByteString(stdInput);
-    else
-        currentKeyHandle = NULL;
+    value = line + key_end;
+    while (*value == ' ' || *value == '\t') value++;
 
-    return res;
+    if (*value == '=') value++;
+    if (*value == ' ') value++; /* at most one space is skipped */
+
+    line[key_end] = 0;
+
+    if (open_key(parser, line) != ERROR_SUCCESS)
+    {
+        output_message(STRING_OPEN_KEY_FAILED, line);
+        return line;
+    }
 
+    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;
 }
 
-/******************************************************************************
- * Close the currently opened key.
- */
-static void closeKey(void)
+/* handler for parser LINE_START state */
+static WCHAR *line_start_state(struct parser *parser, WCHAR *pos)
 {
-    if (currentKeyHandle)
+    WCHAR *line, *p;
+
+    if (!(line = get_line(parser->file)))
+        return NULL;
+
+    for (p = line; *p; p++)
     {
-        HeapFree(GetProcessHeap(), 0, currentKeyName);
-        RegCloseKey(currentKeyHandle);
-        currentKeyHandle = NULL;
+        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;
+        }
     }
+
+    return p;
 }
 
-/******************************************************************************
- * 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)
+/* handler for parser KEY_NAME state */
+static WCHAR *key_name_state(struct parser *parser, WCHAR *pos)
 {
-    WCHAR* val_name;                   /* registry value name   */
-    WCHAR* val_data;                   /* registry value data   */
-    int line_idx = 0;                 /* current character under analysis */
-    LONG res;
+    WCHAR *p = pos, *key_end;
 
-    /* get value name */
-    while ( isspaceW(line[line_idx]) ) line_idx++;
-    if (line[line_idx] == '@' && line[line_idx + 1] == '=') {
-        line[line_idx] = '\0';
-        val_name = line;
-        line_idx++;
-    } else if (line[line_idx] == '\"') {
-        line_idx++;
-        val_name = line + line_idx;
-        while (line[line_idx]) {
-            if (line[line_idx] == '\\')   /* skip escaped character */
-            {
-                line_idx += 2;
-            } else {
-                if (line[line_idx] == '\"') {
-                    line[line_idx] = '\0';
-                    line_idx++;
-                    break;
-                } else {
-                    line_idx++;
-                }
-            }
-        }
-        while ( isspaceW(line[line_idx]) ) line_idx++;
-        if (!line[line_idx]) {
-            fprintf(stderr, "%S: warning: unexpected EOL\n", getAppName());
-            return;
-        }
-        if (line[line_idx] != '=') {
-            char* lineA;
-            line[line_idx] = '\"';
-            lineA = GetMultiByteString(line);
-            fprintf(stderr,"%S: warning: unrecognized line: '%s'\n", getAppName(), lineA);
-            HeapFree(GetProcessHeap(), 0, lineA);
-            return;
-        }
+    if (*p == ' ' || *p == '\t' || !(key_end = wcsrchr(p, ']')))
+        goto done;
 
-    } else {
-        char* lineA = GetMultiByteString(line);
-        fprintf(stderr,"%S: warning: unrecognized line: '%s'\n", getAppName(), lineA);
-        HeapFree(GetProcessHeap(), 0, lineA);
-        return;
-    }
-    line_idx++;                   /* skip the '=' character */
-
-    while ( isspaceW(line[line_idx]) ) line_idx++;
-    val_data = line + line_idx;
-    /* trim trailing blanks */
-    line_idx = strlenW(val_data);
-    while (line_idx > 0 && isspaceW(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 )
+    *key_end = 0;
+
+    if (*p == '-')
     {
-        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);
+        set_state(parser, DELETE_KEY);
+        return p + 1;
     }
+    else if (open_key(parser, p) != ERROR_SUCCESS)
+        output_message(STRING_OPEN_KEY_FAILED, p);
+
+done:
+    set_state(parser, LINE_START);
+    return p;
 }
 
-/******************************************************************************
- * 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)
+/* handler for parser DELETE_KEY state */
+static WCHAR *delete_key_state(struct parser *parser, WCHAR *pos)
 {
-    /*
-     * We encountered the end of the file, make sure we
-     * close the opened key and exit
-     */
-    if (stdInput == NULL) {
-        closeKey();
-        return;
-    }
+    WCHAR *p = pos;
 
-    if      ( stdInput[0] == '[')      /* We are reading a new key */
-    {
-        WCHAR* keyEnd;
-        closeKey();                    /* Close the previous key */
+    close_key(parser);
 
-        /* Get rid of the square brackets */
-        stdInput++;
-        keyEnd = strrchrW(stdInput, ']');
-        if (keyEnd)
-            *keyEnd='\0';
+    if (*p == 'H' || *p == 'h')
+        delete_registry_key(p);
 
-        /* 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 */
+    set_state(parser, LINE_START);
+    return p;
+}
+
+/* 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;
+
+    set_state(parser, DATA_START);
+    return pos + 1;
+}
+
+/* handler for parser QUOTED_VALUE_NAME state */
+static WCHAR *quoted_value_name_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *val_name = pos, *p;
+
+    free(parser->value_name);
+    parser->value_name = NULL;
+
+    if (!REGPROC_unescape_string(val_name, &p))
+        goto invalid;
+
+    /* 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);
+
+    set_state(parser, DATA_START);
+    return p;
+
+invalid:
+    set_state(parser, LINE_START);
+    return val_name;
+}
+
+/* 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++;
+
+    /* 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;
+
+done:
+    set_state(parser, LINE_START);
+    return p;
+}
+
+/* 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;
+
+    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)
+{
+    WCHAR *line = pos;
+
+    if (!parse_data_type(parser, &line))
     {
-        processSetValue(stdInput, isUnicode);
-    } else
+        set_state(parser, LINE_START);
+        return line;
+    }
+
+    switch (parser->parse_type)
     {
-        /* 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_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;
 }
 
-/******************************************************************************
- * Processes a registry file.
- * Correctly processes comments (in # and ; form), line continuation.
- *
- * Parameters:
- *   in - input stream to read from
- *   first_chars - beginning of stream, read due to Unicode check
- */
-static void processRegLinesA(FILE *in, char* first_chars)
+/* handler for parser STRING_DATA state */
+static WCHAR *string_data_state(struct parser *parser, WCHAR *pos)
 {
-    LPSTR line           = NULL;  /* line read from input stream */
-    ULONG lineSize       = REG_VAL_BUF_SIZE;
+    WCHAR *line;
 
-    line = HeapAlloc(GetProcessHeap(), 0, lineSize);
-    CHECK_ENOUGH_MEMORY(line);
-    memcpy(line, first_chars, 2);
+    parser->data = pos;
 
-    while (!feof(in)) {
-        LPSTR s; /* The pointer into line for where the current fgets should read */
-        WCHAR* lineW;
-        s = line;
+    if (!REGPROC_unescape_string(parser->data, &line))
+        goto invalid;
 
-        if(first_chars)
-        {
-            s += 2;
-            first_chars = NULL;
-        }
+    while (*line == ' ' || *line == '\t') line++;
+    if (*line && *line != ';') goto invalid;
 
-        for (;;) {
-            size_t size_remaining;
-            int size_to_get, i;
-            char *s_eol; /* various local uses */
+    parser->data_size = (lstrlenW(parser->data) + 1) * sizeof(WCHAR);
 
-            /* 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);
-            }
+    set_state(parser, SET_VALUE);
+    return line;
 
-            /* 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);
-
-            /* get a single line. note that `i' must be one past the last
-             * meaningful character in `s' when this loop exits */
-            for(i = 0; i < size_to_get-1; ++i){
-                int xchar;
-
-                xchar = fgetc(in);
-                s[i] = xchar;
-                if(xchar == EOF){
-                    if(ferror(in)){
-                        perror("While reading input");
-                        exit(IO_ERROR);
-                    }else
-                        assert(feof(in));
-                    break;
-                }
-                if(s[i] == '\r'){
-                    /* read the next character iff it's \n */
-                    if(i+2 >= size_to_get){
-                        /* buffer too short, so put back the EOL char to
-                         * read next cycle */
-                        ungetc('\r', in);
-                        break;
-                    }
-                    s[i+1] = fgetc(in);
-                    if(s[i+1] != '\n'){
-                        ungetc(s[i+1], in);
-                        i = i+1;
-                    }else
-                        i = i+2;
-                    break;
-                }
-                if(s[i] == '\n'){
-                    i = i+1;
-                    break;
-                }
-            }
-            s[i] = '\0';
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return line;
+}
 
-            /* If we didn't read the eol nor the eof go around for the rest */
-            s_eol = strpbrk (s, "\r\n");
-            if (!feof (in) && !s_eol) {
-                s = strchr (s, '\0');
-                continue;
-            }
+/* handler for parser DWORD_DATA state */
+static WCHAR *dword_data_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *line = pos;
 
-            /* If it is a comment line then discard it and go around again */
-            if (line [0] == '#' || line [0] == ';') {
-                s = line;
-                continue;
-            }
+    parser->data = malloc(sizeof(DWORD));
 
-            /* Remove any line feed.  Leave s_eol on the first \0 */
-            if (s_eol) {
-               if (*s_eol == '\r' && *(s_eol+1) == '\n')
-                   *(s_eol+1) = '\0';
-               *s_eol = '\0';
-            } else
-                s_eol = strchr (s, '\0');
-
-            /* 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;
-            }
+    if (!convert_hex_to_dword(line, parser->data))
+        goto invalid;
 
-            lineW = GetWideString(line);
+    parser->data_size = sizeof(DWORD);
 
-            break; /* That is the full virtual line */
-        }
+    set_state(parser, SET_VALUE);
+    return line;
 
-        processRegEntry(lineW, FALSE);
-        HeapFree(GetProcessHeap(), 0, lineW);
+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)
+    {
+        set_state(parser, EOL_BACKSLASH);
+        return line;
     }
-    processRegEntry(NULL, FALSE);
 
-    HeapFree(GetProcessHeap(), 0, 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;
+}
+
+/* handler for parser EOL_BACKSLASH state */
+static WCHAR *eol_backslash_state(struct parser *parser, WCHAR *pos)
+{
+    WCHAR *p = pos;
+
+    while (*p == ' ' || *p == '\t') p++;
+    if (*p && *p != ';') goto invalid;
+
+    set_state(parser, HEX_MULTILINE);
+    return pos;
+
+invalid:
+    free_parser_data(parser);
+    set_state(parser, LINE_START);
+    return p;
 }
 
-static void processRegLinesW(FILE *in)
+/* handler for parser HEX_MULTILINE state */
+static WCHAR *hex_multiline_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 *line;
+
+    if (!(line = get_line(parser->file)))
+    {
+        prepare_hex_string_data(parser);
+        set_state(parser, SET_VALUE);
+        return 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;
+}
+
+/* handler for parser UNKNOWN_DATA state */
+static WCHAR *unknown_data_state(struct parser *parser, WCHAR *pos)
+{
+    output_message(STRING_UNKNOWN_DATA_FORMAT, parser->data_type);
+
+    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;
+}
 
-    WCHAR* s; /* The pointer into buf for where the current fgets should read */
-    WCHAR* line; /* The start of the current line */
+static WCHAR *get_lineA(FILE *fp)
+{
+    static WCHAR *lineW;
+    static size_t size;
+    static char *buf, *next;
+    char *line;
 
-    buf = HeapAlloc(GetProcessHeap(), 0, lineSize * sizeof(WCHAR));
-    CHECK_ENOUGH_MEMORY(buf);
+    free(lineW);
 
-    s = buf;
-    line = buf;
+    if (!fp) goto cleanup;
 
-    while(!feof(in)) {
-        size_t size_remaining;
-        int size_to_get;
-        WCHAR *s_eol = NULL; /* various local uses */
+    if (!size)
+    {
+        size = REG_VAL_BUF_SIZE;
+        buf = malloc(size);
+        *buf = 0;
+        next = buf;
+    }
+    line = next;
 
-        /* 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 */
+    while (next)
+    {
+        char *p = strpbrk(line, "\r\n");
+        if (!p)
         {
-            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;
+            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;
-            s = buf + lineSize - size_remaining;
-            lineSize = new_size;
-            size_remaining = lineSize - (s-buf);
+            continue;
         }
+        next = p + 1;
+        if (*p == '\r' && *(p + 1) == '\n') next++;
+        *p = 0;
+        lineW = GetWideString(line);
+        return lineW;
+    }
 
-        /* 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);
-
-        CharsInBuf = fread(s, sizeof(WCHAR), size_to_get - 1, in);
-        s[CharsInBuf] = 0;
-
-        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.
-                */
-            }
-        }
+cleanup:
+    lineW = NULL;
+    if (size) free(buf);
+    size = 0;
+    return NULL;
+}
+
+static WCHAR *get_lineW(FILE *fp)
+{
+    static size_t size;
+    static WCHAR *buf, *next;
+    WCHAR *line;
+
+    if (!fp) goto cleanup;
+
+    if (!size)
+    {
+        size = REG_VAL_BUF_SIZE;
+        buf = malloc(size * sizeof(WCHAR));
+        *buf = 0;
+        next = buf;
+    }
+    line = next;
 
-        /* If we didn't read the eol nor the eof go around for the rest */
-        while(1)
+    while (next)
+    {
+        WCHAR *p = wcspbrk(line, L"\r\n");
+        if (!p)
         {
-            const WCHAR line_endings[] = {'\r','\n',0};
-            s_eol = strpbrkW(line, line_endings);
-
-            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, (strlenW(line)+1) * sizeof(WCHAR));
-                line = buf;
-                s = strchrW(line, '\0');
-                break;
+            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 it is a comment line then discard it and go around again */
-            if (*line == '#' || *line == ';') {
-                if (*s_eol == '\r' && *(s_eol+1) == '\n')
-                    line = s_eol + 2;
-                else
-                    line = s_eol + 1;
-                continue;
+            if (!(count = fread(buf + len, sizeof(WCHAR), size - len - 1, fp)))
+            {
+                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;
+    }
 
-            /* If there is a concatenating \\ then go around again */
-            if (*(s_eol-1) == '\\') {
-                WCHAR* NextLine = s_eol + 1;
+cleanup:
+    if (size) free(buf);
+    size = 0;
+    return NULL;
+}
 
-                if(*s_eol == '\r' && *(s_eol+1) == '\n')
-                    NextLine++;
+/******************************************************************************
+ * Reads contents of the specified file into the registry.
+ */
+BOOL import_registry_file(FILE *reg_file)
+{
+    BYTE s[2];
+    struct parser parser;
+    WCHAR *pos;
 
-                while(*(NextLine+1) == ' ' || *(NextLine+1) == '\t')
-                    NextLine++;
+    if (!reg_file || (fread(s, 2, 1, reg_file) != 1))
+        return FALSE;
 
-                MoveMemory(s_eol - 1, NextLine, (CharsInBuf - (NextLine - s) + 1)*sizeof(WCHAR));
-                CharsInBuf -= NextLine - s_eol + 1;
-                s_eol = 0;
-                continue;
-            }
+    parser.is_unicode = (s[0] == 0xff && s[1] == 0xfe);
+    get_line = parser.is_unicode ? get_lineW : get_lineA;
 
-            /* Remove any line feed.  Leave s_eol on the last \0 */
-            if (*s_eol == '\r' && *(s_eol + 1) == '\n')
-                *s_eol++ = '\0';
-            *s_eol = '\0';
+    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;
 
-            processRegEntry(line, TRUE);
-            line = s_eol + 1;
-            s_eol = 0;
-            continue; /* That is the full virtual line */
-        }
-    }
+    pos = parser.two_wchars;
 
-    processRegEntry(NULL, TRUE);
+    /* parser main loop */
+    while (pos)
+        pos = (parser_funcs[parser.state])(&parser, pos);
 
-    HeapFree(GetProcessHeap(), 0, buf);
-}
+    if (parser.reg_version == REG_VERSION_FUZZY || parser.reg_version == REG_VERSION_INVALID)
+        return parser.reg_version == REG_VERSION_FUZZY;
 
-/****************************************************************************
- * REGPROC_print_error
- *
- * Print the message for GetLastError
- */
+    free(parser.value_name);
+    close_key(&parser);
 
-static void REGPROC_print_error(void)
-{
-    LPVOID lpMsgBuf;
-    DWORD error_code;
-    int status;
-
-    error_code = GetLastError ();
-    status = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-                            NULL, error_code, 0, (LPSTR) &lpMsgBuf, 0, NULL);
-    if (!status) {
-        fprintf(stderr,"%S: Cannot display message for error %lu, status %lu\n",
-                getAppName(), error_code, GetLastError());
-        exit(1);
-    }
-    puts(lpMsgBuf);
-    LocalFree(lpMsgBuf);
-    exit(1);
+    return TRUE;
 }
 
 /******************************************************************************
- * Checks whether the buffer has enough room for the string or required size.
- * Resizes the buffer if necessary.
+ * Removes the registry key with all subkeys. Parses full key name.
  *
  * 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.
+ * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
+ *      empty, points to register key class, does not exist.
  */
-static void REGPROC_resize_char_buffer(WCHAR **buffer, DWORD *len, DWORD required_len)
+void delete_registry_key(WCHAR *reg_key_name)
 {
-    required_len++;
-    if (required_len > *len) {
-        *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);
+    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
 }
 
-/******************************************************************************
- * 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)
+static void REGPROC_write_line(FILE *fp, const WCHAR *str, BOOL unicode)
 {
-    if (required_size > *size) {
-        *size = required_size;
-        if (!*buffer)
-            *buffer = HeapAlloc(GetProcessHeap(), 0, *size);
-        else
-            *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *size);
-        CHECK_ENOUGH_MEMORY(*buffer);
+    if (unicode)
+        fwrite(str, sizeof(WCHAR), lstrlenW(str), fp);
+    else
+    {
+        char *strA = GetMultiByteString(str);
+        fputs(strA, fp);
+        free(strA);
     }
 }
 
-/******************************************************************************
- * 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)
+static WCHAR *REGPROC_escape_string(WCHAR *str, size_t str_len, size_t *line_len)
 {
-    DWORD i, pos;
-    DWORD extra = 0;
+    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++;
+    }
 
-    REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + 10);
+    buf = malloc((str_len + escape_count + 1) * sizeof(WCHAR));
 
-    /* escaping characters */
-    pos = *line_len;
-    for (i = 0; i < str_len; i++) {
+    for (i = 0, pos = 0; i < str_len; i++, pos++)
+    {
         WCHAR c = str[i];
-        switch (c) {
+
+        if (!c) break;
+
+        switch (c)
+        {
+        case '\r':
+            buf[pos++] = '\\';
+            buf[pos] = 'r';
+            break;
         case '\n':
-            extra++;
-            REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + extra);
-            (*line_buf)[pos++] = '\\';
-            (*line_buf)[pos++] = 'n';
+            buf[pos++] = '\\';
+            buf[pos] = 'n';
             break;
-
         case '\\':
+            buf[pos++] = '\\';
+            buf[pos] = '\\';
+            break;
         case '"':
-            extra++;
-            REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + extra);
-            (*line_buf)[pos++] = '\\';
-            /* Fall through */
-
-        default:
-            (*line_buf)[pos++] = c;
+            buf[pos++] = '\\';
+            buf[pos] = '"';
             break;
+        default:
+            buf[pos] = c;
         }
     }
-    (*line_buf)[pos] = '\0';
+
+    buf[pos] = 0;
     *line_len = pos;
+    return buf;
 }
 
-static void REGPROC_export_binary(WCHAR **line_buf, DWORD *line_buf_size, DWORD *line_len, DWORD type, BYTE *value, DWORD value_size, BOOL unicode)
+static size_t export_value_name(FILE *fp, WCHAR *name, size_t len, BOOL unicode)
 {
-    DWORD hex_pos, data_pos;
-    const WCHAR *hex_prefix;
-    const WCHAR hex[] = {'h','e','x',':',0};
-    WCHAR hex_buf[17];
-    const WCHAR concat[] = {'\\','\r','\n',' ',' ',0};
-    DWORD concat_prefix, concat_len;
-    const WCHAR newline[] = {'\r','\n',0};
-    CHAR* value_multibyte = NULL;
-
-    if (type == REG_BINARY) {
-        hex_prefix = hex;
-    } else {
-        const WCHAR hex_format[] = {'h','e','x','(','%','x',')',':',0};
-        hex_prefix = hex_buf;
-        sprintfW(hex_buf, hex_format, type);
-        if ((type == REG_SZ || type == REG_EXPAND_SZ || type == REG_MULTI_SZ) && !unicode)
-        {
-            value_multibyte = GetMultiByteStringN((WCHAR*)value, value_size / sizeof(WCHAR), &value_size);
-            value = (BYTE*)value_multibyte;
-        }
+    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
+    {
+        line_len = lstrlenW(default_name);
+        REGPROC_write_line(fp, default_name, unicode);
     }
 
-    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) or *line_len divides
-     *   without a remainder then we may overestimate
-     *   the needed number of lines by one. But that's ok.
-     * - The trailing '\r' takes the place of a comma so
-     *   we only need to add 1 for the trailing '\n'
-     */
-    *line_len += *line_len / (REG_FILE_HEX_LINE_LEN - concat_prefix) * concat_len + 1;
-    REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len);
-    lstrcpyW(*line_buf + hex_pos, hex_prefix);
-    if (value_size)
+    return line_len;
+}
+
+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)
+{
+    static const WCHAR hex[] = L"hex:";
+    size_t line_len;
+
+    if (type == REG_BINARY)
+    {
+        line_len = lstrlenW(hex);
+        REGPROC_write_line(fp, hex, unicode);
+    }
+    else
     {
-        const WCHAR format[] = {'%','0','2','x',0};
-        DWORD i, column;
+        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);
+    }
 
-        column = data_pos; /* no line wrap yet */
-        i = 0;
-        while (1)
-        {
-            sprintfW(*line_buf + data_pos, format, (unsigned int)value[i]);
-            data_pos += 2;
-            if (++i == value_size)
-                break;
+    return line_len;
+}
 
-            (*line_buf)[data_pos++] = ',';
-            column += 3;
+#define MAX_HEX_CHARS 77
 
-            /* wrap the line */
-            if (column >= REG_FILE_HEX_LINE_LEN) {
-                lstrcpyW(*line_buf + data_pos, concat);
-                data_pos += concat_len;
-                column = concat_prefix;
-            }
+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++)
+    {
+#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)
+        {
+            REGPROC_write_line(fp, *buf, unicode);
+            REGPROC_write_line(fp, L"\\\r\n  ", unicode);
+            line_len = 2;
+            pos = 0;
         }
     }
-    lstrcpyW(*line_buf + data_pos, newline);
-    HeapFree(GetProcessHeap(), 0, value_multibyte);
 }
 
-/******************************************************************************
- * Writes the given line to a file, in multi-byte or wide characters
- */
-static void REGPROC_write_line(FILE *file, const WCHAR* str, BOOL unicode)
+static void export_newline(FILE *fp, BOOL unicode)
 {
-    if(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)
     {
-        fwrite(str, sizeof(WCHAR), lstrlenW(str), file);
-    } else
+    case REG_SZ:
+        export_string_data(&buf, data, size);
+        break;
+    case REG_DWORD:
+        if (size)
+        {
+            export_dword_data(&buf, data);
+            break;
+        }
+        /* 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 (size || type == REG_SZ)
     {
-        char* strA = GetMultiByteString(str);
-        fputs(strA, file);
-        HeapFree(GetProcessHeap(), 0, strA);
+        REGPROC_write_line(fp, buf, unicode);
+        free(buf);
     }
+
+    export_newline(fp, unicode);
 }
 
-/******************************************************************************
- * 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 WCHAR *build_subkey_path(WCHAR *path, DWORD path_len, WCHAR *subkey_name, DWORD subkey_len)
 {
-    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[] = {'\r','\n','[','%','s',']','\r','\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) {
-        REGPROC_print_error();
-    }
-    curr_len = strlenW(*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 */
-    sprintfW(*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) {
-        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) {
-            /* 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);
-        } else if (ret != ERROR_SUCCESS) {
-            more_data = FALSE;
-            if (ret != ERROR_NO_MORE_ITEMS) {
-                REGPROC_print_error();
-            }
-        } else {
-            DWORD line_len;
-            i++;
+    WCHAR *subkey_path;
 
-            if ((*val_name_buf)[0]) {
-                const WCHAR val_start[] = {'"','%','s','"','=',0};
-
-                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);
-
-                line_len = 3 + lstrlenW(*val_name_buf);
-                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len);
-                sprintfW(*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);
-            }
+    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
 
-            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[] = {'"','\r','\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 subtract 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;
-            }
+    return subkey_path;
+}
 
-            case REG_DWORD:
-            {
-                WCHAR format[] = {'d','w','o','r','d',':','%','0','8','x','\r','\n',0};
+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);
+}
 
-                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + 15);
-                sprintfW(*line_buf + line_len, format, *((DWORD *)*val_buf));
-                break;
-            }
+#define MAX_SUBKEY_LEN   257
 
-            default:
+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;
+    for (;;)
+    {
+        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)
+        {
+            export_data(fp, value_name, value_len, type, data, data_size, unicode);
+            i++;
+        }
+        else if (rc == ERROR_MORE_DATA)
+        {
+            if (data_size > max_data_bytes)
             {
-                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);
+                max_data_bytes = data_size;
+                data = realloc(data, max_data_bytes);
             }
-                /* 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);
+            else
+            {
+                max_value_len *= 2;
+                value_name = realloc(value_name, max_value_len * sizeof(WCHAR));
             }
-            REGPROC_write_line(file, *line_buf, unicode);
         }
+        else break;
     }
 
-    i = 0;
-    more_data = TRUE;
-    (*reg_key_name_buf)[curr_len] = '\\';
-    while(more_data) {
-        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) {
-            /* 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);
-        } else if (ret != ERROR_SUCCESS) {
-            more_data = FALSE;
-            if (ret != ERROR_NO_MORE_ITEMS) {
-                REGPROC_print_error();
-            }
-        } else {
-            HKEY subkey;
+    free(data);
+    free(value_name);
 
-            i++;
-            if (RegOpenKeyW(key, *reg_key_name_buf + curr_len + 1,
-                           &subkey) == ERROR_SUCCESS) {
-                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);
+    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_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 in binary mode for export.
- */
 static FILE *REGPROC_open_export_file(WCHAR *file_name, BOOL unicode)
 {
     FILE *file;
-    WCHAR dash = '-';
 
-    if (strncmpW(file_name,&dash,1)==0) {
-        file=stdout;
+    if (!lstrcmpW(file_name, L"-"))
+    {
+        file = stdout;
         _setmode(_fileno(file), _O_BINARY);
-    } else
+    }
+    else
     {
-        CHAR* file_nameA = GetMultiByteString(file_name);
-        file = fopen(file_nameA, "wb");
-        if (!file) {
-            perror("");
-            fprintf(stderr,"%S: Can't open file \"%s\"\n", getAppName(), file_nameA);
-            HeapFree(GetProcessHeap(), 0, file_nameA);
-            exit(1);
+        file = _wfopen(file_name, L"wb");
+        if (!file)
+        {
+            _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
         }
-        HeapFree(GetProcessHeap(), 0, file_nameA);
     }
-    if(unicode)
-    {
-        const BYTE unicode_seq[] = {0xff,0xfe};
-        const WCHAR header[] = {'W','i','n','d','o','w','s',' ','R','e','g','i','s','t','r','y',' ','E','d','i','t','o','r',' ','V','e','r','s','i','o','n',' ','5','.','0','0','\r','\n'};
-        fwrite(unicode_seq, sizeof(BYTE), sizeof(unicode_seq)/sizeof(unicode_seq[0]), file);
-        fwrite(header, sizeof(WCHAR), sizeof(header)/sizeof(header[0]), file);
-    } else
+
+    if (unicode)
     {
-        fputs("REGEDIT4\r\n", 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\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 (reg_key_name && reg_key_name[0]) {
-        HKEY reg_key_class;
-        WCHAR *branch_name = NULL;
-        HKEY key;
-
-        REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_size,
-                                   lstrlenW(reg_key_name));
-        lstrcpyW(reg_key_name_buf, reg_key_name);
-
-        /* 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 {
-        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 (!RegOpenKeyExW(key_class, subkey, 0, KEY_READ, &key))
+        return key;
+
+    output_message(STRING_OPEN_KEY_FAILED, path);
+    return NULL;
+}
+
+static BOOL export_key(WCHAR *file_name, WCHAR *path, BOOL unicode)
+{
+    HKEY key_class, key;
+    WCHAR *subkey;
+    FILE *fp;
 
-    if (file) {
-        fclose(file);
+    if (!(key_class = parse_key_name(path, &subkey)))
+    {
+        if (subkey) *(subkey - 1) = 0;
+        output_message(STRING_INVALID_SYSTEM_KEY, path);
+        return FALSE;
     }
-    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
-            {
-                processRegLinesA(reg_file, (char*)s);
-            }
+            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);
 }