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