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