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