Merged msacm and msacm32 dlls.
[wine] / misc / lstr.c
1 /*
2  * String functions
3  *
4  * Copyright 1993 Yngvi Sigurjonsson (yngvi@hafro.is)
5  * Copyright 1996 Marcus Meissner
6  */
7
8 #include "config.h"
9
10 #include <stdarg.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15
16 #ifdef HAVE_WCTYPE_H
17 # include <wctype.h>
18 #else
19 # define iswalnum(c) isalnum(c)
20 # define iswalpha(c) isalpha(c)
21 # define iswupper(c) isupper(c)
22 # define iswlower(c) islower(c)
23 #endif  /* HAVE_WCTYPE_H */
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "wine/winbase16.h"
30 #include "wine/winuser16.h"
31 #include "wine/unicode.h"
32 #include "winnls.h"
33 #include "task.h"
34 #include "heap.h"
35 #include "ldt.h"
36 #include "stackframe.h"
37 #include "module.h"
38 #include "debugtools.h"
39
40 DEFAULT_DEBUG_CHANNEL(resource);
41
42 extern const WORD OLE2NLS_CT_CType3_LUT[]; /* FIXME: does not belong here */
43
44 /* Funny to divide them between user and kernel. */
45
46 /***********************************************************************
47  *              IsCharAlpha (USER.433)
48  */
49 BOOL16 WINAPI IsCharAlpha16(CHAR ch)
50 {
51   return isalpha(ch);   /* This is probably not right for NLS */
52 }
53
54 /***********************************************************************
55  *              IsCharAlphaNumeric (USER.434)
56  */
57 BOOL16 WINAPI IsCharAlphaNumeric16(CHAR ch)
58 {
59     return isalnum(ch);
60 }
61
62 /***********************************************************************
63  *              IsCharUpper (USER.435)
64  */
65 BOOL16 WINAPI IsCharUpper16(CHAR ch)
66 {
67   return isupper(ch);
68 }
69
70 /***********************************************************************
71  *              IsCharLower (USER.436)
72  */
73 BOOL16 WINAPI IsCharLower16(CHAR ch)
74 {
75   return islower(ch);
76 }
77
78 /***********************************************************************
79  *           AnsiUpper16   (USER.431)
80  */
81 SEGPTR WINAPI AnsiUpper16( SEGPTR strOrChar )
82 {
83   /* I am not sure if the locale stuff works with toupper, but then again 
84      I am not sure if the Linux libc locale stuffs works at all */
85
86     /* uppercase only one char if strOrChar < 0x10000 */
87     if (HIWORD(strOrChar))
88     {
89         char *s;
90         for (s = PTR_SEG_TO_LIN(strOrChar); *s; s++) *s = toupper(*s);
91         return strOrChar;
92     }
93     else return toupper((char)strOrChar);
94 }
95
96
97 /***********************************************************************
98  *           AnsiUpperBuff16   (USER.437)
99  */
100 UINT16 WINAPI AnsiUpperBuff16( LPSTR str, UINT16 len )
101 {
102     UINT count = len ? len : 65536;
103     for (; count; count--, str++) *str = toupper(*str);
104     return len;
105 }
106
107 /***********************************************************************
108  *           AnsiLower16   (USER.432)
109  */
110 SEGPTR WINAPI AnsiLower16( SEGPTR strOrChar )
111 {
112   /* I am not sure if the locale stuff works with toupper, but then again 
113      I am not sure if the Linux libc locale stuffs works at all */
114
115     /* lowercase only one char if strOrChar < 0x10000 */
116     if (HIWORD(strOrChar))
117     {
118         char *s;
119         for (s = PTR_SEG_TO_LIN( strOrChar ); *s; s++) *s = tolower( *s );
120         return strOrChar;
121     }
122     else return tolower((char)strOrChar);
123 }
124
125
126 /***********************************************************************
127  *           AnsiLowerBuff16   (USER.438)
128  */
129 UINT16 WINAPI AnsiLowerBuff16( LPSTR str, UINT16 len )
130 {
131     UINT count = len ? len : 65536;
132     for (; count; count--, str++) *str = tolower(*str);
133     return len;
134 }
135
136
137 /***********************************************************************
138  *           AnsiNext16   (USER.472)
139  */
140 SEGPTR WINAPI AnsiNext16(SEGPTR current)
141 {
142     return (*(char *)PTR_SEG_TO_LIN(current)) ? current + 1 : current;
143 }
144
145
146 /***********************************************************************
147  *           AnsiPrev16   (USER.473)
148  */
149 SEGPTR WINAPI AnsiPrev16( SEGPTR start, SEGPTR current )
150 {
151     return (current == start) ? start : current - 1;
152 }
153
154
155 /***********************************************************************
156  *           CharNextA   (USER32.29)
157  */
158 LPSTR WINAPI CharNextA( LPCSTR ptr )
159 {
160     if (!*ptr) return (LPSTR)ptr;
161     if (IsDBCSLeadByte( *ptr ) && (*(ptr+1) != 0) ) return (LPSTR)(ptr + 2);
162     return (LPSTR)(ptr + 1);
163 }
164
165
166 /***********************************************************************
167  *           CharNextExA   (USER32.30)
168  */
169 LPSTR WINAPI CharNextExA( WORD codepage, LPCSTR ptr, DWORD flags )
170 {
171     if (!*ptr) return (LPSTR)ptr;
172     if (IsDBCSLeadByteEx( codepage, *ptr ) && (*(ptr+1) != 0) ) return (LPSTR)(ptr + 2);
173     return (LPSTR)(ptr + 1);
174 }
175
176
177 /***********************************************************************
178  *           CharNextExW   (USER32.31)
179  */
180 LPWSTR WINAPI CharNextExW(WORD codepage,LPCWSTR x,DWORD flags)
181 {
182     /* FIXME: add DBCS / codepage stuff */
183     if (*x) return (LPWSTR)(x+1);
184     else return (LPWSTR)x;
185 }
186
187 /***********************************************************************
188  *           CharNextW   (USER32.32)
189  */
190 LPWSTR WINAPI CharNextW(LPCWSTR x)
191 {
192     if (*x) return (LPWSTR)(x+1);
193     else return (LPWSTR)x;
194 }
195
196 /***********************************************************************
197  *           CharPrevA   (USER32.33)
198  */
199 LPSTR WINAPI CharPrevA( LPCSTR start, LPCSTR ptr )
200 {
201     while (*start && (start < ptr))
202     {
203         LPCSTR next = CharNextA( start );
204         if (next >= ptr) break;
205         start = next;
206     }
207     return (LPSTR)start;
208 }
209
210
211 /***********************************************************************
212  *           CharPrevExA   (USER32.34)
213  */
214 LPSTR WINAPI CharPrevExA( WORD codepage, LPCSTR start, LPCSTR ptr, DWORD flags )
215 {
216     while (*start && (start < ptr))
217     {
218         LPCSTR next = CharNextExA( codepage, start, flags );
219         if (next > ptr) break;
220         start = next;
221     }
222     return (LPSTR)start;
223 }
224
225
226 /***********************************************************************
227  *           CharPrevExW   (USER32.35)
228  */
229 LPWSTR WINAPI CharPrevExW(WORD codepage,LPCWSTR start,LPCWSTR x,DWORD flags)
230 {
231     /* FIXME: add DBCS / codepage stuff */
232     if (x>start) return (LPWSTR)(x-1);
233     else return (LPWSTR)x;
234 }
235
236 /***********************************************************************
237  *           CharPrevW   (USER32.36)
238  */
239 LPWSTR WINAPI CharPrevW(LPCWSTR start,LPCWSTR x)
240 {
241     if (x>start) return (LPWSTR)(x-1);
242     else return (LPWSTR)x;
243 }
244
245 /***********************************************************************
246  *           CharLowerA   (USER32.25)
247  * FIXME: handle current locale
248  */
249 LPSTR WINAPI CharLowerA(LPSTR x)
250 {
251     LPSTR       s;
252
253     if (HIWORD(x))
254     {
255         s=x;
256         while (*s)
257         {
258             *s=tolower(*s);
259             s++;
260         }
261         return x;
262     }
263     else return (LPSTR)tolower((char)(int)x);
264 }
265
266 /***********************************************************************
267  *           CharLowerBuffA   (USER32.26)
268  * FIXME: handle current locale
269  */
270 DWORD WINAPI CharLowerBuffA(LPSTR x,DWORD buflen)
271 {
272     DWORD done=0;
273
274     if (!x) return 0; /* YES */
275     while (*x && (buflen--))
276     {
277         *x=tolower(*x);
278         x++;
279         done++;
280     }
281     return done;
282 }
283
284 /***********************************************************************
285  *           CharLowerBuffW   (USER32.27)
286  */
287 DWORD WINAPI CharLowerBuffW(LPWSTR x,DWORD buflen)
288 {
289     DWORD done=0;
290
291     if (!x) return 0; /* YES */
292     while (*x && (buflen--))
293     {
294         *x=tolowerW(*x);
295         x++;
296         done++;
297     }
298     return done;
299 }
300
301 /***********************************************************************
302  *           CharLowerW   (USER32.28)
303  * FIXME: handle current locale
304  */
305 LPWSTR WINAPI CharLowerW(LPWSTR x)
306 {
307     if (HIWORD(x))
308     {
309         LPWSTR s = x;
310         while (*s)
311         {
312             *s = tolowerW(*s);
313             s++;
314         }
315         return x;
316     }
317     else return (LPWSTR)((UINT)tolowerW(LOWORD(x)));
318 }
319
320 /***********************************************************************
321  *           CharUpperA   (USER32.41)
322  * FIXME: handle current locale
323  */
324 LPSTR WINAPI CharUpperA(LPSTR x)
325 {
326     if (HIWORD(x))
327     {
328         LPSTR s = x;
329         while (*s)
330         {
331             *s=toupper(*s);
332             s++;
333         }
334         return x;
335     }
336     return (LPSTR)toupper((char)(int)x);
337 }
338
339 /***********************************************************************
340  *           CharUpperBuffA   (USER32.42)
341  * FIXME: handle current locale
342  */
343 DWORD WINAPI CharUpperBuffA(LPSTR x,DWORD buflen)
344 {
345     DWORD done=0;
346
347     if (!x) return 0; /* YES */
348     while (*x && (buflen--))
349     {
350         *x=toupper(*x);
351         x++;
352         done++;
353     }
354     return done;
355 }
356
357 /***********************************************************************
358  *           CharUpperBuffW   (USER32.43)
359  * FIXME: handle current locale
360  */
361 DWORD WINAPI CharUpperBuffW(LPWSTR x,DWORD buflen)
362 {
363     DWORD done=0;
364
365     if (!x) return 0; /* YES */
366     while (*x && (buflen--))
367     {
368         *x=toupperW(*x);
369         x++;
370         done++;
371     }
372     return done;
373 }
374
375 /***********************************************************************
376  *           CharUpperW   (USER32.44)
377  * FIXME: handle current locale
378  */
379 LPWSTR WINAPI CharUpperW(LPWSTR x)
380 {
381     if (HIWORD(x))
382     {
383         LPWSTR s = x;
384         while (*s)
385         {
386             *s = toupperW(*s);
387             s++;
388         }
389         return x;
390     }
391     else return (LPWSTR)((UINT)toupperW(LOWORD(x)));
392 }
393
394 /***********************************************************************
395  *           IsCharAlphaA   (USER32.331)
396  * FIXME: handle current locale
397  */
398 BOOL WINAPI IsCharAlphaA(CHAR x)
399 {
400     return (OLE2NLS_CT_CType3_LUT[(unsigned char)x] & C3_ALPHA);
401 }
402
403 /***********************************************************************
404  *           IsCharAlphaNumericA   (USER32.332)
405  * FIXME: handle current locale
406  */
407 BOOL WINAPI IsCharAlphaNumericA(CHAR x)
408 {
409     return IsCharAlphaA(x) || isdigit(x) ;
410 }
411
412 /***********************************************************************
413  *           IsCharAlphaNumericW   (USER32.333)
414  * FIXME: handle current locale
415  */
416 BOOL WINAPI IsCharAlphaNumericW(WCHAR x)
417 {
418     return iswalnum(x);
419 }
420
421 /***********************************************************************
422  *           IsCharAlphaW   (USER32.334)
423  * FIXME: handle current locale
424  */
425 BOOL WINAPI IsCharAlphaW(WCHAR x)
426 {
427     return iswalpha(x);
428 }
429
430 /***********************************************************************
431  *           IsCharLowerA   (USER32.335)
432  * FIXME: handle current locale
433  */
434 BOOL WINAPI IsCharLowerA(CHAR x)
435 {
436     return islower(x);
437 }
438
439 /***********************************************************************
440  *           IsCharLowerW   (USER32.336)
441  * FIXME: handle current locale
442  */
443 BOOL WINAPI IsCharLowerW(WCHAR x)
444 {
445     return iswlower(x);
446 }
447
448 /***********************************************************************
449  *           IsCharUpperA   (USER32.337)
450  * FIXME: handle current locale
451  */
452 BOOL WINAPI IsCharUpperA(CHAR x)
453 {
454     return isupper(x);
455 }
456
457 /***********************************************************************
458  *           IsCharUpperW   (USER32.338)
459  * FIXME: handle current locale
460  */
461 BOOL WINAPI IsCharUpperW(WCHAR x)
462 {
463     return iswupper(x);
464 }
465
466 /***********************************************************************
467  *           FormatMessage16   (USER.606)
468  */
469 DWORD WINAPI FormatMessage16(
470     DWORD   dwFlags,
471     SEGPTR lpSource, /*not always a valid pointer*/
472     WORD   dwMessageId,
473     WORD   dwLanguageId,
474     LPSTR  lpBuffer, /* *((HLOCAL16*)) for FORMAT_MESSAGE_ALLOCATE_BUFFER*/
475     WORD   nSize,
476     LPDWORD args /* va_list *args */
477 ) {
478 #ifdef __i386__
479 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
480     LPSTR       target,t;
481     DWORD       talloced;
482     LPSTR       from,f;
483     DWORD       width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
484     BOOL        eos = FALSE;
485     LPSTR       allocstring = NULL;
486
487     TRACE("(0x%lx,%lx,%d,0x%x,%p,%d,%p)\n",
488           dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
489     if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK) 
490         FIXME("line wrapping (%lu) not supported.\n", width);
491     from = NULL;
492     if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
493         from = HEAP_strdupA( GetProcessHeap(), 0, PTR_SEG_TO_LIN(lpSource));
494     if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) {
495         from = HeapAlloc( GetProcessHeap(),0,200 );
496         sprintf(from,"Systemmessage, messageid = 0x%08x\n",dwMessageId);
497     }
498     if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) {
499         INT16   bufsize;
500         HINSTANCE16 hinst16 = ((HMODULE)lpSource & 0xffff);
501
502         dwMessageId &= 0xFFFF;
503         bufsize=LoadString16(hinst16,dwMessageId,NULL,0);
504         if (bufsize) {
505             from = HeapAlloc( GetProcessHeap(), 0, bufsize +1);
506             LoadString16(hinst16,dwMessageId,from,bufsize+1);
507         }
508     }
509     target      = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
510     t   = target;
511     talloced= 100;
512
513 #define ADD_TO_T(c) \
514         *t++=c;\
515         if (t-target == talloced) {\
516                 target  = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
517                 t       = target+talloced;\
518                 talloced*=2;\
519         }
520
521     if (from) {
522         f=from;
523         while (*f && !eos) {
524             if (*f=='%') {
525                 int     insertnr;
526                 char    *fmtstr,*x,*lastf;
527                 DWORD   *argliststart;
528
529                 fmtstr = NULL;
530                 lastf = f;
531                 f++;
532                 if (!*f) {
533                     ADD_TO_T('%');
534                     continue;
535                 }
536                 switch (*f) {
537                 case '1':case '2':case '3':case '4':case '5':
538                 case '6':case '7':case '8':case '9':
539                     insertnr=*f-'0';
540                     switch (f[1]) {
541                     case '0':case '1':case '2':case '3':
542                     case '4':case '5':case '6':case '7':
543                     case '8':case '9':
544                         f++;
545                         insertnr=insertnr*10+*f-'0';
546                         f++;
547                         break;
548                     default:
549                         f++;
550                         break;
551                     }
552                     if (*f=='!') {
553                         f++;
554                         if (NULL!=(x=strchr(f,'!'))) {
555                             *x='\0';
556                             fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
557                             sprintf(fmtstr,"%%%s",f);
558                             f=x+1;
559                         } else {
560                             fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
561                             sprintf(fmtstr,"%%%s",f);
562                             f+=strlen(f); /*at \0*/
563                         }
564                     } else
565                         if(!args) 
566                             break;
567                         else
568                           fmtstr=HEAP_strdupA(GetProcessHeap(),0,"%s");
569                     if (args) {
570                         int     sz;
571                         LPSTR   b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 100);
572                         
573                         argliststart=args+insertnr-1;
574                        
575                         /* CMF - This makes a BIG assumption about va_list */
576                         while (wvsnprintf16(b, sz, fmtstr, (va_list) argliststart) < 0) {
577                             b = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, b, sz += 100);
578                         }
579                         for (x=b; *x; x++) ADD_TO_T(*x);
580                     } else {
581                         /* NULL args - copy formatstr 
582                          * (probably wrong)
583                          */
584                         while ((lastf<f)&&(*lastf)) {
585                             ADD_TO_T(*lastf++);
586                         }
587                     }
588                     HeapFree(GetProcessHeap(),0,fmtstr);
589                     break;
590                 case '0': /* Just stop processing format string */
591                     eos = TRUE;
592                     f++;
593                     break;
594                 case 'n': /* 16 bit version just outputs 'n' */
595                 default:
596                     ADD_TO_T(*f++);
597                     break;
598                 }
599             } else { /* '\n' or '\r' gets mapped to "\r\n" */
600                 if(*f == '\n' || *f == '\r') {
601                     if (width == 0) {
602                         ADD_TO_T('\r');
603                         ADD_TO_T('\n');
604                         if(*f++ == '\r' && *f == '\n')
605                             f++;
606                     }
607                 } else {
608                     ADD_TO_T(*f++);
609                 }
610             }
611         }
612         *t='\0';
613     }
614     talloced = strlen(target)+1;
615     if (nSize && talloced<nSize) {
616         target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
617     }
618     TRACE("-- %s\n",debugstr_a(target));
619     if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
620         /* nSize is the MINIMUM size */
621         *((HLOCAL16*)lpBuffer)=  LocalAlloc16(LPTR,talloced);
622         allocstring=PTR_SEG_OFF_TO_LIN(CURRENT_DS,*((HLOCAL16*)lpBuffer));
623         memcpy( allocstring,target,talloced);
624     } else
625         lstrcpynA(lpBuffer,target,nSize);
626     HeapFree(GetProcessHeap(),0,target);
627     if (from) HeapFree(GetProcessHeap(),0,from);
628     return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? 
629         strlen(allocstring):
630         strlen(lpBuffer);
631 #else
632         return 0;
633 #endif /* __i386__ */
634 }
635 #undef ADD_TO_T
636
637 /***********************************************************************
638  *           FormatMessageA   (KERNEL32.138)
639  * FIXME: missing wrap,FROM_SYSTEM message-loading,
640  */
641 DWORD WINAPI FormatMessageA(
642         DWORD   dwFlags,
643         LPCVOID lpSource,
644         DWORD   dwMessageId,
645         DWORD   dwLanguageId,
646         LPSTR   lpBuffer,
647         DWORD   nSize,
648         LPDWORD args /* va_list *args */
649 ) {
650 #ifdef __i386__
651 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
652         LPSTR   target,t;
653         DWORD   talloced;
654         LPSTR   from,f;
655         DWORD   width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
656         BOOL    eos = FALSE;
657
658         TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
659                      dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
660         if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK) 
661                 FIXME("line wrapping (%lu) not supported.\n", width);
662         from = NULL;
663         if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
664                 from = HEAP_strdupA( GetProcessHeap(), 0, (LPSTR)lpSource);
665         if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) {
666                 from = HeapAlloc( GetProcessHeap(),0,200 );
667                 sprintf(from,"Systemmessage, messageid = 0x%08lx\n",dwMessageId);
668         }
669         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) {
670                 INT     bufsize;
671
672                 dwMessageId &= 0xFFFF;
673                 bufsize=LoadMessageA((HMODULE)lpSource,dwMessageId,dwLanguageId,NULL,100);
674                 if (bufsize) {
675                         from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
676                         LoadMessageA((HMODULE)lpSource,dwMessageId,dwLanguageId,from,bufsize+1);
677                 }
678         }
679         target  = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
680         t       = target;
681         talloced= 100;
682
683 #define ADD_TO_T(c) \
684         *t++=c;\
685         if (t-target == talloced) {\
686                 target  = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
687                 t       = target+talloced;\
688                 talloced*=2;\
689         }
690
691         if (from) {
692                 f=from;
693                 while (*f && !eos) {
694                         if (*f=='%') {
695                                 int     insertnr;
696                                 char    *fmtstr,*x,*lastf;
697                                 DWORD   *argliststart;
698
699                                 fmtstr = NULL;
700                                 lastf = f;
701                                 f++;
702                                 if (!*f) {
703                                         ADD_TO_T('%');
704                                         continue;
705                                 }
706                                 switch (*f) {
707                                 case '1':case '2':case '3':case '4':case '5':
708                                 case '6':case '7':case '8':case '9':
709                                         insertnr=*f-'0';
710                                         switch (f[1]) {
711                                         case '0':case '1':case '2':case '3':
712                                         case '4':case '5':case '6':case '7':
713                                         case '8':case '9':
714                                                 f++;
715                                                 insertnr=insertnr*10+*f-'0';
716                                                 f++;
717                                                 break;
718                                         default:
719                                                 f++;
720                                                 break;
721                                         }
722                                         if (*f=='!') {
723                                                 f++;
724                                                 if (NULL!=(x=strchr(f,'!'))) {
725                                                         *x='\0';
726                                                         fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
727                                                         sprintf(fmtstr,"%%%s",f);
728                                                         f=x+1;
729                                                 } else {
730                                                         fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
731                                                         sprintf(fmtstr,"%%%s",f);
732                                                         f+=strlen(f); /*at \0*/
733                                                 }
734                                         } else
735                                                 if(!args) 
736                                                   break;
737                                         else
738                                                 fmtstr=HEAP_strdupA(GetProcessHeap(),0,"%s");
739                                         if (args) {
740                                                 int     sz;
741                                                 LPSTR   b = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz = 100);
742
743                                                 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
744                                                         argliststart=args+insertnr-1;
745                                                 else
746                                                         argliststart=(*(DWORD**)args)+insertnr-1;
747
748                                                 /* CMF - This makes a BIG assumption about va_list */
749                                                 while (wvsnprintfA(b, sz, fmtstr, (va_list) argliststart) < 0) {
750                                                         b = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, b, sz += 100);
751                                                 }
752                                                 for (x=b; *x; x++) ADD_TO_T(*x);
753
754                                                 HeapFree(GetProcessHeap(),0,b);
755                                         } else {
756                                                 /* NULL args - copy formatstr 
757                                                  * (probably wrong)
758                                                  */
759                                                 while ((lastf<f)&&(*lastf)) {
760                                                         ADD_TO_T(*lastf++);
761                                                 }
762                                         }
763                                         HeapFree(GetProcessHeap(),0,fmtstr);
764                                         break;
765                                 case 'n':
766                                         ADD_TO_T('\r');
767                                         ADD_TO_T('\n');
768                                         f++;
769                                         break;
770                                 case '0':
771                                         eos = TRUE;
772                                         f++;
773                                         break;
774                                 default:
775                                         ADD_TO_T(*f++)
776                                         break;
777                                 }
778                         } else { /* '\n' or '\r' gets mapped to "\r\n" */
779                             if(*f == '\n' || *f == '\r') {
780                                 if (width == 0) {
781                                     ADD_TO_T('\r');
782                                     ADD_TO_T('\n');
783                                     if(*f++ == '\r' && *f == '\n')
784                                         f++;
785                                 }
786                             } else {
787                                 ADD_TO_T(*f++);
788                             }
789                         }
790                 }
791                 *t='\0';
792         }
793         talloced = strlen(target)+1;
794         if (nSize && talloced<nSize) {
795                 target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
796         }
797         TRACE("-- %s\n",debugstr_a(target));
798         if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
799                 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(GMEM_ZEROINIT,max(nSize, talloced));
800                 memcpy(*(LPSTR*)lpBuffer,target,talloced);
801         } else {
802                 lstrcpynA(lpBuffer,target,nSize);
803         }
804         HeapFree(GetProcessHeap(),0,target);
805         if (from) HeapFree(GetProcessHeap(),0,from);
806         return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? 
807                         strlen(*(LPSTR*)lpBuffer):
808                         strlen(lpBuffer);
809 #else
810         return 0;
811 #endif /* __i386__ */
812 }
813 #undef ADD_TO_T
814
815
816 /***********************************************************************
817  *           FormatMessageW   (KERNEL32.138)
818  */
819 DWORD WINAPI FormatMessageW(
820         DWORD   dwFlags,
821         LPCVOID lpSource,
822         DWORD   dwMessageId,
823         DWORD   dwLanguageId,
824         LPWSTR  lpBuffer,
825         DWORD   nSize,
826         LPDWORD args /* va_list *args */
827 ) {
828 #ifdef __i386__
829 /* This implementation is completely dependant on the format of the va_list on x86 CPUs */
830         LPSTR   target,t;
831         DWORD   talloced;
832         LPSTR   from,f;
833         DWORD   width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
834         BOOL    eos = FALSE;
835
836         TRACE("(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
837                      dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args);
838         if (width && width != FORMAT_MESSAGE_MAX_WIDTH_MASK) 
839                 FIXME("line wrapping not supported.\n");
840         from = NULL;
841         if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
842                 from = HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)lpSource);
843         if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) {
844                 /* gather information from system message tables ... */
845                 from = HeapAlloc( GetProcessHeap(),0,200 );
846                 sprintf(from,"Systemmessage, messageid = 0x%08lx\n",dwMessageId);
847         }
848         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) {
849                 INT     bufsize;
850
851                 dwMessageId &= 0xFFFF;
852                 bufsize=LoadMessageA((HMODULE)lpSource,dwMessageId,dwLanguageId,NULL,100);
853                 if (bufsize)
854                 {
855                     from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
856                     LoadMessageA((HMODULE)lpSource,dwMessageId,dwLanguageId,from,bufsize+1);
857                 }
858         }
859         target  = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100 );
860         t       = target;
861         talloced= 100;
862
863 #define ADD_TO_T(c) \
864         *t++=c;\
865         if (t-target == talloced) {\
866                 target  = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
867                 t       = target+talloced;\
868                 talloced*=2;\
869         }
870
871         if (from) {
872                 f=from;
873                 while (*f && !eos) {
874                         if (*f=='%') {
875                                 int     insertnr;
876                                 char    *fmtstr,*sprintfbuf,*x;
877                                 DWORD   *argliststart;
878
879                                 fmtstr = NULL;
880                                 f++;
881                                 if (!*f) {
882                                         ADD_TO_T('%');
883                                         continue;
884                                 }
885                                 switch (*f) {
886                                 case '1':case '2':case '3':case '4':case '5':
887                                 case '6':case '7':case '8':case '9':
888                                         insertnr=*f-'0';
889                                         switch (f[1]) {
890                                         case '0':case '1':case '2':case '3':
891                                         case '4':case '5':case '6':case '7':
892                                         case '8':case '9':
893                                                 f++;
894                                                 insertnr=insertnr*10+*f-'0';
895                                                 f++;
896                                                 break;
897                                         default:
898                                                 f++;
899                                                 break;
900                                         }
901                                         if (*f=='!') {
902                                                 f++;
903                                                 if (NULL!=(x=strchr(f,'!')))
904                                                 {
905                                                     *x='\0';
906                                                     fmtstr=HeapAlloc( GetProcessHeap(), 0, strlen(f)+2);
907                                                         sprintf(fmtstr,"%%%s",f);
908                                                         f=x+1;
909                                                 } else {
910                                                         fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f));
911                                                         sprintf(fmtstr,"%%%s",f);
912                                                         f+=strlen(f); /*at \0*/
913                                                 }
914                                         } else
915                                                 if(!args)
916                                                   break;
917                                         else
918                                                 fmtstr=HEAP_strdupA( GetProcessHeap(),0,"%s");
919                                         if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
920                                                 argliststart=args+insertnr-1;
921                                         else
922                                                 argliststart=(*(DWORD**)args)+insertnr-1;
923
924                                         if (fmtstr[strlen(fmtstr)-1]=='s' && argliststart[0]) {
925                                                 DWORD   xarr[3];
926
927                                                 xarr[0]=(DWORD)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)(*(argliststart+0)));
928                                                 /* possible invalid pointers */
929                                                 xarr[1]=*(argliststart+1);
930                                                 xarr[2]=*(argliststart+2);
931                                                 sprintfbuf=HeapAlloc(GetProcessHeap(),0,lstrlenW((LPWSTR)argliststart[0])*2+1);
932
933                                                 /* CMF - This makes a BIG assumption about va_list */
934                                                 vsprintf(sprintfbuf, fmtstr, (va_list) xarr);
935                                         } else {
936                                                 sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
937
938                                                 /* CMF - This makes a BIG assumption about va_list */
939                                                 wvsprintfA(sprintfbuf, fmtstr, (va_list) argliststart);
940                                         }
941                                         x=sprintfbuf;
942                                         while (*x) {
943                                                 ADD_TO_T(*x++);
944                                         }
945                                         HeapFree(GetProcessHeap(),0,sprintfbuf);
946                                         HeapFree(GetProcessHeap(),0,fmtstr);
947                                         break;
948                                 case 'n':
949                                         ADD_TO_T('\r');
950                                         ADD_TO_T('\n');
951                                         f++;
952                                         break;
953                                 case '0':
954                                         eos = TRUE;
955                                         f++;
956                                         break;
957                                 default:
958                                         ADD_TO_T(*f++)
959                                         break;
960                                 }
961                         } else { /* '\n' or '\r' gets mapped to "\r\n" */
962                             if(*f == '\n' || *f == '\r') {
963                                 if (width == 0) {
964                                     ADD_TO_T('\r');
965                                     ADD_TO_T('\n');
966                                     if(*f++ == '\r' && *f == '\n')
967                                        f++;
968                                 }
969                             } else {
970                                 ADD_TO_T(*f++);
971                             }
972                         }
973                 }
974                 *t='\0';
975         }
976         talloced = strlen(target)+1;
977         if (nSize && talloced<nSize)
978                 target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
979         if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
980                 /* nSize is the MINIMUM size */
981                 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc(GMEM_ZEROINIT,talloced*2+2);
982                 lstrcpynAtoW(*(LPWSTR*)lpBuffer,target,talloced);
983         } else
984                 lstrcpynAtoW(lpBuffer,target,nSize);
985         HeapFree(GetProcessHeap(),0,target);
986         if (from) HeapFree(GetProcessHeap(),0,from);
987         return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? 
988                         lstrlenW(*(LPWSTR*)lpBuffer):
989                         lstrlenW(lpBuffer);
990 #else
991         return 0;
992 #endif /* __i386__ */
993 }
994 #undef ADD_TO_T