From: Jonathan Vollebregt Subject: [PATCH 4/6] reg: Add wchar/raw data conversion functions Message-Id: <1414411809-1943-4-git-send-email-jnvsor@gmail.com> Date: Mon, 27 Oct 2014 13:10:07 +0100 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 | 186 ++++++++++++++++++++++++++++++++++++++++------- programs/reg/reg.h | 3 + programs/reg/reg.rc | 3 + programs/reg/tests/reg.c | 55 +++++++------- 4 files changed, 194 insertions(+), 53 deletions(-) diff --git a/programs/reg/reg.c b/programs/reg/reg.c index 581ecb9..b15bd41 100755 --- a/programs/reg/reg.c +++ b/programs/reg/reg.c @@ -203,6 +203,20 @@ static HKEY path_get_key(const WCHAR *path) return k; } +static const WCHAR *type_get_wchar(const DWORD type) +{ + static const WCHAR unknown[] = {'U','N','K','N','O','W','N','_','T','Y','P','E',0}; + DWORD i; + + for (i = 0; i < ARRAY_COUNT(type_rels); i++) + { + if (type_rels[i].type == type) + return type_rels[i].name; + } + + return unknown; +} + static DWORD wchar_get_type(const WCHAR *type_name) { DWORD i; @@ -216,43 +230,157 @@ static DWORD wchar_get_type(const WCHAR *type_name) return -1; } -static LPBYTE get_regdata(LPWSTR data, DWORD reg_type, WCHAR separator, DWORD *reg_count) +static BYTE *data_default(const DWORD type, DWORD *size_out) { - LPBYTE out_data = NULL; - *reg_count = 0; + static const WCHAR unhandled[] = {'\t','%','s',' ','(','%','u',')','\n',0}; + BYTE *output; - switch (reg_type) - { + switch (type){ case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: { - *reg_count = (lstrlenW(data) + 1) * sizeof(WCHAR); - out_data = HeapAlloc(GetProcessHeap(),0,*reg_count); - lstrcpyW((LPWSTR)out_data,data); - break; + output = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)); + * (WCHAR *) output = 0; + *size_out = sizeof(WCHAR); + return output; } 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; - } - *reg_count = sizeof(DWORD); - out_data = HeapAlloc(GetProcessHeap(),0,*reg_count); - ((LPDWORD)out_data)[0] = val; - break; + reg_message(STRING_NAN); + return NULL; + } + case REG_BINARY: + { + output = HeapAlloc(GetProcessHeap(), 0, 1); + *size_out = 0; + return output; } 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); + reg_message(STRING_UNHANDLED_TYPE); + reg_printfW(unhandled, type_get_wchar(type), type); + return NULL; } } +} + +static BYTE *wchar_get_data(const WCHAR *input, const DWORD type, + const WCHAR separator, DWORD *size_out) +{ + static const WCHAR unhandled[] = {'\t','%','s',' ','(','%','u',')','\n',0}; + BYTE *output = NULL; + DWORD i; - return out_data; + if (!input) + return data_default(type, size_out); + + switch (type){ + case REG_SZ: + case REG_EXPAND_SZ: + { + i = (strlenW(input) + 1) * sizeof(WCHAR); + output = HeapAlloc(GetProcessHeap(), 0, i); + lstrcpyW((WCHAR *) output, input); + *size_out = i; + return output; + } + 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) { + reg_message(STRING_NAN); + return NULL; + } + + output = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD)); + * (DWORD *) output = i; + *size_out = sizeof(DWORD); + return output; + } + case REG_MULTI_SZ: + { + 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) + { + reg_message(STRING_INVALID_CMDLINE); + HeapFree(GetProcessHeap(), 0, temp); + return NULL; + } + temp[p] = 0; + + if (!separator) + i++; + } + else + temp[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 && temp[p - 2]) + temp[p++] = 0; + + *size_out = p * sizeof(WCHAR); + return HeapReAlloc(GetProcessHeap(), 0, temp, p * sizeof(WCHAR)); + } + case REG_BINARY: + { + 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 + { + reg_message(STRING_INVALID_CMDLINE); + HeapFree(GetProcessHeap(), 0, temp); + return NULL; + } + } + + 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; + return HeapReAlloc(GetProcessHeap(), 0, temp-odd, p); + } + default: + { + reg_message(STRING_UNHANDLED_TYPE); + reg_printfW(unhandled, type_get_wchar(type), type); + return NULL; + } + } } static int reg_add(WCHAR *key_name, WCHAR *value_name, BOOL value_empty, @@ -310,7 +438,15 @@ 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); + reg_message(STRING_ERROR); + 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 2221647..45c4782 100644 --- a/programs/reg/reg.h +++ b/programs/reg/reg.h @@ -30,3 +30,6 @@ #define STRING_INVALID_CMDLINE 107 #define STRING_NO_REMOTE 108 #define STRING_CANNOT_FIND 109 +#define STRING_ERROR 110 +#define STRING_UNHANDLED_TYPE 111 +#define STRING_NAN 112 diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc index 6f4c0c8..8eba7c9 100644 --- a/programs/reg/reg.rc +++ b/programs/reg/reg.rc @@ -35,4 +35,7 @@ STRINGTABLE STRING_INVALID_CMDLINE, "Error: Invalid command line parameters\n" 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_ERROR, "Error: An internal error occurred\n" + STRING_UNHANDLED_TYPE, "Error: Unhandled Type" + 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 1043929..efa4c48 100644 --- a/programs/reg/tests/reg.c +++ b/programs/reg/tests/reg.c @@ -163,11 +163,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); @@ -175,11 +175,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); @@ -189,14 +189,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); @@ -210,8 +210,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 */ @@ -228,11 +228,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); @@ -246,22 +246,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); @@ -273,7 +272,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 */ @@ -281,15 +280,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); @@ -297,7 +296,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); @@ -306,16 +305,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