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