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