- Fix definition of RTL_CRITICAL_SECTION_DEBUG.
[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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include <k32.h>
28
29 #define NDEBUG
30
31 #include "../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_messageA (internal)
80 */
81
82 static LPSTR load_messageA( HMODULE module, UINT id, WORD lang )
83 {
84 PRTL_MESSAGE_RESOURCE_ENTRY mre;
85 char *buffer;
86 NTSTATUS Status;
87
88 TRACE("module = %p, id = %08x\n", module, id );
89
90 if (!module) module = GetModuleHandleW( NULL );
91 Status = RtlFindMessage( module, (ULONG) RT_MESSAGETABLE, lang, id, &mre );
92 if (!NT_SUCCESS(Status))
93 return NULL;
94
95 if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
96 {
97 int len = WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, NULL, 0, NULL, NULL );
98 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
99 WideCharToMultiByte( CP_ACP, 0, (const WCHAR *)mre->Text, -1, buffer, len, NULL, NULL );
100 }
101 else
102 {
103 int len = strlen((const char*)mre->Text) + 1;
104 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
105 memcpy( buffer, mre->Text, len );
106 }
107 //TRACE("returning %s\n", wine_dbgstr_a(buffer));
108 return buffer;
109 }
110
111
112
113 static LPWSTR load_messageW( HMODULE module, UINT id, WORD lang )
114 {
115 PRTL_MESSAGE_RESOURCE_ENTRY mre;
116 WCHAR *buffer;
117 NTSTATUS Status;
118
119 TRACE("module = %p, id = %08x\n", module, id );
120
121 if (!module) module = GetModuleHandleW( NULL );
122 Status = RtlFindMessage( module, (ULONG) RT_MESSAGETABLE, lang, id, &mre );
123 if (!NT_SUCCESS(Status))
124 return NULL;
125
126 if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
127 {
128 int len = (strlenW( (const WCHAR *)mre->Text ) + 1) * sizeof(WCHAR);
129 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
130 memcpy( buffer, mre->Text, len );
131 }
132 else
133 {
134 int len = MultiByteToWideChar( CP_ACP, 0, (const char *)mre->Text, -1, NULL, 0 );
135 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
136 MultiByteToWideChar( CP_ACP, 0, (const char*)mre->Text, -1, buffer, len );
137 }
138 //TRACE("returning %s\n", wine_dbgstr_w(buffer));
139 return buffer;
140 }
141
142 /***********************************************************************
143 * FormatMessageA (KERNEL32.@)
144 * FIXME: missing wrap,
145 *
146 * @implemented
147 */
148 DWORD WINAPI FormatMessageA(
149 DWORD dwFlags,
150 LPCVOID lpSource,
151 DWORD dwMessageId,
152 DWORD dwLanguageId,
153 LPSTR lpBuffer,
154 DWORD nSize,
155 va_list* _args )
156 {
157 LPDWORD args=(LPDWORD)_args;
158 HMODULE kernel32_handle = GetModuleHandleW(kernel32W);
159
160 #if defined(__i386__) || defined(__sparc__)
161 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
162 LPSTR target,t;
163 DWORD talloced;
164 LPSTR from,f;
165 DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
166 BOOL eos = FALSE;
167 CHAR ch;
168
169 TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
170 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
171 if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
172 &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
173 || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
174
175 if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
176 FIXME("line wrapping (%lu) not supported.\n", width);
177 from = NULL;
178 if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
179 {
180 from = HeapAlloc( GetProcessHeap(), 0, strlen((LPCSTR)lpSource)+1 );
181 if (from == NULL)
182 {
183 return 0;
184 }
185 strcpy( from, (LPCSTR)lpSource );
186 }
187 else {
188 from = NULL;
189 if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
190 from = load_messageA( (HMODULE)lpSource, dwMessageId, (WORD)dwLanguageId );
191 if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
192 from = load_messageA( kernel32_handle, dwMessageId, (WORD)dwLanguageId );
193
194 if (!from)
195 {
196 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
197 return 0;
198 }
199 }
200 target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
201 if(target == NULL)
202 {
203 HeapFree(GetProcessHeap(),0,from);
204 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
205 return 0;
206 }
207 t = target;
208 talloced= 100;
209
210 #define ADD_TO_T(c) do { \
211 *t++=c;\
212 if (t-target == talloced) {\
213 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
214 t = target+talloced;\
215 talloced*=2;\
216 }\
217 } while (0)
218
219 if (from) {
220 f=from;
221 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
222 while (*f && !eos)
223 ADD_TO_T(*f++);
224 }
225 else {
226 while (*f && !eos) {
227 if (*f=='%') {
228 int insertnr;
229 char *fmtstr,*x,*lastf;
230 DWORD *argliststart;
231
232 fmtstr = NULL;
233 lastf = f;
234 f++;
235 if (!*f) {
236 ADD_TO_T('%');
237 continue;
238 }
239 switch (*f) {
240 case '1':case '2':case '3':case '4':case '5':
241 case '6':case '7':case '8':case '9':
242 insertnr=*f-'0';
243 switch (f[1]) {
244 case '0':case '1':case '2':case '3':
245 case '4':case '5':case '6':case '7':
246 case '8':case '9':
247 f++;
248 insertnr=insertnr*10+*f-'0';
249 f++;
250 break;
251 default:
252 f++;
253 break;
254 }
255 if (*f=='!') {
256 f++;
257 if (NULL!=(x=strchr(f,'!'))) {
258 *x='\0';
259 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
260 if(fmtstr == NULL)
261 {
262 HeapFree(GetProcessHeap(),0,from);
263 HeapFree(GetProcessHeap(),0,target);
264 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
265 return 0;
266 }
267 sprintf(fmtstr,"%%%s",f);
268 f=x+1;
269 } else {
270 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
271 if(fmtstr == NULL)
272 {
273 HeapFree(GetProcessHeap(),0,from);
274 HeapFree(GetProcessHeap(),0,target);
275 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
276 return 0;
277 }
278 sprintf(fmtstr,"%%%s",f);
279 f+=strlen(f); /*at \0*/
280 }
281 } else {
282 if(!args) break;
283 fmtstr = HeapAlloc(GetProcessHeap(),0,3);
284 if(fmtstr == NULL)
285 {
286 HeapFree(GetProcessHeap(),0,from);
287 HeapFree(GetProcessHeap(),0,target);
288 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
289 return 0;
290 }
291 strcpy( fmtstr, "%s" );
292 }
293 if (args) {
294 int sz;
295 LPSTR b;
296
297 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
298 argliststart=args+insertnr-1;
299 else
300 argliststart=(*(DWORD**)args)+insertnr-1;
301
302 b = NULL;
303 sz = 0;
304 do {
305 if (b) {
306 HeapFree(GetProcessHeap(), 0, b);
307 }
308 sz += 256;
309 b = HeapAlloc(GetProcessHeap(), 0, sz);
310 /* CMF - This makes a BIG assumption about va_list */
311 } while (0 > _vsnprintf(b, sz, fmtstr, (va_list) argliststart));
312 x=b;
313 while(*x)
314 ADD_TO_T(*x++);
315
316 HeapFree(GetProcessHeap(),0,b);
317 } else {
318 /* NULL args - copy formatstr
319 * (probably wrong)
320 */
321 while ((lastf<f)&&(*lastf)) {
322 ADD_TO_T(*lastf++);
323 }
324 }
325 HeapFree(GetProcessHeap(),0,fmtstr);
326 break;
327 case 'n':
328 ADD_TO_T('\r');
329 ADD_TO_T('\n');
330 f++;
331 break;
332 case '0':
333 eos = TRUE;
334 f++;
335 break;
336 default:
337 ADD_TO_T(*f++);
338 break;
339 }
340 } else {
341 ch = *f;
342 f++;
343 if (ch == '\r') {
344 if (*f == '\n')
345 f++;
346 if(width)
347 ADD_TO_T(' ');
348 else
349 {
350 ADD_TO_T('\r');
351 ADD_TO_T('\n');
352 }
353 } else {
354 if (ch == '\n')
355 {
356 if(width)
357 ADD_TO_T(' ');
358 else
359 {
360 ADD_TO_T('\r');
361 ADD_TO_T('\n');
362 }
363 }
364 else
365 ADD_TO_T(ch);
366 }
367 }
368 }
369 }
370 *t='\0';
371 }
372 talloced = strlen(target)+1;
373 if (nSize && talloced<nSize) {
374 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
375 }
376 //TRACE("-- %s\n",debugstr_a(target));
377 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
378 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
379 memcpy(*(LPSTR*)lpBuffer,target,talloced);
380 } else {
381 lstrcpynA(lpBuffer,target,nSize);
382 }
383 HeapFree(GetProcessHeap(),0,target);
384 HeapFree(GetProcessHeap(),0,from);
385 TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer):strlen(lpBuffer));
386 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
387 strlen(*(LPSTR*)lpBuffer):
388 strlen(lpBuffer);
389 #else
390 return 0;
391 #endif /* __i386__ */
392 }
393 #undef ADD_TO_T
394
395 /***********************************************************************
396 * FormatMessageW (KERNEL32.@)
397 *
398 * @implemented
399 */
400 DWORD WINAPI FormatMessageW(
401 DWORD dwFlags,
402 LPCVOID lpSource,
403 DWORD dwMessageId,
404 DWORD dwLanguageId,
405 LPWSTR lpBuffer,
406 DWORD nSize,
407 va_list* _args )
408 {
409 HMODULE kernel32_handle = GetModuleHandleW(kernel32W);
410 LPDWORD args=(LPDWORD)_args;
411 #if defined(__i386__) || defined(__sparc__)
412 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
413 LPWSTR target,t;
414 DWORD talloced,len;
415 LPWSTR from,f;
416 DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
417 BOOL eos = FALSE;
418 WCHAR ch;
419
420 TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
421 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
422 if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
423 &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
424 || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
425
426 if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
427 FIXME("line wrapping not supported.\n");
428 from = NULL;
429 if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
430 from = HeapAlloc( GetProcessHeap(), 0, (strlenW((LPCWSTR)lpSource) + 1) *
431 sizeof(WCHAR) );
432 if(from == NULL)
433 {
434 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
435 return 0;
436 }
437 strcpyW( from, (LPCWSTR)lpSource );
438 }
439 else {
440 from = NULL;
441 if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
442 from = load_messageW( (HMODULE)lpSource, dwMessageId, (WORD)dwLanguageId );
443 if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
444 from = load_messageW( kernel32_handle, dwMessageId,(WORD)dwLanguageId );
445
446 if (!from)
447 {
448 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
449 return 0;
450 }
451 }
452
453 target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
454 if(target == NULL)
455 {
456 HeapFree(GetProcessHeap(),0,from);
457 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
458 return 0;
459 }
460 t = target;
461 talloced= 100;
462
463 #define ADD_TO_T(c) do {\
464 *t++=c;\
465 if (t-target == talloced) {\
466 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
467 t = target+talloced;\
468 talloced*=2;\
469 } \
470 } while (0)
471
472 if (from) {
473 f=from;
474 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
475 while (*f && !eos)
476 ADD_TO_T(*f++);
477 }
478 else {
479 while (*f && !eos) {
480 if (*f=='%') {
481 int insertnr;
482 WCHAR *fmtstr,*sprintfbuf,*x,*lastf;
483 DWORD *argliststart;
484
485 fmtstr = NULL;
486 lastf = f;
487 f++;
488 if (!*f) {
489 ADD_TO_T('%');
490 continue;
491 }
492
493 switch (*f) {
494 case '1':case '2':case '3':case '4':case '5':
495 case '6':case '7':case '8':case '9':
496 insertnr=*f-'0';
497 switch (f[1]) {
498 case '0':case '1':case '2':case '3':
499 case '4':case '5':case '6':case '7':
500 case '8':case '9':
501 f++;
502 insertnr=insertnr*10+*f-'0';
503 f++;
504 break;
505 default:
506 f++;
507 break;
508 }
509 if (*f=='!') {
510 f++;
511 if (NULL!=(x=strchrW(f,'!'))) {
512 *x='\0';
513 fmtstr=HeapAlloc( GetProcessHeap(), 0,(strlenW(f)+2)*sizeof(WCHAR));
514 if(fmtstr == NULL)
515 {
516 HeapFree(GetProcessHeap(),0,from);
517 HeapFree(GetProcessHeap(),0,target);
518 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
519 return 0;
520 }
521 sprintfW(fmtstr,PCNTFMTWSTR,f);
522 f=x+1;
523 } else {
524 fmtstr=HeapAlloc(GetProcessHeap(),0,(strlenW(f)+2)*sizeof(WCHAR));
525 if(fmtstr == NULL)
526 {
527 HeapFree(GetProcessHeap(),0,from);
528 HeapFree(GetProcessHeap(),0,target);
529 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
530 return 0;
531 }
532 sprintfW(fmtstr,PCNTFMTWSTR,f);
533 f+=strlenW(f); /*at \0*/
534 }
535 } else {
536 if(!args) break;
537 fmtstr = HeapAlloc( GetProcessHeap(),0,3*sizeof(WCHAR));
538 if(fmtstr == NULL)
539 {
540 HeapFree(GetProcessHeap(),0,from);
541 HeapFree(GetProcessHeap(),0,target);
542 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
543 return 0;
544 }
545 strcpyW( fmtstr, FMTWSTR );
546 }
547
548 if (args) {
549 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
550 argliststart=args+insertnr-1;
551 else
552 argliststart=(*(DWORD**)args)+insertnr-1;
553
554 len = 0;
555 sprintfbuf = NULL;
556 do {
557 if (sprintfbuf) {
558 HeapFree(GetProcessHeap(),0,sprintfbuf);
559 }
560 len += 256;
561 sprintfbuf=HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
562 /* CMF - This makes a BIG assumption about va_list */
563 } while (0 > _vsnwprintf(sprintfbuf, len, fmtstr, (va_list) argliststart));
564 x=sprintfbuf;
565 while (*x) {
566 ADD_TO_T(*x++);
567 }
568 HeapFree(GetProcessHeap(),0,sprintfbuf);
569
570 } else {
571 /* NULL args - copy formatstr
572 * (probably wrong)
573 */
574 while ((lastf<f)&&(*lastf)) {
575 ADD_TO_T(*lastf++);
576 }
577 }
578
579 HeapFree(GetProcessHeap(),0,fmtstr);
580 break;
581 case 'n':
582 ADD_TO_T('\r');
583 ADD_TO_T('\n');
584 f++;
585 break;
586 case '0':
587 eos = TRUE;
588 f++;
589 break;
590 default:
591 ADD_TO_T(*f++);
592 break;
593 }
594 } else {
595 ch = *f;
596 f++;
597 if (ch == '\r') {
598 if (*f == '\n')
599 f++;
600 if(width)
601 ADD_TO_T(' ');
602 else
603 {
604 ADD_TO_T('\r');
605 ADD_TO_T('\n');
606 }
607 } else {
608 if (ch == '\n')
609 {
610 if(width)
611 ADD_TO_T(' ');
612 else
613 {
614 ADD_TO_T('\r');
615 ADD_TO_T('\n');
616 }
617 }
618 else
619 ADD_TO_T(ch);
620 }
621 }
622 }
623 }
624 *t='\0';
625 }
626 talloced = strlenW(target)+1;
627 if (nSize && talloced<nSize)
628 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize*sizeof(WCHAR));
629 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
630 /* nSize is the MINIMUM size */
631 DWORD len = strlenW(target) + 1;
632 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR));
633 strcpyW(*(LPWSTR*)lpBuffer, target);
634 }
635 else lstrcpynW(lpBuffer, target, nSize);
636
637 HeapFree(GetProcessHeap(),0,target);
638 HeapFree(GetProcessHeap(),0,from);
639 //TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
640 // *(LPWSTR*)lpBuffer : lpBuffer));
641 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
642 strlenW(*(LPWSTR*)lpBuffer):
643 strlenW(lpBuffer);
644 #else
645 return 0;
646 #endif /* __i386__ */
647 }
648 #undef ADD_TO_T
649