[PATCH 5/7] reg: Add wchar/raw data conversion functions

Jonathan Vollebregt jnvsor at gmail.com
Tue Nov 4 14:15:12 CST 2014


In the case of REG_SZ and the like, it may seem like the functions
perform an unncessary copy of the strings which should already be
in memory ripe for the taking.

However because these functions handle more than one type of data
the calling function needs to be able to free the data afterwards.

Simply returning the input string would result in a function
freeing one of it's own parameters, so we make sure to return
new memory by making a copy.
---
 programs/reg/reg.c       | 163 +++++++++++++++++++++++++++++++++++++++--------
 programs/reg/reg.h       |   2 +
 programs/reg/reg.rc      |   2 +
 programs/reg/tests/reg.c |  55 ++++++++--------
 4 files changed, 169 insertions(+), 53 deletions(-)

diff --git a/programs/reg/reg.c b/programs/reg/reg.c
index 7b7d297..45e0d64c 100755
--- a/programs/reg/reg.c
+++ b/programs/reg/reg.c
@@ -24,6 +24,7 @@
 
 #define ERROR_NO_REMOTE         20000
 #define ERROR_INVALID_TYPE      20001
+#define ERROR_NAN               20002
 
 typedef struct
 {
@@ -154,6 +155,10 @@ static int reg_print_error(LONG error_code)
             return reg_message(STRING_NO_REMOTE);
         case ERROR_INVALID_TYPE:
             return reg_message(STRING_INVALID_TYPE);
+        case ERROR_UNSUPPORTED_TYPE:
+            return reg_message(STRING_UNSUPPORTED_TYPE);
+        case ERROR_NAN:
+            return reg_message(STRING_NAN);
         default:
         {
             static const WCHAR error_string[] = {'%','0','5','d',':',' ','%','s',0};
@@ -235,43 +240,143 @@ static LSTATUS wchar_get_type(const WCHAR *type_name, DWORD *out)
     return ERROR_INVALID_TYPE;
 }
 
-static LPBYTE get_regdata(LPWSTR data, DWORD reg_type, WCHAR separator, DWORD *reg_count)
+static LSTATUS data_default(const DWORD type, DWORD *size_out, BYTE **out)
 {
-    LPBYTE out_data = NULL;
-    *reg_count = 0;
+    switch (type)
+    {
+        case REG_SZ:
+        case REG_EXPAND_SZ:
+        case REG_MULTI_SZ:
+        {
+            *out = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR));
+            ** (WCHAR **) out = 0;
+            *size_out = sizeof(WCHAR);
+            return ERROR_SUCCESS;
+        }
+        case REG_DWORD:
+        case REG_DWORD_BIG_ENDIAN:
+        {
+            return ERROR_NAN;
+        }
+        case REG_BINARY:
+        {
+            *out = NULL;
+            *size_out = 0;
+            return ERROR_SUCCESS;
+        }
+        default:
+            return ERROR_UNSUPPORTED_TYPE;
+    }
+}
+
+static LSTATUS wchar_get_data(  const WCHAR *input,     const DWORD type,
+                                const WCHAR separator,  DWORD *size_out,    BYTE **out)
+{
+    DWORD i;
 
-    switch (reg_type)
+    if (!input)
+        return data_default(type, size_out, out);
+
+    switch (type)
     {
         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;
+            i = (strlenW(input) + 1) * sizeof(WCHAR);
+            *out = HeapAlloc(GetProcessHeap(), 0, i);
+            memcpy(*out, input, i);
+            *size_out = i;
+            return ERROR_SUCCESS;
         }
         case REG_DWORD:
+        case REG_DWORD_BIG_ENDIAN:
+        {
+            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)
+                return ERROR_NAN;
+
+            *out = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
+            ** (DWORD **) out = i;
+            *size_out = sizeof(DWORD);
+            return ERROR_SUCCESS;
+        }
+        case REG_MULTI_SZ:
         {
-            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;
+            WCHAR *temp = 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 || temp[p - 1] == 0)
+                    {
+                        HeapFree(GetProcessHeap(), 0, temp);
+                        return ERROR_BAD_COMMAND;
+                    }
+                    temp[p] = 0;
+
+                    if (!separator)
+                        i++;
+                }
+                else
+                    temp[p] = input[i];
             }
