kernel32: Separate format string parsing in FormatMessageA into a new function.
[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  *      format_messageA    (internal)
409  */
410 static LPSTR format_messageA( DWORD dwFlags, LPCSTR fmtstr, struct format_args *format_args )
411 {
412     LPSTR target,t;
413     DWORD talloced;
414     LPCSTR f;
415     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
416     BOOL eos = FALSE;
417     CHAR ch;
418
419     target = t = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
420     talloced = 100;
421
422 #define ADD_TO_T(c) do { \
423         *t++=c;\
424         if ((DWORD)(t-target) == talloced) {\
425             target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
426             t = target+talloced;\
427             talloced*=2;\
428       }\
429 } while (0)
430
431     f = fmtstr;
432     if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
433         while (*f && !eos)
434             ADD_TO_T(*f++);
435     }
436     else {
437         while (*f && !eos) {
438             if (*f=='%') {
439                 int insertnr;
440                 char *str,*x;
441
442                 f++;
443                 if (!*f) {
444                     ADD_TO_T('%');
445                     continue;
446                 }
447                 switch (*f) {
448                 case '1':case '2':case '3':case '4':case '5':
449                 case '6':case '7':case '8':case '9':
450                     insertnr = *f-'0';
451                     switch (f[1]) {
452                     case '0':case '1':case '2':case '3':
453                     case '4':case '5':case '6':case '7':
454                     case '8':case '9':
455                         f++;
456                         insertnr = insertnr*10 + *f-'0';
457                         f++;
458                         break;
459                     default:
460                         f++;
461                         break;
462                     }
463                     f = format_insertA( insertnr, f, dwFlags, format_args, &str );
464                     for (x = str; *x; x++) ADD_TO_T(*x);
465                     HeapFree( GetProcessHeap(), 0, str );
466                     break;
467                 case 'n':
468                     ADD_TO_T('\r');
469                     ADD_TO_T('\n');
470                     f++;
471                     break;
472                 case '0':
473                     eos = TRUE;
474                     f++;
475                     break;
476                 default:
477                     ADD_TO_T(*f++);
478                     break;
479                 }
480             } else {
481                 ch = *f;
482                 f++;
483                 if (ch == '\r') {
484                     if (*f == '\n')
485                         f++;
486                     if(width)
487                         ADD_TO_T(' ');
488                     else
489                     {
490                         ADD_TO_T('\r');
491                         ADD_TO_T('\n');
492                     }
493                 } else {
494                     if (ch == '\n')
495                     {
496                         if(width)
497                             ADD_TO_T(' ');
498                         else
499                         {
500                             ADD_TO_T('\r');
501                             ADD_TO_T('\n');
502                         }
503                     }
504                     else
505                         ADD_TO_T(ch);
506                 }
507             }
508         }
509     }
510     *t = '\0';
511
512     return target;
513 }
514 #undef ADD_TO_T
515
516 /**********************************************************************
517  *      format_messageW    (internal)
518  */
519 static LPWSTR format_messageW( DWORD dwFlags, LPCWSTR fmtstr, struct format_args *format_args )
520 {
521     LPWSTR target,t;
522     DWORD talloced;
523     LPCWSTR f;
524     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
525     BOOL eos = FALSE;
526     WCHAR ch;
527
528     target = t = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
529     talloced = 100;
530
531 #define ADD_TO_T(c)  do {\
532     *t++=c;\
533     if ((DWORD)(t-target) == talloced) {\
534         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
535         t = target+talloced;\
536         talloced*=2;\
537     } \
538 } while (0)
539
540     f = fmtstr;
541     if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
542         while (*f && !eos)
543             ADD_TO_T(*f++);
544     }
545     else {
546         while (*f && !eos) {
547             if (*f=='%') {
548                 int insertnr;
549                 WCHAR *str,*x;
550
551                 f++;
552                 if (!*f) {
553                     ADD_TO_T('%');
554                     continue;
555                 }
556
557                 switch (*f) {
558                 case '1':case '2':case '3':case '4':case '5':
559                 case '6':case '7':case '8':case '9':
560                     insertnr = *f-'0';
561                     switch (f[1]) {
562                     case '0':case '1':case '2':case '3':
563                     case '4':case '5':case '6':case '7':
564                     case '8':case '9':
565                         f++;
566                         insertnr = insertnr*10 + *f-'0';
567                         f++;
568                         break;
569                     default:
570                         f++;
571                         break;
572                     }
573                     f = format_insertW( insertnr, f, dwFlags, format_args, &str );
574                     for (x = str; *x; x++) ADD_TO_T(*x);
575                     HeapFree( GetProcessHeap(), 0, str );
576                     break;
577                 case 'n':
578                     ADD_TO_T('\r');
579                     ADD_TO_T('\n');
580                     f++;
581                     break;
582                 case '0':
583                     eos = TRUE;
584                     f++;
585                     break;
586                 default:
587                     ADD_TO_T(*f++);
588                     break;
589                 }
590             } else {
591                 ch = *f;
592                 f++;
593                 if (ch == '\r') {
594                     if (*f == '\n')
595                         f++;
596                     if(width)
597                         ADD_TO_T(' ');
598                     else
599                     {
600                         ADD_TO_T('\r');
601                         ADD_TO_T('\n');
602                     }
603                 } else {
604                     if (ch == '\n')
605                     {
606                         if(width)
607                             ADD_TO_T(' ');
608                         else
609                         {
610                             ADD_TO_T('\r');
611                             ADD_TO_T('\n');
612                         }
613                     }
614                     else
615                         ADD_TO_T(ch);
616                 }
617             }
618         }
619     }
620     *t = '\0';
621
622     return target;
623 }
624 #undef ADD_TO_T
625
626 /***********************************************************************
627  *           FormatMessageA   (KERNEL32.@)
628  * FIXME: missing wrap,
629  */
630 DWORD WINAPI FormatMessageA(
631         DWORD   dwFlags,
632         LPCVOID lpSource,
633         DWORD   dwMessageId,
634         DWORD   dwLanguageId,
635         LPSTR   lpBuffer,
636         DWORD   nSize,
637         __ms_va_list* args )
638 {
639     struct format_args format_args;
640     DWORD ret = 0;
641     LPSTR       target;
642     DWORD       talloced;
643     LPSTR       from;
644     DWORD       width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
645
646     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
647           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
648     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
649         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
650            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
651
652     if ((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) && !lpBuffer)
653     {
654         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
655         return 0;
656     }
657
658     if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
659     {
660         format_args.args = (ULONG_PTR *)args;
661         format_args.list = NULL;
662         format_args.last = 0;
663     }
664     else
665     {
666         format_args.args = NULL;
667         format_args.list = args;
668         format_args.last = 0;
669     }
670
671     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
672         FIXME("line wrapping (%u) not supported.\n", width);
673     from = NULL;
674     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
675     {
676         from = HeapAlloc( GetProcessHeap(), 0, strlen(lpSource) + 1 );
677         strcpy( from, lpSource );
678     }
679     else {
680         from = NULL;
681         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
682             from = load_messageA( (HMODULE)lpSource, dwMessageId, dwLanguageId );
683         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
684             from = load_messageA( kernel32_handle, dwMessageId, dwLanguageId );
685         if (!from) return 0;
686     }
687
688     target = format_messageA( dwFlags, from, &format_args );
689
690     talloced = strlen(target)+1;
691     TRACE("-- %s\n",debugstr_a(target));
692     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
693         *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
694         memcpy(*(LPSTR*)lpBuffer,target,talloced);
695     } else {
696         if (nSize < talloced)
697         {
698             SetLastError(ERROR_INSUFFICIENT_BUFFER);
699             goto failure;
700         }
701         strcpy(lpBuffer, target);
702     }
703
704     ret = talloced - 1; /* null terminator */
705 failure:
706     HeapFree(GetProcessHeap(),0,target);
707     HeapFree(GetProcessHeap(),0,from);
708     if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
709     TRACE("-- returning %u\n", ret);
710     return ret;
711 }
712 #undef ADD_TO_T
713
714
715 /***********************************************************************
716  *           FormatMessageW   (KERNEL32.@)
717  */
718 DWORD WINAPI FormatMessageW(
719         DWORD   dwFlags,
720         LPCVOID lpSource,
721         DWORD   dwMessageId,
722         DWORD   dwLanguageId,
723         LPWSTR  lpBuffer,
724         DWORD   nSize,
725         __ms_va_list* args )
726 {
727     struct format_args format_args;
728     DWORD ret = 0;
729     LPWSTR target;
730     DWORD talloced;
731     LPWSTR from;
732     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
733
734     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
735           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
736     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
737         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
738            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
739
740     if (!lpBuffer)
741     {
742         SetLastError(ERROR_INVALID_PARAMETER);
743         return 0;
744     }
745
746     if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
747     {
748         format_args.args = (ULONG_PTR *)args;
749         format_args.list = NULL;
750         format_args.last = 0;
751     }
752     else
753     {
754         format_args.args = NULL;
755         format_args.list = args;
756         format_args.last = 0;
757     }
758
759     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
760         FIXME("line wrapping not supported.\n");
761     from = NULL;
762     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
763         from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
764             sizeof(WCHAR) );
765         strcpyW( from, lpSource );
766     }
767     else {
768         from = NULL;
769         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
770             from = load_messageW( (HMODULE)lpSource, dwMessageId, dwLanguageId );
771         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
772             from = load_messageW( kernel32_handle, dwMessageId, dwLanguageId );
773         if (!from) return 0;
774     }
775
776     target = format_messageW( dwFlags, from, &format_args );
777
778     talloced = strlenW(target)+1;
779     TRACE("-- %s\n",debugstr_w(target));
780     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
781         /* nSize is the MINIMUM size */
782         *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT, max(nSize, talloced)*sizeof(WCHAR));
783         strcpyW(*(LPWSTR*)lpBuffer, target);
784     }
785     else
786     {
787         if (nSize < talloced)
788         {
789             SetLastError(ERROR_INSUFFICIENT_BUFFER);
790             goto failure;
791         }
792         strcpyW(lpBuffer, target);
793     }
794
795     ret = talloced - 1; /* null terminator */
796 failure:
797     HeapFree(GetProcessHeap(),0,target);
798     HeapFree(GetProcessHeap(),0,from);
799     if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
800     TRACE("-- returning %u\n", ret);
801     return ret;
802 }
803 #undef ADD_TO_T