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