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