Coverage Report

Created: 2024-02-25 06:14

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