Handle FORMAT_MESSAGE_FROM_HMODULE and FORMAT_MESSAGE_FROM_SYSTEM both
[wine] / dlls / kernel / format_msg.c
1 /*
2  * FormatMessage implementation
3  *
4  * Copyright 1996 Marcus Meissner
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "wine/unicode.h"
32
33 #include "heap.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(resource);
37
38
39 /* Messages...used by FormatMessage32* (KERNEL32.something)
40  *
41  * They can be specified either directly or using a message ID and
42  * loading them from the resource.
43  *
44  * The resourcedata has following format:
45  * start:
46  * 0: DWORD nrofentries
47  * nrofentries * subentry:
48  *      0: DWORD firstentry
49  *      4: DWORD lastentry
50  *      8: DWORD offset from start to the stringentries
51  *
52  * (lastentry-firstentry) * stringentry:
53  * 0: WORD len (0 marks end)    [ includes the 4 byte header length ]
54  * 2: WORD flags
55  * 4: CHAR[len-4]
56  *      (stringentry i of a subentry refers to the ID 'firstentry+i')
57  *
58  * Yes, ANSI strings in win32 resources. Go figure.
59  */
60
61 /**********************************************************************
62  *      load_messageA           (internal)
63  */
64 static INT load_messageA( HMODULE instance, UINT id, WORD lang,
65                           LPSTR buffer, INT buflen )
66 {
67     HGLOBAL     hmem;
68     HRSRC       hrsrc;
69     PMESSAGE_RESOURCE_DATA      mrd;
70     PMESSAGE_RESOURCE_BLOCK     mrb;
71     PMESSAGE_RESOURCE_ENTRY     mre;
72     int         i,slen;
73
74     TRACE("instance = %08lx, id = %08lx, buffer = %p, length = %ld\n", (DWORD)instance, (DWORD)id, buffer, (DWORD)buflen);
75
76     /*FIXME: I am not sure about the '1' ... But I've only seen those entries*/
77     hrsrc = FindResourceExW(instance,RT_MESSAGETABLEW,(LPWSTR)1,lang);
78     if (!hrsrc) return 0;
79     hmem = LoadResource( instance, hrsrc );
80     if (!hmem) return 0;
81
82     mrd = (PMESSAGE_RESOURCE_DATA)LockResource(hmem);
83     mre = NULL;
84     mrb = &(mrd->Blocks[0]);
85     for (i=mrd->NumberOfBlocks;i--;) {
86         if ((id>=mrb->LowId) && (id<=mrb->HighId)) {
87             mre = (PMESSAGE_RESOURCE_ENTRY)(((char*)mrd)+mrb->OffsetToEntries);
88             id  -= mrb->LowId;
89             break;
90         }
91         mrb++;
92     }
93     if (!mre)
94         return 0;
95     for (i=id;i--;) {
96         if (!mre->Length)
97                 return 0;
98         mre = (PMESSAGE_RESOURCE_ENTRY)(((char*)mre)+mre->Length);
99     }
100     slen=mre->Length;
101     TRACE("     - strlen=%d\n",slen);
102     i = min(buflen - 1, slen);
103     if (buffer == NULL)
104         return slen;
105     if (i>0) {
106         if (mre->Flags & MESSAGE_RESOURCE_UNICODE)
107             WideCharToMultiByte( CP_ACP, 0, (LPWSTR)mre->Text, -1, buffer, i, NULL, NULL );
108         else
109             lstrcpynA(buffer, (LPSTR)mre->Text, i);
110         buffer[i]=0;
111     } else {
112         if (buflen>1) {
113             buffer[0]=0;
114             return 0;
115         }
116     }
117     if (buffer)
118             TRACE("'%s' copied !\n", buffer);
119     return i;
120 }
121
122 #if 0  /* FIXME */
123 /**********************************************************************
124  *      load_messageW   (internal)
125  */
126 static INT load_messageW( HMODULE instance, UINT id, WORD lang,
127                           LPWSTR buffer, INT buflen )
128 {
129     INT retval;
130     LPSTR buffer2 = NULL;
131     if (buffer && buflen)
132         buffer2 = HeapAlloc( GetProcessHeap(), 0, buflen );
133     retval = load_messageA(instance,id,lang,buffer2,buflen);
134     if (buffer)
135     {
136         if (retval) {
137             lstrcpynAtoW( buffer, buffer2, buflen );
138             retval = strlenW( buffer );
139         }
140         HeapFree( GetProcessHeap(), 0, buffer2 );
141     }
142     return retval;
143 }
144 #endif
145
146
147 /***********************************************************************
148  *           FormatMessageA   (KERNEL32.@)
149  * FIXME: missing wrap,
150  */
151 DWORD WINAPI FormatMessageA(
152         DWORD   dwFlags,
153         LPCVOID lpSource,
154         DWORD   dwMessageId,
155         DWORD   dwLanguageId,
156         LPSTR   lpBuffer,
157         DWORD   nSize,
158         va_list* _args )
159 {
160     LPDWORD args=(LPDWORD)_args;
161 #if defined(__i386__) || defined(__sparc__)
162 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
163     LPSTR       target,t;
164     DWORD       talloced;
165     LPSTR       from,f;
166     DWORD       width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
167     BOOL    eos = FALSE;
168     INT bufsize;
169     HMODULE     hmodule = (HMODULE)lpSource;
170     CHAR        ch;
171
172     TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
173           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
174     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
175         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
176            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
177
178     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
179         FIXME("line wrapping (%lu) not supported.\n", width);
180     from = NULL;
181     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
182     {
183         from = HeapAlloc( GetProcessHeap(), 0, strlen((LPSTR)lpSource)+1 );
184         strcpy( from, (LPSTR)lpSource );
185     }
186     else {
187         bufsize = 0;
188
189         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
190         {
191            bufsize=load_messageA(hmodule,dwMessageId,dwLanguageId,NULL,100);
192            if ((!bufsize) && (!dwLanguageId)) {
193                 bufsize=load_messageA(hmodule,dwMessageId,
194                                       MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),NULL,100);
195                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
196                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),NULL,100);
197                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
198                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
199                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
200                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
201                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
202                                                     MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),NULL,100);
203             }
204         }
205         if ((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) && (!bufsize))
206         {
207            hmodule = GetModuleHandleA("kernel32");
208            bufsize=load_messageA(hmodule,dwMessageId,dwLanguageId,NULL,100);
209            if ((!bufsize) && (!dwLanguageId)) {
210                 bufsize=load_messageA(hmodule,dwMessageId,
211                                       MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),NULL,100);
212                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
213                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),NULL,100);
214                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
215                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
216                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
217                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
218                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
219                                                     MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),NULL,100);
220             }
221         }
222
223         if (!bufsize) {
224             SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
225             return 0;
226         }
227  
228         from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
229         load_messageA(hmodule,dwMessageId,dwLanguageId,from,bufsize+1);
230     }
231     target      = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
232     t   = target;
233     talloced= 100;
234
235 #define ADD_TO_T(c) do { \
236         *t++=c;\
237         if (t-target == talloced) {\
238             target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
239             t = target+talloced;\
240             talloced*=2;\
241       }\
242 } while (0)
243
244     if (from) {
245         f=from;
246         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
247             while (*f && !eos)
248                 ADD_TO_T(*f++);
249         }
250         else {
251             while (*f && !eos) {
252                 if (*f=='%') {
253                     int insertnr;
254                     char *fmtstr,*x,*lastf;
255                     DWORD *argliststart;
256
257                     fmtstr = NULL;
258                     lastf = f;
259                     f++;
260                     if (!*f) {
261                         ADD_TO_T('%');
262                         continue;
263                     }
264                     switch (*f) {
265                     case '1':case '2':case '3':case '4':case '5':
266                     case '6':case '7':case '8':case '9':
267                         insertnr=*f-'0';
268                         switch (f[1]) {
269                         case '0':case '1':case '2':case '3':
270                         case '4':case '5':case '6':case '7':
271                         case '8':case '9':
272                             f++;
273                             insertnr=insertnr*10+*f-'0';
274                             f++;
275                             break;
276                         default:
277                             f++;
278                             break;
279                         }
280                         if (*f=='!') {
281                             f++;
282                             if (NULL!=(x=strchr(f,'!'))) {
283                                 *x='\0';
284                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
285                                 sprintf(fmtstr,"%%%s",f);
286                                 f=x+1;
287                             } else {
288                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
289                                 sprintf(fmtstr,"%%%s",f);
290                                 f+=strlen(f); /*at \0*/
291                             }
292                         } else {
293                             if(!args) break;
294                             fmtstr = HeapAlloc(GetProcessHeap(),0,3);
295                             strcpy( fmtstr, "%s" );
296                         }
297                         if (args) {
298                             int sz;
299                             LPSTR b;
300
301                             if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
302                                 argliststart=args+insertnr-1;
303                             else
304                                 argliststart=(*(DWORD**)args)+insertnr-1;
305
306                                 /* FIXME: precision and width components are not handled correctly */
307                             if ( (strcmp(fmtstr, "%ls") == 0) || (strcmp(fmtstr,"%S") == 0) ) {
308                                 sz = WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, NULL, 0, NULL, NULL);
309                                 b = HeapAlloc(GetProcessHeap(), 0, sz);
310                                 WideCharToMultiByte( CP_ACP, 0, *(WCHAR**)argliststart, -1, b, sz, NULL, NULL);
311                             } else {
312                                 b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 1000);
313                                 /* CMF - This makes a BIG assumption about va_list */
314                                 TRACE("A BIG assumption\n");
315                                 vsnprintf(b, sz, fmtstr, (va_list) argliststart);
316                             }
317                             for (x=b; *x; x++) ADD_TO_T(*x);
318
319                             HeapFree(GetProcessHeap(),0,b);
320                         } else {
321                                 /* NULL args - copy formatstr
322                                  * (probably wrong)
323                                  */
324                             while ((lastf<f)&&(*lastf)) {
325                                 ADD_TO_T(*lastf++);
326                             }
327                         }
328                         HeapFree(GetProcessHeap(),0,fmtstr);
329                         break;
330                     case 'n':
331                         ADD_TO_T('\r');
332                         ADD_TO_T('\n');
333                         f++;
334                         break;
335                     case '0':
336                         eos = TRUE;
337                         f++;
338                         break;
339                     default:
340                         ADD_TO_T(*f++);
341                         break;
342                     }
343                 } else {
344                     ch = *f;
345                     f++;
346                     if (ch == '\r') {
347                         if (*f == '\n')
348                             f++;
349                         if(width)
350                             ADD_TO_T(' ');
351                         else
352                         {
353                             ADD_TO_T('\r');
354                             ADD_TO_T('\n');
355                         }
356                     } else {
357                         if (ch == '\n')
358                         {
359                             if(width)
360                                 ADD_TO_T(' ');
361                             else
362                             {
363                                 ADD_TO_T('\r');
364                                 ADD_TO_T('\n');
365                             }
366                         }
367                         else
368                             ADD_TO_T(ch);
369                     }
370                 }
371             }
372         }
373         *t='\0';
374     }
375     talloced = strlen(target)+1;
376     if (nSize && talloced<nSize) {
377         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
378     }
379     TRACE("-- %s\n",debugstr_a(target));
380     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
381         *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(GMEM_ZEROINIT,max(nSize, talloced));
382         memcpy(*(LPSTR*)lpBuffer,target,talloced);
383     } else {
384         lstrcpynA(lpBuffer,target,nSize);
385     }
386     HeapFree(GetProcessHeap(),0,target);
387     if (from) HeapFree(GetProcessHeap(),0,from);
388     TRACE("-- returning %d\n", (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?  strlen(*(LPSTR*)lpBuffer):strlen(lpBuffer));
389     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
390         strlen(*(LPSTR*)lpBuffer):
391             strlen(lpBuffer);
392 #else
393     return 0;
394 #endif /* __i386__ */
395 }
396 #undef ADD_TO_T
397
398
399 /***********************************************************************
400  *           FormatMessageW   (KERNEL32.@)
401  */
402 DWORD WINAPI FormatMessageW(
403         DWORD   dwFlags,
404         LPCVOID lpSource,
405         DWORD   dwMessageId,
406         DWORD   dwLanguageId,
407         LPWSTR  lpBuffer,
408         DWORD   nSize,
409         va_list* _args )
410 {
411     LPDWORD args=(LPDWORD)_args;
412 #if defined(__i386__) || defined(__sparc__)
413 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
414     LPSTR target,t;
415     DWORD talloced;
416     LPSTR from,f;
417     DWORD width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
418     BOOL eos = FALSE;
419     INT bufsize;
420     HMODULE hmodule = (HMODULE)lpSource;
421     CHAR ch;
422
423     TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
424           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
425     if ((dwFlags & FORMAT_MESSAGE_FROM_STRING)
426         &&((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
427            || (dwFlags & FORMAT_MESSAGE_FROM_HMODULE))) return 0;
428
429     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK)
430         FIXME("line wrapping not supported.\n");
431     from = NULL;
432     if (dwFlags & FORMAT_MESSAGE_FROM_STRING) {
433         from = HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)lpSource);
434     }
435     else {
436         bufsize = 0;
437
438         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE)
439         {
440            bufsize=load_messageA(hmodule,dwMessageId,dwLanguageId,NULL,100);
441            if ((!bufsize) && (!dwLanguageId)) {
442                 bufsize=load_messageA(hmodule,dwMessageId,
443                                       MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),NULL,100);
444                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
445                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),NULL,100);
446                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
447                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
448                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
449                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
450                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
451                                                     MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),NULL,100);
452             }
453         }
454         if ((dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) && (!bufsize))
455         {
456            hmodule = GetModuleHandleA("kernel32");
457            bufsize=load_messageA(hmodule,dwMessageId,dwLanguageId,NULL,100);
458            if ((!bufsize) && (!dwLanguageId)) {
459                 bufsize=load_messageA(hmodule,dwMessageId,
460                                       MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),NULL,100);
461                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
462                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),NULL,100);
463                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
464                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
465                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
466                                                     MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),NULL,100);
467                 if (!bufsize) bufsize=load_messageA(hmodule,dwMessageId,
468                                                     MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),NULL,100);
469             }
470         }
471
472         if (!bufsize) {
473             SetLastError (ERROR_RESOURCE_LANG_NOT_FOUND);
474             return 0;
475         }
476  
477         from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
478         load_messageA(hmodule,dwMessageId,dwLanguageId,from,bufsize+1);
479     }
480     target = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 );
481     t = target;
482     talloced= 100;
483
484 #define ADD_TO_T(c)  do {\
485     *t++=c;\
486     if (t-target == talloced) {\
487         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
488         t = target+talloced;\
489         talloced*=2;\
490     } \
491 } while (0)
492
493     if (from) {
494         f=from;
495         if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) {
496             while (*f && !eos)
497                 ADD_TO_T(*f++);
498         }
499         else {
500             while (*f && !eos) {
501                 if (*f=='%') {
502                     int insertnr;
503                     char *fmtstr,*sprintfbuf,*x;
504                     DWORD *argliststart;
505
506                     fmtstr = NULL;
507                     f++;
508                     if (!*f) {
509                         ADD_TO_T('%');
510                         continue;
511                     }
512
513                     switch (*f) {
514                     case '1':case '2':case '3':case '4':case '5':
515                     case '6':case '7':case '8':case '9':
516                         insertnr=*f-'0';
517                         switch (f[1]) {
518                         case '0':case '1':case '2':case '3':
519                         case '4':case '5':case '6':case '7':
520                         case '8':case '9':
521                             f++;
522                             insertnr=insertnr*10+*f-'0';
523                             f++;
524                             break;
525                         default:
526                             f++;
527                             break;
528                         }
529                         if (*f=='!') {
530                             f++;
531                             if (NULL!=(x=strchr(f,'!'))) {
532                                 *x='\0';
533                                 fmtstr=HeapAlloc( GetProcessHeap(), 0, strlen(f)+2);
534                                 sprintf(fmtstr,"%%%s",f);
535                                 f=x+1;
536                             } else {
537                                 fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f));
538                                 sprintf(fmtstr,"%%%s",f);
539                                 f+=strlen(f); /*at \0*/
540                             }
541                         } else {
542                             if(!args) break;
543                             fmtstr = HeapAlloc( GetProcessHeap(),0,3);
544                             strcpy( fmtstr, "%s" );
545                         }
546                         if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
547                             argliststart=args+insertnr-1;
548                         else
549                             argliststart=(*(DWORD**)args)+insertnr-1;
550
551                         if (fmtstr[strlen(fmtstr)-1]=='s' && argliststart[0]) {
552                             DWORD xarr[3];
553
554                             xarr[0]=(DWORD)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)(*(argliststart+0)));
555                             /* possible invalid pointers */
556                             xarr[1]=*(argliststart+1);
557                             xarr[2]=*(argliststart+2);
558                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,strlenW((LPWSTR)argliststart[0])*2+1);
559
560                             /* CMF - This makes a BIG assumption about va_list */
561                             vsprintf(sprintfbuf, fmtstr, (va_list) xarr);
562                             HeapFree(GetProcessHeap(), 0, (LPVOID) xarr[0]);
563                         } else {
564                             sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
565
566                             /* CMF - This makes a BIG assumption about va_list */
567                             vsprintf(sprintfbuf, fmtstr, (va_list) argliststart);
568                         }
569                         x=sprintfbuf;
570                         while (*x) {
571                             ADD_TO_T(*x++);
572                         }
573                         HeapFree(GetProcessHeap(),0,sprintfbuf);
574                         HeapFree(GetProcessHeap(),0,fmtstr);
575                         break;
576                     case 'n':
577                         ADD_TO_T('\r');
578                         ADD_TO_T('\n');
579                         f++;
580                         break;
581                     case '0':
582                         eos = TRUE;
583                         f++;
584                         break;
585                     default:
586                         ADD_TO_T(*f++);
587                         break;
588                     }
589                 } else {
590                     ch = *f;
591                     f++;
592                     if (ch == '\r') {
593                         if (*f == '\n')
594                             f++;
595                         if(width)
596                             ADD_TO_T(' ');
597                         else
598                         {
599                             ADD_TO_T('\r');
600                             ADD_TO_T('\n');
601                         }
602                     } else {
603                         if (ch == '\n')
604                         {
605                             if(width)
606                                 ADD_TO_T(' ');
607                             else
608                             {
609                                 ADD_TO_T('\r');
610                                 ADD_TO_T('\n');
611                             }
612                         }
613                         else
614                             ADD_TO_T(ch);
615                     }
616                 }
617             }
618         }
619         *t='\0';
620     }
621     talloced = strlen(target)+1;
622     if (nSize && talloced<nSize)
623         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
624     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
625         /* nSize is the MINIMUM size */
626         DWORD len = MultiByteToWideChar( CP_ACP, 0, target, -1, NULL, 0 );
627         *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(GMEM_ZEROINIT,len*sizeof(WCHAR));
628         MultiByteToWideChar( CP_ACP, 0, target, -1, *(LPWSTR*)lpBuffer, len );
629     }
630     else
631     {
632         if (nSize > 0 && !MultiByteToWideChar( CP_ACP, 0, target, -1, lpBuffer, nSize ))
633             lpBuffer[nSize-1] = 0;
634     }
635     HeapFree(GetProcessHeap(),0,target);
636     if (from) HeapFree(GetProcessHeap(),0,from);
637     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ?
638         strlenW(*(LPWSTR*)lpBuffer):
639             strlenW(lpBuffer);
640 #else
641     return 0;
642 #endif /* __i386__ */
643 }
644 #undef ADD_TO_T