user32: Add a test for DefDlgProc(WM_SETFOCUS), make the test pass under Wine.
[wine] / dlls / kernel32 / format_msg.c
1 /*
2  * FormatMessage implementation
3  *
4  * Copyright 1996 Marcus Meissner
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winerror.h"
32 #include "winternl.h"
33 #include "winuser.h"
34 #include "winnls.h"
35 #include "wine/unicode.h"
36 #include "kernel_private.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(resource);
40
41
42 /* Messages...used by FormatMessage (KERNEL32.something)
43  *
44  * They can be specified either directly or using a message ID and
45  * loading them from the resource.
46  *
47  * The resourcedata has following format:
48  * start:
49  * 0: DWORD nrofentries
50  * nrofentries * subentry:
51  *      0: DWORD firstentry
52  *      4: DWORD lastentry
53  *      8: DWORD offset from start to the stringentries
54  *
55  * (lastentry-firstentry) * stringentry:
56  * 0: WORD len (0 marks end)    [ includes the 4 byte header length ]
57  * 2: WORD flags
58  * 4: CHAR[len-4]
59  *      (stringentry i of a subentry refers to the ID 'firstentry+i')
60  *
61  * Yes, ANSI strings in win32 resources. Go figure.
62  */
63
64 static const WCHAR PCNTFMTWSTR[] = { '%','%','%','s',0 };
65 static const WCHAR FMTWSTR[] = { '%','s',0 };
66
67 /**********************************************************************
68  *      load_messageW           (internal)
69  */
70 static LPWSTR load_messageW( HMODULE module, UINT id, WORD lang )
71 {
72     const MESSAGE_RESOURCE_ENTRY *mre;
73     WCHAR *buffer;
74
75     TRACE("module = %p, id = %08x\n", module, id );
76
77     if (!module) module = GetModuleHandleW( NULL );
78     if (RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre ) != STATUS_SUCCESS) return NULL;
79
80     if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
81     {
82         int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR);
83         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
84         memcpy( buffer, mre->Text, len );
85     }
86     else
87     {
88         int len = MultiByteToWideChar( CP_ACP, 0, (const char *)mre->Text, -1, NULL, 0 );
89         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
90         MultiByteToWideChar( CP_ACP, 0, (const char*)mre->Text, -1, buffer, len );
91     }
92     TRACE("returning %s\n", wine_dbgstr_w(buffer));
93     return buffer;
94 }
95
96
97 /**********************************************************************
98  *      load_messageA           (internal)
99  */
100 static LPSTR load_messageA( HMODULE module, UINT id, WORD lang )
101 {
102     const MESSAGE_RESOURCE_ENTRY *mre;
103     char *buffer;
104
105     TRACE("module = %p, id = %08x\n", module, id );
106
107     if (!module) module = GetModuleHandleW( NULL );
108     if (RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &mre ) != STATUS_SUCCESS) return NULL;
109
110     if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
111     {
112         int len = WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, NULL, 0, NULL, NULL );
113         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
114         WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, buffer, len, NULL, NULL );
115     }
116     else
117     {
118         int len = strlen((const char*)mre->Text) + 1;
119         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
120         memcpy( buffer, mre->Text, len );
121     }
122     TRACE("returning %s\n", wine_dbgstr_a(buffer));
123     return buffer;
124 }
125
126
127 /***********************************************************************
128  *           FormatMessageA   (KERNEL32.@)
129  * FIXME: missing wrap,
130  */
131 DWORD WINAPI FormatMessageA(
132         DWORD   dwFlags,
133         LPCVOID lpSource,
134         DWORD   dwMessageId,
135         DWORD   dwLanguageId,
136         LPSTR   lpBuffer,
137         DWORD   nSize,
138         va_list* _args )
139 {
140     LPDWORD args=(LPDWORD)_args;
141     DWORD ret = 0;
142 #if defined(__i386__) || defined(__sparc__)
143 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
144     LPSTR       target,t;
145     DWORD       talloced;
146     LPSTR       from,f;
147     DWORD       width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
148     BOOL    eos = FALSE;
149     CHAR        ch;
150
151     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
152           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
153     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
154         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
155            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
156
157     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
158         FIXME("line wrapping (%u) not supported.\n", width);
159     from = NULL;
160     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
161     {
162         from = HeapAlloc( GetProcessHeap(), 0, strlen((LPCSTR)lpSource)+1 );
163         strcpy( from, (LPCSTR)lpSource );
164     }
165     else {
166         from = NULL;
167         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
168             from = load_messageA( (HMODULE)lpSource, dwMessageId, dwLanguageId );
169         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
170             from = load_messageA( kernel32_handle, dwMessageId, dwLanguageId );
171
172         if (!from)
173         {
174             SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
175             return 0;
176         }
177     }
178     target      = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
179     t   = target;
180     talloced= 100;
181
182 #define ADD_TO_T(c) do { \
183         *t++=c;\
184         if ((DWORD)(t-target) == talloced) {\
185             target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
186             t = target+talloced;\
187             talloced*=2;\
188       }\
189 } while (0)
190
191     if (from) {
192         f=from;
193         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
194             while (*f && !eos)
195                 ADD_TO_T(*f++);
196         }
197         else {
198             while (*f && !eos) {
199                 if (*f=='%') {
200                     int insertnr;
201                     char *fmtstr,*x,*lastf;
202                     DWORD *argliststart;
203
204                     fmtstr = NULL;
205                     lastf = f;
206                     f++;
207                     if (!*f) {
208                         ADD_TO_T('%');
209                         continue;
210                     }
211                     switch (*f) {
212                     case '1':case '2':case '3':case '4':case '5':
213                     case '6':case '7':case '8':case '9':
214                         insertnr=*f-'0';
215                         switch (f[1]) {
216                         case '0':case '1':case '2':case '3':
217                         case '4':case '5':case '6':case '7':
218                         case '8':case '9':
219                             f++;
220                             insertnr=insertnr*10+*f-'0';
221                             f++;
222                             break;
223                         default:
224                             f++;
225                             break;
226                         }
227                         if (*f=='!') {
228                             f++;
229                             if (NULL!=(x=strchr(f,'!'))) {
230                                 *x='\0';
231                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
232                                 sprintf(fmtstr,"%%%s",f);
233                                 f=x+1;
234                             } else {
235                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
236                                 sprintf(fmtstr,"%%%s",f);
237                                 f+=strlen(f); /*at \0*/
238                             }
239                         } else {
240                             if(!args) break;
241                             fmtstr = HeapAlloc(GetProcessHeap(),0,3);
242                             strcpy( fmtstr, "%s" );
243                         }
244                         if (args) {
245                             int sz;
246                             LPSTR b;
247
248                             if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
249                                 argliststart=args+insertnr-1;
250                             else
251                                 argliststart=(*(DWORD**)args)+insertnr-1;
252
253                                 /* FIXME: precision and width components are not handled correctly */
254                             if ( (strcmp(fmtstr, "%ls") == 0) || (strcmp(fmtstr,"%S") == 0) ) {
255                                 sz = WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, NULL, 0, NULL, NULL);
256                                 b = HeapAlloc(GetProcessHeap(), 0, sz);
257                                 WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, b, sz, NULL, NULL);
258                             } else {
259                                 b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 1000);
260                                 /* CMF - This makes a BIG assumption about va_list */
261                                 TRACE("A BIG assumption\n");
262                                 vsnprintf(b, sz, fmtstr, (va_list) argliststart);
263                             }
264                             for (x=b; *x; x++) ADD_TO_T(*x);
265
266                             HeapFree(GetProcessHeap(),0,b);
267                         } else {
268                                 /* NULL args - copy formatstr
269                                  * (probably wrong)
270                                  */
271                             while ((lastf<f)&&(*lastf)) {
272                                 ADD_TO_T(*lastf++);
273                             }
274                         }
275                         HeapFree(GetProcessHeap(),0,fmtstr);
276                         break;
277                     case 'n':
278                         ADD_TO_T('\r');
279                         ADD_TO_T('\n');
280                         f++;
281                         break;
282                     case '0':
283                         eos = TRUE;
284                         f++;
285                         break;
286                     default:
287                         ADD_TO_T(*f++);
288                         break;
289                     }
290                 } else {
291                     ch = *f;
292                     f++;
293                     if (ch == '\r') {
294                         if (*f == '\n')
295                             f++;
296                         if(width)
297                             ADD_TO_T(' ');
298                         else
299                         {
300                             ADD_TO_T('\r');
301                             ADD_TO_T('\n');
302                         }
303                     } else {
304                         if (ch == '\n')
305                         {
306                             if(width)
307                                 ADD_TO_T(' ');
308                             else
309                             {
310                                 ADD_TO_T('\r');
311                                 ADD_TO_T('\n');
312                             }
313                         }
314                         else
315                             ADD_TO_T(ch);
316                     }
317                 }
318             }
319         }
320         *t='\0';
321     }
322     talloced = strlen(target)+1;
323     if (nSize && talloced<nSize) {
324         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
325     }
326     TRACE("-- %s\n",debugstr_a(target));
327     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
328         *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
329         memcpy(*(LPSTR*)lpBuffer,target,talloced);
330     } else {
331         lstrcpynA(lpBuffer,target,nSize);
332     }
333     HeapFree(GetProcessHeap(),0,target);
334     HeapFree(GetProcessHeap(),0,from);
335     ret = (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer) : strlen(lpBuffer);
336 #endif /* __i386__ */
337     TRACE("-- returning %d\n", ret);
338     return ret;
339 }
340 #undef ADD_TO_T
341
342
343 /***********************************************************************
344  *           FormatMessageW   (KERNEL32.@)
345  */
346 DWORD WINAPI FormatMessageW(
347         DWORD   dwFlags,
348         LPCVOID lpSource,
349         DWORD   dwMessageId,
350         DWORD   dwLanguageId,
351         LPWSTR  lpBuffer,
352         DWORD   nSize,
353         va_list* _args )
354 {
355     LPDWORD args=(LPDWORD)_args;
356 #if defined(__i386__) || defined(__sparc__)
357 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
358     LPWSTR target,t;
359     DWORD talloced;
360     LPWSTR from,f;
361     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
362     BOOL eos = FALSE;
363     WCHAR ch;
364
365     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
366           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
367     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
368         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
369            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
370
371     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
372         FIXME("line wrapping not supported.\n");
373     from = NULL;
374     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
375         from = HeapAlloc( GetProcessHeap(), 0, (strlenW((LPCWSTR)lpSource) + 1) *
376             sizeof(WCHAR) );
377         strcpyW( from, (LPCWSTR)lpSource );
378     }
379     else {
380         from = NULL;
381         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
382             from = load_messageW( (HMODULE)lpSource, dwMessageId, dwLanguageId );
383         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
384             from = load_messageW( kernel32_handle, dwMessageId, dwLanguageId );
385
386         if (!from)
387         {
388             SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
389             return 0;
390         }
391     }
392     target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
393     t = target;
394     talloced= 100;
395
396 #define ADD_TO_T(c)  do {\
397     *t++=c;\
398     if ((DWORD)(t-target) == talloced) {\
399         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
400         t = target+talloced;\
401         talloced*=2;\
402     } \
403 } while (0)
404
405     if (from) {
406         f=from;
407         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
408             while (*f && !eos)
409                 ADD_TO_T(*f++);
410         }
411         else {
412             while (*f && !eos) {
413                 if (*f=='%') {
414                     int insertnr;
415                     WCHAR *fmtstr,*sprintfbuf,*x;
416                     DWORD *argliststart;
417
418                     fmtstr = NULL;
419                     f++;
420                     if (!*f) {
421                         ADD_TO_T('%');
422                         continue;
423                     }
424
425                     switch (*f) {
426                     case '1':case '2':case '3':case '4':case '5':
427                     case '6':case '7':case '8':case '9':
428                         insertnr=*f-'0';
429                         switch (f[1]) {
430                         case '0':case '1':case '2':case '3':
431                         case '4':case '5':case '6':case '7':
432                         case '8':case '9':
433                             f++;
434                             insertnr=insertnr*10+*f-'0';
435                             f++;
436                             break;
437                         default:
438                             f++;
439                             break;
440                         }
441                         if (*f=='!') {
442                             f++;
443                             if (NULL!=(x=strchrW(f,'!'))) {
444                                 *x='\0';
445                                 fmtstr=HeapAlloc( GetProcessHeap(), 0,(strlenW(f)+2)*sizeof(WCHAR));
446                                 sprintfW(fmtstr,PCNTFMTWSTR,f);
447                                 f=x+1;
448                             } else {
449                                 fmtstr=HeapAlloc(GetProcessHeap(),0,(strlenW(f)+2)*sizeof(WCHAR));
450                                 sprintfW(fmtstr,PCNTFMTWSTR,f);
451                                 f+=strlenW(f); /*at \0*/
452                             }
453                         } else {
454                             if(!args) break;
455                             fmtstr = HeapAlloc( GetProcessHeap(),0,3*sizeof(WCHAR));
456                             strcpyW( fmtstr, FMTWSTR );
457                         }
458                         if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
459                             argliststart=args+insertnr-1;
460                         else
461                             argliststart=(*(DWORD**)args)+insertnr-1;
462
463                         if (fmtstr[strlenW(fmtstr)-1]=='s' && argliststart[0]) {
464                             DWORD xarr[3];
465
466                             xarr[0]=*(argliststart+0);
467                             /* possible invalid pointers */
468                             xarr[1]=*(argliststart+1);
469                             xarr[2]=*(argliststart+2);
470                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,(strlenW((LPWSTR)argliststart[0])*2+1)*sizeof(WCHAR));
471
472                             /* CMF - This makes a BIG assumption about va_list */
473                             vsprintfW(sprintfbuf, fmtstr, (va_list) xarr);
474                         } else {
475                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
476
477                             /* CMF - This makes a BIG assumption about va_list */
478                             vsprintfW(sprintfbuf, fmtstr, (va_list) argliststart);
479                         }
480                         x=sprintfbuf;
481                         while (*x) {
482                             ADD_TO_T(*x++);
483                         }
484                         HeapFree(GetProcessHeap(),0,sprintfbuf);
485                         HeapFree(GetProcessHeap(),0,fmtstr);
486                         break;
487                     case 'n':
488                         ADD_TO_T('\r');
489                         ADD_TO_T('\n');
490                         f++;
491                         break;
492                     case '0':
493                         eos = TRUE;
494                         f++;
495                         break;
496                     default:
497                         ADD_TO_T(*f++);
498                         break;
499                     }
500                 } else {
501                     ch = *f;
502                     f++;
503                     if (ch == '\r') {
504                         if (*f == '\n')
505                             f++;
506                         if(width)
507                             ADD_TO_T(' ');
508                         else
509                         {
510                             ADD_TO_T('\r');
511                             ADD_TO_T('\n');
512                         }
513                     } else {
514                         if (ch == '\n')
515                         {
516                             if(width)
517                                 ADD_TO_T(' ');
518                             else
519                             {
520                                 ADD_TO_T('\r');
521                                 ADD_TO_T('\n');
522                             }
523                         }
524                         else
525                             ADD_TO_T(ch);
526                     }
527                 }
528             }
529         }
530         *t='\0';
531     }
532     talloced = strlenW(target)+1;
533     if (nSize && talloced<nSize)
534         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize*sizeof(WCHAR));
535     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
536         /* nSize is the MINIMUM size */
537         DWORD len = strlenW(target) + 1;
538         *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR));
539         strcpyW(*(LPWSTR*)lpBuffer, target);
540     }
541     else lstrcpynW(lpBuffer, target, nSize);
542
543     HeapFree(GetProcessHeap(),0,target);
544     HeapFree(GetProcessHeap(),0,from);
545     TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
546         *(LPWSTR*)lpBuffer : lpBuffer));
547     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
548         strlenW(*(LPWSTR*)lpBuffer):
549             strlenW(lpBuffer);
550 #else
551     return 0;
552 #endif /* __i386__ */
553 }
554 #undef ADD_TO_T