Better implementation of GetCalendarInfo{A,W}, not perfect.
[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, sign;
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         sign = 0;
322         if (!(format.flags & WPRINTF_LEFTALIGN))
323             for (i = format.precision; i < format.width; i++, maxlen--)
324                 *p++ = ' ';
325         switch(format.type)
326         {
327         case WPR_WCHAR:  /* No Unicode in Win16 */
328         case WPR_CHAR:
329             *p= cur_arg.char_view;
330             /* wsprintf16 (unlike wsprintf) ignores null characters */
331             if (*p != '\0') p++;
332             else if (format.width > 1) *p++ = ' ';
333             else len = 0;
334             break;
335         case WPR_WSTRING:  /* No Unicode in Win16 */
336         case WPR_STRING:
337             if (len) memcpy( p, cur_arg.lpcstr_view, len );
338             p += len;
339             break;
340         case WPR_HEXA:
341             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
342             {
343                 *p++ = '0';
344                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
345                 maxlen -= 2;
346                 len -= 2;
347             }
348             /* fall through */
349         case WPR_SIGNED:
350             /* Transfer the sign now, just in case it will be zero-padded*/
351             if (number[0] == '-')
352             {
353                 *p++ = '-';
354                 sign = 1;
355             }
356             /* fall through */
357         case WPR_UNSIGNED:
358             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
359             if (len > sign) memcpy( p, number + sign, len - sign );
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   (USER32.@) (Not a Windows API, but we export it from USER32 anyway)
377  */
378 INT WINAPI wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
379 {
380     WPRINTF_FORMAT format;
381     LPSTR p = buffer;
382     UINT i, len, sign;
383     CHAR number[20];
384     WPRINTF_DATA argData;
385
386     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
387
388     while (*spec && (maxlen > 1))
389     {
390         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
391         spec++;
392         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
393         spec += WPRINTF_ParseFormatA( spec, &format );
394
395         switch(format.type)
396         {
397         case WPR_WCHAR:
398             argData.wchar_view = (WCHAR)va_arg( args, int );
399             break;
400         case WPR_CHAR:
401             argData.char_view = (CHAR)va_arg( args, int );
402             break;
403         case WPR_STRING:
404             argData.lpcstr_view = va_arg( args, LPCSTR );
405             break;
406         case WPR_WSTRING:
407             argData.lpcwstr_view = va_arg( args, LPCWSTR );
408             break;
409         case WPR_HEXA:
410         case WPR_SIGNED:
411         case WPR_UNSIGNED:
412             argData.int_view = va_arg( args, INT );
413             break;
414         default:
415             argData.wchar_view = 0;
416             break;
417         }
418
419         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
420         sign = 0;
421         if (!(format.flags & WPRINTF_LEFTALIGN))
422             for (i = format.precision; i < format.width; i++, maxlen--)
423                 *p++ = ' ';
424         switch(format.type)
425         {
426         case WPR_WCHAR:
427             *p++ = argData.wchar_view;
428             break;
429         case WPR_CHAR:
430             *p++ = argData.char_view;
431             break;
432         case WPR_STRING:
433             memcpy( p, argData.lpcstr_view, len );
434             p += len;
435             break;
436         case WPR_WSTRING:
437             {
438                 LPCWSTR ptr = argData.lpcwstr_view;
439                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
440             }
441             break;
442         case WPR_HEXA:
443             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
444             {
445                 *p++ = '0';
446                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
447                 maxlen -= 2;
448                 len -= 2;
449             }
450             /* fall through */
451         case WPR_SIGNED:
452             /* Transfer the sign now, just in case it will be zero-padded*/
453             if (number[0] == '-')
454             {
455                 *p++ = '-';
456                 sign = 1;
457             }
458             /* fall through */
459         case WPR_UNSIGNED:
460             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
461             memcpy( p, number + sign, len - sign  );
462             p += len - sign;
463             break;
464         case WPR_UNKNOWN:
465             continue;
466         }
467         if (format.flags & WPRINTF_LEFTALIGN)
468             for (i = format.precision; i < format.width; i++, maxlen--)
469                 *p++ = ' ';
470         maxlen -= len;
471     }
472     *p = 0;
473     TRACE("%s\n",debugstr_a(buffer));
474     return (maxlen > 1) ? (INT)(p - buffer) : -1;
475 }
476
477
478 /***********************************************************************
479  *           wvsnprintfW   (USER32.@) (Not a Windows API, but we export it from USER32 anyway)
480  */
481 INT WINAPI wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
482 {
483     WPRINTF_FORMAT format;
484     LPWSTR p = buffer;
485     UINT i, len, sign;
486     CHAR number[20];
487     WPRINTF_DATA argData;
488
489     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
490
491     while (*spec && (maxlen > 1))
492     {
493         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
494         spec++;
495         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
496         spec += WPRINTF_ParseFormatW( spec, &format );
497
498         switch(format.type)
499         {
500         case WPR_WCHAR:
501             argData.wchar_view = (WCHAR)va_arg( args, int );
502             break;
503         case WPR_CHAR:
504             argData.char_view = (CHAR)va_arg( args, int );
505             break;
506         case WPR_STRING:
507             argData.lpcstr_view = va_arg( args, LPCSTR );
508             break;
509         case WPR_WSTRING:
510             argData.lpcwstr_view = va_arg( args, LPCWSTR );
511             break;
512         case WPR_HEXA:
513         case WPR_SIGNED:
514         case WPR_UNSIGNED:
515             argData.int_view = va_arg( args, INT );
516             break;
517         default:
518             argData.wchar_view = 0;
519             break;
520         }
521
522         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
523         sign = 0;
524         if (!(format.flags & WPRINTF_LEFTALIGN))
525             for (i = format.precision; i < format.width; i++, maxlen--)
526                 *p++ = ' ';
527         switch(format.type)
528         {
529         case WPR_WCHAR:
530             *p++ = argData.wchar_view;
531             break;
532         case WPR_CHAR:
533             *p++ = argData.char_view;
534             break;
535         case WPR_STRING:
536             {
537                 LPCSTR ptr = argData.lpcstr_view;
538                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
539             }
540             break;
541         case WPR_WSTRING:
542             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
543             p += len;
544             break;
545         case WPR_HEXA:
546             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
547             {
548                 *p++ = '0';
549                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
550                 maxlen -= 2;
551                 len -= 2;
552             }
553             /* fall through */
554         case WPR_SIGNED:
555             /* Transfer the sign now, just in case it will be zero-padded*/
556             if (number[0] == '-')
557             {
558                 *p++ = '-';
559                 sign = 1;
560             }
561             /* fall through */
562         case WPR_UNSIGNED:
563             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
564             for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
565             break;
566         case WPR_UNKNOWN:
567             continue;
568         }
569         if (format.flags & WPRINTF_LEFTALIGN)
570             for (i = format.precision; i < format.width; i++, maxlen--)
571                 *p++ = ' ';
572         maxlen -= len;
573     }
574     *p = 0;
575     TRACE("%s\n",debugstr_w(buffer));
576     return (maxlen > 1) ? (INT)(p - buffer) : -1;
577 }
578
579
580 /***********************************************************************
581  *           wvsprintf   (USER.421)
582  */
583 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
584 {
585     INT16 res;
586
587     TRACE("for %p got:\n",buffer);
588     res = wvsnprintf16( buffer, 1024, spec, args );
589     return ( res == -1 ) ? 1024 : res;
590 }
591
592
593 /***********************************************************************
594  *           wvsprintfA   (USER32.@)
595  */
596 INT WINAPI wvsprintfA( LPSTR buffer, LPCSTR spec, va_list args )
597 {
598     INT res = wvsnprintfA( buffer, 1024, spec, args );
599     return ( res == -1 ) ? 1024 : res;
600 }
601
602
603 /***********************************************************************
604  *           wvsprintfW   (USER32.@)
605  */
606 INT WINAPI wvsprintfW( LPWSTR buffer, LPCWSTR spec, va_list args )
607 {
608     INT res = wvsnprintfW( buffer, 1024, spec, args );
609     return ( res == -1 ) ? 1024 : res;
610 }
611
612
613 /***********************************************************************
614  *           _wsprintf   (USER.420)
615  */
616 INT16 WINAPIV wsprintf16(void)
617 {
618     VA_LIST16 valist;
619     INT16 res;
620     SEGPTR buffer, spec;
621
622     VA_START16( valist );
623     buffer = VA_ARG16( valist, SEGPTR );
624     spec   = VA_ARG16( valist, SEGPTR );
625     res = wvsnprintf16( MapSL(buffer), 1024, MapSL(spec), valist );
626     VA_END16( valist );
627     return ( res == -1 ) ? 1024 : res;
628 }
629
630
631 /***********************************************************************
632  *           wsprintfA   (USER32.@)
633  */
634 INT WINAPIV wsprintfA( LPSTR buffer, LPCSTR spec, ... )
635 {
636     va_list valist;
637     INT res;
638
639     va_start( valist, spec );
640     res = wvsnprintfA( buffer, 1024, spec, valist );
641     va_end( valist );
642     return ( res == -1 ) ? 1024 : res;
643 }
644
645
646 /***********************************************************************
647  *           wsprintfW   (USER32.@)
648  */
649 INT WINAPIV wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
650 {
651     va_list valist;
652     INT res;
653
654     va_start( valist, spec );
655     res = wvsnprintfW( buffer, 1024, spec, valist );
656     va_end( valist );
657     return ( res == -1 ) ? 1024 : res;
658 }