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();
}
--
1.5.4.3