1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1999 - 2022, 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.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.
21 * SPDX-License-Identifier: curl
25 * A merge of Bjorn Reese's format() function and Daniel's dsprintf()
26 * 1.0. A full blooded printf() clone with full support for <num>$
27 * everywhere (parameters, widths and precisions) including variabled
28 * sized parameters (like doubles, long longs, long doubles and even
29 * void * in 64-bit architectures).
31 * Current restrictions:
32 * - Max 128 parameters
33 * - No 'long double' support.
35 * If you ever want truly portable and good *printf() clones, the project that
36 * took on from here is named 'Trio' and you find more details on the trio web
37 * page at https://daniel.haxx.se/projects/trio/
40 #include "curl_setup.h"
42 #include <curl/mprintf.h>
44 #include "curl_memory.h"
45 /* The last #include file should be: */
49 * If SIZEOF_SIZE_T has not been defined, default to the size of long.
53 # define LONG_LONG_TYPE long long
54 # define HAVE_LONG_LONG_TYPE
56 # if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
57 # define LONG_LONG_TYPE __int64
58 # define HAVE_LONG_LONG_TYPE
60 # undef LONG_LONG_TYPE
61 # undef HAVE_LONG_LONG_TYPE
66 * Non-ANSI integer extensions
69 #if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \
70 (defined(__POCC__) && defined(_MSC_VER)) || \
71 (defined(_WIN32_WCE)) || \
72 (defined(__MINGW32__)) || \
73 (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
74 # define MP_HAVE_INT_EXTENSIONS
78 * Max integer data types that mprintf.c is capable
81 #ifdef HAVE_LONG_LONG_TYPE
82 # define mp_intmax_t LONG_LONG_TYPE
83 # define mp_uintmax_t unsigned LONG_LONG_TYPE
85 # define mp_intmax_t long
86 # define mp_uintmax_t unsigned long
89 #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
90 fit negative DBL_MAX (317 letters) */
91 #define MAX_PARAMETERS 128 /* lame static limit */
97 /* Lower-case digits. */
98 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
100 /* Upper-case digits. */
101 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
105 if(stream((unsigned char)(x), (FILE *)data) != -1) \
108 return done; /* return immediately on failure */ \
111 /* Data type to read from the arglist */
122 FORMAT_WIDTH /* For internal use */
125 /* conversion and display flags */
129 FLAGS_SHOWSIGN = 1<<1,
134 FLAGS_LONGLONG = 1<<6,
135 FLAGS_LONGDOUBLE = 1<<7,
136 FLAGS_PAD_NIL = 1<<8,
137 FLAGS_UNSIGNED = 1<<9,
141 FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
142 FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
143 FLAGS_PREC = 1<<15, /* precision was specified */
144 FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
145 FLAGS_CHAR = 1<<17, /* %c story */
146 FLAGS_FLOATE = 1<<18, /* %e or %E */
147 FLAGS_FLOATG = 1<<19 /* %g or %G */
153 long width; /* width OR width parameter number */
154 long precision; /* precision OR precision parameter number */
159 mp_intmax_t as_signed;
160 mp_uintmax_t as_unsigned;
174 bool fail; /* if an alloc has failed and thus the output is not the complete
178 static long dprintf_DollarString(char *input, char **end)
181 while(ISDIGIT(*input)) {
182 if(number < MAX_PARAMETERS) {
184 number += *input - '0';
188 if(number <= MAX_PARAMETERS && ('$' == *input)) {
195 static bool dprintf_IsQualifierNoDollar(const char *fmt)
197 #if defined(MP_HAVE_INT_EXTENSIONS)
198 if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) {
204 case '-': case '+': case ' ': case '#': case '.':
205 case '0': case '1': case '2': case '3': case '4':
206 case '5': case '6': case '7': case '8': case '9':
207 case 'h': case 'l': case 'L': case 'z': case 'q':
209 #if defined(MP_HAVE_INT_EXTENSIONS)
219 /******************************************************************
222 * Create an index with the type of each parameter entry and its
223 * value (may vary in size)
225 * Returns zero on success.
227 ******************************************************************/
229 static int dprintf_Pass1(const char *format, struct va_stack *vto,
230 char **endpos, va_list arglist)
232 char *fmt = (char *)format;
245 continue; /* while */
250 /* Handle the positional case (N$) */
254 this_param = dprintf_DollarString(fmt, &fmt);
256 /* we got no positional, get the next counter */
257 this_param = param_num;
259 if(this_param > max_param)
260 max_param = this_param;
263 * The parameter with number 'i' should be used. Next, we need
264 * to get SIZE and TYPE of the parameter. Add the information
271 /* Handle the flags */
273 while(dprintf_IsQualifierNoDollar(fmt)) {
274 #if defined(MP_HAVE_INT_EXTENSIONS)
275 if(!strncmp(fmt, "I32", 3)) {
279 else if(!strncmp(fmt, "I64", 3)) {
280 flags |= FLAGS_LONGLONG;
288 flags |= FLAGS_SPACE;
291 flags |= FLAGS_SHOWSIGN;
295 flags &= ~FLAGS_PAD_NIL;
302 /* The precision is picked from a specified parameter */
304 flags |= FLAGS_PRECPARAM;
308 i = dprintf_DollarString(fmt, &fmt);
312 precision = param_num;
314 if(precision > max_param)
315 max_param = precision;
319 precision = strtol(fmt, &fmt, 10);
321 if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
322 (FLAGS_PREC | FLAGS_PRECPARAM))
323 /* it is not permitted to use both kinds of precision for the same
328 flags |= FLAGS_SHORT;
330 #if defined(MP_HAVE_INT_EXTENSIONS)
332 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
333 flags |= FLAGS_LONGLONG;
340 if(flags & FLAGS_LONG)
341 flags |= FLAGS_LONGLONG;
346 flags |= FLAGS_LONGDOUBLE;
349 flags |= FLAGS_LONGLONG;
352 /* the code below generates a warning if -Wunreachable-code is
354 #if (SIZEOF_SIZE_T > SIZEOF_LONG)
355 flags |= FLAGS_LONGLONG;
361 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
362 flags |= FLAGS_LONGLONG;
368 if(!(flags & FLAGS_LEFT))
369 flags |= FLAGS_PAD_NIL;
371 case '1': case '2': case '3': case '4':
372 case '5': case '6': case '7': case '8': case '9':
373 flags |= FLAGS_WIDTH;
374 width = strtol(fmt-1, &fmt, 10);
376 case '*': /* Special case */
377 flags |= FLAGS_WIDTHPARAM;
380 i = dprintf_DollarString(fmt, &fmt);
385 if(width > max_param)
395 /* Handle the specifier */
399 if((i < 0) || (i >= MAX_PARAMETERS))
400 /* out of allowed range */
408 vto[i].type = FORMAT_STRING;
411 vto[i].type = FORMAT_INTPTR;
414 vto[i].type = FORMAT_PTR;
417 vto[i].type = FORMAT_INT;
420 vto[i].type = FORMAT_INT;
421 flags |= FLAGS_UNSIGNED;
424 vto[i].type = FORMAT_INT;
425 flags |= FLAGS_OCTAL;
428 vto[i].type = FORMAT_INT;
429 flags |= FLAGS_HEX|FLAGS_UNSIGNED;
432 vto[i].type = FORMAT_INT;
433 flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
436 vto[i].type = FORMAT_INT;
440 vto[i].type = FORMAT_DOUBLE;
443 vto[i].type = FORMAT_DOUBLE;
444 flags |= FLAGS_FLOATE;
447 vto[i].type = FORMAT_DOUBLE;
448 flags |= FLAGS_FLOATE|FLAGS_UPPER;
451 vto[i].type = FORMAT_DOUBLE;
452 flags |= FLAGS_FLOATG;
455 vto[i].type = FORMAT_DOUBLE;
456 flags |= FLAGS_FLOATG|FLAGS_UPPER;
459 vto[i].type = FORMAT_UNKNOWN;
463 vto[i].flags = flags;
464 vto[i].width = width;
465 vto[i].precision = precision;
467 if(flags & FLAGS_WIDTHPARAM) {
468 /* we have the width specified from a parameter, so we make that
469 parameter's info setup properly */
471 if((k < 0) || (k >= MAX_PARAMETERS))
472 /* out of allowed range */
475 vto[k].type = FORMAT_WIDTH;
476 vto[k].flags = FLAGS_NEW;
477 /* can't use width or precision of width! */
479 vto[k].precision = 0;
481 if(flags & FLAGS_PRECPARAM) {
482 /* we have the precision specified from a parameter, so we make that
483 parameter's info setup properly */
484 long k = precision - 1;
485 if((k < 0) || (k >= MAX_PARAMETERS))
486 /* out of allowed range */
488 vto[i].precision = k;
489 vto[k].type = FORMAT_WIDTH;
490 vto[k].flags = FLAGS_NEW;
491 /* can't use width or precision of width! */
493 vto[k].precision = 0;
495 *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */
499 /* Read the arg list parameters into our data list */
500 for(i = 0; i<max_param; i++) {
501 /* Width/precision arguments must be read before the main argument
502 they are attached to */
503 if(vto[i].flags & FLAGS_WIDTHPARAM) {
504 vto[vto[i].width].data.num.as_signed =
505 (mp_intmax_t)va_arg(arglist, int);
507 if(vto[i].flags & FLAGS_PRECPARAM) {
508 vto[vto[i].precision].data.num.as_signed =
509 (mp_intmax_t)va_arg(arglist, int);
512 switch(vto[i].type) {
514 vto[i].data.str = va_arg(arglist, char *);
520 vto[i].data.ptr = va_arg(arglist, void *);
524 #ifdef HAVE_LONG_LONG_TYPE
525 if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
526 vto[i].data.num.as_unsigned =
527 (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
528 else if(vto[i].flags & FLAGS_LONGLONG)
529 vto[i].data.num.as_signed =
530 (mp_intmax_t)va_arg(arglist, mp_intmax_t);
534 if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
535 vto[i].data.num.as_unsigned =
536 (mp_uintmax_t)va_arg(arglist, unsigned long);
537 else if(vto[i].flags & FLAGS_LONG)
538 vto[i].data.num.as_signed =
539 (mp_intmax_t)va_arg(arglist, long);
540 else if(vto[i].flags & FLAGS_UNSIGNED)
541 vto[i].data.num.as_unsigned =
542 (mp_uintmax_t)va_arg(arglist, unsigned int);
544 vto[i].data.num.as_signed =
545 (mp_intmax_t)va_arg(arglist, int);
550 vto[i].data.dnum = va_arg(arglist, double);
554 /* Argument has been read. Silently convert it into an integer
557 vto[i].type = FORMAT_INT;
569 static int dprintf_formatf(
570 void *data, /* untouched by format(), just sent to the stream() function in
571 the second argument */
572 /* function pointer called for each output character */
573 int (*stream)(int, FILE *),
574 const char *format, /* %-formatted string */
575 va_list ap_save) /* list of parameters */
577 /* Base-36 digits for numbers. */
578 const char *digits = lower_digits;
580 /* Pointer into the format string. */
583 /* Number of characters written. */
586 long param; /* current parameter to read */
587 long param_num = 0; /* parameter counter */
589 struct va_stack vto[MAX_PARAMETERS];
590 char *endpos[MAX_PARAMETERS];
595 /* 'workend' points to the final buffer byte position, but with an extra
596 byte as margin to avoid the (false?) warning Coverity gives us
598 char *workend = &work[sizeof(work) - 2];
600 /* Do the actual %-code parsing */
601 if(dprintf_Pass1(format, vto, endpos, ap_save))
604 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
609 /* Format spec modifiers. */
612 /* Width of a field. */
615 /* Precision of a field. */
618 /* Decimal integer is negative. */
621 /* Base of a number to be written. */
624 /* Integral values to be written. */
627 /* Used to convert negative in positive. */
628 mp_intmax_t signed_num;
633 /* This isn't a format spec, so write everything out until the next one
634 OR end of string is reached. */
637 } while(*++f && ('%' != *f));
643 /* Check for "%%". Note that although the ANSI standard lists
644 '%' as a conversion specifier, it says "The complete format
645 specification shall be `%%'," so we can avoid all the width
646 and precision processing. */
653 /* If this is a positional parameter, the position must follow immediately
654 after the %, thus create a %<num>$ sequence */
655 param = dprintf_DollarString(f, &f);
662 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
663 third %s will pick the 3rd argument */
667 /* pick up the specified width */
668 if(p->flags & FLAGS_WIDTHPARAM) {
669 width = (long)vto[p->width].data.num.as_signed;
670 param_num++; /* since the width is extracted from a parameter, we
671 must skip that to get to the next one properly */
673 /* "A negative field width is taken as a '-' flag followed by a
674 positive field width." */
676 p->flags |= FLAGS_LEFT;
677 p->flags &= ~FLAGS_PAD_NIL;
683 /* pick up the specified precision */
684 if(p->flags & FLAGS_PRECPARAM) {
685 prec = (long)vto[p->precision].data.num.as_signed;
686 param_num++; /* since the precision is extracted from a parameter, we
687 must skip that to get to the next one properly */
689 /* "A negative precision is taken as if the precision were
693 else if(p->flags & FLAGS_PREC)
698 is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
702 num = p->data.num.as_unsigned;
703 if(p->flags & FLAGS_CHAR) {
705 if(!(p->flags & FLAGS_LEFT))
709 if(p->flags & FLAGS_LEFT)
714 if(p->flags & FLAGS_OCTAL) {
715 /* Octal unsigned integer. */
717 goto unsigned_number;
719 else if(p->flags & FLAGS_HEX) {
720 /* Hexadecimal unsigned integer. */
722 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
724 goto unsigned_number;
726 else if(p->flags & FLAGS_UNSIGNED) {
727 /* Decimal unsigned integer. */
729 goto unsigned_number;
732 /* Decimal integer. */
735 is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
737 /* signed_num might fail to hold absolute negative minimum by 1 */
738 signed_num = p->data.num.as_signed + (mp_intmax_t)1;
739 signed_num = -signed_num;
740 num = (mp_uintmax_t)signed_num;
741 num += (mp_uintmax_t)1;
747 /* Unsigned number of base BASE. */
751 /* Number of base BASE. */
753 /* Supply a default precision if none was given. */
757 /* Put the number in WORK. */
760 *w-- = digits[num % base];
763 width -= (long)(workend - w);
764 prec -= (long)(workend - w);
766 if(is_alt && base == 8 && prec <= 0) {
773 while(prec-- > 0 && w >= work)
777 if(is_alt && base == 16)
780 if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
783 if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
789 else if(p->flags & FLAGS_SHOWSIGN)
791 else if(p->flags & FLAGS_SPACE)
794 if(is_alt && base == 16) {
796 if(p->flags & FLAGS_UPPER)
802 if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
806 /* Write the number. */
807 while(++w <= workend) {
811 if(p->flags & FLAGS_LEFT)
819 static const char null[] = "(nil)";
823 str = (char *) p->data.str;
825 /* Write null[] if there's space. */
826 if(prec == -1 || prec >= (long) sizeof(null) - 1) {
828 len = sizeof(null) - 1;
829 /* Disable quotes around (nil) */
830 p->flags &= (~FLAGS_ALT);
839 else if(*str == '\0')
844 width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
846 if(p->flags & FLAGS_ALT)
849 if(!(p->flags&FLAGS_LEFT))
853 for(; len && *str; len--)
855 if(p->flags&FLAGS_LEFT)
859 if(p->flags & FLAGS_ALT)
865 /* Generic pointer. */
868 ptr = (void *) p->data.ptr;
870 /* If the pointer is not NULL, write it as a %#x spec. */
872 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
879 /* Write "(nil)" for a nil pointer. */
880 static const char strnil[] = "(nil)";
883 width -= (long)(sizeof(strnil) - 1);
884 if(p->flags & FLAGS_LEFT)
887 for(point = strnil; *point != '\0'; ++point)
889 if(!(p->flags & FLAGS_LEFT))
898 char formatbuf[32]="%";
899 char *fptr = &formatbuf[1];
900 size_t left = sizeof(formatbuf)-strlen(formatbuf);
904 if(p->flags & FLAGS_WIDTH)
906 else if(p->flags & FLAGS_WIDTHPARAM)
907 width = (long)vto[p->width].data.num.as_signed;
910 if(p->flags & FLAGS_PREC)
912 else if(p->flags & FLAGS_PRECPARAM)
913 prec = (long)vto[p->precision].data.num.as_signed;
915 if(p->flags & FLAGS_LEFT)
917 if(p->flags & FLAGS_SHOWSIGN)
919 if(p->flags & FLAGS_SPACE)
921 if(p->flags & FLAGS_ALT)
927 if(width >= (long)sizeof(work))
928 width = sizeof(work)-1;
929 /* RECURSIVE USAGE */
930 len = curl_msnprintf(fptr, left, "%ld", width);
935 /* for each digit in the integer part, we can have one less
937 size_t maxprec = sizeof(work) - 2;
938 double val = p->data.dnum;
939 if(width > 0 && prec <= width)
946 if(prec > (long)maxprec)
947 prec = (long)maxprec-1;
950 /* RECURSIVE USAGE */
951 len = curl_msnprintf(fptr, left, ".%ld", prec);
954 if(p->flags & FLAGS_LONG)
957 if(p->flags & FLAGS_FLOATE)
958 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
959 else if(p->flags & FLAGS_FLOATG)
960 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
964 *fptr = 0; /* and a final null-termination */
967 #pragma clang diagnostic push
968 #pragma clang diagnostic ignored "-Wformat-nonliteral"
970 /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
973 (snprintf)(work, sizeof(work), formatbuf, p->data.dnum);
975 (sprintf)(work, formatbuf, p->data.dnum);
978 #pragma clang diagnostic pop
980 DEBUGASSERT(strlen(work) <= sizeof(work));
981 for(fptr = work; *fptr; fptr++)
987 /* Answer the count of characters written. */
988 #ifdef HAVE_LONG_LONG_TYPE
989 if(p->flags & FLAGS_LONGLONG)
990 *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
993 if(p->flags & FLAGS_LONG)
994 *(long *) p->data.ptr = (long)done;
995 else if(!(p->flags & FLAGS_SHORT))
996 *(int *) p->data.ptr = (int)done;
998 *(short *) p->data.ptr = (short)done;
1004 f = *end++; /* goto end of %-code */
1010 /* fputc() look-alike */
1011 static int addbyter(int output, FILE *data)
1013 struct nsprintf *infop = (struct nsprintf *)data;
1014 unsigned char outc = (unsigned char)output;
1016 if(infop->length < infop->max) {
1017 /* only do this if we haven't reached max length yet */
1018 infop->buffer[0] = outc; /* store */
1019 infop->buffer++; /* increase pointer */
1020 infop->length++; /* we are now one byte larger */
1021 return outc; /* fputc() returns like this on success */
1026 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1030 struct nsprintf info;
1032 info.buffer = buffer;
1034 info.max = maxlength;
1036 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1038 /* we terminate this with a zero byte */
1039 if(info.max == info.length) {
1040 /* we're at maximum, scrap the last letter */
1041 info.buffer[-1] = 0;
1042 DEBUGASSERT(retcode);
1043 retcode--; /* don't count the nul byte */
1051 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1054 va_list ap_save; /* argument pointer */
1055 va_start(ap_save, format);
1056 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1061 /* fputc() look-alike */
1062 static int alloc_addbyter(int output, FILE *data)
1064 struct asprintf *infop = (struct asprintf *)data;
1065 unsigned char outc = (unsigned char)output;
1067 if(Curl_dyn_addn(infop->b, &outc, 1)) {
1069 return -1; /* fail */
1071 return outc; /* fputc() returns like this on success */
1074 extern int Curl_dyn_vprintf(struct dynbuf *dyn,
1075 const char *format, va_list ap_save);
1077 /* appends the formatted string, returns 0 on success, 1 on error */
1078 int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
1080 struct asprintf info;
1084 (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1086 Curl_dyn_free(info.b);
1092 char *curl_mvaprintf(const char *format, va_list ap_save)
1094 struct asprintf info;
1097 Curl_dyn_init(info.b, DYN_APRINTF);
1100 (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1102 Curl_dyn_free(info.b);
1105 if(Curl_dyn_len(info.b))
1106 return Curl_dyn_ptr(info.b);
1110 char *curl_maprintf(const char *format, ...)
1114 va_start(ap_save, format);
1115 s = curl_mvaprintf(format, ap_save);
1120 static int storebuffer(int output, FILE *data)
1122 char **buffer = (char **)data;
1123 unsigned char outc = (unsigned char)output;
1126 return outc; /* act like fputc() ! */
1129 int curl_msprintf(char *buffer, const char *format, ...)
1131 va_list ap_save; /* argument pointer */
1133 va_start(ap_save, format);
1134 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1136 *buffer = 0; /* we terminate this with a zero byte */
1140 int curl_mprintf(const char *format, ...)
1143 va_list ap_save; /* argument pointer */
1144 va_start(ap_save, format);
1146 retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1151 int curl_mfprintf(FILE *whereto, const char *format, ...)
1154 va_list ap_save; /* argument pointer */
1155 va_start(ap_save, format);
1156 retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1161 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1164 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1165 *buffer = 0; /* we terminate this with a zero byte */
1169 int curl_mvprintf(const char *format, va_list ap_save)
1171 return dprintf_formatf(stdout, fputc, format, ap_save);
1174 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1176 return dprintf_formatf(whereto, fputc, format, ap_save);