Coverage Report

Created: 2026-09-14 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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.22M
#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
32
                        fit negative DBL_MAX (317 letters) */
33
7.47M
#define MAX_PARAMETERS 128 /* number of input arguments */
34
5.74M
#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
538
  do {                                    \
44
538
    if(stream((unsigned char)(x), userp)) \
45
538
      return TRUE;                        \
46
538
    (*donep)++;                           \
47
538
  } 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
/* Output bytes are staged here and appended to the dynbuf in chunks: the
130
   dynbuf append overhead (bounds checks, growth, memcpy, null termination)
131
   once per byte dominates formatting. 512 bytes of stack holds a typical
132
   request line or header in a single flush. */
133
#define ASPRINTF_STAGE_SIZE 512
134
135
struct asprintf {
136
  struct dynbuf *b;
137
  char merr;
138
  size_t nstage;
139
  unsigned char stage[ASPRINTF_STAGE_SIZE];
140
};
141
142
/* the provided input number is 1-based but this returns the number 0-based.
143
 *
144
 * returns -1 if no valid number was provided.
145
 */
146
static int dollarstring(const char *p, const char **end)
147
1.81M
{
148
1.81M
  curl_off_t num;
149
1.81M
  if(curlx_str_number(&p, &num, MAX_PARAMETERS) ||
150
1.02M
     curlx_str_single(&p, '$') || !num)
151
1.81M
    return -1;
152
0
  *end = p;
153
0
  return (int)num - 1;
154
1.81M
}
155
156
5.65M
#define is_arg_used(x, y)   ((x)[(y) / 8] & (1 << ((y) & 7)))
157
5.65M
#define mark_arg_used(x, y) ((x)[(y) / 8] |= (unsigned char)(1 << ((y) & 7)))
158
159
/*
160
 * Parse the format string.
161
 *
162
 * Create two arrays. One describes the inputs, one describes the outputs.
163
 *
164
 * Returns zero on success.
165
 */
166
167
7.43M
#define PFMT_OK          0
168
0
#define PFMT_DOLLAR      1 /* bad dollar for main param */
169
0
#define PFMT_DOLLARWIDTH 2 /* bad dollar use for width */
170
0
#define PFMT_DOLLARPREC  3 /* bad dollar use for precision */
171
0
#define PFMT_MANYARGS    4 /* too many input arguments used */
172
0
#define PFMT_PREC        5 /* precision overflow */
173
0
#define PFMT_PRECMIX     6 /* bad mix of precision specifiers */
174
0
#define PFMT_WIDTH       7 /* width overflow */
175
0
#define PFMT_INPUTGAP    8 /* gap in arguments */
176
0
#define PFMT_WIDTHARG    9 /* attempted to use same arg twice, for width */
177
0
#define PFMT_PRECARG    10 /* attempted to use same arg twice, for prec */
178
0
#define PFMT_MANYSEGS   11 /* maxed out output segments */
179
180
static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar,
181
                       int *precp, int *widthp)
182
5.61M
{
183
5.61M
  const char *fmt = *fmtp;
184
5.61M
  bool loopit = TRUE;
185
5.61M
  unsigned int flags = 0;
186
5.61M
  int width = 0;
187
5.61M
  int precision = 0;
188
189
  /* Handle the flags */
190
6.69M
  do {
191
6.69M
    switch(*fmt++) {
192
0
    case ' ':
193
0
      flags |= FLAGS_SPACE;
194
0
      break;
195
0
    case '+':
196
0
      flags |= FLAGS_SHOWSIGN;
197
0
      break;
198
0
    case '-':
199
0
      flags |= FLAGS_LEFT;
200
0
      flags &= ~(unsigned int)FLAGS_PAD_NIL;
201
0
      break;
202
0
    case '#':
203
0
      flags |= FLAGS_ALT;
204
0
      break;
205
39.4k
    case '.':
206
39.4k
      if('*' == *fmt) {
207
        /* The precision is picked from a specified parameter */
208
39.4k
        flags |= FLAGS_PRECPARAM;
209
39.4k
        fmt++;
210
211
39.4k
        if(use_dollar == DOLLAR_USE) {
212
0
          precision = dollarstring(fmt, &fmt);
213
0
          if(precision < 0)
214
            /* illegal combo */
215
0
            return PFMT_DOLLARPREC;
216
0
        }
217
39.4k
        else
218
          /* get it from the next argument */
219
39.4k
          precision = -1;
220
39.4k
      }
221
0
      else {
222
0
        bool is_neg;
223
0
        curl_off_t num;
224
0
        flags |= FLAGS_PREC;
225
0
        is_neg = ('-' == *fmt);
226
0
        if(is_neg)
227
0
          fmt++;
228
0
        if(curlx_str_number(&fmt, &num, INT_MAX))
229
0
          return PFMT_PREC;
230
0
        precision = (int)num;
231
0
        if(is_neg)
232
0
          precision = -precision;
233
0
      }
234
39.4k
      if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
235
39.4k
         (FLAGS_PREC | FLAGS_PRECPARAM))
236
        /* it is not permitted to use both kinds of precision for the same
237
           argument */
238
0
        return PFMT_PRECMIX;
239
39.4k
      break;
240
39.4k
    case 'h':
241
0
      flags |= FLAGS_SHORT;
242
0
      break;
243
#ifdef _WIN32
244
    case 'I':
245
      /* Non-ANSI integer extensions I32 I64 */
246
      if((fmt[0] == '3') && (fmt[1] == '2')) {
247
        flags |= FLAGS_LONG;
248
        fmt += 2;
249
      }
250
      else if((fmt[0] == '6') && (fmt[1] == '4')) {
251
        flags |= FLAGS_LONGLONG;
252
        fmt += 2;
253
      }
254
      else {
255
#if SIZEOF_CURL_OFF_T > SIZEOF_LONG
256
        flags |= FLAGS_LONGLONG;
257
#else
258
        flags |= FLAGS_LONG;
259
#endif
260
      }
261
      break;
262
#endif /* _WIN32 */
263
15.9k
    case 'l':
264
15.9k
      if(flags & FLAGS_LONG)
265
0
        flags |= FLAGS_LONGLONG;
266
15.9k
      else
267
15.9k
        flags |= FLAGS_LONG;
268
15.9k
      break;
269
0
    case 'L':
270
0
      flags |= FLAGS_LONGDOUBLE;
271
0
      break;
272
0
    case 'q':
273
0
      flags |= FLAGS_LONGLONG;
274
0
      break;
275
391
    case 'z':
276
      /* the code below generates a warning if -Wunreachable-code is
277
         used */
278
#if SIZEOF_SIZE_T > SIZEOF_LONG
279
      flags |= FLAGS_LONGLONG;
280
#else
281
391
      flags |= FLAGS_LONG;
282
391
#endif
283
391
      break;
284
0
    case 'O':
285
#if SIZEOF_CURL_OFF_T > SIZEOF_LONG
286
      flags |= FLAGS_LONGLONG;
287
#else
288
0
      flags |= FLAGS_LONG;
289
0
#endif
290
0
      break;
291
1.01M
    case '0':
292
1.01M
      if(!(flags & FLAGS_LEFT))
293
1.01M
        flags |= FLAGS_PAD_NIL;
294
1.01M
      FALLTHROUGH();
295
1.01M
    case '1':
296
1.02M
    case '2':
297
1.02M
    case '3':
298
1.02M
    case '4':
299
1.02M
    case '5':
300
1.03M
    case '6':
301
1.03M
    case '7':
302
1.03M
    case '8':
303
1.03M
    case '9': {
304
1.03M
      curl_off_t num;
305
1.03M
      flags |= FLAGS_WIDTH;
306
1.03M
      fmt--;
307
1.03M
      if(curlx_str_number(&fmt, &num, INT_MAX))
308
0
        return PFMT_WIDTH;
309
1.03M
      width = (int)num;
310
1.03M
      break;
311
1.03M
    }
312
0
    case '*':  /* read width from argument list */
313
0
      flags |= FLAGS_WIDTHPARAM;
314
0
      if(use_dollar == DOLLAR_USE) {
315
0
        width = dollarstring(fmt, &fmt);
316
0
        if(width < 0)
317
          /* illegal combo */
318
0
          return PFMT_DOLLARWIDTH;
319
0
      }
320
0
      else
321
        /* pick from the next argument */
322
0
        width = -1;
323
0
      break;
324
5.61M
    default:
325
5.61M
      loopit = FALSE;
326
5.61M
      fmt--;
327
5.61M
      break;
328
6.69M
    } /* switch */
329
6.69M
  } while(loopit); /* do */
