Fixed FormatMessage[A/W] to not suck.
[reactos.git] / reactos / lib / kernel32 / misc / errormsg.c
1 /* $Id: errormsg.c,v 1.15 2003/11/19 22:19:17 sedwards Exp $
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 <ddk/ntddk.h>
27 #include <kernel32/kernel32.h>
28
29 #define TRACE DPRINT
30 #define FIXME DPRINT
31
32 /* strdup macros */
33 /* DO NOT USE IT!! it will go away soon */
34 inline static LPSTR HEAP_strdupWtoA( HANDLE heap, DWORD flags, LPCWSTR str )
35 {
36 LPSTR ret;
37 INT len;
38
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 );
43 return ret;
44 }
45
46 /* INTERNAL */
47
48 #include <stdio.h>
49 #include <string.h>
50
51 typedef struct tagMESSAGE_RESOURCE_ENTRY {
52 WORD Length;
53 WORD Flags;
54 BYTE Text[1];
55 } MESSAGE_RESOURCE_ENTRY,*PMESSAGE_RESOURCE_ENTRY;
56 #define MESSAGE_RESOURCE_UNICODE 0x0001
57
58 typedef struct tagMESSAGE_RESOURCE_BLOCK {
59 DWORD LowId;
60 DWORD HighId;
61 DWORD OffsetToEntries;
62 } MESSAGE_RESOURCE_BLOCK,*PMESSAGE_RESOURCE_BLOCK;
63
64 typedef struct tagMESSAGE_RESOURCE_DATA {
65 DWORD NumberOfBlocks;
66 MESSAGE_RESOURCE_BLOCK Blocks[ 1 ];
67 } MESSAGE_RESOURCE_DATA,*PMESSAGE_RESOURCE_DATA;
68
69
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)
76
77 /* Messages...used by FormatMessage32* (KERNEL32.something)
78 *
79 * They can be specified either directly or using a message ID and
80 * loading them from the resource.
81 *
82 * The resourcedata has following format:
83 * start:
84 * 0: DWORD nrofentries
85 * nrofentries * subentry:
86 * 0: DWORD firstentry
87 * 4: DWORD lastentry
88 * 8: DWORD offset from start to the stringentries
89 *
90 * (lastentry-firstentry) * stringentry:
91 * 0: WORD len (0 marks end) [ includes the 4 byte header length ]
92 * 2: WORD flags
93 * 4: CHAR[len-4]
94 * (stringentry i of a subentry refers to the ID 'firstentry+i')
95 *
96 * Yes, ANSI strings in win32 resources. Go figure.
97 */
98
99 /**********************************************************************
100 * load_messageA (internal)
101 */
102 static INT load_messageA( HMODULE instance, UINT id, WORD lang,
103 LPSTR buffer, INT buflen )
104 {
105 HGLOBAL hmem;
106 HRSRC hrsrc;
107 PMESSAGE_RESOURCE_DATA mrd;
108 PMESSAGE_RESOURCE_BLOCK mrb;
109 PMESSAGE_RESOURCE_ENTRY mre;
110 int i,slen;
111
112 //TRACE("instance = %08lx, id = %08lx, buffer = %p, length = %ld\n", (DWORD)instance, (DWORD)id, buffer, (DWORD)buflen);
113
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 );
118 if (!hmem) return 0;
119
120 mrd = (PMESSAGE_RESOURCE_DATA)LockResource(hmem);
121 mre = NULL;
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);
126 id -= mrb->LowId;
127 break;
128 }
129 mrb++;
130 }
131 if (!mre)
132 return 0;
133 for (i=id;i--;) {
134 if (!mre->Length)
135 return 0;
136 mre = (PMESSAGE_RESOURCE_ENTRY)(((char*)mre)+mre->Length);
137 }
138 slen=mre->Length;
139 //TRACE(" - strlen=%d\n",slen);
140 i = min(buflen - 1, slen);
141 if (buffer == NULL)
142 return slen;
143 if (i>0) {
144 if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
145 WideCharToMultiByte( CP_ACP, 0, (LPWSTR)mre->Text, -1, buffer, i, NULL, NULL );
146 else
147 lstrcpynA(buffer, (LPSTR)mre->Text, i);
148 buffer[i]=0;
149 } else {
150 if (buflen>1) {
151 buffer[0]=0;
152 return 0;
153 }
154 }
155 if (buffer) {
156 //TRACE("'%s' copied !\n", buffer);
157 //TRACE("'%s'\n", buffer);
158 }
159 return i;
160 }
161
162 #if 0 /* FIXME */
163 /**********************************************************************
164 * load_messageW (internal)
165 */
166 static INT load_messageW( HMODULE instance, UINT id, WORD lang,
167 LPWSTR buffer, INT buflen )
168 {
169 INT retval;
170 LPSTR buffer2 = NULL;
171 if (buffer && buflen)
172 buffer2 = HeapAlloc( GetProcessHeap(), 0, buflen );
173 retval = load_messageA(instance,id,lang,buffer2,buflen);
174 if (buffer)
175 {
176 if (retval) {
177 lstrcpynAtoW( buffer, buffer2, buflen );
178 retval = lstrlenW( buffer );
179 }
180 HeapFree( GetProcessHeap(), 0, buffer2 );
181 }
182 return retval;
183 }
184 #endif
185
186
187 /***********************************************************************
188 * FormatMessageA (KERNEL32.@)
189 * FIXME: missing wrap,
190 *
191 * @implemented
192 */
193 DWORD WINAPI FormatMessageA(
194 DWORD dwFlags,
195 LPCVOID lpSource,
196 DWORD dwMessageId,
197 DWORD dwLanguageId,
198 LPSTR lpBuffer,
199 DWORD nSize,
200 va_list* _args )
201 {
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 */
205 LPSTR target,t;
206 DWORD talloced;
207 LPSTR from,f;
208 DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
209 BOOL eos = FALSE;
210 INT bufsize;
211 HMODULE hmodule = (HMODULE)lpSource;
212 CHAR ch;
213
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;
218
219 if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
220 //FIXME("line wrapping (%lu) not supported.\n", width);
221 from = NULL;
222 if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
223 {
224 from = RtlAllocateHeap(RtlGetProcessHeap(), 0, lstrlenA((LPSTR)lpSource)+1 );
225 strcpy( from, (LPSTR)lpSource );
226 }
227 else {
228 bufsize = 0;
229
230 if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
231 {
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);
244 }
245 }
246 if ((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) && (!bufsize))
247 {
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);
261 }
262 }
263
264 if (!bufsize) {
265 TRACE("FormatMessageA: dwFlags=%#x hmodule=%#x dwMessageId=%#x - could not load message\n", dwFlags, hmodule, dwMessageId);
266 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
267 return 0;
268 }
269
270 from = RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize + 1 );
271 load_messageA(hmodule,dwMessageId,dwLanguageId,from,bufsize+1);
272 }
273 target = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 100);
274 t = target;
275 talloced= 100;
276
277 #define ADD_TO_T(c) do { \
278 *t++=c;\
279 if (t-target == talloced) {\
280 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
281 t = target+talloced;\
282 talloced*=2;\
283 }\
284 } while (0)
285
286 if (from) {
287 f=from;
288 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
289 while (*f && !eos)
290 ADD_TO_T(*f++);
291 }
292 else {
293 while (*f && !eos) {
294 if (*f=='%') {
295 int insertnr;
296 char *fmtstr,*x,*lastf;
297 DWORD *argliststart;
298
299 fmtstr = NULL;
300 lastf = f;
301 f++;
302 if (!*f) {
303 ADD_TO_T('%');
304 continue;
305 }
306 switch (*f) {
307 case '1':case '2':case '3':case '4':case '5':
308 case '6':case '7':case '8':case '9':
309 insertnr=*f-'0';
310 switch (f[1]) {
311 case '0':case '1':case '2':case '3':
312 case '4':case '5':case '6':case '7':
313 case '8':case '9':
314 f++;
315 insertnr=insertnr*10+*f-'0';
316 f++;
317 break;
318 default:
319 f++;
320 break;
321 }
322 if (*f=='!') {
323 f++;
324 if (NULL!=(x=strchr(f,'!'))) {
325 *x='\0';
326 fmtstr=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f)+2);
327 sprintf(fmtstr,"%%%s",f);
328 f=x+1;
329 } else {
330 fmtstr=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f)+2);
331 sprintf(fmtstr,"%%%s",f);
332 f+=lstrlenA(f); /*at \0*/
333 }
334 } else {
335 if(!args) break;
336 fmtstr = RtlAllocateHeap(RtlGetProcessHeap(),0,3);
337 strcpy( fmtstr, "%s" );
338 }
339 if (args) {
340 int sz;
341 LPSTR b;
342
343 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
344 argliststart=args+insertnr-1;
345 else
346 argliststart=(*(DWORD**)args)+insertnr-1;
347
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);
353 } else {
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);
358 }
359 for (x=b; *x; x++) ADD_TO_T(*x);
360
361 RtlFreeHeap(RtlGetProcessHeap(),0,b);
362 } else {
363 /* NULL args - copy formatstr
364 * (probably wrong)
365 */
366 while ((lastf<f)&&(*lastf)) {
367 ADD_TO_T(*lastf++);
368 }
369 }
370 RtlFreeHeap(GetProcessHeap(),0,fmtstr);
371 break;
372 case 'n':
373 ADD_TO_T('\r');
374 ADD_TO_T('\n');
375 f++;
376 break;
377 case '0':
378 eos = TRUE;
379 f++;
380 break;
381 default:
382 ADD_TO_T(*f++);
383 break;
384 }
385 } else {
386 ch = *f;
387 f++;
388 if (ch == '\r') {
389 if (*f == '\n')
390 f++;
391 if(width)
392 ADD_TO_T(' ');
393 else
394 {
395 ADD_TO_T('\r');
396 ADD_TO_T('\n');
397 }
398 } else {
399 if (ch == '\n')
400 {
401 if(width)
402 ADD_TO_T(' ');
403 else
404 {
405 ADD_TO_T('\r');
406 ADD_TO_T('\n');
407 }
408 }
409 else
410 ADD_TO_T(ch);
411 }
412 }
413 }
414 }
415 *t='\0';
416 }
417 talloced = lstrlenA(target)+1;
418 if (nSize && talloced<nSize) {
419 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
420 }
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);
425 } else {
426 lstrcpynA(lpBuffer,target,nSize);
427 }
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):
433 lstrlenA(lpBuffer);
434 #else
435 FIXME("FormatMessageA: unimplemented\n");
436 return 0;
437 #endif /* __i386__ */
438 }
439 #undef ADD_TO_T
440
441
442 /***********************************************************************
443 * FormatMessageW (KERNEL32.@)
444 *
445 * @implemented
446 */
447 DWORD WINAPI FormatMessageW(
448 DWORD dwFlags,
449 LPCVOID lpSource,
450 DWORD dwMessageId,
451 DWORD dwLanguageId,
452 LPWSTR lpBuffer,
453 DWORD nSize,
454 va_list* _args)
455 {
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 */
459 LPSTR target,t;
460 DWORD talloced;
461 LPSTR from,f;
462 DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
463 BOOL eos = FALSE;
464 INT bufsize;
465 HMODULE hmodule = (HMODULE)lpSource;
466 CHAR ch;
467
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;
472
473 if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK) {
474 //FIXME("line wrapping not supported.\n");
475 }
476 from = NULL;
477 if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
478 from = (LPSTR)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)lpSource);
479 }
480 else {
481 bufsize = 0;
482
483 if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
484 {
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);
497 }
498 }
499 if ((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) && (!bufsize))
500 {
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);
514 }
515 }
516
517 if (!bufsize) {
518 TRACE("FormatMessageW: dwFlags=%#x hmodule=%#x dwMessageId=%#x - could not load message\n", dwFlags, hmodule, dwMessageId);
519 SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
520 return 0;
521 }
522
523 from = RtlAllocateHeap(RtlGetProcessHeap(), 0, bufsize + 1 );
524 load_messageA(hmodule,dwMessageId,dwLanguageId,from,bufsize+1);
525 }
526 target = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 100 );
527 t = target;
528 talloced= 100;
529
530 #define ADD_TO_T(c) do {\
531 *t++=c;\
532 if (t-target == talloced) {\
533 target = (char*)RtlReAllocateHeap(RtlGetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
534 t = target+talloced;\
535 talloced*=2;\
536 } \
537 } while (0)
538
539 if (from) {
540 f=from;
541 if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
542 while (*f && !eos)
543 ADD_TO_T(*f++);
544 }
545 else {
546 while (*f && !eos) {
547 if (*f=='%') {
548 int insertnr;
549 char *fmtstr,*sprintfbuf,*x;
550 DWORD *argliststart;
551
552 fmtstr = NULL;
553 f++;
554 if (!*f) {
555 ADD_TO_T('%');
556 continue;
557 }
558
559 switch (*f) {
560 case '1':case '2':case '3':case '4':case '5':
561 case '6':case '7':case '8':case '9':
562 insertnr=*f-'0';
563 switch (f[1]) {
564 case '0':case '1':case '2':case '3':
565 case '4':case '5':case '6':case '7':
566 case '8':case '9':
567 f++;
568 insertnr=insertnr*10+*f-'0';
569 f++;
570 break;
571 default:
572 f++;
573 break;
574 }
575 if (*f=='!') {
576 f++;
577 if (NULL!=(x=strchr(f,'!'))) {
578 *x='\0';
579 fmtstr=RtlAllocateHeap(RtlGetProcessHeap(), 0, lstrlenA(f)+2);
580 sprintf(fmtstr,"%%%s",f);
581 f=x+1;
582 } else {
583 fmtstr=RtlAllocateHeap(RtlGetProcessHeap(),0,lstrlenA(f));
584 sprintf(fmtstr,"%%%s",f);
585 f+=lstrlenA(f); /*at \0*/
586 }
587 } else {
588 if(!args) break;
589 fmtstr = RtlAllocateHeap(RtlGetProcessHeap(),0,3);
590 strcpy( fmtstr, "%s" );
591 }
592 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
593 argliststart=args+insertnr-1;
594 else
595 argliststart=(*(DWORD**)args)+insertnr-1;
596
597 if (fmtstr[lstrlenA(fmtstr)-1]=='s' && argliststart[0]) {
598 DWORD xarr[3];
599
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);
605
606 /* CMF - This makes a BIG assumption about va_list */
607 vsprintf(sprintfbuf, fmtstr, (va_list) xarr);
608 RtlFreeHeap(RtlGetProcessHeap(), 0, (LPVOID) xarr[0]);
609 } else {
610 sprintfbuf=RtlAllocateHeap(RtlGetProcessHeap(),0,100);
611
612 /* CMF - This makes a BIG assumption about va_list */
613 vsprintf(sprintfbuf, fmtstr, (va_list) argliststart);
614 }
615 x=sprintfbuf;
616 while (*x) {
617 ADD_TO_T(*x++);
618 }
619 RtlFreeHeap(RtlGetProcessHeap(),0,sprintfbuf);
620 RtlFreeHeap(RtlGetProcessHeap(),0,fmtstr);
621 break;
622 case 'n':
623 ADD_TO_T('\r');
624 ADD_TO_T('\n');
625 f++;
626 break;
627 case '0':
628 eos = TRUE;
629 f++;
630 break;
631 default:
632 ADD_TO_T(*f++);
633 break;
634 }
635 } else {
636 ch = *f;
637 f++;
638 if (ch == '\r') {
639 if (*f == '\n')
640 f++;
641 if(width)
642 ADD_TO_T(' ');
643 else
644 {
645 ADD_TO_T('\r');
646 ADD_TO_T('\n');
647 }
648 } else {
649 if (ch == '\n')
650 {
651 if(width)
652 ADD_TO_T(' ');
653 else
654 {
655 ADD_TO_T('\r');
656 ADD_TO_T('\n');
657 }
658 }
659 else
660 ADD_TO_T(ch);
661 }
662 }
663 }
664 }
665 *t='\0';
666 }
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 );
675 }
676 else
677 {
678 if (nSize > 0 && !MultiByteToWideChar( CP_ACP, 0, target, -1, lpBuffer, nSize ))
679 lpBuffer[nSize-1] = 0;
680 }
681 RtlFreeHeap(RtlGetProcessHeap(),0,target);
682 if (from) RtlFreeHeap(RtlGetProcessHeap(),0,from);
683 return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
684 lstrlenW(*(LPWSTR*)lpBuffer):
685 lstrlenW(lpBuffer);
686 #else
687 FIXME("FormatMessageW: unimplemented\n");
688 return 0;
689 #endif /* __i386__ */
690 }
691 #undef ADD_TO_T