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