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