GetDlgItemText should always try to NULL terminate the string.
[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 "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33
34 #include "wine/winbase16.h"
35 #include "wine/winuser16.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 'p':
139         res->width = 8;
140         res->flags |= WPRINTF_ZEROPAD;
141         /* fall through */
142     case 'X':
143         res->flags |= WPRINTF_UPPER_HEX;
144         /* fall through */
145     case 'x':
146         res->type = WPR_HEXA;
147         break;
148     default: /* unknown format char */
149         res->type = WPR_UNKNOWN;
150         p--;  /* print format as normal char */
151         break;
152     }
153     return (INT)(p - format) + 1;
154 }
155
156
157 /***********************************************************************
158  *           WPRINTF_ParseFormatW
159  *
160  * Parse a format specification. A format specification has the form:
161  *
162  * [-][#][0][width][.precision]type
163  *
164  * Return value is the length of the format specification in characters.
165  */
166 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
167 {
168     LPCWSTR p = format;
169
170     res->flags = 0;
171     res->width = 0;
172     res->precision = 0;
173     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
174     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
175     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
176     while ((*p >= '0') && (*p <= '9'))  /* width field */
177     {
178         res->width = res->width * 10 + *p - '0';
179         p++;
180     }
181     if (*p == '.')  /* precision field */
182     {
183         p++;
184         while ((*p >= '0') && (*p <= '9'))
185         {
186             res->precision = res->precision * 10 + *p - '0';
187             p++;
188         }
189     }
190     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
191     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
192     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
193     switch((CHAR)*p)
194     {
195     case 'c':
196         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
197         break;
198     case 'C':
199         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
200         break;
201     case 'd':
202     case 'i':
203         res->type = WPR_SIGNED;
204         break;
205     case 's':
206         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
207         break;
208     case 'S':
209         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
210         break;
211     case 'u':
212         res->type = WPR_UNSIGNED;
213         break;
214     case 'p':
215         res->width = 8;
216         res->flags |= WPRINTF_ZEROPAD;
217         /* fall through */
218     case 'X':
219         res->flags |= WPRINTF_UPPER_HEX;
220         /* fall through */
221     case 'x':
222         res->type = WPR_HEXA;
223         break;
224     default:
225         res->type = WPR_UNKNOWN;
226         p--;  /* print format as normal char */
227         break;
228     }
229     return (INT)(p - format) + 1;
230 }
231
232
233 /***********************************************************************
234  *           WPRINTF_GetLen
235  */
236 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
237                               LPSTR number, UINT maxlen )
238 {
239     UINT len;
240
241     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
242     if (format->width > maxlen) format->width = maxlen;
243     switch(format->type)
244     {
245     case WPR_CHAR:
246     case WPR_WCHAR:
247         return (format->precision = 1);
248     case WPR_STRING:
249         if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
250         for (len = 0; !format->precision || (len < format->precision); len++)
251             if (!*(arg->lpcstr_view + len)) break;
252         if (len > maxlen) len = maxlen;
253         return (format->precision = len);
254     case WPR_WSTRING:
255         if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
256         for (len = 0; !format->precision || (len < format->precision); len++)
257             if (!*(arg->lpcwstr_view + len)) break;
258         if (len > maxlen) len = maxlen;
259         return (format->precision = len);
260     case WPR_SIGNED:
261         len = sprintf( number, "%d", arg->int_view );
262         break;
263     case WPR_UNSIGNED:
264         len = sprintf( number, "%u", (UINT)arg->int_view );
265         break;
266     case WPR_HEXA:
267         len = sprintf( number,
268                        (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
269                        (UINT)arg->int_view);
270         break;
271     default:
272         return 0;
273     }
274     if (len > maxlen) len = maxlen;
275     if (format->precision < len) format->precision = len;
276     if (format->precision > maxlen) format->precision = maxlen;
277     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
278         format->precision = format->width;
279     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
280     return len;
281 }
282
283
284 /***********************************************************************
285  *           wvsnprintf16   (Not a Windows API)
286  */
287 static INT16 wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec, VA_LIST16 args )
288 {
289     WPRINTF_FORMAT format;
290     LPSTR p = buffer;
291     UINT i, len, sign;
292     CHAR number[20];
293     WPRINTF_DATA cur_arg;
294     SEGPTR seg_str;
295
296     while (*spec && (maxlen > 1))
297     {
298         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
299         spec++;
300         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
301         spec += WPRINTF_ParseFormatA( spec, &format );
302         switch(format.type)
303         {
304         case WPR_WCHAR:  /* No Unicode in Win16 */
305         case WPR_CHAR:
306             cur_arg.char_view = VA_ARG16( args, CHAR );
307             break;
308         case WPR_WSTRING:  /* No Unicode in Win16 */
309         case WPR_STRING:
310             seg_str = VA_ARG16( args, SEGPTR );
311             if (IsBadReadPtr16(seg_str, 1 )) cur_arg.lpcstr_view = "";
312             else cur_arg.lpcstr_view = MapSL( seg_str );
313             break;
314         case WPR_SIGNED:
315             if (!(format.flags & WPRINTF_LONG))
316             {
317                 cur_arg.int_view = VA_ARG16( args, INT16 );
318                 break;
319             }
320             /* fall through */
321         case WPR_HEXA:
322         case WPR_UNSIGNED:
323             if (format.flags & WPRINTF_LONG)
324                 cur_arg.int_view = VA_ARG16( args, UINT );
325             else
326                 cur_arg.int_view = VA_ARG16( args, UINT16 );
327             break;
328         case WPR_UNKNOWN:
329             continue;
330         }
331         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
332         sign = 0;
333         if (!(format.flags & WPRINTF_LEFTALIGN))
334             for (i = format.precision; i < format.width; i++, maxlen--)
335                 *p++ = ' ';
336         switch(format.type)
337         {
338         case WPR_WCHAR:  /* No Unicode in Win16 */
339         case WPR_CHAR:
340             *p= cur_arg.char_view;
341             /* wsprintf16 (unlike wsprintf) ignores null characters */
342             if (*p != '\0') p++;
343             else if (format.width > 1) *p++ = ' ';
344             else len = 0;
345             break;
346         case WPR_WSTRING:  /* No Unicode in Win16 */
347         case WPR_STRING:
348             if (len) memcpy( p, cur_arg.lpcstr_view, len );
349             p += len;
350             break;
351         case WPR_HEXA:
352             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
353             {
354                 *p++ = '0';
355                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
356                 maxlen -= 2;
357                 len -= 2;
358             }
359             /* fall through */
360         case WPR_SIGNED:
361             /* Transfer the sign now, just in case it will be zero-padded*/
362             if (number[0] == '-')
363             {
364                 *p++ = '-';
365                 sign = 1;
366             }
367             /* fall through */
368         case WPR_UNSIGNED:
369             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
370             if (len > sign) memcpy( p, number + sign, len - sign );
371             p += len-sign;
372             break;
373         case WPR_UNKNOWN:
374             continue;
375         }
376         if (format.flags & WPRINTF_LEFTALIGN)
377             for (i = format.precision; i < format.width; i++, maxlen--)
378                 *p++ = ' ';
379         maxlen -= len;
380     }
381     *p = 0;
382     return (maxlen > 1) ? (INT)(p - buffer) : -1;
383 }
384
385
386 /***********************************************************************
387  *           wvsnprintfA   (internal)
388  */
389 static INT wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
390 {
391     WPRINTF_FORMAT format;
392     LPSTR p = buffer;
393     UINT i, len, sign;
394     CHAR number[20];
395     WPRINTF_DATA argData;
396
397     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
398
399     while (*spec && (maxlen > 1))
400     {
401         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
402         spec++;
403         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
404         spec += WPRINTF_ParseFormatA( spec, &format );
405
406         switch(format.type)
407         {
408         case WPR_WCHAR:
409             argData.wchar_view = (WCHAR)va_arg( args, int );
410             break;
411         case WPR_CHAR:
412             argData.char_view = (CHAR)va_arg( args, int );
413             break;
414         case WPR_STRING:
415             argData.lpcstr_view = va_arg( args, LPCSTR );
416             break;
417         case WPR_WSTRING:
418             argData.lpcwstr_view = va_arg( args, LPCWSTR );
419             break;
420         case WPR_HEXA:
421         case WPR_SIGNED:
422         case WPR_UNSIGNED:
423             argData.int_view = va_arg( args, INT );
424             break;
425         default:
426             argData.wchar_view = 0;
427             break;
428         }
429
430         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
431         sign = 0;
432         if (!(format.flags & WPRINTF_LEFTALIGN))
433             for (i = format.precision; i < format.width; i++, maxlen--)
434                 *p++ = ' ';
435         switch(format.type)
436         {
437         case WPR_WCHAR:
438             *p++ = argData.wchar_view;
439             break;
440         case WPR_CHAR:
441             *p++ = argData.char_view;
442             break;
443         case WPR_STRING:
444             memcpy( p, argData.lpcstr_view, len );
445             p += len;
446             break;
447         case WPR_WSTRING:
448             {
449                 LPCWSTR ptr = argData.lpcwstr_view;
450                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
451             }
452             break;
453         case WPR_HEXA:
454             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
455             {
456                 *p++ = '0';
457                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
458                 maxlen -= 2;
459                 len -= 2;
460             }
461             /* fall through */
462         case WPR_SIGNED:
463             /* Transfer the sign now, just in case it will be zero-padded*/
464             if (number[0] == '-')
465             {
466                 *p++ = '-';
467                 sign = 1;
468             }
469             /* fall through */
470         case WPR_UNSIGNED:
471             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
472             memcpy( p, number + sign, len - sign  );
473             p += len - sign;
474             break;
475         case WPR_UNKNOWN:
476             continue;
477         }
478         if (format.flags & WPRINTF_LEFTALIGN)
479             for (i = format.precision; i < format.width; i++, maxlen--)
480                 *p++ = ' ';
481         maxlen -= len;
482     }
483     *p = 0;
484     TRACE("%s\n",debugstr_a(buffer));
485     return (maxlen > 1) ? (INT)(p - buffer) : -1;
486 }
487
488
489 /***********************************************************************
490  *           wvsnprintfW   (internal)
491  */
492 static INT wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
493 {
494     WPRINTF_FORMAT format;
495     LPWSTR p = buffer;
496     UINT i, len, sign;
497     CHAR number[20];
498     WPRINTF_DATA argData;
499
500     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
501
502     while (*spec && (maxlen > 1))
503     {
504         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
505         spec++;
506         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
507         spec += WPRINTF_ParseFormatW( spec, &format );
508
509         switch(format.type)
510         {
511         case WPR_WCHAR:
512             argData.wchar_view = (WCHAR)va_arg( args, int );
513             break;
514         case WPR_CHAR:
515             argData.char_view = (CHAR)va_arg( args, int );
516             break;
517         case WPR_STRING:
518             argData.lpcstr_view = va_arg( args, LPCSTR );
519             break;
520         case WPR_WSTRING:
521             argData.lpcwstr_view = va_arg( args, LPCWSTR );
522             break;
523         case WPR_HEXA:
524         case WPR_SIGNED:
525         case WPR_UNSIGNED:
526             argData.int_view = va_arg( args, INT );
527             break;
528         default:
529             argData.wchar_view = 0;
530             break;
531         }
532
533         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
534         sign = 0;
535         if (!(format.flags & WPRINTF_LEFTALIGN))
536             for (i = format.precision; i < format.width; i++, maxlen--)
537                 *p++ = ' ';
538         switch(format.type)
539         {
540         case WPR_WCHAR:
541             *p++ = argData.wchar_view;
542             break;
543         case WPR_CHAR:
544             *p++ = argData.char_view;
545             break;
546         case WPR_STRING:
547             {
548                 LPCSTR ptr = argData.lpcstr_view;
549                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
550             }
551             break;
552         case WPR_WSTRING:
553             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
554             p += len;
555             break;
556         case WPR_HEXA:
557             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
558             {
559                 *p++ = '0';
560                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
561                 maxlen -= 2;
562                 len -= 2;
563             }
564             /* fall through */
565         case WPR_SIGNED:
566             /* Transfer the sign now, just in case it will be zero-padded*/
567             if (number[0] == '-')
568             {
569                 *p++ = '-';
570                 sign = 1;
571             }
572             /* fall through */
573         case WPR_UNSIGNED:
574             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
575             for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
576             break;
577         case WPR_UNKNOWN:
578             continue;
579         }
580         if (format.flags & WPRINTF_LEFTALIGN)
581             for (i = format.precision; i < format.width; i++, maxlen--)
582                 *p++ = ' ';
583         maxlen -= len;
584     }
585     *p = 0;
586     TRACE("%s\n",debugstr_w(buffer));
587     return (maxlen > 1) ? (INT)(p - buffer) : -1;
588 }
589
590
591 /***********************************************************************
592  *           wvsprintf   (USER.421)
593  */
594 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, VA_LIST16 args )
595 {
596     INT16 res;
597
598     TRACE("for %p got:\n",buffer);
599     res = wvsnprintf16( buffer, 1024, spec, args );
600     return ( res == -1 ) ? 1024 : res;
601 }
602
603
604 /***********************************************************************
605  *           wvsprintfA   (USER32.@)
606  */
607 INT WINAPI wvsprintfA( LPSTR buffer, LPCSTR spec, va_list args )
608 {
609     INT res = wvsnprintfA( buffer, 1024, spec, args );
610     return ( res == -1 ) ? 1024 : res;
611 }
612
613
614 /***********************************************************************
615  *           wvsprintfW   (USER32.@)
616  */
617 INT WINAPI wvsprintfW( LPWSTR buffer, LPCWSTR spec, va_list args )
618 {
619     INT res = wvsnprintfW( buffer, 1024, spec, args );
620     return ( res == -1 ) ? 1024 : res;
621 }
622
623
624 /***********************************************************************
625  *           _wsprintf   (USER.420)
626  */
627 INT16 WINAPIV wsprintf16( LPSTR buffer, LPCSTR spec, VA_LIST16 valist )
628 {
629     INT16 res;
630
631     res = wvsnprintf16( buffer, 1024, spec, valist );
632     return ( res == -1 ) ? 1024 : res;
633 }
634
635
636 /***********************************************************************
637  *           wsprintfA   (USER32.@)
638  */
639 INT WINAPIV wsprintfA( LPSTR buffer, LPCSTR spec, ... )
640 {
641     va_list valist;
642     INT res;
643
644     va_start( valist, spec );
645     res = wvsnprintfA( buffer, 1024, spec, valist );
646     va_end( valist );
647     return ( res == -1 ) ? 1024 : res;
648 }
649
650
651 /***********************************************************************
652  *           wsprintfW   (USER32.@)
653  */
654 INT WINAPIV wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
655 {
656     va_list valist;
657     INT res;
658
659     va_start( valist, spec );
660     res = wvsnprintfW( buffer, 1024, spec, valist );
661     va_end( valist );
662     return ( res == -1 ) ? 1024 : res;
663 }