Release 980301
[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 <stdio.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include "config.h"
14
15 #ifdef HAVE_WCTYPE_H
16 # include <wctype.h>
17 #else
18 # define towlower(c) tolower(c)
19 # define towupper(c) toupper(c)
20 # define iswalnum(c) isalnum(c)
21 # define iswalpha(c) isalpha(c)
22 # define iswupper(c) isupper(c)
23 # define iswlower(c) islower(c)
24 #endif  /* HAVE_WCTYPE_H */
25
26
27 #include "windows.h"
28 #include "winnt.h"      /* HEAP_ macros */
29 #include "task.h"
30 #include "heap.h"
31 #include "ldt.h"
32 #include "stackframe.h"
33 #include "module.h"
34 #include "debug.h"
35
36 /* Funny to divide them between user and kernel. */
37
38 /* be careful: always use functions from wctype.h if character > 255 */
39
40 /***********************************************************************
41  *              IsCharAlpha (USER.433)
42  */
43 BOOL16 WINAPI IsCharAlpha16(CHAR ch)
44 {
45   return isalpha(ch);   /* This is probably not right for NLS */
46 }
47
48 /***********************************************************************
49  *              IsCharAlphanumeric (USER.434)
50  */
51 BOOL16 WINAPI IsCharAlphaNumeric16(CHAR ch)
52 {
53     return isalnum(ch);
54 }
55
56 /***********************************************************************
57  *              IsCharUpper (USER.435)
58  */
59 BOOL16 WINAPI IsCharUpper16(CHAR ch)
60 {
61   return isupper(ch);
62 }
63
64 /***********************************************************************
65  *              IsCharLower (USER.436)
66  */
67 BOOL16 WINAPI IsCharLower16(CHAR ch)
68 {
69   return islower(ch);
70 }
71
72 /***********************************************************************
73  *           AnsiUpper16   (USER.431)
74  */
75 SEGPTR WINAPI AnsiUpper16( SEGPTR strOrChar )
76 {
77   /* I am not sure if the locale stuff works with toupper, but then again 
78      I am not sure if the Linux libc locale stuffs works at all */
79
80     /* uppercase only one char if strOrChar < 0x10000 */
81     if (HIWORD(strOrChar))
82     {
83         char *s;
84         for (s = PTR_SEG_TO_LIN(strOrChar); *s; s++) *s = toupper(*s);
85         return strOrChar;
86     }
87     else return toupper((char)strOrChar);
88 }
89
90
91 /***********************************************************************
92  *           AnsiUpperBuff16   (USER.437)
93  */
94 UINT16 WINAPI AnsiUpperBuff16( LPSTR str, UINT16 len )
95 {
96     UINT32 count = len ? len : 65536;
97     for (; count; count--, str++) *str = toupper(*str);
98     return len;
99 }
100
101 /***********************************************************************
102  *           AnsiLower16   (USER.432)
103  */
104 SEGPTR WINAPI AnsiLower16( 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     /* lowercase 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 = tolower( *s );
114         return strOrChar;
115     }
116     else return tolower((char)strOrChar);
117 }
118
119
120 /***********************************************************************
121  *           AnsiLowerBuff16   (USER.438)
122  */
123 UINT16 WINAPI AnsiLowerBuff16( LPSTR str, UINT16 len )
124 {
125     UINT32 count = len ? len : 65536;
126     for (; count; count--, str++) *str = tolower(*str);
127     return len;
128 }
129
130
131 /***********************************************************************
132  *           AnsiNext16   (USER.472)
133  */
134 SEGPTR WINAPI AnsiNext16(SEGPTR current)
135 {
136     return (*(char *)PTR_SEG_TO_LIN(current)) ? current + 1 : current;
137 }
138
139
140 /***********************************************************************
141  *           AnsiPrev16   (USER.473)
142  */
143 SEGPTR WINAPI AnsiPrev16( SEGPTR start, SEGPTR current )
144 {
145     return (current == start) ? start : current - 1;
146 }
147
148
149 /***********************************************************************
150  *           OutputDebugString16   (KERNEL.115)
151  */
152 void WINAPI OutputDebugString16( LPCSTR str )
153 {
154     char *module;
155     char *p, *buffer = HeapAlloc( GetProcessHeap(), 0, strlen(str)+2 );
156     /* Remove CRs */
157     for (p = buffer; *str; str++) if (*str != '\r') *p++ = *str;
158     *p = '\0';
159     if ((p > buffer) && (p[-1] == '\n')) p[1] = '\0'; /* Remove trailing \n */
160     module = MODULE_GetModuleName( GetCurrentTask() );
161     fprintf( stderr, "OutputDebugString: %s says '%s'\n",
162              module ? module : "???", buffer );
163     HeapFree( GetProcessHeap(), 0, buffer );
164 }
165
166
167 /***********************************************************************
168  *           OutputDebugString32A   (KERNEL32
169  */
170 void WINAPI OutputDebugString32A( LPCSTR str )
171 {
172     OutputDebugString16( str );
173 }
174
175
176
177 /***********************************************************************
178  *           OutputDebugString32W   (KERNEL32
179  */
180 void WINAPI OutputDebugString32W( LPCWSTR str )
181 {
182     LPSTR p = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
183     OutputDebugString32A( p );
184     HeapFree( GetProcessHeap(), 0, p );
185 }
186
187
188
189 /***********************************************************************
190  *           CharNext32A   (USER32.28)
191  */
192 LPSTR WINAPI CharNext32A( LPCSTR ptr )
193 {
194     if (!*ptr) return (LPSTR)ptr;
195     if (IsDBCSLeadByte32( *ptr )) return (LPSTR)(ptr + 2);
196     return (LPSTR)(ptr + 1);
197 }
198
199
200 /***********************************************************************
201  *           CharNextEx32A   (USER32.29)
202  */
203 LPSTR WINAPI CharNextEx32A( WORD codepage, LPCSTR ptr, DWORD flags )
204 {
205     if (!*ptr) return (LPSTR)ptr;
206     if (IsDBCSLeadByteEx( codepage, *ptr )) return (LPSTR)(ptr + 2);
207     return (LPSTR)(ptr + 1);
208 }
209
210
211 /***********************************************************************
212  *           CharNextExW   (USER32.30)
213  */
214 LPWSTR WINAPI CharNextEx32W(WORD codepage,LPCWSTR x,DWORD flags)
215 {
216     /* FIXME: add DBCS / codepage stuff */
217     if (*x) return (LPWSTR)(x+1);
218     else return (LPWSTR)x;
219 }
220
221 /***********************************************************************
222  *           CharNextW   (USER32.31)
223  */
224 LPWSTR WINAPI CharNext32W(LPCWSTR x)
225 {
226     if (*x) return (LPWSTR)(x+1);
227     else return (LPWSTR)x;
228 }
229
230 /***********************************************************************
231  *           CharPrev32A   (USER32.32)
232  */
233 LPSTR WINAPI CharPrev32A( LPCSTR start, LPCSTR ptr )
234 {
235     while (*start && (start < ptr))
236     {
237         LPCSTR next = CharNext32A( start );
238         if (next >= ptr) break;
239         start = next;
240     }
241     return (LPSTR)start;
242 }
243
244
245 /***********************************************************************
246  *           CharPrevEx32A   (USER32.33)
247  */
248 LPSTR WINAPI CharPrevEx32A( WORD codepage, LPCSTR start, LPCSTR ptr, DWORD flags )
249 {
250     while (*start && (start < ptr))
251     {
252         LPCSTR next = CharNextEx32A( codepage, start, flags );
253         if (next > ptr) break;
254         start = next;
255     }
256     return (LPSTR)start;
257 }
258
259
260 /***********************************************************************
261  *           CharPrevExW   (USER32.34)
262  */
263 LPWSTR WINAPI CharPrevEx32W(WORD codepage,LPCWSTR start,LPCWSTR x,DWORD flags)
264 {
265     /* FIXME: add DBCS / codepage stuff */
266     if (x>start) return (LPWSTR)(x-1);
267     else return (LPWSTR)x;
268 }
269
270 /***********************************************************************
271  *           CharPrevW   (USER32.35)
272  */
273 LPWSTR WINAPI CharPrev32W(LPCWSTR start,LPCWSTR x)
274 {
275     if (x>start) return (LPWSTR)(x-1);
276     else return (LPWSTR)x;
277 }
278
279 /***********************************************************************
280  *           CharLowerA   (USER32.24)
281  * FIXME: handle current locale
282  */
283 LPSTR WINAPI CharLower32A(LPSTR x)
284 {
285     LPSTR       s;
286
287     if (HIWORD(x))
288     {
289         s=x;
290         while (*s)
291         {
292             *s=tolower(*s);
293             s++;
294         }
295         return x;
296     }
297     else return (LPSTR)tolower((char)(int)x);
298 }
299
300 /***********************************************************************
301  *           CharLowerBuffA   (USER32.25)
302  * FIXME: handle current locale
303  */
304 DWORD WINAPI CharLowerBuff32A(LPSTR x,DWORD buflen)
305 {
306     DWORD done=0;
307
308     if (!x) return 0; /* YES */
309     while (*x && (buflen--))
310     {
311         *x=tolower(*x);
312         x++;
313         done++;
314     }
315     return done;
316 }
317
318 /***********************************************************************
319  *           CharLowerBuffW   (USER32.26)
320  * FIXME: handle current locale
321  */
322 DWORD WINAPI CharLowerBuff32W(LPWSTR x,DWORD buflen)
323 {
324     DWORD done=0;
325
326     if (!x) return 0; /* YES */
327     while (*x && (buflen--))
328     {
329         *x=towlower(*x);
330         x++;
331         done++;
332     }
333     return done;
334 }
335
336 /***********************************************************************
337  *           CharLowerW   (USER32.27)
338  * FIXME: handle current locale
339  */
340 LPWSTR WINAPI CharLower32W(LPWSTR x)
341 {
342     if (HIWORD(x))
343     {
344         LPWSTR s = x;
345         while (*s)
346         {
347             *s=towlower(*s);
348             s++;
349         }
350         return x;
351     }
352     else return (LPWSTR)towlower(LOWORD(x));
353 }
354
355 /***********************************************************************
356  *           CharUpper32A   (USER32.40)
357  * FIXME: handle current locale
358  */
359 LPSTR WINAPI CharUpper32A(LPSTR x)
360 {
361     if (HIWORD(x))
362     {
363         LPSTR s = x;
364         while (*s)
365         {
366             *s=toupper(*s);
367             s++;
368         }
369         return x;
370     }
371     return (LPSTR)toupper((char)(int)x);
372 }
373
374 /***********************************************************************
375  *           CharUpperBuffA   (USER32.41)
376  * FIXME: handle current locale
377  */
378 DWORD WINAPI CharUpperBuff32A(LPSTR x,DWORD buflen)
379 {
380     DWORD done=0;
381
382     if (!x) return 0; /* YES */
383     while (*x && (buflen--))
384     {
385         *x=toupper(*x);
386         x++;
387         done++;
388     }
389     return done;
390 }
391
392 /***********************************************************************
393  *           CharUpperBuffW   (USER32.42)
394  * FIXME: handle current locale
395  */
396 DWORD WINAPI CharUpperBuff32W(LPWSTR x,DWORD buflen)
397 {
398     DWORD done=0;
399
400     if (!x) return 0; /* YES */
401     while (*x && (buflen--))
402     {
403         *x=towupper(*x);
404         x++;
405         done++;
406     }
407     return done;
408 }
409
410 /***********************************************************************
411  *           CharUpperW   (USER32.43)
412  * FIXME: handle current locale
413  */
414 LPWSTR WINAPI CharUpper32W(LPWSTR x)
415 {
416     if (HIWORD(x))
417     {
418         LPWSTR s = x;
419         while (*s)
420         {
421             *s=towupper(*s);
422             s++;
423         }
424         return x;
425     }
426     else return (LPWSTR)towupper(LOWORD(x));
427 }
428
429 /***********************************************************************
430  *           IsCharAlphaA   (USER32.330)
431  * FIXME: handle current locale
432  */
433 BOOL32 WINAPI IsCharAlpha32A(CHAR x)
434 {
435     return isalpha(x);
436 }
437
438 /***********************************************************************
439  *           IsCharAlphaNumericA   (USER32.331)
440  * FIXME: handle current locale
441  */
442 BOOL32 WINAPI IsCharAlphaNumeric32A(CHAR x)
443 {
444     return isalnum(x);
445 }
446
447 /***********************************************************************
448  *           IsCharAlphaNumericW   (USER32.332)
449  * FIXME: handle current locale
450  */
451 BOOL32 WINAPI IsCharAlphaNumeric32W(WCHAR x)
452 {
453     return iswalnum(x);
454 }
455
456 /***********************************************************************
457  *           IsCharAlphaW   (USER32.333)
458  * FIXME: handle current locale
459  */
460 BOOL32 WINAPI IsCharAlpha32W(WCHAR x)
461 {
462     return iswalpha(x);
463 }
464
465 /***********************************************************************
466  *           IsCharLower32A   (USER32.334)
467  * FIXME: handle current locale
468  */
469 BOOL32 WINAPI IsCharLower32A(CHAR x)
470 {
471     return islower(x);
472 }
473
474 /***********************************************************************
475  *           IsCharLower32W   (USER32.335)
476  * FIXME: handle current locale
477  */
478 BOOL32 WINAPI IsCharLower32W(WCHAR x)
479 {
480     return iswlower(x);
481 }
482
483 /***********************************************************************
484  *           IsCharUpper32A   (USER32.336)
485  * FIXME: handle current locale
486  */
487 BOOL32 WINAPI IsCharUpper32A(CHAR x)
488 {
489     return isupper(x);
490 }
491
492 /***********************************************************************
493  *           IsCharUpper32W   (USER32.337)
494  * FIXME: handle current locale
495  */
496 BOOL32 WINAPI IsCharUpper32W(WCHAR x)
497 {
498     return iswupper(x);
499 }
500
501 /***********************************************************************
502  *           FormatMessage32A   (KERNEL32.138)
503  * FIXME: missing wrap,FROM_SYSTEM message-loading,
504  */
505 DWORD WINAPI FormatMessage32A(
506         DWORD   dwFlags,
507         LPCVOID lpSource,
508         DWORD   dwMessageId,
509         DWORD   dwLanguageId,
510         LPSTR   lpBuffer,
511         DWORD   nSize,
512         LPDWORD args /* va_list *args */
513 ) {
514         LPSTR   target,t;
515         DWORD   talloced;
516         LPSTR   from,f;
517         DWORD   width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
518         DWORD   nolinefeed = 0;
519
520         dprintf_info(resource,
521                 "FormatMessage32A(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
522                 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args
523         );
524         if (width) 
525                 fprintf(stdnimp,"       - line wrapping not supported.\n");
526         from = NULL;
527         if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
528                 from = HEAP_strdupA( GetProcessHeap(), 0, (LPSTR)lpSource);
529         if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) {
530                 from = HeapAlloc( GetProcessHeap(),0,200 );
531                 sprintf(from,"Systemmessage, messageid = 0x%08lx\n",dwMessageId);
532         }
533         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) {
534                 INT32   bufsize;
535
536                 dwMessageId &= 0xFFFF;
537                 bufsize=LoadMessage32A((HMODULE32)lpSource,dwMessageId,dwLanguageId,NULL,100);
538                 if (bufsize) {
539                         from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
540                         LoadMessage32A((HMODULE32)lpSource,dwMessageId,dwLanguageId,from,bufsize+1);
541                 }
542         }
543         target  = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 100);
544         t       = target;
545         talloced= 100;
546
547 #define ADD_TO_T(c) \
548         *t++=c;\
549         if (t-target == talloced) {\
550                 target  = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,talloced*2);\
551                 t       = target+talloced;\
552                 talloced*=2;\
553         }
554
555         if (from) {
556                 f=from;
557                 while (*f) {
558                         if (*f=='%') {
559                                 int     insertnr;
560                                 char    *fmtstr,*sprintfbuf,*x,*lastf;
561                                 DWORD   *argliststart;
562
563                                 fmtstr = NULL;
564                                 lastf = f;
565                                 f++;
566                                 if (!*f) {
567                                         ADD_TO_T('%');
568                                         continue;
569                                 }
570                                 switch (*f) {
571                                 case '1':case '2':case '3':case '4':case '5':
572                                 case '6':case '7':case '8':case '9':
573                                         insertnr=*f-'0';
574                                         switch (f[1]) {
575                                         case '0':case '1':case '2':case '3':
576                                         case '4':case '5':case '6':case '7':
577                                         case '8':case '9':
578                                                 f++;
579                                                 insertnr=insertnr*10+*f-'0';
580                                                 f++;
581                                                 break;
582                                         default:
583                                                 f++;
584                                                 break;
585                                         }
586                                         if (*f=='!') {
587                                                 f++;
588                                                 if (NULL!=(x=strchr(f,'!'))) {
589                                                         *x='\0';
590                                                         fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f)+2);
591                                                         sprintf(fmtstr,"%%%s",f);
592                                                         f=x+1;
593                                                 } else {
594                                                         fmtstr=HeapAlloc(GetProcessHeap(),0,strlen(f));
595                                                         sprintf(fmtstr,"%%%s",f);
596                                                         f+=strlen(f); /*at \0*/
597                                                 }
598                                         } else
599                                                 fmtstr=HEAP_strdupA(GetProcessHeap(),0,"%s");
600                                         if (args) {
601                                                 if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
602                                                         argliststart=args+insertnr-1;
603                                                 else
604                                                     argliststart=(*(DWORD**)args)+insertnr-1;
605
606                                                 if (fmtstr[strlen(fmtstr)]=='s')
607                                                         sprintfbuf=HeapAlloc(GetProcessHeap(),0,strlen((LPSTR)argliststart[0])+1);
608                                                 else
609                                                         sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
610
611                                                 /* CMF - This makes a BIG assumption about va_list */
612                                                 vsprintf(sprintfbuf, fmtstr, (va_list) argliststart);
613                                                 x=sprintfbuf;
614                                                 while (*x) {
615                                                         ADD_TO_T(*x++);
616                                                 }
617                                                 HeapFree(GetProcessHeap(),0,sprintfbuf);
618                                         } else {
619                                                 /* NULL args - copy formatstr 
620                                                  * (probably wrong)
621                                                  */
622                                                 while (lastf<f) {
623                                                         ADD_TO_T(*lastf++);
624                                                 }
625                                         }
626                                         HeapFree(GetProcessHeap(),0,fmtstr);
627                                         break;
628                                 case 'n':
629                                         /* FIXME: perhaps add \r too? */
630                                         ADD_TO_T('\n');
631                                         f++;
632                                         break;
633                                 case '0':
634                                         nolinefeed=1;
635                                         f++;
636                                         break;
637                                 default:ADD_TO_T(*f++)
638                                         break;
639
640                                 }
641                         } else {
642                                 ADD_TO_T(*f++)
643                         }
644                 }
645                 *t='\0';
646         }
647         if (!nolinefeed && t[-1]!='\n')
648                 ADD_TO_T('\n');
649         talloced = strlen(target)+1;
650         if (nSize && talloced<nSize) {
651                 target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
652         }
653         if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
654                 /* nSize is the MINIMUM size */
655                 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc32(GMEM_ZEROINIT,talloced);
656                 memcpy(*(LPSTR*)lpBuffer,target,talloced);
657         } else
658                 strncpy(lpBuffer,target,nSize);
659         HeapFree(GetProcessHeap(),0,target);
660         if (from) HeapFree(GetProcessHeap(),0,from);
661         return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? 
662                         strlen(*(LPSTR*)lpBuffer):
663                         strlen(lpBuffer);
664 }
665 #undef ADD_TO_T
666
667
668 /***********************************************************************
669  *           FormatMessage32W   (KERNEL32.138)
670  */
671 DWORD WINAPI FormatMessage32W(
672         DWORD   dwFlags,
673         LPCVOID lpSource,
674         DWORD   dwMessageId,
675         DWORD   dwLanguageId,
676         LPWSTR  lpBuffer,
677         DWORD   nSize,
678         LPDWORD args /* va_list *args */
679 ) {
680         LPSTR   target,t;
681         DWORD   talloced;
682         LPSTR   from,f;
683         DWORD   width = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK;
684         DWORD   nolinefeed = 0;
685
686         dprintf_info(resource,
687                 "FormatMessage32A(0x%lx,%p,%ld,0x%lx,%p,%ld,%p)\n",
688                 dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize,args
689         );
690         if (width) 
691                 fprintf(stdnimp,"       - line wrapping not supported.\n");
692         from = NULL;
693         if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
694                 from = HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)lpSource);
695         if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) {
696                 /* gather information from system message tables ... */
697                 from = HeapAlloc( GetProcessHeap(),0,200 );
698                 sprintf(from,"Systemmessage, messageid = 0x%08lx\n",dwMessageId);
699         }
700         if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) {
701                 INT32   bufsize;
702
703                 dwMessageId &= 0xFFFF;
704                 bufsize=LoadMessage32A((HMODULE32)lpSource,dwMessageId,dwLanguageId,NULL,100);
705                 if (bufsize)
706                 {
707                     from = HeapAlloc( GetProcessHeap(), 0, bufsize + 1 );
708                     LoadMessage32A((HMODULE32)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) {
726                         if (*f=='%') {
727                                 int     insertnr;
728                                 char    *fmtstr,*sprintfbuf,*x;
729                                 DWORD   *argliststart;
730
731                                 fmtstr = NULL;
732                                 f++;
733                                 if (!*f) {
734                                         ADD_TO_T('%');
735                                         continue;
736                                 }
737                                 switch (*f) {
738                                 case '1':case '2':case '3':case '4':case '5':
739                                 case '6':case '7':case '8':case '9':
740                                         insertnr=*f-'0';
741                                         switch (f[1]) {
742                                         case '0':case '1':case '2':case '3':
743                                         case '4':case '5':case '6':case '7':
744                                         case '8':case '9':
745                                                 f++;
746                                                 insertnr=insertnr*10+*f-'0';
747                                                 f++;
748                                                 break;
749                                         default:
750                                                 f++;
751                                                 break;
752                                         }
753                                         if (*f=='!') {
754                                                 f++;
755                                                 if (NULL!=(x=strchr(f,'!')))
756                                                 {
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                                                 fmtstr=HEAP_strdupA( GetProcessHeap(),0,"%s");
768                                         if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)
769                                                 argliststart=args+insertnr-1;
770                                         else
771                                                 argliststart=(*(DWORD**)args)+insertnr-1;
772
773                                         if (fmtstr[strlen(fmtstr)]=='s') {
774                                                 DWORD   xarr[3];
775
776                                                 xarr[0]=(DWORD)HEAP_strdupWtoA(GetProcessHeap(),0,(LPWSTR)(*(argliststart+0)));
777                                                 /* possible invalid pointers */
778                                                 xarr[1]=*(argliststart+1);
779                                                 xarr[2]=*(argliststart+2);
780                                                 sprintfbuf=HeapAlloc(GetProcessHeap(),0,lstrlen32W((LPWSTR)argliststart[0])*2+1);
781
782                                                 /* CMF - This makes a BIG assumption about va_list */
783                                                 vsprintf(sprintfbuf, fmtstr, (va_list) xarr);
784                                         } else {
785                                                 sprintfbuf=HeapAlloc(GetProcessHeap(),0,100);
786
787                                                 /* CMF - This makes a BIG assumption about va_list */
788                                                 vsprintf(sprintfbuf, fmtstr, (va_list) argliststart);
789                                         }
790                                         x=sprintfbuf;
791                                         while (*x) {
792                                                 ADD_TO_T(*x++);
793                                         }
794                                         HeapFree(GetProcessHeap(),0,sprintfbuf);
795                                         HeapFree(GetProcessHeap(),0,fmtstr);
796                                         break;
797                                 case 'n':
798                                         /* FIXME: perhaps add \r too? */
799                                         ADD_TO_T('\n');
800                                         f++;
801                                         break;
802                                 case '0':
803                                         nolinefeed=1;
804                                         f++;
805                                         break;
806                                 default:ADD_TO_T(*f++)
807                                         break;
808
809                                 }
810                         } else {
811                                 ADD_TO_T(*f++)
812                         }
813                 }
814                 *t='\0';
815         }
816         if (!nolinefeed && t[-1]!='\n')
817                 ADD_TO_T('\n');
818         talloced = strlen(target)+1;
819         if (nSize && talloced<nSize)
820                 target = (char*)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,target,nSize);
821         if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
822                 /* nSize is the MINIMUM size */
823                 *((LPVOID*)lpBuffer) = (LPVOID)LocalAlloc32(GMEM_ZEROINIT,talloced*2+2);
824                 lstrcpynAtoW(*(LPWSTR*)lpBuffer,target,talloced);
825         } else
826                 lstrcpynAtoW(lpBuffer,target,nSize);
827         HeapFree(GetProcessHeap(),0,target);
828         if (from) HeapFree(GetProcessHeap(),0,from);
829         return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? 
830                         lstrlen32W(*(LPWSTR*)lpBuffer):
831                         lstrlen32W(lpBuffer);
832 }
833 #undef ADD_TO_T