1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1999 - 2022, Daniel Stenberg, <[email protected]>, 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. */
98static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
99
100/* Upper-case digits. */
101static 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 */
112typedef 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 */
126enum {
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
150struct 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
166struct nsprintf {
167 char *buffer;
168 size_t length;
169 size_t max;
170};
171
172struct 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
178static 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
195static 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
229static 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 break;
322 case 'h':
323 flags |= FLAGS_SHORT;
324 break;
325#if defined(MP_HAVE_INT_EXTENSIONS)
326 case 'I':
327#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
328 flags |= FLAGS_LONGLONG;
329#else
330 flags |= FLAGS_LONG;
331#endif
332 break;
333#endif
334 case 'l':
335 if(flags & FLAGS_LONG)
336 flags |= FLAGS_LONGLONG;
337 else
338 flags |= FLAGS_LONG;
339 break;
340 case 'L':
341 flags |= FLAGS_LONGDOUBLE;
342 break;
343 case 'q':
344 flags |= FLAGS_LONGLONG;
345 break;
346 case 'z':
347 /* the code below generates a warning if -Wunreachable-code is
348 used */
349#if (SIZEOF_SIZE_T > SIZEOF_LONG)
350 flags |= FLAGS_LONGLONG;
351#else
352 flags |= FLAGS_LONG;
353#endif
354 break;
355 case 'O':
356#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
357 flags |= FLAGS_LONGLONG;
358#else
359 flags |= FLAGS_LONG;
360#endif
361 break;
362 case '0':
363 if(!(flags & FLAGS_LEFT))
364 flags |= FLAGS_PAD_NIL;
365 /* FALLTHROUGH */
366 case '1': case '2': case '3': case '4':
367 case '5': case '6': case '7': case '8': case '9':
368 flags |= FLAGS_WIDTH;
369 width = strtol(fmt-1, &fmt, 10);
370 break;
371 case '*': /* Special case */
372 flags |= FLAGS_WIDTHPARAM;
373 param_num++;
374
375 i = dprintf_DollarString(fmt, &fmt);
376 if(i)
377 width = i;
378 else
379 width = param_num;
380 if(width > max_param)
381 max_param = width;
382 break;
383 case '\0':
384 fmt--;
385 default:
386 break;
387 }
388 } /* switch */
389
390 /* Handle the specifier */
391
392 i = this_param - 1;
393
394 if((i < 0) || (i >= MAX_PARAMETERS))
395 /* out of allowed range */
396 return 1;
397
398 switch (*fmt) {
399 case 'S':
400 flags |= FLAGS_ALT;
401 /* FALLTHROUGH */
402 case 's':
403 vto[i].type = FORMAT_STRING;
404 break;
405 case 'n':
406 vto[i].type = FORMAT_INTPTR;
407 break;
408 case 'p':
409 vto[i].type = FORMAT_PTR;
410 break;
411 case 'd': case 'i':
412 vto[i].type = FORMAT_INT;
413 break;
414 case 'u':
415 vto[i].type = FORMAT_INT;
416 flags |= FLAGS_UNSIGNED;
417 break;
418 case 'o':
419 vto[i].type = FORMAT_INT;
420 flags |= FLAGS_OCTAL;
421 break;
422 case 'x':
423 vto[i].type = FORMAT_INT;
424 flags |= FLAGS_HEX|FLAGS_UNSIGNED;
425 break;
426 case 'X':
427 vto[i].type = FORMAT_INT;
428 flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
429 break;
430 case 'c':
431 vto[i].type = FORMAT_INT;
432 flags |= FLAGS_CHAR;
433 break;
434 case 'f':
435 vto[i].type = FORMAT_DOUBLE;
436 break;
437 case 'e':
438 vto[i].type = FORMAT_DOUBLE;
439 flags |= FLAGS_FLOATE;
440 break;
441 case 'E':
442 vto[i].type = FORMAT_DOUBLE;
443 flags |= FLAGS_FLOATE|FLAGS_UPPER;
444 break;
445 case 'g':
446 vto[i].type = FORMAT_DOUBLE;
447 flags |= FLAGS_FLOATG;
448 break;
449 case 'G':
450 vto[i].type = FORMAT_DOUBLE;
451 flags |= FLAGS_FLOATG|FLAGS_UPPER;
452 break;
453 default:
454 vto[i].type = FORMAT_UNKNOWN;
455 break;
456 } /* switch */
457
458 vto[i].flags = flags;
459 vto[i].width = width;
460 vto[i].precision = precision;
461
462 if(flags & FLAGS_WIDTHPARAM) {
463 /* we have the width specified from a parameter, so we make that
464 parameter's info setup properly */
465 long k = width - 1;
466 if((k < 0) || (k >= MAX_PARAMETERS))
467 /* out of allowed range */
468 return 1;
469 vto[i].width = k;
470 vto[k].type = FORMAT_WIDTH;
471 vto[k].flags = FLAGS_NEW;
472 /* can't use width or precision of width! */
473 vto[k].width = 0;
474 vto[k].precision = 0;
475 }
476 if(flags & FLAGS_PRECPARAM) {
477 /* we have the precision specified from a parameter, so we make that
478 parameter's info setup properly */
479 long k = precision - 1;
480 if((k < 0) || (k >= MAX_PARAMETERS))
481 /* out of allowed range */
482 return 1;
483 vto[i].precision = k;
484 vto[k].type = FORMAT_WIDTH;
485 vto[k].flags = FLAGS_NEW;
486 /* can't use width or precision of width! */
487 vto[k].width = 0;
488 vto[k].precision = 0;
489 }
490 *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */
491 }
492 }
493
494 /* Read the arg list parameters into our data list */
495 for(i = 0; i<max_param; i++) {
496 /* Width/precision arguments must be read before the main argument
497 they are attached to */
498 if(vto[i].flags & FLAGS_WIDTHPARAM) {
499 vto[vto[i].width].data.num.as_signed =
500 (mp_intmax_t)va_arg(arglist, int);
501 }
502 if(vto[i].flags & FLAGS_PRECPARAM) {
503 vto[vto[i].precision].data.num.as_signed =
504 (mp_intmax_t)va_arg(arglist, int);
505 }
506
507 switch(vto[i].type) {
508 case FORMAT_STRING:
509 vto[i].data.str = va_arg(arglist, char *);
510 break;
511
512 case FORMAT_INTPTR:
513 case FORMAT_UNKNOWN:
514 case FORMAT_PTR:
515 vto[i].data.ptr = va_arg(arglist, void *);
516 break;
517
518 case FORMAT_INT:
519#ifdef HAVE_LONG_LONG_TYPE
520 if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
521 vto[i].data.num.as_unsigned =
522 (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
523 else if(vto[i].flags & FLAGS_LONGLONG)
524 vto[i].data.num.as_signed =
525 (mp_intmax_t)va_arg(arglist, mp_intmax_t);
526 else
527#endif
528 {
529 if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
530 vto[i].data.num.as_unsigned =
531 (mp_uintmax_t)va_arg(arglist, unsigned long);
532 else if(vto[i].flags & FLAGS_LONG)
533 vto[i].data.num.as_signed =
534 (mp_intmax_t)va_arg(arglist, long);
535 else if(vto[i].flags & FLAGS_UNSIGNED)
536 vto[i].data.num.as_unsigned =
537 (mp_uintmax_t)va_arg(arglist, unsigned int);
538 else
539 vto[i].data.num.as_signed =
540 (mp_intmax_t)va_arg(arglist, int);
541 }
542 break;
543
544 case FORMAT_DOUBLE:
545 vto[i].data.dnum = va_arg(arglist, double);
546 break;
547
548 case FORMAT_WIDTH:
549 /* Argument has been read. Silently convert it into an integer
550 * for later use
551 */
552 vto[i].type = FORMAT_INT;
553 break;
554
555 default:
556 break;
557 }
558 }
559
560 return 0;
561
562}
563
564static int dprintf_formatf(
565 void *data, /* untouched by format(), just sent to the stream() function in
566 the second argument */
567 /* function pointer called for each output character */
568 int (*stream)(int, FILE *),
569 const char *format, /* %-formatted string */
570 va_list ap_save) /* list of parameters */
571{
572 /* Base-36 digits for numbers. */
573 const char *digits = lower_digits;
574
575 /* Pointer into the format string. */
576 char *f;
577
578 /* Number of characters written. */
579 int done = 0;
580
581 long param; /* current parameter to read */
582 long param_num = 0; /* parameter counter */
583
584 struct va_stack vto[MAX_PARAMETERS];
585 char *endpos[MAX_PARAMETERS];
586 char **end;
587 char work[BUFFSIZE];
588 struct va_stack *p;
589
590 /* 'workend' points to the final buffer byte position, but with an extra
591 byte as margin to avoid the (false?) warning Coverity gives us
592 otherwise */
593 char *workend = &work[sizeof(work) - 2];
594
595 /* Do the actual %-code parsing */
596 if(dprintf_Pass1(format, vto, endpos, ap_save))
597 return 0;
598
599 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
600 created for us */
601
602 f = (char *)format;
603 while(*f != '\0') {
604 /* Format spec modifiers. */
605 int is_alt;
606
607 /* Width of a field. */
608 long width;
609
610 /* Precision of a field. */
611 long prec;
612
613 /* Decimal integer is negative. */
614 int is_neg;
615
616 /* Base of a number to be written. */
617 unsigned long base;
618
619 /* Integral values to be written. */
620 mp_uintmax_t num;
621
622 /* Used to convert negative in positive. */
623 mp_intmax_t signed_num;
624
625 char *w;
626
627 if(*f != '%') {
628 /* This isn't a format spec, so write everything out until the next one
629 OR end of string is reached. */
630 do {
631 OUTCHAR(*f);
632 } while(*++f && ('%' != *f));
633 continue;
634 }
635
636 ++f;
637
638 /* Check for "%%". Note that although the ANSI standard lists
639 '%' as a conversion specifier, it says "The complete format
640 specification shall be `%%'," so we can avoid all the width
641 and precision processing. */
642 if(*f == '%') {
643 ++f;
644 OUTCHAR('%');
645 continue;
646 }
647
648 /* If this is a positional parameter, the position must follow immediately
649 after the %, thus create a %<num>$ sequence */
650 param = dprintf_DollarString(f, &f);
651
652 if(!param)
653 param = param_num;
654 else
655 --param;
656
657 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
658 third %s will pick the 3rd argument */
659
660 p = &vto[param];
661
662 /* pick up the specified width */
663 if(p->flags & FLAGS_WIDTHPARAM) {
664 width = (long)vto[p->width].data.num.as_signed;
665 param_num++; /* since the width is extracted from a parameter, we
666 must skip that to get to the next one properly */
667 if(width < 0) {
668 /* "A negative field width is taken as a '-' flag followed by a
669 positive field width." */
670 width = -width;
671 p->flags |= FLAGS_LEFT;
672 p->flags &= ~FLAGS_PAD_NIL;
673 }
674 }
675 else
676 width = p->width;
677
678 /* pick up the specified precision */
679 if(p->flags & FLAGS_PRECPARAM) {
680 prec = (long)vto[p->precision].data.num.as_signed;
681 param_num++; /* since the precision is extracted from a parameter, we
682 must skip that to get to the next one properly */
683 if(prec < 0)
684 /* "A negative precision is taken as if the precision were
685 omitted." */
686 prec = -1;
687 }
688 else if(p->flags & FLAGS_PREC)
689 prec = p->precision;
690 else
691 prec = -1;
692
693 is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
694
695 switch(p->type) {
696 case FORMAT_INT:
697 num = p->data.num.as_unsigned;
698 if(p->flags & FLAGS_CHAR) {
699 /* Character. */
700 if(!(p->flags & FLAGS_LEFT))
701 while(--width > 0)
702 OUTCHAR(' ');
703 OUTCHAR((char) num);
704 if(p->flags & FLAGS_LEFT)
705 while(--width > 0)
706 OUTCHAR(' ');
707 break;
708 }
709 if(p->flags & FLAGS_OCTAL) {
710 /* Octal unsigned integer. */
711 base = 8;
712 goto unsigned_number;
713 }
714 else if(p->flags & FLAGS_HEX) {
715 /* Hexadecimal unsigned integer. */
716
717 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
718 base = 16;
719 goto unsigned_number;
720 }
721 else if(p->flags & FLAGS_UNSIGNED) {
722 /* Decimal unsigned integer. */
723 base = 10;
724 goto unsigned_number;
725 }
726
727 /* Decimal integer. */
728 base = 10;
729
730 is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
731 if(is_neg) {
732 /* signed_num might fail to hold absolute negative minimum by 1 */
733 signed_num = p->data.num.as_signed + (mp_intmax_t)1;
734 signed_num = -signed_num;
735 num = (mp_uintmax_t)signed_num;
736 num += (mp_uintmax_t)1;
737 }
738
739 goto number;
740
741 unsigned_number:
742 /* Unsigned number of base BASE. */
743 is_neg = 0;
744
745 number:
746 /* Number of base BASE. */
747
748 /* Supply a default precision if none was given. */
749 if(prec == -1)
750 prec = 1;
751
752 /* Put the number in WORK. */
753 w = workend;
754 while(num > 0) {
755 *w-- = digits[num % base];
756 num /= base;
757 }
758 width -= (long)(workend - w);
759 prec -= (long)(workend - w);
760
761 if(is_alt && base == 8 && prec <= 0) {
762 *w-- = '0';
763 --width;
764 }
765
766 if(prec > 0) {
767 width -= prec;
768 while(prec-- > 0 && w >= work)
769 *w-- = '0';
770 }
771
772 if(is_alt && base == 16)
773 width -= 2;
774
775 if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
776 --width;
777
778 if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
779 while(width-- > 0)
780 OUTCHAR(' ');
781
782 if(is_neg)
783 OUTCHAR('-');
784 else if(p->flags & FLAGS_SHOWSIGN)
785 OUTCHAR('+');
786 else if(p->flags & FLAGS_SPACE)
787 OUTCHAR(' ');
788
789 if(is_alt && base == 16) {
790 OUTCHAR('0');
791 if(p->flags & FLAGS_UPPER)
792 OUTCHAR('X');
793 else
794 OUTCHAR('x');
795 }
796
797 if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
798 while(width-- > 0)
799 OUTCHAR('0');
800
801 /* Write the number. */
802 while(++w <= workend) {
803 OUTCHAR(*w);
804 }
805
806 if(p->flags & FLAGS_LEFT)
807 while(width-- > 0)
808 OUTCHAR(' ');
809 break;
810
811 case FORMAT_STRING:
812 /* String. */
813 {
814 static const char null[] = "(nil)";
815 const char *str;
816 size_t len;
817
818 str = (char *) p->data.str;
819 if(!str) {
820 /* Write null[] if there's space. */
821 if(prec == -1 || prec >= (long) sizeof(null) - 1) {
822 str = null;
823 len = sizeof(null) - 1;
824 /* Disable quotes around (nil) */
825 p->flags &= (~FLAGS_ALT);
826 }
827 else {
828 str = "";
829 len = 0;
830 }
831 }
832 else if(prec != -1)
833 len = (size_t)prec;
834 else if(*str == '\0')
835 len = 0;
836 else
837 len = strlen(str);
838
839 width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
840
841 if(p->flags & FLAGS_ALT)
842 OUTCHAR('"');
843
844 if(!(p->flags&FLAGS_LEFT))
845 while(width-- > 0)
846 OUTCHAR(' ');
847
848 for(; len && *str; len--)
849 OUTCHAR(*str++);
850 if(p->flags&FLAGS_LEFT)
851 while(width-- > 0)
852 OUTCHAR(' ');
853
854 if(p->flags & FLAGS_ALT)
855 OUTCHAR('"');
856 }
857 break;
858
859 case FORMAT_PTR:
860 /* Generic pointer. */
861 {
862 void *ptr;
863 ptr = (void *) p->data.ptr;
864 if(ptr) {
865 /* If the pointer is not NULL, write it as a %#x spec. */
866 base = 16;
867 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
868 is_alt = 1;
869 num = (size_t) ptr;
870 is_neg = 0;
871 goto number;
872 }
873 else {
874 /* Write "(nil)" for a nil pointer. */
875 static const char strnil[] = "(nil)";
876 const char *point;
877
878 width -= (long)(sizeof(strnil) - 1);
879 if(p->flags & FLAGS_LEFT)
880 while(width-- > 0)
881 OUTCHAR(' ');
882 for(point = strnil; *point != '\0'; ++point)
883 OUTCHAR(*point);
884 if(!(p->flags & FLAGS_LEFT))
885 while(width-- > 0)
886 OUTCHAR(' ');
887 }
888 }
889 break;
890
891 case FORMAT_DOUBLE:
892 {
893 char formatbuf[32]="%";
894 char *fptr = &formatbuf[1];
895 size_t left = sizeof(formatbuf)-strlen(formatbuf);
896 int len;
897
898 width = -1;
899 if(p->flags & FLAGS_WIDTH)
900 width = p->width;
901 else if(p->flags & FLAGS_WIDTHPARAM)
902 width = (long)vto[p->width].data.num.as_signed;
903
904 prec = -1;
905 if(p->flags & FLAGS_PREC)
906 prec = p->precision;
907 else if(p->flags & FLAGS_PRECPARAM)
908 prec = (long)vto[p->precision].data.num.as_signed;
909
910 if(p->flags & FLAGS_LEFT)
911 *fptr++ = '-';
912 if(p->flags & FLAGS_SHOWSIGN)
913 *fptr++ = '+';
914 if(p->flags & FLAGS_SPACE)
915 *fptr++ = ' ';
916 if(p->flags & FLAGS_ALT)
917 *fptr++ = '#';
918
919 *fptr = 0;
920
921 if(width >= 0) {
922 if(width >= (long)sizeof(work))
923 width = sizeof(work)-1;
924 /* RECURSIVE USAGE */
925 len = curl_msnprintf(fptr, left, "%ld", width);
926 fptr += len;
927 left -= len;
928 }
929 if(prec >= 0) {
930 /* for each digit in the integer part, we can have one less
931 precision */
932 size_t maxprec = sizeof(work) - 2;
933 double val = p->data.dnum;
934 if(width > 0 && prec <= width)
935 maxprec -= width;
936 while(val >= 10.0) {
937 val /= 10;
938 maxprec--;
939 }
940
941 if(prec > (long)maxprec)
942 prec = (long)maxprec-1;
943 if(prec < 0)
944 prec = 0;
945 /* RECURSIVE USAGE */
946 len = curl_msnprintf(fptr, left, ".%ld", prec);
947 fptr += len;
948 }
949 if(p->flags & FLAGS_LONG)
950 *fptr++ = 'l';
951
952 if(p->flags & FLAGS_FLOATE)
953 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
954 else if(p->flags & FLAGS_FLOATG)
955 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
956 else
957 *fptr++ = 'f';
958
959 *fptr = 0; /* and a final zero termination */
960
961#ifdef __clang__
962#pragma clang diagnostic push
963#pragma clang diagnostic ignored "-Wformat-nonliteral"
964#endif
965 /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
966 output characters */
967 (sprintf)(work, formatbuf, p->data.dnum);
968#ifdef __clang__
969#pragma clang diagnostic pop
970#endif
971 DEBUGASSERT(strlen(work) <= sizeof(work));
972 for(fptr = work; *fptr; fptr++)
973 OUTCHAR(*fptr);
974 }
975 break;
976
977 case FORMAT_INTPTR:
978 /* Answer the count of characters written. */
979#ifdef HAVE_LONG_LONG_TYPE
980 if(p->flags & FLAGS_LONGLONG)
981 *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
982 else
983#endif
984 if(p->flags & FLAGS_LONG)
985 *(long *) p->data.ptr = (long)done;
986 else if(!(p->flags & FLAGS_SHORT))
987 *(int *) p->data.ptr = (int)done;
988 else
989 *(short *) p->data.ptr = (short)done;
990 break;
991
992 default:
993 break;
994 }
995 f = *end++; /* goto end of %-code */
996
997 }
998 return done;
999}
1000
1001/* fputc() look-alike */
1002static int addbyter(int output, FILE *data)
1003{
1004 struct nsprintf *infop = (struct nsprintf *)data;
1005 unsigned char outc = (unsigned char)output;
1006
1007 if(infop->length < infop->max) {
1008 /* only do this if we haven't reached max length yet */
1009 infop->buffer[0] = outc; /* store */
1010 infop->buffer++; /* increase pointer */
1011 infop->length++; /* we are now one byte larger */
1012 return outc; /* fputc() returns like this on success */
1013 }
1014 return -1;
1015}
1016
1017int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1018 va_list ap_save)
1019{
1020 int retcode;
1021 struct nsprintf info;
1022
1023 info.buffer = buffer;
1024 info.length = 0;
1025 info.max = maxlength;
1026
1027 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1028 if(info.max) {
1029 /* we terminate this with a zero byte */
1030 if(info.max == info.length) {
1031 /* we're at maximum, scrap the last letter */
1032 info.buffer[-1] = 0;
1033 DEBUGASSERT(retcode);
1034 retcode--; /* don't count the nul byte */
1035 }
1036 else
1037 info.buffer[0] = 0;
1038 }
1039 return retcode;
1040}
1041
1042int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1043{
1044 int retcode;
1045 va_list ap_save; /* argument pointer */
1046 va_start(ap_save, format);
1047 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1048 va_end(ap_save);
1049 return retcode;
1050}
1051
1052/* fputc() look-alike */
1053static int alloc_addbyter(int output, FILE *data)
1054{
1055 struct asprintf *infop = (struct asprintf *)data;
1056 unsigned char outc = (unsigned char)output;
1057
1058 if(Curl_dyn_addn(infop->b, &outc, 1)) {
1059 infop->fail = 1;
1060 return -1; /* fail */
1061 }
1062 return outc; /* fputc() returns like this on success */
1063}
1064
1065extern int Curl_dyn_vprintf(struct dynbuf *dyn,
1066 const char *format, va_list ap_save);
1067
1068/* appends the formatted string, returns 0 on success, 1 on error */
1069int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
1070{
1071 struct asprintf info;
1072 info.b = dyn;
1073 info.fail = 0;
1074
1075 (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1076 if(info.fail) {
1077 Curl_dyn_free(info.b);
1078 return 1;
1079 }
1080 return 0;
1081}
1082
1083char *curl_mvaprintf(const char *format, va_list ap_save)
1084{
1085 struct asprintf info;
1086 struct dynbuf dyn;
1087 info.b = &dyn;
1088 Curl_dyn_init(info.b, DYN_APRINTF);
1089 info.fail = 0;
1090
1091 (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1092 if(info.fail) {
1093 Curl_dyn_free(info.b);
1094 return NULL;
1095 }
1096 if(Curl_dyn_len(info.b))
1097 return Curl_dyn_ptr(info.b);
1098 return strdup("");
1099}
1100
1101char *curl_maprintf(const char *format, ...)
1102{
1103 va_list ap_save;
1104 char *s;
1105 va_start(ap_save, format);
1106 s = curl_mvaprintf(format, ap_save);
1107 va_end(ap_save);
1108 return s;
1109}
1110
1111static int storebuffer(int output, FILE *data)
1112{
1113 char **buffer = (char **)data;
1114 unsigned char outc = (unsigned char)output;
1115 **buffer = outc;
1116 (*buffer)++;
1117 return outc; /* act like fputc() ! */
1118}
1119
1120int curl_msprintf(char *buffer, const char *format, ...)
1121{
1122 va_list ap_save; /* argument pointer */
1123 int retcode;
1124 va_start(ap_save, format);
1125 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1126 va_end(ap_save);
1127 *buffer = 0; /* we terminate this with a zero byte */
1128 return retcode;
1129}
1130
1131int curl_mprintf(const char *format, ...)
1132{
1133 int retcode;
1134 va_list ap_save; /* argument pointer */
1135 va_start(ap_save, format);
1136
1137 retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1138 va_end(ap_save);
1139 return retcode;
1140}
1141
1142int curl_mfprintf(FILE *whereto, const char *format, ...)
1143{
1144 int retcode;
1145 va_list ap_save; /* argument pointer */
1146 va_start(ap_save, format);
1147 retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1148 va_end(ap_save);
1149 return retcode;
1150}
1151
1152int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1153{
1154 int retcode;
1155 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1156 *buffer = 0; /* we terminate this with a zero byte */
1157 return retcode;
1158}
1159
1160int curl_mvprintf(const char *format, va_list ap_save)
1161{
1162 return dprintf_formatf(stdout, fputc, format, ap_save);
1163}
1164
1165int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1166{
1167 return dprintf_formatf(whereto, fputc, format, ap_save);
1168}
1169