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