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