Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/mprintf.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1999 - 2017, 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.haxx.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 * | |
| 22 * Purpose: | |
| 23 * A merge of Bjorn Reese's format() function and Daniel's dsprintf() | |
| 24 * 1.0. A full blooded printf() clone with full support for <num>$ | |
| 25 * everywhere (parameters, widths and precisions) including variabled | |
| 26 * sized parameters (like doubles, long longs, long doubles and even | |
| 27 * void * in 64-bit architectures). | |
| 28 * | |
| 29 * Current restrictions: | |
| 30 * - Max 128 parameters | |
| 31 * - No 'long double' support. | |
| 32 * | |
| 33 * If you ever want truly portable and good *printf() clones, the project that | |
| 34 * took on from here is named 'Trio' and you find more details on the trio web | |
| 35 * page at https://daniel.haxx.se/projects/trio/ | |
| 36 */ | |
| 37 | |
| 38 #include "curl_setup.h" | |
| 39 #include <curl/mprintf.h> | |
| 40 | |
| 41 #include "curl_memory.h" | |
| 42 /* The last #include file should be: */ | |
| 43 #include "memdebug.h" | |
| 44 | |
| 45 /* | |
| 46 * If SIZEOF_SIZE_T has not been defined, default to the size of long. | |
| 47 */ | |
| 48 | |
| 49 #ifdef HAVE_LONGLONG | |
| 50 # define LONG_LONG_TYPE long long | |
| 51 # define HAVE_LONG_LONG_TYPE | |
| 52 #else | |
| 53 # if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) | |
| 54 # define LONG_LONG_TYPE __int64 | |
| 55 # define HAVE_LONG_LONG_TYPE | |
| 56 # else | |
| 57 # undef LONG_LONG_TYPE | |
| 58 # undef HAVE_LONG_LONG_TYPE | |
| 59 # endif | |
| 60 #endif | |
| 61 | |
| 62 /* | |
| 63 * Non-ANSI integer extensions | |
| 64 */ | |
| 65 | |
| 66 #if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \ | |
| 67 (defined(__WATCOMC__) && defined(__386__)) || \ | |
| 68 (defined(__POCC__) && defined(_MSC_VER)) || \ | |
| 69 (defined(_WIN32_WCE)) || \ | |
| 70 (defined(__MINGW32__)) || \ | |
| 71 (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)) | |
| 72 # define MP_HAVE_INT_EXTENSIONS | |
| 73 #endif | |
| 74 | |
| 75 /* | |
| 76 * Max integer data types that mprintf.c is capable | |
| 77 */ | |
| 78 | |
| 79 #ifdef HAVE_LONG_LONG_TYPE | |
| 80 # define mp_intmax_t LONG_LONG_TYPE | |
| 81 # define mp_uintmax_t unsigned LONG_LONG_TYPE | |
| 82 #else | |
| 83 # define mp_intmax_t long | |
| 84 # define mp_uintmax_t unsigned long | |
| 85 #endif | |
| 86 | |
| 87 #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should | |
| 88 fit negative DBL_MAX (317 letters) */ | |
| 89 #define MAX_PARAMETERS 128 /* lame static limit */ | |
| 90 | |
| 91 #ifdef __AMIGA__ | |
| 92 # undef FORMAT_INT | |
| 93 #endif | |
| 94 | |
| 95 /* Lower-case digits. */ | |
| 96 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; | |
| 97 | |
| 98 /* Upper-case digits. */ | |
| 99 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
| 100 | |
| 101 #define OUTCHAR(x) \ | |
| 102 do{ \ | |
| 103 if(stream((unsigned char)(x), (FILE *)data) != -1) \ | |
| 104 done++; \ | |
| 105 else \ | |
| 106 return done; /* return immediately on failure */ \ | |
| 107 } WHILE_FALSE | |
| 108 | |
| 109 /* Data type to read from the arglist */ | |
| 110 typedef enum { | |
| 111 FORMAT_UNKNOWN = 0, | |
| 112 FORMAT_STRING, | |
| 113 FORMAT_PTR, | |
| 114 FORMAT_INT, | |
| 115 FORMAT_INTPTR, | |
| 116 FORMAT_LONG, | |
| 117 FORMAT_LONGLONG, | |
| 118 FORMAT_DOUBLE, | |
| 119 FORMAT_LONGDOUBLE, | |
| 120 FORMAT_WIDTH /* For internal use */ | |
| 121 } FormatType; | |
| 122 | |
| 123 /* conversion and display flags */ | |
| 124 enum { | |
| 125 FLAGS_NEW = 0, | |
| 126 FLAGS_SPACE = 1<<0, | |
| 127 FLAGS_SHOWSIGN = 1<<1, | |
| 128 FLAGS_LEFT = 1<<2, | |
| 129 FLAGS_ALT = 1<<3, | |
| 130 FLAGS_SHORT = 1<<4, | |
| 131 FLAGS_LONG = 1<<5, | |
| 132 FLAGS_LONGLONG = 1<<6, | |
| 133 FLAGS_LONGDOUBLE = 1<<7, | |
| 134 FLAGS_PAD_NIL = 1<<8, | |
| 135 FLAGS_UNSIGNED = 1<<9, | |
| 136 FLAGS_OCTAL = 1<<10, | |
| 137 FLAGS_HEX = 1<<11, | |
| 138 FLAGS_UPPER = 1<<12, | |
| 139 FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */ | |
| 140 FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */ | |
| 141 FLAGS_PREC = 1<<15, /* precision was specified */ | |
| 142 FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */ | |
| 143 FLAGS_CHAR = 1<<17, /* %c story */ | |
| 144 FLAGS_FLOATE = 1<<18, /* %e or %E */ | |
| 145 FLAGS_FLOATG = 1<<19 /* %g or %G */ | |
| 146 }; | |
| 147 | |
| 148 typedef struct { | |
| 149 FormatType type; | |
| 150 int flags; | |
| 151 long width; /* width OR width parameter number */ | |
| 152 long precision; /* precision OR precision parameter number */ | |
| 153 union { | |
| 154 char *str; | |
| 155 void *ptr; | |
| 156 union { | |
| 157 mp_intmax_t as_signed; | |
| 158 mp_uintmax_t as_unsigned; | |
| 159 } num; | |
| 160 double dnum; | |
| 161 } data; | |
| 162 } va_stack_t; | |
| 163 | |
| 164 struct nsprintf { | |
| 165 char *buffer; | |
| 166 size_t length; | |
| 167 size_t max; | |
| 168 }; | |
| 169 | |
| 170 struct asprintf { | |
| 171 char *buffer; /* allocated buffer */ | |
| 172 size_t len; /* length of string */ | |
| 173 size_t alloc; /* length of alloc */ | |
| 174 int fail; /* (!= 0) if an alloc has failed and thus | |
| 175 the output is not the complete data */ | |
| 176 }; | |
| 177 | |
| 178 static long dprintf_DollarString(char *input, char **end) | |
| 179 { | |
| 180 int number = 0; | |
| 181 while(ISDIGIT(*input)) { | |
| 182 number *= 10; | |
| 183 number += *input-'0'; | |
| 184 input++; | |
| 185 } | |
| 186 if(number && ('$'==*input++)) { | |
| 187 *end = input; | |
| 188 return number; | |
| 189 } | |
| 190 return 0; | |
| 191 } | |
| 192 | |
| 193 static bool dprintf_IsQualifierNoDollar(const char *fmt) | |
| 194 { | |
| 195 #if defined(MP_HAVE_INT_EXTENSIONS) | |
| 196 if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) { | |
| 197 return TRUE; | |
| 198 } | |
| 199 #endif | |
| 200 | |
| 201 switch(*fmt) { | |
| 202 case '-': case '+': case ' ': case '#': case '.': | |
| 203 case '0': case '1': case '2': case '3': case '4': | |
| 204 case '5': case '6': case '7': case '8': case '9': | |
| 205 case 'h': case 'l': case 'L': case 'z': case 'q': | |
| 206 case '*': case 'O': | |
| 207 #if defined(MP_HAVE_INT_EXTENSIONS) | |
| 208 case 'I': | |
| 209 #endif | |
| 210 return TRUE; | |
| 211 | |
| 212 default: | |
| 213 return FALSE; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 /****************************************************************** | |
| 218 * | |
| 219 * Pass 1: | |
| 220 * Create an index with the type of each parameter entry and its | |
| 221 * value (may vary in size) | |
| 222 * | |
| 223 * Returns zero on success. | |
| 224 * | |
| 225 ******************************************************************/ | |
| 226 | |
| 227 static int dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos, | |
| 228 va_list arglist) | |
| 229 { | |
| 230 char *fmt = (char *)format; | |
| 231 int param_num = 0; | |
| 232 long this_param; | |
| 233 long width; | |
| 234 long precision; | |
| 235 int flags; | |
| 236 long max_param = 0; | |
| 237 long i; | |
| 238 | |
| 239 while(*fmt) { | |
| 240 if(*fmt++ == '%') { | |
| 241 if(*fmt == '%') { | |
| 242 fmt++; | |
| 243 continue; /* while */ | |
| 244 } | |
| 245 | |
| 246 flags = FLAGS_NEW; | |
| 247 | |
| 248 /* Handle the positional case (N$) */ | |
| 249 | |
| 250 param_num++; | |
| 251 | |
| 252 this_param = dprintf_DollarString(fmt, &fmt); | |
| 253 if(0 == this_param) | |
| 254 /* we got no positional, get the next counter */ | |
| 255 this_param = param_num; | |
| 256 | |
| 257 if(this_param > max_param) | |
| 258 max_param = this_param; | |
| 259 | |
| 260 /* | |
| 261 * The parameter with number 'i' should be used. Next, we need | |
| 262 * to get SIZE and TYPE of the parameter. Add the information | |
| 263 * to our array. | |
| 264 */ | |
| 265 | |
| 266 width = 0; | |
| 267 precision = 0; | |
| 268 | |
| 269 /* Handle the flags */ | |
| 270 | |
| 271 while(dprintf_IsQualifierNoDollar(fmt)) { | |
| 272 #if defined(MP_HAVE_INT_EXTENSIONS) | |
| 273 if(!strncmp(fmt, "I32", 3)) { | |
| 274 flags |= FLAGS_LONG; | |
| 275 fmt += 3; | |
| 276 } | |
| 277 else if(!strncmp(fmt, "I64", 3)) { | |
| 278 flags |= FLAGS_LONGLONG; | |
| 279 fmt += 3; | |
| 280 } | |
| 281 else | |
| 282 #endif | |
| 283 | |
| 284 switch(*fmt++) { | |
| 285 case ' ': | |
| 286 flags |= FLAGS_SPACE; | |
| 287 break; | |
| 288 case '+': | |
| 289 flags |= FLAGS_SHOWSIGN; | |
| 290 break; | |
| 291 case '-': | |
| 292 flags |= FLAGS_LEFT; | |
| 293 flags &= ~FLAGS_PAD_NIL; | |
| 294 break; | |
| 295 case '#': | |
| 296 flags |= FLAGS_ALT; | |
| 297 break; | |
| 298 case '.': | |
| 299 if('*' == *fmt) { | |
| 300 /* The precision is picked from a specified parameter */ | |
| 301 | |
| 302 flags |= FLAGS_PRECPARAM; | |
| 303 fmt++; | |
| 304 param_num++; | |
| 305 | |
| 306 i = dprintf_DollarString(fmt, &fmt); | |
| 307 if(i) | |
| 308 precision = i; | |
| 309 else | |
| 310 precision = param_num; | |
| 311 | |
| 312 if(precision > max_param) | |
| 313 max_param = precision; | |
| 314 } | |
| 315 else { | |
| 316 flags |= FLAGS_PREC; | |
| 317 precision = strtol(fmt, &fmt, 10); | |
| 318 } | |
| 319 break; | |
| 320 case 'h': | |
| 321 flags |= FLAGS_SHORT; | |
| 322 break; | |
| 323 #if defined(MP_HAVE_INT_EXTENSIONS) | |
| 324 case 'I': | |
| 325 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) | |
| 326 flags |= FLAGS_LONGLONG; | |
| 327 #else | |
| 328 flags |= FLAGS_LONG; | |
| 329 #endif | |
| 330 break; | |
| 331 #endif | |
| 332 case 'l': | |
| 333 if(flags & FLAGS_LONG) | |
| 334 flags |= FLAGS_LONGLONG; | |
| 335 else | |
| 336 flags |= FLAGS_LONG; | |
| 337 break; | |
| 338 case 'L': | |
| 339 flags |= FLAGS_LONGDOUBLE; | |
| 340 break; | |
| 341 case 'q': | |
| 342 flags |= FLAGS_LONGLONG; | |
| 343 break; | |
| 344 case 'z': | |
| 345 /* the code below generates a warning if -Wunreachable-code is | |
| 346 used */ | |
| 347 #if (SIZEOF_SIZE_T > SIZEOF_LONG) | |
| 348 flags |= FLAGS_LONGLONG; | |
| 349 #else | |
| 350 flags |= FLAGS_LONG; | |
| 351 #endif | |
| 352 break; | |
| 353 case 'O': | |
| 354 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) | |
| 355 flags |= FLAGS_LONGLONG; | |
| 356 #else | |
| 357 flags |= FLAGS_LONG; | |
| 358 #endif | |
| 359 break; | |
| 360 case '0': | |
| 361 if(!(flags & FLAGS_LEFT)) | |
| 362 flags |= FLAGS_PAD_NIL; | |
| 363 /* FALLTHROUGH */ | |
| 364 case '1': case '2': case '3': case '4': | |
| 365 case '5': case '6': case '7': case '8': case '9': | |
| 366 flags |= FLAGS_WIDTH; | |
| 367 width = strtol(fmt-1, &fmt, 10); | |
| 368 break; | |
| 369 case '*': /* Special case */ | |
| 370 flags |= FLAGS_WIDTHPARAM; | |
| 371 param_num++; | |
| 372 | |
| 373 i = dprintf_DollarString(fmt, &fmt); | |
| 374 if(i) | |
| 375 width = i; | |
| 376 else | |
| 377 width = param_num; | |
| 378 if(width > max_param) | |
| 379 max_param = width; | |
| 380 break; | |
| 381 default: | |
| 382 break; | |
| 383 } | |
| 384 } /* switch */ | |
| 385 | |
| 386 /* Handle the specifier */ | |
| 387 | |
| 388 i = this_param - 1; | |
| 389 | |
| 390 if((i < 0) || (i >= MAX_PARAMETERS)) | |
| 391 /* out of allowed range */ | |
| 392 return 1; | |
| 393 | |
| 394 switch (*fmt) { | |
| 395 case 'S': | |
| 396 flags |= FLAGS_ALT; | |
| 397 /* FALLTHROUGH */ | |
| 398 case 's': | |
| 399 vto[i].type = FORMAT_STRING; | |
| 400 break; | |
| 401 case 'n': | |
| 402 vto[i].type = FORMAT_INTPTR; | |
| 403 break; | |
| 404 case 'p': | |
| 405 vto[i].type = FORMAT_PTR; | |
| 406 break; | |
| 407 case 'd': case 'i': | |
| 408 vto[i].type = FORMAT_INT; | |
| 409 break; | |
| 410 case 'u': | |
| 411 vto[i].type = FORMAT_INT; | |
| 412 flags |= FLAGS_UNSIGNED; | |
| 413 break; | |
| 414 case 'o': | |
| 415 vto[i].type = FORMAT_INT; | |
| 416 flags |= FLAGS_OCTAL; | |
| 417 break; | |
| 418 case 'x': | |
| 419 vto[i].type = FORMAT_INT; | |
| 420 flags |= FLAGS_HEX|FLAGS_UNSIGNED; | |
| 421 break; | |
| 422 case 'X': | |
| 423 vto[i].type = FORMAT_INT; | |
| 424 flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; | |
| 425 break; | |
| 426 case 'c': | |
| 427 vto[i].type = FORMAT_INT; | |
| 428 flags |= FLAGS_CHAR; | |
| 429 break; | |
| 430 case 'f': | |
| 431 vto[i].type = FORMAT_DOUBLE; | |
| 432 break; | |
| 433 case 'e': | |
| 434 vto[i].type = FORMAT_DOUBLE; | |
| 435 flags |= FLAGS_FLOATE; | |
| 436 break; | |
| 437 case 'E': | |
| 438 vto[i].type = FORMAT_DOUBLE; | |
| 439 flags |= FLAGS_FLOATE|FLAGS_UPPER; | |
| 440 break; | |
| 441 case 'g': | |
| 442 vto[i].type = FORMAT_DOUBLE; | |
| 443 flags |= FLAGS_FLOATG; | |
| 444 break; | |
| 445 case 'G': | |
| 446 vto[i].type = FORMAT_DOUBLE; | |
| 447 flags |= FLAGS_FLOATG|FLAGS_UPPER; | |
| 448 break; | |
| 449 default: | |
| 450 vto[i].type = FORMAT_UNKNOWN; | |
| 451 break; | |
| 452 } /* switch */ | |
| 453 | |
| 454 vto[i].flags = flags; | |
| 455 vto[i].width = width; | |
| 456 vto[i].precision = precision; | |
| 457 | |
| 458 if(flags & FLAGS_WIDTHPARAM) { | |
| 459 /* we have the width specified from a parameter, so we make that | |
| 460 parameter's info setup properly */ | |
| 461 long k = width - 1; | |
| 462 vto[i].width = k; | |
| 463 vto[k].type = FORMAT_WIDTH; | |
| 464 vto[k].flags = FLAGS_NEW; | |
| 465 /* can't use width or precision of width! */ | |
| 466 vto[k].width = 0; | |
| 467 vto[k].precision = 0; | |
| 468 } | |
| 469 if(flags & FLAGS_PRECPARAM) { | |
| 470 /* we have the precision specified from a parameter, so we make that | |
| 471 parameter's info setup properly */ | |
| 472 long k = precision - 1; | |
| 473 vto[i].precision = k; | |
| 474 vto[k].type = FORMAT_WIDTH; | |
| 475 vto[k].flags = FLAGS_NEW; | |
| 476 /* can't use width or precision of width! */ | |
| 477 vto[k].width = 0; | |
| 478 vto[k].precision = 0; | |
| 479 } | |
| 480 *endpos++ = fmt + 1; /* end of this sequence */ | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 /* Read the arg list parameters into our data list */ | |
| 485 for(i = 0; i<max_param; i++) { | |
| 486 /* Width/precision arguments must be read before the main argument | |
| 487 they are attached to */ | |
| 488 if(vto[i].flags & FLAGS_WIDTHPARAM) { | |
| 489 vto[vto[i].width].data.num.as_signed = | |
| 490 (mp_intmax_t)va_arg(arglist, int); | |
| 491 } | |
| 492 if(vto[i].flags & FLAGS_PRECPARAM) { | |
| 493 vto[vto[i].precision].data.num.as_signed = | |
| 494 (mp_intmax_t)va_arg(arglist, int); | |
| 495 } | |
| 496 | |
| 497 switch(vto[i].type) { | |
| 498 case FORMAT_STRING: | |
| 499 vto[i].data.str = va_arg(arglist, char *); | |
| 500 break; | |
| 501 | |
| 502 case FORMAT_INTPTR: | |
| 503 case FORMAT_UNKNOWN: | |
| 504 case FORMAT_PTR: | |
| 505 vto[i].data.ptr = va_arg(arglist, void *); | |
| 506 break; | |
| 507 | |
| 508 case FORMAT_INT: | |
| 509 #ifdef HAVE_LONG_LONG_TYPE | |
| 510 if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED)) | |
| 511 vto[i].data.num.as_unsigned = | |
| 512 (mp_uintmax_t)va_arg(arglist, mp_uintmax_t); | |
| 513 else if(vto[i].flags & FLAGS_LONGLONG) | |
| 514 vto[i].data.num.as_signed = | |
| 515 (mp_intmax_t)va_arg(arglist, mp_intmax_t); | |
| 516 else | |
| 517 #endif | |
| 518 { | |
| 519 if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED)) | |
| 520 vto[i].data.num.as_unsigned = | |
| 521 (mp_uintmax_t)va_arg(arglist, unsigned long); | |
| 522 else if(vto[i].flags & FLAGS_LONG) | |
| 523 vto[i].data.num.as_signed = | |
| 524 (mp_intmax_t)va_arg(arglist, long); | |
| 525 else if(vto[i].flags & FLAGS_UNSIGNED) | |
| 526 vto[i].data.num.as_unsigned = | |
| 527 (mp_uintmax_t)va_arg(arglist, unsigned int); | |
| 528 else | |
| 529 vto[i].data.num.as_signed = | |
| 530 (mp_intmax_t)va_arg(arglist, int); | |
| 531 } | |
| 532 break; | |
| 533 | |
| 534 case FORMAT_DOUBLE: | |
| 535 vto[i].data.dnum = va_arg(arglist, double); | |
| 536 break; | |
| 537 | |
| 538 case FORMAT_WIDTH: | |
| 539 /* Argument has been read. Silently convert it into an integer | |
| 540 * for later use | |
| 541 */ | |
| 542 vto[i].type = FORMAT_INT; | |
| 543 break; | |
| 544 | |
| 545 default: | |
| 546 break; | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 return 0; | |
| 551 | |
| 552 } | |
| 553 | |
| 554 static int dprintf_formatf( | |
| 555 void *data, /* untouched by format(), just sent to the stream() function in | |
| 556 the second argument */ | |
| 557 /* function pointer called for each output character */ | |
| 558 int (*stream)(int, FILE *), | |
| 559 const char *format, /* %-formatted string */ | |
| 560 va_list ap_save) /* list of parameters */ | |
| 561 { | |
| 562 /* Base-36 digits for numbers. */ | |
| 563 const char *digits = lower_digits; | |
| 564 | |
| 565 /* Pointer into the format string. */ | |
| 566 char *f; | |
| 567 | |
| 568 /* Number of characters written. */ | |
| 569 int done = 0; | |
| 570 | |
| 571 long param; /* current parameter to read */ | |
| 572 long param_num = 0; /* parameter counter */ | |
| 573 | |
| 574 va_stack_t vto[MAX_PARAMETERS]; | |
| 575 char *endpos[MAX_PARAMETERS]; | |
| 576 char **end; | |
| 577 | |
| 578 char work[BUFFSIZE]; | |
| 579 | |
| 580 va_stack_t *p; | |
| 581 | |
| 582 /* 'workend' points to the final buffer byte position, but with an extra | |
| 583 byte as margin to avoid the (false?) warning Coverity gives us | |
| 584 otherwise */ | |
| 585 char *workend = &work[sizeof(work) - 2]; | |
| 586 | |
| 587 /* Do the actual %-code parsing */ | |
| 588 if(dprintf_Pass1(format, vto, endpos, ap_save)) | |
| 589 return -1; | |
| 590 | |
| 591 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1() | |
| 592 created for us */ | |
| 593 | |
| 594 f = (char *)format; | |
| 595 while(*f != '\0') { | |
| 596 /* Format spec modifiers. */ | |
| 597 int is_alt; | |
| 598 | |
| 599 /* Width of a field. */ | |
| 600 long width; | |
| 601 | |
| 602 /* Precision of a field. */ | |
| 603 long prec; | |
| 604 | |
| 605 /* Decimal integer is negative. */ | |
| 606 int is_neg; | |
| 607 | |
| 608 /* Base of a number to be written. */ | |
| 609 unsigned long base; | |
| 610 | |
| 611 /* Integral values to be written. */ | |
| 612 mp_uintmax_t num; | |
| 613 | |
| 614 /* Used to convert negative in positive. */ | |
| 615 mp_intmax_t signed_num; | |
| 616 | |
| 617 char *w; | |
| 618 | |
| 619 if(*f != '%') { | |
| 620 /* This isn't a format spec, so write everything out until the next one | |
| 621 OR end of string is reached. */ | |
| 622 do { | |
| 623 OUTCHAR(*f); | |
| 624 } while(*++f && ('%' != *f)); | |
| 625 continue; | |
| 626 } | |
| 627 | |
| 628 ++f; | |
| 629 | |
| 630 /* Check for "%%". Note that although the ANSI standard lists | |
| 631 '%' as a conversion specifier, it says "The complete format | |
| 632 specification shall be `%%'," so we can avoid all the width | |
| 633 and precision processing. */ | |
| 634 if(*f == '%') { | |
| 635 ++f; | |
| 636 OUTCHAR('%'); | |
| 637 continue; | |
| 638 } | |
| 639 | |
| 640 /* If this is a positional parameter, the position must follow immediately | |
| 641 after the %, thus create a %<num>$ sequence */ | |
| 642 param = dprintf_DollarString(f, &f); | |
| 643 | |
| 644 if(!param) | |
| 645 param = param_num; | |
| 646 else | |
| 647 --param; | |
| 648 | |
| 649 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the | |
| 650 third %s will pick the 3rd argument */ | |
| 651 | |
| 652 p = &vto[param]; | |
| 653 | |
| 654 /* pick up the specified width */ | |
| 655 if(p->flags & FLAGS_WIDTHPARAM) { | |
| 656 width = (long)vto[p->width].data.num.as_signed; | |
| 657 param_num++; /* since the width is extracted from a parameter, we | |
| 658 must skip that to get to the next one properly */ | |
| 659 if(width < 0) { | |
| 660 /* "A negative field width is taken as a '-' flag followed by a | |
| 661 positive field width." */ | |
| 662 width = -width; | |
| 663 p->flags |= FLAGS_LEFT; | |
| 664 p->flags &= ~FLAGS_PAD_NIL; | |
| 665 } | |
| 666 } | |
| 667 else | |
| 668 width = p->width; | |
| 669 | |
| 670 /* pick up the specified precision */ | |
| 671 if(p->flags & FLAGS_PRECPARAM) { | |
| 672 prec = (long)vto[p->precision].data.num.as_signed; | |
| 673 param_num++; /* since the precision is extracted from a parameter, we | |
| 674 must skip that to get to the next one properly */ | |
| 675 if(prec < 0) | |
| 676 /* "A negative precision is taken as if the precision were | |
| 677 omitted." */ | |
| 678 prec = -1; | |
| 679 } | |
| 680 else if(p->flags & FLAGS_PREC) | |
| 681 prec = p->precision; | |
| 682 else | |
| 683 prec = -1; | |
| 684 | |
| 685 is_alt = (p->flags & FLAGS_ALT) ? 1 : 0; | |
| 686 | |
| 687 switch(p->type) { | |
| 688 case FORMAT_INT: | |
| 689 num = p->data.num.as_unsigned; | |
| 690 if(p->flags & FLAGS_CHAR) { | |
| 691 /* Character. */ | |
| 692 if(!(p->flags & FLAGS_LEFT)) | |
| 693 while(--width > 0) | |
| 694 OUTCHAR(' '); | |
| 695 OUTCHAR((char) num); | |
| 696 if(p->flags & FLAGS_LEFT) | |
| 697 while(--width > 0) | |
| 698 OUTCHAR(' '); | |
| 699 break; | |
| 700 } | |
| 701 if(p->flags & FLAGS_OCTAL) { | |
| 702 /* Octal unsigned integer. */ | |
| 703 base = 8; | |
| 704 goto unsigned_number; | |
| 705 } | |
| 706 else if(p->flags & FLAGS_HEX) { | |
| 707 /* Hexadecimal unsigned integer. */ | |
| 708 | |
| 709 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; | |
| 710 base = 16; | |
| 711 goto unsigned_number; | |
| 712 } | |
| 713 else if(p->flags & FLAGS_UNSIGNED) { | |
| 714 /* Decimal unsigned integer. */ | |
| 715 base = 10; | |
| 716 goto unsigned_number; | |
| 717 } | |
| 718 | |
| 719 /* Decimal integer. */ | |
| 720 base = 10; | |
| 721 | |
| 722 is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0; | |
| 723 if(is_neg) { | |
| 724 /* signed_num might fail to hold absolute negative minimum by 1 */ | |
| 725 signed_num = p->data.num.as_signed + (mp_intmax_t)1; | |
| 726 signed_num = -signed_num; | |
| 727 num = (mp_uintmax_t)signed_num; | |
| 728 num += (mp_uintmax_t)1; | |
| 729 } | |
| 730 | |
| 731 goto number; | |
| 732 | |
| 733 unsigned_number: | |
| 734 /* Unsigned number of base BASE. */ | |
| 735 is_neg = 0; | |
| 736 | |
| 737 number: | |
| 738 /* Number of base BASE. */ | |
| 739 | |
| 740 /* Supply a default precision if none was given. */ | |
| 741 if(prec == -1) | |
| 742 prec = 1; | |
| 743 | |
| 744 /* Put the number in WORK. */ | |
| 745 w = workend; | |
| 746 while(num > 0) { | |
| 747 *w-- = digits[num % base]; | |
| 748 num /= base; | |
| 749 } | |
| 750 width -= (long)(workend - w); | |
| 751 prec -= (long)(workend - w); | |
| 752 | |
| 753 if(is_alt && base == 8 && prec <= 0) { | |
| 754 *w-- = '0'; | |
| 755 --width; | |
| 756 } | |
| 757 | |
| 758 if(prec > 0) { | |
| 759 width -= prec; | |
| 760 while(prec-- > 0) | |
| 761 *w-- = '0'; | |
| 762 } | |
| 763 | |
| 764 if(is_alt && base == 16) | |
| 765 width -= 2; | |
| 766 | |
| 767 if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE)) | |
| 768 --width; | |
| 769 | |
| 770 if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL)) | |
| 771 while(width-- > 0) | |
| 772 OUTCHAR(' '); | |
| 773 | |
| 774 if(is_neg) | |
| 775 OUTCHAR('-'); | |
| 776 else if(p->flags & FLAGS_SHOWSIGN) | |
| 777 OUTCHAR('+'); | |
| 778 else if(p->flags & FLAGS_SPACE) | |
| 779 OUTCHAR(' '); | |
| 780 | |
| 781 if(is_alt && base == 16) { | |
| 782 OUTCHAR('0'); | |
| 783 if(p->flags & FLAGS_UPPER) | |
| 784 OUTCHAR('X'); | |
| 785 else | |
| 786 OUTCHAR('x'); | |
| 787 } | |
| 788 | |
| 789 if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL)) | |
| 790 while(width-- > 0) | |
| 791 OUTCHAR('0'); | |
| 792 | |
| 793 /* Write the number. */ | |
| 794 while(++w <= workend) { | |
| 795 OUTCHAR(*w); | |
| 796 } | |
| 797 | |
| 798 if(p->flags & FLAGS_LEFT) | |
| 799 while(width-- > 0) | |
| 800 OUTCHAR(' '); | |
| 801 break; | |
| 802 | |
| 803 case FORMAT_STRING: | |
| 804 /* String. */ | |
| 805 { | |
| 806 static const char null[] = "(nil)"; | |
| 807 const char *str; | |
| 808 size_t len; | |
| 809 | |
| 810 str = (char *) p->data.str; | |
| 811 if(str == NULL) { | |
| 812 /* Write null[] if there's space. */ | |
| 813 if(prec == -1 || prec >= (long) sizeof(null) - 1) { | |
| 814 str = null; | |
| 815 len = sizeof(null) - 1; | |
| 816 /* Disable quotes around (nil) */ | |
| 817 p->flags &= (~FLAGS_ALT); | |
| 818 } | |
| 819 else { | |
| 820 str = ""; | |
| 821 len = 0; | |
| 822 } | |
| 823 } | |
| 824 else if(prec != -1) | |
| 825 len = (size_t)prec; | |
| 826 else | |
| 827 len = strlen(str); | |
| 828 | |
| 829 width -= (len > LONG_MAX) ? LONG_MAX : (long)len; | |
| 830 | |
| 831 if(p->flags & FLAGS_ALT) | |
| 832 OUTCHAR('"'); | |
| 833 | |
| 834 if(!(p->flags&FLAGS_LEFT)) | |
| 835 while(width-- > 0) | |
| 836 OUTCHAR(' '); | |
| 837 | |
| 838 for(; len && *str; len--) | |
| 839 OUTCHAR(*str++); | |
| 840 if(p->flags&FLAGS_LEFT) | |
| 841 while(width-- > 0) | |
| 842 OUTCHAR(' '); | |
| 843 | |
| 844 if(p->flags & FLAGS_ALT) | |
| 845 OUTCHAR('"'); | |
| 846 } | |
| 847 break; | |
| 848 | |
| 849 case FORMAT_PTR: | |
| 850 /* Generic pointer. */ | |
| 851 { | |
| 852 void *ptr; | |
| 853 ptr = (void *) p->data.ptr; | |
| 854 if(ptr != NULL) { | |
| 855 /* If the pointer is not NULL, write it as a %#x spec. */ | |
| 856 base = 16; | |
| 857 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; | |
| 858 is_alt = 1; | |
| 859 num = (size_t) ptr; | |
| 860 is_neg = 0; | |
| 861 goto number; | |
| 862 } | |
| 863 else { | |
| 864 /* Write "(nil)" for a nil pointer. */ | |
| 865 static const char strnil[] = "(nil)"; | |
| 866 const char *point; | |
| 867 | |
| 868 width -= (long)(sizeof(strnil) - 1); | |
| 869 if(p->flags & FLAGS_LEFT) | |
| 870 while(width-- > 0) | |
| 871 OUTCHAR(' '); | |
| 872 for(point = strnil; *point != '\0'; ++point) | |
| 873 OUTCHAR(*point); | |
| 874 if(! (p->flags & FLAGS_LEFT)) | |
| 875 while(width-- > 0) | |
| 876 OUTCHAR(' '); | |
| 877 } | |
| 878 } | |
| 879 break; | |
| 880 | |
| 881 case FORMAT_DOUBLE: | |
| 882 { | |
| 883 char formatbuf[32]="%"; | |
| 884 char *fptr = &formatbuf[1]; | |
| 885 size_t left = sizeof(formatbuf)-strlen(formatbuf); | |
| 886 int len; | |
| 887 | |
| 888 width = -1; | |
| 889 if(p->flags & FLAGS_WIDTH) | |
| 890 width = p->width; | |
| 891 else if(p->flags & FLAGS_WIDTHPARAM) | |
| 892 width = (long)vto[p->width].data.num.as_signed; | |
| 893 | |
| 894 prec = -1; | |
| 895 if(p->flags & FLAGS_PREC) | |
| 896 prec = p->precision; | |
| 897 else if(p->flags & FLAGS_PRECPARAM) | |
| 898 prec = (long)vto[p->precision].data.num.as_signed; | |
| 899 | |
| 900 if(p->flags & FLAGS_LEFT) | |
| 901 *fptr++ = '-'; | |
| 902 if(p->flags & FLAGS_SHOWSIGN) | |
| 903 *fptr++ = '+'; | |
| 904 if(p->flags & FLAGS_SPACE) | |
| 905 *fptr++ = ' '; | |
| 906 if(p->flags & FLAGS_ALT) | |
| 907 *fptr++ = '#'; | |
| 908 | |
| 909 *fptr = 0; | |
| 910 | |
| 911 if(width >= 0) { | |
| 912 if(width >= (long)sizeof(work)) | |
| 913 width = sizeof(work)-1; | |
| 914 /* RECURSIVE USAGE */ | |
| 915 len = curl_msnprintf(fptr, left, "%ld", width); | |
| 916 fptr += len; | |
| 917 left -= len; | |
| 918 } | |
| 919 if(prec >= 0) { | |
| 920 /* for each digit in the integer part, we can have one less | |
| 921 precision */ | |
| 922 size_t maxprec = sizeof(work) - 2; | |
| 923 double val = p->data.dnum; | |
| 924 while(val >= 10.0) { | |
| 925 val /= 10; | |
| 926 maxprec--; | |
| 927 } | |
| 928 | |
| 929 if(prec > (long)maxprec) | |
| 930 prec = (long)maxprec-1; | |
| 931 /* RECURSIVE USAGE */ | |
| 932 len = curl_msnprintf(fptr, left, ".%ld", prec); | |
| 933 fptr += len; | |
| 934 } | |
| 935 if(p->flags & FLAGS_LONG) | |
| 936 *fptr++ = 'l'; | |
| 937 | |
| 938 if(p->flags & FLAGS_FLOATE) | |
| 939 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e'); | |
| 940 else if(p->flags & FLAGS_FLOATG) | |
| 941 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g'); | |
| 942 else | |
| 943 *fptr++ = 'f'; | |
| 944 | |
| 945 *fptr = 0; /* and a final zero termination */ | |
| 946 | |
| 947 /* NOTE NOTE NOTE!! Not all sprintf implementations return number of | |
| 948 output characters */ | |
| 949 (sprintf)(work, formatbuf, p->data.dnum); | |
| 950 DEBUGASSERT(strlen(work) <= sizeof(work)); | |
| 951 for(fptr = work; *fptr; fptr++) | |
| 952 OUTCHAR(*fptr); | |
| 953 } | |
| 954 break; | |
| 955 | |
| 956 case FORMAT_INTPTR: | |
| 957 /* Answer the count of characters written. */ | |
| 958 #ifdef HAVE_LONG_LONG_TYPE | |
| 959 if(p->flags & FLAGS_LONGLONG) | |
| 960 *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done; | |
| 961 else | |
| 962 #endif | |
| 963 if(p->flags & FLAGS_LONG) | |
| 964 *(long *) p->data.ptr = (long)done; | |
| 965 else if(!(p->flags & FLAGS_SHORT)) | |
| 966 *(int *) p->data.ptr = (int)done; | |
| 967 else | |
| 968 *(short *) p->data.ptr = (short)done; | |
| 969 break; | |
| 970 | |
| 971 default: | |
| 972 break; | |
| 973 } | |
| 974 f = *end++; /* goto end of %-code */ | |
| 975 | |
| 976 } | |
| 977 return done; | |
| 978 } | |
| 979 | |
| 980 /* fputc() look-alike */ | |
| 981 static int addbyter(int output, FILE *data) | |
| 982 { | |
| 983 struct nsprintf *infop = (struct nsprintf *)data; | |
| 984 unsigned char outc = (unsigned char)output; | |
| 985 | |
| 986 if(infop->length < infop->max) { | |
| 987 /* only do this if we haven't reached max length yet */ | |
| 988 infop->buffer[0] = outc; /* store */ | |
| 989 infop->buffer++; /* increase pointer */ | |
| 990 infop->length++; /* we are now one byte larger */ | |
| 991 return outc; /* fputc() returns like this on success */ | |
| 992 } | |
| 993 return -1; | |
| 994 } | |
| 995 | |
| 996 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, | |
| 997 va_list ap_save) | |
| 998 { | |
| 999 int retcode; | |
| 1000 struct nsprintf info; | |
| 1001 | |
| 1002 info.buffer = buffer; | |
| 1003 info.length = 0; | |
| 1004 info.max = maxlength; | |
| 1005 | |
| 1006 retcode = dprintf_formatf(&info, addbyter, format, ap_save); | |
| 1007 if((retcode != -1) && info.max) { | |
| 1008 /* we terminate this with a zero byte */ | |
| 1009 if(info.max == info.length) | |
| 1010 /* we're at maximum, scrap the last letter */ | |
| 1011 info.buffer[-1] = 0; | |
| 1012 else | |
| 1013 info.buffer[0] = 0; | |
| 1014 } | |
| 1015 return retcode; | |
| 1016 } | |
| 1017 | |
| 1018 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) | |
| 1019 { | |
| 1020 int retcode; | |
| 1021 va_list ap_save; /* argument pointer */ | |
| 1022 va_start(ap_save, format); | |
| 1023 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); | |
| 1024 va_end(ap_save); | |
| 1025 return retcode; | |
| 1026 } | |
| 1027 | |
| 1028 /* fputc() look-alike */ | |
| 1029 static int alloc_addbyter(int output, FILE *data) | |
| 1030 { | |
| 1031 struct asprintf *infop = (struct asprintf *)data; | |
| 1032 unsigned char outc = (unsigned char)output; | |
| 1033 | |
| 1034 if(!infop->buffer) { | |
| 1035 infop->buffer = malloc(32); | |
| 1036 if(!infop->buffer) { | |
| 1037 infop->fail = 1; | |
| 1038 return -1; /* fail */ | |
| 1039 } | |
| 1040 infop->alloc = 32; | |
| 1041 infop->len = 0; | |
| 1042 } | |
| 1043 else if(infop->len + 1 >= infop->alloc) { | |
| 1044 char *newptr = NULL; | |
| 1045 size_t newsize = infop->alloc*2; | |
| 1046 | |
| 1047 /* detect wrap-around or other overflow problems */ | |
| 1048 if(newsize > infop->alloc) | |
| 1049 newptr = realloc(infop->buffer, newsize); | |
| 1050 | |
| 1051 if(!newptr) { | |
| 1052 infop->fail = 1; | |
| 1053 return -1; /* fail */ | |
| 1054 } | |
| 1055 infop->buffer = newptr; | |
| 1056 infop->alloc = newsize; | |
| 1057 } | |
| 1058 | |
| 1059 infop->buffer[ infop->len ] = outc; | |
| 1060 | |
| 1061 infop->len++; | |
| 1062 | |
| 1063 return outc; /* fputc() returns like this on success */ | |
| 1064 } | |
| 1065 | |
| 1066 char *curl_maprintf(const char *format, ...) | |
| 1067 { | |
| 1068 va_list ap_save; /* argument pointer */ | |
| 1069 int retcode; | |
| 1070 struct asprintf info; | |
| 1071 | |
| 1072 info.buffer = NULL; | |
| 1073 info.len = 0; | |
| 1074 info.alloc = 0; | |
| 1075 info.fail = 0; | |
| 1076 | |
| 1077 va_start(ap_save, format); | |
| 1078 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); | |
| 1079 va_end(ap_save); | |
| 1080 if((-1 == retcode) || info.fail) { | |
| 1081 if(info.alloc) | |
| 1082 free(info.buffer); | |
| 1083 return NULL; | |
| 1084 } | |
| 1085 if(info.alloc) { | |
| 1086 info.buffer[info.len] = 0; /* we terminate this with a zero byte */ | |
| 1087 return info.buffer; | |
| 1088 } | |
| 1089 return strdup(""); | |
| 1090 } | |
| 1091 | |
| 1092 char *curl_mvaprintf(const char *format, va_list ap_save) | |
| 1093 { | |
| 1094 int retcode; | |
| 1095 struct asprintf info; | |
| 1096 | |
| 1097 info.buffer = NULL; | |
| 1098 info.len = 0; | |
| 1099 info.alloc = 0; | |
| 1100 info.fail = 0; | |
| 1101 | |
| 1102 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); | |
| 1103 if((-1 == retcode) || info.fail) { | |
| 1104 if(info.alloc) | |
| 1105 free(info.buffer); | |
| 1106 return NULL; | |
| 1107 } | |
| 1108 | |
| 1109 if(info.alloc) { | |
| 1110 info.buffer[info.len] = 0; /* we terminate this with a zero byte */ | |
| 1111 return info.buffer; | |
| 1112 } | |
| 1113 return strdup(""); | |
| 1114 } | |
| 1115 | |
| 1116 static int storebuffer(int output, FILE *data) | |
| 1117 { | |
| 1118 char **buffer = (char **)data; | |
| 1119 unsigned char outc = (unsigned char)output; | |
| 1120 **buffer = outc; | |
| 1121 (*buffer)++; | |
| 1122 return outc; /* act like fputc() ! */ | |
| 1123 } | |
| 1124 | |
| 1125 int curl_msprintf(char *buffer, const char *format, ...) | |
| 1126 { | |
| 1127 va_list ap_save; /* argument pointer */ | |
| 1128 int retcode; | |
| 1129 va_start(ap_save, format); | |
| 1130 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); | |
| 1131 va_end(ap_save); | |
| 1132 *buffer = 0; /* we terminate this with a zero byte */ | |
| 1133 return retcode; | |
| 1134 } | |
| 1135 | |
| 1136 int curl_mprintf(const char *format, ...) | |
| 1137 { | |
| 1138 int retcode; | |
| 1139 va_list ap_save; /* argument pointer */ | |
| 1140 va_start(ap_save, format); | |
| 1141 | |
| 1142 retcode = dprintf_formatf(stdout, fputc, format, ap_save); | |
| 1143 va_end(ap_save); | |
| 1144 return retcode; | |
| 1145 } | |
| 1146 | |
| 1147 int curl_mfprintf(FILE *whereto, const char *format, ...) | |
| 1148 { | |
| 1149 int retcode; | |
| 1150 va_list ap_save; /* argument pointer */ | |
| 1151 va_start(ap_save, format); | |
| 1152 retcode = dprintf_formatf(whereto, fputc, format, ap_save); | |
| 1153 va_end(ap_save); | |
| 1154 return retcode; | |
| 1155 } | |
| 1156 | |
| 1157 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) | |
| 1158 { | |
| 1159 int retcode; | |
| 1160 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); | |
| 1161 *buffer = 0; /* we terminate this with a zero byte */ | |
| 1162 return retcode; | |
| 1163 } | |
| 1164 | |
| 1165 int curl_mvprintf(const char *format, va_list ap_save) | |
| 1166 { | |
| 1167 return dprintf_formatf(stdout, fputc, format, ap_save); | |
| 1168 } | |
| 1169 | |
| 1170 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) | |
| 1171 { | |
| 1172 return dprintf_formatf(whereto, fputc, format, ap_save); | |
| 1173 } |
