9ca05a54fe06a4d6404db53abe56696741b0ea45
[reactos.git] / reactos / dll / win32 / 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 (from == 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 if(target == NULL)
201 {
202 HeapFree(GetProcessHeap(),0,from);
203 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
204 return 0;
205 }
206 t = target;
207 talloced= 100;
208
209 #define ADD_TO_T(c) do { \
210 *t++=c;\
211 if (t-target == talloced) {\
212 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
213 t = target+talloced;\
214 talloced*=2;\
215 }\
216 } while (0)
217
218 if (from) {
219 f=from;
220 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
221 while (*f && !eos)
222 ADD_TO_T(*f++);
223 }
224 else {
225 while (*f && !eos) {
226 if (*f=='%') {
227 int insertnr;
228 char *fmtstr,*x,*lastf;
229 DWORD *argliststart;
230
231 fmtstr = NULL;
232 lastf = f;
233 f++;
234 if (!*f) {
235 ADD_TO_T('%');
236 continue;
237 }
238 switch (*f) {
239 case '1':case '2':case '3':case '4':case '5':
240 case '6':case '7':case '8':case '9':
241 insertnr=*f-'0';
242 switch (f[1]) {
243 case '0':case '1':case '2':case '3':
244 case '4':case '5':case '6':case '7':
245 case '8':case '9':
246 f++;
247 insertnr=insertnr*10+*f-'0';
248 f++;
249 break;
250 default:
251 f++;
252 break;
253 }
254 if (*f=='!') {
255 f++;
256 if (NULL!=(x=strchr(f,'!'))) {
257 *x='\0';
258 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
259 if(fmtstr == NULL)
260 {
261 HeapFree(GetProcessHeap(),0,from);
262 HeapFree(GetProcessHeap(),0,target);
263 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
264 return 0;
265 }
266 sprintf(fmtstr,"%%%s",f);
267 f=x+1;
268 } else {
269 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
270 if(fmtstr == NULL)
271 {
272 HeapFree(GetProcessHeap(),0,from);
273 HeapFree(GetProcessHeap(),0,target);
274 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
275 return 0;
276 }
277 sprintf(fmtstr,"%%%s",f);
278 f+=strlen(f); /*at \0*/
279 }
280 } else {
281 if(!args) break;
282 fmtstr = HeapAlloc(GetProcessHeap(),0,3);
283 if(fmtstr == NULL)
284 {
285 HeapFree(GetProcessHeap(),0,from);
286 HeapFree(GetProcessHeap(),0,target);
287 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
288 return 0;
289 }
290 strcpy( fmtstr, "%s" );
291 }
292 if (args) {
293 int sz;
294 LPSTR b;
295
296 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
297 argliststart=args+insertnr-1;
298 else
299 argliststart=(*(DWORD**)args)+insertnr-1;
300
301 b = NULL;
302 sz = 0;
303 do {
304 if (b) {
305 HeapFree(GetProcessHeap(), 0, b);
306 }
307 sz += 256;
308 b = HeapAlloc(GetProcessHeap(), 0, sz);
309 /* CMF - This makes a BIG assumption about va_list */
310 } while (0 > _vsnprintf(b, sz, fmtstr, (va_list) argliststart));
311 x=b;
312 while(*x)
313 ADD_TO_T(*x++);
314
315 HeapFree(GetProcessHeap(),0,b);
316 } else {
317 /* NULL args - copy formatstr
318 * (probably wrong)
319 */
320 while ((lastf<f)&&(*lastf)) {
321 ADD_TO_T(*lastf++);
322 }
323 }
324 HeapFree(GetProcessHeap(),0,fmtstr);
325 break;
326 case 'n':
327 ADD_TO_T('\r');
328 ADD_TO_T('\n');
329 f++;
330 break;
331 case '0':
332 eos = TRUE;
333 f++;
334 break;
335 default:
336 ADD_TO_T(*f++);
337 break;
338 }
339 } else {
340 ch = *f;
341 f++;
342 if (ch == '\r') {
343 if (*f == '\n')
344 f++;
345 if(width)
346 ADD_TO_T(' ');
347 else
348 {
349 ADD_TO_T('\r');
350 ADD_TO_T('\n');
351 }
352 } else {
353 if (ch == '\n')
354 {
355 if(width)
356 ADD_TO_T(' ');
357 else
358 {
359 ADD_TO_T('\r');
360 ADD_TO_T('\n');
361 }
362 }
363 else
364 ADD_TO_T(ch);
365 }
366 }
367 }
368 }
369 *t='\0';
370 }
371 talloced = strlen(target)+1;
372 if (nSize && talloced<nSize) {
373 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
374 }
375 TRACE("-- %s\n",debugstr_a(target));
376 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
377 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
378 memcpy(*(LPSTR*)lpBuffer,target,talloced);
379 } else {
380 lstrcpynA(lpBuffer,target,nSize);
381 }
382 HeapFree(GetProcessHeap(),0,target);
383 HeapFree(GetProcessHeap(),0,from);
384 TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer):strlen(lpBuffer));
385 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
386 strlen(*(LPSTR*)lpBuffer):
387 strlen(lpBuffer);
388 #else
389 return 0;
390 #endif /* __i386__ */
391 }
392 #undef ADD_TO_T
393
394 /***********************************************************************
395 * FormatMessageW (KERNEL32.@)
396 *
397 * @implemented
398 */
399 DWORD WINAPI FormatMessageW(
400 DWORD dwFlags,
401 LPCVOID lpSource,
402 DWORD dwMessageId,
403 DWORD dwLanguageId,
404 LPWSTR lpBuffer,
405 DWORD nSize,
406 va_list* _args )
407 {
408 HMODULE kernel32_handle = GetModuleHandleW(kernel32W);
409 LPDWORD args=(LPDWORD)_args;
410 #if defined(__i386__) || defined(__sparc__)
411 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
412 LPWSTR target,t;
413 DWORD talloced,len;
414 LPWSTR from,f;
415 DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
416 BOOL eos = FALSE;
417 WCHAR ch;
418
419 TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
420 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
421 if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
422 &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
423 || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
424
425 if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
426 FIXME("line wrapping not supported.\n");
427 from = NULL;
428 if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
429 from = HeapAlloc( GetProcessHeap(), 0, (strlenW((LPCWSTR)lpSource) + 1) *
430 sizeof(WCHAR) );
431 if(from == NULL)
432 {
433 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
434 return 0;
435 }
436 strcpyW( from, (LPCWSTR)lpSource );
437 }
438 else {
439 from = NULL;
440 if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
441 from = load_messageW( (HMODULE)lpSource, dwMessageId, dwLanguageId );
442 if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
443 from = load_messageW( kernel32_handle, dwMessageId, dwLanguageId );
444
445 if (!from)
446 {
447 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
448 return 0;
449 }
450 }
451
452 target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
453 if(target == NULL)
454 {
455 HeapFree(GetProcessHeap(),0,from);
456 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
457 return 0;
458 }
459 t = target;
460 talloced= 100;
461
462 #define ADD_TO_T(c) do {\
463 *t++=c;\
464 if (t-target == talloced) {\
465 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
466 t = target+talloced;\
467 talloced*=2;\
468 } \
469 } while (0)
470
471 if (from) {
472 f=from;
473 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
474 while (*f && !eos)
475 ADD_TO_T(*f++);
476 }
477 else {
478 while (*f && !eos) {
479 if (*f=='%') {
480 int insertnr;
481 WCHAR *fmtstr,*sprintfbuf,*x,*lastf;
482 DWORD *argliststart;
483
484 fmtstr = NULL;
485 lastf = f;
486 f++;
487 if (!*f) {
488 ADD_TO_T('%');
489 continue;
490 }
491
492 switch (*f) {
493 case '1':case '2':case '3':case '4':case '5':
494 case '6':case '7':case '8':case '9':
495 insertnr=*f-'0';
496 switch (f[1]) {
497 case '0':case '1':case '2':case '3':
498 case '4':case '5':case '6':case '7':
499 case '8':case '9':
500 f++;
501 insertnr=insertnr*10+*f-'0';
502 f++;
503 break;
504 default:
505 f++;
506 break;
507 }
508 if (*f=='!') {
509 f++;
510 if (NULL!=(x=strchrW(f,'!'))) {
511 *x='\0';
512 fmtstr=HeapAlloc( GetProcessHeap(), 0,(strlenW(f)+2)*sizeof(WCHAR));
513 if(fmtstr == NULL)
514 {
515 HeapFree(GetProcessHeap(),0,from);
516 HeapFree(GetProcessHeap(),0,target);
517 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
518 return 0;
519 }
520 sprintfW(fmtstr,PCNTFMTWSTR,f);
521 f=x+1;
522 } else {
523 fmtstr=HeapAlloc(GetProcessHeap(),0,(strlenW(f)+2)*sizeof(WCHAR));
524 if(fmtstr == NULL)
525 {
526 HeapFree(GetProcessHeap(),0,from);
527 HeapFree(GetProcessHeap(),0,target);
528 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
529 return 0;
530 }
531 sprintfW(fmtstr,PCNTFMTWSTR,f);
532 f+=strlenW(f); /*at \0*/
533 }
534 } else {
535 if(!args) break;
536 fmtstr = HeapAlloc( GetProcessHeap(),0,3*sizeof(WCHAR));
537 if(fmtstr == NULL)
538 {
539 HeapFree(GetProcessHeap(),0,from);
540 HeapFree(GetProcessHeap(),0,target);
541 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
542 return 0;
543 }
544 strcpyW( fmtstr, FMTWSTR );
545 }
546
547 if (args) {
548 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
549 argliststart=args+insertnr-1;
550 else
551 argliststart=(*(DWORD**)args)+insertnr-1;
552
553 len = 0;
554 sprintfbuf = NULL;
555 do {
556 if (sprintfbuf) {
557 HeapFree(GetProcessHeap(),0,sprintfbuf);
558 }
559 len += 256;
560 sprintfbuf=HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
561 /* CMF - This makes a BIG assumption about va_list */
562 } while (0 > _vsnwprintf(sprintfbuf, len, fmtstr, (va_list) argliststart));
563 x=sprintfbuf;
564 while (*x) {
565 ADD_TO_T(*x++);
566 }
567 HeapFree(GetProcessHeap(),0,sprintfbuf);
568
569 } else {
570 /* NULL args - copy formatstr
571 * (probably wrong)
572 */
573 while ((lastf<f)&&(*lastf)) {
574 ADD_TO_T(*lastf++);
575 }
576 }
577
578 HeapFree(GetProcessHeap(),0,fmtstr);
579 break;
580 case 'n':
581 ADD_TO_T('\r');
582 ADD_TO_T('\n');
583 f++;
584 break;
585 case '0':
586 eos = TRUE;
587 f++;
588 break;
589 default:
590 ADD_TO_T(*f++);
591 break;
592 }
593 } else {
594 ch = *f;
595 f++;
596 if (ch == '\r') {
597 if (*f == '\n')
598 f++;
599 if(width)
600 ADD_TO_T(' ');
601 else
602 {
603 ADD_TO_T('\r');
604 ADD_TO_T('\n');
605 }
606 } else {
607 if (ch == '\n')
608 {
609 if(width)
610 ADD_TO_T(' ');
611 else
612 {
613 ADD_TO_T('\r');
614 ADD_TO_T('\n');
615 }
616 }
617 else
618 ADD_TO_T(ch);
619 }
620 }
621 }
622 }
623 *t='\0';
624 }
625 talloced = strlenW(target)+1;
626 if (nSize && talloced<nSize)
627 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize*sizeof(WCHAR));
628 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
629 /* nSize is the MINIMUM size */
630 DWORD len = strlenW(target) + 1;
631 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR));
632 strcpyW(*(LPWSTR*)lpBuffer, target);
633 }
634 else lstrcpynW(lpBuffer, target, nSize);
635
636 HeapFree(GetProcessHeap(),0,target);
637 HeapFree(GetProcessHeap(),0,from);
638 TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
639 *(LPWSTR*)lpBuffer : lpBuffer));
640 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
641 strlenW(*(LPWSTR*)lpBuffer):
642 strlenW(lpBuffer);
643 #else
644 return 0;
645 #endif /* __i386__ */
646 }
647 #undef ADD_TO_T
648