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