Release 960616
[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 wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec, LPCVOID args )
234 {
235     WPRINTF_FORMAT format;
236     LPSTR p = buffer;
237     UINT32 i, len;
238     CHAR number[20];
239     DWORD cur_arg;
240
241     while (*spec && (maxlen > 1))
242     {
243         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
244         spec++;
245         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
246         spec += WPRINTF_ParseFormatA( spec, &format );
247         switch(format.type)
248         {
249         case WPR_CHAR:
250             cur_arg = (DWORD)*(CHAR *)args;
251             args = (WORD *)args + 1;
252             break;
253         case WPR_STRING:
254             cur_arg = (DWORD)PTR_SEG_TO_LIN( *(SEGPTR *)args );
255             args = (SEGPTR *)args + 1;
256             break;
257         case WPR_HEXA:
258         case WPR_SIGNED:
259         case WPR_UNSIGNED:
260             if (format.flags & WPRINTF_LONG)
261             {
262                 cur_arg = (DWORD)*(UINT32 *)args;
263                 args = (UINT32 *)args + 1;
264             }
265             else
266             {
267                 cur_arg = (DWORD)*(UINT16 *)args;
268                 args = (UINT16 *)args + 1;
269             }
270             break;
271         case WPR_WCHAR:
272         case WPR_WSTRING:
273             fprintf( stderr, "Unicode not supported in wsprintf16\n" );
274             continue;
275         }
276         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
277         if (!(format.flags & WPRINTF_LEFTALIGN))
278             for (i = format.precision; i < format.width; i++, maxlen--)
279                 *p++ = ' ';
280         switch(format.type)
281         {
282         case WPR_CHAR:
283             if ((*p = (CHAR)cur_arg)) p++;
284             else if (format.width > 1) *p++ = ' ';
285             else len = 0;
286             break;
287         case WPR_STRING:
288             if (len) memcpy( p, (LPCSTR)cur_arg, len );
289             p += len;
290             break;
291         case WPR_HEXA:
292             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
293             {
294                 *p++ = '0';
295                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
296                 maxlen -= 2;
297                 len -= 2;
298                 format.precision -= 2;
299                 format.width -= 2;
300             }
301             /* fall through */
302         case WPR_SIGNED:
303         case WPR_UNSIGNED:
304             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
305             if (len) memcpy( p, number, len );
306             p += len;
307             break;
308         case WPR_WCHAR:
309         case WPR_WSTRING:
310             fprintf( stderr, "Unicode not supported in wsprintf16\n" );
311             break;
312         }
313         if (format.flags & WPRINTF_LEFTALIGN)
314             for (i = format.precision; i < format.width; i++, maxlen--)
315                 *p++ = ' ';
316         maxlen -= len;
317     }
318     *p = 0;
319     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
320 }
321
322
323 /***********************************************************************
324  *           wvsnprintf32A   (Not a Windows API)
325  */
326 INT32 wvsnprintf32A( LPSTR buffer, UINT32 maxlen, LPCSTR spec, LPCVOID args )
327 {
328     WPRINTF_FORMAT format;
329     LPSTR p = buffer;
330     UINT32 i, len;
331     CHAR number[20];
332
333     while (*spec && (maxlen > 1))
334     {
335         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
336         spec++;
337         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
338         spec += WPRINTF_ParseFormatA( spec, &format );
339         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
340         if (!(format.flags & WPRINTF_LEFTALIGN))
341             for (i = format.precision; i < format.width; i++, maxlen--)
342                 *p++ = ' ';
343         switch(format.type)
344         {
345         case WPR_WCHAR:
346             if ((*p = (CHAR)*(WCHAR *)args)) p++;
347             else if (format.width > 1) *p++ = ' ';
348             else len = 0;
349             break;
350         case WPR_CHAR:
351             if ((*p = *(CHAR *)args)) p++;
352             else if (format.width > 1) *p++ = ' ';
353             else len = 0;
354             break;
355         case WPR_STRING:
356             if (len) memcpy( p, *(LPCSTR *)args, len );
357             p += len;
358             break;
359         case WPR_WSTRING:
360             for (i = 0; i < len; i++) *p++ = (CHAR)*(*(LPCWSTR *)args + i);
361             break;
362         case WPR_HEXA:
363             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
364             {
365                 *p++ = '0';
366                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
367                 maxlen -= 2;
368                 len -= 2;
369                 format.precision -= 2;
370                 format.width -= 2;
371             }
372             /* fall through */
373         case WPR_SIGNED:
374         case WPR_UNSIGNED:
375             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
376             if (len) memcpy( p, number, len );
377             p += len;
378             break;
379         }
380         if (format.flags & WPRINTF_LEFTALIGN)
381             for (i = format.precision; i < format.width; i++, maxlen--)
382                 *p++ = ' ';
383         args = (INT32 *)args + 1;
384         maxlen -= len;
385     }
386     *p = 0;
387     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
388 }
389
390
391 /***********************************************************************
392  *           wvsnprintf32W   (Not a Windows API)
393  */
394 INT32 wvsnprintf32W( LPWSTR buffer, UINT32 maxlen, LPCWSTR spec, LPCVOID args )
395 {
396     WPRINTF_FORMAT format;
397     LPWSTR p = buffer;
398     UINT32 i, len;
399     CHAR number[20];
400
401     while (*spec && (maxlen > 1))
402     {
403         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
404         spec++;
405         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
406         spec += WPRINTF_ParseFormatW( spec, &format );
407         len = WPRINTF_GetLen( &format, args, number, maxlen - 1 );
408         if (!(format.flags & WPRINTF_LEFTALIGN))
409             for (i = format.precision; i < format.width; i++, maxlen--)
410                 *p++ = ' ';
411         switch(format.type)
412         {
413         case WPR_WCHAR:
414             if ((*p = *(WCHAR *)args)) p++;
415             else if (format.width > 1) *p++ = ' ';
416             else len = 0;
417             break;
418         case WPR_CHAR:
419             if ((*p = (WCHAR)*(CHAR *)args)) p++;
420             else if (format.width > 1) *p++ = ' ';
421             else len = 0;
422             break;
423         case WPR_STRING:
424             for (i = 0; i < len; i++) *p++ = (WCHAR)*(*(LPCSTR *)args + i);
425             break;
426         case WPR_WSTRING:
427             if (len) memcpy( p, *(LPCWSTR *)args, len * sizeof(WCHAR) );
428             p += len;
429             break;
430         case WPR_HEXA:
431             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
432             {
433                 *p++ = '0';
434                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
435                 maxlen -= 2;
436                 len -= 2;
437                 format.precision -= 2;
438                 format.width -= 2;
439             }
440             /* fall through */
441         case WPR_SIGNED:
442         case WPR_UNSIGNED:
443             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
444             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
445             break;
446         }
447         if (format.flags & WPRINTF_LEFTALIGN)
448             for (i = format.precision; i < format.width; i++, maxlen--)
449                 *p++ = ' ';
450         args = (INT32 *)args + 1;
451         maxlen -= len;
452     }
453     *p = 0;
454     return (maxlen > 1) ? (INT32)(p - buffer) : -1;
455 }
456
457
458 /***********************************************************************
459  *           wvsprintf16   (USER.421)
460  */
461 INT16 wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
462 {
463     return wvsnprintf16( buffer, 0xffff, spec, args );
464 }
465
466
467 /***********************************************************************
468  *           wvsprintf32A   (USER32.586)
469  */
470 INT32 wvsprintf32A( LPSTR buffer, LPCSTR spec, LPCVOID args )
471 {
472     return wvsnprintf32A( buffer, 0xffffffff, spec, args );
473 }
474
475
476 /***********************************************************************
477  *           wvsprintf32W   (USER32.587)
478  */
479 INT32 wvsprintf32W( LPWSTR buffer, LPCWSTR spec, LPCVOID args )
480 {
481     return wvsnprintf32W( buffer, 0xffffffff, spec, args );
482 }
483
484
485 /***********************************************************************
486  *           wsprintf16   (USER.420)
487  */
488 /* Winelib version */
489 INT16 wsprintf16( LPSTR buffer, LPCSTR spec, ... )
490 {
491     va_list valist;
492     INT16 res;
493
494     va_start( valist, spec );
495     /* Note: we call the 32-bit version, because the args are 32-bit */
496     res = (INT16)wvsprintf32A( buffer, spec, (LPCVOID)valist );
497     va_end( valist );
498     return res;
499 }
500
501 /* Emulator version */
502 #ifndef WINELIB
503 INT16 WIN16_wsprintf16(void)
504 {
505     SEGPTR *win_stack = (DWORD *)CURRENT_STACK16->args;
506     LPSTR buffer = (LPSTR)PTR_SEG_TO_LIN(win_stack[0]);
507     LPCSTR spec  = (LPCSTR)PTR_SEG_TO_LIN(win_stack[1]);
508     return wvsprintf16( buffer, spec, &win_stack[2] );
509
510 }
511 #endif  /* WINELIB */
512
513
514 /***********************************************************************
515  *           wsprintf32A   (USER32.584)
516  */
517 /* Winelib version */
518 INT32 wsprintf32A( LPSTR buffer, LPCSTR spec, ... )
519 {
520     va_list valist;
521     INT32 res;
522
523     va_start( valist, spec );
524     res = wvsprintf32A( buffer, spec, (LPCVOID)valist );
525     va_end( valist );
526     return res;
527 }
528
529 /* Emulator version */
530 INT32 WIN32_wsprintf32A( int *args )
531 {
532     return wvsprintf32A( (LPSTR)args[0], (LPCSTR)args[1], (LPCVOID)&args[2] );
533 }
534
535
536 /***********************************************************************
537  *           wsprintf32W   (USER32.585)
538  */
539 /* Winelib version */
540 INT32 wsprintf32W( LPWSTR buffer, LPCWSTR spec, ... )
541 {
542     va_list valist;
543     INT32 res;
544
545     va_start( valist, spec );
546     res = wvsprintf32W( buffer, spec, (LPCVOID)valist );
547     va_end( valist );
548     return res;
549 }
550
551 /* Emulator version */
552 INT32 WIN32_wsprintf32W( int *args )
553 {
554     return wvsprintf32W( (LPWSTR)args[0], (LPCWSTR)args[1], (LPCVOID)&args[2]);
555 }