Release 971130
[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     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
153     switch((CHAR)*p)
154     {
155     case 'c':
156         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
157         break;
158     case 'C':
159         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
160         break;
161     case 'd':
162     case 'i':
163         res->type = WPR_SIGNED;
164         break;
165     case 's':
166         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
167         break;
168     case 'S':
169         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
170         break;
171     case 'u':
172         res->type = WPR_UNSIGNED;
173         break;
174     case 'X':
175         res->flags |= WPRINTF_UPPER_HEX;
176         /* fall through */
177     case 'x':
178         res->type = WPR_HEXA;
179         break;
180     default:
181         fprintf( stderr, "wvsprintf32W: unknown format '%c'\n", (CHAR)*p );
182         break;
183     }
184     return (INT32)(p - format) + 1;
185 }
186
187
188 /***********************************************************************
189  *           WPRINTF_GetLen
190  */
191 static UINT32 WPRINTF_GetLen( WPRINTF_FORMAT *format, LPCVOID arg,
192                               LPSTR number, UINT32 maxlen )
193 {
194     UINT32 len;
195
196     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
197     if (format->width > maxlen) format->width = maxlen;
198     switch(format->type)
199     {
200     case WPR_CHAR:
201     case WPR_WCHAR:
202         return (format->precision = 1);
203     case WPR_STRING:
204         for (len = 0; !format->precision || (len < format->precision); len++)
205             if (!*(*(LPCSTR *)arg + len)) break;
206         if (len > maxlen) len = maxlen;
207         return (format->precision = len);
208     case WPR_WSTRING:
209         for (len = 0; !format->precision || (len < format->precision); len++)
210             if (!*(*(LPCWSTR *)arg + len)) break;
211         if (len > maxlen) len = maxlen;
212         return (format->precision = len);
213     case WPR_SIGNED:
214         len = sprintf( number, "%d", *(INT32 *)arg );
215         break;
216     case WPR_UNSIGNED:
217         len = sprintf( number, "%u", *(UINT32 *)arg );
218         break;
219     case WPR_HEXA:
220         len = sprintf( number,
221                         (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
222                         *(UINT32 *)arg );
223         if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
224         break;
225     default:
226         return 0;
227     }
228     if (len > maxlen) len = maxlen;
229     if (format->precision < len) format->precision = len;
230     if (format->precision > maxlen) format->precision = maxlen;
231     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
232         format->precision = format->width;
233     return len;
234 }
235
236
237 /***********************************************************************
238  *           wvsnprintf16   (Not a Windows API)
239  */
240 INT16 WINAPI wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec,
241                            LPCVOID args )
242 {
243     WPRINTF_FORMAT format;
244     LPSTR p = buffer;
245     UINT32 i, len;
246     CHAR number[20];
247     DWORD cur_arg;
248
249     while (*spec && (maxlen > 1))
250     {
251         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
252         spec++;
253         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
254         spec += WPRINTF_ParseFormatA( spec, &format );
255         switch(format.type)
256         {
257         case WPR_WCHAR:  /* No Unicode in Win16 */
258         case WPR_CHAR:
259             cur_arg = (DWORD)VA_ARG16( args, CHAR );
260             break;
261         case WPR_WSTRING:  /* No Unicode in Win16 */
262         case WPR_STRING:
263             cur_arg = (DWORD)VA_ARG16( args, SEGPTR );
264             if (IsBadReadPtr16( (SEGPTR)cur_arg, 1 )) cur_arg = (DWORD)"";
265             else cur_arg = (DWORD)PTR_SEG_TO_LIN( (SEGPTR)cur_arg );
266             break;
267         case WPR_SIGNED:
268             if (!(format.flags & WPRINTF_LONG))
269             {
270                 cur_arg = (DWORD)(INT32)VA_ARG16( args, INT16 );
271                 break;
272             }
273             /* fall through */
274         case WPR_HEXA:
275         case WPR_UNSIGNED:
276             if (format.flags & WPRINTF_LONG)
277                 cur_arg = (DWORD)VA_ARG16( args, UINT32 );
278             else
279                 cur_arg = (DWORD)VA_ARG16( args, UINT16 );
280             break;
281         }
282         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
283         if (!(format.flags & WPRINTF_LEFTALIGN))
284             for (i = format.precision; i < format.width; i++, maxlen--)
285                 *p++ = ' ';
286         switch(format.type)
287         {
288         case WPR_WCHAR:
289         case WPR_CHAR:
290             if ((*p = (CHAR)cur_arg)) p++;
291             else if (format.width > 1) *p++ = ' ';
292             else len = 0;
293             break;
294         case WPR_WSTRING:
295         case WPR_STRING:
296             if (len) memcpy( p, (LPCSTR)cur_arg, len );
297             p += len;
298             break;
299         case WPR_HEXA:
300             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
301             {
302                 *p++ = '0';
303                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
304                 maxlen -= 2;
305                 len -= 2;
306                 format.precision -= 2;
307                 format.width -= 2;
308             }
309             /* fall through */
310         case WPR_SIGNED:
311         case WPR_UNSIGNED:
312             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
313             if (len) memcpy( p, number, len );
314             p += len;
315             break;
316         }
317         if (format.flags & WPRINTF_LEFTALIGN)
318             for (i = format.precision; i < format.width; i++, maxlen--)
319                 *p++ = ' ';
320         maxlen -= len;
321     }
322     *p = 0;
323     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
324 }
325
326
327 /***********************************************************************
328  *           wvsnprintf32A   (Not a Windows API)
329  */
330 INT32 WINAPI wvsnprintf32A( LPSTR buffer, UINT32 maxlen, LPCSTR spec,
331                             va_list args )
332 {
333     WPRINTF_FORMAT format;
334     LPSTR p = buffer;
335     UINT32 i, len;
336     CHAR number[20];
337
338     while (*spec && (maxlen > 1))
339     {
340         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
341         spec++;
342         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
343         spec += WPRINTF_ParseFormatA( spec, &format );
344         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
345         if (!(format.flags & WPRINTF_LEFTALIGN))
346             for (i = format.precision; i < format.width; i++, maxlen--)
347                 *p++ = ' ';
348         switch(format.type)
349         {
350         case WPR_WCHAR:
351             if ((*p = (CHAR)va_arg( args, WCHAR ))) p++;
352             else if (format.width > 1) *p++ = ' ';
353             else len = 0;
354             break;
355         case WPR_CHAR:
356             if ((*p = va_arg( args, CHAR ))) p++;
357             else if (format.width > 1) *p++ = ' ';
358             else len = 0;
359             break;
360         case WPR_STRING:
361             memcpy( p, va_arg( args, LPCSTR ), len );
362             p += len;
363             break;
364         case WPR_WSTRING:
365             {
366                 LPCWSTR ptr = va_arg( args, LPCWSTR );
367                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
368             }
369             break;
370         case WPR_HEXA:
371             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
372             {
373                 *p++ = '0';
374                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
375                 maxlen -= 2;
376                 len -= 2;
377                 format.precision -= 2;
378                 format.width -= 2;
379             }
380             /* fall through */
381         case WPR_SIGNED:
382         case WPR_UNSIGNED:
383             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
384             memcpy( p, number, len );
385             p += len;
386             (void)va_arg( args, INT32 ); /* Go to the next arg */
387             break;
388         }
389         if (format.flags & WPRINTF_LEFTALIGN)
390             for (i = format.precision; i < format.width; i++, maxlen--)
391                 *p++ = ' ';
392         maxlen -= len;
393     }
394     *p = 0;
395     dprintf_string(stddeb,"%s\n",buffer);
396     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
397 }
398
399
400 /***********************************************************************
401  *           wvsnprintf32W   (Not a Windows API)
402  */
403 INT32 WINAPI wvsnprintf32W( LPWSTR buffer, UINT32 maxlen, LPCWSTR spec,
404                             va_list args )
405 {
406     WPRINTF_FORMAT format;
407     LPWSTR p = buffer;
408     UINT32 i, len;
409     CHAR number[20];
410
411     while (*spec && (maxlen > 1))
412     {
413         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
414         spec++;
415         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
416         spec += WPRINTF_ParseFormatW( spec, &format );
417         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
418         if (!(format.flags & WPRINTF_LEFTALIGN))
419             for (i = format.precision; i < format.width; i++, maxlen--)
420                 *p++ = ' ';
421         switch(format.type)
422         {
423         case WPR_WCHAR:
424             if ((*p = va_arg( args, WCHAR ))) p++;
425             else if (format.width > 1) *p++ = ' ';
426             else len = 0;
427             break;
428         case WPR_CHAR:
429             if ((*p = (WCHAR)va_arg( args, CHAR ))) p++;
430             else if (format.width > 1) *p++ = ' ';
431             else len = 0;
432             break;
433         case WPR_STRING:
434             {
435                 LPCSTR ptr = va_arg( args, LPCSTR );
436                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
437             }
438             break;
439         case WPR_WSTRING:
440             if (len) memcpy( p, va_arg( args, LPCWSTR ), len * sizeof(WCHAR) );
441             p += len;
442             break;
443         case WPR_HEXA:
444             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
445             {
446                 *p++ = '0';
447                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
448                 maxlen -= 2;
449                 len -= 2;
450                 format.precision -= 2;
451                 format.width -= 2;
452             }
453             /* fall through */
454         case WPR_SIGNED:
455         case WPR_UNSIGNED:
456             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
457             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
458             (void)va_arg( args, INT32 ); /* Go to the next arg */
459             break;
460         }
461         if (format.flags & WPRINTF_LEFTALIGN)
462             for (i = format.precision; i < format.width; i++, maxlen--)
463                 *p++ = ' ';
464         maxlen -= len;
465     }
466     *p = 0;
467     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
468 }
469
470
471 /***********************************************************************
472  *           wvsprintf16   (USER.421)
473  */
474 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
475 {
476     dprintf_string(stddeb,"wvsprintf16 for %p got ",buffer);
477     return wvsnprintf16( buffer, 0xffff, spec, args );
478 }
479
480
481 /***********************************************************************
482  *           wvsprintf32A   (USER32.586)
483  */
484 INT32 WINAPI wvsprintf32A( LPSTR buffer, LPCSTR spec, va_list args )
485 {
486     dprintf_string(stddeb,"wvsprintf32A for %p got ",buffer);
487     return wvsnprintf32A( buffer, 0xffffffff, spec, args );
488 }
489
490
491 /***********************************************************************
492  *           wvsprintf32W   (USER32.587)
493  */
494 INT32 WINAPI wvsprintf32W( LPWSTR buffer, LPCWSTR spec, va_list args )
495 {
496     dprintf_string(stddeb,"wvsprintf32W for %p got ",buffer);
497     return wvsnprintf32W( buffer, 0xffffffff, spec, args );
498 }
499
500
501 /***********************************************************************
502  *           wsprintf16   (USER.420)
503  */
504 /* Winelib version */
505 INT16 WINAPIV wsprintf16( LPSTR buffer, LPCSTR spec, ... )
506 {
507     va_list valist;
508     INT16 res;
509
510     dprintf_string(stddeb,"wsprintf16 for %p got ",buffer);
511     va_start( valist, spec );
512     /* Note: we call the 32-bit version, because the args are 32-bit */
513     res = (INT16)wvsnprintf32A( buffer, 0xffffffff, spec, valist );
514     va_end( valist );
515     return res;
516 }
517
518 /* Emulator version */
519 INT16 WINAPIV WIN16_wsprintf16(void)
520 {
521     VA_LIST16 valist;
522     INT16 res;
523     SEGPTR buffer, spec;
524
525     VA_START16( valist );
526     buffer = VA_ARG16( valist, SEGPTR );
527     spec   = VA_ARG16( valist, SEGPTR );
528     dprintf_string(stddeb,"WIN16_wsprintf16 got ");
529     res = wvsnprintf16( (LPSTR)PTR_SEG_TO_LIN(buffer), 0xffff,
530                         (LPCSTR)PTR_SEG_TO_LIN(spec), valist );
531     VA_END16( valist );
532     return res;
533 }
534
535
536 /***********************************************************************
537  *           wsprintf32A   (USER32.585)
538  */
539 INT32 WINAPIV wsprintf32A( LPSTR buffer, LPCSTR spec, ... )
540 {
541     va_list valist;
542     INT32 res;
543
544     dprintf_string(stddeb,"wsprintf32A for %p got ",buffer);
545     va_start( valist, spec );
546     res = wvsnprintf32A( buffer, 0xffffffff, spec, valist );
547     va_end( valist );
548     return res;
549 }
550
551
552 /***********************************************************************
553  *           wsprintf32W   (USER32.586)
554  */
555 INT32 WINAPIV wsprintf32W( LPWSTR buffer, LPCWSTR spec, ... )
556 {
557     va_list valist;
558     INT32 res;
559
560     dprintf_string(stddeb,"wsprintf32W for %p\n",buffer);
561     va_start( valist, spec );
562     res = wvsnprintf32W( buffer, 0xffffffff, spec, valist );
563     va_end( valist );
564     return res;
565 }
566
567
568 /***********************************************************************
569  *           wsnprintf16   (Not a Windows API)
570  */
571 INT16 WINAPIV wsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec, ... )
572 {
573     va_list valist;
574     INT16 res;
575
576     va_start( valist, spec );
577     res = wvsnprintf16( buffer, maxlen, spec, valist );
578     va_end( valist );
579     return res;
580 }
581
582
583 /***********************************************************************
584  *           wsnprintf32A   (Not a Windows API)
585  */
586 INT32 WINAPIV wsnprintf32A( LPSTR buffer, UINT32 maxlen, LPCSTR spec, ... )
587 {
588     va_list valist;
589     INT32 res;
590
591     va_start( valist, spec );
592     res = wvsnprintf32A( buffer, maxlen, spec, valist );
593     va_end( valist );
594     return res;
595 }
596
597
598 /***********************************************************************
599  *           wsnprintf32W   (Not a Windows API)
600  */
601 INT32 WINAPIV wsnprintf32W( LPWSTR buffer, UINT32 maxlen, LPCWSTR spec, ... )
602 {
603     va_list valist;
604     INT32 res;
605
606     va_start( valist, spec );
607     res = wvsnprintf32W( buffer, maxlen, spec, valist );
608     va_end( valist );
609     return res;
610 }