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