330
5.61M
  *flagsp = flags;
331
5.61M
  *precp = precision;
332
5.61M
  *widthp = width;
333
5.61M
  *fmtp = fmt;
334
5.61M
  return PFMT_OK;
335
5.61M
}
336
337
static bool parse_conversion(const char f, unsigned int *flagp,
338
                             FormatType *typep)
339
5.61M
{
340
5.61M
  unsigned int flags = *flagp;
341
5.61M
  FormatType type;
342
5.61M
  switch(f) {
343
0
  case 'S':
344
0
    flags |= FLAGS_ALT;
345
0
    type = MTYPE_STRING;
346
0
    break;
347
4.38M
  case 's':
348
4.38M
    type = MTYPE_STRING;
349
4.38M
    break;
350
0
  case 'n':
351
0
    type = MTYPE_INTPTR;
352
0
    break;
353
0
  case 'p':
354
0
    type = MTYPE_PTR;
355
0
    break;
356
110k
  case 'd':
357
110k
  case 'i':
358
110k
    if(flags & FLAGS_LONGLONG)
359
0
      type = MTYPE_LONGLONG;
360
110k
    else if(flags & FLAGS_LONG)
361
15.9k
      type = MTYPE_LONG;
362
94.9k
    else
363
94.9k
      type = MTYPE_INT;
364
110k
    break;
365
95.6k
  case 'u':
366
95.6k
    if(flags & FLAGS_LONGLONG)
367
0
      type = MTYPE_LONGLONGU;
368
95.6k
    else if(flags & FLAGS_LONG)
369
0
      type = MTYPE_LONGU;
370
95.6k
    else
371
95.6k
      type = MTYPE_INTU;
372
95.6k
    flags |= FLAGS_UNSIGNED;
373
95.6k
    break;
374
0
  case 'o':
375
0
    if(flags & FLAGS_LONGLONG)
376
0
      type = MTYPE_LONGLONGU;
377
0
    else if(flags & FLAGS_LONG)
378
0
      type = MTYPE_LONGU;
379
0
    else
380
0
      type = MTYPE_INTU;
381
0
    flags |= FLAGS_OCTAL | FLAGS_UNSIGNED;
382
0
    break;
383
394
  case 'x':
384
394
    if(flags & FLAGS_LONGLONG)
385
0
      type = MTYPE_LONGLONGU;
386
394
    else if(flags & FLAGS_LONG)
387
391
      type = MTYPE_LONGU;
388
3
    else
389
3
      type = MTYPE_INTU;
390
394
    flags |= FLAGS_HEX | FLAGS_UNSIGNED;
391
394
    break;
392
1.01M
  case 'X':
393
1.01M
    if(flags & FLAGS_LONGLONG)
394
0
      type = MTYPE_LONGLONGU;
395
1.01M
    else if(flags & FLAGS_LONG)
396
0
      type = MTYPE_LONGU;
397
1.01M
    else
398
1.01M
      type = MTYPE_INTU;
399
1.01M
    flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED;
400
1.01M
    break;
401
530
  case 'c':
402
530
    type = MTYPE_INT;
403
530
    flags |= FLAGS_CHAR;
404
530
    break;
405
0
  case 'f':
406
0
    type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE;
407
0
    break;
408
0
  case 'F':
409
0
    type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE;
410
0
    flags |= FLAGS_UPPER;
411
0
    break;
412
0
  case 'e':
413
0
    type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE;
414
0
    flags |= FLAGS_FLOATE;
415
0
    break;
416
0
  case 'E':
417
0
    type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE;
418
0
    flags |= FLAGS_FLOATE | FLAGS_UPPER;
419
0
    break;
420
0
  case 'g':
421
0
    type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE;
422
0
    flags |= FLAGS_FLOATG;
423
0
    break;
424
0
  case 'G':
425
0
    type = flags & FLAGS_LONGDOUBLE ? MTYPE_LONGDOUBLE : MTYPE_DOUBLE;
426
0
    flags |= FLAGS_FLOATG | FLAGS_UPPER;
427
0
    break;
428
0
  default:
429
    /* invalid instruction, disregard and continue */
430
0
    return TRUE;
431
5.61M
  } /* switch */
432
433
5.61M
  *flagp |= flags;
434
5.61M
  *typep = type;
435
5.61M
  return FALSE;
436
5.61M
}
437
438
static int parsefmt(const char *format,
439
                    struct outsegment *out,
440
                    struct va_input *in,
441
                    int *opieces,
442
                    int *ipieces, va_list arglist)
