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