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