amstream: COM cleanup for the IMediaStreamFilter iface.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "windef.h"
30 #include "winbase.h"
31 #define NO_SHLWAPI_REG
32 #include "shlwapi.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(string);
37
38
39 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
40 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
41 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
42 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
43 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
44 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
45 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
46 #define WPRINTF_INTPTR      0x0080  /* Pointer-size arg ('I' prefix) */
47 #define WPRINTF_I64         0x0100  /* 64-bit arg ('I64' 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     LONGLONG 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     else if (*p == 'I')
117     {
118         if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; }
119         else if (p[1] == '3' && p[2] == '2') p += 3;
120         else { res->flags |= WPRINTF_INTPTR; p++; }
121     }
122     switch(*p)
123     {
124     case 'c':
125         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
126         break;
127     case 'C':
128         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
129         break;
130     case 'd':
131     case 'i':
132         res->type = WPR_SIGNED;
133         break;
134     case 's':
135         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
136         break;
137     case 'S':
138         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
139         break;
140     case 'u':
141         res->type = WPR_UNSIGNED;
142         break;
143     case 'p':
144         res->width = 2 * sizeof(void *);
145         res->flags |= WPRINTF_ZEROPAD | WPRINTF_INTPTR;
146         /* fall through */
147     case 'X':
148         res->flags |= WPRINTF_UPPER_HEX;
149         /* fall through */
150     case 'x':
151         res->type = WPR_HEXA;
152         break;
153     default: /* unknown format char */
154         res->type = WPR_UNKNOWN;
155         p--;  /* print format as normal char */
156         break;
157     }
158     return (INT)(p - format) + 1;
159 }
160
161
162 /***********************************************************************
163  *           WPRINTF_ParseFormatW
164  *
165  * Parse a format specification. A format specification has the form:
166  *
167  * [-][#][0][width][.precision]type
168  *
169  * Return value is the length of the format specification in characters.
170  */
171 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
172 {
173     LPCWSTR p = format;
174
175     res->flags = 0;
176     res->width = 0;
177     res->precision = 0;
178     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
179     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
180     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
181     while ((*p >= '0') && (*p <= '9'))  /* width field */
182     {
183         res->width = res->width * 10 + *p - '0';
184         p++;
185     }
186     if (*p == '.')  /* precision field */
187     {
188         p++;
189         while ((*p >= '0') && (*p <= '9'))
190         {
191             res->precision = res->precision * 10 + *p - '0';
192             p++;
193         }
194     }
195     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
196     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
197     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
198     else if (*p == 'I')
199     {
200         if (p[1] == '6' && p[2] == '4') { res->flags |= WPRINTF_I64; p += 3; }
201         else if (p[1] == '3' && p[2] == '2') p += 3;
202         else { res->flags |= WPRINTF_INTPTR; p++; }
203     }
204     switch(*p)
205     {
206     case 'c':
207         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
208         break;
209     case 'C':
210         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
211         break;
212     case 'd':
213     case 'i':
214         res->type = WPR_SIGNED;
215         break;
216     case 's':
217         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
218         break;
219     case 'S':
220         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
221         break;
222     case 'u':
223         res->type = WPR_UNSIGNED;
224         break;
225     case 'p':
226         res->width = 2 * sizeof(void *);
227         res->flags |= WPRINTF_ZEROPAD | WPRINTF_INTPTR;
228         /* fall through */
229     case 'X':
230         res->flags |= WPRINTF_UPPER_HEX;
231         /* fall through */
232     case 'x':
233         res->type = WPR_HEXA;
234         break;
235     default:
236         res->type = WPR_UNKNOWN;
237         p--;  /* print format as normal char */
238         break;
239     }
240     return (INT)(p - format) + 1;
241 }
242
243
244 /***********************************************************************
245  *           WPRINTF_GetLen
246  */
247 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
248                               LPSTR number, UINT maxlen )
249 {
250     UINT len;
251
252     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
253     if (format->width > maxlen) format->width = maxlen;
254     switch(format->type)
255     {
256     case WPR_CHAR:
257     case WPR_WCHAR:
258         return (format->precision = 1);
259     case WPR_STRING:
260         if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
261         for (len = 0; !format->precision || (len < format->precision); len++)
262             if (!*(arg->lpcstr_view + len)) break;
263         if (len > maxlen) len = maxlen;
264         return (format->precision = len);
265     case WPR_WSTRING:
266         if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
267         for (len = 0; !format->precision || (len < format->precision); len++)
268             if (!*(arg->lpcwstr_view + len)) break;
269         if (len > maxlen) len = maxlen;
270         return (format->precision = len);
271     case WPR_SIGNED:
272     case WPR_UNSIGNED:
273     case WPR_HEXA:
274     {
275         const char *digits = (format->flags & WPRINTF_UPPER_HEX) ? "0123456789ABCDEF" : "0123456789abcdef";
276         ULONGLONG num = arg->int_view;
277         int base = format->type == WPR_HEXA ? 16 : 10;
278         char buffer[20], *p = buffer, *dst = number;
279
280         if (format->type == WPR_SIGNED && arg->int_view < 0)
281         {
282             *dst++ = '-';
283             num = -arg->int_view;
284         }
285         if (format->flags & WPRINTF_INTPTR) num = (UINT_PTR)num;
286         else if (!(format->flags & WPRINTF_I64)) num = (UINT)num;
287
288         do
289         {
290             *p++ = digits[num % base];
291             num /= base;
292         } while (num);
293         while (p > buffer) *dst++ = *(--p);
294         *dst = 0;
295         len = dst - number;
296         break;
297     }
298     default:
299         return 0;
300     }
301     if (len > maxlen) len = maxlen;
302     if (format->precision < len) format->precision = len;
303     if (format->precision > maxlen) format->precision = maxlen;
304     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
305         format->precision = format->width;
306     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
307     return len;
308 }
309
310
311 /***********************************************************************
312  *           wvnsprintfA   (SHLWAPI.@)
313  *
314  * Print formatted output to a string, up to a maximum number of chars.
315  *
316  * PARAMS
317  * buffer [O] Destination for output string
318  * maxlen [I] Maximum number of characters to write
319  * spec   [I] Format string
320  *
321  * RETURNS
322  *  Success: The number of characters written.
323  *  Failure: -1.
324  */
325 INT WINAPI wvnsprintfA( LPSTR buffer, INT maxlen, LPCSTR spec, __ms_va_list args )
326 {
327     WPRINTF_FORMAT format;
328     LPSTR p = buffer;
329     UINT i, len, sign;
330     CHAR number[20];
331     WPRINTF_DATA argData;
332
333     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
334
335     while (*spec && (maxlen > 1))
336     {
337         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
338         spec++;
339         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
340         spec += WPRINTF_ParseFormatA( spec, &format );
341
342         switch(format.type)
343         {
344         case WPR_WCHAR:
345             argData.wchar_view = (WCHAR)va_arg( args, int );
346             break;
347         case WPR_CHAR:
348             argData.char_view = (CHAR)va_arg( args, int );
349             break;
350         case WPR_STRING:
351             argData.lpcstr_view = va_arg( args, LPCSTR );
352             break;
353         case WPR_WSTRING:
354             argData.lpcwstr_view = va_arg( args, LPCWSTR );
355             break;
356         case WPR_HEXA:
357         case WPR_SIGNED:
358         case WPR_UNSIGNED:
359             if (format.flags & WPRINTF_INTPTR) argData.int_view = va_arg(args, INT_PTR);
360             else if (format.flags & WPRINTF_I64) argData.int_view = va_arg(args, LONGLONG);
361             else argData.int_view = va_arg(args, INT);
362             break;
363         default:
364             argData.wchar_view = 0;
365             break;
366         }
367
368         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
369         sign = 0;
370         if (!(format.flags & WPRINTF_LEFTALIGN))
371             for (i = format.precision; i < format.width; i++, maxlen--)
372                 *p++ = ' ';
373         switch(format.type)
374         {
375         case WPR_WCHAR:
376             *p++ = argData.wchar_view;
377             break;
378         case WPR_CHAR:
379             *p++ = argData.char_view;
380             break;
381         case WPR_STRING:
382             memcpy( p, argData.lpcstr_view, len );
383             p += len;
384             break;
385         case WPR_WSTRING:
386             {
387                 LPCWSTR ptr = argData.lpcwstr_view;
388                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
389             }
390             break;
391         case WPR_HEXA:
392             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
393             {
394                 *p++ = '0';
395                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
396                 maxlen -= 2;
397                 len -= 2;
398             }
399             /* fall through */
400         case WPR_SIGNED:
401             /* Transfer the sign now, just in case it will be zero-padded*/
402             if (number[0] == '-')
403             {
404                 *p++ = '-';
405                 sign = 1;
406             }
407             /* fall through */
408         case WPR_UNSIGNED:
409             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
410             memcpy( p, number + sign, len - sign  );
411             p += len - sign;
412             break;
413         case WPR_UNKNOWN:
414             continue;
415         }
416         if (format.flags & WPRINTF_LEFTALIGN)
417             for (i = format.precision; i < format.width; i++, maxlen--)
418                 *p++ = ' ';
419         maxlen -= len;
420     }
421     *p = 0;
422     TRACE("%s\n",debugstr_a(buffer));
423     return (maxlen > 1) ? (INT)(p - buffer) : -1;
424 }
425
426
427 /***********************************************************************
428  *           wvnsprintfW   (SHLWAPI.@)
429  *
430  * See wvnsprintfA.
431  */
432 INT WINAPI wvnsprintfW( LPWSTR buffer, INT maxlen, LPCWSTR spec, __ms_va_list args )
433 {
434     WPRINTF_FORMAT format;
435     LPWSTR p = buffer;
436     UINT i, len, sign;
437     CHAR number[20];
438     WPRINTF_DATA argData;
439
440     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
441
442     while (*spec && (maxlen > 1))
443     {
444         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
445         spec++;
446         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
447         spec += WPRINTF_ParseFormatW( spec, &format );
448
449         switch(format.type)
450         {
451         case WPR_WCHAR:
452             argData.wchar_view = (WCHAR)va_arg( args, int );
453             break;
454         case WPR_CHAR:
455             argData.char_view = (CHAR)va_arg( args, int );
456             break;
457         case WPR_STRING:
458             argData.lpcstr_view = va_arg( args, LPCSTR );
459             break;
460         case WPR_WSTRING:
461             argData.lpcwstr_view = va_arg( args, LPCWSTR );
462             break;
463         case WPR_HEXA:
464         case WPR_SIGNED:
465         case WPR_UNSIGNED:
466             if (format.flags & WPRINTF_INTPTR) argData.int_view = va_arg(args, INT_PTR);
467             else if (format.flags & WPRINTF_I64) argData.int_view = va_arg(args, LONGLONG);
468             else argData.int_view = va_arg(args, INT);
469             break;
470         default:
471             argData.wchar_view = 0;
472             break;
473         }
474
475         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
476         sign = 0;
477         if (!(format.flags & WPRINTF_LEFTALIGN))
478             for (i = format.precision; i < format.width; i++, maxlen--)
479                 *p++ = ' ';
480         switch(format.type)
481         {
482         case WPR_WCHAR:
483             *p++ = argData.wchar_view;
484             break;
485         case WPR_CHAR:
486             *p++ = argData.char_view;
487             break;
488         case WPR_STRING:
489             {
490                 LPCSTR ptr = argData.lpcstr_view;
491                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
492             }
493             break;
494         case WPR_WSTRING:
495             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
496             p += len;
497             break;
498         case WPR_HEXA:
499             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
500             {
501                 *p++ = '0';
502                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
503                 maxlen -= 2;
504                 len -= 2;
505             }
506             /* fall through */
507         case WPR_SIGNED:
508             /* Transfer the sign now, just in case it will be zero-padded*/
509             if (number[0] == '-')
510             {
511                 *p++ = '-';
512                 sign = 1;
513             }
514             /* fall through */
515         case WPR_UNSIGNED:
516             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
517             for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
518             break;
519         case WPR_UNKNOWN:
520             continue;
521         }
522         if (format.flags & WPRINTF_LEFTALIGN)
523             for (i = format.precision; i < format.width; i++, maxlen--)
524                 *p++ = ' ';
525         maxlen -= len;
526     }
527     *p = 0;
528     TRACE("%s\n",debugstr_w(buffer));
529     return (maxlen > 1) ? (INT)(p - buffer) : -1;
530 }
531
532
533 /*************************************************************************
534  *           wnsprintfA   (SHLWAPI.@)
535  *
536  * Print formatted output to a string, up to a maximum number of chars.
537  *
538  * PARAMS
539  * lpOut      [O] Destination for output string
540  * cchLimitIn [I] Maximum number of characters to write
541  * lpFmt      [I] Format string
542  *
543  * RETURNS
544  *  Success: The number of characters written.
545  *  Failure: -1.
546  */
547 int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...)
548 {
549     __ms_va_list valist;
550     INT res;
551
552     __ms_va_start( valist, lpFmt );
553     res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist );
554     __ms_va_end( valist );
555     return res;
556 }
557
558
559 /*************************************************************************
560  *           wnsprintfW   (SHLWAPI.@)
561  *
562  * See wnsprintfA.
563  */
564 int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...)
565 {
566     __ms_va_list valist;
567     INT res;
568
569     __ms_va_start( valist, lpFmt );
570     res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist );
571     __ms_va_end( valist );
572     return res;
573 }