List 16-bit resources separately as RC_SRCS16.
[wine] / dlls / user / wsprintf.c
1 /*
2  * wsprintf functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  */
6
7 #include <stdarg.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include "wine/winbase16.h"
11 #include "windef.h"
12 #include "wingdi.h"
13 #include "winuser.h"
14 #include "stackframe.h"
15 #include "debugtools.h"
16
17 DEFAULT_DEBUG_CHANNEL(string);
18
19
20 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
21 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
22 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
23 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
24 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
25 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
26 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
27
28 typedef enum
29 {
30     WPR_UNKNOWN,
31     WPR_CHAR,
32     WPR_WCHAR,
33     WPR_STRING,
34     WPR_WSTRING,
35     WPR_SIGNED,
36     WPR_UNSIGNED,
37     WPR_HEXA
38 } WPRINTF_TYPE;
39
40 typedef struct
41 {
42     UINT         flags;
43     UINT         width;
44     UINT         precision;
45     WPRINTF_TYPE   type;
46 } WPRINTF_FORMAT;
47
48 typedef union {
49     WCHAR   wchar_view;
50     CHAR    char_view;
51     LPCSTR  lpcstr_view;
52     LPCWSTR lpcwstr_view;
53     INT     int_view;
54 } WPRINTF_DATA;
55
56 static const CHAR null_stringA[] = "(null)";
57 static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };
58
59 /***********************************************************************
60  *           WPRINTF_ParseFormatA
61  *
62  * Parse a format specification. A format specification has the form:
63  *
64  * [-][#][0][width][.precision]type
65  *
66  * Return value is the length of the format specification in characters.
67  */
68 static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
69 {
70     LPCSTR p = format;
71
72     res->flags = 0;
73     res->width = 0;
74     res->precision = 0;
75     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
76     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
77     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
78     while ((*p >= '0') && (*p <= '9'))  /* width field */
79     {
80         res->width = res->width * 10 + *p - '0';
81         p++;
82     }
83     if (*p == '.')  /* precision field */
84     {
85         p++;
86         while ((*p >= '0') && (*p <= '9'))
87         {
88             res->precision = res->precision * 10 + *p - '0';
89             p++;
90         }
91     }
92     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
93     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
94     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
95     switch(*p)
96     {
97     case 'c':
98         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
99         break;
100     case 'C':
101         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
102         break;
103     case 'd':
104     case 'i':
105         res->type = WPR_SIGNED;
106         break;
107     case 's':
108         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
109         break;
110     case 'S':
111         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? 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         break;
241     default:
242         return 0;
243     }
244     if (len > maxlen) len = maxlen;
245     if (format->precision < len) format->precision = len;
246     if (format->precision > maxlen) format->precision = maxlen;
247     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
248         format->precision = format->width;
249     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
250     return len;
251 }
252
253
254 /***********************************************************************
255  *           wvsnprintf16   (Not a Windows API)
256  */
257 static INT16 wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec,
258                            LPCVOID args )
259 {
260     WPRINTF_FORMAT format;
261     LPSTR p = buffer;
262     UINT i, len;
263     CHAR number[20];
264     WPRINTF_DATA cur_arg;
265     SEGPTR seg_str;
266
267     while (*spec && (maxlen > 1))
268     {
269         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
270         spec++;
271         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
272         spec += WPRINTF_ParseFormatA( spec, &format );
273         switch(format.type)
274         {
275         case WPR_WCHAR:  /* No Unicode in Win16 */
276         case WPR_CHAR:
277             cur_arg.char_view = VA_ARG16( args, CHAR );
278             break;
279         case WPR_WSTRING:  /* No Unicode in Win16 */
280         case WPR_STRING:
281             seg_str = VA_ARG16( args, SEGPTR );
282             if (IsBadReadPtr16(seg_str, 1 )) cur_arg.lpcstr_view = "";
283             else cur_arg.lpcstr_view = MapSL( seg_str );
284             break;
285         case WPR_SIGNED:
286             if (!(format.flags & WPRINTF_LONG))
287             {
288                 cur_arg.int_view = VA_ARG16( args, INT16 );
289                 break;
290             }
291             /* fall through */
292         case WPR_HEXA:
293         case WPR_UNSIGNED:
294             if (format.flags & WPRINTF_LONG)
295                 cur_arg.int_view = VA_ARG16( args, UINT );
296             else
297                 cur_arg.int_view = VA_ARG16( args, UINT16 );
298             break;
299         case WPR_UNKNOWN:
300             continue;
301         }
302         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
303         if (!(format.flags & WPRINTF_LEFTALIGN))
304             for (i = format.precision; i < format.width; i++, maxlen--)
305                 *p++ = ' ';
306         switch(format.type)
307         {
308         case WPR_WCHAR:  /* No Unicode in Win16 */
309         case WPR_CHAR:
310             *p= cur_arg.char_view;
311             if (*p != '\0') p++;
312             else if (format.width > 1) *p++ = ' ';
313             else len = 0;
314             break;
315         case WPR_WSTRING:  /* No Unicode in Win16 */
316         case WPR_STRING:
317             if (len) memcpy( p, cur_arg.lpcstr_view, len );
318             p += len;
319             break;
320         case WPR_HEXA:
321             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
322             {
323                 *p++ = '0';
324                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
325                 maxlen -= 2;
326                 len -= 2;
327             }
328             /* fall through */
329         case WPR_SIGNED:
330         case WPR_UNSIGNED:
331             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
332             if (len) memcpy( p, number, len );
333             p += len;
334             break;
335         case WPR_UNKNOWN:
336             continue;
337         }
338         if (format.flags & WPRINTF_LEFTALIGN)
339             for (i = format.precision; i < format.width; i++, maxlen--)
340                 *p++ = ' ';
341         maxlen -= len;
342     }
343     *p = 0;
344     return (maxlen > 1) ? (INT)(p - buffer) : -1;
345 }
346
347
348 /***********************************************************************
349  *           wvsnprintfA   (USER32.@) (Not a Windows API, but we export it from USER32 anyway)
350  */
351 INT WINAPI wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
352 {
353     WPRINTF_FORMAT format;
354     LPSTR p = buffer;
355     UINT i, len;
356     CHAR number[20];
357     WPRINTF_DATA argData;
358
359     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
360
361     while (*spec && (maxlen > 1))
362     {
363         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
364         spec++;
365         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
366         spec += WPRINTF_ParseFormatA( spec, &format );
367
368         switch(format.type)
369         {
370         case WPR_WCHAR:
371             argData.wchar_view = (WCHAR)va_arg( args, int );
372             break;
373         case WPR_CHAR:
374             argData.char_view = (CHAR)va_arg( args, int );
375             break;
376         case WPR_STRING:
377             argData.lpcstr_view = va_arg( args, LPCSTR );
378             break;
379         case WPR_WSTRING:
380             argData.lpcwstr_view = va_arg( args, LPCWSTR );
381             break;
382         case WPR_HEXA:
383         case WPR_SIGNED:
384         case WPR_UNSIGNED:
385             argData.int_view = va_arg( args, INT );
386             break;
387         default:
388             argData.wchar_view = 0;
389             break;
390         }
391
392         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
393         if (!(format.flags & WPRINTF_LEFTALIGN))
394             for (i = format.precision; i < format.width; i++, maxlen--)
395                 *p++ = ' ';
396         switch(format.type)
397         {
398         case WPR_WCHAR:
399             *p = argData.wchar_view;
400             if (*p != '\0') p++;
401             else if (format.width > 1) *p++ = ' ';
402             else len = 0;
403             break;
404         case WPR_CHAR:
405             *p = argData.char_view;
406             if (*p != '\0') p++;
407             else if (format.width > 1) *p++ = ' ';
408             else len = 0;
409             break;
410         case WPR_STRING:
411             memcpy( p, argData.lpcstr_view, len );
412             p += len;
413             break;
414         case WPR_WSTRING:
415             {
416                 LPCWSTR ptr = argData.lpcwstr_view;
417                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
418             }
419             break;
420         case WPR_HEXA:
421             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
422             {
423                 *p++ = '0';
424                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
425                 maxlen -= 2;
426                 len -= 2;
427             }
428             /* fall through */
429         case WPR_SIGNED:
430         case WPR_UNSIGNED:
431             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
432             memcpy( p, number, len );
433             p += len;
434             break;
435         case WPR_UNKNOWN:
436             continue;
437         }
438         if (format.flags & WPRINTF_LEFTALIGN)
439             for (i = format.precision; i < format.width; i++, maxlen--)
440                 *p++ = ' ';
441         maxlen -= len;
442     }
443     *p = 0;
444     TRACE("%s\n",debugstr_a(buffer));
445     return (maxlen > 1) ? (INT)(p - buffer) : -1;
446 }
447
448
449 /***********************************************************************
450  *           wvsnprintfW   (USER32.@) (Not a Windows API, but we export it from USER32 anyway)
451  */
452 INT WINAPI wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
453 {
454     WPRINTF_FORMAT format;
455     LPWSTR p = buffer;
456     UINT i, len;
457     CHAR number[20];
458     WPRINTF_DATA argData;
459
460     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
461
462     while (*spec && (maxlen > 1))
463     {
464         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
465         spec++;
466         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
467         spec += WPRINTF_ParseFormatW( spec, &format );
468
469         switch(format.type)
470         {
471         case WPR_WCHAR:
472             argData.wchar_view = (WCHAR)va_arg( args, int );
473             break;
474         case WPR_CHAR:
475             argData.char_view = (CHAR)va_arg( args, int );
476             break;
477         case WPR_STRING:
478             argData.lpcstr_view = va_arg( args, LPCSTR );
479             break;
480         case WPR_WSTRING:
481             argData.lpcwstr_view = va_arg( args, LPCWSTR );
482             break;
483         case WPR_HEXA:
484         case WPR_SIGNED:
485         case WPR_UNSIGNED:
486             argData.int_view = va_arg( args, INT );
487             break;
488         default:
489             argData.wchar_view = 0;
490             break;
491         }
492
493         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
494         if (!(format.flags & WPRINTF_LEFTALIGN))
495             for (i = format.precision; i < format.width; i++, maxlen--)
496                 *p++ = ' ';
497         switch(format.type)
498         {
499         case WPR_WCHAR:
500             *p = argData.wchar_view;
501             if (*p != '\0') p++;
502             else if (format.width > 1) *p++ = ' ';
503             else len = 0;
504             break;
505         case WPR_CHAR:
506             *p = argData.char_view;
507             if (*p != '\0') p++;
508             else if (format.width > 1) *p++ = ' ';
509             else len = 0;
510             break;
511         case WPR_STRING:
512             {
513                 LPCSTR ptr = argData.lpcstr_view;
514                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
515             }
516             break;
517         case WPR_WSTRING:
518             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
519             p += len;
520             break;
521         case WPR_HEXA:
522             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
523             {
524                 *p++ = '0';
525                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
526                 maxlen -= 2;
527                 len -= 2;
528             }
529             /* fall through */
530         case WPR_SIGNED:
531         case WPR_UNSIGNED:
532             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
533             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
534             break;
535         case WPR_UNKNOWN:
536             continue;
537         }
538         if (format.flags & WPRINTF_LEFTALIGN)
539             for (i = format.precision; i < format.width; i++, maxlen--)
540                 *p++ = ' ';
541         maxlen -= len;
542     }
543     *p = 0;
544     TRACE("%s\n",debugstr_w(buffer));
545     return (maxlen > 1) ? (INT)(p - buffer) : -1;
546 }
547
548
549 /***********************************************************************
550  *           wvsprintf16   (USER.421)
551  */
552 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
553 {
554     INT16 res;
555
556     TRACE("for %p got:\n",buffer);
557     res = wvsnprintf16( buffer, 1024, spec, args );
558     return ( res == -1 ) ? 1024 : res;
559 }
560
561
562 /***********************************************************************
563  *           wvsprintfA   (USER32.@)
564  */
565 INT WINAPI wvsprintfA( LPSTR buffer, LPCSTR spec, va_list args )
566 {
567     INT res = wvsnprintfA( buffer, 1024, spec, args );
568     return ( res == -1 ) ? 1024 : res;
569 }
570
571
572 /***********************************************************************
573  *           wvsprintfW   (USER32.@)
574  */
575 INT WINAPI wvsprintfW( LPWSTR buffer, LPCWSTR spec, va_list args )
576 {
577     INT res = wvsnprintfW( buffer, 1024, spec, args );
578     return ( res == -1 ) ? 1024 : res;
579 }
580
581
582 /***********************************************************************
583  *           wsprintf16   (USER.420)
584  */
585 INT16 WINAPIV wsprintf16(void)
586 {
587     VA_LIST16 valist;
588     INT16 res;
589     SEGPTR buffer, spec;
590
591     VA_START16( valist );
592     buffer = VA_ARG16( valist, SEGPTR );
593     spec   = VA_ARG16( valist, SEGPTR );
594     res = wvsnprintf16( MapSL(buffer), 1024, MapSL(spec), valist );
595     VA_END16( valist );
596     return ( res == -1 ) ? 1024 : res;
597 }
598
599
600 /***********************************************************************
601  *           wsprintfA   (USER32.@)
602  */
603 INT WINAPIV wsprintfA( LPSTR buffer, LPCSTR spec, ... )
604 {
605     va_list valist;
606     INT res;
607
608     va_start( valist, spec );
609     res = wvsnprintfA( buffer, 1024, spec, valist );
610     va_end( valist );
611     return ( res == -1 ) ? 1024 : res;
612 }
613
614
615 /***********************************************************************
616  *           wsprintfW   (USER32.@)
617  */
618 INT WINAPIV wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
619 {
620     va_list valist;
621     INT res;
622
623     va_start( valist, spec );
624     res = wvsnprintfW( buffer, 1024, spec, valist );
625     va_end( valist );
626     return ( res == -1 ) ? 1024 : res;
627 }