kernel32: Add line wrapping support to FormatMessage().

Francois Gouget fgouget at free.fr
Mon Sep 24 01:38:13 CDT 2012


---
 dlls/kernel32/format_msg.c       |   65 +++++++++++++++++++++-----
 dlls/kernel32/tests/format_msg.c |   96 +++++++++++++++++++-------------------
 2 files changed, 101 insertions(+), 60 deletions(-)

diff --git a/dlls/kernel32/format_msg.c b/dlls/kernel32/format_msg.c
index 101c2fa..7ce4f0a 100644
--- a/dlls/kernel32/format_msg.c
+++ b/dlls/kernel32/format_msg.c
@@ -266,14 +266,59 @@ struct _format_message_data
     LPWSTR formatted;
     DWORD size;
     LPWSTR t;
+    LPWSTR space;
+    BOOL inspace;
+    DWORD width, w;
 };
 
 static void format_add_char(struct _format_message_data *fmd, WCHAR c)
 {
     *fmd->t++ = c;
+    if (fmd->width && fmd->width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
+    {
+        switch (c) {
+        case '\r':
+        case '\n':
+            fmd->space = NULL;
+            fmd->inspace = FALSE;
+            fmd->w = 0;
+            break;
+        case ' ':
+            if (!fmd->inspace)
+                fmd->space = fmd->t - 1;
+            fmd->inspace = TRUE;
+            fmd->w++;
+            break;
+        default:
+            fmd->inspace = FALSE;
+            fmd->w++;
+        }
+        if (fmd->w == fmd->width) {
+            LPWSTR notspace;
+            if (fmd->space) {
+                notspace = fmd->space;
+                while (*notspace == ' ' && notspace != fmd->t)
+                    notspace++;
+            } else
+                notspace = fmd->space = fmd->t;
+            fmd->w = fmd->t - notspace;
+            memmove(fmd->space+2, notspace, fmd->w * sizeof(*fmd->t));
+            *fmd->space++ = '\r';
+            *fmd->space++ = '\n';
+            fmd->t = fmd->space + fmd->w;
+            fmd->space = NULL;
+            fmd->inspace = FALSE;
+        }
+    }
     if ((DWORD)(fmd->t - fmd->formatted) == fmd->size) {
-        fmd->formatted = HeapReAlloc(GetProcessHeap(), 0, fmd->formatted, (fmd->size * 2) * sizeof(WCHAR));
+        DWORD_PTR ispace = fmd->space - fmd->formatted;
+        /* Allocate two extra characters so we can insert a '\r\n' in
+         * the middle of a word.
+         */
+        fmd->formatted = HeapReAlloc(GetProcessHeap(), 0, fmd->formatted, (fmd->size * 2 + 2) * sizeof(WCHAR));
         fmd->t = fmd->formatted + fmd->size;
+        if (fmd->space)
+            fmd->space = fmd->formatted + ispace;
         fmd->size *= 2;
     }
 }
@@ -286,12 +331,15 @@ static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr
 {
     struct _format_message_data fmd;
     LPCWSTR f;
-    DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
     BOOL eos = FALSE;
 
     fmd.size = 100;
-    fmd.formatted = fmd.t = HeapAlloc( GetProcessHeap(), 0, fmd.size * sizeof(WCHAR) );
+    fmd.formatted = fmd.t = HeapAlloc( GetProcessHeap(), 0, (fmd.size + 2) * sizeof(WCHAR) );
 
+    fmd.width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
+    fmd.w = 0;
+    fmd.inspace = FALSE;
+    fmd.space = NULL;
     f = fmtstr;
     while (*f && !eos) {
         if (*f=='%') {
@@ -362,7 +410,7 @@ static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr
             if (ch == '\r') {
                 if (*f == '\n')
                     f++;
-                if(width)
+                if(fmd.width)
                     format_add_char(&fmd, ' ');
                 else
                 {
@@ -372,7 +420,7 @@ static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr
             } else {
                 if (ch == '\n')
                 {
-                    if(width)
+                    if(fmd.width)
                         format_add_char(&fmd, ' ');
                     else
                     {
@@ -392,7 +440,6 @@ static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr
 
 /***********************************************************************
  *           FormatMessageA   (KERNEL32.@)
- * FIXME: missing wrap,
  */
 DWORD WINAPI FormatMessageA(
 	DWORD	dwFlags,
@@ -408,7 +455,6 @@ DWORD WINAPI FormatMessageA(
     LPWSTR	target;
     DWORD	destlength;
     LPWSTR	from;
-    DWORD	width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
 
     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
@@ -437,8 +483,6 @@ DWORD WINAPI FormatMessageA(
         format_args.last = 0;
     }
 
-    if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
-        FIXME("line wrapping (%u) not supported.\n", width);
     from = NULL;
     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
     {
@@ -514,7 +558,6 @@ DWORD WINAPI FormatMessageW(
     LPWSTR target;
     DWORD talloced;
     LPWSTR from;
-    DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
 
     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
@@ -541,8 +584,6 @@ DWORD WINAPI FormatMessageW(
         format_args.last = 0;
     }
 
-    if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
-        FIXME("line wrapping not supported.\n");
     from = NULL;
     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
         from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
diff --git a/dlls/kernel32/tests/format_msg.c b/dlls/kernel32/tests/format_msg.c
index cf711f6..005e6c4 100644
--- a/dlls/kernel32/tests/format_msg.c
+++ b/dlls/kernel32/tests/format_msg.c
@@ -1012,42 +1012,42 @@ static void test_message_wrap(void)
     /* Wrap the last word */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
 
     /* Wrap the very last word */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 20,
                          "short long long line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret);
-    todo_wine ok(!strcmp("short long long\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret);
+    ok(!strcmp("short long long\r\nline", out),"failed out=[%s]\n",out);
 
     /* Strictly less than 10 characters per line! */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10,
                          "short long line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong line", out),"failed out=[%s]\n",out);
 
     /* Handling of duplicate spaces */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 16,
                          "short long    line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 16,
                          "short long    wordlongerthanaline", 0, 0, out, sizeof(out), NULL);
     ok(ret == 33, "Expected FormatMessageW to return 33, got %d\n", ret);
-    todo_wine ok(!strcmp("short long\r\nwordlongerthanal\r\nine", out),"failed out=[%s]\n",out);
+    ok(!strcmp("short long\r\nwordlongerthanal\r\nine", out),"failed out=[%s]\n",out);
 
     /* Breaking in the middle of spaces */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 12,
                          "short long    line", 0, 0, out, sizeof(out), NULL);
     ok(ret == 18, "Expected FormatMessageW to return 18, got %d\n", ret);
-    todo_wine ok(!strcmp("short long\r\n  line", out),"failed out=[%s]\n",out);
+    ok(!strcmp("short long\r\n  line", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 12,
                          "short long    wordlongerthanaline", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 35, "Expected FormatMessageW to return 35, got %d\n", ret);
-    todo_wine ok(!strcmp("short long\r\n\r\nwordlongerth\r\nanaline", out),"failed out=[%s]\n",out);
+    ok(ret == 35, "Expected FormatMessageW to return 35, got %d\n", ret);
+    ok(!strcmp("short long\r\n\r\nwordlongerth\r\nanaline", out),"failed out=[%s]\n",out);
 
     /* Handling of start-of-string spaces */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 15,
@@ -1058,55 +1058,55 @@ static void test_message_wrap(void)
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "   shortlong line", 0, 0, out, sizeof(out), NULL);
     ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
-    todo_wine ok(!strcmp("\r\nshortlong\r\nline", out),"failed out=[%s]\n",out);
+    ok(!strcmp("\r\nshortlong\r\nline", out),"failed out=[%s]\n",out);
 
     /* Handling of start-of-line spaces */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "l1%n   shortlong line", 0, 0, out, sizeof(out), NULL);
     ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret);
-    todo_wine ok(!strcmp("l1\r\n\r\nshortlong\r\nline", out),"failed out=[%s]\n",out);
+    ok(!strcmp("l1\r\n\r\nshortlong\r\nline", out),"failed out=[%s]\n",out);
 
     /* Pure space wrapping */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 5,
                          "                ", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 7, "Expected FormatMessageW to return 7, got %d\n", ret);
-    todo_wine ok(!strcmp("\r\n\r\n\r\n ", out),"failed out=[%s]\n",out);
+    ok(ret == 7, "Expected FormatMessageW to return 7, got %d\n", ret);
+    ok(!strcmp("\r\n\r\n\r\n ", out),"failed out=[%s]\n",out);
 
     /* Handling of trailing spaces */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 5,
                          "l1               ", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret);
-    todo_wine ok(!strcmp("l1\r\n\r\n\r\n  ", out),"failed out=[%s]\n",out);
+    ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret);
+    ok(!strcmp("l1\r\n\r\n\r\n  ", out),"failed out=[%s]\n",out);
 
     /* Word that just fills the line */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
                          "shortlon", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret);
-    todo_wine ok(!strcmp("shortlon\r\n", out),"failed out=[%s]\n",out);
+    ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret);
+    ok(!strcmp("shortlon\r\n", out),"failed out=[%s]\n",out);
 
     /* Word longer than the line */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
                          "shortlongline", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret);
-    todo_wine ok(!strcmp("shortlon\r\ngline", out),"failed out=[%s]\n",out);
+    ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret);
+    ok(!strcmp("shortlon\r\ngline", out),"failed out=[%s]\n",out);
 
     /* Wrap the line multiple times */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 7,
                          "short long line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out);
 
     /* '\n's in the source are ignored */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short\nlong line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
 
     /* Wrap even before a '%n' */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
                          "shortlon%n", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 12, "Expected FormatMessageW to return 12, got %d\n", ret);
