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