443
1.82M
{
444
1.82M
  const char *fmt = format;
445
1.82M
  int param_num = 0;
446
1.82M
  int max_param = -1;
447
1.82M
  int i;
448
1.82M
  int ocount = 0;
449
1.82M
  unsigned char usedinput[MAX_PARAMETERS / 8];
450
1.82M
  size_t outlen = 0;
451
1.82M
  struct outsegment *optr;
452
1.82M
  int use_dollar = DOLLAR_UNKNOWN;
453
1.82M
  const char *start = fmt;
454
455
  /* clear, set a bit for each used input */
456
1.82M
  memset(usedinput, 0, sizeof(usedinput));
457
458
20.6M
  while(*fmt) {
459
18.8M
    if(*fmt == '%') {
460
6.63M
      struct va_input *iptr;
461
6.63M
      FormatType type;
462
6.63M
      unsigned int flags = 0;
463
6.63M
      int width = 0;
464
6.63M
      int precision = 0;
465
6.63M
      int param = -1;
466
6.63M
      int rc;
467
6.63M
      fmt++;
468
6.63M
      outlen = (size_t)(fmt - start - 1);
469
6.63M
      if(*fmt == '%') {
470
        /* this means a %% that should be output only as %. Create an output
471
           segment. */
472
1.02M
        if(outlen) {
473
783
          optr = &out[ocount++];
474
783
          if(ocount > MAX_SEGMENTS)
475
0
            return PFMT_MANYSEGS;
476
783
          optr->input = 0;
477
783
          optr->flags = FLAGS_SUBSTR;
478
783
          optr->start = start;
479
783
          optr->outlen = outlen;
480
783
        }
481
1.02M
        start = fmt;
482
1.02M
        fmt++;
483
1.02M
        continue; /* while */
484
1.02M
      }
485
486
5.61M
      if(use_dollar != DOLLAR_NOPE) {
487
1.81M
        param = dollarstring(fmt, &fmt);
488
1.81M
        if(param < 0) {
489
1.81M
          if(use_dollar == DOLLAR_USE)
490
            /* illegal combo */
491
0
            return PFMT_DOLLAR;
492
493
          /* we got no positional, get the next arg */
494
1.81M
          param = -1;
495
1.81M
          use_dollar = DOLLAR_NOPE;
496
1.81M
        }
497
0
        else
498
0
          use_dollar = DOLLAR_USE;
499
1.81M
      }
500
501
5.61M
      rc = parse_flags(&fmt, &flags, use_dollar, &precision, &width);
502
5.61M
      if(rc)
503
0
        return rc;
504
505
5.61M
      if(parse_conversion(*fmt, &flags, &type))
506
0
        continue;
507
508
5.61M
      if(flags & FLAGS_WIDTHPARAM) {
509
0
        if(width < 0)
510
0
          width = param_num++;
511
0
        else {
512
          /* if this identifies a parameter already used, this is illegal */
513
0
          if(is_arg_used(usedinput, width))
514
0
            return PFMT_WIDTHARG;
515
0
        }
516
0
        if(width >= MAX_PARAMETERS)
517
0
          return PFMT_MANYARGS;
518
0
        if(width >= max_param)
519
0
          max_param = width;
520
521
0
        in[width].type = MTYPE_WIDTH;
522
        /* mark as used */
523
0
        mark_arg_used(usedinput, width);
524
0
      }
525
526
5.61M
      if(flags & FLAGS_PRECPARAM) {
527
39.4k
        if(precision < 0)
528
39.4k
          precision = param_num++;
529
0
        else {
530
          /* if this identifies a parameter already used, this is illegal */
531
0
          if(is_arg_used(usedinput, precision))
532
0
            return PFMT_PRECARG;
533
0
        }
534
39.4k
        if(precision >= MAX_PARAMETERS)
535
0
          return PFMT_MANYARGS;
536
39.4k
        if(precision >= max_param)
537
39.4k
          max_param = precision;
538
539
39.4k
        in[precision].type = MTYPE_PRECISION;
540
39.4k
        mark_arg_used(usedinput, precision);
541
39.4k
      }
542
543
      /* Handle the specifier */
544
5.61M
      if(param < 0)
545
5.61M
        param = param_num++;
546
5.61M
      if(param >= MAX_PARAMETERS)
547
0
        return PFMT_MANYARGS;
548
5.61M
      if(param >= max_param)
549
5.61M
        max_param = param;
550
551
5.61M
      iptr = &in[param];
552
5.61M
      iptr->type = type;
553
554
      /* mark this input as used */
555
5.61M
      mark_arg_used(usedinput, param);
556
557
5.61M
      fmt++;
558
5.61M
      optr = &out[ocount++];
559
5.61M
      if(ocount > MAX_SEGMENTS)
560
0
        return PFMT_MANYSEGS;
561
5.61M
      optr->input = (unsigned int)param;
562
5.61M
      optr->flags = flags;
563
5.61M
      optr->width = width;
564
5.61M
      optr->precision = precision;
565
5.61M
      optr->start = start;
566
5.61M
      optr->outlen = outlen;
567
5.61M
      start = fmt;
568
5.61M
    }
569
12.1M
    else
570
12.1M
      fmt++;
571
18.8M
  }
572
573
  /* is there a trailing piece */
574
1.82M
  outlen = (size_t)(fmt - start);
575
1.82M
  if(outlen) {
576
132k
    optr = &out[ocount++];
577
132k
    if(ocount > MAX_SEGMENTS)
578
0
      return PFMT_MANYSEGS;
579
132k
    optr->input = 0;
580
132k
    optr->flags = FLAGS_SUBSTR;
581
132k
    optr->start = start;
582
132k
    optr->outlen = outlen;
583
132k
  }
584
585
  /* Read the arg list parameters into our data list */
586
7.47M
  for(i = 0; i < max_param + 1; i++) {
587
5.65M
    struct va_input *iptr = &in[i];
588
5.65M
    if(!is_arg_used(usedinput, i))
589
      /* bad input */
590
0
      return PFMT_INPUTGAP;
591
592
    /* based on the type, read the correct argument */
593
5.65M
    switch(iptr->type) {
594
4.38M
    case MTYPE_STRING:
595
4.38M
      iptr->val.str = va_arg(arglist, const char *);
596
4.38M
      break;
597
598
0
    case MTYPE_INTPTR:
599
0
    case MTYPE_PTR:
600
0
      iptr->val.ptr = va_arg(arglist, void *);
601
0
      break;
602
603
0
    case MTYPE_LONGLONGU:
604
0
      iptr->val.numu = va_arg(arglist, uint64_t);
605
0
      break;
606
607
0
    case MTYPE_LONGLONG:
608
0
      iptr->val.nums = va_arg(arglist, int64_t);
609
0
      break;
610
611
391
    case MTYPE_LONGU:
612
391
      iptr->val.numu = va_arg(arglist, unsigned long);
613
391
      break;
614
615
15.9k
    case MTYPE_LONG:
616
15.9k
      iptr->val.nums = va_arg(arglist, long);
617
15.9k
      break;
618
619
1.11M
    case MTYPE_INTU:
620
1.11M
      iptr->val.numu = va_arg(arglist, unsigned int);
621
1.11M
      break;
622
623
95.4k
    case MTYPE_INT:
624
95.4k
    case MTYPE_WIDTH:
625
134k
    case MTYPE_PRECISION:
626
134k
      iptr->val.nums = va_arg(arglist, int);
627
134k
      break;
628
629
0
    case MTYPE_DOUBLE:
630
0
      iptr->val.dnum = va_arg(arglist, double);
631
0
      break;
632
633
0
    case MTYPE_LONGDOUBLE:
634
0
      iptr->val.dnum = (double)va_arg(arglist, long double);
635
0
      break;
636
637
0
    default:
638
0
      DEBUGASSERT(NULL); /* unexpected */
639
0
      break;
640
5.65M
    }
641
5.65M
  }
642
1.82M
  *ipieces = max_param + 1;
643
1.82M
  *opieces = ocount;
644
645
1.82M
  return PFMT_OK;
646
1.82M
}
647
648
struct mproperty {
649
  int width;            /* Width of a field. */
650
  int prec;             /* Precision of a field. */
651
  unsigned int flags;
652
};
653
654
/* block output callback: consume up to 'len' bytes, return the number of
655
   bytes accepted. Accepting fewer than 'len' bytes aborts formatting, like
656
   the byte callback returning failure. May be NULL, in which case output
657
   falls back to the per-byte 'stream' callback. */
