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