Release 971101
[wine] / misc / wsprintf.c
1 /*
2  * wsprintf functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  */
6
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include "windows.h"
11 #include "ldt.h"
12 #include "stackframe.h"
13 #include "debug.h"
14 #include "stddebug.h"
15
16 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
17 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
18 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
19 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
20 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
21 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
22 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
23
24 typedef enum
25 {
26     WPR_CHAR,
27     WPR_WCHAR,
28     WPR_STRING,
29     WPR_WSTRING,
30     WPR_SIGNED,
31     WPR_UNSIGNED,
32     WPR_HEXA
33 } WPRINTF_TYPE;
34
35 typedef struct
36 {
37     UINT32         flags;
38     UINT32         width;
39     UINT32         precision;
40     WPRINTF_TYPE   type;
41 } WPRINTF_FORMAT;
42
43
44 /***********************************************************************
45  *           WPRINTF_ParseFormatA
46  *
47  * Parse a format specification. A format specification has the form:
48  *
49  * [-][#][0][width][.precision]type
50  *
51  * Return value is the length of the format specification in characters.
52  */
53 static INT32 WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
54 {
55     LPCSTR p = format;
56
57     res->flags = 0;
58     res->width = 0;
59     res->precision = 0;
60     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
61     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
62     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
63     while ((*p >= '0') && (*p <= '9'))  /* width field */
64     {
65         res->width = res->width * 10 + *p - '0';
66         p++;
67     }
68     if (*p == '.')  /* precision field */
69     {
70         p++;
71         while ((*p >= '0') && (*p <= '9'))
72         {
73             res->precision = res->precision * 10 + *p - '0';
74             p++;
75         }
76     }
77     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
78     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
79     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
80     switch(*p)
81     {
82     case 'c':
83         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
84         break;
85     case 'C':
86         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
87         break;
88     case 'd':
89     case 'i':
90         res->type = WPR_SIGNED;
91         break;
92     case 's':
93         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) 
94                     ? WPR_WSTRING : WPR_STRING;
95         break;
96     case 'S':
97         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE))
98                     ? WPR_STRING : WPR_WSTRING;
99         break;
100     case 'u':
101         res->type = WPR_UNSIGNED;
102         break;
103     case 'X':
104         res->flags |= WPRINTF_UPPER_HEX;
105         /* fall through */
106     case 'x':
107         res->type = WPR_HEXA;
108         break;
109     default:
110         fprintf( stderr, "wvsprintf32A: unknown format '%c'\n", *p );
111         break;
112     }
113     return (INT32)(p - format) + 1;
114 }
115
116
117 /***********************************************************************
118  *           WPRINTF_ParseFormatW
119  *
120  * Parse a format specification. A format specification has the form:
121  *
122  * [-][#][0][width][.precision]type
123  *
124  * Return value is the length of the format specification in characters.
125  */
126 static INT32 WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
127 {
128     LPCWSTR p = format;
129
130     res->flags = 0;
131     res->width = 0;
132     res->precision = 0;
133     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
134     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
135     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
136     while ((*p >= '0') && (*p <= '9'))  /* width field */
137     {
138         res->width = res->width * 10 + *p - '0';
139         p++;
140     }
141     if (*p == '.')  /* precision field */
142     {
143         p++;
144         while ((*p >= '0') && (*p <= '9'))
145         {
146             res->precision = res->precision * 10 + *p - '0';
147             p++;
148         }
149     }
150     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
151     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
152     switch((CHAR)*p)
153     {
154     case 'c':
155         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
156         break;
157     case 'C':
158         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
159         break;
160     case 'd':
161     case 'i':
162         res->type = WPR_SIGNED;
163         break;
164     case 's':
165         res->type = (res->flags & WPRINTF_SHORT) ? WPR_STRING : WPR_WSTRING;
166         break;
167     case 'S':
168         res->type = (res->flags & WPRINTF_LONG) ? WPR_WSTRING : WPR_STRING;
169         break;
170     case 'u':
171         res->type = WPR_UNSIGNED;
172         break;
173     case 'X':
174         res->flags |= WPRINTF_UPPER_HEX;
175         /* fall through */
176     case 'x':
177         res->type = WPR_HEXA;
178         break;
179     default:
180         fprintf( stderr, "wvsprintf32W: unknown format '%c'\n", (CHAR)*p );
181         break;
182     }
183     return (INT32)(p - format) + 1;
184 }
185
186
187 /***********************************************************************
188  *           WPRINTF_GetLen
189  */
190 static UINT32 WPRINTF_GetLen( WPRINTF_FORMAT *format, LPCVOID arg,
191                               LPSTR number, UINT32 maxlen )
192 {
193     UINT32 len;
194
195     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
196     if (format->width > maxlen) format->width = maxlen;
197     switch(format->type)
198     {
199     case WPR_CHAR:
200     case WPR_WCHAR:
201         return (format->precision = 1);
202     case WPR_STRING:
203         for (len = 0; !format->precision || (len < format->precision); len++)
204             if (!*(*(LPCSTR *)arg + len)) break;
205         if (len > maxlen) len = maxlen;
206         return (format->precision = len);
207     case WPR_WSTRING:
208         for (len = 0; !format->precision || (len < format->precision); len++)
209             if (!*(*(LPCWSTR *)arg + len)) break;
210         if (len > maxlen) len = maxlen;
211         return (format->precision = len);
212     case WPR_SIGNED:
213         len = sprintf( number, "%d", *(INT32 *)arg );
214         break;
215     case WPR_UNSIGNED:
216         len = sprintf( number, "%u", *(UINT32 *)arg );
217         break;
218     case WPR_HEXA:
219         len = sprintf( number,
220                         (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
221                         *(UINT32 *)arg );
222         if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
223         break;
224     default:
225         return 0;
226     }
227     if (len > maxlen) len = maxlen;
228     if (format->precision < len) format->precision = len;
229     if (format->precision > maxlen) format->precision = maxlen;
230     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
231         format->precision = format->width;
232     return len;
233 }
234
235
236 /***********************************************************************
237  *           wvsnprintf16   (Not a Windows API)
238  */
239 INT16 WINAPI wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec,
240                            LPCVOID args )
241 {
242     WPRINTF_FORMAT format;
243     LPSTR p = buffer;
244     UINT32 i, len;
245     CHAR number[20];
246     DWORD cur_arg;
247
248     while (*spec && (maxlen > 1))
249     {
250         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
251         spec++;
252         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
253         spec += WPRINTF_ParseFormatA( spec, &format );
254         switch(format.type)
255         {
256         case WPR_WCHAR:  /* No Unicode in Win16 */
257         case WPR_CHAR:
258             cur_arg = (DWORD)VA_ARG16( args, CHAR );
259             break;
260         case WPR_WSTRING:  /* No Unicode in Win16 */
261         case WPR_STRING:
262             cur_arg = (DWORD)VA_ARG16( args, SEGPTR );
263             if (IsBadReadPtr16( (SEGPTR)cur_arg, 1 )) cur_arg = (DWORD)"";
264             else cur_arg = (DWORD)PTR_SEG_TO_LIN( (SEGPTR)cur_arg );
265             break;
266         case WPR_SIGNED:
267             if (!(format.flags & WPRINTF_LONG))
268             {
269                 cur_arg = (DWORD)(INT32)VA_ARG16( args, INT16 );
270                 break;
271             }
272             /* fall through */
273         case WPR_HEXA:
274         case WPR_UNSIGNED:
275             if (format.flags & WPRINTF_LONG)
276                 cur_arg = (DWORD)VA_ARG16( args, UINT32 );
277             else
278                 cur_arg = (DWORD)VA_ARG16( args, UINT16 );
279             break;
280         }
281         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
282         if (!(format.flags & WPRINTF_LEFTALIGN))
283             for (i = format.precision; i < format.width; i++, maxlen--)
284                 *p++ = ' ';
285         switch(format.type)
286         {
287         case WPR_WCHAR:
288         case WPR_CHAR:
289             if ((*p = (CHAR)cur_arg)) p++;
290             else if (format.width > 1) *p++ = ' ';
291             else len = 0;
292             break;
293         case WPR_WSTRING:
294         case WPR_STRING:
295             if (len) memcpy( p, (LPCSTR)cur_arg, len );
296             p += len;
297             break;
298         case WPR_HEXA:
299             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
300             {
301                 *p++ = '0';
302                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
303                 maxlen -= 2;
304                 len -= 2;
305                 format.precision -= 2;
306                 format.width -= 2;
307             }
308             /* fall through */
309         case WPR_SIGNED:
310         case WPR_UNSIGNED:
311             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
312             if (len) memcpy( p, number, len );
313             p += len;
314             break;
315         }
316         if (format.flags & WPRINTF_LEFTALIGN)
317             for (i = format.precision; i < format.width; i++, maxlen--)
318                 *p++ = ' ';
319         maxlen -= len;
320     }
321     *p = 0;
322     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
323 }
324
325
326 /***********************************************************************
327  *           wvsnprintf32A   (Not a Windows API)
328  */
329 INT32 WINAPI wvsnprintf32A( LPSTR buffer, UINT32 maxlen, LPCSTR spec,
330                             va_list args )
331 {
332     WPRINTF_FORMAT format;
333     LPSTR p = buffer;
334     UINT32 i, len;
335     CHAR number[20];
336
337     while (*spec && (maxlen > 1))
338     {
339         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
340         spec++;
341         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
342         spec += WPRINTF_ParseFormatA( spec, &format );
343         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
344         if (!(format.flags & WPRINTF_LEFTALIGN))
345             for (i = format.precision; i < format.width; i++, maxlen--)
346                 *p++ = ' ';
347         switch(format.type)
348         {
349         case WPR_WCHAR:
350             if ((*p = (CHAR)va_arg( args, WCHAR ))) p++;
351             else if (format.width > 1) *p++ = ' ';
352             else len = 0;
353             break;
354         case WPR_CHAR:
355             if ((*p = va_arg( args, CHAR ))) p++;
356             else if (format.width > 1) *p++ = ' ';
357             else len = 0;
358             break;
359         case WPR_STRING:
360             memcpy( p, va_arg( args, LPCSTR ), len );
361             p += len;
362             break;
363         case WPR_WSTRING:
364             {
365                 LPCWSTR ptr = va_arg( args, LPCWSTR );
366                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
367             }
368             break;
369         case WPR_HEXA:
370             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
371             {
372                 *p++ = '0';
373                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
374                 maxlen -= 2;
375                 len -= 2;
376                 format.precision -= 2;
377                 format.width -= 2;
378             }
379             /* fall through */
380         case WPR_SIGNED:
381         case WPR_UNSIGNED:
382             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
383             memcpy( p, number, len );
384             p += len;
385             (void)va_arg( args, INT32 ); /* Go to the next arg */
386             break;
387         }
388         if (format.flags & WPRINTF_LEFTALIGN)
389             for (i = format.precision; i < format.width; i++, maxlen--)
390                 *p++ = ' ';
391         maxlen -= len;
392     }
393     *p = 0;
394     dprintf_string(stddeb,"%s\n",buffer);
395     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
396 }
397
398
399 /***********************************************************************
400  *           wvsnprintf32W   (Not a Windows API)
401  */
402 INT32 WINAPI wvsnprintf32W( LPWSTR buffer, UINT32 maxlen, LPCWSTR spec,
403                             va_list args )
404 {
405     WPRINTF_FORMAT format;
406     LPWSTR p = buffer;
407     UINT32 i, len;
408     CHAR number[20];
409
410     while (*spec && (maxlen > 1))
411     {
412         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
413         spec++;
414         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
415         spec += WPRINTF_ParseFormatW( spec, &format );
416         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
417         if (!(format.flags & WPRINTF_LEFTALIGN))
418             for (i = format.precision; i < format.width; i++, maxlen--)
419                 *p++ = ' ';
420         switch(format.type)
421         {
422         case WPR_WCHAR:
423             if ((*p = va_arg( args, WCHAR ))) p++;
424             else if (format.width > 1) *p++ = ' ';
425             else len = 0;
426             break;
427         case WPR_CHAR:
428             if ((*p = (WCHAR)va_arg( args, CHAR ))) p++;
429             else if (format.width > 1) *p++ = ' ';
430             else len = 0;
431             break;
432         case WPR_STRING:
433             {
434                 LPCSTR ptr = va_arg( args, LPCSTR );
435                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
436             }
437             break;
438         case WPR_WSTRING:
439             if (len) memcpy( p, va_arg( args, LPCWSTR ), len * sizeof(WCHAR) );
440             p += len;
441             break;
442         case WPR_HEXA:
443             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
444             {
445                 *p++ = '0';
446                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
447                 maxlen -= 2;
448                 len -= 2;
449                 format.precision -= 2;
450                 format.width -= 2;
451             }
452             /* fall through */
453         case WPR_SIGNED:
454         case WPR_UNSIGNED:
455             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
456             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
457             (void)va_arg( args, INT32 ); /* Go to the next arg */
458             break;
459         }
460         if (format.flags & WPRINTF_LEFTALIGN)
461             for (i = format.precision; i < format.width; i++, maxlen--)
462                 *p++ = ' ';
463         maxlen -= len;
464     }
465     *p = 0;
466     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
467 }
468
469
470 /***********************************************************************
471  *           wvsprintf16   (USER.421)
472  */
473 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
474 {
475     dprintf_string(stddeb,"wvsprintf16 for %p got ",buffer);
476     return wvsnprintf16( buffer, 0xffff, spec, args );
477 }
478
479
480 /***********************************************************************
481  *           wvsprintf32A   (USER32.586)
482  */
483 INT32 WINAPI wvsprintf32A( LPSTR buffer, LPCSTR spec, va_list args )
484 {
485     dprintf_string(stddeb,"wvsprintf32A for %p got ",buffer);
486     return wvsnprintf32A( buffer, 0xffffffff, spec, args );
487 }
488
489
490 /***********************************************************************
491  *           wvsprintf32W   (USER32.587)
492  */
493 INT32 WINAPI wvsprintf32W( LPWSTR buffer, LPCWSTR spec, va_list args )
494 {
495     dprintf_string(stddeb,"wvsprintf32W for %p got ",buffer);
496     return wvsnprintf32W( buffer, 0xffffffff, spec, args );
497 }
498
499
500 /***********************************************************************
501  *           wsprintf16   (USER.420)
502  */
503 /* Winelib version */
504 INT16 WINAPIV wsprintf16( LPSTR buffer, LPCSTR spec, ... )
505 {
506     va_list valist;
507     INT16 res;
508
509     dprintf_string(stddeb,"wsprintf16 for %p got ",buffer);
510     va_start( valist, spec );
511     /* Note: we call the 32-bit version, because the args are 32-bit */
512     res = (INT16)wvsnprintf32A( buffer, 0xffffffff, spec, valist );
513     va_end( valist );
514     return res;
515 }
516
517 /* Emulator version */
518 INT16 WINAPIV WIN16_wsprintf16(void)
519 {
520     VA_LIST16 valist;
521     INT16 res;
522     SEGPTR buffer, spec;
523
524     VA_START16( valist );
525     buffer = VA_ARG16( valist, SEGPTR );
526     spec   = VA_ARG16( valist, SEGPTR );
527     dprintf_string(stddeb,"WIN16_wsprintf16 got ");
528     res = wvsnprintf16( (LPSTR)PTR_SEG_TO_LIN(buffer), 0xffff,
529                         (LPCSTR)PTR_SEG_TO_LIN(spec), valist );
530     VA_END16( valist );
531     return res;
532 }
533
534
535 /***********************************************************************
536  *           wsprintf32A   (USER32.585)
537  */
538 INT32 WINAPIV wsprintf32A( LPSTR buffer, LPCSTR spec, ... )
539 {
540     va_list valist;
541     INT32 res;
542
543     dprintf_string(stddeb,"wsprintf32A for %p got ",buffer);
544     va_start( valist, spec );
545     res = wvsnprintf32A( buffer, 0xffffffff, spec, valist );
546     va_end( valist );
547     return res;
548 }
549
550
551 /***********************************************************************
552  *           wsprintf32W   (USER32.586)
553  */
554 INT32 WINAPIV wsprintf32W( LPWSTR buffer, LPCWSTR spec, ... )
555 {
556     va_list valist;
557     INT32 res;
558
559     dprintf_string(stddeb,"wsprintf32W for %p\n",buffer);
560     va_start( valist, spec );
561     res = wvsnprintf32W( buffer, 0xffffffff, spec, valist );
562     va_end( valist );
563     return res;
564 }
565
566
567 /***********************************************************************
568  *           wsnprintf16   (Not a Windows API)
569  */
570 INT16 WINAPIV wsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec, ... )
571 {
572     va_list valist;
573     INT16 res;
574
575     va_start( valist, spec );
576     res = wvsnprintf16( buffer, maxlen, spec, valist );
577     va_end( valist );
578     return res;
579 }
580
581
582 /***********************************************************************
583  *           wsnprintf32A   (Not a Windows API)
584  */
585 INT32 WINAPIV wsnprintf32A( LPSTR buffer, UINT32 maxlen, LPCSTR spec, ... )
586 {
587     va_list valist;
588     INT32 res;
589
590     va_start( valist, spec );
591     res = wvsnprintf32A( buffer, maxlen, spec, valist );
592     va_end( valist );
593     return res;
594 }
595
596
597 /***********************************************************************
598  *           wsnprintf32W   (Not a Windows API)
599  */
600 INT32 WINAPIV wsnprintf32W( LPWSTR buffer, UINT32 maxlen, LPCWSTR spec, ... )
601 {
602     va_list valist;
603     INT32 res;
604
605     va_start( valist, spec );
606     res = wvsnprintf32W( buffer, maxlen, spec, valist );
607     va_end( valist );
608     return res;
609 }