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