Coverage Report

Created: 2026-03-12 06:35

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