658
typedef size_t (*mp_streamn)(const unsigned char *buf, size_t len, void *f);
659
660
/* commit any output the sink is holding. Returns non-zero on failure. Sinks
661
   whose accepted bytes are already committed do not need one. */
662
typedef int (*mp_flush)(void *f);
663
664
/* emit a run of bytes through the block callback when available, else byte
665
   by byte. Returns TRUE to abort formatting (mirroring OUTCHAR). */
666
static bool stream_run(void *userp,
667
                       int (*stream)(unsigned char, void *),
668
                       mp_streamn streamn,
669
                       const unsigned char *buf, size_t len, int *donep)
670
7.58M
{
671
7.58M
  if(!len)
672
3.48M
    return FALSE;
673
4.10M
  if(streamn) {
674
4.10M
    size_t n = streamn(buf, len, userp);
675
4.10M
    *donep += (int)n;
676
4.10M
    return n != len;
677
4.10M
  }
678
0
  while(len--) {
679
0
    if(stream(*buf++, userp))
680
0
      return TRUE;
681
0
    (*donep)++;
682
0
  }
683
0
  return FALSE;
684
0
}
685
686
/* Padding is always spaces or zeros, so it comes from a constant rather than a
687
   buffer that has to be filled first. 16 keeps every width used in the tree to
688
   a single run; wider padding loops. */
689
static const char pad_spaces[] = "                ";
690
static const char pad_zeros[] = "0000000000000000";
691
10.9k
#define PAD_RUN 16 /* strlen() of both of the above */
692
693
/* emit 'num' copies of the pad byte. Returns TRUE to abort formatting. */
694
static bool stream_pad(void *userp,
695
                       int (*stream)(unsigned char, void *),
696
                       mp_streamn streamn,
697
                       unsigned char pad, int num, int *donep)
698
5.61M
{
699
5.61M
  const unsigned char *src;
700
5.61M
  if(num <= 0)
701
5.60M
    return FALSE;
702
10.9k
  if(pad == '0')
703
1.52k
    src = (const unsigned char *)pad_zeros;
704
9.46k
  else
705
9.46k
    src = (const unsigned char *)pad_spaces;
706
10.9k
  while(num > PAD_RUN) {
707
0
    if(stream_run(userp, stream, streamn, src, PAD_RUN, donep))
708
0
      return TRUE;
709
0
    num -= PAD_RUN;
710
0
  }
711
10.9k
  return stream_run(userp, stream, streamn, src, (size_t)num, donep);
712
10.9k
}
713
714
/* emit the leading NUL-terminated part of a run of at most 'len' bytes.
715
   Returns TRUE to abort formatting.
716
717
   A byte loop and not memchr(): 'len' is the requested precision, which
718
   out_string() does not clamp to the string length, so "%.64s" on a 10-byte
719
   string arrives with len == 64 and memchr() would read past the object. */
720
static bool stream_zrun(void *userp,
721
                        int (*stream)(unsigned char, void *),
722
                        mp_streamn streamn,
723
                        const char *str, size_t len, int *donep)
724
6.34M
{
725
6.34M
  size_t n = 0;
726
282M
  while(n < len && str[n])
727
276M
    n++;
728
6.34M
  return stream_run(userp, stream, streamn,
729
6.34M
                    (const unsigned char *)str, n, donep);
730
6.34M
}
731
732
static bool out_double(void *userp,
733
                       int (*stream)(unsigned char, void *),
734
                       mp_streamn streamn,
735
                       struct mproperty *p,
736
                       double dnum,
737
                       char *work, int *donep)
738
0
{
739
0
  char fmt[32] = "%";
740
0
  char *fptr = &fmt[1];
741
0
  size_t left = sizeof(fmt) - strlen(fmt);
742
0
  int flags = p->flags;
743
0
  int width = p->width;
744
0
  int prec = p->prec;
745
746
0
  if(flags & FLAGS_LEFT)
747
0
    *fptr++ = '-';
748
0
  if(flags & FLAGS_SHOWSIGN)
749
0
    *fptr++ = '+';
750
0
  if(flags & FLAGS_SPACE)
751
0
    *fptr++ = ' ';
752
0
  if(flags & FLAGS_ALT)
753
0
    *fptr++ = '#';
754
755
0
  *fptr = 0;
756
757
0
  if(width >= 0) {
758
0
    size_t dlen;
759
0
    if(width >= BUFFSIZE)
760
0
      width = BUFFSIZE - 1;
761
    /* RECURSIVE USAGE */
762
0
    dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
763
0
    fptr += dlen;
764
0
    left -= dlen;
765
0
  }
766
0
  if(prec >= 0) {
767
    /* for each digit in the integer part, we can have one less
768
       precision */
769
0
    int maxprec = BUFFSIZE - 1;
770
0
    double val = dnum;
771
0
    int len;
772
0
    if(prec > maxprec)
773
0
      prec = maxprec - 1;
774
0
    if(width > 0 && prec <= width)
775
0
      maxprec -= width;
776
0
    while(maxprec && (val >= 10.0)) {
777
0
      val /= 10;
778
0
      maxprec--;
779
0
    }
780
781
0
    if(prec > maxprec)
782
0
      prec = maxprec - 1;
783
0
    if(prec < 0)
784
0
      prec = 0;
785
    /* RECURSIVE USAGE */
786
0
    len = curl_msnprintf(fptr, left, ".%d", prec);
787
0
    fptr += len;
788
0
  }
789
0
  if(flags & FLAGS_LONG)
790
0
    *fptr++ = 'l';
791
792
0
  if(flags & FLAGS_FLOATE)
793
0
    *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e');
794
0
  else if(flags & FLAGS_FLOATG)
795
0
    *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
796
0
  else
797
0
    *fptr++ = (flags & FLAGS_UPPER) ? 'F' : 'f';
798
799
0
  *fptr = 0; /* and a final null-termination */
800
801
  /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
802
     output characters */
803
0
#ifdef CURL_HAVE_DIAG
804
0
#pragma GCC diagnostic push
805
0
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
806
0
#endif
807
#ifdef _WIN32
808
  curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum);
809
#else
810
  /* !checksrc! disable BANNEDFUNC 2 */
