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