Fixed a race condition on RPC worker thread creation, and a typo.
[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  * Print formatted output to a string, up to a maximum number of chars.
276  *
277  * PARAMS
278  * lpOut      [O] Destination for output string
279  * cchLimitIn [I] Maximum number of characters to write
280  * lpFmt      [I] Format string
281  *
282  * RETURNS
283  *  Success: The number of characters written.
284  *  Failure: -1.
285  */
286 INT WINAPI wvnsprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
287 {
288     WPRINTF_FORMAT format;
289     LPSTR p = buffer;
290     UINT i, len, sign;
291     CHAR number[20];
292     WPRINTF_DATA argData;
293
294     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
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
303         switch(format.type)
304         {
305         case WPR_WCHAR:
306             argData.wchar_view = (WCHAR)va_arg( args, int );
307             break;
308         case WPR_CHAR:
309             argData.char_view = (CHAR)va_arg( args, int );
310             break;
311         case WPR_STRING:
312             argData.lpcstr_view = va_arg( args, LPCSTR );
313             break;
314         case WPR_WSTRING:
315             argData.lpcwstr_view = va_arg( args, LPCWSTR );
316             break;
317         case WPR_HEXA:
318         case WPR_SIGNED:
319         case WPR_UNSIGNED:
320             argData.int_view = va_arg( args, INT );
321             break;
322         default:
323             argData.wchar_view = 0;
324             break;
325         }
326
327         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
328         sign = 0;
329         if (!(format.flags & WPRINTF_LEFTALIGN))
330             for (i = format.precision; i < format.width; i++, maxlen--)
331                 *p++ = ' ';
332         switch(format.type)
333         {
334         case WPR_WCHAR:
335             *p++ = argData.wchar_view;
336             break;
337         case WPR_CHAR:
338             *p++ = argData.char_view;
339             break;
340         case WPR_STRING:
341             memcpy( p, argData.lpcstr_view, len );
342             p += len;
343             break;
344         case WPR_WSTRING:
345             {
346                 LPCWSTR ptr = argData.lpcwstr_view;
347                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
348             }
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             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     TRACE("%s\n",debugstr_a(buffer));
382     return (maxlen > 1) ? (INT)(p - buffer) : -1;
383 }
384
385
386 /***********************************************************************
387  *           wvnsprintfW   (SHLWAPI.@)
388  *
389  * See wvnsprintfA.
390  */
391 INT WINAPI wvnsprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
392 {
393     WPRINTF_FORMAT format;
394     LPWSTR p = buffer;
395     UINT i, len, sign;
396     CHAR number[20];
397     WPRINTF_DATA argData;
398
399     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
400
401     while (*spec && (maxlen > 1))
402     {
403         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
404         spec++;
405         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
406         spec += WPRINTF_ParseFormatW( spec, &format );
407
408         switch(format.type)
409         {
410         case WPR_WCHAR:
411             argData.wchar_view = (WCHAR)va_arg( args, int );
412             break;
413         case WPR_CHAR:
414             argData.char_view = (CHAR)va_arg( args, int );
415             break;
416         case WPR_STRING:
417             argData.lpcstr_view = va_arg( args, LPCSTR );
418             break;
419         case WPR_WSTRING:
420             argData.lpcwstr_view = va_arg( args, LPCWSTR );
421             break;
422         case WPR_HEXA:
423         case WPR_SIGNED:
424         case WPR_UNSIGNED:
425             argData.int_view = va_arg( args, INT );
426             break;
427         default:
428             argData.wchar_view = 0;
429             break;
430         }
431
432         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
433         sign = 0;
434         if (!(format.flags & WPRINTF_LEFTALIGN))
435             for (i = format.precision; i < format.width; i++, maxlen--)
436                 *p++ = ' ';
437         switch(format.type)
438         {
439         case WPR_WCHAR:
440             *p++ = argData.wchar_view;
441             break;
442         case WPR_CHAR:
443             *p++ = argData.char_view;
444             break;
445         case WPR_STRING:
446             {
447                 LPCSTR ptr = argData.lpcstr_view;
448                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
449             }
450             break;
451         case WPR_WSTRING:
452             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
453             p += len;
454             break;
455         case WPR_HEXA:
456             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
457             {
458                 *p++ = '0';
459                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
460                 maxlen -= 2;
461                 len -= 2;
462             }
463             /* fall through */
464         case WPR_SIGNED:
465             /* Transfer the sign now, just in case it will be zero-padded*/
466             if (number[0] == '-')
467             {
468                 *p++ = '-';
469                 sign = 1;
470             }
471             /* fall through */
472         case WPR_UNSIGNED:
473             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
474             for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
475             break;
476         case WPR_UNKNOWN:
477             continue;
478         }
479         if (format.flags & WPRINTF_LEFTALIGN)
480             for (i = format.precision; i < format.width; i++, maxlen--)
481                 *p++ = ' ';
482         maxlen -= len;
483     }
484     *p = 0;
485     TRACE("%s\n",debugstr_w(buffer));
486     return (maxlen > 1) ? (INT)(p - buffer) : -1;
487 }
488
489
490 /*************************************************************************
491  *           wnsprintfA   (SHLWAPI.@)
492  *
493  * Print formatted output to a string, up to a maximum number of chars.
494  *
495  * PARAMS
496  * lpOut      [O] Destination for output string
497  * cchLimitIn [I] Maximum number of characters to write
498  * lpFmt      [I] Format string
499  *
500  * RETURNS
501  *  Success: The number of characters written.
502  *  Failure: -1.
503  */
504 int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...)
505 {
506     va_list valist;
507     INT res;
508
509     va_start( valist, lpFmt );
510     res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist );
511     va_end( valist );
512     return res;
513 }
514
515
516 /*************************************************************************
517  *           wnsprintfW   (SHLWAPI.@)
518  *
519  * See wnsprintfA.
520  */
521 int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...)
522 {
523     va_list valist;
524     INT res;
525
526     va_start( valist, lpFmt );
527     res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist );
528     va_end( valist );
529     return res;
530 }