kernel32/tests: Fix some of the heap tests for 64-bit.
[wine] / dlls / kernel32 / format_msg.c
1 /*
2  * FormatMessage implementation
3  *
4  * Copyright 1996 Marcus Meissner
5  * Copyright 2009 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winerror.h"
33 #include "winternl.h"
34 #include "winuser.h"
35 #include "winnls.h"
36 #include "wine/unicode.h"
37 #include "kernel_private.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(resource);
41
42 struct format_args
43 {
44     ULONG_PTR    *args;
45     __ms_va_list *list;
46     int           last;
47 };
48
49 /* Messages used by FormatMessage
50  *
51  * They can be specified either directly or using a message ID and
52  * loading them from the resource.
53  *
54  * The resourcedata has following format:
55  * start:
56  * 0: DWORD nrofentries
57  * nrofentries * subentry:
58  *      0: DWORD firstentry
59  *      4: DWORD lastentry
60  *      8: DWORD offset from start to the stringentries
61  *
62  * (lastentry-firstentry) * stringentry:
63  * 0: WORD len (0 marks end)    [ includes the 4 byte header length ]
64  * 2: WORD flags
65  * 4: CHAR[len-4]
66  *      (stringentry i of a subentry refers to the ID 'firstentry+i')
67  *
68  * Yes, ANSI strings in win32 resources. Go figure.
69  */
70
71 static const WCHAR PCNTFMTWSTR[] = { '%','%','%','s',0 };
72 static const WCHAR FMTWSTR[] = { '%','s',0 };
73
74 /**********************************************************************
75  *      load_messageW           (internal)
76  */
77 static LPWSTR load_messageW( HMODULE module, UINT id, WORD lang )
78 {
79     const MESSAGE_RESOURCE_ENTRY *mre;
80     WCHAR *buffer;
81     NTSTATUS status;
82
83     TRACE("module = %p, id = %08x\n", module, id );
84
85     if (!module) module = GetModuleHandleW( NULL );
86     if ((status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre )) != STATUS_SUCCESS)
87     {
88         SetLastError( RtlNtStatusToDosError(status) );
89         return NULL;
90     }
91
92     if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
93     {
94         int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR);
95         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
96         memcpy( buffer, mre->Text, len );
97     }
98     else
99     {
100         int len = MultiByteToWideChar( CP_ACP, 0, (const char *)mre->Text, -1, NULL, 0 );
101         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
102         MultiByteToWideChar( CP_ACP, 0, (const char*)mre->Text, -1, buffer, len );
103     }
104     TRACE("returning %s\n", wine_dbgstr_w(buffer));
105     return buffer;
106 }
107
108
109 /**********************************************************************
110  *      load_messageA           (internal)
111  */
112 static LPSTR load_messageA( HMODULE module, UINT id, WORD lang )
113 {
114     const MESSAGE_RESOURCE_ENTRY *mre;
115     char *buffer;
116     NTSTATUS status;
117
118     TRACE("module = %p, id = %08x\n", module, id );
119
120     if (!module) module = GetModuleHandleW( NULL );
121     if ((status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre )) != STATUS_SUCCESS)
122     {
123         SetLastError( RtlNtStatusToDosError(status) );
124         return NULL;
125     }
126
127     if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
128     {
129         int len = WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, NULL, 0, NULL, NULL );
130         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
131         WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, buffer, len, NULL, NULL );
132     }
133     else
134     {
135         int len = strlen((const char*)mre->Text) + 1;
136         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
137         memcpy( buffer, mre->Text, len );
138     }
139     TRACE("returning %s\n", wine_dbgstr_a(buffer));
140     return buffer;
141 }
142
143
144 /**********************************************************************
145  *      get_arg    (internal)
146  */
147 static ULONG_PTR get_arg( int nr, DWORD flags, struct format_args *args )
148 {
149     if (nr == -1) nr = args->last + 1;
150     if (args->list)
151     {
152         if (!args->args) args->args = HeapAlloc( GetProcessHeap(), 0, 99 * sizeof(ULONG_PTR) );
153         while (nr > args->last)
154             args->args[args->last++] = va_arg( *args->list, ULONG_PTR );
155     }
156     if (nr > args->last) args->last = nr;
157     return args->args[nr - 1];
158 }
159
160
161 /**********************************************************************
162  *      format_insertA    (internal)
163  */
164 static LPCSTR format_insertA( int insert, LPCSTR format, DWORD flags,
165                               struct format_args *args, LPSTR *result )
166 {
167     char *astring = NULL, *p, fmt[256];
168     ULONG_PTR arg;
169     int size;
170
171     if (*format != '!')  /* simple string */
172     {
173         char *str = (char *)get_arg( insert, flags, args );
174         *result = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 );
175         strcpy( *result, str );
176         return format;
177     }
178
179     format++;
180     p = fmt;
181     *p++ = '%';
182
183     while (*format == '0' ||
184            *format == '+' ||
185            *format == '-' ||
186            *format == ' ' ||
187            *format == '*' ||
188            *format == '#')
189     {
190         if (*format == '*')
191         {
192             p += sprintf( p, "%lu", get_arg( insert, flags, args ));
193             insert = -1;
194             format++;
195         }
196         else *p++ = *format++;
197     }
198     while (isdigit(*format)) *p++ = *format++;
199
200     if (*format == '.')
201     {
202         *p++ = *format++;
203         if (*format == '*')
204         {
205             p += sprintf( p, "%lu", get_arg( insert, flags, args ));
206             insert = -1;
207             format++;
208         }
209         else
210             while (isdigit(*format)) *p++ = *format++;
211     }
212
213     /* replicate MS bug: drop an argument when using va_list with width/precision */
214     if (insert == -1 && args->list) args->last--;
215     arg = get_arg( insert, flags, args );
216
217     /* check for wide string format */
218     if ((format[0] == 'l' && format[1] == 's') ||
219         (format[0] == 'l' && format[1] == 'S') ||
220         (format[0] == 'w' && format[1] == 's') ||
221         (format[0] == 'S'))
222     {
223         DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)arg, -1, /*FIXME*/
224                                          NULL, 0, NULL, NULL );
225         astring = HeapAlloc( GetProcessHeap(), 0, len );
226         WideCharToMultiByte( CP_ACP, 0, (WCHAR *)arg, -1, astring, len, NULL, NULL );
227         arg = (ULONG_PTR)astring;
228         *p++ = 's';
229     }
230     /* check for wide character format */
231     else if ((format[0] == 'l' && format[1] == 'c') ||
232              (format[0] == 'l' && format[1] == 'C') ||
233              (format[0] == 'w' && format[1] == 'c') ||
234              (format[0] == 'C'))
235     {
236         WCHAR ch = arg;
237         DWORD len = WideCharToMultiByte( CP_ACP, 0, &ch, 1, NULL, 0, NULL, NULL );
238         astring = HeapAlloc( GetProcessHeap(), 0, len + 1 );
239         WideCharToMultiByte( CP_ACP, 0, &ch, 1, astring, len, NULL, NULL );
240         astring[len] = 0;
241         arg = (ULONG_PTR)astring;
242         *p++ = 's';
243     }
244     /* check for ascii string format */
245     else if ((format[0] == 'h' && format[1] == 's') ||
246              (format[0] == 'h' && format[1] == 'S'))
247     {
248         *p++ = 's';
249     }
250     /* check for ascii character format */
251     else if ((format[0] == 'h' && format[1] == 'c') ||
252              (format[0] == 'h' && format[1] == 'C'))
253     {
254         *p++ = 'c';
255     }
256     /* FIXME: handle I64 etc. */
257     else while (*format && *format != '!') *p++ = *format++;
258
259     *p = 0;
260     size = 256;
261     for (;;)
262     {
263         char *ret = HeapAlloc( GetProcessHeap(), 0, size );
264         int needed = snprintf( ret, size, fmt, arg );
265         if (needed == -1 || needed >= size)
266         {
267             HeapFree( GetProcessHeap(), 0, ret );
268             size = max( needed + 1, size * 2 );
269         }
270         else
271         {
272             *result = ret;
273             break;
274         }
275     }
276
277     while (*format && *format != '!') format++;
278     if (*format == '!') format++;
279
280     HeapFree( GetProcessHeap(), 0, astring );
281     return format;
282 }
283
284
285 /**********************************************************************
286  *      format_insertW    (internal)
287  */
288 static LPCWSTR format_insertW( int insert, LPCWSTR format, DWORD flags,
289                                struct format_args *args, LPWSTR *result )
290 {
291     static const WCHAR fmt_lu[] = {'%','l','u',0};
292     WCHAR *wstring = NULL, *p, fmt[256];
293     ULONG_PTR arg;
294     int size;
295
296     if (*format != '!')  /* simple string */
297     {
298         WCHAR *str = (WCHAR *)get_arg( insert, flags, args );
299         *result = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) );
300         strcpyW( *result, str );
301         return format;
302     }
303
304     format++;
305     p = fmt;
306     *p++ = '%';
307
308     while (*format == '0' ||
309            *format == '+' ||
310            *format == '-' ||
311            *format == ' ' ||
312            *format == '*' ||
313            *format == '#')
314     {
315         if (*format == '*')
316         {
317             p += sprintfW( p, fmt_lu, get_arg( insert, flags, args ));
318             insert = -1;
319             format++;
320         }
321         else *p++ = *format++;
322     }
323     while (isdigitW(*format)) *p++ = *format++;
324
325     if (*format == '.')
326     {
327         *p++ = *format++;
328         if (*format == '*')
329         {
330             p += sprintfW( p, fmt_lu, get_arg( insert, flags, args ));
331             insert = -1;
332             format++;
333         }
334         else
335             while (isdigitW(*format)) *p++ = *format++;
336     }
337
338     /* replicate MS bug: drop an argument when using va_list with width/precision */
339     if (insert == -1 && args->list) args->last--;
340     arg = get_arg( insert, flags, args );
341
342     /* check for ascii string format */
343     if ((format[0] == 'h' && format[1] == 's') ||
344         (format[0] == 'h' && format[1] == 'S') ||
345         (format[0] == 'S'))
346     {
347         DWORD len = MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, /*FIXME*/ NULL, 0 );
348         wstring = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
349         MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, wstring, len );
350         arg = (ULONG_PTR)wstring;
351         *p++ = 's';
352     }
353     /* check for ascii character format */
354     else if ((format[0] == 'h' && format[1] == 'c') ||
355              (format[0] == 'h' && format[1] == 'C') ||
356              (format[0] == 'C'))
357     {
358         char ch = arg;
359         wstring = HeapAlloc( GetProcessHeap(), 0, 2 * sizeof(WCHAR) );
360         MultiByteToWideChar( CP_ACP, 0, &ch, 1, wstring, 1 );
361         wstring[1] = 0;
362         arg = (ULONG_PTR)wstring;
363         *p++ = 's';
364     }
365     /* check for wide string format */
366     else if ((format[0] == 'l' && format[1] == 's') ||
367              (format[0] == 'l' && format[1] == 'S') ||
368              (format[0] == 'w' && format[1] == 's'))
369     {
370         *p++ = 's';
371     }
372     /* check for wide character format */
373     else if ((format[0] == 'l' && format[1] == 'c') ||
374              (format[0] == 'l' && format[1] == 'C') ||
375              (format[0] == 'w' && format[1] == 'c'))
376     {
377         *p++ = 'c';
378     }
379     /* FIXME: handle I64 etc. */
380     else while (*format && *format != '!') *p++ = *format++;
381
382     *p = 0;
383     size = 256;
384     for (;;)
385     {
386         WCHAR *ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) );
387         int needed = snprintfW( ret, size, fmt, arg );
388         if (needed == -1 || needed >= size)
389         {
390             HeapFree( GetProcessHeap(), 0, ret );
391             size = max( needed + 1, size * 2 );
392         }
393         else
394         {
395             *result = ret;
396             break;
397         }
398     }
399
400     while (*format && *format != '!') format++;
401     if (*format == '!') format++;
402
403     HeapFree( GetProcessHeap(), 0, wstring );
404     return format;
405 }
406
407
408 /***********************************************************************
409  *           FormatMessageA   (KERNEL32.@)
410  * FIXME: missing wrap,
411  */
412 DWORD WINAPI FormatMessageA(
413         DWORD   dwFlags,
414         LPCVOID lpSource,
415         DWORD   dwMessageId,
416         DWORD   dwLanguageId,
417         LPSTR   lpBuffer,
418         DWORD   nSize,
419         __ms_va_list* args )
420 {
421     struct format_args format_args;
422     DWORD ret = 0;
423     LPSTR       target,t;
424     DWORD       talloced;
425     LPSTR       from;
426     LPCSTR f;
427     DWORD       width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
428     BOOL    eos = FALSE;
429     CHAR        ch;
430
431     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
432           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
433     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
434         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
435            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
436
437     if (!lpBuffer)
438     {
439         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
440         return 0;
441     }
442
443     if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
444     {
445         format_args.args = (ULONG_PTR *)args;
446         format_args.list = NULL;
447         format_args.last = 0;
448     }
449     else
450     {
451         format_args.args = NULL;
452         format_args.list = args;
453         format_args.last = 0;
454     }
455
456     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
457         FIXME("line wrapping (%u) not supported.\n", width);
458     from = NULL;
459     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
460     {
461         from = HeapAlloc( GetProcessHeap(), 0, strlen(lpSource) + 1 );
462         strcpy( from, lpSource );
463     }
464     else {
465         from = NULL;
466         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
467             from = load_messageA( (HMODULE)lpSource, dwMessageId, dwLanguageId );
468         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
469             from = load_messageA( kernel32_handle, dwMessageId, dwLanguageId );
470         if (!from) return 0;
471     }
472     target      = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
473     t   = target;
474     talloced= 100;
475
476 #define ADD_TO_T(c) do { \
477         *t++=c;\
478         if ((DWORD)(t-target) == talloced) {\
479             target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
480             t = target+talloced;\
481             talloced*=2;\
482       }\
483 } while (0)
484
485     if (from) {
486         f=from;
487         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
488             while (*f && !eos)
489                 ADD_TO_T(*f++);
490         }
491         else {
492             while (*f && !eos) {
493                 if (*f=='%') {
494                     int insertnr;
495                     char *str,*x;
496
497                     f++;
498                     if (!*f) {
499                         ADD_TO_T('%');
500                         continue;
501                     }
502                     switch (*f) {
503                     case '1':case '2':case '3':case '4':case '5':
504                     case '6':case '7':case '8':case '9':
505                         insertnr=*f-'0';
506                         switch (f[1]) {
507                         case '0':case '1':case '2':case '3':
508                         case '4':case '5':case '6':case '7':
509                         case '8':case '9':
510                             f++;
511                             insertnr=insertnr*10+*f-'0';
512                             f++;
513                             break;
514                         default:
515                             f++;
516                             break;
517                         }
518                         f = format_insertA( insertnr, f, dwFlags, &format_args, &str );
519                         for (x = str; *x; x++) ADD_TO_T(*x);
520                         HeapFree( GetProcessHeap(), 0, str );
521                         break;
522                     case 'n':
523                         ADD_TO_T('\r');
524                         ADD_TO_T('\n');
525                         f++;
526                         break;
527                     case '0':
528                         eos = TRUE;
529                         f++;
530                         break;
531                     default:
532                         ADD_TO_T(*f++);
533                         break;
534                     }
535                 } else {
536                     ch = *f;
537                     f++;
538                     if (ch == '\r') {
539                         if (*f == '\n')
540                             f++;
541                         if(width)
542                             ADD_TO_T(' ');
543                         else
544                         {
545                             ADD_TO_T('\r');
546                             ADD_TO_T('\n');
547                         }
548                     } else {
549                         if (ch == '\n')
550                         {
551                             if(width)
552                                 ADD_TO_T(' ');
553                             else
554                             {
555                                 ADD_TO_T('\r');
556                                 ADD_TO_T('\n');
557                             }
558                         }
559                         else
560                             ADD_TO_T(ch);
561                     }
562                 }
563             }
564         }
565         *t='\0';
566     }
567     talloced = strlen(target)+1;
568     if (nSize && talloced<nSize) {
569         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
570     }
571     TRACE("-- %s\n",debugstr_a(target));
572     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
573         *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
574         memcpy(*(LPSTR*)lpBuffer,target,talloced);
575     } else {
576         lstrcpynA(lpBuffer,target,nSize);
577     }
578     HeapFree(GetProcessHeap(),0,target);
579     HeapFree(GetProcessHeap(),0,from);
580     if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
581     ret = (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer) : strlen(lpBuffer);
582     TRACE("-- returning %d\n", ret);
583     return ret;
584 }
585 #undef ADD_TO_T
586
587
588 /***********************************************************************
589  *           FormatMessageW   (KERNEL32.@)
590  */
591 DWORD WINAPI FormatMessageW(
592         DWORD   dwFlags,
593         LPCVOID lpSource,
594         DWORD   dwMessageId,
595         DWORD   dwLanguageId,
596         LPWSTR  lpBuffer,
597         DWORD   nSize,
598         __ms_va_list* args )
599 {
600     struct format_args format_args;
601     LPWSTR target,t;
602     DWORD talloced;
603     LPWSTR from;
604     LPCWSTR f;
605     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
606     BOOL eos = FALSE;
607     WCHAR ch;
608
609     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
610           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
611     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
612         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
613            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
614
615     if (!lpBuffer)
616     {
617         SetLastError(ERROR_INVALID_PARAMETER);
618         return 0;
619     }
620
621     if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
622     {
623         format_args.args = (ULONG_PTR *)args;
624         format_args.list = NULL;
625         format_args.last = 0;
626     }
627     else
628     {
629         format_args.args = NULL;
630         format_args.list = args;
631         format_args.last = 0;
632     }
633
634     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
635         FIXME("line wrapping not supported.\n");
636     from = NULL;
637     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
638         from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
639             sizeof(WCHAR) );
640         strcpyW( from, lpSource );
641     }
642     else {
643         from = NULL;
644         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
645             from = load_messageW( (HMODULE)lpSource, dwMessageId, dwLanguageId );
646         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
647             from = load_messageW( kernel32_handle, dwMessageId, dwLanguageId );
648         if (!from) return 0;
649     }
650     target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
651     t = target;
652     talloced= 100;
653
654 #define ADD_TO_T(c)  do {\
655     *t++=c;\
656     if ((DWORD)(t-target) == talloced) {\
657         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
658         t = target+talloced;\
659         talloced*=2;\
660     } \
661 } while (0)
662
663     if (from) {
664         f=from;
665         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
666             while (*f && !eos)
667                 ADD_TO_T(*f++);
668         }
669         else {
670             while (*f && !eos) {
671                 if (*f=='%') {
672                     int insertnr;
673                     WCHAR *str,*x;
674
675                     f++;
676                     if (!*f) {
677                         ADD_TO_T('%');
678                         continue;
679                     }
680
681                     switch (*f) {
682                     case '1':case '2':case '3':case '4':case '5':
683                     case '6':case '7':case '8':case '9':
684                         insertnr=*f-'0';
685                         switch (f[1]) {
686                         case '0':case '1':case '2':case '3':
687                         case '4':case '5':case '6':case '7':
688                         case '8':case '9':
689                             f++;
690                             insertnr=insertnr*10+*f-'0';
691                             f++;
692                             break;
693                         default:
694                             f++;
695                             break;
696                         }
697                         f = format_insertW( insertnr, f, dwFlags, &format_args, &str );
698                         for (x = str; *x; x++) ADD_TO_T(*x);
699                         HeapFree( GetProcessHeap(), 0, str );
700                         break;
701                     case 'n':
702                         ADD_TO_T('\r');
703                         ADD_TO_T('\n');
704                         f++;
705                         break;
706                     case '0':
707                         eos = TRUE;
708                         f++;
709                         break;
710                     default:
711                         ADD_TO_T(*f++);
712                         break;
713                     }
714                 } else {
715                     ch = *f;
716                     f++;
717                     if (ch == '\r') {
718                         if (*f == '\n')
719                             f++;
720                         if(width)
721                             ADD_TO_T(' ');
722                         else
723                         {
724                             ADD_TO_T('\r');
725                             ADD_TO_T('\n');
726                         }
727                     } else {
728                         if (ch == '\n')
729                         {
730                             if(width)
731                                 ADD_TO_T(' ');
732                             else
733                             {
734                                 ADD_TO_T('\r');
735                                 ADD_TO_T('\n');
736                             }
737                         }
738                         else
739                             ADD_TO_T(ch);
740                     }
741                 }
742             }
743         }
744         *t='\0';
745     }
746     talloced = strlenW(target)+1;
747     if (nSize && talloced<nSize)
748         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize*sizeof(WCHAR));
749     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
750         /* nSize is the MINIMUM size */
751         DWORD len = strlenW(target) + 1;
752         *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR));
753         strcpyW(*(LPWSTR*)lpBuffer, target);
754     }
755     else lstrcpynW(lpBuffer, target, nSize);
756
757     HeapFree(GetProcessHeap(),0,target);
758     HeapFree(GetProcessHeap(),0,from);
759     if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
760     TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
761         *(LPWSTR*)lpBuffer : lpBuffer));
762     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
763         strlenW(*(LPWSTR*)lpBuffer):
764             strlenW(lpBuffer);
765 }
766 #undef ADD_TO_T