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