-    todo_wine ok(!strcmp("shortlon\r\n\r\n", out),"failed out=[%s]\n",out);
+    ok(ret == 12, "Expected FormatMessageW to return 12, got %d\n", ret);
+    ok(!strcmp("shortlon\r\n\r\n", out),"failed out=[%s]\n",out);
 
     /* '%n's count as starting a new line and combine with line wrapping */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10,
@@ -1116,8 +1116,8 @@ static void test_message_wrap(void)
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
                          "short%nlong line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out);
 
     /* '%r's also count as starting a new line and all */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10,
@@ -1127,14 +1127,14 @@ static void test_message_wrap(void)
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
                          "short%rlong line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\rlong\r\nline", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\rlong\r\nline", out),"failed out=[%s]\n",out);
 
     /* IGNORE_INSERTS does not prevent line wrapping or disable '%n' */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS | 8,
                          "short%nlong line%1", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 19, "Expected FormatMessageW to return 19, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong\r\nline%1", out),"failed out=[%s]\n",out);
+    ok(ret == 19, "Expected FormatMessageW to return 19, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\r\nline%1", out),"failed out=[%s]\n",out);
 
     /* MAX_WIDTH_MASK is the same as specifying an infinite line width */
     strcpy(in, "first line%n");
@@ -1152,38 +1152,38 @@ static void test_message_wrap(void)
     /* Wrapping and non-space characters */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long\tline", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong\tline", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\tline", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long-line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong-line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong-line", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long_line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong_line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong_line", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long.line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong.line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong.line", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long,line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong,line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong,line", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long!line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong!line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong!line", out),"failed out=[%s]\n",out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
                          "short long?line", 0, 0, out, sizeof(out), NULL);
-    todo_wine ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
-    todo_wine ok(!strcmp("short\r\nlong?line", out),"failed out=[%s]\n",out);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong?line", out),"failed out=[%s]\n",out);
 }
 
 static void test_message_insufficient_buffer(void)
-- 
1.7.10.4



More information about the wine-patches mailing list