kernel32: fix PROFILE_Load to handle mac line endings

Erik Inge Bolsø knan-wine at anduin.net
Sun Sep 14 07:48:51 CDT 2008


Fixes bug 15281. With testcase.
---
 dlls/kernel32/profile.c       |    3 +-
 dlls/kernel32/tests/profile.c |  268 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 270 insertions(+), 1 deletions(-)

Testcase has been run on win2k by Nicolas Le Cam:
http://www.winehq.org/pipermail/wine-devel/2008-September/069107.html

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



More information about the wine-patches mailing list