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