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