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