Coverage Report

Created: 2026-04-29 07:01

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