Add comment on what is known about the current state of the controls
[wine] / dlls / shlwapi / 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 user32. If you change something here make sure
22  * to change it in user32 too.
23  */
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include <stdio.h>
28
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.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  *           wvnsprintfA   (SHLWAPI.@)
274  */
275 INT WINAPI wvnsprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
276 {
277     WPRINTF_FORMAT format;
278     LPSTR p = buffer;
279     UINT i, len, sign;
280     CHAR number[20];
281     WPRINTF_DATA argData;
282
283     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
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
292         switch(format.type)
293         {
294         case WPR_WCHAR:
295             argData.wchar_view = (WCHAR)va_arg( args, int );
296             break;
297         case WPR_CHAR:
298             argData.char_view = (CHAR)va_arg( args, int );
299             break;
300         case WPR_STRING:
301             argData.lpcstr_view = va_arg( args, LPCSTR );
302             break;
303         case WPR_WSTRING:
304             argData.lpcwstr_view = va_arg( args, LPCWSTR );
305             break;
306         case WPR_HEXA:
307         case WPR_SIGNED:
308         case WPR_UNSIGNED:
309             argData.int_view = va_arg( args, INT );
310             break;
311         default:
312             argData.wchar_view = 0;
313             break;
314         }
315
316         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
317         sign = 0;
318         if (!(format.flags & WPRINTF_LEFTALIGN))
319             for (i = format.precision; i < format.width; i++, maxlen--)
320                 *p++ = ' ';
321         switch(format.type)
322         {
323         case WPR_WCHAR:
324             *p++ = argData.wchar_view;
325             break;
326         case WPR_CHAR:
327             *p++ = argData.char_view;
328             break;
329         case WPR_STRING:
330             memcpy( p, argData.lpcstr_view, len );
331             p += len;
332             break;
333         case WPR_WSTRING:
334             {
335                 LPCWSTR ptr = argData.lpcwstr_view;
336                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
337             }
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             /* Transfer the sign now, just in case it will be zero-padded*/
350             if (number[0] == '-')
351             {
352                 *p++ = '-';
353                 sign = 1;
354             }
355             /* fall through */
356         case WPR_UNSIGNED:
357             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
358             memcpy( p, number + sign, len - sign  );
359             p += len - sign;
360             break;
361         case WPR_UNKNOWN:
362             continue;
363         }
364         if (format.flags & WPRINTF_LEFTALIGN)
365             for (i = format.precision; i < format.width; i++, maxlen--)
366                 *p++ = ' ';
367         maxlen -= len;
368     }
369     *p = 0;
370     TRACE("%s\n",debugstr_a(buffer));
371     return (maxlen > 1) ? (INT)(p - buffer) : -1;
372 }
373
374
375 /***********************************************************************
376  *           wvnsprintfW   (SHLWAPI.@)
377  */
378 INT WINAPI wvnsprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
379 {
380     WPRINTF_FORMAT format;
381     LPWSTR 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_w(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_ParseFormatW( 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             {
434                 LPCSTR ptr = argData.lpcstr_view;
435                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
436             }
437             break;
438         case WPR_WSTRING:
439             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
440             p += len;
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             for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
462             break;
463         case WPR_UNKNOWN:
464             continue;
465         }
466         if (format.flags & WPRINTF_LEFTALIGN)
467             for (i = format.precision; i < format.width; i++, maxlen--)
468                 *p++ = ' ';
469         maxlen -= len;
470     }
471     *p = 0;
472     TRACE("%s\n",debugstr_w(buffer));
473     return (maxlen > 1) ? (INT)(p - buffer) : -1;
474 }
475
476
477 /*************************************************************************
478  *           wnsprintfA   (SHLWAPI.@)
479  */
480 int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...)
481 {
482     va_list valist;
483     INT res;
484
485     va_start( valist, lpFmt );
486     res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist );
487     va_end( valist );
488     return res;
489 }
490
491
492 /*************************************************************************
493  *           wnsprintfW   (SHLWAPI.@)
494  */
495 int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...)
496 {
497     va_list valist;
498     INT res;
499
500     va_start( valist, lpFmt );
501     res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist );
502     va_end( valist );
503     return res;
504 }