From: xantares 09 Subject: [PATCH 2/4] reg: Add wchar/raw data conversion functions Message-Id: Date: Fri, 31 Jul 2015 05:59:42 +0000 --- programs/reg/reg.c | 145 ++++++++++++++++++++++++++++++++++++++--------- programs/reg/reg.h | 1 + programs/reg/reg.rc | 1 + programs/reg/tests/reg.c | 57 +++++++++---------- 4 files changed, 149 insertions(+), 55 deletions(-) diff --git a/programs/reg/reg.c b/programs/reg/reg.c index cf6425e..b2447fc 100644 --- a/programs/reg/reg.c +++ b/programs/reg/reg.c @@ -18,10 +18,15 @@ #include #include +#include #include "reg.h" #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*A)) +WINE_DEFAULT_DEBUG_CHANNEL(reg); + +static const WCHAR empty_wstr[] = {0}; + static const WCHAR short_hklm[] = {'H','K','L','M',0}; static const WCHAR short_hkcu[] = {'H','K','C','U',0}; static const WCHAR short_hkcr[] = {'H','K','C','R',0}; @@ -189,43 +194,126 @@ static DWORD wchar_get_type(const WCHAR *type_name) return ~0u; } -static LPBYTE get_regdata(LPWSTR data, DWORD reg_type, WCHAR separator, DWORD *reg_count) +static BYTE *wchar_get_data(const WCHAR *input, const DWORD type, const WCHAR separator, + DWORD *size_out) { - LPBYTE out_data = NULL; - *reg_count = 0; + DWORD i; + + if (!input) + input = empty_wstr; - switch (reg_type) + switch (type) { + case REG_NONE: case REG_SZ: + case REG_EXPAND_SZ: { - *reg_count = (lstrlenW(data) + 1) * sizeof(WCHAR); - out_data = HeapAlloc(GetProcessHeap(),0,*reg_count); - lstrcpyW((LPWSTR)out_data,data); - break; + BYTE *data; + + i = (strlenW(input) + 1) * sizeof(WCHAR); + *size_out = i; + data = HeapAlloc(GetProcessHeap(), 0, i); + memcpy(data, input, i); + return data; } case REG_DWORD: + case REG_DWORD_BIG_ENDIAN: { - LPWSTR rest; - DWORD val; - val = strtolW(data, &rest, 0); - if (rest == data) { - static const WCHAR nonnumber[] = {'E','r','r','o','r',':',' ','/','d',' ','r','e','q','u','i','r','e','s',' ','n','u','m','b','e','r','.','\n',0}; - reg_printfW(nonnumber); - break; + BYTE *data; + WCHAR *temp; + + if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X')) + i = strtoulW(input, &temp, 16); + else + i = strtoulW(input, &temp, 10); + + if (input[0] == '-' || temp[0] || temp == input) + { + reg_message(STRING_INVALID_DWORD); + return NULL; } - *reg_count = sizeof(DWORD); - out_data = HeapAlloc(GetProcessHeap(),0,*reg_count); - ((LPDWORD)out_data)[0] = val; - break; + + if (i == 0xffffffff) + WINE_FIXME("Check for integer overflow.\n"); + + data = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD)); + *(DWORD *) data = i; + *size_out = sizeof(DWORD); + return data; + } + case REG_MULTI_SZ: + { + WCHAR *data = HeapAlloc(GetProcessHeap(), 0, (strlenW(input) + 1) * sizeof(WCHAR)); + DWORD p; + + for (i = 0, p = 0; i <= strlenW(input); i++, p++) + { + /* If this character is the separator, or no separator has been given and these + * characters are "\\0", then add a 0 indicating the end of this string */ + if ( (separator && input[i] == separator) || + (!separator && input[i] == '\\' && input[i + 1] == '0') ) + { + /* If it's the first character or the previous one was a separator */ + if (!p || data[p - 1] == 0) + { + HeapFree(GetProcessHeap(), 0, data); + reg_message(STRING_INVALID_CMDLINE); + return NULL; + } + data[p] = 0; + + if (!separator) + i++; + } + else + data[p] = input[i]; + } + + /* Add a 0 to the end if the string wasn't "", and it wasn't + * double-0-terminated already (In the case of a trailing separator) */ + if (p > 1 && data[p - 2]) + data[p++] = 0; + + *size_out = p * sizeof(WCHAR); + return (BYTE *) data; + } + case REG_BINARY: + { + BYTE *data = HeapAlloc(GetProcessHeap(), 0, strlenW(input)); + DWORD p, odd; + + for (i = 0; i < strlenW(input); i++) + { + if (input[i] >= '0' && input[i] <= '9') + data[i] = input[i] - '0'; + else if (input[i] >= 'a' && input[i] <= 'f') + data[i] = input[i] - 'a' + 10; + else if (input[i] >= 'A' && input[i] <= 'F') + data[i] = input[i] - 'A' + 10; + else + { + HeapFree(GetProcessHeap(), 0, data); + reg_message(STRING_INVALID_CMDLINE); + return NULL; + } + } + + odd = i & 1; + p = i >> 1; + data += odd; + + for (i = 0; i < p; i++) + data[i] = (data[i * 2] << 4) | data[i * 2 + 1]; + + *size_out = p + odd; + return data - odd; } default: { - static const WCHAR unhandled[] = {'U','n','h','a','n','d','l','e','d',' ','T','y','p','e',' ','0','x','%','x',' ',' ','d','a','t','a',' ','%','s','\n',0}; - reg_printfW(unhandled, reg_type,data); + WINE_FIXME("Add support for registry type: %u\n", type); + return NULL; } } - - return out_data; } static BOOL sane_path(const WCHAR *key) @@ -286,7 +374,14 @@ static int reg_add(WCHAR *key_name, WCHAR *value_name, BOOL value_empty, } if (data) - reg_data = get_regdata(data,reg_type,separator,®_count); + { + reg_data = wchar_get_data(data, reg_type, separator, ®_count); + if (!reg_data) + { + RegCloseKey(subkey); + return 1; + } + } RegSetValueExW(subkey,value_name,0,reg_type,reg_data,reg_count); HeapFree(GetProcessHeap(),0,reg_data); @@ -336,8 +431,6 @@ static int reg_delete(WCHAR *key_name, WCHAR *value_name, BOOL value_empty, /* Delete subtree only if no /v* option is given */ if (!value_name && !value_empty && !value_all) { - static const WCHAR empty_wstr[] = {0}; - err = RegDeleteTreeW(subkey, NULL); if (err != ERROR_SUCCESS) { diff --git a/programs/reg/reg.h b/programs/reg/reg.h index 6fca7eb..c6fb151 100644 --- a/programs/reg/reg.h +++ b/programs/reg/reg.h @@ -31,3 +31,4 @@ #define STRING_NO_REMOTE 108 #define STRING_CANNOT_FIND 109 #define STRING_UNSUPPORTED_TYPE 110 +#define STRING_INVALID_DWORD 111 diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc index 94dddf2..dbd92ea 100644 --- a/programs/reg/reg.rc +++ b/programs/reg/reg.rc @@ -36,4 +36,5 @@ STRINGTABLE STRING_NO_REMOTE, "Error: Unable to access remote machine\n" STRING_CANNOT_FIND, "Error: The system was unable to find the specified registry key or value\n" STRING_UNSUPPORTED_TYPE, "Error: Unsupported type\n" + STRING_INVALID_DWORD, "Error: /d must be positive number\n" } diff --git a/programs/reg/tests/reg.c b/programs/reg/tests/reg.c index dbfce89..99b4813 100644 --- a/programs/reg/tests/reg.c +++ b/programs/reg/tests/reg.c @@ -154,7 +154,7 @@ static void test_add(void) /* REG_NONE */ run_reg_exe("reg add HKCU\\" KEY_BASE " /v none0 /d deadbeef /t REG_NONE /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %d\n", r); - verify_reg(hkey, "none0", REG_NONE, "d\0e\0a\0d\0b\0e\0e\0f\0\0", 18, TODO_REG_SIZE); + verify_reg(hkey, "none0", REG_NONE, "d\0e\0a\0d\0b\0e\0e\0f\0\0", 18, 0); /* REG_SZ */ run_reg_exe("reg add HKCU\\" KEY_BASE " /d WineTest /f", &r); @@ -188,11 +188,11 @@ static void test_add(void) /* REG_EXPAND_SZ */ run_reg_exe("reg add HKCU\\" KEY_BASE " /v expand0 /t REG_EXpand_sz /d \"dead%PATH%beef\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "expand0", REG_EXPAND_SZ, "dead%PATH%beef", 15, TODO_REG_SIZE); + verify_reg(hkey, "expand0", REG_EXPAND_SZ, "dead%PATH%beef", 15, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /v expand1 /t REG_EXpand_sz /d \"dead^%PATH^%beef\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "expand1", REG_EXPAND_SZ, "dead^%PATH^%beef", 17, TODO_REG_SIZE); + verify_reg(hkey, "expand1", REG_EXPAND_SZ, "dead^%PATH^%beef", 17, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_EXPAND_SZ /v expand2 /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); @@ -200,11 +200,11 @@ static void test_add(void) run_reg_exe("reg add HKEY_CURRENT_USER\\" KEY_BASE " /ve /t REG_EXPAND_SZ /d WineTEST /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "", REG_EXPAND_SZ, "WineTEST", 9, TODO_REG_SIZE); + verify_reg(hkey, "", REG_EXPAND_SZ, "WineTEST", 9, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_EXPAND_SZ /v expand3 /f /d \"\"", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "expand3", REG_EXPAND_SZ, "", 1, TODO_REG_SIZE); + verify_reg(hkey, "expand3", REG_EXPAND_SZ, "", 1, 0); /* REG_BINARY */ run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin0 /f", &r); @@ -214,14 +214,14 @@ static void test_add(void) run_reg_exe("reg add HKEY_CURRENT_USER\\" KEY_BASE " /ve /t REG_BINARY /d deadbeef /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); dword = 0xefbeadde; - verify_reg(hkey, "", REG_BINARY, &dword, sizeof(DWORD), TODO_REG_SIZE); + verify_reg(hkey, "", REG_BINARY, &dword, sizeof(DWORD), 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin1 /f /d 0xDeAdBeEf", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin2 /f /d x01", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin3 /f /d 01x", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin4 /f /d DeAdBeEf0DD", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); @@ -235,8 +235,8 @@ static void test_add(void) err = RegQueryValueExA(hkey, "bin4", NULL, &type, (void *) (buffer+12), &size); ok(err == ERROR_SUCCESS, "RegQueryValueEx failed: got %d\n", err); ok(type == REG_BINARY, "got wrong type %u\n", type); - todo_wine ok(size == 6, "got wrong size %u\n", size); - todo_wine ok(memcmp(buffer, buffer+12, 6) == 0 || + ok(size == 6, "got wrong size %u\n", size); + ok(memcmp(buffer, buffer+12, 6) == 0 || broken(memcmp(buffer+6, buffer+12, 6) == 0 /* WinXP */), "got wrong data\n"); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin5 /d \"\" /f", &r); @@ -257,11 +257,11 @@ static void test_add(void) todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %d, expected 0\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword2 /t REG_DWORD /d zzz /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword3 /t REG_DWORD /d deadbeef /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword4 /t REG_DWORD /d 123xyz /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword5 /t reg_dword /d 12345678 /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); @@ -275,22 +275,21 @@ static void test_add(void) ok(err == ERROR_SUCCESS, "RegQueryValueEx failed: got %d\n", err); ok(type == REG_DWORD, "got wrong type %d, expected %d\n", type, REG_DWORD); ok(size == sizeof(DWORD), "got wrong size %d, expected %d\n", size, (int)sizeof(DWORD)); - todo_wine ok(dword == 123 || broken(dword == 0123 /* WinXP */), + ok(dword == 123 || broken(dword == 0123 /* WinXP */), "got wrong data %d, expected %d\n", dword, 123); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword7 /t reg_dword /d 0xabcdefg /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword8 /t REG_dword /d 0xdeadbeef /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); dword = 0xdeadbeef; - verify_reg(hkey, "dword8", REG_DWORD, &dword, sizeof(dword), - (sizeof(long) > sizeof(DWORD)) ? 0 : TODO_REG_DATA); + verify_reg(hkey, "dword8", REG_DWORD, &dword, sizeof(dword), 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_DWORD /v dword9 /f /d -1", &r); - todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_DWORD /v dword10 /f /d -0x1", &r); - todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword8 /t REG_dword /d 0x01ffffffff /f", &r); todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %d\n", r); @@ -305,7 +304,7 @@ static void test_add(void) run_reg_exe("reg add HKCU\\" KEY_BASE " /v DWORD_BE /t REG_DWORD_BIG_ENDIAN /d 456 /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); dword = 456; - verify_reg(hkey, "DWORD_BE", REG_DWORD_BIG_ENDIAN, &dword, sizeof(dword), TODO_REG_SIZE); + verify_reg(hkey, "DWORD_BE", REG_DWORD_BIG_ENDIAN, &dword, sizeof(dword), 0); /* REG_DWORD_BIG_ENDIAN is broken in every version of windows. It behaves like * an ordinary REG_DWORD - that is little endian. GG */ @@ -313,15 +312,15 @@ static void test_add(void) run_reg_exe("reg add HKCU\\" KEY_BASE " /v multi0 /t REG_MULTI_SZ /d \"three\\0little\\0strings\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); memcpy(buffer, "three\0little\0strings\0", 22); - verify_reg(hkey, "multi0", REG_MULTI_SZ, buffer, 22, TODO_REG_SIZE); + verify_reg(hkey, "multi0", REG_MULTI_SZ, buffer, 22, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi1 /s \"#\" /d \"three#little#strings\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "multi1", REG_MULTI_SZ, buffer, 22, TODO_REG_SIZE); + verify_reg(hkey, "multi1", REG_MULTI_SZ, buffer, 22, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi2 /d \"\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "multi2", REG_MULTI_SZ, &buffer[21], 1, TODO_REG_SIZE); + verify_reg(hkey, "multi2", REG_MULTI_SZ, &buffer[21], 1, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi3 /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); @@ -329,7 +328,7 @@ static void test_add(void) run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi4 /s \"#\" /d \"threelittlestrings\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); - verify_reg(hkey, "multi4", REG_MULTI_SZ, "threelittlestrings\0", 20, TODO_REG_SIZE); + verify_reg(hkey, "multi4", REG_MULTI_SZ, "threelittlestrings\0", 20, 0); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi5 /s \"#randomgibberish\" /d \"three#little#strings\" /f", &r); todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); @@ -338,16 +337,16 @@ static void test_add(void) run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi7 /s \"\" /d \"three#little#strings\" /f", &r); todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi8 /s \"#\" /d \"##\" /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi9 /s \"#\" /d \"two##strings\" /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi10 /s \"#\" /d \"#a\" /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r); run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi11 /s \"#\" /d \"a#\" /f", &r); ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r); buffer[0]='a'; buffer[1]=0; buffer[2]=0; - verify_reg(hkey, "multi11", REG_MULTI_SZ, buffer, 3, TODO_REG_SIZE); + verify_reg(hkey, "multi11", REG_MULTI_SZ, buffer, 3, 0); RegCloseKey(hkey); -- 2.5.0
---
programs/reg/reg.c       | 145 ++++++++++++++++++++++++++++++++++++++---------
programs/reg/reg.h       |   1 +
programs/reg/reg.rc      |   1 +
programs/reg/tests/reg.c |  57 +++++++++----------
4 files changed, 149 insertions(+), 55 deletions(-)

