Release 970824
[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
14 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
15 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
16 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
17 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
18 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
19 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
20
21 typedef enum
22 {
23     WPR_CHAR,
24     WPR_WCHAR,
25     WPR_STRING,
26     WPR_WSTRING,
27     WPR_SIGNED,
28     WPR_UNSIGNED,
29     WPR_HEXA
30 } WPRINTF_TYPE;
31
32 typedef struct
33 {
34     UINT32         flags;
35     UINT32         width;
36     UINT32         precision;
37     WPRINTF_TYPE   type;
38 } WPRINTF_FORMAT;
39
40
41 /***********************************************************************
42  *           WPRINTF_ParseFormatA
43  *
44  * Parse a format specification. A format specification has the form:
45  *
46  * [-][#][0][width][.precision]type
47  *
48  * Return value is the length of the format specification in characters.
49  */
50 static INT32 WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
51 {
52     LPCSTR p = format;
53
54     res->flags = 0;
55     res->width = 0;
56     res->precision = 0;
57     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
58     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
59     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
60     while ((*p >= '0') && (*p <= '9'))  /* width field */
61     {
62         res->width = res->width * 10 + *p - '0';
63         p++;
64     }
65     if (*p == '.')  /* precision field */
66     {
67         p++;
68         while ((*p >= '0') && (*p <= '9'))
69         {
70             res->precision = res->precision * 10 + *p - '0';
71             p++;
72         }
73     }
74     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
75     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
76     switch(*p)
77     {
78     case 'c':
79         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
80         break;
81     case 'C':
82         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
83         break;
84     case 'd':
85     case 'i':
86         res->type = WPR_SIGNED;
87         break;
88     case 's':
89         res->type = (res->flags & WPRINTF_LONG) ? WPR_WSTRING : WPR_STRING;
90         break;
91     case 'S':
92         res->type = (res->flags & WPRINTF_SHORT) ? WPR_STRING : WPR_WSTRING;
93         break;
94     case 'u':
95         res->type = WPR_UNSIGNED;
96         break;
97     case 'X':
98         res->flags |= WPRINTF_UPPER_HEX;
99         /* fall through */
100     case 'x':
101         res->type = WPR_HEXA;
102         break;
103     default:
104         fprintf( stderr, "wvsprintf32A: unknown format '%c'\n", *p );
105         break;
106     }
107     return (INT32)(p - format) + 1;
108 }
109
110
111 /***********************************************************************
112  *           WPRINTF_ParseFormatW
113  *
114  * Parse a format specification. A format specification has the form:
115  *
116  * [-][#][0][width][.precision]type
117  *
118  * Return value is the length of the format specification in characters.
119  */
120 static INT32 WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
121 {
122     LPCWSTR p = format;
123
124     res->flags = 0;
125     res->width = 0;
126     res->precision = 0;
127     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
128     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
129     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
130     while ((*p >= '0') && (*p <= '9'))  /* width field */
131     {
132         res->width = res->width * 10 + *p - '0';
133         p++;
134     }
135     if (*p == '.')  /* precision field */
136     {
137         p++;
138         while ((*p >= '0') && (*p <= '9'))
139         {
140             res->precision = res->precision * 10 + *p - '0';
141             p++;
142         }
143     }
144     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
145     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
146     switch((CHAR)*p)
147     {
148     case 'c':
149         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
150         break;
151     case 'C':
152         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
153         break;
154     case 'd':
155     case 'i':
156         res->type = WPR_SIGNED;
157         break;
158     case 's':
159         res->type = (res->flags & WPRINTF_SHORT) ? WPR_STRING : WPR_WSTRING;
160         break;
161     case 'S':
162         res->type = (res->flags & WPRINTF_LONG) ? WPR_WSTRING : WPR_STRING;
163         break;
164     case 'u':
165         res->type = WPR_UNSIGNED;
166         break;
167     case 'X':
168         res->flags |= WPRINTF_UPPER_HEX;
169         /* fall through */
170     case 'x':
171         res->type = WPR_HEXA;
172         break;
173     default:
174         fprintf( stderr, "wvsprintf32W: unknown format '%c'\n", (CHAR)*p );
175         break;
176     }
177     return (INT32)(p - format) + 1;
178 }
179
180
181 /***********************************************************************
182  *           WPRINTF_GetLen
183  */
184 static UINT32 WPRINTF_GetLen( WPRINTF_FORMAT *format, LPCVOID arg,
185                               LPSTR number, UINT32 maxlen )
186 {
187     UINT32 len;
188
189     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
190     if (format->width > maxlen) format->width = maxlen;
191     switch(format->type)
192     {
193     case WPR_CHAR:
194     case WPR_WCHAR:
195         return (format->precision = 1);
196     case WPR_STRING:
197         for (len = 0; !format->precision || (len < format->precision); len++)
198             if (!*(*(LPCSTR *)arg + len)) break;
199         if (len > maxlen) len = maxlen;
200         return (format->precision = len);
201     case WPR_WSTRING:
202         for (len = 0; !format->precision || (len < format->precision); len++)
203             if (!*(*(LPCWSTR *)arg + len)) break;
204         if (len > maxlen) len = maxlen;
205         return (format->precision = len);
206     case WPR_SIGNED:
207         len = sprintf( number, "%d", *(INT32 *)arg );
208         break;
209     case WPR_UNSIGNED:
210         len = sprintf( number, "%u", *(UINT32 *)arg );
211         break;
212     case WPR_HEXA:
213         len = sprintf( number,
214                         (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
215                         *(UINT32 *)arg );
216         if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
217         break;
218     default:
219         return 0;
220     }
221     if (len > maxlen) len = maxlen;
222     if (format->precision < len) format->precision = len;
223     if (format->precision > maxlen) format->precision = maxlen;
224     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
225         format->precision = format->width;
226     return len;
227 }
228
229
230 /***********************************************************************
231  *           wvsnprintf16   (Not a Windows API)
232  */
233 INT16 WINAPI wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec,
234                            LPCVOID args )
235 {
236     WPRINTF_FORMAT format;
237     LPSTR p = buffer;
238     UINT32 i, len;
239     CHAR number[20];
240     DWORD cur_arg;
241
242     while (*spec && (maxlen > 1))
243     {
244         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
245         spec++;
246         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
247         spec += WPRINTF_ParseFormatA( spec, &format );
248         switch(format.type)
249         {
250         case WPR_WCHAR:  /* No Unicode in Win16 */
251         case WPR_CHAR:
252             cur_arg = (DWORD)*(CHAR *)args;
253             args = (WORD *)args + 1;
254             break;
255         case WPR_WSTRING:  /* No Unicode in Win16 */
256         case WPR_STRING:
257             if (IsBadReadPtr16( *(SEGPTR *)args, 1 )) cur_arg = (DWORD)"";
258             else cur_arg = (DWORD)PTR_SEG_TO_LIN( *(SEGPTR *)args );
259             args = (SEGPTR *)args + 1;
260             break;
261         case WPR_SIGNED:
262             if (!(format.flags & WPRINTF_LONG))
263             {
264                 cur_arg = (DWORD)(INT32)*(INT16 *)args;
265                 args = (INT16 *)args + 1;
266                 break;
267             }
268             /* fall through */
269         case WPR_HEXA:
270         case WPR_UNSIGNED:
271             if (format.flags & WPRINTF_LONG)
272             {
273                 cur_arg = (DWORD)*(UINT32 *)args;
274                 args = (UINT32 *)args + 1;
275             }
276             else
277             {
278                 cur_arg = (DWORD)*(UINT16 *)args;
279                 args = (UINT16 *)args + 1;
280             }
281             break;
282         }
283         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
284         if (!(format.flags & WPRINTF_LEFTALIGN))
285             for (i = format.precision; i < format.width; i++, maxlen--)
286                 *p++ = ' ';
287         switch(format.type)
288         {
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_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         case WPR_WCHAR:
316         case WPR_WSTRING:
317             fprintf( stderr, "Unicode not supported in wsprintf16\n" );
318             break;
319         }
320         if (format.flags & WPRINTF_LEFTALIGN)
321             for (i = format.precision; i < format.width; i++, maxlen--)
322                 *p++ = ' ';
323         maxlen -= len;
324     }
325     *p = 0;
326     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
327 }
328
329
330 /***********************************************************************
331  *           wvsnprintf32A   (Not a Windows API)
332  */
333 INT32 WINAPI wvsnprintf32A( LPSTR buffer, UINT32 maxlen, LPCSTR spec,
334                             LPCVOID args )
335 {
336     WPRINTF_FORMAT format;
337     LPSTR p = buffer;
338     UINT32 i, len;
339     CHAR number[20];
340
341     while (*spec && (maxlen > 1))
342     {
343         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
344         spec++;
345         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
346         spec += WPRINTF_ParseFormatA( spec, &format );
347         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
348         if (!(format.flags & WPRINTF_LEFTALIGN))
349             for (i = format.precision; i < format.width; i++, maxlen--)
350                 *p++ = ' ';
351         switch(format.type)
352         {
353         case WPR_WCHAR:
354             if ((*p = (CHAR)*(WCHAR *)args)) p++;
355             else if (format.width > 1) *p++ = ' ';
356             else len = 0;
357             break;
358         case WPR_CHAR:
359             if ((*p = *(CHAR *)args)) p++;
360             else if (format.width > 1) *p++ = ' ';
361             else len = 0;
362             break;
363         case WPR_STRING:
364             if (len) memcpy( p, *(LPCSTR *)args, len );
365             p += len;
366             break;
367         case WPR_WSTRING:
368             for (i = 0; i < len; i++) *p++ = (CHAR)*(*(LPCWSTR *)args + i);
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             if (len) memcpy( p, number, len );
385             p += len;
386             break;
387         }
388         if (format.flags & WPRINTF_LEFTALIGN)
389             for (i = format.precision; i < format.width; i++, maxlen--)
390                 *p++ = ' ';
391         args = (INT32 *)args + 1;
392         maxlen -= len;
393     }
394     *p = 0;
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                             LPCVOID 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 = *(WCHAR *)args)) p++;
424             else if (format.width > 1) *p++ = ' ';
425             else len = 0;
426             break;
427         case WPR_CHAR:
428             if ((*p = (WCHAR)*(CHAR *)args)) p++;
429             else if (format.width > 1) *p++ = ' ';
430             else len = 0;
431             break;
432         case WPR_STRING:
433             for (i = 0; i < len; i++) *p++ = (WCHAR)*(*(LPCSTR *)args + i);
434             break;
435         case WPR_WSTRING:
436             if (len) memcpy( p, *(LPCWSTR *)args, len * sizeof(WCHAR) );
437             p += len;
438             break;
439         case WPR_HEXA:
440             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
441             {
442                 *p++ = '0';
443                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
444                 maxlen -= 2;
445                 len -= 2;
446                 format.precision -= 2;
447                 format.width -= 2;
448             }
449             /* fall through */
450         case WPR_SIGNED:
451         case WPR_UNSIGNED:
452             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
453             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
454             break;
455         }
456         if (format.flags & WPRINTF_LEFTALIGN)
457             for (i = format.precision; i < format.width; i++, maxlen--)
458                 *p++ = ' ';
459         args = (INT32 *)args + 1;
460         maxlen -= len;
461     }
462     *p = 0;
463     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
464 }
465
466
467 /***********************************************************************
468  *           wvsprintf16   (USER.421)
469  */
470 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
471 {
472     return wvsnprintf16( buffer, 0xffff, spec, args );
473 }
474
475
476 /***********************************************************************
477  *           wvsprintf32A   (USER32.586)
478  */
479 INT32 WINAPI wvsprintf32A( LPSTR buffer, LPCSTR spec, LPCVOID args )
480 {
481     return wvsnprintf32A( buffer, 0xffffffff, spec, args );
482 }
483
484
485 /***********************************************************************
486  *           wvsprintf32W   (USER32.587)
487  */
488 INT32 WINAPI wvsprintf32W( LPWSTR buffer, LPCWSTR spec, LPCVOID args )
489 {
490     return wvsnprintf32W( buffer, 0xffffffff, spec, args );
491 }
492
493
494 /***********************************************************************
495  *           wsprintf16   (USER.420)
496  */
497 /* Winelib version */
498 INT16 WINAPIV wsprintf16( LPSTR buffer, LPCSTR spec, ... )
499 {
500     va_list valist;
501     INT16 res;
502
503     va_start( valist, spec );
504     /* Note: we call the 32-bit version, because the args are 32-bit */
505     res = (INT16)wvsprintf32A( buffer, spec, (LPCVOID)valist );
506     va_end( valist );
507     return res;
508 }
509
510 /* Emulator version */
511 INT16 WINAPIV WIN16_wsprintf16(void)
512 {
513     SEGPTR *win_stack = (SEGPTR *)CURRENT_STACK16->args;
514     LPSTR buffer = (LPSTR)PTR_SEG_TO_LIN(win_stack[0]);
515     LPCSTR spec  = (LPCSTR)PTR_SEG_TO_LIN(win_stack[1]);
516     return wvsprintf16( buffer, spec, &win_stack[2] );
517
518 }
519
520
521 /***********************************************************************
522  *           wsprintf32A   (USER32.584)
523  */
524 /* Winelib version */
525 INT32 WINAPIV wsprintf32A( LPSTR buffer, LPCSTR spec, ... )
526 {
527     va_list valist;
528     INT32 res;
529
530     va_start( valist, spec );
531     res = wvsprintf32A( buffer, spec, (LPCVOID)valist );
532     va_end( valist );
533     return res;
534 }
535
536 /* Emulator version */
537 INT32 WINAPIV WIN32_wsprintf32A( DWORD *args )
538 {
539     return wvsprintf32A( (LPSTR)args[0], (LPCSTR)args[1], (LPCVOID)&args[2] );
540 }
541
542
543 /***********************************************************************
544  *           wsprintf32W   (USER32.585)
545  */
546 /* Winelib version */
547 INT32 WINAPIV wsprintf32W( LPWSTR buffer, LPCWSTR spec, ... )
548 {
549     va_list valist;
550     INT32 res;
551
552     va_start( valist, spec );
553     res = wvsprintf32W( buffer, spec, (LPCVOID)valist );
554     va_end( valist );
555     return res;
556 }
557
558 /* Emulator version */
559 INT32 WINAPIV WIN32_wsprintf32W( DWORD *args )
560 {
561     return wvsprintf32W( (LPWSTR)args[0], (LPCWSTR)args[1], (LPCVOID)&args[2]);
562 }