Could someone run this testcase on native windows for me, please? :)
kernel32: fix PROFILE_Load to handle mac line endings
Fixes bug 15281. With testcase. --- dlls/kernel32/profile.c | 3 +- dlls/kernel32/tests/profile.c | 268 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 1 deletions(-)
diff --git a/dlls/kernel32/profile.c b/dlls/kernel32/profile.c index 22d4cb5..850f3cc 100644 --- a/dlls/kernel32/profile.c +++ b/dlls/kernel32/profile.c @@ -407,6 +407,7 @@ static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding) { szLineStart = next_line; next_line = memchrW(szLineStart, '\n', szEnd - szLineStart); + if (!next_line) next_line = memchrW(szLineStart, '\r', szEnd - szLineStart); if (!next_line) next_line = szEnd; else next_line++; szLineEnd = next_line; @@ -415,7 +416,7 @@ static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
/* get rid of white space */ while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++; - while ((szLineEnd > szLineStart) && ((szLineEnd[-1] == '\n') || PROFILE_isspaceW(szLineEnd[-1]))) szLineEnd--; + while ((szLineEnd > szLineStart) && ((szLineEnd[-1] == '\n') || (szLineEnd[-1] == '\r') || PROFILE_isspaceW(szLineEnd[-1]))) szLineEnd--;
if (szLineStart >= szLineEnd) continue;
diff --git a/dlls/kernel32/tests/profile.c b/dlls/kernel32/tests/profile.c index 8a3e83b..1cc963c 100644 --- a/dlls/kernel32/tests/profile.c +++ b/dlls/kernel32/tests/profile.c @@ -694,6 +694,273 @@ static void test_GetPrivateProfileString(void) DeleteFileA(filename); }
+/* as above, but cr-only terminated lines */ +static void test_cr(void) +{ + DWORD ret; + CHAR buf[MAX_PATH]; + CHAR def_val[MAX_PATH]; + CHAR path[MAX_PATH]; + CHAR windir[MAX_PATH]; + /* NT series crashes on r/o empty strings, so pass an r/w + empty string and check for modification */ + CHAR emptystr[MAX_PATH] = ""; + LPSTR tempfile; + + static const char filename[] = ".\winetest.ini"; + static const char content[]= + "[section1]\r" + "name1=val1\r" + "name2="val2"\r" + "name3\r" + "name4=a\r" + "[section2]\r"; + + create_test_file(filename, content, sizeof(content)); + + /* Run this test series with caching. Wine won't cache profile + files younger than 2.1 seconds. */ + Sleep(2500); + + /* lpAppName is NULL */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA(NULL, "name1", "default", + buf, MAX_PATH, filename); + ok(ret == 18, "Expected 18, got %d\n", ret); + ok(!memcmp(buf, "section1\0section2\0", ret + 1), + "Expected "section1\0section2\0", got "%s"\n", buf); + + /* lpAppName is empty */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA(emptystr, "name1", "default", + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "AppName modified\n"); + + /* lpAppName is missing */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("notasection", "name1", "default", + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + + /* lpAppName is empty, lpDefault is NULL */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA(emptystr, "name1", NULL, + buf, MAX_PATH, filename); + ok(ret == 0, "Expected 0, got %d\n", ret); + ok(!lstrcmpA(buf, ""), "Expected "", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "AppName modified\n"); + + /* lpAppName is empty, lpDefault is empty */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA(emptystr, "name1", "", + buf, MAX_PATH, filename); + ok(ret == 0, "Expected 0, got %d\n", ret); + ok(!lstrcmpA(buf, ""), "Expected "", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "AppName modified\n"); + + /* lpAppName is empty, lpDefault has trailing blank characters */ + lstrcpyA(buf, "kumquat"); + /* lpDefault must be writable (trailing blanks are removed inplace in win9x) */ + lstrcpyA(def_val, "default "); + ret = GetPrivateProfileStringA(emptystr, "name1", def_val, + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "AppName modified\n"); + + /* lpAppName is empty, many blank characters in lpDefault */ + lstrcpyA(buf, "kumquat"); + /* lpDefault must be writable (trailing blanks are removed inplace in win9x) */ + lstrcpyA(def_val, "one two "); + ret = GetPrivateProfileStringA(emptystr, "name1", def_val, + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "one two"), "Expected "one two", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "AppName modified\n"); + + /* lpAppName is empty, blank character but not trailing in lpDefault */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA(emptystr, "name1", "one two", + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "one two"), "Expected "one two", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "AppName modified\n"); + + /* lpKeyName is NULL */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", NULL, "default", + buf, MAX_PATH, filename); + ok(ret == 18, "Expected 18, got %d\n", ret); + ok(!memcmp(buf, "name1\0name2\0name4\0", ret + 1), + "Expected "name1\0name2\0name4\0", got "%s"\n", buf); + + /* lpKeyName is empty */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", emptystr, "default", + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "KeyName modified\n"); + + /* lpKeyName is missing */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "notakey", "default", + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + + /* lpKeyName is empty, lpDefault is NULL */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", emptystr, NULL, + buf, MAX_PATH, filename); + ok(ret == 0, "Expected 0, got %d\n", ret); + ok(!lstrcmpA(buf, ""), "Expected "", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "KeyName modified\n"); + + /* lpKeyName is empty, lpDefault is empty */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", emptystr, "", + buf, MAX_PATH, filename); + ok(ret == 0, "Expected 0, got %d\n", ret); + ok(!lstrcmpA(buf, ""), "Expected "", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "KeyName modified\n"); + + /* lpKeyName is empty, lpDefault has trailing blank characters */ + lstrcpyA(buf, "kumquat"); + /* lpDefault must be writable (trailing blanks are removed inplace in win9x) */ + lstrcpyA(def_val, "default "); + ret = GetPrivateProfileStringA("section1", emptystr, def_val, + buf, MAX_PATH, filename); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + ok(emptystr_ok(emptystr), "KeyName modified\n"); + + if (0) /* crashes */ + { + /* lpReturnedString is NULL */ + ret = GetPrivateProfileStringA("section1", "name1", "default", + NULL, MAX_PATH, filename); + } + + /* lpFileName is NULL */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, MAX_PATH, NULL); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + + /* lpFileName is empty */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, MAX_PATH, ""); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + + /* lpFileName is nonexistent */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, MAX_PATH, "nonexistent"); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + + /* nSize is 0 */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, 0, filename); + ok(ret == 0, "Expected 0, got %d\n", ret); + ok(!lstrcmpA(buf, "kumquat"), "Expected buf to be unchanged, got "%s"\n", buf); + + /* nSize is exact size of output */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, 4, filename); + ok(ret == 3, "Expected 3, got %d\n", ret); + ok(!lstrcmpA(buf, "val"), "Expected "val", got "%s"\n", buf); + + /* nSize has room for NULL terminator */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, 5, filename); + ok(ret == 4, "Expected 4, got %d\n", ret); + ok(!lstrcmpA(buf, "val1"), "Expected "val1", got "%s"\n", buf); + + /* output is 1 character */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name4", "default", + buf, MAX_PATH, filename); + ok(ret == 1, "Expected 1, got %d\n", ret); + ok(!lstrcmpA(buf, "a"), "Expected "a", got "%s"\n", buf); + + /* output is 1 character, no room for NULL terminator */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name4", "default", + buf, 1, filename); + ok(ret == 0, "Expected 0, got %d\n", ret); + ok(!lstrcmpA(buf, ""), "Expected "", got "%s"\n", buf); + + /* lpAppName is NULL, not enough room for final section name */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA(NULL, "name1", "default", + buf, 16, filename); + ok(ret == 14, "Expected 14, got %d\n", ret); + ok(!memcmp(buf, "section1\0secti\0", ret + 1), + "Expected "section1\0secti\0", got "%s"\n", buf); + + /* lpKeyName is NULL, not enough room for final key name */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", NULL, "default", + buf, 16, filename); + ok(ret == 14, "Expected 14, got %d\n", ret); + ok(!memcmp(buf, "name1\0name2\0na\0", ret + 1), + "Expected "name1\0name2\0na\0", got "%s"\n", buf); + + /* key value has quotation marks which are stripped */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name2", "default", + buf, MAX_PATH, filename); + ok(ret == 4, "Expected 4, got %d\n", ret); + ok(!lstrcmpA(buf, "val2"), "Expected "val2", got "%s"\n", buf); + + /* case does not match */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "NaMe1", "default", + buf, MAX_PATH, filename); + ok(ret == 4, "Expected 4, got %d\n", ret); + ok(!lstrcmpA(buf, "val1"), "Expected "val1", got "%s"\n", buf); + + /* only filename is used */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "NaMe1", "default", + buf, MAX_PATH, "winetest.ini"); + ok(ret == 7, "Expected 7, got %d\n", ret); + ok(!lstrcmpA(buf, "default"), "Expected "default", got "%s"\n", buf); + + GetWindowsDirectoryA(windir, MAX_PATH); + GetTempFileNameA(windir, "pre", 0, path); + tempfile = strrchr(path, '\') + 1; + create_test_file(path, content, sizeof(content)); + + /* only filename is used, file exists in windows directory */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "NaMe1", "default", + buf, MAX_PATH, tempfile); + ok(ret == 4, "Expected 4, got %d\n", ret); + ok(!lstrcmpA(buf, "val1"), "Expected "val1", got "%s"\n", buf); + + /* successful case */ + lstrcpyA(buf, "kumquat"); + ret = GetPrivateProfileStringA("section1", "name1", "default", + buf, MAX_PATH, filename); + ok(ret == 4, "Expected 4, got %d\n", ret); + ok(!lstrcmpA(buf, "val1"), "Expected "val1", got "%s"\n", buf); + + DeleteFileA(path); + DeleteFileA(filename); +} + START_TEST(profile) { test_profile_int(); @@ -704,4 +971,5 @@ START_TEST(profile) test_profile_delete_on_close(); test_profile_refresh(); test_GetPrivateProfileString(); + test_cr(); }