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