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[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */ 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[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */ 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++ = (BYTE)*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++ = (BYTE)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 } 574