mprintf.c (32257B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 */ 24 25 #include "curl_setup.h" 26 #include "curlx/dynbuf.h" 27 #include "curl_printf.h" 28 #include "curlx/strparse.h" 29 30 #include "curl_memory.h" 31 /* The last #include file should be: */ 32 #include "memdebug.h" 33 34 #ifdef HAVE_LONGLONG 35 # define LONG_LONG_TYPE long long 36 # define HAVE_LONG_LONG_TYPE 37 #elif defined(_MSC_VER) 38 # define LONG_LONG_TYPE __int64 39 # define HAVE_LONG_LONG_TYPE 40 #else 41 # undef LONG_LONG_TYPE 42 # undef HAVE_LONG_LONG_TYPE 43 #endif 44 45 /* 46 * Max integer data types that mprintf.c is capable 47 */ 48 49 #ifdef HAVE_LONG_LONG_TYPE 50 # define mp_intmax_t LONG_LONG_TYPE 51 # define mp_uintmax_t unsigned LONG_LONG_TYPE 52 #else 53 # define mp_intmax_t long 54 # define mp_uintmax_t unsigned long 55 #endif 56 57 #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should 58 fit negative DBL_MAX (317 letters) */ 59 #define MAX_PARAMETERS 128 /* number of input arguments */ 60 #define MAX_SEGMENTS 128 /* number of output segments */ 61 62 #ifdef __AMIGA__ 63 # undef FORMAT_INT 64 #endif 65 66 /* Lower-case digits. */ 67 const unsigned char Curl_ldigits[] = "0123456789abcdef"; 68 69 /* Upper-case digits. */ 70 const unsigned char Curl_udigits[] = "0123456789ABCDEF"; 71 72 #define OUTCHAR(x) \ 73 do { \ 74 if(stream((unsigned char)x, userp)) \ 75 return TRUE; \ 76 (*donep)++; \ 77 } while(0) 78 79 /* Data type to read from the arglist */ 80 typedef enum { 81 FORMAT_STRING, 82 FORMAT_PTR, 83 FORMAT_INTPTR, 84 FORMAT_INT, 85 FORMAT_LONG, 86 FORMAT_LONGLONG, 87 FORMAT_INTU, 88 FORMAT_LONGU, 89 FORMAT_LONGLONGU, 90 FORMAT_DOUBLE, 91 FORMAT_LONGDOUBLE, 92 FORMAT_WIDTH, 93 FORMAT_PRECISION 94 } FormatType; 95 96 /* conversion and display flags */ 97 enum { 98 FLAGS_SPACE = 1 << 0, 99 FLAGS_SHOWSIGN = 1 << 1, 100 FLAGS_LEFT = 1 << 2, 101 FLAGS_ALT = 1 << 3, 102 FLAGS_SHORT = 1 << 4, 103 FLAGS_LONG = 1 << 5, 104 FLAGS_LONGLONG = 1 << 6, 105 FLAGS_LONGDOUBLE = 1 << 7, 106 FLAGS_PAD_NIL = 1 << 8, 107 FLAGS_UNSIGNED = 1 << 9, 108 FLAGS_OCTAL = 1 << 10, 109 FLAGS_HEX = 1 << 11, 110 FLAGS_UPPER = 1 << 12, 111 FLAGS_WIDTH = 1 << 13, /* '*' or '*<num>$' used */ 112 FLAGS_WIDTHPARAM = 1 << 14, /* width PARAMETER was specified */ 113 FLAGS_PREC = 1 << 15, /* precision was specified */ 114 FLAGS_PRECPARAM = 1 << 16, /* precision PARAMETER was specified */ 115 FLAGS_CHAR = 1 << 17, /* %c story */ 116 FLAGS_FLOATE = 1 << 18, /* %e or %E */ 117 FLAGS_FLOATG = 1 << 19, /* %g or %G */ 118 FLAGS_SUBSTR = 1 << 20 /* no input, only substring */ 119 }; 120 121 enum { 122 DOLLAR_UNKNOWN, 123 DOLLAR_NOPE, 124 DOLLAR_USE 125 }; 126 127 /* 128 * Describes an input va_arg type and hold its value. 129 */ 130 struct va_input { 131 FormatType type; /* FormatType */ 132 union { 133 const char *str; 134 void *ptr; 135 mp_intmax_t nums; /* signed */ 136 mp_uintmax_t numu; /* unsigned */ 137 double dnum; 138 } val; 139 }; 140 141 /* 142 * Describes an output segment. 143 */ 144 struct outsegment { 145 int width; /* width OR width parameter number */ 146 int precision; /* precision OR precision parameter number */ 147 unsigned int flags; 148 unsigned int input; /* input argument array index */ 149 const char *start; /* format string start to output */ 150 size_t outlen; /* number of bytes from the format string to output */ 151 }; 152 153 struct nsprintf { 154 char *buffer; 155 size_t length; 156 size_t max; 157 }; 158 159 struct asprintf { 160 struct dynbuf *b; 161 char merr; 162 }; 163 164 /* the provided input number is 1-based but this returns the number 0-based. 165 166 returns -1 if no valid number was provided. 167 */ 168 static int dollarstring(const char *p, const char **end) 169 { 170 curl_off_t num; 171 if(curlx_str_number(&p, &num, MAX_PARAMETERS) || 172 curlx_str_single(&p, '$') || !num) 173 return -1; 174 *end = p; 175 return (int)num - 1; 176 } 177 178 #define is_arg_used(x,y) ((x)[(y)/8] & (1 << ((y)&7))) 179 #define mark_arg_used(x,y) ((x)[y/8] |= (unsigned char)(1 << ((y)&7))) 180 181 /* 182 * Parse the format string. 183 * 184 * Create two arrays. One describes the inputs, one describes the outputs. 185 * 186 * Returns zero on success. 187 */ 188 189 #define PFMT_OK 0 190 #define PFMT_DOLLAR 1 /* bad dollar for main param */ 191 #define PFMT_DOLLARWIDTH 2 /* bad dollar use for width */ 192 #define PFMT_DOLLARPREC 3 /* bad dollar use for precision */ 193 #define PFMT_MANYARGS 4 /* too many input arguments used */ 194 #define PFMT_PREC 5 /* precision overflow */ 195 #define PFMT_PRECMIX 6 /* bad mix of precision specifiers */ 196 #define PFMT_WIDTH 7 /* width overflow */ 197 #define PFMT_INPUTGAP 8 /* gap in arguments */ 198 #define PFMT_WIDTHARG 9 /* attempted to use same arg twice, for width */ 199 #define PFMT_PRECARG 10 /* attempted to use same arg twice, for prec */ 200 #define PFMT_MANYSEGS 11 /* maxed out output segments */ 201 202 static int parsefmt(const char *format, 203 struct outsegment *out, 204 struct va_input *in, 205 int *opieces, 206 int *ipieces, va_list arglist) 207 { 208 const char *fmt = format; 209 int param_num = 0; 210 int max_param = -1; 211 int i; 212 int ocount = 0; 213 unsigned char usedinput[MAX_PARAMETERS/8]; 214 size_t outlen = 0; 215 struct outsegment *optr; 216 int use_dollar = DOLLAR_UNKNOWN; 217 const char *start = fmt; 218 219 /* clear, set a bit for each used input */ 220 memset(usedinput, 0, sizeof(usedinput)); 221 222 while(*fmt) { 223 if(*fmt == '%') { 224 struct va_input *iptr; 225 bool loopit = TRUE; 226 FormatType type; 227 unsigned int flags = 0; 228 int width = 0; 229 int precision = 0; 230 int param = -1; 231 fmt++; 232 outlen = (size_t)(fmt - start - 1); 233 if(*fmt == '%') { 234 /* this means a %% that should be output only as %. Create an output 235 segment. */ 236 if(outlen) { 237 optr = &out[ocount++]; 238 if(ocount > MAX_SEGMENTS) 239 return PFMT_MANYSEGS; 240 optr->input = 0; 241 optr->flags = FLAGS_SUBSTR; 242 optr->start = start; 243 optr->outlen = outlen; 244 } 245 start = fmt; 246 fmt++; 247 continue; /* while */ 248 } 249 250 if(use_dollar != DOLLAR_NOPE) { 251 param = dollarstring(fmt, &fmt); 252 if(param < 0) { 253 if(use_dollar == DOLLAR_USE) 254 /* illegal combo */ 255 return PFMT_DOLLAR; 256 257 /* we got no positional, just get the next arg */ 258 param = -1; 259 use_dollar = DOLLAR_NOPE; 260 } 261 else 262 use_dollar = DOLLAR_USE; 263 } 264 265 /* Handle the flags */ 266 while(loopit) { 267 switch(*fmt++) { 268 case ' ': 269 flags |= FLAGS_SPACE; 270 break; 271 case '+': 272 flags |= FLAGS_SHOWSIGN; 273 break; 274 case '-': 275 flags |= FLAGS_LEFT; 276 flags &= ~(unsigned int)FLAGS_PAD_NIL; 277 break; 278 case '#': 279 flags |= FLAGS_ALT; 280 break; 281 case '.': 282 if('*' == *fmt) { 283 /* The precision is picked from a specified parameter */ 284 flags |= FLAGS_PRECPARAM; 285 fmt++; 286 287 if(use_dollar == DOLLAR_USE) { 288 precision = dollarstring(fmt, &fmt); 289 if(precision < 0) 290 /* illegal combo */ 291 return PFMT_DOLLARPREC; 292 } 293 else 294 /* get it from the next argument */ 295 precision = -1; 296 } 297 else { 298 bool is_neg; 299 curl_off_t num; 300 flags |= FLAGS_PREC; 301 is_neg = ('-' == *fmt); 302 if(is_neg) 303 fmt++; 304 if(curlx_str_number(&fmt, &num, INT_MAX)) 305 return PFMT_PREC; 306 precision = (int)num; 307 if(is_neg) 308 precision = -precision; 309 } 310 if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == 311 (FLAGS_PREC | FLAGS_PRECPARAM)) 312 /* it is not permitted to use both kinds of precision for the same 313 argument */ 314 return PFMT_PRECMIX; 315 break; 316 case 'h': 317 flags |= FLAGS_SHORT; 318 break; 319 #ifdef _WIN32 320 case 'I': 321 /* Non-ANSI integer extensions I32 I64 */ 322 if((fmt[0] == '3') && (fmt[1] == '2')) { 323 flags |= FLAGS_LONG; 324 fmt += 2; 325 } 326 else if((fmt[0] == '6') && (fmt[1] == '4')) { 327 flags |= FLAGS_LONGLONG; 328 fmt += 2; 329 } 330 else { 331 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) 332 flags |= FLAGS_LONGLONG; 333 #else 334 flags |= FLAGS_LONG; 335 #endif 336 } 337 break; 338 #endif /* _WIN32 */ 339 case 'l': 340 if(flags & FLAGS_LONG) 341 flags |= FLAGS_LONGLONG; 342 else 343 flags |= FLAGS_LONG; 344 break; 345 case 'L': 346 flags |= FLAGS_LONGDOUBLE; 347 break; 348 case 'q': 349 flags |= FLAGS_LONGLONG; 350 break; 351 case 'z': 352 /* the code below generates a warning if -Wunreachable-code is 353 used */ 354 #if (SIZEOF_SIZE_T > SIZEOF_LONG) 355 flags |= FLAGS_LONGLONG; 356 #else 357 flags |= FLAGS_LONG; 358 #endif 359 break; 360 case 'O': 361 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) 362 flags |= FLAGS_LONGLONG; 363 #else 364 flags |= FLAGS_LONG; 365 #endif 366 break; 367 case '0': 368 if(!(flags & FLAGS_LEFT)) 369 flags |= FLAGS_PAD_NIL; 370 FALLTHROUGH(); 371 case '1': case '2': case '3': case '4': 372 case '5': case '6': case '7': case '8': case '9': { 373 curl_off_t num; 374 flags |= FLAGS_WIDTH; 375 fmt--; 376 if(curlx_str_number(&fmt, &num, INT_MAX)) 377 return PFMT_WIDTH; 378 width = (int)num; 379 break; 380 } 381 case '*': /* read width from argument list */ 382 flags |= FLAGS_WIDTHPARAM; 383 if(use_dollar == DOLLAR_USE) { 384 width = dollarstring(fmt, &fmt); 385 if(width < 0) 386 /* illegal combo */ 387 return PFMT_DOLLARWIDTH; 388 } 389 else 390 /* pick from the next argument */ 391 width = -1; 392 break; 393 default: 394 loopit = FALSE; 395 fmt--; 396 break; 397 } /* switch */ 398 } /* while */ 399 400 switch(*fmt) { 401 case 'S': 402 flags |= FLAGS_ALT; 403 FALLTHROUGH(); 404 case 's': 405 type = FORMAT_STRING; 406 break; 407 case 'n': 408 type = FORMAT_INTPTR; 409 break; 410 case 'p': 411 type = FORMAT_PTR; 412 break; 413 case 'd': 414 case 'i': 415 if(flags & FLAGS_LONGLONG) 416 type = FORMAT_LONGLONG; 417 else if(flags & FLAGS_LONG) 418 type = FORMAT_LONG; 419 else 420 type = FORMAT_INT; 421 break; 422 case 'u': 423 if(flags & FLAGS_LONGLONG) 424 type = FORMAT_LONGLONGU; 425 else if(flags & FLAGS_LONG) 426 type = FORMAT_LONGU; 427 else 428 type = FORMAT_INTU; 429 flags |= FLAGS_UNSIGNED; 430 break; 431 case 'o': 432 if(flags & FLAGS_LONGLONG) 433 type = FORMAT_LONGLONGU; 434 else if(flags & FLAGS_LONG) 435 type = FORMAT_LONGU; 436 else 437 type = FORMAT_INTU; 438 flags |= FLAGS_OCTAL|FLAGS_UNSIGNED; 439 break; 440 case 'x': 441 if(flags & FLAGS_LONGLONG) 442 type = FORMAT_LONGLONGU; 443 else if(flags & FLAGS_LONG) 444 type = FORMAT_LONGU; 445 else 446 type = FORMAT_INTU; 447 flags |= FLAGS_HEX|FLAGS_UNSIGNED; 448 break; 449 case 'X': 450 if(flags & FLAGS_LONGLONG) 451 type = FORMAT_LONGLONGU; 452 else if(flags & FLAGS_LONG) 453 type = FORMAT_LONGU; 454 else 455 type = FORMAT_INTU; 456 flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; 457 break; 458 case 'c': 459 type = FORMAT_INT; 460 flags |= FLAGS_CHAR; 461 break; 462 case 'f': 463 type = FORMAT_DOUBLE; 464 break; 465 case 'e': 466 type = FORMAT_DOUBLE; 467 flags |= FLAGS_FLOATE; 468 break; 469 case 'E': 470 type = FORMAT_DOUBLE; 471 flags |= FLAGS_FLOATE|FLAGS_UPPER; 472 break; 473 case 'g': 474 type = FORMAT_DOUBLE; 475 flags |= FLAGS_FLOATG; 476 break; 477 case 'G': 478 type = FORMAT_DOUBLE; 479 flags |= FLAGS_FLOATG|FLAGS_UPPER; 480 break; 481 default: 482 /* invalid instruction, disregard and continue */ 483 continue; 484 } /* switch */ 485 486 if(flags & FLAGS_WIDTHPARAM) { 487 if(width < 0) 488 width = param_num++; 489 else { 490 /* if this identifies a parameter already used, this is illegal */ 491 if(is_arg_used(usedinput, width)) 492 return PFMT_WIDTHARG; 493 } 494 if(width >= MAX_PARAMETERS) 495 return PFMT_MANYARGS; 496 if(width >= max_param) 497 max_param = width; 498 499 in[width].type = FORMAT_WIDTH; 500 /* mark as used */ 501 mark_arg_used(usedinput, width); 502 } 503 504 if(flags & FLAGS_PRECPARAM) { 505 if(precision < 0) 506 precision = param_num++; 507 else { 508 /* if this identifies a parameter already used, this is illegal */ 509 if(is_arg_used(usedinput, precision)) 510 return PFMT_PRECARG; 511 } 512 if(precision >= MAX_PARAMETERS) 513 return PFMT_MANYARGS; 514 if(precision >= max_param) 515 max_param = precision; 516 517 in[precision].type = FORMAT_PRECISION; 518 mark_arg_used(usedinput, precision); 519 } 520 521 /* Handle the specifier */ 522 if(param < 0) 523 param = param_num++; 524 if(param >= MAX_PARAMETERS) 525 return PFMT_MANYARGS; 526 if(param >= max_param) 527 max_param = param; 528 529 iptr = &in[param]; 530 iptr->type = type; 531 532 /* mark this input as used */ 533 mark_arg_used(usedinput, param); 534 535 fmt++; 536 optr = &out[ocount++]; 537 if(ocount > MAX_SEGMENTS) 538 return PFMT_MANYSEGS; 539 optr->input = (unsigned int)param; 540 optr->flags = flags; 541 optr->width = width; 542 optr->precision = precision; 543 optr->start = start; 544 optr->outlen = outlen; 545 start = fmt; 546 } 547 else 548 fmt++; 549 } 550 551 /* is there a trailing piece */ 552 outlen = (size_t)(fmt - start); 553 if(outlen) { 554 optr = &out[ocount++]; 555 if(ocount > MAX_SEGMENTS) 556 return PFMT_MANYSEGS; 557 optr->input = 0; 558 optr->flags = FLAGS_SUBSTR; 559 optr->start = start; 560 optr->outlen = outlen; 561 } 562 563 /* Read the arg list parameters into our data list */ 564 for(i = 0; i < max_param + 1; i++) { 565 struct va_input *iptr = &in[i]; 566 if(!is_arg_used(usedinput, i)) 567 /* bad input */ 568 return PFMT_INPUTGAP; 569 570 /* based on the type, read the correct argument */ 571 switch(iptr->type) { 572 case FORMAT_STRING: 573 iptr->val.str = va_arg(arglist, const char *); 574 break; 575 576 case FORMAT_INTPTR: 577 case FORMAT_PTR: 578 iptr->val.ptr = va_arg(arglist, void *); 579 break; 580 581 case FORMAT_LONGLONGU: 582 iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t); 583 break; 584 585 case FORMAT_LONGLONG: 586 iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t); 587 break; 588 589 case FORMAT_LONGU: 590 iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long); 591 break; 592 593 case FORMAT_LONG: 594 iptr->val.nums = (mp_intmax_t)va_arg(arglist, long); 595 break; 596 597 case FORMAT_INTU: 598 iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int); 599 break; 600 601 case FORMAT_INT: 602 case FORMAT_WIDTH: 603 case FORMAT_PRECISION: 604 iptr->val.nums = (mp_intmax_t)va_arg(arglist, int); 605 break; 606 607 case FORMAT_DOUBLE: 608 iptr->val.dnum = va_arg(arglist, double); 609 break; 610 611 default: 612 DEBUGASSERT(NULL); /* unexpected */ 613 break; 614 } 615 } 616 *ipieces = max_param + 1; 617 *opieces = ocount; 618 619 return PFMT_OK; 620 } 621 622 struct mproperty { 623 int width; /* Width of a field. */ 624 int prec; /* Precision of a field. */ 625 unsigned int flags; 626 }; 627 628 static bool out_double(void *userp, 629 int (*stream)(unsigned char, void *), 630 struct mproperty *p, 631 double dnum, 632 char *work, int *donep) 633 { 634 char formatbuf[32]="%"; 635 char *fptr = &formatbuf[1]; 636 size_t left = sizeof(formatbuf)-strlen(formatbuf); 637 int flags = p->flags; 638 int width = p->width; 639 int prec = p->prec; 640 641 if(flags & FLAGS_LEFT) 642 *fptr++ = '-'; 643 if(flags & FLAGS_SHOWSIGN) 644 *fptr++ = '+'; 645 if(flags & FLAGS_SPACE) 646 *fptr++ = ' '; 647 if(flags & FLAGS_ALT) 648 *fptr++ = '#'; 649 650 *fptr = 0; 651 652 if(width >= 0) { 653 size_t dlen; 654 if(width >= BUFFSIZE) 655 width = BUFFSIZE - 1; 656 /* RECURSIVE USAGE */ 657 dlen = (size_t)curl_msnprintf(fptr, left, "%d", width); 658 fptr += dlen; 659 left -= dlen; 660 } 661 if(prec >= 0) { 662 /* for each digit in the integer part, we can have one less 663 precision */ 664 int maxprec = BUFFSIZE - 1; 665 double val = dnum; 666 int len; 667 if(prec > maxprec) 668 prec = maxprec - 1; 669 if(width > 0 && prec <= width) 670 maxprec -= width; 671 while(val >= 10.0) { 672 val /= 10; 673 maxprec--; 674 } 675 676 if(prec > maxprec) 677 prec = maxprec - 1; 678 if(prec < 0) 679 prec = 0; 680 /* RECURSIVE USAGE */ 681 len = curl_msnprintf(fptr, left, ".%d", prec); 682 fptr += len; 683 } 684 if(flags & FLAGS_LONG) 685 *fptr++ = 'l'; 686 687 if(flags & FLAGS_FLOATE) 688 *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e'); 689 else if(flags & FLAGS_FLOATG) 690 *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g'); 691 else 692 *fptr++ = 'f'; 693 694 *fptr = 0; /* and a final null-termination */ 695 696 #ifdef __clang__ 697 #pragma clang diagnostic push 698 #pragma clang diagnostic ignored "-Wformat-nonliteral" 699 #endif 700 /* NOTE NOTE NOTE!! Not all sprintf implementations return number of 701 output characters */ 702 #ifdef HAVE_SNPRINTF 703 /* !checksrc! disable LONGLINE */ 704 /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ 705 (snprintf)(work, BUFFSIZE, formatbuf, dnum); 706 #ifdef _WIN32 707 /* Old versions of the Windows CRT do not terminate the snprintf output 708 buffer if it reaches the max size so we do that here. */ 709 work[BUFFSIZE - 1] = 0; 710 #endif 711 #else 712 (sprintf)(work, formatbuf, dnum); 713 #endif 714 #ifdef __clang__ 715 #pragma clang diagnostic pop 716 #endif 717 DEBUGASSERT(strlen(work) < BUFFSIZE); 718 while(*work) { 719 if(stream(*work++, userp)) 720 return TRUE; 721 (*donep)++; 722 } 723 return 0; 724 } 725 726 static bool out_number(void *userp, 727 int (*stream)(unsigned char, void *), 728 struct mproperty *p, 729 mp_uintmax_t num, 730 mp_intmax_t nums, 731 char *work, int *donep) 732 { 733 const unsigned char *digits = Curl_ldigits; 734 int flags = p->flags; 735 int width = p->width; 736 int prec = p->prec; 737 bool is_alt = flags & FLAGS_ALT; 738 bool is_neg = FALSE; 739 int base = 10; 740 741 /* 'workend' points to the final buffer byte position, but with an extra 742 byte as margin to avoid the (FALSE?) warning Coverity gives us 743 otherwise */ 744 char *workend = &work[BUFFSIZE - 2]; 745 char *w; 746 747 if(flags & FLAGS_CHAR) { 748 /* Character. */ 749 if(!(flags & FLAGS_LEFT)) 750 while(--width > 0) 751 OUTCHAR(' '); 752 OUTCHAR((char) num); 753 if(flags & FLAGS_LEFT) 754 while(--width > 0) 755 OUTCHAR(' '); 756 return FALSE; 757 } 758 if(flags & FLAGS_OCTAL) 759 /* Octal unsigned integer */ 760 base = 8; 761 762 else if(flags & FLAGS_HEX) { 763 /* Hexadecimal unsigned integer */ 764 digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits; 765 base = 16; 766 } 767 else if(flags & FLAGS_UNSIGNED) 768 /* Decimal unsigned integer */ 769 ; 770 771 else { 772 /* Decimal integer. */ 773 is_neg = (nums < 0); 774 if(is_neg) { 775 /* signed_num might fail to hold absolute negative minimum by 1 */ 776 mp_intmax_t signed_num; /* Used to convert negative in positive. */ 777 signed_num = nums + (mp_intmax_t)1; 778 signed_num = -signed_num; 779 num = (mp_uintmax_t)signed_num; 780 num += (mp_uintmax_t)1; 781 } 782 } 783 784 /* Supply a default precision if none was given. */ 785 if(prec == -1) 786 prec = 1; 787 788 /* Put the number in WORK. */ 789 w = workend; 790 DEBUGASSERT(base <= 16); 791 switch(base) { 792 case 10: 793 while(num > 0) { 794 *w-- = (char)('0' + (num % 10)); 795 num /= 10; 796 } 797 break; 798 default: 799 while(num > 0) { 800 *w-- = digits[num % base]; 801 num /= base; 802 } 803 break; 804 } 805 width -= (int)(workend - w); 806 prec -= (int)(workend - w); 807 808 if(is_alt && base == 8 && prec <= 0) { 809 *w-- = '0'; 810 --width; 811 } 812 813 if(prec > 0) { 814 width -= prec; 815 while(prec-- > 0 && w >= work) 816 *w-- = '0'; 817 } 818 819 if(is_alt && base == 16) 820 width -= 2; 821 822 if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE)) 823 --width; 824 825 if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL)) 826 while(width-- > 0) 827 OUTCHAR(' '); 828 829 if(is_neg) 830 OUTCHAR('-'); 831 else if(flags & FLAGS_SHOWSIGN) 832 OUTCHAR('+'); 833 else if(flags & FLAGS_SPACE) 834 OUTCHAR(' '); 835 836 if(is_alt && base == 16) { 837 OUTCHAR('0'); 838 if(flags & FLAGS_UPPER) 839 OUTCHAR('X'); 840 else 841 OUTCHAR('x'); 842 } 843 844 if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL)) 845 while(width-- > 0) 846 OUTCHAR('0'); 847 848 /* Write the number. */ 849 while(++w <= workend) { 850 OUTCHAR(*w); 851 } 852 853 if(flags & FLAGS_LEFT) 854 while(width-- > 0) 855 OUTCHAR(' '); 856 857 return FALSE; 858 } 859 860 static const char nilstr[] = "(nil)"; 861 862 static bool out_string(void *userp, 863 int (*stream)(unsigned char, void *), 864 struct mproperty *p, 865 const char *str, 866 int *donep) 867 { 868 int flags = p->flags; 869 int width = p->width; 870 int prec = p->prec; 871 size_t len; 872 873 if(!str) { 874 /* Write null string if there is space. */ 875 if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) { 876 str = nilstr; 877 len = sizeof(nilstr) - 1; 878 /* Disable quotes around (nil) */ 879 flags &= ~(unsigned int)FLAGS_ALT; 880 } 881 else { 882 str = ""; 883 len = 0; 884 } 885 } 886 else if(prec != -1) 887 len = (size_t)prec; 888 else if(*str == '\0') 889 len = 0; 890 else 891 len = strlen(str); 892 893 width -= (len > INT_MAX) ? INT_MAX : (int)len; 894 895 if(flags & FLAGS_ALT) 896 OUTCHAR('"'); 897 898 if(!(flags & FLAGS_LEFT)) 899 while(width-- > 0) 900 OUTCHAR(' '); 901 902 for(; len && *str; len--) 903 OUTCHAR(*str++); 904 if(flags & FLAGS_LEFT) 905 while(width-- > 0) 906 OUTCHAR(' '); 907 908 if(flags & FLAGS_ALT) 909 OUTCHAR('"'); 910 911 return FALSE; 912 } 913 914 static bool out_pointer(void *userp, 915 int (*stream)(unsigned char, void *), 916 struct mproperty *p, 917 const char *ptr, 918 char *work, 919 int *donep) 920 { 921 /* Generic pointer. */ 922 if(ptr) { 923 size_t num = (size_t) ptr; 924 925 /* If the pointer is not NULL, write it as a %#x spec. */ 926 p->flags |= FLAGS_HEX|FLAGS_ALT; 927 if(out_number(userp, stream, p, num, 0, work, donep)) 928 return TRUE; 929 } 930 else { 931 /* Write "(nil)" for a nil pointer. */ 932 const char *point; 933 int width = p->width; 934 int flags = p->flags; 935 936 width -= (int)(sizeof(nilstr) - 1); 937 if(flags & FLAGS_LEFT) 938 while(width-- > 0) 939 OUTCHAR(' '); 940 for(point = nilstr; *point; ++point) 941 OUTCHAR(*point); 942 if(!(flags & FLAGS_LEFT)) 943 while(width-- > 0) 944 OUTCHAR(' '); 945 } 946 return FALSE; 947 } 948 949 /* 950 * formatf() - the general printf function. 951 * 952 * It calls parsefmt() to parse the format string. It populates two arrays; 953 * one that describes the input arguments and one that describes a number of 954 * output segments. 955 * 956 * On success, the input array describes the type of all arguments and their 957 * values. 958 * 959 * The function then iterates over the output segments and outputs them one 960 * by one until done. Using the appropriate input arguments (if any). 961 * 962 * All output is sent to the 'stream()' callback, one byte at a time. 963 */ 964 965 static int formatf( 966 void *userp, /* untouched by format(), just sent to the stream() function in 967 the second argument */ 968 /* function pointer called for each output character */ 969 int (*stream)(unsigned char, void *), 970 const char *format, /* %-formatted string */ 971 va_list ap_save) /* list of parameters */ 972 { 973 int done = 0; /* number of characters written */ 974 int i; 975 int ocount = 0; /* number of output segments */ 976 int icount = 0; /* number of input arguments */ 977 978 struct outsegment output[MAX_SEGMENTS]; 979 struct va_input input[MAX_PARAMETERS]; 980 char work[BUFFSIZE + 2]; 981 982 /* Parse the format string */ 983 if(parsefmt(format, output, input, &ocount, &icount, ap_save)) 984 return 0; 985 986 for(i = 0; i < ocount; i++) { 987 struct outsegment *optr = &output[i]; 988 struct va_input *iptr = &input[optr->input]; 989 struct mproperty p; 990 size_t outlen = optr->outlen; 991 992 if(outlen) { 993 const char *str = optr->start; 994 for(; outlen && *str; outlen--) { 995 if(stream(*str++, userp)) 996 return done; 997 done++; 998 } 999 if(optr->flags & FLAGS_SUBSTR) 1000 /* this is just a substring */ 1001 continue; 1002 } 1003 1004 p.flags = optr->flags; 1005 1006 /* pick up the specified width */ 1007 if(p.flags & FLAGS_WIDTHPARAM) { 1008 p.width = (int)input[optr->width].val.nums; 1009 if(p.width < 0) { 1010 /* "A negative field width is taken as a '-' flag followed by a 1011 positive field width." */ 1012 if(p.width == INT_MIN) 1013 p.width = INT_MAX; 1014 else 1015 p.width = -p.width; 1016 p.flags |= FLAGS_LEFT; 1017 p.flags &= ~(unsigned int)FLAGS_PAD_NIL; 1018 } 1019 } 1020 else 1021 p.width = optr->width; 1022 1023 /* pick up the specified precision */ 1024 if(p.flags & FLAGS_PRECPARAM) { 1025 p.prec = (int)input[optr->precision].val.nums; 1026 if(p.prec < 0) 1027 /* "A negative precision is taken as if the precision were 1028 omitted." */ 1029 p.prec = -1; 1030 } 1031 else if(p.flags & FLAGS_PREC) 1032 p.prec = optr->precision; 1033 else 1034 p.prec = -1; 1035 1036 switch(iptr->type) { 1037 case FORMAT_INTU: 1038 case FORMAT_LONGU: 1039 case FORMAT_LONGLONGU: 1040 p.flags |= FLAGS_UNSIGNED; 1041 if(out_number(userp, stream, &p, iptr->val.numu, 0, work, &done)) 1042 return done; 1043 break; 1044 1045 case FORMAT_INT: 1046 case FORMAT_LONG: 1047 case FORMAT_LONGLONG: 1048 if(out_number(userp, stream, &p, iptr->val.numu, 1049 iptr->val.nums, work, &done)) 1050 return done; 1051 break; 1052 1053 case FORMAT_STRING: 1054 if(out_string(userp, stream, &p, iptr->val.str, &done)) 1055 return done; 1056 break; 1057 1058 case FORMAT_PTR: 1059 if(out_pointer(userp, stream, &p, iptr->val.ptr, work, &done)) 1060 return done; 1061 break; 1062 1063 case FORMAT_DOUBLE: 1064 if(out_double(userp, stream, &p, iptr->val.dnum, work, &done)) 1065 return done; 1066 break; 1067 1068 case FORMAT_INTPTR: 1069 /* Answer the count of characters written. */ 1070 #ifdef HAVE_LONG_LONG_TYPE 1071 if(p.flags & FLAGS_LONGLONG) 1072 *(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done; 1073 else 1074 #endif 1075 if(p.flags & FLAGS_LONG) 1076 *(long *) iptr->val.ptr = (long)done; 1077 else if(!(p.flags & FLAGS_SHORT)) 1078 *(int *) iptr->val.ptr = (int)done; 1079 else 1080 *(short *) iptr->val.ptr = (short)done; 1081 break; 1082 1083 default: 1084 break; 1085 } 1086 } 1087 return done; 1088 } 1089 1090 /* fputc() look-alike */ 1091 static int addbyter(unsigned char outc, void *f) 1092 { 1093 struct nsprintf *infop = f; 1094 if(infop->length < infop->max) { 1095 /* only do this if we have not reached max length yet */ 1096 *infop->buffer++ = (char)outc; /* store */ 1097 infop->length++; /* we are now one byte larger */ 1098 return 0; /* fputc() returns like this on success */ 1099 } 1100 return 1; 1101 } 1102 1103 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, 1104 va_list ap_save) 1105 { 1106 int retcode; 1107 struct nsprintf info; 1108 1109 info.buffer = buffer; 1110 info.length = 0; 1111 info.max = maxlength; 1112 1113 retcode = formatf(&info, addbyter, format, ap_save); 1114 if(info.max) { 1115 /* we terminate this with a zero byte */ 1116 if(info.max == info.length) { 1117 /* we are at maximum, scrap the last letter */ 1118 info.buffer[-1] = 0; 1119 DEBUGASSERT(retcode); 1120 retcode--; /* do not count the nul byte */ 1121 } 1122 else 1123 info.buffer[0] = 0; 1124 } 1125 return retcode; 1126 } 1127 1128 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) 1129 { 1130 int retcode; 1131 va_list ap_save; /* argument pointer */ 1132 va_start(ap_save, format); 1133 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); 1134 va_end(ap_save); 1135 return retcode; 1136 } 1137 1138 /* fputc() look-alike */ 1139 static int alloc_addbyter(unsigned char outc, void *f) 1140 { 1141 struct asprintf *infop = f; 1142 CURLcode result = curlx_dyn_addn(infop->b, &outc, 1); 1143 if(result) { 1144 infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM; 1145 return 1 ; /* fail */ 1146 } 1147 return 0; 1148 } 1149 1150 /* appends the formatted string, returns MERR error code */ 1151 int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) 1152 { 1153 struct asprintf info; 1154 info.b = dyn; 1155 info.merr = MERR_OK; 1156 1157 (void)formatf(&info, alloc_addbyter, format, ap_save); 1158 if(info.merr) { 1159 curlx_dyn_free(info.b); 1160 return info.merr; 1161 } 1162 return 0; 1163 } 1164 1165 char *curl_mvaprintf(const char *format, va_list ap_save) 1166 { 1167 struct asprintf info; 1168 struct dynbuf dyn; 1169 info.b = &dyn; 1170 curlx_dyn_init(info.b, DYN_APRINTF); 1171 info.merr = MERR_OK; 1172 1173 (void)formatf(&info, alloc_addbyter, format, ap_save); 1174 if(info.merr) { 1175 curlx_dyn_free(info.b); 1176 return NULL; 1177 } 1178 if(curlx_dyn_len(info.b)) 1179 return curlx_dyn_ptr(info.b); 1180 return strdup(""); 1181 } 1182 1183 char *curl_maprintf(const char *format, ...) 1184 { 1185 va_list ap_save; 1186 char *s; 1187 va_start(ap_save, format); 1188 s = curl_mvaprintf(format, ap_save); 1189 va_end(ap_save); 1190 return s; 1191 } 1192 1193 static int storebuffer(unsigned char outc, void *f) 1194 { 1195 char **buffer = f; 1196 **buffer = (char)outc; 1197 (*buffer)++; 1198 return 0; 1199 } 1200 1201 int curl_msprintf(char *buffer, const char *format, ...) 1202 { 1203 va_list ap_save; /* argument pointer */ 1204 int retcode; 1205 va_start(ap_save, format); 1206 retcode = formatf(&buffer, storebuffer, format, ap_save); 1207 va_end(ap_save); 1208 *buffer = 0; /* we terminate this with a zero byte */ 1209 return retcode; 1210 } 1211 1212 static int fputc_wrapper(unsigned char outc, void *f) 1213 { 1214 int out = outc; 1215 FILE *s = f; 1216 int rc = fputc(out, s); 1217 return rc == EOF; 1218 } 1219 1220 int curl_mprintf(const char *format, ...) 1221 { 1222 int retcode; 1223 va_list ap_save; /* argument pointer */ 1224 va_start(ap_save, format); 1225 retcode = formatf(stdout, fputc_wrapper, format, ap_save); 1226 va_end(ap_save); 1227 return retcode; 1228 } 1229 1230 int curl_mfprintf(FILE *whereto, const char *format, ...) 1231 { 1232 int retcode; 1233 va_list ap_save; /* argument pointer */ 1234 va_start(ap_save, format); 1235 retcode = formatf(whereto, fputc_wrapper, format, ap_save); 1236 va_end(ap_save); 1237 return retcode; 1238 } 1239 1240 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) 1241 { 1242 int retcode = formatf(&buffer, storebuffer, format, ap_save); 1243 *buffer = 0; /* we terminate this with a zero byte */ 1244 return retcode; 1245 } 1246 1247 int curl_mvprintf(const char *format, va_list ap_save) 1248 { 1249 return formatf(stdout, fputc_wrapper, format, ap_save); 1250 } 1251 1252 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) 1253 { 1254 return formatf(whereto, fputc_wrapper, format, ap_save); 1255 }