1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1999 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
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).
29 * Current restrictions:
30 * - Max 128 parameters
31 * - No 'long double' support.
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/
38 #include "curl_setup.h"
40 #include <curl/mprintf.h>
42 #include "curl_memory.h"
43 /* The last #include file should be: */
47 * If SIZEOF_SIZE_T has not been defined, default to the size of long.
51 # define LONG_LONG_TYPE long long
52 # define HAVE_LONG_LONG_TYPE
54 # if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
55 # define LONG_LONG_TYPE __int64
56 # define HAVE_LONG_LONG_TYPE
58 # undef LONG_LONG_TYPE
59 # undef HAVE_LONG_LONG_TYPE
64 * Non-ANSI integer extensions
67 #if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \
68 (defined(__WATCOMC__) && defined(__386__)) || \
69 (defined(__POCC__) && defined(_MSC_VER)) || \
70 (defined(_WIN32_WCE)) || \
71 (defined(__MINGW32__)) || \
72 (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
73 # define MP_HAVE_INT_EXTENSIONS
77 * Max integer data types that mprintf.c is capable
80 #ifdef HAVE_LONG_LONG_TYPE
81 # define mp_intmax_t LONG_LONG_TYPE
82 # define mp_uintmax_t unsigned LONG_LONG_TYPE
84 # define mp_intmax_t long
85 # define mp_uintmax_t unsigned long
88 #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
89 fit negative DBL_MAX (317 letters) */
90 #define MAX_PARAMETERS 128 /* lame static limit */
96 /* Lower-case digits. */
97 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
99 /* Upper-case digits. */
100 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
104 if(stream((unsigned char)(x), (FILE *)data) != -1) \
107 return done; /* return immediately on failure */ \
110 /* Data type to read from the arglist */
121 FORMAT_WIDTH /* For internal use */
124 /* conversion and display flags */
128 FLAGS_SHOWSIGN = 1<<1,
133 FLAGS_LONGLONG = 1<<6,
134 FLAGS_LONGDOUBLE = 1<<7,
135 FLAGS_PAD_NIL = 1<<8,
136 FLAGS_UNSIGNED = 1<<9,
140 FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
141 FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
142 FLAGS_PREC = 1<<15, /* precision was specified */
143 FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
144 FLAGS_CHAR = 1<<17, /* %c story */
145 FLAGS_FLOATE = 1<<18, /* %e or %E */
146 FLAGS_FLOATG = 1<<19 /* %g or %G */
152 long width; /* width OR width parameter number */
153 long precision; /* precision OR precision parameter number */
158 mp_intmax_t as_signed;
159 mp_uintmax_t as_unsigned;
173 bool fail; /* if an alloc has failed and thus the output is not the complete
177 static long dprintf_DollarString(char *input, char **end)
180 while(ISDIGIT(*input)) {
181 if(number < MAX_PARAMETERS) {
183 number += *input - '0';
187 if(number <= MAX_PARAMETERS && ('$' == *input)) {
194 static bool dprintf_IsQualifierNoDollar(const char *fmt)
196 #if defined(MP_HAVE_INT_EXTENSIONS)
197 if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) {
203 case '-': case '+': case ' ': case '#': case '.':
204 case '0': case '1': case '2': case '3': case '4':
205 case '5': case '6': case '7': case '8': case '9':
206 case 'h': case 'l': case 'L': case 'z': case 'q':
208 #if defined(MP_HAVE_INT_EXTENSIONS)
218 /******************************************************************
221 * Create an index with the type of each parameter entry and its
222 * value (may vary in size)
224 * Returns zero on success.
226 ******************************************************************/
228 static int dprintf_Pass1(const char *format, struct va_stack *vto,
229 char **endpos, va_list arglist)
231 char *fmt = (char *)format;
244 continue; /* while */
249 /* Handle the positional case (N$) */
253 this_param = dprintf_DollarString(fmt, &fmt);
255 /* we got no positional, get the next counter */
256 this_param = param_num;
258 if(this_param > max_param)
259 max_param = this_param;
262 * The parameter with number 'i' should be used. Next, we need
263 * to get SIZE and TYPE of the parameter. Add the information
270 /* Handle the flags */
272 while(dprintf_IsQualifierNoDollar(fmt)) {
273 #if defined(MP_HAVE_INT_EXTENSIONS)
274 if(!strncmp(fmt, "I32", 3)) {
278 else if(!strncmp(fmt, "I64", 3)) {
279 flags |= FLAGS_LONGLONG;
287 flags |= FLAGS_SPACE;
290 flags |= FLAGS_SHOWSIGN;
294 flags &= ~FLAGS_PAD_NIL;
301 /* The precision is picked from a specified parameter */
303 flags |= FLAGS_PRECPARAM;
307 i = dprintf_DollarString(fmt, &fmt);
311 precision = param_num;
313 if(precision > max_param)
314 max_param = precision;
318 precision = strtol(fmt, &fmt, 10);
322 flags |= FLAGS_SHORT;
324 #if defined(MP_HAVE_INT_EXTENSIONS)
326 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
327 flags |= FLAGS_LONGLONG;
334 if(flags & FLAGS_LONG)
335 flags |= FLAGS_LONGLONG;
340 flags |= FLAGS_LONGDOUBLE;
343 flags |= FLAGS_LONGLONG;
346 /* the code below generates a warning if -Wunreachable-code is
348 #if (SIZEOF_SIZE_T > SIZEOF_LONG)
349 flags |= FLAGS_LONGLONG;
355 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
356 flags |= FLAGS_LONGLONG;
362 if(!(flags & FLAGS_LEFT))
363 flags |= FLAGS_PAD_NIL;
365 case '1': case '2': case '3': case '4':
366 case '5': case '6': case '7': case '8': case '9':
367 flags |= FLAGS_WIDTH;
368 width = strtol(fmt-1, &fmt, 10);
370 case '*': /* Special case */
371 flags |= FLAGS_WIDTHPARAM;
374 i = dprintf_DollarString(fmt, &fmt);
379 if(width > max_param)
389 /* Handle the specifier */
393 if((i < 0) || (i >= MAX_PARAMETERS))
394 /* out of allowed range */
402 vto[i].type = FORMAT_STRING;
405 vto[i].type = FORMAT_INTPTR;
408 vto[i].type = FORMAT_PTR;
411 vto[i].type = FORMAT_INT;
414 vto[i].type = FORMAT_INT;
415 flags |= FLAGS_UNSIGNED;
418 vto[i].type = FORMAT_INT;
419 flags |= FLAGS_OCTAL;
422 vto[i].type = FORMAT_INT;
423 flags |= FLAGS_HEX|FLAGS_UNSIGNED;
426 vto[i].type = FORMAT_INT;
427 flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
430 vto[i].type = FORMAT_INT;
434 vto[i].type = FORMAT_DOUBLE;
437 vto[i].type = FORMAT_DOUBLE;
438 flags |= FLAGS_FLOATE;
441 vto[i].type = FORMAT_DOUBLE;
442 flags |= FLAGS_FLOATE|FLAGS_UPPER;
445 vto[i].type = FORMAT_DOUBLE;
446 flags |= FLAGS_FLOATG;
449 vto[i].type = FORMAT_DOUBLE;
450 flags |= FLAGS_FLOATG|FLAGS_UPPER;
453 vto[i].type = FORMAT_UNKNOWN;
457 vto[i].flags = flags;
458 vto[i].width = width;
459 vto[i].precision = precision;
461 if(flags & FLAGS_WIDTHPARAM) {
462 /* we have the width specified from a parameter, so we make that
463 parameter's info setup properly */
465 if((k < 0) || (k >= MAX_PARAMETERS))
466 /* out of allowed range */
469 vto[k].type = FORMAT_WIDTH;
470 vto[k].flags = FLAGS_NEW;
471 /* can't use width or precision of width! */
473 vto[k].precision = 0;
475 if(flags & FLAGS_PRECPARAM) {
476 /* we have the precision specified from a parameter, so we make that
477 parameter's info setup properly */
478 long k = precision - 1;
479 if((k < 0) || (k >= MAX_PARAMETERS))
480 /* out of allowed range */
482 vto[i].precision = k;
483 vto[k].type = FORMAT_WIDTH;
484 vto[k].flags = FLAGS_NEW;
485 /* can't use width or precision of width! */
487 vto[k].precision = 0;
489 *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */
493 /* Read the arg list parameters into our data list */
494 for(i = 0; i<max_param; i++) {
495 /* Width/precision arguments must be read before the main argument
496 they are attached to */
497 if(vto[i].flags & FLAGS_WIDTHPARAM) {
498 vto[vto[i].width].data.num.as_signed =
499 (mp_intmax_t)va_arg(arglist, int);
501 if(vto[i].flags & FLAGS_PRECPARAM) {
502 vto[vto[i].precision].data.num.as_signed =
503 (mp_intmax_t)va_arg(arglist, int);
506 switch(vto[i].type) {
508 vto[i].data.str = va_arg(arglist, char *);
514 vto[i].data.ptr = va_arg(arglist, void *);
518 #ifdef HAVE_LONG_LONG_TYPE
519 if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
520 vto[i].data.num.as_unsigned =
521 (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
522 else if(vto[i].flags & FLAGS_LONGLONG)
523 vto[i].data.num.as_signed =
524 (mp_intmax_t)va_arg(arglist, mp_intmax_t);
528 if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
529 vto[i].data.num.as_unsigned =
530 (mp_uintmax_t)va_arg(arglist, unsigned long);
531 else if(vto[i].flags & FLAGS_LONG)
532 vto[i].data.num.as_signed =
533 (mp_intmax_t)va_arg(arglist, long);
534 else if(vto[i].flags & FLAGS_UNSIGNED)
535 vto[i].data.num.as_unsigned =
536 (mp_uintmax_t)va_arg(arglist, unsigned int);
538 vto[i].data.num.as_signed =
539 (mp_intmax_t)va_arg(arglist, int);
544 vto[i].data.dnum = va_arg(arglist, double);
548 /* Argument has been read. Silently convert it into an integer
551 vto[i].type = FORMAT_INT;
563 static int dprintf_formatf(
564 void *data, /* untouched by format(), just sent to the stream() function in
565 the second argument */
566 /* function pointer called for each output character */
567 int (*stream)(int, FILE *),
568 const char *format, /* %-formatted string */
569 va_list ap_save) /* list of parameters */
571 /* Base-36 digits for numbers. */
572 const char *digits = lower_digits;
574 /* Pointer into the format string. */
577 /* Number of characters written. */
580 long param; /* current parameter to read */
581 long param_num = 0; /* parameter counter */
583 struct va_stack vto[MAX_PARAMETERS];
584 char *endpos[MAX_PARAMETERS];
589 /* 'workend' points to the final buffer byte position, but with an extra
590 byte as margin to avoid the (false?) warning Coverity gives us
592 char *workend = &work[sizeof(work) - 2];
594 /* Do the actual %-code parsing */
595 if(dprintf_Pass1(format, vto, endpos, ap_save))
598 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
603 /* Format spec modifiers. */
606 /* Width of a field. */
609 /* Precision of a field. */
612 /* Decimal integer is negative. */
615 /* Base of a number to be written. */
618 /* Integral values to be written. */
621 /* Used to convert negative in positive. */
622 mp_intmax_t signed_num;
627 /* This isn't a format spec, so write everything out until the next one
628 OR end of string is reached. */
631 } while(*++f && ('%' != *f));
637 /* Check for "%%". Note that although the ANSI standard lists
638 '%' as a conversion specifier, it says "The complete format
639 specification shall be `%%'," so we can avoid all the width
640 and precision processing. */
647 /* If this is a positional parameter, the position must follow immediately
648 after the %, thus create a %<num>$ sequence */
649 param = dprintf_DollarString(f, &f);
656 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
657 third %s will pick the 3rd argument */
661 /* pick up the specified width */
662 if(p->flags & FLAGS_WIDTHPARAM) {
663 width = (long)vto[p->width].data.num.as_signed;
664 param_num++; /* since the width is extracted from a parameter, we
665 must skip that to get to the next one properly */
667 /* "A negative field width is taken as a '-' flag followed by a
668 positive field width." */
670 p->flags |= FLAGS_LEFT;
671 p->flags &= ~FLAGS_PAD_NIL;
677 /* pick up the specified precision */
678 if(p->flags & FLAGS_PRECPARAM) {
679 prec = (long)vto[p->precision].data.num.as_signed;
680 param_num++; /* since the precision is extracted from a parameter, we
681 must skip that to get to the next one properly */
683 /* "A negative precision is taken as if the precision were
687 else if(p->flags & FLAGS_PREC)
692 is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
696 num = p->data.num.as_unsigned;
697 if(p->flags & FLAGS_CHAR) {
699 if(!(p->flags & FLAGS_LEFT))
703 if(p->flags & FLAGS_LEFT)
708 if(p->flags & FLAGS_OCTAL) {
709 /* Octal unsigned integer. */
711 goto unsigned_number;
713 else if(p->flags & FLAGS_HEX) {
714 /* Hexadecimal unsigned integer. */
716 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
718 goto unsigned_number;
720 else if(p->flags & FLAGS_UNSIGNED) {
721 /* Decimal unsigned integer. */
723 goto unsigned_number;
726 /* Decimal integer. */
729 is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
731 /* signed_num might fail to hold absolute negative minimum by 1 */
732 signed_num = p->data.num.as_signed + (mp_intmax_t)1;
733 signed_num = -signed_num;
734 num = (mp_uintmax_t)signed_num;
735 num += (mp_uintmax_t)1;
741 /* Unsigned number of base BASE. */
745 /* Number of base BASE. */
747 /* Supply a default precision if none was given. */
751 /* Put the number in WORK. */
754 *w-- = digits[num % base];
757 width -= (long)(workend - w);
758 prec -= (long)(workend - w);
760 if(is_alt && base == 8 && prec <= 0) {
767 while(prec-- > 0 && w >= work)
771 if(is_alt && base == 16)
774 if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
777 if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
783 else if(p->flags & FLAGS_SHOWSIGN)
785 else if(p->flags & FLAGS_SPACE)
788 if(is_alt && base == 16) {
790 if(p->flags & FLAGS_UPPER)
796 if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
800 /* Write the number. */
801 while(++w <= workend) {
805 if(p->flags & FLAGS_LEFT)
813 static const char null[] = "(nil)";
817 str = (char *) p->data.str;
819 /* Write null[] if there's space. */
820 if(prec == -1 || prec >= (long) sizeof(null) - 1) {
822 len = sizeof(null) - 1;
823 /* Disable quotes around (nil) */
824 p->flags &= (~FLAGS_ALT);
836 width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
838 if(p->flags & FLAGS_ALT)
841 if(!(p->flags&FLAGS_LEFT))
845 for(; len && *str; len--)
847 if(p->flags&FLAGS_LEFT)
851 if(p->flags & FLAGS_ALT)
857 /* Generic pointer. */
860 ptr = (void *) p->data.ptr;
862 /* If the pointer is not NULL, write it as a %#x spec. */
864 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
871 /* Write "(nil)" for a nil pointer. */
872 static const char strnil[] = "(nil)";
875 width -= (long)(sizeof(strnil) - 1);
876 if(p->flags & FLAGS_LEFT)
879 for(point = strnil; *point != '\0'; ++point)
881 if(!(p->flags & FLAGS_LEFT))
890 char formatbuf[32]="%";
891 char *fptr = &formatbuf[1];
892 size_t left = sizeof(formatbuf)-strlen(formatbuf);
896 if(p->flags & FLAGS_WIDTH)
898 else if(p->flags & FLAGS_WIDTHPARAM)
899 width = (long)vto[p->width].data.num.as_signed;
902 if(p->flags & FLAGS_PREC)
904 else if(p->flags & FLAGS_PRECPARAM)
905 prec = (long)vto[p->precision].data.num.as_signed;
907 if(p->flags & FLAGS_LEFT)
909 if(p->flags & FLAGS_SHOWSIGN)
911 if(p->flags & FLAGS_SPACE)
913 if(p->flags & FLAGS_ALT)
919 if(width >= (long)sizeof(work))
920 width = sizeof(work)-1;
921 /* RECURSIVE USAGE */
922 len = curl_msnprintf(fptr, left, "%ld", width);
927 /* for each digit in the integer part, we can have one less
929 size_t maxprec = sizeof(work) - 2;
930 double val = p->data.dnum;
931 if(width > 0 && prec <= width)
938 if(prec > (long)maxprec)
939 prec = (long)maxprec-1;
942 /* RECURSIVE USAGE */
943 len = curl_msnprintf(fptr, left, ".%ld", prec);
946 if(p->flags & FLAGS_LONG)
949 if(p->flags & FLAGS_FLOATE)
950 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
951 else if(p->flags & FLAGS_FLOATG)
952 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
956 *fptr = 0; /* and a final zero termination */
958 /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
960 (sprintf)(work, formatbuf, p->data.dnum);
961 DEBUGASSERT(strlen(work) <= sizeof(work));
962 for(fptr = work; *fptr; fptr++)
968 /* Answer the count of characters written. */
969 #ifdef HAVE_LONG_LONG_TYPE
970 if(p->flags & FLAGS_LONGLONG)
971 *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
974 if(p->flags & FLAGS_LONG)
975 *(long *) p->data.ptr = (long)done;
976 else if(!(p->flags & FLAGS_SHORT))
977 *(int *) p->data.ptr = (int)done;
979 *(short *) p->data.ptr = (short)done;
985 f = *end++; /* goto end of %-code */
991 /* fputc() look-alike */
992 static int addbyter(int output, FILE *data)
994 struct nsprintf *infop = (struct nsprintf *)data;
995 unsigned char outc = (unsigned char)output;
997 if(infop->length < infop->max) {
998 /* only do this if we haven't reached max length yet */
999 infop->buffer[0] = outc; /* store */
1000 infop->buffer++; /* increase pointer */
1001 infop->length++; /* we are now one byte larger */
1002 return outc; /* fputc() returns like this on success */
1007 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1011 struct nsprintf info;
1013 info.buffer = buffer;
1015 info.max = maxlength;
1017 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1018 if((retcode != -1) && info.max) {
1019 /* we terminate this with a zero byte */
1020 if(info.max == info.length)
1021 /* we're at maximum, scrap the last letter */
1022 info.buffer[-1] = 0;
1029 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1032 va_list ap_save; /* argument pointer */
1033 va_start(ap_save, format);
1034 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1039 /* fputc() look-alike */
1040 static int alloc_addbyter(int output, FILE *data)
1042 struct asprintf *infop = (struct asprintf *)data;
1043 unsigned char outc = (unsigned char)output;
1045 if(Curl_dyn_addn(infop->b, &outc, 1)) {
1047 return -1; /* fail */
1049 return outc; /* fputc() returns like this on success */
1052 extern int Curl_dyn_vprintf(struct dynbuf *dyn,
1053 const char *format, va_list ap_save);
1055 /* appends the formatted string, returns 0 on success, 1 on error */
1056 int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
1059 struct asprintf info;
1063 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1064 if((-1 == retcode) || info.fail) {
1065 Curl_dyn_free(info.b);
1071 char *curl_mvaprintf(const char *format, va_list ap_save)
1074 struct asprintf info;
1077 Curl_dyn_init(info.b, DYN_APRINTF);
1080 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1081 if((-1 == retcode) || info.fail) {
1082 Curl_dyn_free(info.b);
1085 if(Curl_dyn_len(info.b))
1086 return Curl_dyn_ptr(info.b);
1090 char *curl_maprintf(const char *format, ...)
1094 va_start(ap_save, format);
1095 s = curl_mvaprintf(format, ap_save);
1100 static int storebuffer(int output, FILE *data)
1102 char **buffer = (char **)data;
1103 unsigned char outc = (unsigned char)output;
1106 return outc; /* act like fputc() ! */
1109 int curl_msprintf(char *buffer, const char *format, ...)
1111 va_list ap_save; /* argument pointer */
1113 va_start(ap_save, format);
1114 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1116 *buffer = 0; /* we terminate this with a zero byte */
1120 int curl_mprintf(const char *format, ...)
1123 va_list ap_save; /* argument pointer */
1124 va_start(ap_save, format);
1126 retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1131 int curl_mfprintf(FILE *whereto, const char *format, ...)
1134 va_list ap_save; /* argument pointer */
1135 va_start(ap_save, format);
1136 retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1141 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1144 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1145 *buffer = 0; /* we terminate this with a zero byte */
1149 int curl_mvprintf(const char *format, va_list ap_save)
1151 return dprintf_formatf(stdout, fputc, format, ap_save);
1154 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1156 return dprintf_formatf(whereto, fputc, format, ap_save);