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