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