811
0
  snprintf(work, BUFFSIZE, fmt, dnum);
812
0
#endif
813
0
#ifdef CURL_HAVE_DIAG
814
0
#pragma GCC diagnostic pop
815
0
#endif
816
0
  DEBUGASSERT(strlen(work) < BUFFSIZE);
817
0
  return stream_run(userp, stream, streamn,
818
0
                    (const unsigned char *)work, strlen(work), donep);
819
0
}
820
821
static bool out_number(void *userp,
822
                       int (*stream)(unsigned char, void *),
823
                       mp_streamn streamn,
824
                       struct mproperty *p,
825
                       uint64_t num,
826
                       int64_t nums,
827
                       char *work, int *donep)
828
1.22M
{
829
1.22M
  const unsigned char *digits = Curl_ldigits;
830
1.22M
  int flags = p->flags;
831
1.22M
  int width = p->width;
832
1.22M
  int prec = p->prec;
833
1.22M
  bool is_alt = flags & FLAGS_ALT;
834
1.22M
  bool is_neg = FALSE;
835
1.22M
  int base = 10;
836
837
  /* 'workend' points to the final buffer byte position, but with an extra
838
     byte as margin to avoid the (FALSE?) warning Coverity gives us
839
     otherwise */
840
1.22M
  char *workend = &work[BUFFSIZE - 2];
841
1.22M
  char *w;
842
843
1.22M
  if(flags & FLAGS_CHAR) {
844
    /* Character. */
845
530
    if(!(flags & FLAGS_LEFT)) {
846
530
      if(stream_pad(userp, stream, streamn, ' ', width - 1, donep))
847
0
        return TRUE;
848
530
      width = 1;
849
530
    }
850
530
    OUTCHAR((char)num);
851
530
    if(flags & FLAGS_LEFT) {
852
0
      if(stream_pad(userp, stream, streamn, ' ', width - 1, donep))
853
0
        return TRUE;
854
0
    }
855
530
    return FALSE;
856
530
  }
857
1.22M
  if(flags & FLAGS_OCTAL)
858
    /* Octal unsigned integer */
859
0
    base = 8;
860
861
1.22M
  else if(flags & FLAGS_HEX) {
862
    /* Hexadecimal unsigned integer */
863
1.01M
    digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits;
864
1.01M
    base = 16;
865
1.01M
  }
866
206k
  else if(flags & FLAGS_UNSIGNED)
867
    /* Decimal unsigned integer */
868
95.6k
    ;
869
870
110k
  else {
871
    /* Decimal integer. */
872
110k
    is_neg = (nums < 0);
873
110k
    if(is_neg) {
874
      /* signed_num might fail to hold absolute negative minimum by 1 */
875
8
      int64_t signed_num; /* Used to convert negative in positive. */
876
8
      signed_num = nums + (int64_t)1;
877
8
      signed_num = -signed_num;
878
8
      num = (uint64_t)signed_num;
879
8
      num += (uint64_t)1;
880
8
    }
881
110k
  }
882
883
  /* Supply a default precision if none was given. */
884
1.22M
  if(prec == -1)
885
1.22M
    prec = 1;
886
887
  /* Put the number in WORK. */
888
1.22M
  w = workend;
889
1.22M
  DEBUGASSERT(base <= 16);
890
1.22M
  switch(base) {
891
206k
  case 10:
892
466k
    while(num > 0) {
893
259k
      *w-- = (char)('0' + (num % 10));
894
259k
      num /= 10;
895
259k
    }
896
206k
    break;
897
1.01M
  default:
898
3.05M
    while(num > 0) {
899
2.03M
      *w-- = digits[num % base];
900
2.03M
      num /= base;
901
2.03M
    }
902
1.01M
    break;
903
1.22M
  }
904
1.22M
  width -= (int)(workend - w);
905
1.22M
  prec -= (int)(workend - w);
906
907
1.22M
  if(is_alt && base == 8 && prec <= 0) {
908
0
    *w-- = '0';
909
0
    --width;
910
0
  }
911
912
1.22M
  if(prec > 0) {
913
89.5k
    width -= prec;
914
179k
    while(prec-- > 0 && w >= work)
915
89.5k
      *w-- = '0';
916
89.5k
  }
917
918
1.22M
  if(is_alt && base == 16)
919
0
    width -= 2;
920
921
1.22M
  if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
922
8
    --width;
923
924
1.22M
  if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL)) {
925
206k
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
926
0
      return TRUE;
927
206k
    width = 0;
928
206k
  }
929
930
1.22M
  if(is_neg)
931
8
    OUTCHAR('-');
932
1.22M
  else if(flags & FLAGS_SHOWSIGN)
933
0
    OUTCHAR('+');
934
1.22M
  else if(flags & FLAGS_SPACE)
935
0
    OUTCHAR(' ');
936
937
1.22M
  if(is_alt && base == 16) {
938
0
    OUTCHAR('0');
939
0
    if(flags & FLAGS_UPPER)
940
0
      OUTCHAR('X');
941
0
    else
942
0
      OUTCHAR('x');
943
0
  }
944
945
1.22M
  if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL)) {
946
1.01M
    if(stream_pad(userp, stream, streamn, '0', width, donep))
947
0
      return TRUE;
948
1.01M
    width = 0;
949
1.01M
  }
950
951
  /* Write the number. */
952
1.22M
  if(stream_run(userp, stream, streamn, (const unsigned char *)w + 1,
953
1.22M
                (size_t)(workend - w), donep))
954
2
    return TRUE;
955
956
1.22M
  if(flags & FLAGS_LEFT) {
957
0
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
958
0
      return TRUE;
959
0
  }
960
961
1.22M
  return FALSE;
962
1.22M
}
963
964
static const char nilstr[] = "(nil)";
965
966
static bool out_string(void *userp,
967
                       int (*stream)(unsigned char, void *),
968
                       mp_streamn streamn,
969
                       struct mproperty *p,
970
                       const char *str,
971
                       int *donep)
