Coverage Report

Created: 2026-06-30 08:33

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