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