972
4.38M
{
973
4.38M
  int flags = p->flags;
974
4.38M
  int width = p->width;
975
4.38M
  int prec = p->prec;
976
4.38M
  size_t len;
977
978
4.38M
  if(!str) {
979
    /* Write null string if there is space. */
980
1.45k
    if(prec == -1 || prec >= (int)CURL_CSTRLEN(nilstr)) {
981
1.45k
      str = nilstr;
982
1.45k
      len = CURL_CSTRLEN(nilstr);
983
      /* Disable quotes around (nil) */
984
1.45k
      flags &= ~(unsigned int)FLAGS_ALT;
985
1.45k
    }
986
0
    else {
987
0
      str = "";
988
0
      len = 0;
989
0
    }
990
1.45k
  }
991
4.38M
  else if(prec != -1)
992
39.4k
    len = (size_t)prec;
993
4.34M
  else if(*str == '\0')
994
3.47M
    len = 0;
995
868k
  else
996
868k
    len = strlen(str);
997
998
4.38M
  width -= (len > INT_MAX) ? INT_MAX : (int)len;
999
1000
4.38M
  if(flags & FLAGS_ALT)
1001
0
    OUTCHAR('"');
1002
1003
4.38M
  if(!(flags & FLAGS_LEFT)) {
1004
4.38M
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
1005
0
      return TRUE;
1006
4.38M
    width = 0;
1007
4.38M
  }
1008
1009
4.38M
  if(stream_zrun(userp, stream, streamn, str, len, donep))
1010
31
    return TRUE;
1011
4.38M
  if(flags & FLAGS_LEFT) {
1012
0
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
1013
0
      return TRUE;
1014
0
  }
1015
1016
4.38M
  if(flags & FLAGS_ALT)
1017
0
    OUTCHAR('"');
1018
1019
4.38M
  return FALSE;
1020
4.38M
}
1021
1022
static bool out_pointer(void *userp,
1023
                        int (*stream)(unsigned char, void *),
1024
                        mp_streamn streamn,
1025
                        struct mproperty *p,
1026
                        const char *ptr,
1027
                        char *work,
1028
                        int *donep)
1029
0
{
1030
  /* Generic pointer. */
1031
0
  if(ptr) {
1032
0
    size_t num = (size_t)ptr;
1033
1034
    /* If the pointer is not NULL, write it as a %#x spec. */
1035
0
    p->flags |= FLAGS_HEX | FLAGS_ALT;
1036
0
    if(out_number(userp, stream, streamn, p, num, 0, work, donep))
1037
0
      return TRUE;
1038
0
  }
1039
0
  else {
1040
    /* Write "(nil)" for a nil pointer. */
1041
0
    int width = p->width;
1042
0
    int flags = p->flags;
1043
1044
0
    width -= (int)CURL_CSTRLEN(nilstr);
1045
0
    if(flags & FLAGS_LEFT) {
1046
0
      if(stream_pad(userp, stream, streamn, ' ', width, donep))
1047
0
        return TRUE;
1048
0
      width = 0;
1049
0
    }
1050
0
    if(stream_run(userp, stream, streamn, (const unsigned char *)nilstr,
1051
0
                  sizeof(nilstr) - 1, donep))
1052
0
      return TRUE;
1053
0
    if(!(flags & FLAGS_LEFT)) {
1054
0
      if(stream_pad(userp, stream, streamn, ' ', width, donep))
1055
0
        return TRUE;
1056
0
    }
1057
0
  }
1058
0
  return FALSE;
1059
0
}
1060
1061
/*
1062
 * formatf() - the general printf function.
1063
 *
1064
 * It calls parsefmt() to parse the format string. It populates two arrays;
1065
 * one that describes the input arguments and one that describes a number of
1066
 * output segments.
1067
 *
1068
 * On success, the input array describes the type of all arguments and their
1069
 * values.
1070
 *
1071
 * The function then iterates over the output segments and outputs them one
1072
 * by one until done. Using the appropriate input arguments (if any).
1073
 *
1074
 * All output is sent to the 'stream()' callback, one byte at a time.
1075
 */
1076
static int formatf(void *userp, /* untouched by format(), sent to the
1077
                                   stream() function in the second argument */
1078
                   /* function pointer called for each output character */
1079
                   int (*stream)(unsigned char, void *),
1080
                   /* optional block-output function pointer */
1081
                   mp_streamn streamn,
1082
                   /* optional, commits output the sink is holding */
1083
                   mp_flush flush,
1084
                   const char *format, /* %-formatted string */
1085
                   va_list ap_save) /* list of parameters */
1086
1.82M
{
1087
1.82M
  int done = 0;   /* number of characters written */
1088
1.82M
  int i;
1089
1.82M
  int ocount = 0; /* number of output segments */
1090
1.82M
  int icount = 0; /* number of input arguments */
1091
1092
1.82M
  struct outsegment output[MAX_SEGMENTS];
1093
1.82M
  struct va_input input[MAX_PARAMETERS];
1094
1.82M
  char work[BUFFSIZE + 2];
1095
1096
  /* Parse the format string */
1097
1.82M
  if(parsefmt(format, output, input, &ocount, &icount, ap_save))
1098
0
    return 0;
1099
1100
7.56M
  for(i = 0; i < ocount; i++) {
1101
5.74M
    struct outsegment *optr = &output[i];
1102
5.74M
    struct va_input *iptr = &input[optr->input];
1103
5.74M
    struct mproperty p;
1104
5.74M
    size_t outlen = optr->outlen;
1105
1106
5.74M
    if(outlen) {
1107
1.96M
      if(stream_zrun(userp, stream, streamn, optr->start, outlen, &done))
1108
4
        return done;
1109
1.96M
      if(optr->flags & FLAGS_SUBSTR)
1110
        /* this is a substring */
1111
132k
        continue;
1112
1.96M
    }
1113
1114
5.61M
    p.flags = optr->flags;
1115
1116
    /* pick up the specified width */
1117
5.61M
    if(p.flags & FLAGS_WIDTHPARAM) {
1118
0
      p.width = (int)input[optr->width].val.nums;
1119
0
      if(p.width < 0) {
1120
        /* "A negative field width is taken as a '-' flag followed by a
1121
           positive field width." */
1122
0
        if(p.width == INT_MIN)
1123
0
          p.width = INT_MAX;
1124
0
        else
1125
0
          p.width = -p.width;
1126
0
        p.flags |= FLAGS_LEFT;
1127
0
        p.flags &= ~(unsigned int)FLAGS_PAD_NIL;
1128
0
      }
1129
0
    }
1130
5.61M
    else
1131
5.61M
      p.width = optr->width;
1132
1133
    /* pick up the specified precision */
1134
5.61M
    if(p.flags & FLAGS_PRECPARAM) {
1135
39.4k
      p.prec = (int)input[optr->precision].val.nums;
1136
39.4k
      if(p.prec < 0)
1137
        /* "A negative precision is taken as if the precision were
1138
           omitted." */
1139
0
        p.prec = -1;
1140
39.4k
    }
1141
5.57M
    else if(p.flags & FLAGS_PREC)
1142
0
      p.prec = optr->precision;
1143
5.57M
    else
1144
5.57M
      p.prec = -1;
1145
1146
5.61M
    switch(iptr->type) {
1147
1.11M
    case MTYPE_INTU:
1148
1.11M
    case MTYPE_LONGU:
1149
1.11M
    case MTYPE_LONGLONGU:
1150
1.11M
      p.flags |= FLAGS_UNSIGNED;
1151
1.11M
      if(out_number(userp, stream, streamn, &p, iptr->val.numu, 0, work,
1152
1.11M
                    &done))
1153
1
        return done;
1154
1.11M
      break;
1155
1156
1.11M
    case MTYPE_INT:
1157
111k
    case MTYPE_LONG:
1158
111k
    case MTYPE_LONGLONG:
1159
111k
      if(out_number(userp, stream, streamn, &p, iptr->val.numu,
1160
111k
                    iptr->val.nums, work, &done))
1161
1
        return done;
1162
111k
      break;
1163
1164
4.38M
    case MTYPE_STRING:
1165
4.38M
      if(out_string(userp, stream, streamn, &p, iptr->val.str, &done))
1166
31
        return done;
1167
4.38M
      break;
1168
1169
4.38M
    case MTYPE_PTR:
1170
0
      if(out_pointer(userp, stream, streamn, &p, iptr->val.ptr, work,
1171
0
                     &done))
1172
0
        return done;
1173
0
      break;
1174
1175
0
    case MTYPE_DOUBLE:
1176
0
    case MTYPE_LONGDOUBLE:
1177
0
      if(out_double(userp, stream, streamn, &p, iptr->val.dnum, work,
1178
0
                    &done))
1179
0
        return done;
1180
0
      break;
1181
1182
0
    case MTYPE_INTPTR:
1183
      /* %n is observable by the caller, so commit what is pending first and
1184
         perform it only if that succeeded, as a sink that never holds output
1185
         would. */
1186
0
      if(flush && flush(userp))
1187
0
        return done;
1188
      /* Answer the count of characters written. */
1189
0
      if(p.flags & FLAGS_LONGLONG)
1190
0
        *(int64_t *)iptr->val.ptr = (int64_t)done;
1191
0
      else if(p.flags & FLAGS_LONG)
1192
0
        *(long *)iptr->val.ptr = (long)done;
1193
0
      else if(!(p.flags & FLAGS_SHORT))
1194
0
        *(int *)iptr->val.ptr = done;
1195
0
      else
1196
0
        *(short *)iptr->val.ptr = (short)done;
1197
0
      break;
1198
1199
0
    default:
1200
0
      break;
1201
5.61M
    }
1202
5.61M
  }
1203
1.82M
  return done;
1204
1.82M
}
1205
1206
/* fputc() look-alike */
1207
static int addbyter(unsigned char outc, void *f)
1208
530
{
1209
530
  struct nsprintf *infop = f;
1210
530
  if(infop->length < infop->max) {
1211
    /* only do this if we have not reached max length yet */
1212
530
    *infop->buffer++ = (char)outc; /* store */
1213
530
    infop->length++; /* we are now one byte larger */
1214
530
    return 0;     /* fputc() returns like this on success */
1215
530
  }
1216
0
  return 1;
1217
530
}
1218
1219
/* block variant of addbyter */
1220
static size_t addrun(const unsigned char *buf, size_t len, void *f)
1221
267k
{
1222
267k
  struct nsprintf *infop = f;
1223
267k
  size_t fit = infop->max - infop->length;
1224
267k
  if(len > fit)
1225
5
    len = fit;
1226
267k
  if(len) {
1227
267k
    memcpy(infop->buffer, buf, len);
1228
267k
    infop->buffer += len;
1229
267k
    infop->length += len;
1230
267k
  }
1231
267k
  return len;
1232
267k
}
1233
1234
int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1235
                    va_list args)
