Printf.cpp (27711B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Portable safe sprintf code. 9 * 10 * Author: Kipp E.B. Hickman 11 */ 12 13 #include "double-conversion/double-to-string.h" 14 #include "mozilla/AllocPolicy.h" 15 #include "mozilla/Printf.h" 16 #include "mozilla/UniquePtrExtensions.h" 17 #include "mozilla/Vector.h" 18 19 #include <stdarg.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 #if defined(XP_WIN) 25 # include <windows.h> 26 #endif 27 28 using double_conversion::DoubleToStringConverter; 29 using DTSC = DoubleToStringConverter; 30 31 /* 32 * Numbered Argument State 33 */ 34 struct NumArgState { 35 int type; // type of the current ap 36 va_list ap; // point to the corresponding position on ap 37 }; 38 39 using NumArgStateVector = 40 mozilla::Vector<NumArgState, 20, mozilla::MallocAllocPolicy>; 41 42 // For values up to and including TYPE_DOUBLE, the lowest bit indicates 43 // whether the type is signed (0) or unsigned (1). 44 #define TYPE_SHORT 0 45 #define TYPE_USHORT 1 46 #define TYPE_INTN 2 47 #define TYPE_UINTN 3 48 #define TYPE_LONG 4 49 #define TYPE_ULONG 5 50 #define TYPE_LONGLONG 6 51 #define TYPE_ULONGLONG 7 52 #define TYPE_DOUBLE 8 53 #define TYPE_STRING 9 54 #define TYPE_INTSTR 10 55 #define TYPE_POINTER 11 56 #if defined(XP_WIN) 57 # define TYPE_WSTRING 12 58 #endif 59 #define TYPE_SCHAR 14 60 #define TYPE_UCHAR 15 61 #define TYPE_UNKNOWN 20 62 63 #define FLAG_LEFT 0x1 64 #define FLAG_SIGNED 0x2 65 #define FLAG_SPACED 0x4 66 #define FLAG_ZEROS 0x8 67 #define FLAG_NEG 0x10 68 69 static const char hex[] = "0123456789abcdef"; 70 static const char HEX[] = "0123456789ABCDEF"; 71 72 // Fill into the buffer using the data in src 73 bool mozilla::PrintfTarget::fill2(const char* src, int srclen, int width, 74 int flags) { 75 char space = ' '; 76 77 width -= srclen; 78 if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting 79 if (flags & FLAG_ZEROS) { 80 space = '0'; 81 } 82 while (--width >= 0) { 83 if (!emit(&space, 1)) { 84 return false; 85 } 86 } 87 } 88 89 // Copy out the source data 90 if (!emit(src, srclen)) { 91 return false; 92 } 93 94 if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting 95 while (--width >= 0) { 96 if (!emit(&space, 1)) { 97 return false; 98 } 99 } 100 } 101 return true; 102 } 103 104 /* 105 * Fill a number. The order is: optional-sign zero-filling conversion-digits 106 */ 107 bool mozilla::PrintfTarget::fill_n(const char* src, int srclen, int width, 108 int prec, int type, int flags) { 109 int zerowidth = 0; 110 int precwidth = 0; 111 int leftspaces = 0; 112 int rightspaces = 0; 113 int cvtwidth; 114 char sign = 0; 115 116 if ((type & 1) == 0) { 117 if (flags & FLAG_NEG) { 118 sign = '-'; 119 } else if (flags & FLAG_SIGNED) { 120 sign = '+'; 121 } else if (flags & FLAG_SPACED) { 122 sign = ' '; 123 } 124 } 125 cvtwidth = (sign ? 1 : 0) + srclen; 126 127 if (prec > 0 && (type != TYPE_DOUBLE)) { 128 if (prec > srclen) { 129 precwidth = prec - srclen; // Need zero filling 130 cvtwidth += precwidth; 131 } 132 } 133 134 if ((flags & FLAG_ZEROS) && ((type == TYPE_DOUBLE) || (prec < 0))) { 135 if (width > cvtwidth) { 136 zerowidth = width - cvtwidth; // Zero filling 137 cvtwidth += zerowidth; 138 } 139 } 140 141 if (flags & FLAG_LEFT) { 142 if (width > cvtwidth) { 143 // Space filling on the right (i.e. left adjusting) 144 rightspaces = width - cvtwidth; 145 } 146 } else { 147 if (width > cvtwidth) { 148 // Space filling on the left (i.e. right adjusting) 149 leftspaces = width - cvtwidth; 150 } 151 } 152 while (--leftspaces >= 0) { 153 if (!emit(" ", 1)) { 154 return false; 155 } 156 } 157 if (sign) { 158 if (!emit(&sign, 1)) { 159 return false; 160 } 161 } 162 while (--precwidth >= 0) { 163 if (!emit("0", 1)) { 164 return false; 165 } 166 } 167 while (--zerowidth >= 0) { 168 if (!emit("0", 1)) { 169 return false; 170 } 171 } 172 if (!emit(src, uint32_t(srclen))) { 173 return false; 174 } 175 while (--rightspaces >= 0) { 176 if (!emit(" ", 1)) { 177 return false; 178 } 179 } 180 return true; 181 } 182 183 // All that the cvt_* functions care about as far as the TYPE_* constants is 184 // that the low bit is set to indicate unsigned, or unset to indicate signed. 185 // So we don't try to hard to ensure that the passed TYPE_* constant lines 186 // up with the actual size of the number being printed here. The main printf 187 // code, below, does have to care so that the correct bits are extracted from 188 // the varargs list. 189 bool mozilla::PrintfTarget::appendIntDec(int32_t num) { 190 int flags = 0; 191 long n = num; 192 if (n < 0) { 193 n = -n; 194 flags |= FLAG_NEG; 195 } 196 return cvt_l(n, -1, -1, 10, TYPE_INTN, flags, hex); 197 } 198 199 bool mozilla::PrintfTarget::appendIntDec(uint32_t num) { 200 return cvt_l(num, -1, -1, 10, TYPE_UINTN, 0, hex); 201 } 202 203 bool mozilla::PrintfTarget::appendIntOct(uint32_t num) { 204 return cvt_l(num, -1, -1, 8, TYPE_UINTN, 0, hex); 205 } 206 207 bool mozilla::PrintfTarget::appendIntHex(uint32_t num) { 208 return cvt_l(num, -1, -1, 16, TYPE_UINTN, 0, hex); 209 } 210 211 bool mozilla::PrintfTarget::appendIntDec(int64_t num) { 212 int flags = 0; 213 if (num < 0) { 214 num = -num; 215 flags |= FLAG_NEG; 216 } 217 return cvt_ll(num, -1, -1, 10, TYPE_INTN, flags, hex); 218 } 219 220 bool mozilla::PrintfTarget::appendIntDec(uint64_t num) { 221 return cvt_ll(num, -1, -1, 10, TYPE_UINTN, 0, hex); 222 } 223 224 bool mozilla::PrintfTarget::appendIntOct(uint64_t num) { 225 return cvt_ll(num, -1, -1, 8, TYPE_UINTN, 0, hex); 226 } 227 228 bool mozilla::PrintfTarget::appendIntHex(uint64_t num) { 229 return cvt_ll(num, -1, -1, 16, TYPE_UINTN, 0, hex); 230 } 231 232 /* Convert a long into its printable form. */ 233 bool mozilla::PrintfTarget::cvt_l(long num, int width, int prec, int radix, 234 int type, int flags, const char* hexp) { 235 char cvtbuf[100]; 236 char* cvt; 237 int digits; 238 239 // according to the man page this needs to happen 240 if ((prec == 0) && (num == 0)) { 241 return fill_n("", 0, width, prec, type, flags); 242 } 243 244 // Converting decimal is a little tricky. In the unsigned case we 245 // need to stop when we hit 10 digits. In the signed case, we can 246 // stop when the number is zero. 247 cvt = cvtbuf + sizeof(cvtbuf); 248 digits = 0; 249 while (num) { 250 int digit = (((unsigned long)num) % radix) & 0xF; 251 *--cvt = hexp[digit]; 252 digits++; 253 num = (long)(((unsigned long)num) / radix); 254 } 255 if (digits == 0) { 256 *--cvt = '0'; 257 digits++; 258 } 259 260 // Now that we have the number converted without its sign, deal with 261 // the sign and zero padding. 262 return fill_n(cvt, digits, width, prec, type, flags); 263 } 264 265 /* Convert a 64-bit integer into its printable form. */ 266 bool mozilla::PrintfTarget::cvt_ll(int64_t num, int width, int prec, int radix, 267 int type, int flags, const char* hexp) { 268 // According to the man page, this needs to happen. 269 if (prec == 0 && num == 0) { 270 return fill_n("", 0, width, prec, type, flags); 271 } 272 273 // Converting decimal is a little tricky. In the unsigned case we 274 // need to stop when we hit 10 digits. In the signed case, we can 275 // stop when the number is zero. 276 int64_t rad = int64_t(radix); 277 char cvtbuf[100]; 278 char* cvt = cvtbuf + sizeof(cvtbuf); 279 int digits = 0; 280 while (num != 0) { 281 int64_t quot = uint64_t(num) / rad; 282 int64_t rem = uint64_t(num) % rad; 283 int32_t digit = int32_t(rem); 284 *--cvt = hexp[digit & 0xf]; 285 digits++; 286 num = quot; 287 } 288 if (digits == 0) { 289 *--cvt = '0'; 290 digits++; 291 } 292 293 // Now that we have the number converted without its sign, deal with 294 // the sign and zero padding. 295 return fill_n(cvt, digits, width, prec, type, flags); 296 } 297 298 template <size_t N> 299 constexpr static size_t lengthof(const char (&)[N]) { 300 return N - 1; 301 } 302 303 // Longest possible output from ToFixed for positive numbers: 304 // [0-9]{kMaxFixedDigitsBeforePoint}\.[0-9]{kMaxFixedDigitsAfterPoint}? 305 constexpr int FIXED_MAX_CHARS = 306 DTSC::kMaxFixedDigitsBeforePoint + 1 + DTSC::kMaxFixedDigitsAfterPoint; 307 308 // Longest possible output from ToExponential: 309 // [1-9]\.[0-9]{kMaxExponentialDigits}e[+-][0-9]{1,3} 310 // (std::numeric_limits<double>::max() has exponent 308). 311 constexpr int EXPONENTIAL_MAX_CHARS = 312 lengthof("1.") + DTSC::kMaxExponentialDigits + lengthof("e+999"); 313 314 // Longest possible output from ToPrecise: 315 // [0-9\.]{kMaxPrecisionDigits+1} or 316 // [1-9]\.[0-9]{kMaxPrecisionDigits-1}e[+-][0-9]{1,3} 317 constexpr int PRECISE_MAX_CHARS = 318 lengthof("1.") + DTSC::kMaxPrecisionDigits - 1 + lengthof("e+999"); 319 320 constexpr int DTSC_MAX_CHARS = 321 std::max({FIXED_MAX_CHARS, EXPONENTIAL_MAX_CHARS, PRECISE_MAX_CHARS}); 322 323 /* 324 * Convert a double precision floating point number into its printable 325 * form. 326 */ 327 bool mozilla::PrintfTarget::cvt_f(double d, char c, int width, int prec, 328 int flags) { 329 bool lower = islower(c); 330 const char* inf = lower ? "inf" : "INF"; 331 const char* nan = lower ? "nan" : "NAN"; 332 char e = lower ? 'e' : 'E'; 333 DoubleToStringConverter converter(DTSC::UNIQUE_ZERO | DTSC::NO_TRAILING_ZERO | 334 DTSC::EMIT_POSITIVE_EXPONENT_SIGN, 335 inf, nan, e, 0, 0, 4, 0, 2); 336 // Longest of the above cases, plus space for a terminal nul character. 337 char buf[DTSC_MAX_CHARS + 1]; 338 double_conversion::StringBuilder builder(buf, sizeof(buf)); 339 bool success = false; 340 if (std::signbit(d)) { 341 d = std::abs(d); 342 flags |= FLAG_NEG; 343 } 344 if (!std::isfinite(d)) { 345 flags &= ~FLAG_ZEROS; 346 } 347 // "If the precision is missing, it shall be taken as 6." 348 if (prec < 0) { 349 prec = 6; 350 } 351 switch (c) { 352 case 'e': 353 case 'E': 354 success = converter.ToExponential(d, prec, &builder); 355 break; 356 case 'f': 357 case 'F': 358 success = converter.ToFixed(d, prec, &builder); 359 break; 360 case 'g': 361 case 'G': 362 // "If an explicit precision is zero, it shall be taken as 1." 363 success = converter.ToPrecision(d, prec ? prec : 1, &builder); 364 break; 365 } 366 if (!success) { 367 return false; 368 } 369 int len = builder.position(); 370 char* cvt = builder.Finalize(); 371 return fill_n(cvt, len, width, prec, TYPE_DOUBLE, flags); 372 } 373 374 /* 375 * Convert a string into its printable form. "width" is the output 376 * width. "prec" is the maximum number of characters of "s" to output, 377 * where -1 means until NUL. 378 */ 379 bool mozilla::PrintfTarget::cvt_s(const char* s, int width, int prec, 380 int flags) { 381 if (prec == 0) { 382 return true; 383 } 384 if (!s) { 385 s = "(null)"; 386 } 387 388 // Limit string length by precision value 389 int slen = int(strlen(s)); 390 if (0 < prec && prec < slen) { 391 slen = prec; 392 } 393 394 // and away we go 395 return fill2(s, slen, width, flags); 396 } 397 398 /* 399 * BuildArgArray stands for Numbered Argument list Sprintf 400 * for example, 401 * fmp = "%4$i, %2$d, %3s, %1d"; 402 * the number must start from 1, and no gap among them 403 */ 404 static bool BuildArgArray(const char* fmt, va_list ap, NumArgStateVector& nas) { 405 size_t number = 0, cn = 0, i; 406 const char* p; 407 char c; 408 409 // First pass: 410 // Detemine how many legal % I have got, then allocate space. 411 412 p = fmt; 413 i = 0; 414 while ((c = *p++) != 0) { 415 if (c != '%') { 416 continue; 417 } 418 if ((c = *p++) == '%') { // skip %% case 419 continue; 420 } 421 422 while (c != 0) { 423 if (c > '9' || c < '0') { 424 if (c == '$') { // numbered argument case 425 if (i > 0) { 426 MOZ_CRASH("Bad format string"); 427 } 428 number++; 429 } else { // non-numbered argument case 430 if (number > 0) { 431 MOZ_CRASH("Bad format string"); 432 } 433 i = 1; 434 } 435 break; 436 } 437 438 c = *p++; 439 } 440 } 441 442 if (number == 0) { 443 return true; 444 } 445 446 // Only allow a limited number of arguments. 447 MOZ_RELEASE_ASSERT(number <= 20); 448 449 if (!nas.growByUninitialized(number)) { 450 return false; 451 } 452 453 for (i = 0; i < number; i++) { 454 nas[i].type = TYPE_UNKNOWN; 455 } 456 457 // Second pass: 458 // Set nas[].type. 459 460 p = fmt; 461 while ((c = *p++) != 0) { 462 if (c != '%') { 463 continue; 464 } 465 c = *p++; 466 if (c == '%') { 467 continue; 468 } 469 470 cn = 0; 471 while (c && c != '$') { // should improve error check later 472 cn = cn * 10 + c - '0'; 473 c = *p++; 474 } 475 476 if (!c || cn < 1 || cn > number) { 477 MOZ_CRASH("Bad format string"); 478 } 479 480 // nas[cn] starts from 0, and make sure nas[cn].type is not assigned. 481 cn--; 482 if (nas[cn].type != TYPE_UNKNOWN) { 483 continue; 484 } 485 486 c = *p++; 487 488 // flags 489 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { 490 c = *p++; 491 } 492 493 // width 494 if (c == '*') { 495 // not supported feature, for the argument is not numbered 496 MOZ_CRASH("Bad format string"); 497 } 498 499 while ((c >= '0') && (c <= '9')) { 500 c = *p++; 501 } 502 503 // precision 504 if (c == '.') { 505 c = *p++; 506 if (c == '*') { 507 // not supported feature, for the argument is not numbered 508 MOZ_CRASH("Bad format string"); 509 } 510 511 while ((c >= '0') && (c <= '9')) { 512 c = *p++; 513 } 514 } 515 516 // size 517 nas[cn].type = TYPE_INTN; 518 if (c == 'h') { 519 nas[cn].type = TYPE_SHORT; 520 c = *p++; 521 if (c == 'h') { 522 nas[cn].type = TYPE_SCHAR; 523 c = *p++; 524 } 525 } else if (c == 'L') { 526 nas[cn].type = TYPE_LONGLONG; 527 c = *p++; 528 } else if (c == 'l') { 529 nas[cn].type = TYPE_LONG; 530 c = *p++; 531 if (c == 'l') { 532 nas[cn].type = TYPE_LONGLONG; 533 c = *p++; 534 } 535 } else if (c == 'z' || c == 'I') { 536 static_assert(sizeof(size_t) == sizeof(int) || 537 sizeof(size_t) == sizeof(long) || 538 sizeof(size_t) == sizeof(long long), 539 "size_t is not one of the expected sizes"); 540 nas[cn].type = sizeof(size_t) == sizeof(int) ? TYPE_INTN 541 : sizeof(size_t) == sizeof(long) ? TYPE_LONG 542 : TYPE_LONGLONG; 543 c = *p++; 544 } else if (c == 't') { 545 static_assert(sizeof(ptrdiff_t) == sizeof(int) || 546 sizeof(ptrdiff_t) == sizeof(long) || 547 sizeof(ptrdiff_t) == sizeof(long long), 548 "ptrdiff_t is not one of the expected sizes"); 549 nas[cn].type = sizeof(ptrdiff_t) == sizeof(int) ? TYPE_INTN 550 : sizeof(ptrdiff_t) == sizeof(long) ? TYPE_LONG 551 : TYPE_LONGLONG; 552 c = *p++; 553 } else if (c == 'j') { 554 static_assert(sizeof(intmax_t) == sizeof(int) || 555 sizeof(intmax_t) == sizeof(long) || 556 sizeof(intmax_t) == sizeof(long long), 557 "intmax_t is not one of the expected sizes"); 558 nas[cn].type = sizeof(intmax_t) == sizeof(int) ? TYPE_INTN 559 : sizeof(intmax_t) == sizeof(long) ? TYPE_LONG 560 : TYPE_LONGLONG; 561 c = *p++; 562 } 563 564 // format 565 switch (c) { 566 case 'd': 567 case 'c': 568 case 'i': 569 break; 570 571 case 'o': 572 case 'u': 573 case 'x': 574 case 'X': 575 // Mark as unsigned type. 576 nas[cn].type |= 1; 577 break; 578 579 case 'e': 580 case 'E': 581 case 'f': 582 case 'F': 583 case 'g': 584 case 'G': 585 nas[cn].type = TYPE_DOUBLE; 586 break; 587 588 case 'p': 589 nas[cn].type = TYPE_POINTER; 590 break; 591 592 case 'S': 593 #if defined(XP_WIN) 594 nas[cn].type = TYPE_WSTRING; 595 #else 596 MOZ_ASSERT(0); 597 nas[cn].type = TYPE_UNKNOWN; 598 #endif 599 break; 600 601 case 's': 602 #if defined(XP_WIN) 603 if (nas[cn].type == TYPE_LONG) { 604 nas[cn].type = TYPE_WSTRING; 605 break; 606 } 607 #endif 608 // Other type sizes are not supported here. 609 MOZ_ASSERT(nas[cn].type == TYPE_INTN); 610 nas[cn].type = TYPE_STRING; 611 break; 612 613 case 'n': 614 nas[cn].type = TYPE_INTSTR; 615 break; 616 617 default: 618 MOZ_ASSERT(0); 619 nas[cn].type = TYPE_UNKNOWN; 620 break; 621 } 622 623 // get a legal para. 624 if (nas[cn].type == TYPE_UNKNOWN) { 625 MOZ_CRASH("Bad format string"); 626 } 627 } 628 629 // Third pass: 630 // Fill nas[].ap. 631 632 cn = 0; 633 while (cn < number) { 634 // A TYPE_UNKNOWN here means that the format asked for a 635 // positional argument without specifying the meaning of some 636 // earlier argument. 637 MOZ_ASSERT(nas[cn].type != TYPE_UNKNOWN); 638 639 va_copy(nas[cn].ap, ap); 640 641 switch (nas[cn].type) { 642 case TYPE_SCHAR: 643 case TYPE_UCHAR: 644 case TYPE_SHORT: 645 case TYPE_USHORT: 646 case TYPE_INTN: 647 case TYPE_UINTN: 648 (void)va_arg(ap, int); 649 break; 650 case TYPE_LONG: 651 (void)va_arg(ap, long); 652 break; 653 case TYPE_ULONG: 654 (void)va_arg(ap, unsigned long); 655 break; 656 case TYPE_LONGLONG: 657 (void)va_arg(ap, long long); 658 break; 659 case TYPE_ULONGLONG: 660 (void)va_arg(ap, unsigned long long); 661 break; 662 case TYPE_STRING: 663 (void)va_arg(ap, char*); 664 break; 665 case TYPE_INTSTR: 666 (void)va_arg(ap, int*); 667 break; 668 case TYPE_DOUBLE: 669 (void)va_arg(ap, double); 670 break; 671 case TYPE_POINTER: 672 (void)va_arg(ap, void*); 673 break; 674 #if defined(XP_WIN) 675 case TYPE_WSTRING: 676 (void)va_arg(ap, wchar_t*); 677 break; 678 #endif 679 680 default: 681 MOZ_CRASH(); 682 } 683 684 cn++; 685 } 686 687 return true; 688 } 689 690 mozilla::PrintfTarget::PrintfTarget() : mEmitted(0) {} 691 692 bool mozilla::PrintfTarget::vprint(const char* fmt, va_list ap) { 693 char c; 694 int flags, width, prec, radix, type; 695 union { 696 char ch; 697 int i; 698 long l; 699 long long ll; 700 double d; 701 const char* s; 702 int* ip; 703 void* p; 704 #if defined(XP_WIN) 705 const wchar_t* ws; 706 #endif 707 } u{}; 708 const char* hexp; 709 int i; 710 711 // Build an argument array, IF the fmt is numbered argument 712 // list style, to contain the Numbered Argument list pointers. 713 714 NumArgStateVector nas; 715 if (!BuildArgArray(fmt, ap, nas)) { 716 // the fmt contains error Numbered Argument format, jliu@netscape.com 717 MOZ_CRASH("Bad format string"); 718 } 719 720 while ((c = *fmt++) != 0) { 721 if (c != '%') { 722 if (!emit(fmt - 1, 1)) { 723 return false; 724 } 725 726 continue; 727 } 728 729 // Gobble up the % format string. Hopefully we have handled all 730 // of the strange cases! 731 flags = 0; 732 c = *fmt++; 733 if (c == '%') { 734 // quoting a % with %% 735 if (!emit(fmt - 1, 1)) { 736 return false; 737 } 738 739 continue; 740 } 741 742 if (!nas.empty()) { 743 // the fmt contains the Numbered Arguments feature 744 i = 0; 745 while (c && c != '$') { // should improve error check later 746 i = (i * 10) + (c - '0'); 747 c = *fmt++; 748 } 749 750 if (nas[i - 1].type == TYPE_UNKNOWN) { 751 MOZ_CRASH("Bad format string"); 752 } 753 754 ap = nas[i - 1].ap; 755 c = *fmt++; 756 } 757 758 // Examine optional flags. Note that we do not implement the 759 // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is 760 // somewhat ambiguous and not ideal, which is perhaps why 761 // the various sprintf() implementations are inconsistent 762 // on this feature. 763 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { 764 if (c == '-') { 765 flags |= FLAG_LEFT; 766 } 767 if (c == '+') { 768 flags |= FLAG_SIGNED; 769 } 770 if (c == ' ') { 771 flags |= FLAG_SPACED; 772 } 773 if (c == '0') { 774 flags |= FLAG_ZEROS; 775 } 776 c = *fmt++; 777 } 778 if (flags & FLAG_SIGNED) { 779 flags &= ~FLAG_SPACED; 780 } 781 if (flags & FLAG_LEFT) { 782 flags &= ~FLAG_ZEROS; 783 } 784 785 // width 786 if (c == '*') { 787 c = *fmt++; 788 width = va_arg(ap, int); 789 if (width < 0) { 790 width = -width; 791 flags |= FLAG_LEFT; 792 flags &= ~FLAG_ZEROS; 793 } 794 } else { 795 width = 0; 796 while ((c >= '0') && (c <= '9')) { 797 width = (width * 10) + (c - '0'); 798 c = *fmt++; 799 } 800 } 801 802 // precision 803 prec = -1; 804 if (c == '.') { 805 c = *fmt++; 806 if (c == '*') { 807 c = *fmt++; 808 prec = va_arg(ap, int); 809 } else { 810 prec = 0; 811 while ((c >= '0') && (c <= '9')) { 812 prec = (prec * 10) + (c - '0'); 813 c = *fmt++; 814 } 815 } 816 } 817 818 // size 819 type = TYPE_INTN; 820 if (c == 'h') { 821 type = TYPE_SHORT; 822 c = *fmt++; 823 if (c == 'h') { 824 type = TYPE_SCHAR; 825 c = *fmt++; 826 } 827 } else if (c == 'L') { 828 type = TYPE_LONGLONG; 829 c = *fmt++; 830 } else if (c == 'l') { 831 type = TYPE_LONG; 832 c = *fmt++; 833 if (c == 'l') { 834 type = TYPE_LONGLONG; 835 c = *fmt++; 836 } 837 } else if (c == 'z' || c == 'I') { 838 static_assert(sizeof(size_t) == sizeof(int) || 839 sizeof(size_t) == sizeof(long) || 840 sizeof(size_t) == sizeof(long long), 841 "size_t is not one of the expected sizes"); 842 type = sizeof(size_t) == sizeof(int) ? TYPE_INTN 843 : sizeof(size_t) == sizeof(long) ? TYPE_LONG 844 : TYPE_LONGLONG; 845 c = *fmt++; 846 } else if (c == 't') { 847 static_assert(sizeof(ptrdiff_t) == sizeof(int) || 848 sizeof(ptrdiff_t) == sizeof(long) || 849 sizeof(ptrdiff_t) == sizeof(long long), 850 "ptrdiff_t is not one of the expected sizes"); 851 type = sizeof(ptrdiff_t) == sizeof(int) ? TYPE_INTN 852 : sizeof(ptrdiff_t) == sizeof(long) ? TYPE_LONG 853 : TYPE_LONGLONG; 854 c = *fmt++; 855 } else if (c == 'j') { 856 static_assert(sizeof(intmax_t) == sizeof(int) || 857 sizeof(intmax_t) == sizeof(long) || 858 sizeof(intmax_t) == sizeof(long long), 859 "intmax_t is not one of the expected sizes"); 860 type = sizeof(intmax_t) == sizeof(int) ? TYPE_INTN 861 : sizeof(intmax_t) == sizeof(long) ? TYPE_LONG 862 : TYPE_LONGLONG; 863 c = *fmt++; 864 } 865 866 // format 867 hexp = hex; 868 switch (c) { 869 case 'd': 870 case 'i': // decimal/integer 871 radix = 10; 872 goto fetch_and_convert; 873 874 case 'o': // octal 875 radix = 8; 876 type |= 1; 877 goto fetch_and_convert; 878 879 case 'u': // unsigned decimal 880 radix = 10; 881 type |= 1; 882 goto fetch_and_convert; 883 884 case 'x': // unsigned hex 885 radix = 16; 886 type |= 1; 887 goto fetch_and_convert; 888 889 case 'X': // unsigned HEX 890 radix = 16; 891 hexp = HEX; 892 type |= 1; 893 goto fetch_and_convert; 894 895 fetch_and_convert: 896 switch (type) { 897 case TYPE_SCHAR: 898 u.l = (signed char)va_arg(ap, int); 899 if (u.l < 0) { 900 u.l = -u.l; 901 flags |= FLAG_NEG; 902 } 903 goto do_long; 904 case TYPE_UCHAR: 905 u.l = (unsigned char)va_arg(ap, unsigned int); 906 goto do_long; 907 case TYPE_SHORT: 908 u.l = (short)va_arg(ap, int); 909 if (u.l < 0) { 910 u.l = -u.l; 911 flags |= FLAG_NEG; 912 } 913 goto do_long; 914 case TYPE_USHORT: 915 u.l = (unsigned short)va_arg(ap, unsigned int); 916 goto do_long; 917 case TYPE_INTN: 918 u.l = va_arg(ap, int); 919 if (u.l < 0) { 920 u.l = -u.l; 921 flags |= FLAG_NEG; 922 } 923 goto do_long; 924 case TYPE_UINTN: 925 u.l = (long)va_arg(ap, unsigned int); 926 goto do_long; 927 928 case TYPE_LONG: 929 u.l = va_arg(ap, long); 930 if (u.l < 0) { 931 u.l = -u.l; 932 flags |= FLAG_NEG; 933 } 934 goto do_long; 935 case TYPE_ULONG: 936 u.l = (long)va_arg(ap, unsigned long); 937 do_long: 938 if (!cvt_l(u.l, width, prec, radix, type, flags, hexp)) { 939 return false; 940 } 941 942 break; 943 944 case TYPE_LONGLONG: 945 u.ll = va_arg(ap, long long); 946 if (u.ll < 0) { 947 u.ll = -u.ll; 948 flags |= FLAG_NEG; 949 } 950 goto do_longlong; 951 case TYPE_POINTER: 952 u.ll = (uintptr_t)va_arg(ap, void*); 953 goto do_longlong; 954 case TYPE_ULONGLONG: 955 u.ll = va_arg(ap, unsigned long long); 956 do_longlong: 957 if (!cvt_ll(u.ll, width, prec, radix, type, flags, hexp)) { 958 return false; 959 } 960 961 break; 962 } 963 break; 964 965 case 'e': 966 case 'E': 967 case 'f': 968 case 'F': 969 case 'g': 970 case 'G': 971 u.d = va_arg(ap, double); 972 if (!cvt_f(u.d, c, width, prec, flags)) { 973 return false; 974 } 975 976 break; 977 978 case 'c': 979 if ((flags & FLAG_LEFT) == 0) { 980 while (width-- > 1) { 981 if (!emit(" ", 1)) { 982 return false; 983 } 984 } 985 } 986 switch (type) { 987 case TYPE_SHORT: 988 case TYPE_INTN: 989 u.ch = va_arg(ap, int); 990 if (!emit(&u.ch, 1)) { 991 return false; 992 } 993 break; 994 } 995 if (flags & FLAG_LEFT) { 996 while (width-- > 1) { 997 if (!emit(" ", 1)) { 998 return false; 999 } 1000 } 1001 } 1002 break; 1003 1004 case 'p': 1005 type = TYPE_POINTER; 1006 radix = 16; 1007 goto fetch_and_convert; 1008 1009 case 's': 1010 if (type == TYPE_INTN) { 1011 u.s = va_arg(ap, const char*); 1012 if (!cvt_s(u.s, width, prec, flags)) { 1013 return false; 1014 } 1015 break; 1016 } 1017 MOZ_ASSERT(type == TYPE_LONG); 1018 [[fallthrough]]; 1019 case 'S': 1020 #if defined(XP_WIN) 1021 { 1022 u.ws = va_arg(ap, const wchar_t*); 1023 1024 int rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL); 1025 if (rv == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) { 1026 if (!cvt_s("<unicode errors in string>", width, prec, flags)) { 1027 return false; 1028 } 1029 } else { 1030 if (rv == 0) { 1031 rv = 1; 1032 } 1033 UniqueFreePtr<char[]> buf((char*)malloc(rv)); 1034 WideCharToMultiByte(CP_ACP, 0, u.ws, -1, buf.get(), rv, NULL, NULL); 1035 buf[rv - 1] = '\0'; 1036 1037 if (!cvt_s(buf.get(), width, prec, flags)) { 1038 return false; 1039 } 1040 } 1041 } 1042 #else 1043 // Not supported here. 1044 MOZ_ASSERT(0); 1045 #endif 1046 break; 1047 1048 case 'n': 1049 u.ip = va_arg(ap, int*); 1050 if (u.ip) { 1051 *u.ip = mEmitted; 1052 } 1053 break; 1054 1055 default: 1056 // Not a % token after all... skip it 1057 if (!emit("%", 1)) { 1058 return false; 1059 } 1060 if (!emit(fmt - 1, 1)) { 1061 return false; 1062 } 1063 } 1064 } 1065 1066 return true; 1067 } 1068 1069 /************************************************************************/ 1070 1071 bool mozilla::PrintfTarget::print(const char* format, ...) { 1072 va_list ap; 1073 1074 va_start(ap, format); 1075 bool result = vprint(format, ap); 1076 va_end(ap); 1077 return result; 1078 } 1079 1080 #undef TYPE_SHORT 1081 #undef TYPE_USHORT 1082 #undef TYPE_INTN 1083 #undef TYPE_UINTN 1084 #undef TYPE_LONG 1085 #undef TYPE_ULONG 1086 #undef TYPE_LONGLONG 1087 #undef TYPE_ULONGLONG 1088 #undef TYPE_STRING 1089 #undef TYPE_DOUBLE 1090 #undef TYPE_INTSTR 1091 #undef TYPE_POINTER 1092 #undef TYPE_WSTRING 1093 #undef TYPE_UNKNOWN 1094 #undef TYPE_SCHAR 1095 #undef TYPE_UCHAR 1096 1097 #undef FLAG_LEFT 1098 #undef FLAG_SIGNED 1099 #undef FLAG_SPACED 1100 #undef FLAG_ZEROS 1101 #undef FLAG_NEG