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. */
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(input: fmt, end: &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(input: fmt, end: &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(nptr: fmt, endptr: &fmt, base: 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(nptr: fmt-1, endptr: &fmt, base: 10);
375 break;
376 case '*': /* Special case */
377 flags |= FLAGS_WIDTHPARAM;
378 param_num++;
379
380 i = dprintf_DollarString(input: fmt, end: &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
569static 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, arglist: 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(input: f, end: &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
746unsigned_number:
747 /* Unsigned number of base BASE. */
748 is_neg = 0;
749
750number:
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(s: 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(s: 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(buffer: fptr, maxlength: left, format: "%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(buffer: fptr, maxlength: left, format: ".%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)(s: work, maxlen: sizeof(work), format: 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 */
1011static 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
1026int 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(data: &info, stream: 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
1051int 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 */
1062static 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(s: infop->b, mem: &outc, len: 1)) {
1068 infop->fail = 1;
1069 return -1; /* fail */
1070 }
1071 return outc; /* fputc() returns like this on success */
1072}
1073
1074extern 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 */
1078int 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(data: &info, stream: alloc_addbyter, format, ap_save);
1085 if(info.fail) {
1086 Curl_dyn_free(s: info.b);
1087 return 1;
1088 }
1089 return 0;
1090}
1091
1092char *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(s: info.b, DYN_APRINTF);
1098 info.fail = 0;
1099
1100 (void)dprintf_formatf(data: &info, stream: alloc_addbyter, format, ap_save);
1101 if(info.fail) {
1102 Curl_dyn_free(s: info.b);
1103 return NULL;
1104 }
1105 if(Curl_dyn_len(s: info.b))
1106 return Curl_dyn_ptr(s: info.b);
1107 return strdup("");
1108}
1109
1110char *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
1120static 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
1129int 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(data: &buffer, stream: storebuffer, format, ap_save);
1135 va_end(ap_save);
1136 *buffer = 0; /* we terminate this with a zero byte */
1137 return retcode;
1138}
1139
1140int 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, stream: fputc, format, ap_save);
1147 va_end(ap_save);
1148 return retcode;
1149}
1150
1151int 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(data: whereto, stream: fputc, format, ap_save);
1157 va_end(ap_save);
1158 return retcode;
1159}
1160
1161int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1162{
1163 int retcode;
1164 retcode = dprintf_formatf(data: &buffer, stream: storebuffer, format, ap_save);
1165 *buffer = 0; /* we terminate this with a zero byte */
1166 return retcode;
1167}
1168
1169int curl_mvprintf(const char *format, va_list ap_save)
1170{
1171 return dprintf_formatf(stdout, stream: fputc, format, ap_save);
1172}
1173
1174int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1175{
1176 return dprintf_formatf(data: whereto, stream: fputc, format, ap_save);
1177}
1178