1236
72.3k
{
1237
72.3k
  int retcode;
1238
72.3k
  struct nsprintf info;
1239
1240
72.3k
  info.buffer = buffer;
1241
72.3k
  info.length = 0;
1242
72.3k
  info.max = maxlength;
1243
1244
72.3k
  retcode = formatf(&info, addbyter, addrun, NULL, format, args);
1245
72.3k
  if(info.max) {
1246
    /* we terminate this with a zero byte */
1247
72.3k
    if(info.max == info.length) {
1248
      /* we are at maximum, scrap the last letter */
1249
5
      info.buffer[-1] = 0;
1250
5
      DEBUGASSERT(retcode);
1251
5
      retcode--; /* do not count the nul byte */
1252
5
    }
1253
72.3k
    else
1254
72.3k
      info.buffer[0] = 0;
1255
72.3k
  }
1256
72.3k
  return retcode;
1257
72.3k
}
1258
1259
int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1260
72.3k
{
1261
72.3k
  int retcode;
1262
72.3k
  va_list args; /* argument pointer */
1263
72.3k
  va_start(args, format);
1264
72.3k
  retcode = curl_mvsnprintf(buffer, maxlength, format, args);
1265
72.3k
  va_end(args);
1266
72.3k
  return retcode;
1267
72.3k
}
1268
1269
/* append the staged bytes to the dynbuf. Returns 1 on failure (and sets
1270
   merr), 0 on success. */
1271
static int alloc_flush(void *f)
1272
2.23M
{
1273
2.23M
  struct asprintf *infop = f;
1274
2.23M
  if(infop->nstage) {
1275
2.23M
    CURLcode result = curlx_dyn_addn(infop->b, infop->stage, infop->nstage);
1276
2.23M
    infop->nstage = 0;
1277
2.23M
    if(result) {
1278
0
      infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
1279
0
      return 1; /* fail */
1280
0
    }
1281
2.23M
  }
1282
2.23M
  return 0;
1283
2.23M
}
1284
1285
/* append directly, bypassing the stage. Returns 1 on failure. */
1286
static int alloc_addn(struct asprintf *infop, const unsigned char *buf,
1287
                      size_t len)
1288
32
{
1289
32
  CURLcode result = curlx_dyn_addn(infop->b, buf, len);
1290
32
  if(result) {
1291
32
    infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
1292
32
    return 1; /* fail */
1293
32
  }
1294
0
  return 0;
1295
32
}
1296
1297
/* TRUE if 'more' additional bytes still fit the dynbuf size limit, counting
1298
   what is already staged. curlx_dyn_addn() rejects an append when
1299
   len + leng + 1 exceeds it. */