-            *reg_count = sizeof(DWORD);
-            out_data = HeapAlloc(GetProcessHeap(),0,*reg_count);
-            ((LPDWORD)out_data)[0] = val;
-            break;
+
+            /* 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 && temp[p - 2])
+                temp[p++] = 0;
+
+            *size_out = p * sizeof(WCHAR);
+            *out = HeapReAlloc(GetProcessHeap(), 0, temp, p * sizeof(WCHAR));
+            return ERROR_SUCCESS;
         }
-        default:
+        case REG_BINARY:
         {
-            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);
+            BYTE * temp = HeapAlloc(GetProcessHeap(), 0, strlenW(input));
+            DWORD p, odd;
+
+            for (i = 0, p = 0; i < strlenW(input); i++, p++)
+            {
+                if (input[i] >= '0' && input[i] <= '9')
+                    temp[p] = input[i] - '0';
+                else if (input[i] >= 'a' && input[i] <= 'f')
+                    temp[p] = input[i] - 'a' + 10;
+                else if (input[i] >= 'A' && input[i] <= 'F')
+                    temp[p] = input[i] - 'A' + 10;
+                else
+                {
+                    HeapFree(GetProcessHeap(), 0, temp);
+                    return ERROR_BAD_COMMAND;
+                }
+            }
+
+            odd = p & 1;
+            temp += odd;
+            p /= 2;
+
+            for (i = 0; i < p; i++)
+                temp[i] = (temp[i * 2] << 4) | temp[i * 2 + 1];
+
+            p += odd;
+            *size_out = p;
+            *out = HeapReAlloc(GetProcessHeap(), 0, temp-odd, p);
+            return ERROR_SUCCESS;
         }
+        default:
+            return ERROR_UNSUPPORTED_TYPE;
     }
-
-    return out_data;
 }
 
 static int reg_add(WCHAR *key_name, WCHAR *value_name, BOOL value_empty,
@@ -329,7 +434,15 @@ static int reg_add(WCHAR *key_name, WCHAR *value_name, BOOL value_empty,
         }
 
         if (data)
-            reg_data = get_regdata(data,reg_type,separator,&reg_count);
+        {
+            err = wchar_get_data(data, reg_type, separator, &reg_count, &reg_data);
+            if (err != ERROR_SUCCESS)
+            {
+                RegCloseKey(subkey);
+                reg_print_error(err);
+                return 1;
+            }
+        }
 
         RegSetValueExW(subkey,value_name,0,reg_type,reg_data,reg_count);
         HeapFree(GetProcessHeap(),0,reg_data);
diff --git a/programs/reg/reg.h b/programs/reg/reg.h
index d49d3d8..334e7ad 100644
--- a/programs/reg/reg.h
+++ b/programs/reg/reg.h
@@ -32,3 +32,5 @@
 #define STRING_CANNOT_FIND      109
 #define STRING_ERROR            110
 #define STRING_INVALID_TYPE     111
+#define STRING_UNSUPPORTED_TYPE 112
+#define STRING_NAN              113
diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc
index 9892b53..a53013c 100644
--- a/programs/reg/reg.rc
+++ b/programs/reg/reg.rc
@@ -37,4 +37,6 @@ STRINGTABLE
     STRING_CANNOT_FIND, "Error: The system was unable to find the specified registry key or value\n"
     STRING_ERROR, "Error: "
     STRING_INVALID_TYPE, "Error: Invalid type\n"
+    STRING_UNSUPPORTED_TYPE, "Error: Unsupported type\n"
+    STRING_NAN, "Error: This type requires /d to be a positive number\n"
 }
diff --git a/programs/reg/tests/reg.c b/programs/reg/tests/reg.c
index dbc8731..5215a15 100644
--- a/programs/reg/tests/reg.c
+++ b/programs/reg/tests/reg.c
@@ -174,11 +174,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);
@@ -186,11 +186,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);
@@ -200,14 +200,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);
@@ -221,8 +221,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");
 
     /* REG_DWORD */
@@ -239,11 +239,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);
@@ -257,22 +257,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);
 
     /* REG_DWORD_LITTLE_ENDIAN */
     run_reg_exe("reg add HKCU\\" KEY_BASE " /v DWORD_LE /t REG_DWORD_LITTLE_ENDIAN /d 456 /f", &r);
@@ -284,7 +283,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 */
 
@@ -292,15 +291,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);
@@ -308,7 +307,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);
@@ -317,16 +316,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.1.1




More information about the wine-patches mailing list