diff --git a/programs/reg/reg.c b/programs/reg/reg.c
index cf6425e..b2447fc 100644
--- a/programs/reg/reg.c
+++ b/programs/reg/reg.c
@@ -18,10 +18,15 @@
 
#include <windows.h>
#include <wine/unicode.h>
+#include <wine/debug.h>
#include "reg.h"
 
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*A))
 
+WINE_DEFAULT_DEBUG_CHANNEL(reg);
+
+static const WCHAR empty_wstr[] = {0};
+
static const WCHAR short_hklm[] = {'H','K','L','M',0};
static const WCHAR short_hkcu[] = {'H','K','C','U',0};
static const WCHAR short_hkcr[] = {'H','K','C','R',0};
@@ -189,43 +194,126 @@ static DWORD wchar_get_type(const WCHAR *type_name)
    return ~0u;
}
 
-static LPBYTE get_regdata(LPWSTR data, DWORD reg_type, WCHAR separator, DWORD *reg_count)
+static BYTE *wchar_get_data(const WCHAR *input, const DWORD type, const WCHAR separator,
+    DWORD *size_out)
{
-    LPBYTE out_data = NULL;
-    *reg_count = 0;
+    DWORD i;
+
+    if (!input)
+        input = empty_wstr;
 
-    switch (reg_type)
+    switch (type)
    {
+        case REG_NONE:
        case REG_SZ:
+        case REG_EXPAND_SZ:
        {
-            *reg_count = (lstrlenW(data) + 1) * sizeof(WCHAR);
-            out_data = HeapAlloc(GetProcessHeap(),0,*reg_count);
-            lstrcpyW((LPWSTR)out_data,data);
-            break;
+            BYTE *data;
+
+            i = (strlenW(input) + 1) * sizeof(WCHAR);
+            *size_out = i;
+            data = HeapAlloc(GetProcessHeap(), 0, i);
+            memcpy(data, input, i);
+            return data;
        }
        case REG_DWORD:
+        case REG_DWORD_BIG_ENDIAN:
        {
-            LPWSTR rest;
-            DWORD val;
-            val = strtolW(data, &rest, 0);
-            if (rest == data) {
-                static const WCHAR nonnumber[] = {'E','r','r','o','r',':',' ','/','d',' ','r','e','q','u','i','r','e','s',' ','n','u','m','b','e','r','.','\n',0};
-                reg_printfW(nonnumber);
-                break;
+            BYTE *data;
+            WCHAR *temp;
+
+            if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X'))
+                i = strtoulW(input, &temp, 16);
+            else
+                i = strtoulW(input, &temp, 10);
+
+            if (input[0] == '-' || temp[0] || temp == input)
+            {
+                reg_message(STRING_INVALID_DWORD);
+                return NULL;
            }
-            *reg_count = sizeof(DWORD);
-            out_data = HeapAlloc(GetProcessHeap(),0,*reg_count);
-            ((LPDWORD)out_data)[0] = val;
-            break;
+
+            if (i == 0xffffffff)
+                WINE_FIXME("Check for integer overflow.\n");
+
+            data = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
+            *(DWORD *) data = i;
+            *size_out = sizeof(DWORD);
+            return data;
+        }
+        case REG_MULTI_SZ:
+        {
+            WCHAR *data = HeapAlloc(GetProcessHeap(), 0, (strlenW(input) + 1) * sizeof(WCHAR));
+            DWORD p;
+
+            for (i = 0, p = 0; i <= strlenW(input); i++, p++)
+            {
+                /* If this character is the separator, or no separator has been given and these
+                 * characters are "\\0", then add a 0 indicating the end of this string */
+                if ( (separator && input[i] == separator) ||
+                     (!separator && input[i] == '\\' && input[i + 1] == '0') )
+                {
+                    /* If it's the first character or the previous one was a separator */
+                    if (!p || data[p - 1] == 0)
+                    {
+                        HeapFree(GetProcessHeap(), 0, data);
+                        reg_message(STRING_INVALID_CMDLINE);
+                        return NULL;
+                    }
+                    data[p] = 0;
+
+                    if (!separator)
+                        i++;
+                }
+                else
+                    data[p] = input[i];
+            }
+
+            /* Add a 0 to the end if the string wasn't "", and it wasn't
+             * double-0-terminated already (In the case of a trailing separator) */
+            if (p > 1 && data[p - 2])
+                data[p++] = 0;
+
+            *size_out = p * sizeof(WCHAR);
+            return (BYTE *) data;
+        }
+        case REG_BINARY:
+        {
+            BYTE *data = HeapAlloc(GetProcessHeap(), 0, strlenW(input));
+            DWORD p, odd;
+
+            for (i = 0; i < strlenW(input); i++)
+            {
+                if (input[i] >= '0' && input[i] <= '9')
+                    data[i] = input[i] - '0';
+                else if (input[i] >= 'a' && input[i] <= 'f')
+                    data[i] = input[i] - 'a' + 10;
+                else if (input[i] >= 'A' && input[i] <= 'F')
+                    data[i] = input[i] - 'A' + 10;
+                else
+                {
+                    HeapFree(GetProcessHeap(), 0, data);
+                    reg_message(STRING_INVALID_CMDLINE);
+                    return NULL;
+                }
+            }
+
+            odd = i & 1;
+            p = i >> 1;
+            data += odd;
+
+            for (i = 0; i < p; i++)
+                data[i] = (data[i * 2] << 4) | data[i * 2 + 1];
+
+            *size_out = p + odd;
+            return data - odd;
        }
        default:
        {
-            static const WCHAR unhandled[] = {'U','n','h','a','n','d','l','e','d',' ','T','y','p','e',' ','0','x','%','x',' ',' ','d','a','t','a',' ','%','s','\n',0};
-            reg_printfW(unhandled, reg_type,data);
+            WINE_FIXME("Add support for registry type: %u\n", type);
+            return NULL;
        }
    }
-
-    return out_data;
}
 
static BOOL sane_path(const WCHAR *key)
@@ -286,7 +374,14 @@ static int reg_add(WCHAR *key_name, WCHAR *value_name, BOOL value_empty,
        }
 
        if (data)
-            reg_data = get_regdata(data,reg_type,separator,®_count);
+        {
+            reg_data = wchar_get_data(data, reg_type, separator, ®_count);
+            if (!reg_data)
+            {
+                RegCloseKey(subkey);
+                return 1;
+            }
+        }
 
        RegSetValueExW(subkey,value_name,0,reg_type,reg_data,reg_count);
        HeapFree(GetProcessHeap(),0,reg_data);
@@ -336,8 +431,6 @@ static int reg_delete(WCHAR *key_name, WCHAR *value_name, BOOL value_empty,
    /* Delete subtree only if no /v* option is given */
    if (!value_name && !value_empty && !value_all)
    {
-        static const WCHAR empty_wstr[] = {0};
-
        err = RegDeleteTreeW(subkey, NULL);
        if (err != ERROR_SUCCESS)
        {
diff --git a/programs/reg/reg.h b/programs/reg/reg.h
index 6fca7eb..c6fb151 100644
--- a/programs/reg/reg.h
+++ b/programs/reg/reg.h
@@ -31,3 +31,4 @@
#define STRING_NO_REMOTE        108
#define STRING_CANNOT_FIND      109
#define STRING_UNSUPPORTED_TYPE 110
+#define STRING_INVALID_DWORD    111
diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc
index 94dddf2..dbd92ea 100644
--- a/programs/reg/reg.rc
+++ b/programs/reg/reg.rc
@@ -36,4 +36,5 @@ STRINGTABLE
    STRING_NO_REMOTE, "Error: Unable to access remote machine\n"
    STRING_CANNOT_FIND, "Error: The system was unable to find the specified registry key or value\n"
    STRING_UNSUPPORTED_TYPE, "Error: Unsupported type\n"
+    STRING_INVALID_DWORD, "Error: /d must be positive number\n"
}
diff --git a/programs/reg/tests/reg.c b/programs/reg/tests/reg.c
index dbfce89..99b4813 100644
--- a/programs/reg/tests/reg.c
+++ b/programs/reg/tests/reg.c
@@ -154,7 +154,7 @@ static void test_add(void)
    /* REG_NONE */
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v none0 /d deadbeef /t REG_NONE /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %d\n", r);
-    verify_reg(hkey, "none0", REG_NONE, "d\0e\0a\0d\0b\0e\0e\0f\0\0", 18, TODO_REG_SIZE);
+    verify_reg(hkey, "none0", REG_NONE, "d\0e\0a\0d\0b\0e\0e\0f\0\0", 18, 0);
 
    /* REG_SZ */
    run_reg_exe("reg add HKCU\\" KEY_BASE " /d WineTest /f", &r);
@@ -188,11 +188,11 @@ static void test_add(void)
    /* REG_EXPAND_SZ */
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v expand0 /t REG_EXpand_sz /d \"dead%PATH%beef\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "expand0", REG_EXPAND_SZ, "dead%PATH%beef", 15, TODO_REG_SIZE);
+    verify_reg(hkey, "expand0", REG_EXPAND_SZ, "dead%PATH%beef", 15, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v expand1 /t REG_EXpand_sz /d \"dead^%PATH^%beef\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "expand1", REG_EXPAND_SZ, "dead^%PATH^%beef", 17, TODO_REG_SIZE);
+    verify_reg(hkey, "expand1", REG_EXPAND_SZ, "dead^%PATH^%beef", 17, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_EXPAND_SZ /v expand2 /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
@@ -200,11 +200,11 @@ static void test_add(void)
 
    run_reg_exe("reg add HKEY_CURRENT_USER\\" KEY_BASE " /ve /t REG_EXPAND_SZ /d WineTEST /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "", REG_EXPAND_SZ, "WineTEST", 9, TODO_REG_SIZE);
+    verify_reg(hkey, "", REG_EXPAND_SZ, "WineTEST", 9, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_EXPAND_SZ /v expand3 /f /d \"\"", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "expand3", REG_EXPAND_SZ, "", 1, TODO_REG_SIZE);
+    verify_reg(hkey, "expand3", REG_EXPAND_SZ, "", 1, 0);
 
    /* REG_BINARY */
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin0 /f", &r);
@@ -214,14 +214,14 @@ static void test_add(void)
    run_reg_exe("reg add HKEY_CURRENT_USER\\" KEY_BASE " /ve /t REG_BINARY /d deadbeef /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
    dword = 0xefbeadde;
-    verify_reg(hkey, "", REG_BINARY, &dword, sizeof(DWORD), TODO_REG_SIZE);
+    verify_reg(hkey, "", REG_BINARY, &dword, sizeof(DWORD), 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin1 /f /d 0xDeAdBeEf", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin2 /f /d x01", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin3 /f /d 01x", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin4 /f /d DeAdBeEf0DD", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
@@ -235,8 +235,8 @@ static void test_add(void)
    err = RegQueryValueExA(hkey, "bin4", NULL, &type, (void *) (buffer+12), &size);
    ok(err == ERROR_SUCCESS, "RegQueryValueEx failed: got %d\n", err);
    ok(type == REG_BINARY, "got wrong type %u\n", type);
-    todo_wine ok(size == 6, "got wrong size %u\n", size);
-    todo_wine ok(memcmp(buffer, buffer+12, 6) == 0 ||
+    ok(size == 6, "got wrong size %u\n", size);
+    ok(memcmp(buffer, buffer+12, 6) == 0 ||
        broken(memcmp(buffer+6, buffer+12, 6) == 0 /* WinXP */), "got wrong data\n");
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin5 /d \"\" /f", &r);
@@ -257,11 +257,11 @@ static void test_add(void)
    todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */),
       "got exit code %d, expected 0\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword2 /t REG_DWORD /d zzz /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword3 /t REG_DWORD /d deadbeef /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword4 /t REG_DWORD /d 123xyz /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword5 /t reg_dword /d 12345678 /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);
@@ -275,22 +275,21 @@ static void test_add(void)
    ok(err == ERROR_SUCCESS, "RegQueryValueEx failed: got %d\n", err);
    ok(type == REG_DWORD, "got wrong type %d, expected %d\n", type, REG_DWORD);
    ok(size == sizeof(DWORD), "got wrong size %d, expected %d\n", size, (int)sizeof(DWORD));
-    todo_wine ok(dword == 123 || broken(dword == 0123 /* WinXP */),
+    ok(dword == 123 || broken(dword == 0123 /* WinXP */),
                 "got wrong data %d, expected %d\n", dword, 123);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword7 /t reg_dword /d 0xabcdefg /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword8 /t REG_dword /d 0xdeadbeef /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);
    dword = 0xdeadbeef;
-    verify_reg(hkey, "dword8", REG_DWORD, &dword, sizeof(dword),
-               (sizeof(long) > sizeof(DWORD)) ? 0 : TODO_REG_DATA);
+    verify_reg(hkey, "dword8", REG_DWORD, &dword, sizeof(dword), 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_DWORD /v dword9 /f /d -1", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_DWORD /v dword10 /f /d -0x1", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword8 /t REG_dword /d 0x01ffffffff /f", &r);
    todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %d\n", r);
@@ -305,7 +304,7 @@ static void test_add(void)
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v DWORD_BE /t REG_DWORD_BIG_ENDIAN /d 456 /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
    dword = 456;
-    verify_reg(hkey, "DWORD_BE", REG_DWORD_BIG_ENDIAN, &dword, sizeof(dword), TODO_REG_SIZE);
+    verify_reg(hkey, "DWORD_BE", REG_DWORD_BIG_ENDIAN, &dword, sizeof(dword), 0);
    /* REG_DWORD_BIG_ENDIAN is broken in every version of windows. It behaves like
     * an ordinary REG_DWORD - that is little endian. GG */
 
@@ -313,15 +312,15 @@ static void test_add(void)
    run_reg_exe("reg add HKCU\\" KEY_BASE " /v multi0 /t REG_MULTI_SZ /d \"three\\0little\\0strings\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
    memcpy(buffer, "three\0little\0strings\0", 22);
-    verify_reg(hkey, "multi0", REG_MULTI_SZ, buffer, 22, TODO_REG_SIZE);
+    verify_reg(hkey, "multi0", REG_MULTI_SZ, buffer, 22, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi1 /s \"#\" /d \"three#little#strings\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "multi1", REG_MULTI_SZ, buffer, 22, TODO_REG_SIZE);
+    verify_reg(hkey, "multi1", REG_MULTI_SZ, buffer, 22, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi2 /d \"\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "multi2", REG_MULTI_SZ, &buffer[21], 1, TODO_REG_SIZE);
+    verify_reg(hkey, "multi2", REG_MULTI_SZ, &buffer[21], 1, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi3 /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
@@ -329,7 +328,7 @@ static void test_add(void)
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi4 /s \"#\" /d \"threelittlestrings\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
-    verify_reg(hkey, "multi4", REG_MULTI_SZ, "threelittlestrings\0", 20, TODO_REG_SIZE);
+    verify_reg(hkey, "multi4", REG_MULTI_SZ, "threelittlestrings\0", 20, 0);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi5 /s \"#randomgibberish\" /d \"three#little#strings\" /f", &r);
    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
@@ -338,16 +337,16 @@ static void test_add(void)
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi7 /s \"\" /d \"three#little#strings\" /f", &r);
    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi8 /s \"#\" /d \"##\" /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi9 /s \"#\" /d \"two##strings\" /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi10 /s \"#\" /d \"#a\" /f", &r);
-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);
 
    run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi11 /s \"#\" /d \"a#\" /f", &r);
    ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);
    buffer[0]='a'; buffer[1]=0; buffer[2]=0;
-    verify_reg(hkey, "multi11", REG_MULTI_SZ, buffer, 3, TODO_REG_SIZE);
+    verify_reg(hkey, "multi11", REG_MULTI_SZ, buffer, 3, 0);
 
    RegCloseKey(hkey);
 
--  
2.5.0