1300
static bool alloc_fits(struct asprintf *infop, size_t more)
1301
4.28M
{
1302
4.28M
  return (infop->b->leng + infop->nstage + more + 1) <= infop->b->toobig;
1303
4.28M
}
1304
1305
/* fputc() look-alike */
1306
static int alloc_addbyter(unsigned char outc, void *f)
1307
8
{
1308
8
  struct asprintf *infop = f;
1309
8
  if(infop->nstage == sizeof(infop->stage) && alloc_flush(infop))
1310
0
    return 1; /* fail */
1311
8
  if(!alloc_fits(infop, 1)) {
1312
    /* Does not fit the dynbuf size limit. Flush and append this byte on
1313
       its own, so the limit is reported at exactly this byte and formatting
1314
       stops where it would without staging. %n depends on that. */
1315
0
    if(alloc_flush(infop))
1316
0
      return 1;
1317
0
    return alloc_addn(infop, &outc, 1);
1318
0
  }
1319
8
  infop->stage[infop->nstage++] = outc;
1320
8
  return 0;
1321
8
}
1322
1323
/* block variant of alloc_addbyter */
1324
static size_t alloc_addrun(const unsigned char *buf, size_t len, void *f)
1325
3.80M
{
1326
3.80M
  struct asprintf *infop = f;
1327
3.80M
  size_t accepted = 0;
1328
8.09M
  while(accepted < len) {
1329
4.28M
    size_t room = sizeof(infop->stage) - infop->nstage;
1330
4.28M
    size_t n;
1331
4.28M
    if(!room) {
1332
488k
      if(alloc_flush(infop))
1333
0
        return accepted; /* accepted < len aborts formatting */
1334
488k
      room = sizeof(infop->stage);
1335
488k
    }
1336
4.28M
    n = len - accepted;
1337
4.28M
    if(n > room)
1338
488k
      n = room;
1339
4.28M
    if(!alloc_fits(infop, n)) {
1340
      /* Run does not fit the dynbuf size limit. Stage what does, then
1341
         append the first byte that does not on its own, so the limit is
1342
         reported at exactly that byte. */
1343
32
      size_t fits = infop->b->toobig - infop->b->leng - infop->nstage - 1;
1344
32
      if(fits) {
1345
28
        memcpy(&infop->stage[infop->nstage], buf + accepted, fits);
1346
28
        infop->nstage += fits;
1347
28
        accepted += fits;
1348
28
      }
1349
32
      if(alloc_flush(infop))
1350
0
        return accepted;
1351
32
      if(alloc_addn(infop, buf + accepted, 1))
1352
32
        return accepted; /* accepted < len aborts formatting */
1353
0
      accepted++;
1354
0
      continue;
1355
32
    }
1356
4.28M
    memcpy(&infop->stage[infop->nstage], buf + accepted, n);
1357
4.28M
    infop->nstage += n;
1358
4.28M
    accepted += n;
1359
4.28M
  }
1360
3.80M
  return accepted;
1361
3.80M
}
1362
1363
/* appends the formatted string, returns MERR error code */
1364
int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args)
1365
1.14M
{
1366
1.14M
  struct asprintf info;
1367
1.14M
  info.b = dyn;
1368
1.14M
  info.merr = MERR_OK;
1369
1.14M
  info.nstage = 0;
1370
1371
1.14M
  (void)formatf(&info, alloc_addbyter, alloc_addrun, alloc_flush,
1372
1.14M
                format, args);
1373
1.14M
  if(!info.merr)
1374
1.14M
    (void)alloc_flush(&info);
1375
1.14M
  if(info.merr) {
1376
32
    curlx_dyn_free(info.b);
1377
32
    return info.merr;
1378
32
  }
1379
1.14M
  return 0;
1380
1.14M
}
1381
1382
char *curl_mvaprintf(const char *format, va_list args)
1383
598k
{
1384
598k
  struct asprintf info;
1385
598k
  struct dynbuf dyn;
1386
598k
  info.b = &dyn;
1387
598k
  curlx_dyn_init(info.b, DYN_APRINTF);
1388
598k
  info.merr = MERR_OK;
1389
598k
  info.nstage = 0;
1390
1391
598k
  (void)formatf(&info, alloc_addbyter, alloc_addrun, alloc_flush,
1392
598k
                format, args);
1393
598k
  if(!info.merr)
1394
598k
    (void)alloc_flush(&info);
1395
598k
  if(info.merr) {
1396
0
    curlx_dyn_free(info.b);
1397
0
    return NULL;
1398
0
  }
1399
598k
  if(curlx_dyn_len(info.b))
1400
598k
    return curlx_dyn_ptr(info.b);
1401
0
  return curlx_strdup("");
1402
598k
}
1403
1404
char *curl_maprintf(const char *format, ...)
1405
70.1k
{
1406
70.1k
  va_list args;
1407
70.1k
  char *s;
1408
70.1k
  va_start(args, format);
1409
70.1k
  s = curl_mvaprintf(format, args);
1410
70.1k
  va_end(args);
1411
70.1k
  return s;
1412
70.1k
}
1413
1414
static int storebuffer(unsigned char outc, void *f)
1415
0
{
1416
0
  char **buffer = f;
1417
0
  **buffer = (char)outc;
1418
0
  (*buffer)++;
1419
0
  return 0;
1420
0
}
1421
1422
/* block variant of storebuffer */
1423
static size_t storerun(const unsigned char *buf, size_t len, void *f)
1424
0
{
1425
0
  char **buffer = f;
1426
0
  memcpy(*buffer, buf, len);
1427
0
  *buffer += len;
1428
0
  return len;
1429
0
}
1430
1431
int curl_msprintf(char *buffer, const char *format, ...)
1432
0
{
1433
0
  va_list args; /* argument pointer */
1434
0
  int retcode;
1435
0
  va_start(args, format);
1436
0
  retcode = formatf(&buffer, storebuffer, storerun, NULL, format, args);
1437
0
  va_end(args);
1438
0
  *buffer = 0; /* we terminate this with a zero byte */
1439
0
  return retcode;
1440
0
}
1441
1442
static int fputc_wrapper(unsigned char outc, void *f)
1443
0
{
1444
0
  int out = outc;
1445
0
  FILE *s = f;
1446
0
  int rc = fputc(out, s);
1447
0
  return rc == EOF;
1448
0
}
1449
1450
/* block variant of fputc_wrapper */
1451
static size_t fwrite_wrapper(const unsigned char *buf, size_t len, void *f)
1452
36.6k
{
1453
36.6k
  FILE *s = f;
1454
36.6k
  return fwrite(buf, 1, len, s);
1455
36.6k
}
1456
1457
int curl_mprintf(const char *format, ...)
1458
0
{
1459
0
  int retcode;
1460
0
  va_list args; /* argument pointer */
1461
0
  va_start(args, format);
1462
0
  retcode = formatf(stdout, fputc_wrapper, fwrite_wrapper, NULL, format, args);
1463
0
  va_end(args);
1464
0
  return retcode;
1465
0
}
1466
1467
int curl_mfprintf(FILE *fd, const char *format, ...)
1468
4.23k
{
1469
4.23k
  int retcode;
1470
4.23k
  va_list args; /* argument pointer */
1471
4.23k
  va_start(args, format);
1472
4.23k
  retcode = formatf(fd, fputc_wrapper, fwrite_wrapper, NULL, format, args);
1473
4.23k
  va_end(args);
1474
4.23k
  return retcode;
1475
4.23k
}
1476
1477
int curl_mvsprintf(char *buffer, const char *format, va_list args)
1478
0
{
1479
0
  int retcode = formatf(&buffer, storebuffer, storerun, NULL, format, args);
1480
0
  *buffer = 0; /* we terminate this with a zero byte */
1481
0
  return retcode;
1482
0
}
1483
1484
int curl_mvprintf(const char *format, va_list args)
1485
0
{
1486
0
  return formatf(stdout, fputc_wrapper, fwrite_wrapper, NULL, format, args);
1487
0
}
1488
1489
int curl_mvfprintf(FILE *fd, const char *format, va_list args)
1490
0
{
1491
  return formatf(fd, fputc_wrapper, fwrite_wrapper, NULL, format, args);
1492
0
}