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