From: Jonathan Vollebregt Subject: [resend 6/8] reg: Add wchar/raw data conversion functions Message-Id: <1421255548-6679-6-git-send-email-jnvsor@gmail.com> Date: Wed, 14 Jan 2015 18:12:26 +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 | 140 +++++++++++++++++++++++++++++++++++++++-------- programs/reg/reg.h | 1 + programs/reg/reg.rc | 1 + programs/reg/tests/reg.c | 57 ++++++++++--------- 4 files changed, 146 insertions(+), 53 deletions(-) diff --git a/programs/reg/reg.c b/programs/reg/reg.c index 3ffcc25..0d892fd 100755 --- a/programs/reg/reg.c +++ b/programs/reg/reg.c @@ -18,11 +18,15 @@ #include #include +#include #include "reg.h" #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*A)) #define ERROR_NO_REMOTE 20000 +#define ERROR_INVALID_DWORD 20001 + +WINE_DEFAULT_DEBUG_CHANNEL(reg); static const WCHAR empty_wstr[] = {0}; @@ -146,6 +150,9 @@ static void reg_print_error(LSTATUS error_code) case ERROR_UNSUPPORTED_TYPE: reg_message(STRING_UNSUPPORTED_TYPE); return; + case ERROR_INVALID_DWORD: + reg_message(STRING_INVALID_DWORD); + return; default: { static const WCHAR error_string[] = {'%','0','5','d',':',' ','%','s',0}; @@ -213,43 +220,120 @@ 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 LSTATUS wchar_get_data(const WCHAR *input, const DWORD type, const WCHAR separator, + DWORD *size_out, BYTE **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; + 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_INVALID_DWORD; + + if (i == 0xffffffff) + WINE_FIXME("Check for integer overflow.\n"); + + *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_INVALID_DATA; + } + 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 = (BYTE *) temp; + return ERROR_SUCCESS; + } + 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 + { + HeapFree(GetProcessHeap(), 0, temp); + return ERROR_INVALID_DATA; + } + } + + *out = temp; + odd = p & 1; + temp += odd; + p >>= 1; + + for (i = 0; i < p; i++) + temp[i] = (temp[i * 2] << 4) | temp[i * 2 + 1]; + + *size_out = p + odd; + return ERROR_SUCCESS; } 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 ERROR_UNSUPPORTED_TYPE; } } - - return out_data; } static LSTATUS sane_path(const WCHAR *key) @@ -312,7 +396,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); + { + err = wchar_get_data(data, reg_type, separator, ®_count, ®_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 42de422..00c4d66 100644 --- a/programs/reg/reg.h +++ b/programs/reg/reg.h @@ -32,3 +32,4 @@ #define STRING_CANNOT_FIND 109 #define STRING_ERROR 110 #define STRING_UNSUPPORTED_TYPE 111 +#define STRING_INVALID_DWORD 112 diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc index beafd4f..730d421 100644 --- a/programs/reg/reg.rc +++ b/programs/reg/reg.rc @@ -37,4 +37,5 @@ STRINGTABLE STRING_CANNOT_FIND, "Error: The system was unable to find the specified registry key or value\n" STRING_ERROR, "Unexpected error: " 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.1.4