kernel32: Remove some unused variables (Clang).
[wine/multimedia.git] / dlls / kernel32 / format_msg.c
blob5741713d81a17a00589ca5d31fe60dc9274eb034
1 /*
2 * FormatMessage implementation
4 * Copyright 1996 Marcus Meissner
5 * Copyright 2009 Alexandre Julliard
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.
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.
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
22 #include "config.h"
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
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"
40 WINE_DEFAULT_DEBUG_CHANNEL(resource);
42 struct format_args
44 ULONG_PTR *args;
45 __ms_va_list *list;
46 int last;
49 /* Messages used by FormatMessage
51 * They can be specified either directly or using a message ID and
52 * loading them from the resource.
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
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')
68 * Yes, ANSI strings in win32 resources. Go figure.
71 /**********************************************************************
72 * load_message (internal)
74 static LPWSTR load_message( HMODULE module, UINT id, WORD lang )
76 const MESSAGE_RESOURCE_ENTRY *mre;
77 WCHAR *buffer;
78 NTSTATUS status;
80 TRACE("module = %p, id = %08x\n", module, id );
82 if (!module) module = GetModuleHandleW( NULL );
83 if ((status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre )) != STATUS_SUCCESS)
85 SetLastError( RtlNtStatusToDosError(status) );
86 return NULL;
89 if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
91 int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR);
92 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
93 memcpy( buffer, mre->Text, len );
95 else
97 int len = MultiByteToWideChar( CP_ACP, 0, (const char *)mre->Text, -1, NULL, 0 );
98 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
99 MultiByteToWideChar( CP_ACP, 0, (const char*)mre->Text, -1, buffer, len );
101 TRACE("returning %s\n", wine_dbgstr_w(buffer));
102 return buffer;
105 static LPWSTR search_message( DWORD flags, HMODULE module, UINT id, WORD lang )
107 LPWSTR from = NULL;
108 if (flags & FORMAT_MESSAGE_FROM_HMODULE)
109 from = load_message( module, id, lang );
110 if (!from && (flags & FORMAT_MESSAGE_FROM_SYSTEM))
112 /* Fold win32 hresult to its embedded error code. */
113 if (HRESULT_SEVERITY(id) == SEVERITY_ERROR &&
114 HRESULT_FACILITY(id) == FACILITY_WIN32)
116 id = HRESULT_CODE(id);
118 from = load_message( kernel32_handle, id, lang );
120 return from;
123 /**********************************************************************
124 * get_arg (internal)
126 static ULONG_PTR get_arg( int nr, DWORD flags, struct format_args *args )
128 if (nr == -1) nr = args->last + 1;
129 if (args->list)
131 if (!args->args) args->args = HeapAlloc( GetProcessHeap(), 0, 99 * sizeof(ULONG_PTR) );
132 while (nr > args->last)
133 args->args[args->last++] = va_arg( *args->list, ULONG_PTR );
135 if (nr > args->last) args->last = nr;
136 return args->args[nr - 1];
139 /**********************************************************************
140 * format_insert (internal)
142 static LPCWSTR format_insert( BOOL unicode_caller, int insert, LPCWSTR format,
143 DWORD flags, struct format_args *args,
144 LPWSTR *result )
146 static const WCHAR fmt_u[] = {'%','u',0};
147 WCHAR *wstring = NULL, *p, fmt[256];
148 ULONG_PTR arg;
149 int size;
151 if (*format != '!') /* simple string */
153 arg = get_arg( insert, flags, args );
154 if (unicode_caller || !arg)
156 static const WCHAR nullW[] = {'(','n','u','l','l',')',0};
157 const WCHAR *str = (const WCHAR *)arg;
159 if (!str) str = nullW;
160 *result = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) );
161 strcpyW( *result, str );
163 else
165 const char *str = (const char *)arg;
166 DWORD length = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
167 *result = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
168 MultiByteToWideChar( CP_ACP, 0, str, -1, *result, length );
170 return format;
173 format++;
174 p = fmt;
175 *p++ = '%';
177 while (*format == '0' ||
178 *format == '+' ||
179 *format == '-' ||
180 *format == ' ' ||
181 *format == '*' ||
182 *format == '#')
184 if (*format == '*')
186 p += sprintfW( p, fmt_u, get_arg( insert, flags, args ));
187 insert = -1;
188 format++;
190 else *p++ = *format++;
192 while (isdigitW(*format)) *p++ = *format++;
194 if (*format == '.')
196 *p++ = *format++;
197 if (*format == '*')
199 p += sprintfW( p, fmt_u, get_arg( insert, flags, args ));
200 insert = -1;
201 format++;
203 else
204 while (isdigitW(*format)) *p++ = *format++;
207 /* replicate MS bug: drop an argument when using va_list with width/precision */
208 if (insert == -1 && args->list) args->last--;
209 arg = get_arg( insert, flags, args );
211 /* check for ascii string format */
212 if ((format[0] == 'h' && format[1] == 's') ||
213 (format[0] == 'h' && format[1] == 'S') ||
214 (unicode_caller && format[0] == 'S') ||
215 (!unicode_caller && format[0] == 's'))
217 DWORD len = MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, NULL, 0 );
218 wstring = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
219 MultiByteToWideChar( CP_ACP, 0, (char *)arg, -1, wstring, len );
220 arg = (ULONG_PTR)wstring;
221 *p++ = 's';
223 /* check for ascii character format */
224 else if ((format[0] == 'h' && format[1] == 'c') ||
225 (format[0] == 'h' && format[1] == 'C') ||
226 (unicode_caller && format[0] == 'C') ||
227 (!unicode_caller && format[0] == 'c'))
229 char ch = arg;
230 wstring = HeapAlloc( GetProcessHeap(), 0, 2 * sizeof(WCHAR) );
231 MultiByteToWideChar( CP_ACP, 0, &ch, 1, wstring, 1 );
232 wstring[1] = 0;
233 arg = (ULONG_PTR)wstring;
234 *p++ = 's';
236 /* check for wide string format */
237 else if ((format[0] == 'l' && format[1] == 's') ||
238 (format[0] == 'l' && format[1] == 'S') ||
239 (format[0] == 'w' && format[1] == 's') ||
240 (!unicode_caller && format[0] == 'S'))
242 *p++ = 's';
244 /* check for wide character format */
245 else if ((format[0] == 'l' && format[1] == 'c') ||
246 (format[0] == 'l' && format[1] == 'C') ||
247 (format[0] == 'w' && format[1] == 'c') ||
248 (!unicode_caller && format[0] == 'C'))
250 *p++ = 'c';
252 /* FIXME: handle I64 etc. */
253 else while (*format && *format != '!') *p++ = *format++;
255 *p = 0;
256 size = 256;
257 for (;;)
259 WCHAR *ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) );
260 int needed = snprintfW( ret, size, fmt, arg );
261 if (needed == -1 || needed >= size)
263 HeapFree( GetProcessHeap(), 0, ret );
264 size = max( needed + 1, size * 2 );
266 else
268 *result = ret;
269 break;
273 while (*format && *format != '!') format++;
274 if (*format == '!') format++;
276 HeapFree( GetProcessHeap(), 0, wstring );
277 return format;
280 struct _format_message_data
282 LPWSTR formatted;
283 DWORD size;
284 LPWSTR t;
285 LPWSTR space;
286 BOOL inspace;
287 DWORD width, w;
290 static void format_add_char(struct _format_message_data *fmd, WCHAR c)
292 *fmd->t++ = c;
293 if (fmd->width && fmd->width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
295 switch (c) {
296 case '\r':
297 case '\n':
298 fmd->space = NULL;
299 fmd->inspace = FALSE;
300 fmd->w = 0;
301 break;
302 case ' ':
303 if (!fmd->inspace)
304 fmd->space = fmd->t - 1;
305 fmd->inspace = TRUE;
306 fmd->w++;
307 break;
308 default:
309 fmd->inspace = FALSE;
310 fmd->w++;
312 if (fmd->w == fmd->width) {
313 LPWSTR notspace;
314 if (fmd->space) {
315 notspace = fmd->space;
316 while (notspace != fmd->t && *notspace == ' ')
317 notspace++;
318 } else
319 notspace = fmd->space = fmd->t;
320 fmd->w = fmd->t - notspace;
321 memmove(fmd->space+2, notspace, fmd->w * sizeof(*fmd->t));
322 *fmd->space++ = '\r';
323 *fmd->space++ = '\n';
324 fmd->t = fmd->space + fmd->w;
325 fmd->space = NULL;
326 fmd->inspace = FALSE;
329 if ((DWORD)(fmd->t - fmd->formatted) == fmd->size) {
330 DWORD_PTR ispace = fmd->space - fmd->formatted;
331 /* Allocate two extra characters so we can insert a '\r\n' in
332 * the middle of a word.
334 fmd->formatted = HeapReAlloc(GetProcessHeap(), 0, fmd->formatted, (fmd->size * 2 + 2) * sizeof(WCHAR));
335 fmd->t = fmd->formatted + fmd->size;
336 if (fmd->space)
337 fmd->space = fmd->formatted + ispace;
338 fmd->size *= 2;
342 /**********************************************************************
343 * format_message (internal)
345 static LPWSTR format_message( BOOL unicode_caller, DWORD dwFlags, LPCWSTR fmtstr,
346 struct format_args *format_args )
348 struct _format_message_data fmd;
349 LPCWSTR f;
350 BOOL eos = FALSE;
352 fmd.size = 100;
353 fmd.formatted = fmd.t = HeapAlloc( GetProcessHeap(), 0, (fmd.size + 2) * sizeof(WCHAR) );
355 fmd.width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
356 fmd.w = 0;
357 fmd.inspace = FALSE;
358 fmd.space = NULL;
359 f = fmtstr;
360 while (*f && !eos) {
361 if (*f=='%') {
362 int insertnr;
363 WCHAR *str,*x;
365 f++;
366 switch (*f) {
367 case '1':case '2':case '3':case '4':case '5':
368 case '6':case '7':case '8':case '9':
369 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)
370 goto ignore_inserts;
371 else if (((dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) && !format_args->args) ||
372 (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) && !format_args->list))
374 SetLastError(ERROR_INVALID_PARAMETER);
375 HeapFree(GetProcessHeap(), 0, fmd.formatted);
376 return NULL;
378 insertnr = *f-'0';
379 switch (f[1]) {
380 case '0':case '1':case '2':case '3':
381 case '4':case '5':case '6':case '7':
382 case '8':case '9':
383 f++;
384 insertnr = insertnr*10 + *f-'0';
385 f++;
386 break;
387 default:
388 f++;
389 break;
391 f = format_insert( unicode_caller, insertnr, f, dwFlags, format_args, &str );
392 for (x = str; *x; x++) format_add_char(&fmd, *x);
393 HeapFree( GetProcessHeap(), 0, str );
394 break;
395 case 'n':
396 format_add_char(&fmd, '\r');
397 format_add_char(&fmd, '\n');
398 f++;
399 break;
400 case 'r':
401 format_add_char(&fmd, '\r');
402 f++;
403 break;
404 case 't':
405 format_add_char(&fmd, '\t');
406 f++;
407 break;
408 case '0':
409 eos = TRUE;
410 f++;
411 break;
412 case '\0':
413 SetLastError(ERROR_INVALID_PARAMETER);
414 HeapFree(GetProcessHeap(), 0, fmd.formatted);
415 return NULL;
416 ignore_inserts:
417 default:
418 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)
419 format_add_char(&fmd, '%');
420 format_add_char(&fmd, *f++);
421 break;
423 } else {
424 WCHAR ch = *f;
425 f++;
426 if (ch == '\r') {
427 if (*f == '\n')
428 f++;
429 if(fmd.width)
430 format_add_char(&fmd, ' ');
431 else
433 format_add_char(&fmd, '\r');
434 format_add_char(&fmd, '\n');
436 } else {
437 if (ch == '\n')
439 if(fmd.width)
440 format_add_char(&fmd, ' ');
441 else
443 format_add_char(&fmd, '\r');
444 format_add_char(&fmd, '\n');
447 else
448 format_add_char(&fmd, ch);
452 *fmd.t = '\0';
454 return fmd.formatted;
457 /***********************************************************************
458 * FormatMessageA (KERNEL32.@)
460 DWORD WINAPI FormatMessageA(
461 DWORD dwFlags,
462 LPCVOID lpSource,
463 DWORD dwMessageId,
464 DWORD dwLanguageId,
465 LPSTR lpBuffer,
466 DWORD nSize,
467 __ms_va_list* args )
469 struct format_args format_args;
470 DWORD ret = 0;
471 LPWSTR target;
472 DWORD destlength;
473 LPWSTR from;
475 TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
476 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
478 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
480 if (!lpBuffer)
482 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
483 return 0;
485 else
486 *(LPSTR *)lpBuffer = NULL;
489 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
491 format_args.args = (ULONG_PTR *)args;
492 format_args.list = NULL;
493 format_args.last = 0;
495 else
497 format_args.args = NULL;
498 format_args.list = args;
499 format_args.last = 0;
502 from = NULL;
503 if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
505 DWORD length = MultiByteToWideChar(CP_ACP, 0, lpSource, -1, NULL, 0);
506 from = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
507 MultiByteToWideChar(CP_ACP, 0, lpSource, -1, from, length);
509 else if (dwFlags & (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM))
511 from = search_message( dwFlags, (HMODULE)lpSource, dwMessageId, dwLanguageId );
512 if (!from) return 0;
514 else
516 SetLastError(ERROR_INVALID_PARAMETER);
517 return 0;
520 target = format_message( FALSE, dwFlags, from, &format_args );
521 if (!target)
522 goto failure;
524 TRACE("-- %s\n", debugstr_w(target));
526 /* Only try writing to an output buffer if there are processed characters
527 * in the temporary output buffer. */
528 if (*target)
530 destlength = WideCharToMultiByte(CP_ACP, 0, target, -1, NULL, 0, NULL, NULL);
531 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
533 LPSTR buf = LocalAlloc(LMEM_ZEROINIT, max(nSize, destlength));
534 WideCharToMultiByte(CP_ACP, 0, target, -1, buf, destlength, NULL, NULL);
535 *((LPSTR*)lpBuffer) = buf;
537 else
539 if (nSize < destlength)
541 SetLastError(ERROR_INSUFFICIENT_BUFFER);
542 goto failure;
544 WideCharToMultiByte(CP_ACP, 0, target, -1, lpBuffer, destlength, NULL, NULL);
546 ret = destlength - 1; /* null terminator */
549 failure:
550 HeapFree(GetProcessHeap(),0,target);
551 HeapFree(GetProcessHeap(),0,from);
552 if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
553 TRACE("-- returning %u\n", ret);
554 return ret;
557 /***********************************************************************
558 * FormatMessageW (KERNEL32.@)
560 DWORD WINAPI FormatMessageW(
561 DWORD dwFlags,
562 LPCVOID lpSource,
563 DWORD dwMessageId,
564 DWORD dwLanguageId,
565 LPWSTR lpBuffer,
566 DWORD nSize,
567 __ms_va_list* args )
569 struct format_args format_args;
570 DWORD ret = 0;
571 LPWSTR target;
572 DWORD talloced;
573 LPWSTR from;
575 TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
576 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
578 if (!lpBuffer)
580 SetLastError(ERROR_INVALID_PARAMETER);
581 return 0;
584 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
585 *(LPWSTR *)lpBuffer = NULL;
587 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
589 format_args.args = (ULONG_PTR *)args;
590 format_args.list = NULL;
591 format_args.last = 0;
593 else
595 format_args.args = NULL;
596 format_args.list = args;
597 format_args.last = 0;
600 from = NULL;
601 if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
602 from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
603 sizeof(WCHAR) );
604 strcpyW( from, lpSource );
606 else if (dwFlags & (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM))
608 from = search_message( dwFlags, (HMODULE)lpSource, dwMessageId, dwLanguageId );
609 if (!from) return 0;
611 else
613 SetLastError(ERROR_INVALID_PARAMETER);
614 return 0;
617 target = format_message( TRUE, dwFlags, from, &format_args );
618 if (!target)
619 goto failure;
621 talloced = strlenW(target)+1;
622 TRACE("-- %s\n",debugstr_w(target));
624 /* Only allocate a buffer if there are processed characters in the
625 * temporary output buffer. If a caller supplies the buffer, then
626 * a null terminator will be written to it. */
627 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
629 if (*target)
631 /* nSize is the MINIMUM size */
632 *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT, max(nSize, talloced)*sizeof(WCHAR));
633 strcpyW(*(LPWSTR*)lpBuffer, target);
636 else
638 if (nSize < talloced)
640 SetLastError(ERROR_INSUFFICIENT_BUFFER);
641 goto failure;
643 strcpyW(lpBuffer, target);
646 ret = talloced - 1; /* null terminator */
647 failure:
648 HeapFree(GetProcessHeap(),0,target);
649 HeapFree(GetProcessHeap(),0,from);
650 if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args );
651 TRACE("-- returning %u\n", ret);
652 return ret;