qmgr: Change BITS_IBackgroundCopyJob_SetPriority to a stub.
[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);
270                                 WideCharToMultiByte(CP_ACP, 0, (WCHAR *)argliststart, 1, b, sz, NULL, NULL);
271                             }
272                             else
273                             {
274                                 b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 1000);
275                                 /* CMF - This makes a BIG assumption about va_list */
276                                 TRACE("A BIG assumption\n");
277                                 vsnprintf(b, sz, fmtstr, (va_list) argliststart);
278                             }
279                             for (x=b; *x; x++) ADD_TO_T(*x);
280
281                             HeapFree(GetProcessHeap(),0,b);
282                         } else {
283                                 /* NULL args - copy formatstr
284                                  * (probably wrong)
285                                  */
286                             while ((lastf<f)&&(*lastf)) {
287                                 ADD_TO_T(*lastf++);
288                             }
289                         }
290                         HeapFree(GetProcessHeap(),0,fmtstr);
291                         break;
292                     case 'n':
293                         ADD_TO_T('\r');
294                         ADD_TO_T('\n');
295                         f++;
296                         break;
297                     case '0':
298                         eos = TRUE;
299                         f++;
300                         break;
301                     default:
302                         ADD_TO_T(*f++);
303                         break;
304                     }
305                 } else {
306                     ch = *f;
307                     f++;
308                     if (ch == '\r') {
309                         if (*f == '\n')
310                             f++;
311                         if(width)
312                             ADD_TO_T(' ');
313                         else
314                         {
315                             ADD_TO_T('\r');
316                             ADD_TO_T('\n');
317                         }
318                     } else {
319                         if (ch == '\n')
320                         {
321                             if(width)
322                                 ADD_TO_T(' ');
323                             else
324                             {
325                                 ADD_TO_T('\r');
326                                 ADD_TO_T('\n');
327                             }
328                         }
329                         else
330                             ADD_TO_T(ch);
331                     }
332                 }
333             }
334         }
335         *t='\0';
336     }
337     talloced = strlen(target)+1;
338     if (nSize && talloced<nSize) {
339         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
340     }
341     TRACE("-- %s\n",debugstr_a(target));
342     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
343         *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
344         memcpy(*(LPSTR*)lpBuffer,target,talloced);
345     } else {
346         lstrcpynA(lpBuffer,target,nSize);
347     }
348     HeapFree(GetProcessHeap(),0,target);
349     HeapFree(GetProcessHeap(),0,from);
350     ret = (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer) : strlen(lpBuffer);
351 #endif /* __i386__ */
352     TRACE("-- returning %d\n", ret);
353     return ret;
354 }
355 #undef ADD_TO_T
356
357
358 /***********************************************************************
359  *           FormatMessageW   (KERNEL32.@)
360  */
361 DWORD WINAPI FormatMessageW(
362         DWORD   dwFlags,
363         LPCVOID lpSource,
364         DWORD   dwMessageId,
365         DWORD   dwLanguageId,
366         LPWSTR  lpBuffer,
367         DWORD   nSize,
368         __ms_va_list* _args )
369 {
370     LPDWORD args=(LPDWORD)_args;
371 #if defined(__i386__) || defined(__sparc__)
372 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
373     LPWSTR target,t;
374     DWORD talloced;
375     LPWSTR from,f;
376     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
377     BOOL eos = FALSE;
378     WCHAR ch;
379     static const WCHAR fmt_wc[] = {'%','w','c',0};
380
381     TRACE("(0x%x,%p,%d,0x%x,%p,%d,%p)\n",
382           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
383     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
384         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
385            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
386
387     if (!lpBuffer)
388     {
389         SetLastError(ERROR_INVALID_PARAMETER);
390         return 0;
391     }
392
393     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
394         FIXME("line wrapping not supported.\n");
395     from = NULL;
396     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
397         from = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpSource) + 1) *
398             sizeof(WCHAR) );
399         strcpyW( from, lpSource );
400     }
401     else {
402         from = NULL;
403         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
404             from = load_messageW( (HMODULE)lpSource, dwMessageId, dwLanguageId );
405         if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
406             from = load_messageW( kernel32_handle, dwMessageId, dwLanguageId );
407
408         if (!from)
409         {
410             SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
411             return 0;
412         }
413     }
414     target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
415     t = target;
416     talloced= 100;
417
418 #define ADD_TO_T(c)  do {\
419     *t++=c;\
420     if ((DWORD)(t-target) == talloced) {\
421         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
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                     WCHAR *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=strchrW(f,'!'))) {
466                                 *x='\0';
467                                 fmtstr=HeapAlloc( GetProcessHeap(), 0,(strlenW(f)+2)*sizeof(WCHAR));
468                                 sprintfW(fmtstr,PCNTFMTWSTR,f);
469                                 f=x+1;
470                             } else {
471                                 fmtstr=HeapAlloc(GetProcessHeap(),0,(strlenW(f)+2)*sizeof(WCHAR));
472                                 sprintfW(fmtstr,PCNTFMTWSTR,f);
473                                 f+=strlenW(f); /*at \0*/
474                             }
475                         } else {
476                             if(!args) break;
477                             fmtstr = HeapAlloc( GetProcessHeap(),0,3*sizeof(WCHAR));
478                             strcpyW( fmtstr, FMTWSTR );
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[strlenW(fmtstr)-1]=='s' && argliststart[0]) {
486                             DWORD xarr[3];
487
488                             xarr[0]=*(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)*sizeof(WCHAR));
493
494                             /* CMF - This makes a BIG assumption about va_list */
495                             vsprintfW(sprintfbuf, fmtstr, (va_list) xarr);
496                         }
497                         else if (strcmpW(fmtstr, fmt_wc) == 0) {
498                             sprintfbuf = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * 2);
499                             sprintfbuf[0] = *(WCHAR *)argliststart;
500                             sprintfbuf[1] = 0;
501                         } else {
502                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
503
504                             /* CMF - This makes a BIG assumption about va_list */
505                             vsprintfW(sprintfbuf, fmtstr, (va_list) argliststart);
506                         }
507                         x=sprintfbuf;
508                         while (*x) {
509                             ADD_TO_T(*x++);
510                         }
511                         HeapFree(GetProcessHeap(),0,sprintfbuf);
512                         HeapFree(GetProcessHeap(),0,fmtstr);
513                         break;
514                     case 'n':
515                         ADD_TO_T('\r');
516                         ADD_TO_T('\n');
517                         f++;
518                         break;
519                     case '0':
520                         eos = TRUE;
521                         f++;
522                         break;
523                     default:
524                         ADD_TO_T(*f++);
525                         break;
526                     }
527                 } else {
528                     ch = *f;
529                     f++;
530                     if (ch == '\r') {
531                         if (*f == '\n')
532                             f++;
533                         if(width)
534                             ADD_TO_T(' ');
535                         else
536                         {
537                             ADD_TO_T('\r');
538                             ADD_TO_T('\n');
539                         }
540                     } else {
541                         if (ch == '\n')
542                         {
543                             if(width)
544                                 ADD_TO_T(' ');
545                             else
546                             {
547                                 ADD_TO_T('\r');
548                                 ADD_TO_T('\n');
549                             }
550                         }
551                         else
552                             ADD_TO_T(ch);
553                     }
554                 }
555             }
556         }
557         *t='\0';
558     }
559     talloced = strlenW(target)+1;
560     if (nSize && talloced<nSize)
561         target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize*sizeof(WCHAR));
562     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
563         /* nSize is the MINIMUM size */
564         DWORD len = strlenW(target) + 1;
565         *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR));
566         strcpyW(*(LPWSTR*)lpBuffer, target);
567     }
568     else lstrcpynW(lpBuffer, target, nSize);
569
570     HeapFree(GetProcessHeap(),0,target);
571     HeapFree(GetProcessHeap(),0,from);
572     TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
573         *(LPWSTR*)lpBuffer : lpBuffer));
574     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
575         strlenW(*(LPWSTR*)lpBuffer):
576             strlenW(lpBuffer);
577 #else
578     return 0;
579 #endif /* __i386__ */
580 }
581 #undef ADD_TO_T