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