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