d3d9/tests: Correct nv40 results.
[wine] / dlls / user32 / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(string);
37
38
39 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
40 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
41 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
42 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
43 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
44 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
45 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
46 #define WPRINTF_INTPTR      0x0080  /* Pointer-size arg ('I' prefix) */
47 #define WPRINTF_I64         0x0100  /* 64-bit arg ('I64' prefix) */
48
49 typedef enum
50 {
51     WPR_UNKNOWN,
52     WPR_CHAR,
53     WPR_WCHAR,
54     WPR_STRING,
55     WPR_WSTRING,
56     WPR_SIGNED,
57     WPR_UNSIGNED,
58     WPR_HEXA
59 } WPRINTF_TYPE;
60
61 typedef struct
62 {
63     UINT         flags;
64     UINT         width;
65     UINT         precision;
66     WPRINTF_TYPE   type;
67 } WPRINTF_FORMAT;
68
69 typedef union {
70     WCHAR    wchar_view;
71     CHAR     char_view;
72     LPCSTR   lpcstr_view;
73     LPCWSTR  lpcwstr_view;
74     LONGLONG int_view;
75 } WPRINTF_DATA;
76
77 static const CHAR null_stringA[] = "(null)";
78 static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };
79
80 /***********************************************************************
81  *           WPRINTF_ParseFormatA
82  *
83  * Parse a format specification. A format specification has the form:
84  *
85  * [-][#][0][width][.precision]type
86  *
87  * Return value is the length of the format specification in characters.
88  */
89 static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
90 {
91     LPCSTR p = format;
92
93     res->flags = 0;
94     res->width = 0;
95     res->precision = 0;
96     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
97     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
98     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
99     while ((*p >= '0') && (*p <= '9'))  /* width field */
100     {
101         res->width = res->width * 10 + *p - '0';
102         p++;
103     }
104     if (*p == '.')  /* precision field */
105     {
106         p++;
107         while ((*p >= '0') && (*p <= '9'))
108         {
109             res->precision = res->precision * 10 + *p - '0';
110             p++;
111         }
112     }
113     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
114     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
115     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
116     else if (*p == 'I')
117     {
118         if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; }
119         else if (p[1] == '3' && p[2] == '2') p += 3;
120         else { res->flags |= WPRINTF_INTPTR; p++; }
121     }
122     switch(*p)
123     {
124     case 'c':
125         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
126         break;
127     case 'C':
128         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
129         break;
130     case 'd':
131     case 'i':
132         res->type = WPR_SIGNED;
133         break;
134     case 's':
135         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
136         break;
137     case 'S':
138         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
139         break;
140     case 'u':
141         res->type = WPR_UNSIGNED;
142         break;
143     case 'p':
144         res->width = 2 * sizeof(void *);
145         res->flags |= WPRINTF_ZEROPAD | WPRINTF_INTPTR;
146         /* fall through */
147     case 'X':
148         res->flags |= WPRINTF_UPPER_HEX;
149         /* fall through */
150     case 'x':
151         res->type = WPR_HEXA;
152         break;
153     default: /* unknown format char */
154         res->type = WPR_UNKNOWN;
155         p--;  /* print format as normal char */
156         break;
157     }
158     return (INT)(p - format) + 1;
159 }
160
161
162 /***********************************************************************
163  *           WPRINTF_ParseFormatW
164  *
165  * Parse a format specification. A format specification has the form:
166  *
167  * [-][#][0][width][.precision]type
168  *
169  * Return value is the length of the format specification in characters.
170  */
171 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
172 {
173     LPCWSTR p = format;
174
175     res->flags = 0;
176     res->width = 0;
177     res->precision = 0;
178     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
179     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
180     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
181     while ((*p >= '0') && (*p <= '9'))  /* width field */
182     {
183         res->width = res->width * 10 + *p - '0';
184         p++;
185     }
186     if (*p == '.')  /* precision field */
187     {
188         p++;
189         while ((*p >= '0') && (*p <= '9'))
190         {
191             res->precision = res->precision * 10 + *p - '0';
192             p++;
193         }
194     }
195     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
196     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
197     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
198     else if (*p == 'I')
199     {
200         if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; }
201         else if (p[1] == '3' && p[2] == '2') p += 3;
202         else { res->flags |= WPRINTF_INTPTR; p++; }
203     }
204     switch(*p)
205     {
206     case 'c':
207         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
208         break;
209     case 'C':
210         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
211         break;
212     case 'd':
213     case 'i':
214         res->type = WPR_SIGNED;
215         break;
216     case 's':
217         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
218         break;
219     case 'S':
220         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
221         break;
222     case 'u':
223         res->type = WPR_UNSIGNED;
224         break;
225     case 'p':
226         res->width = 2 * sizeof(void *);
227         res->flags |= WPRINTF_ZEROPAD | WPRINTF_INTPTR;
228         /* fall through */
229     case 'X':
230         res->flags |= WPRINTF_UPPER_HEX;
231         /* fall through */
232     case 'x':
233         res->type = WPR_HEXA;
234         break;
235     default:
236         res->type = WPR_UNKNOWN;
237         p--;  /* print format as normal char */
238         break;
239     }
240     return (INT)(p - format) + 1;
241 }
242
243
244 /***********************************************************************
245  *           WPRINTF_GetLen
246  */
247 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
248                               LPSTR number, UINT maxlen )
249 {
250     UINT len;
251
252     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
253     if (format->width > maxlen) format->width = maxlen;
254     switch(format->type)
255     {
256     case WPR_CHAR:
257     case WPR_WCHAR:
258         return (format->precision = 1);
259     case WPR_STRING:
260         if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
261         for (len = 0; !format->precision || (len < format->precision); len++)
262             if (!*(arg->lpcstr_view + len)) break;
263         if (len > maxlen) len = maxlen;
264         return (format->precision = len);
265     case WPR_WSTRING:
266         if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
267         for (len = 0; !format->precision || (len < format->precision); len++)
268             if (!*(arg->lpcwstr_view + len)) break;
269         if (len > maxlen) len = maxlen;
270         return (format->precision = len);
271     case WPR_SIGNED:
272     case WPR_UNSIGNED:
273     case WPR_HEXA:
274     {
275         const char *digits = (format->flags & WPRINTF_UPPER_HEX) ? "0123456789ABCDEF" : "0123456789abcdef";
276         ULONGLONG num = arg->int_view;
277         int base = format->type == WPR_HEXA ? 16 : 10;
278         char buffer[20], *p = buffer, *dst = number;
279
280         if (format->type == WPR_SIGNED && arg->int_view < 0)
281         {
282             *dst++ = '-';
283             num = -arg->int_view;
284         }
285         if (format->flags & WPRINTF_INTPTR) num = (UINT_PTR)num;
286         else if (!(format->flags & WPRINTF_I64)) num = (UINT)num;
287
288         do
289         {
290             *p++ = digits[num % base];
291             num /= base;
292         } while (num);
293         while (p > buffer) *dst++ = *(--p);
294         *dst = 0;
295         len = dst - number;
296         break;
297     }
298     default:
299         return 0;
300     }
301     if (len > maxlen) len = maxlen;
302     if (format->precision < len) format->precision = len;
303     if (format->precision > maxlen) format->precision = maxlen;
304     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
305         format->precision = format->width;
306     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
307     return len;
308 }
309
310
311 /***********************************************************************
312  *           wvsnprintfA   (internal)
313  */
314 static INT wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, __ms_va_list args )
315 {
316     WPRINTF_FORMAT format;
317     LPSTR p = buffer;
318     UINT i, len, sign;
319     CHAR number[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */
320     WPRINTF_DATA argData;
321
322     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
323
324     while (*spec && (maxlen > 1))
325     {
326         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
327         spec++;
328         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
329         spec += WPRINTF_ParseFormatA( spec, &format );
330
331         switch(format.type)
332         {
333         case WPR_WCHAR:
334             argData.wchar_view = (WCHAR)va_arg( args, int );
335             break;
336         case WPR_CHAR:
337             argData.char_view = (CHAR)va_arg( args, int );
338             break;
339         case WPR_STRING:
340             argData.lpcstr_view = va_arg( args, LPCSTR );
341             break;
342         case WPR_WSTRING:
343             argData.lpcwstr_view = va_arg( args, LPCWSTR );
344             break;
345         case WPR_HEXA:
346         case WPR_SIGNED:
347         case WPR_UNSIGNED:
348             if (format.flags & WPRINTF_INTPTR) argData.int_view = va_arg(args, INT_PTR);
349             else if (format.flags & WPRINTF_I64) argData.int_view = va_arg(args, LONGLONG);
350             else argData.int_view = va_arg(args, INT);
351             break;
352         default:
353             argData.wchar_view = 0;
354             break;
355         }
356
357         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
358         sign = 0;
359         if (!(format.flags & WPRINTF_LEFTALIGN))
360             for (i = format.precision; i < format.width; i++, maxlen--)
361                 *p++ = ' ';
362         switch(format.type)
363         {
364         case WPR_WCHAR:
365             *p++ = argData.wchar_view;
366             break;
367         case WPR_CHAR:
368             *p++ = argData.char_view;
369             break;
370         case WPR_STRING:
371             memcpy( p, argData.lpcstr_view, len );
372             p += len;
373             break;
374         case WPR_WSTRING:
375             {
376                 LPCWSTR ptr = argData.lpcwstr_view;
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             }
388             /* fall through */
389         case WPR_SIGNED:
390             /* Transfer the sign now, just in case it will be zero-padded*/
391             if (number[0] == '-')
392             {
393                 *p++ = '-';
394                 sign = 1;
395             }
396             /* fall through */
397         case WPR_UNSIGNED:
398             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
399             memcpy( p, number + sign, len - sign  );
400             p += len - sign;
401             break;
402         case WPR_UNKNOWN:
403             continue;
404         }
405         if (format.flags & WPRINTF_LEFTALIGN)
406             for (i = format.precision; i < format.width; i++, maxlen--)
407                 *p++ = ' ';
408         maxlen -= len;
409     }
410     *p = 0;
411     TRACE("%s\n",debugstr_a(buffer));
412     return (maxlen > 1) ? (INT)(p - buffer) : -1;
413 }
414
415
416 /***********************************************************************
417  *           wvsnprintfW   (internal)
418  */
419 static INT wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, __ms_va_list args )
420 {
421     WPRINTF_FORMAT format;
422     LPWSTR p = buffer;
423     UINT i, len, sign;
424     CHAR number[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */
425     WPRINTF_DATA argData;
426
427     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
428
429     while (*spec && (maxlen > 1))
430     {
431         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
432         spec++;
433         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
434         spec += WPRINTF_ParseFormatW( spec, &format );
435
436         switch(format.type)
437         {
438         case WPR_WCHAR:
439             argData.wchar_view = (WCHAR)va_arg( args, int );
440             break;
441         case WPR_CHAR:
442             argData.char_view = (CHAR)va_arg( args, int );
443             break;
444         case WPR_STRING:
445             argData.lpcstr_view = va_arg( args, LPCSTR );
446             break;
447         case WPR_WSTRING:
448             argData.lpcwstr_view = va_arg( args, LPCWSTR );
449             break;
450         case WPR_HEXA:
451         case WPR_SIGNED:
452         case WPR_UNSIGNED:
453             if (format.flags & WPRINTF_INTPTR) argData.int_view = va_arg(args, INT_PTR);
454             else if (format.flags & WPRINTF_I64) argData.int_view = va_arg(args, LONGLONG);
455             else argData.int_view = va_arg(args, INT);
456             break;
457         default:
458             argData.wchar_view = 0;
459             break;
460         }
461
462         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
463         sign = 0;
464         if (!(format.flags & WPRINTF_LEFTALIGN))
465             for (i = format.precision; i < format.width; i++, maxlen--)
466                 *p++ = ' ';
467         switch(format.type)
468         {
469         case WPR_WCHAR:
470             *p++ = argData.wchar_view;
471             break;
472         case WPR_CHAR:
473             *p++ = argData.char_view;
474             break;
475         case WPR_STRING:
476             {
477                 LPCSTR ptr = argData.lpcstr_view;
478                 for (i = 0; i < len; i++) *p++ = (BYTE)*ptr++;
479             }
480             break;
481         case WPR_WSTRING:
482             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
483             p += len;
484             break;
485         case WPR_HEXA:
486             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
487             {
488                 *p++ = '0';
489                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
490                 maxlen -= 2;
491                 len -= 2;
492             }
493             /* fall through */
494         case WPR_SIGNED:
495             /* Transfer the sign now, just in case it will be zero-padded*/
496             if (number[0] == '-')
497             {
498                 *p++ = '-';
499                 sign = 1;
500             }
501             /* fall through */
502         case WPR_UNSIGNED:
503             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
504             for (i = sign; i < len; i++) *p++ = (BYTE)number[i];
505             break;
506         case WPR_UNKNOWN:
507             continue;
508         }
509         if (format.flags & WPRINTF_LEFTALIGN)
510             for (i = format.precision; i < format.width; i++, maxlen--)
511                 *p++ = ' ';
512         maxlen -= len;
513     }
514     *p = 0;
515     TRACE("%s\n",debugstr_w(buffer));
516     return (maxlen > 1) ? (INT)(p - buffer) : -1;
517 }
518
519
520 /***********************************************************************
521  *           wvsprintfA   (USER32.@)
522  */
523 INT WINAPI wvsprintfA( LPSTR buffer, LPCSTR spec, __ms_va_list args )
524 {
525     INT res = wvsnprintfA( buffer, 1024, spec, args );
526     return ( res == -1 ) ? 1024 : res;
527 }
528
529
530 /***********************************************************************
531  *           wvsprintfW   (USER32.@)
532  */
533 INT WINAPI wvsprintfW( LPWSTR buffer, LPCWSTR spec, __ms_va_list args )
534 {
535     INT res = wvsnprintfW( buffer, 1024, spec, args );
536     return ( res == -1 ) ? 1024 : res;
537 }
538
539
540 /***********************************************************************
541  *           wsprintfA   (USER32.@)
542  */
543 INT WINAPIV wsprintfA( LPSTR buffer, LPCSTR spec, ... )
544 {
545     __ms_va_list valist;
546     INT res;
547
548     __ms_va_start( valist, spec );
549     res = wvsnprintfA( buffer, 1024, spec, valist );
550     __ms_va_end( valist );
551     return ( res == -1 ) ? 1024 : res;
552 }
553
554
555 /***********************************************************************
556  *           wsprintfW   (USER32.@)
557  */
558 INT WINAPIV wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
559 {
560     __ms_va_list valist;
561     INT res;
562
563     __ms_va_start( valist, spec );
564     res = wvsnprintfW( buffer, 1024, spec, valist );
565     __ms_va_end( valist );
566     return ( res == -1 ) ? 1024 : res;
567 }