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