1 /* $Id: errormsg.c,v 1.15 2003/11/19 22:19:17 sedwards Exp $
3 * reactos/lib/kernel32/misc/errormsg.c
7 * FormatMessage implementation
9 * Copyright 1996 Marcus Meissner
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.
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.
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
26 #include <ddk/ntddk.h>
27 #include <kernel32/kernel32.h>
33 /* DO NOT USE IT!! it will go away soon */
34 inline static LPSTR
HEAP_strdupWtoA( HANDLE heap
, DWORD flags
, LPCWSTR str
)
39 if (!str
) return NULL
;
40 len
= WideCharToMultiByte( CP_ACP
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
41 ret
= RtlAllocateHeap(RtlGetProcessHeap(), flags
, len
);
42 if(ret
) WideCharToMultiByte( CP_ACP
, 0, str
, -1, ret
, len
, NULL
, NULL
);
51 typedef struct tagMESSAGE_RESOURCE_ENTRY
{
55 } MESSAGE_RESOURCE_ENTRY
,*PMESSAGE_RESOURCE_ENTRY
;
56 #define MESSAGE_RESOURCE_UNICODE 0x0001
58 typedef struct tagMESSAGE_RESOURCE_BLOCK
{
61 DWORD OffsetToEntries
;
62 } MESSAGE_RESOURCE_BLOCK
,*PMESSAGE_RESOURCE_BLOCK
;
64 typedef struct tagMESSAGE_RESOURCE_DATA
{
66 MESSAGE_RESOURCE_BLOCK Blocks
[ 1 ];
67 } MESSAGE_RESOURCE_DATA
,*PMESSAGE_RESOURCE_DATA
;
70 //#define RT_RCDATAA MAKEINTRESOURCEA(10)
71 //#define RT_RCDATAW MAKEINTRESOURCEW(10)
72 ////#define RT_RCDATA WINELIB_NAME_AW(RT_RCDATA)
73 //#define RT_MESSAGETABLEA MAKEINTRESOURCEA(11)
74 //#define RT_MESSAGETABLEW MAKEINTRESOURCEW(11)
75 ////#define RT_MESSAGETABLE WINELIB_NAME_AW(RT_MESSAGETABLE)
77 /* Messages...used by FormatMessage32* (KERNEL32.something)
79 * They can be specified either directly or using a message ID and
80 * loading them from the resource.
82 * The resourcedata has following format:
84 * 0: DWORD nrofentries
85 * nrofentries * subentry:
88 * 8: DWORD offset from start to the stringentries
90 * (lastentry-firstentry) * stringentry:
91 * 0: WORD len (0 marks end) [ includes the 4 byte header length ]
94 * (stringentry i of a subentry refers to the ID 'firstentry+i')
96 * Yes, ANSI strings in win32 resources. Go figure.
99 /**********************************************************************
100 * load_messageA (internal)
102 static INT
load_messageA( HMODULE instance
, UINT id
, WORD lang
,
103 LPSTR buffer
, INT buflen
)
107 PMESSAGE_RESOURCE_DATA mrd
;
108 PMESSAGE_RESOURCE_BLOCK mrb
;
109 PMESSAGE_RESOURCE_ENTRY mre
;
112 //TRACE("instance = %08lx, id = %08lx, buffer = %p, length = %ld\n", (DWORD)instance, (DWORD)id, buffer, (DWORD)buflen);
114 /*FIXME: I am not sure about the '1' ... But I've only seen those entries*/
115 hrsrc
= FindResourceExW(instance
,RT_MESSAGETABLEW
,(LPWSTR
)1,lang
);
116 if (!hrsrc
) return 0;
117 hmem
= LoadResource( instance
, hrsrc
);
120 mrd
= (PMESSAGE_RESOURCE_DATA
)LockResource(hmem
);
122 mrb
= &(mrd
->Blocks
[0]);
123 for (i
=mrd
->NumberOfBlocks
;i
--;) {
124 if ((id
>=mrb
->LowId
) && (id
<=mrb
->HighId
)) {
125 mre
= (PMESSAGE_RESOURCE_ENTRY
)(((char*)mrd
)+mrb
->OffsetToEntries
);
136 mre
= (PMESSAGE_RESOURCE_ENTRY
)(((char*)mre
)+mre
->Length
);
139 //TRACE(" - strlen=%d\n",slen);
140 i
= min(buflen
- 1, slen
);
144 if (mre
->Flags
& MESSAGE_RESOURCE_UNICODE
)
145 WideCharToMultiByte( CP_ACP
, 0, (LPWSTR
)mre
->Text
, -1, buffer
, i
, NULL
, NULL
);
147 lstrcpynA(buffer
, (LPSTR
)mre
->Text
, i
);
156 //TRACE("'%s' copied !\n", buffer);
157 //TRACE("'%s'\n", buffer);
163 /**********************************************************************
164 * load_messageW (internal)
166 static INT
load_messageW( HMODULE instance
, UINT id
, WORD lang
,
167 LPWSTR buffer
, INT buflen
)
170 LPSTR buffer2
= NULL
;
171 if (buffer
&& buflen
)
172 buffer2
= HeapAlloc( GetProcessHeap(), 0, buflen
);
173 retval
= load_messageA(instance
,id
,lang
,buffer2
,buflen
);
177 lstrcpynAtoW( buffer
, buffer2
, buflen
);
178 retval
= lstrlenW( buffer
);
180 HeapFree( GetProcessHeap(), 0, buffer2
);
187 /***********************************************************************
188 * FormatMessageA (KERNEL32.@)
189 * FIXME: missing wrap,
193 DWORD WINAPI
FormatMessageA(
202 LPDWORD args
=(LPDWORD
)_args
;
203 #if defined(__i386__) || defined(__sparc__)
204 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
208 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
211 HMODULE hmodule
= (HMODULE
)lpSource
;
214 //TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n", dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
215 if ((dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
216 &&((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
)
217 || (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
))) return 0;
219 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
)
220 //FIXME("line wrapping (%lu) not supported.\n", width);
222 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
224 from
= RtlAllocateHeap(RtlGetProcessHeap(), 0, lstrlenA((LPSTR
)lpSource
)+1 );
225 strcpy( from
, (LPSTR
)lpSource
);
230 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
232 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
233 if ((!bufsize
) && (!dwLanguageId
)) {
234 bufsize
=load_messageA(hmodule
,dwMessageId
,
235 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
236 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
237 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
238 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
239 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
240 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
241 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
242 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
243 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
246 if ((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
) && (!bufsize
))
248 hmodule
= GetModuleHandleA("kernel32");
249 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
250 if ((!bufsize
) && (!dwLanguageId
)) {
251 bufsize
=load_messageA(hmodule
,dwMessageId
,
252 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
253 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
254 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
255 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
256 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
257 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
258 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
259 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
260 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
265 TRACE("FormatMessageA: dwFlags=%#x hmodule=%#x dwMessageId=%#x - could not load message\n", dwFlags
, hmodule
, dwMessageId
);
266 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND
);
270 from
= RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize
+ 1 );
271 load_messageA(hmodule
,dwMessageId
,dwLanguageId
,from
,bufsize
+1);
273 target
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, 100);
277 #define ADD_TO_T(c) do { \
279 if (t-target == talloced) {\
280 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
281 t = target+talloced;\
288 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
) {
296 char *fmtstr
,*x
,*lastf
;
307 case '1':case '2':case '3':case '4':case '5':
308 case '6':case '7':case '8':case '9':
311 case '0':case '1':case '2':case '3':
312 case '4':case '5':case '6':case '7':
315 insertnr
=insertnr
*10+*f
-'0';
324 if (NULL
!=(x
=strchr(f
,'!'))) {
326 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f
)+2);
327 sprintf(fmtstr
,"%%%s",f
);
330 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f
)+2);
331 sprintf(fmtstr
,"%%%s",f
);
332 f
+=lstrlenA(f
); /*at \0*/
336 fmtstr
= RtlAllocateHeap(RtlGetProcessHeap(),0,3);
337 strcpy( fmtstr
, "%s" );
343 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
344 argliststart
=args
+insertnr
-1;
346 argliststart
=(*(DWORD
**)args
)+insertnr
-1;
348 /* FIXME: precision and width components are not handled correctly */
349 if ( (strcmp(fmtstr
, "%ls") == 0) || (strcmp(fmtstr
,"%S") == 0) ) {
350 sz
= WideCharToMultiByte( CP_ACP
, 0, *(WCHAR
**)argliststart
, -1, NULL
, 0, NULL
, NULL
);
351 b
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sz
);
352 WideCharToMultiByte( CP_ACP
, 0, *(WCHAR
**)argliststart
, -1, b
, sz
, NULL
, NULL
);
354 b
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sz
= 1000);
355 /* CMF - This makes a BIG assumption about va_list */
356 //TRACE("A BIG assumption\n");
357 _vsnprintf(b
, sz
, fmtstr
, (va_list) argliststart
);
359 for (x
=b
; *x
; x
++) ADD_TO_T(*x
);
361 RtlFreeHeap(RtlGetProcessHeap(),0,b
);
363 /* NULL args - copy formatstr
366 while ((lastf
<f
)&&(*lastf
)) {
370 RtlFreeHeap(GetProcessHeap(),0,fmtstr
);
417 talloced
= lstrlenA(target
)+1;
418 if (nSize
&& talloced
<nSize
) {
419 target
= (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY
,target
,nSize
);
421 //TRACE("-- %s\n",debugstr_a(target));
422 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
423 *((LPVOID
*)lpBuffer
) = (LPVOID
)LocalAlloc(GMEM_ZEROINIT
,max(nSize
, talloced
));
424 memcpy(*(LPSTR
*)lpBuffer
,target
,talloced
);
426 lstrcpynA(lpBuffer
,target
,nSize
);
428 RtlFreeHeap(RtlGetProcessHeap(),0,target
);
429 if (from
) RtlFreeHeap(RtlGetProcessHeap(),0,from
);
430 //TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? lstrlenA(*(LPSTR*)lpBuffer):lstrlenA(lpBuffer));
431 return (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ?
432 lstrlenA(*(LPSTR
*)lpBuffer
):
435 FIXME("FormatMessageA: unimplemented\n");
437 #endif /* __i386__ */
442 /***********************************************************************
443 * FormatMessageW (KERNEL32.@)
447 DWORD WINAPI
FormatMessageW(
456 LPDWORD args
=(LPDWORD
)_args
;
457 #if defined(__i386__) || defined(__sparc__)
458 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
462 DWORD width
= dwFlags
& FORMAT_MESSAGE_MAX_WIDTH_MASK
;
465 HMODULE hmodule
= (HMODULE
)lpSource
;
468 //TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n", dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
469 if ((dwFlags
& FORMAT_MESSAGE_FROM_STRING
)
470 &&((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
)
471 || (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
))) return 0;
473 if (width
&& width
!= FORMAT_MESSAGE_MAX_WIDTH_MASK
) {
474 //FIXME("line wrapping not supported.\n");
477 if (dwFlags
& FORMAT_MESSAGE_FROM_STRING
) {
478 from
= (LPSTR
)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR
)lpSource
);
483 if (dwFlags
& FORMAT_MESSAGE_FROM_HMODULE
)
485 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
486 if ((!bufsize
) && (!dwLanguageId
)) {
487 bufsize
=load_messageA(hmodule
,dwMessageId
,
488 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
489 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
490 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
491 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
492 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
493 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
494 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
495 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
496 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
499 if ((dwFlags
& FORMAT_MESSAGE_FROM_SYSTEM
) && (!bufsize
))
501 hmodule
= GetModuleHandleA("kernel32");
502 bufsize
=load_messageA(hmodule
,dwMessageId
,dwLanguageId
,NULL
,100);
503 if ((!bufsize
) && (!dwLanguageId
)) {
504 bufsize
=load_messageA(hmodule
,dwMessageId
,
505 MAKELANGID(LANG_NEUTRAL
,SUBLANG_NEUTRAL
),NULL
,100);
506 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
507 MAKELANGID(LANG_NEUTRAL
,SUBLANG_DEFAULT
),NULL
,100);
508 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
509 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
510 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
511 MAKELANGID(LANG_NEUTRAL
,SUBLANG_SYS_DEFAULT
),NULL
,100);
512 if (!bufsize
) bufsize
=load_messageA(hmodule
,dwMessageId
,
513 MAKELANGID(LANG_ENGLISH
,SUBLANG_ENGLISH_US
),NULL
,100);
518 TRACE("FormatMessageW: dwFlags=%#x hmodule=%#x dwMessageId=%#x - could not load message\n", dwFlags
, hmodule
, dwMessageId
);
519 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND
);
523 from
= RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize
+ 1 );
524 load_messageA(hmodule
,dwMessageId
,dwLanguageId
,from
,bufsize
+1);
526 target
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, 100 );
530 #define ADD_TO_T(c) do {\
532 if (t-target == talloced) {\
533 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
534 t = target+talloced;\
541 if (dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
) {
549 char *fmtstr
,*sprintfbuf
,*x
;
560 case '1':case '2':case '3':case '4':case '5':
561 case '6':case '7':case '8':case '9':
564 case '0':case '1':case '2':case '3':
565 case '4':case '5':case '6':case '7':
568 insertnr
=insertnr
*10+*f
-'0';
577 if (NULL
!=(x
=strchr(f
,'!'))) {
579 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(), 0, lstrlenA(f
)+2);
580 sprintf(fmtstr
,"%%%s",f
);
583 fmtstr
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f
));
584 sprintf(fmtstr
,"%%%s",f
);
585 f
+=lstrlenA(f
); /*at \0*/
589 fmtstr
= RtlAllocateHeap(RtlGetProcessHeap(),0,3);
590 strcpy( fmtstr
, "%s" );
592 if (dwFlags
& FORMAT_MESSAGE_ARGUMENT_ARRAY
)
593 argliststart
=args
+insertnr
-1;
595 argliststart
=(*(DWORD
**)args
)+insertnr
-1;
597 if (fmtstr
[lstrlenA(fmtstr
)-1]=='s' && argliststart
[0]) {
600 xarr
[0]=(DWORD
)HEAP_strdupWtoA(RtlGetProcessHeap(),0,(LPWSTR
)(*(argliststart
+0)));
601 /* possible invalid pointers */
602 xarr
[1]=*(argliststart
+1);
603 xarr
[2]=*(argliststart
+2);
604 sprintfbuf
=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenW((LPWSTR
)argliststart
[0])*2+1);
606 /* CMF - This makes a BIG assumption about va_list */
607 vsprintf(sprintfbuf
, fmtstr
, (va_list) xarr
);
608 RtlFreeHeap(RtlGetProcessHeap(), 0, (LPVOID
) xarr
[0]);
610 sprintfbuf
=RtlAllocateHeap(RtlGetProcessHeap(),0,100);
612 /* CMF - This makes a BIG assumption about va_list */
613 vsprintf(sprintfbuf
, fmtstr
, (va_list) argliststart
);
619 RtlFreeHeap(RtlGetProcessHeap(),0,sprintfbuf
);
620 RtlFreeHeap(RtlGetProcessHeap(),0,fmtstr
);
667 talloced
= lstrlenA(target
)+1;
668 if (nSize
&& talloced
<nSize
)
669 target
= (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY
,target
,nSize
);
670 if (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) {
671 /* nSize is the MINIMUM size */
672 DWORD len
= MultiByteToWideChar( CP_ACP
, 0, target
, -1, NULL
, 0 );
673 *((LPVOID
*)lpBuffer
) = (LPVOID
)LocalAlloc(GMEM_ZEROINIT
,len
*sizeof(WCHAR
));
674 MultiByteToWideChar( CP_ACP
, 0, target
, -1, *(LPWSTR
*)lpBuffer
, len
);
678 if (nSize
> 0 && !MultiByteToWideChar( CP_ACP
, 0, target
, -1, lpBuffer
, nSize
))
679 lpBuffer
[nSize
-1] = 0;
681 RtlFreeHeap(RtlGetProcessHeap(),0,target
);
682 if (from
) RtlFreeHeap(RtlGetProcessHeap(),0,from
);
683 return (dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) ?
684 lstrlenW(*(LPWSTR
*)lpBuffer
):
687 FIXME("FormatMessageW: unimplemented\n");
689 #endif /* __i386__ */