fix 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 /* FIXME: precision and width components are not handled correctly */
271 if ( (strcmp(fmtstr, "%ls") == 0) || (strcmp(fmtstr,"%S") == 0) ) {
272 sz = WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, NULL, 0, NULL, NULL);
273 b = HeapAlloc(GetProcessHeap(), 0, sz);
274 WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, b, sz, NULL, NULL);
275 } else {
276 b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 1000);
277 /* CMF - This makes a BIG assumption about va_list */
278 TRACE("A BIG assumption\n");
279 _vsnprintf(b, sz, fmtstr, (va_list) argliststart);
280 }
281 for (x=b; *x; x++) ADD_TO_T(*x);
282
283 HeapFree(GetProcessHeap(),0,b);
284 } else {
285 /* NULL args - copy formatstr
286 * (probably wrong)
287 */
288 while ((lastf<f)&&(*lastf)) {
289 ADD_TO_T(*lastf++);
290 }
291 }
292 HeapFree(GetProcessHeap(),0,fmtstr);
293 break;
294 case 'n':
295 ADD_TO_T('\r');
296 ADD_TO_T('\n');
297 f++;
298 break;
299 case '0':
300 eos = TRUE;
301 f++;
302 break;
303 default:
304 ADD_TO_T(*f++);
305 break;
306 }
307 } else {
308 ch = *f;
309 f++;
310 if (ch == '\r') {
311 if (*f == '\n')
312 f++;
313 if(width)
314 ADD_TO_T(' ');
315 else
316 {
317 ADD_TO_T('\r');
318 ADD_TO_T('\n');
319 }
320 } else {
321 if (ch == '\n')
322 {
323 if(width)
324 ADD_TO_T(' ');
325 else
326 {
327 ADD_TO_T('\r');
328 ADD_TO_T('\n');
329 }
330 }
331 else
332 ADD_TO_T(ch);
333 }
334 }
335 }
336 }
337 *t='\0';
338 }
339 talloced = strlen(target)+1;
340 if (nSize && talloced<nSize) {
341 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
342 }
343 TRACE("-- %s\n",debugstr_a(target));
344 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
345 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced));
346 memcpy(*(LPSTR*)lpBuffer,target,talloced);
347 } else {
348 lstrcpynA(lpBuffer,target,nSize);
349 }
350 HeapFree(GetProcessHeap(),0,target);
351 HeapFree(GetProcessHeap(),0,from);
352 TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer):strlen(lpBuffer));
353 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
354 strlen(*(LPSTR*)lpBuffer):
355 strlen(lpBuffer);
356 #else
357 return 0;
358 #endif /* __i386__ */
359 }
360 #undef ADD_TO_T
361
362 /***********************************************************************
363 * FormatMessageW (KERNEL32.@)
364 *
365 * @implemented
366 */
367 DWORD WINAPI FormatMessageW(
368 DWORD dwFlags,
369 LPCVOID lpSource,
370 DWORD dwMessageId,
371 DWORD dwLanguageId,
372 LPWSTR lpBuffer,
373 DWORD nSize,
374 va_list* _args )
375 {
376 HMODULE kernel32_handle = GetModuleHandleW(kernel32W);
377 LPDWORD args=(LPDWORD)_args;
378 #if defined(__i386__) || defined(__sparc__)
379 /* This implementation is completely dependent on the format of the va_list on x86 CPUs */
380 LPWSTR target,t;
381 DWORD talloced;
382 LPWSTR from,f;
383 DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
384 BOOL eos = FALSE;
385 CHAR ch;
386
387 TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
388 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
389 if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
390 &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
391 || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
392
393 if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
394 FIXME("line wrapping not supported.\n");
395 from = NULL;
396 if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
397 from = HeapAlloc( GetProcessHeap(), 0, (strlenW((LPCWSTR)lpSource) + 1) *
398 sizeof(WCHAR) );
399 strcpyW( from, (LPCWSTR)lpSource );
400 }
401 else {
402 from = NULL;
403 if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
404 from = load_messageW( (HMODULE)lpSource, dwMessageId, dwLanguageId );
405 if (!from && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM))
406 from = load_messageW( kernel32_handle, dwMessageId, dwLanguageId );
407
408 if (!from)
409 {
410 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
411 return 0;
412 }
413 }
414 target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 * sizeof(WCHAR) );
415 t = target;
416 talloced= 100;
417
418 #define ADD_TO_T(c) do {\
419 *t++=c;\
420 if (t-target == talloced) {\
421 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2*sizeof(WCHAR));\
422 t = target+talloced;\
423 talloced*=2;\
424 } \
425 } while (0)
426
427 if (from) {
428 f=from;
429 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
430 while (*f && !eos)
431 ADD_TO_T(*f++);
432 }
433 else {
434 while (*f && !eos) {
435 if (*f=='%') {
436 int insertnr;
437 WCHAR *fmtstr,*sprintfbuf,*x;
438 DWORD *argliststart;
439
440 fmtstr = NULL;
441 f++;
442 if (!*f) {
443 ADD_TO_T('%');
444 continue;
445 }
446
447 switch (*f) {
448 case '1':case '2':case '3':case '4':case '5':
449 case '6':case '7':case '8':case '9':
450 insertnr=*f-'0';
451 switch (f[1]) {
452 case '0':case '1':case '2':case '3':
453 case '4':case '5':case '6':case '7':
454 case '8':case '9':
455 f++;
456 insertnr=insertnr*10+*f-'0';
457 f++;
458 break;
459 default:
460 f++;
461 break;
462 }
463 if (*f=='!') {
464 f++;
465 if (NULL!=(x=strchrW(f,'!'))) {
466 *x='\0';
467 fmtstr=HeapAlloc( GetProcessHeap(), 0,(strlenW(f)+2)*sizeof(WCHAR));
468 sprintfW(fmtstr,PCNTFMTWSTR,f);
469 f=x+1;
470 } else {
471 fmtstr=HeapAlloc(GetProcessHeap(),0,(strlenW(f)+2)*sizeof(WCHAR));
472 sprintfW(fmtstr,PCNTFMTWSTR,f);
473 f+=strlenW(f); /*at \0*/
474 }
475 } else {
476 if(!args) break;
477 fmtstr = HeapAlloc( GetProcessHeap(),0,3*sizeof(WCHAR));
478 strcpyW( fmtstr, FMTWSTR );
479 }
480 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
481 argliststart=args+insertnr-1;
482 else
483 argliststart=(*(DWORD**)args)+insertnr-1;
484
485 if (fmtstr[strlenW(fmtstr)-1]=='s' && argliststart[0]) {
486 DWORD xarr[3];
487
488 xarr[0]=*(argliststart+0);
489 /* possible invalid pointers */
490 xarr[1]=*(argliststart+1);
491 xarr[2]=*(argliststart+2);
492 sprintfbuf=HeapAlloc(GetProcessHeap(),0,(strlenW((LPWSTR)argliststart[0])*2+1)*sizeof(WCHAR));
493 /* CMF - This makes a BIG assumption about va_list */
494 _vsnwprintf(sprintfbuf,(strlenW((LPWSTR)argliststart[0])*2+1)*sizeof(WCHAR), fmtstr, (va_list) xarr);
495 } else {
496 sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
497
498 /* CMF - This makes a BIG assumption about va_list */
499 _vsnwprintf(sprintfbuf,(strlenW((LPWSTR)argliststart[0])*2+1)*sizeof(WCHAR), fmtstr, (va_list) argliststart);
500 }
501 x=sprintfbuf;
502 while (*x) {
503 ADD_TO_T(*x++);
504 }
505 HeapFree(GetProcessHeap(),0,sprintfbuf);
506 HeapFree(GetProcessHeap(),0,fmtstr);
507 break;
508 case 'n':
509 ADD_TO_T('\r');
510 ADD_TO_T('\n');
511 f++;
512 break;
513 case '0':
514 eos = TRUE;
515 f++;
516 break;
517 default:
518 ADD_TO_T(*f++);
519 break;
520 }
521 } else {
522 ch = *f;
523 f++;
524 if (ch == '\r') {
525 if (*f == '\n')
526 f++;
527 if(width)
528 ADD_TO_T(' ');
529 else
530 {
531 ADD_TO_T('\r');
532 ADD_TO_T('\n');
533 }
534 } else {
535 if (ch == '\n')
536 {
537 if(width)
538 ADD_TO_T(' ');
539 else
540 {
541 ADD_TO_T('\r');
542 ADD_TO_T('\n');
543 }
544 }
545 else
546 ADD_TO_T(ch);
547 }
548 }
549 }
550 }
551 *t='\0';
552 }
553 talloced = strlenW(target)+1;
554 if (nSize && talloced<nSize)
555 target = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize*sizeof(WCHAR));
556 if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
557 /* nSize is the MINIMUM size */
558 DWORD len = strlenW(target) + 1;
559 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR));
560 strcpyW(*(LPWSTR*)lpBuffer, target);
561 }
562 else lstrcpynW(lpBuffer, target, nSize);
563
564 HeapFree(GetProcessHeap(),0,target);
565 HeapFree(GetProcessHeap(),0,from);
566 TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
567 *(LPWSTR*)lpBuffer : lpBuffer));
568 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
569 strlenW(*(LPWSTR*)lpBuffer):
570 strlenW(lpBuffer);
571 #else
572 return 0;
573 #endif /* __i386__ */
574 }
575 #undef ADD_TO_T
576