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