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