Coverage Report

Created: 2026-09-14 07:10

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
575k
#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
32
                        fit negative DBL_MAX (317 letters) */
33
2.34M
#define MAX_PARAMETERS 128 /* number of input arguments */
34
1.99M
#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
194
  do {                                    \
44
194
    if(stream((unsigned char)(x), userp)) \
45
194
      return TRUE;                        \
46
194
    (*donep)++;                           \
47
194
  } 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
592k
{
148
592k
  curl_off_t num;
149
592k
  if(curlx_str_number(&p, &num, MAX_PARAMETERS) ||
150
61.5k
     curlx_str_single(&p, '$') || !num)
151
592k
    return -1;
152
0
  *end = p;
153
0
  return (int)num - 1;
154
592k
}
155
156
1.75M
#define is_arg_used(x, y)   ((x)[(y) / 8] & (1 << ((y) & 7)))
157
1.75M
#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
2.30M
#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
1.68M
{
183
1.68M
  const char *fmt = *fmtp;
184
1.68M
  bool loopit = TRUE;
185
1.68M
  unsigned int flags = 0;
186
1.68M
  int width = 0;
187
1.68M
  int precision = 0;
188
189
  /* Handle the flags */
190
2.11M
  do {
191
2.11M
    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
67.5k
    case '.':
206
67.5k
      if('*' == *fmt) {
207
        /* The precision is picked from a specified parameter */
208
67.5k
        flags |= FLAGS_PRECPARAM;
209
67.5k
        fmt++;
210
211
67.5k
        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
67.5k
        else
218
          /* get it from the next argument */
219
67.5k
          precision = -1;
220
67.5k
      }
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
67.5k
      if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
235
67.5k
         (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
67.5k
      break;
240
67.5k
    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
243k
    case 'l':
264
243k
      if(flags & FLAGS_LONG)
265
115
        flags |= FLAGS_LONGLONG;
266
243k
      else
267
243k
        flags |= FLAGS_LONG;
268
243k
      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
57.4k
    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
57.4k
      flags |= FLAGS_LONG;
282
57.4k
#endif
283
57.4k
      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
62.8k
    case '0':
292
62.8k
      if(!(flags & FLAGS_LEFT))
293
62.8k
        flags |= FLAGS_PAD_NIL;
294
62.8k
      FALLTHROUGH();
295
62.8k
    case '1':
296
62.8k
    case '2':
297
62.8k
    case '3':
298
63.1k
    case '4':
299
63.1k
    case '5':
300
63.1k
    case '6':
301
63.1k
    case '7':
302
63.1k
    case '8':
303
63.1k
    case '9': {
304
63.1k
      curl_off_t num;
305
63.1k
      flags |= FLAGS_WIDTH;
306
63.1k
      fmt--;
307
63.1k
      if(curlx_str_number(&fmt, &num, INT_MAX))
308
0
        return PFMT_WIDTH;
309
63.1k
      width = (int)num;
310
63.1k
      break;
311
63.1k
    }
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
1.68M
    default:
325
1.68M
      loopit = FALSE;
326
1.68M
      fmt--;
327
1.68M
      break;
328
2.11M
    } /* switch */
329
2.11M
  } while(loopit); /* do */
330
1.68M
  *flagsp = flags;
331
1.68M
  *precp = precision;
332
1.68M
  *widthp = width;
333
1.68M
  *fmtp = fmt;
334
1.68M
  return PFMT_OK;
335
1.68M
}
336
337
static bool parse_conversion(const char f, unsigned int *flagp,
338
                             FormatType *typep)
339
1.68M
{
340
1.68M
  unsigned int flags = *flagp;
341
1.68M
  FormatType type;
342
1.68M
  switch(f) {
343
0
  case 'S':
344
0
    flags |= FLAGS_ALT;
345
0
    type = MTYPE_STRING;
346
0
    break;
347
1.10M
  case 's':
348
1.10M
    type = MTYPE_STRING;
349
1.10M
    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
390k
  case 'd':
357
390k
  case 'i':
358
390k
    if(flags & FLAGS_LONGLONG)
359
115
      type = MTYPE_LONGLONG;
360
390k
    else if(flags & FLAGS_LONG)
361
243k
      type = MTYPE_LONG;
362
147k
    else
363
147k
      type = MTYPE_INT;
364
390k
    break;
365
122k
  case 'u':
366
122k
    if(flags & FLAGS_LONGLONG)
367
0
      type = MTYPE_LONGLONGU;
368
122k
    else if(flags & FLAGS_LONG)
369
56.9k
      type = MTYPE_LONGU;
370
65.8k
    else
371
65.8k
      type = MTYPE_INTU;
372
122k
    flags |= FLAGS_UNSIGNED;
373
122k
    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
971
  case 'x':
384
971
    if(flags & FLAGS_LONGLONG)
385
0
      type = MTYPE_LONGLONGU;
386
971
    else if(flags & FLAGS_LONG)
387
508
      type = MTYPE_LONGU;
388
463
    else
389
463
      type = MTYPE_INTU;
390
971
    flags |= FLAGS_HEX | FLAGS_UNSIGNED;
391
971
    break;
392
61.4k
  case 'X':
393
61.4k
    if(flags & FLAGS_LONGLONG)
394
0
      type = MTYPE_LONGLONGU;
395
61.4k
    else if(flags & FLAGS_LONG)
396
0
      type = MTYPE_LONGU;
397
61.4k
    else
398
61.4k
      type = MTYPE_INTU;
399
61.4k
    flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED;
400
61.4k
    break;
401
2
  case 'c':
402
2
    type = MTYPE_INT;
403
2
    flags |= FLAGS_CHAR;
404
2
    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
1.68M
  } /* switch */
432
433
1.68M
  *flagp |= flags;
434
1.68M
  *typep = type;
435
1.68M
  return FALSE;
436
1.68M
}
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
617k
{
444
617k
  const char *fmt = format;
445
617k
  int param_num = 0;
446
617k
  int max_param = -1;
447
617k
  int i;
448
617k
  int ocount = 0;
449
617k
  unsigned char usedinput[MAX_PARAMETERS / 8];
450
617k
  size_t outlen = 0;
451
617k
  struct outsegment *optr;
452
617k
  int use_dollar = DOLLAR_UNKNOWN;
453
617k
  const char *start = fmt;
454
455
  /* clear, set a bit for each used input */
456
617k
  memset(usedinput, 0, sizeof(usedinput));
457
458
8.97M
  while(*fmt) {
459
8.35M
    if(*fmt == '%') {
460
1.74M
      struct va_input *iptr;
461
1.74M
      FormatType type;
462
1.74M
      unsigned int flags = 0;
463
1.74M
      int width = 0;
464
1.74M
      int precision = 0;
465
1.74M
      int param = -1;
466
1.74M
      int rc;
467
1.74M
      fmt++;
468
1.74M
      outlen = (size_t)(fmt - start - 1);
469
1.74M
      if(*fmt == '%') {
470
        /* this means a %% that should be output only as %. Create an output
471
           segment. */
472
61.4k
        if(outlen) {
473
0
          optr = &out[ocount++];
474
0
          if(ocount > MAX_SEGMENTS)
475
0
            return PFMT_MANYSEGS;
476
0
          optr->input = 0;
477
0
          optr->flags = FLAGS_SUBSTR;
478
0
          optr->start = start;
479
0
          optr->outlen = outlen;
480
0
        }
481
61.4k
        start = fmt;
482
61.4k
        fmt++;
483
61.4k
        continue; /* while */
484
61.4k
      }
485
486
1.68M
      if(use_dollar != DOLLAR_NOPE) {
487
592k
        param = dollarstring(fmt, &fmt);
488
592k
        if(param < 0) {
489
592k
          if(use_dollar == DOLLAR_USE)
490
            /* illegal combo */
491
0
            return PFMT_DOLLAR;
492
493
          /* we got no positional, get the next arg */
494
592k
          param = -1;
495
592k
          use_dollar = DOLLAR_NOPE;
496
592k
        }
497
0
        else
498
0
          use_dollar = DOLLAR_USE;
499
592k
      }
500
501
1.68M
      rc = parse_flags(&fmt, &flags, use_dollar, &precision, &width);
502
1.68M
      if(rc)
503
0
        return rc;
504
505
1.68M
      if(parse_conversion(*fmt, &flags, &type))
506
0
        continue;
507
508
1.68M
      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
1.68M
      if(flags & FLAGS_PRECPARAM) {
527
67.5k
        if(precision < 0)
528
67.5k
          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
67.5k
        if(precision >= MAX_PARAMETERS)
535
0
          return PFMT_MANYARGS;
536
67.5k
        if(precision >= max_param)
537
67.5k
          max_param = precision;
538
539
67.5k
        in[precision].type = MTYPE_PRECISION;
540
67.5k
        mark_arg_used(usedinput, precision);
541
67.5k
      }
542
543
      /* Handle the specifier */
544
1.68M
      if(param < 0)
545
1.68M
        param = param_num++;
546
1.68M
      if(param >= MAX_PARAMETERS)
547
0
        return PFMT_MANYARGS;
548
1.68M
      if(param >= max_param)
549
1.68M
        max_param = param;
550
551
1.68M
      iptr = &in[param];
552
1.68M
      iptr->type = type;
553
554
      /* mark this input as used */
555
1.68M
      mark_arg_used(usedinput, param);
556
557
1.68M
      fmt++;
558
1.68M
      optr = &out[ocount++];
559
1.68M
      if(ocount > MAX_SEGMENTS)
560
0
        return PFMT_MANYSEGS;
561
1.68M
      optr->input = (unsigned int)param;
562
1.68M
      optr->flags = flags;
563
1.68M
      optr->width = width;
564
1.68M
      optr->precision = precision;
565
1.68M
      optr->start = start;
566
1.68M
      optr->outlen = outlen;
567
1.68M
      start = fmt;
568
1.68M
    }
569
6.61M
    else
570
6.61M
      fmt++;
571
8.35M
  }
572
573
  /* is there a trailing piece */
574
617k
  outlen = (size_t)(fmt - start);
575
617k
  if(outlen) {
576
307k
    optr = &out[ocount++];
577
307k
    if(ocount > MAX_SEGMENTS)
578
0
      return PFMT_MANYSEGS;
579
307k
    optr->input = 0;
580
307k
    optr->flags = FLAGS_SUBSTR;
581
307k
    optr->start = start;
582
307k
    optr->outlen = outlen;
583
307k
  }
584
585
  /* Read the arg list parameters into our data list */
586
2.36M
  for(i = 0; i < max_param + 1; i++) {
587
1.75M
    struct va_input *iptr = &in[i];
588
1.75M
    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
1.75M
    switch(iptr->type) {
594
1.10M
    case MTYPE_STRING:
595
1.10M
      iptr->val.str = va_arg(arglist, const char *);
596
1.10M
      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
115
    case MTYPE_LONGLONG:
608
115
      iptr->val.nums = va_arg(arglist, int64_t);
609
115
      break;
610
611
57.4k
    case MTYPE_LONGU:
612
57.4k
      iptr->val.numu = va_arg(arglist, unsigned long);
613
57.4k
      break;
614
615
243k
    case MTYPE_LONG:
616
243k
      iptr->val.nums = va_arg(arglist, long);
617
243k
      break;
618
619
127k
    case MTYPE_INTU:
620
127k
      iptr->val.numu = va_arg(arglist, unsigned int);
621
127k
      break;
622
623
147k
    case MTYPE_INT:
624
147k
    case MTYPE_WIDTH:
625
215k
    case MTYPE_PRECISION:
626
215k
      iptr->val.nums = va_arg(arglist, int);
627
215k
      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
1.75M
    }
641
1.75M
  }
642
617k
  *ipieces = max_param + 1;
643
617k
  *opieces = ocount;
644
645
617k
  return PFMT_OK;
646
617k
}
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
2.77M
{
671
2.77M
  if(!len)
672
488k
    return FALSE;
673
2.29M
  if(streamn) {
674
2.29M
    size_t n = streamn(buf, len, userp);
675
2.29M
    *donep += (int)n;
676
2.29M
    return n != len;
677
2.29M
  }
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
1.79k
#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
1.68M
{
699
1.68M
  const unsigned char *src;
700
1.68M
  if(num <= 0)
701
1.68M
    return FALSE;
702
1.79k
  if(pad == '0')
703
1.78k
    src = (const unsigned char *)pad_zeros;
704
6
  else
705
6
    src = (const unsigned char *)pad_spaces;
706
1.79k
  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
1.79k
  return stream_run(userp, stream, streamn, src, (size_t)num, donep);
712
1.79k
}
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
2.20M
{
725
2.20M
  size_t n = 0;
726
40.2M
  while(n < len && str[n])
727
38.0M
    n++;
728
2.20M
  return stream_run(userp, stream, streamn,
729
2.20M
                    (const unsigned char *)str, n, donep);
730
2.20M
}
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
575k
{
829
575k
  const unsigned char *digits = Curl_ldigits;
830
575k
  int flags = p->flags;
831
575k
  int width = p->width;
832
575k
  int prec = p->prec;
833
575k
  bool is_alt = flags & FLAGS_ALT;
834
575k
  bool is_neg = FALSE;
835
575k
  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
575k
  char *workend = &work[BUFFSIZE - 2];
841
575k
  char *w;
842
843
575k
  if(flags & FLAGS_CHAR) {
844
    /* Character. */
845
2
    if(!(flags & FLAGS_LEFT)) {
846
2
      if(stream_pad(userp, stream, streamn, ' ', width - 1, donep))
847
0
        return TRUE;
848
2
      width = 1;
849
2
    }
850
2
    OUTCHAR((char)num);
851
2
    if(flags & FLAGS_LEFT) {
852
0
      if(stream_pad(userp, stream, streamn, ' ', width - 1, donep))
853
0
        return TRUE;
854
0
    }
855
2
    return FALSE;
856
2
  }
857
575k
  if(flags & FLAGS_OCTAL)
858
    /* Octal unsigned integer */
859
0
    base = 8;
860
861
575k
  else if(flags & FLAGS_HEX) {
862
    /* Hexadecimal unsigned integer */
863
62.3k
    digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits;
864
62.3k
    base = 16;
865
62.3k
  }
866
513k
  else if(flags & FLAGS_UNSIGNED)
867
    /* Decimal unsigned integer */
868
122k
    ;
869
870
390k
  else {
871
    /* Decimal integer. */
872
390k
    is_neg = (nums < 0);
873
390k
    if(is_neg) {
874
      /* signed_num might fail to hold absolute negative minimum by 1 */
875
192
      int64_t signed_num; /* Used to convert negative in positive. */
876
192
      signed_num = nums + (int64_t)1;
877
192
      signed_num = -signed_num;
878
192
      num = (uint64_t)signed_num;
879
192
      num += (uint64_t)1;
880
192
    }
881
390k
  }
882
883
  /* Supply a default precision if none was given. */
884
575k
  if(prec == -1)
885
575k
    prec = 1;
886
887
  /* Put the number in WORK. */
888
575k
  w = workend;
889
575k
  DEBUGASSERT(base <= 16);
890
575k
  switch(base) {
891
513k
  case 10:
892
1.01M
    while(num > 0) {
893
502k
      *w-- = (char)('0' + (num % 10));
894
502k
      num /= 10;
895
502k
    }
896
513k
    break;
897
62.3k
  default:
898
186k
    while(num > 0) {
899
124k
      *w-- = digits[num % base];
900
124k
      num /= base;
901
124k
    }
902
62.3k
    break;
903
575k
  }
904
575k
  width -= (int)(workend - w);
905
575k
  prec -= (int)(workend - w);
906
907
575k
  if(is_alt && base == 8 && prec <= 0) {
908
0
    *w-- = '0';
909
0
    --width;
910
0
  }
911
912
575k
  if(prec > 0) {
913
284k
    width -= prec;
914
568k
    while(prec-- > 0 && w >= work)
915
284k
      *w-- = '0';
916
284k
  }
917
918
575k
  if(is_alt && base == 16)
919
0
    width -= 2;
920
921
575k
  if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
922
192
    --width;
923
924
575k
  if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL)) {
925
513k
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
926
0
      return TRUE;
927
513k
    width = 0;
928
513k
  }
929
930
575k
  if(is_neg)
931
192
    OUTCHAR('-');
932
575k
  else if(flags & FLAGS_SHOWSIGN)
933
0
    OUTCHAR('+');
934
575k
  else if(flags & FLAGS_SPACE)
935
0
    OUTCHAR(' ');
936
937
575k
  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
575k
  if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL)) {
946
62.8k
    if(stream_pad(userp, stream, streamn, '0', width, donep))
947
0
      return TRUE;
948
62.8k
    width = 0;
949
62.8k
  }
950
951
  /* Write the number. */
952
575k
  if(stream_run(userp, stream, streamn, (const unsigned char *)w + 1,
953
575k
                (size_t)(workend - w), donep))
954
2
    return TRUE;
955
956
575k
  if(flags & FLAGS_LEFT) {
957
0
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
958
0
      return TRUE;
959
0
  }
960
961
575k
  return FALSE;
962
575k
}
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
1.10M
{
973
1.10M
  int flags = p->flags;
974
1.10M
  int width = p->width;
975
1.10M
  int prec = p->prec;
976
1.10M
  size_t len;
977
978
1.10M
  if(!str) {
979
    /* Write null string if there is space. */
980
1.53k
    if(prec == -1 || prec >= (int)CURL_CSTRLEN(nilstr)) {
981
1.53k
      str = nilstr;
982
1.53k
      len = CURL_CSTRLEN(nilstr);
983
      /* Disable quotes around (nil) */
984
1.53k
      flags &= ~(unsigned int)FLAGS_ALT;
985
1.53k
    }
986
0
    else {
987
0
      str = "";
988
0
      len = 0;
989
0
    }
990
1.53k
  }
991
1.10M
  else if(prec != -1)
992
67.3k
    len = (size_t)prec;
993
1.03M
  else if(*str == '\0')
994
487k
    len = 0;
995
552k
  else
996
552k
    len = strlen(str);
997
998
1.10M
  width -= (len > INT_MAX) ? INT_MAX : (int)len;
999
1000
1.10M
  if(flags & FLAGS_ALT)
1001
0
    OUTCHAR('"');
1002
1003
1.10M
  if(!(flags & FLAGS_LEFT)) {
1004
1.10M
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
1005
0
      return TRUE;
1006
1.10M
    width = 0;
1007
1.10M
  }
1008
1009
1.10M
  if(stream_zrun(userp, stream, streamn, str, len, donep))
1010
553
    return TRUE;
1011
1.10M
  if(flags & FLAGS_LEFT) {
1012
0
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
1013
0
      return TRUE;
1014
0
  }
1015
1016
1.10M
  if(flags & FLAGS_ALT)
1017
0
    OUTCHAR('"');
1018
1019
1.10M
  return FALSE;
1020
1.10M
}
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
617k
{
1087
617k
  int done = 0;   /* number of characters written */
1088
617k
  int i;
1089
617k
  int ocount = 0; /* number of output segments */
1090
617k
  int icount = 0; /* number of input arguments */
1091
1092
617k
  struct outsegment output[MAX_SEGMENTS];
1093
617k
  struct va_input input[MAX_PARAMETERS];
1094
617k
  char work[BUFFSIZE + 2];
1095
1096
  /* Parse the format string */
1097
617k
  if(parsefmt(format, output, input, &ocount, &icount, ap_save))
1098
0
    return 0;
1099
1100
2.60M
  for(i = 0; i < ocount; i++) {
1101
1.99M
    struct outsegment *optr = &output[i];
1102
1.99M
    struct va_input *iptr = &input[optr->input];
1103
1.99M
    struct mproperty p;
1104
1.99M
    size_t outlen = optr->outlen;
1105
1106
1.99M
    if(outlen) {
1107
1.09M
      if(stream_zrun(userp, stream, streamn, optr->start, outlen, &done))
1108
10
        return done;
1109
1.09M
      if(optr->flags & FLAGS_SUBSTR)
1110
        /* this is a substring */
1111
307k
        continue;
1112
1.09M
    }
1113
1114
1.68M
    p.flags = optr->flags;
1115
1116
    /* pick up the specified width */
1117
1.68M
    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
1.68M
    else
1131
1.68M
      p.width = optr->width;
1132
1133
    /* pick up the specified precision */
1134
1.68M
    if(p.flags & FLAGS_PRECPARAM) {
1135
67.3k
      p.prec = (int)input[optr->precision].val.nums;
1136
67.3k
      if(p.prec < 0)
1137
        /* "A negative precision is taken as if the precision were
1138
           omitted." */
1139
0
        p.prec = -1;
1140
67.3k
    }
1141
1.61M
    else if(p.flags & FLAGS_PREC)
1142
0
      p.prec = optr->precision;
1143
1.61M
    else
1144
1.61M
      p.prec = -1;
1145
1146
1.68M
    switch(iptr->type) {
1147
127k
    case MTYPE_INTU:
1148
185k
    case MTYPE_LONGU:
1149
185k
    case MTYPE_LONGLONGU:
1150
185k
      p.flags |= FLAGS_UNSIGNED;
1151
185k
      if(out_number(userp, stream, streamn, &p, iptr->val.numu, 0, work,
1152
185k
                    &done))
1153
0
        return done;
1154
185k
      break;
1155
1156
185k
    case MTYPE_INT:
1157
390k
    case MTYPE_LONG:
1158
390k
    case MTYPE_LONGLONG:
1159
390k
      if(out_number(userp, stream, streamn, &p, iptr->val.numu,
1160
390k
                    iptr->val.nums, work, &done))
1161
2
        return done;
1162
390k
      break;
1163
1164
1.10M
    case MTYPE_STRING:
1165
1.10M
      if(out_string(userp, stream, streamn, &p, iptr->val.str, &done))
1166
553
        return done;
1167
1.10M
      break;
1168
1169
1.10M
    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
1.68M
    }
1202
1.68M
  }
1203
616k
  return done;
1204
617k
}
1205
1206
/* fputc() look-alike */
1207
static int addbyter(unsigned char outc, void *f)
1208
158
{
1209
158
  struct nsprintf *infop = f;
1210
158
  if(infop->length < infop->max) {
1211
    /* only do this if we have not reached max length yet */
1212
158
    *infop->buffer++ = (char)outc; /* store */
1213
158
    infop->length++; /* we are now one byte larger */
1214
158
    return 0;     /* fputc() returns like this on success */
1215
158
  }
1216
0
  return 1;
1217
158
}
1218
1219
/* block variant of addbyter */
1220
static size_t addrun(const unsigned char *buf, size_t len, void *f)
1221
1.28M
{
1222
1.28M
  struct nsprintf *infop = f;
1223
1.28M
  size_t fit = infop->max - infop->length;
1224
1.28M
  if(len > fit)
1225
565
    len = fit;
1226
1.28M
  if(len) {
1227
1.28M
    memcpy(infop->buffer, buf, len);
1228
1.28M
    infop->buffer += len;
1229
1.28M
    infop->length += len;
1230
1.28M
  }
1231
1.28M
  return len;
1232
1.28M
}
1233
1234
int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1235
                    va_list args)
1236
299k
{
1237
299k
  int retcode;
1238
299k
  struct nsprintf info;
1239
1240
299k
  info.buffer = buffer;
1241
299k
  info.length = 0;
1242
299k
  info.max = maxlength;
1243
1244
299k
  retcode = formatf(&info, addbyter, addrun, NULL, format, args);
1245
299k
  if(info.max) {
1246
    /* we terminate this with a zero byte */
1247
299k
    if(info.max == info.length) {
1248
      /* we are at maximum, scrap the last letter */
1249
567
      info.buffer[-1] = 0;
1250
567
      DEBUGASSERT(retcode);
1251
567
      retcode--; /* do not count the nul byte */
1252
567
    }
1253
299k
    else
1254
299k
      info.buffer[0] = 0;
1255
299k
  }
1256
299k
  return retcode;
1257
299k
}
1258
1259
int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1260
190k
{
1261
190k
  int retcode;
1262
190k
  va_list args; /* argument pointer */
1263
190k
  va_start(args, format);
1264
190k
  retcode = curl_mvsnprintf(buffer, maxlength, format, args);
1265
190k
  va_end(args);
1266
190k
  return retcode;
1267
190k
}
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
355k
{
1273
355k
  struct asprintf *infop = f;
1274
355k
  if(infop->nstage) {
1275
354k
    CURLcode result = curlx_dyn_addn(infop->b, infop->stage, infop->nstage);
1276
354k
    infop->nstage = 0;
1277
354k
    if(result) {
1278
0
      infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
1279
0
      return 1; /* fail */
1280
0
    }
1281
354k
  }
1282
355k
  return 0;
1283
355k
}
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
0
{
1289
0
  CURLcode result = curlx_dyn_addn(infop->b, buf, len);
1290
0
  if(result) {
1291
0
    infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
1292
0
    return 1; /* fail */
1293
0
  }
1294
0
  return 0;
1295
0
}
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
1.03M
{
1302
1.03M
  return (infop->b->leng + infop->nstage + more + 1) <= infop->b->toobig;
1303
1.03M
}
1304
1305
/* fputc() look-alike */
1306
static int alloc_addbyter(unsigned char outc, void *f)
1307
36
{
1308
36
  struct asprintf *infop = f;
1309
36
  if(infop->nstage == sizeof(infop->stage) && alloc_flush(infop))
1310
0
    return 1; /* fail */
1311
36
  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
36
  infop->stage[infop->nstage++] = outc;
1320
36
  return 0;
1321
36
}
1322
1323
/* block variant of alloc_addbyter */
1324
static size_t alloc_addrun(const unsigned char *buf, size_t len, void *f)
1325
1.00M
{
1326
1.00M
  struct asprintf *infop = f;
1327
1.00M
  size_t accepted = 0;
1328
2.04M
  while(accepted < len) {
1329
1.03M
    size_t room = sizeof(infop->stage) - infop->nstage;
1330
1.03M
    size_t n;
1331
1.03M
    if(!room) {
1332
38.1k
      if(alloc_flush(infop))
1333
0
        return accepted; /* accepted < len aborts formatting */
1334
38.1k
      room = sizeof(infop->stage);
1335
38.1k
    }
1336
1.03M
    n = len - accepted;
1337
1.03M
    if(n > room)
1338
37.8k
      n = room;
1339
1.03M
    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
0
      size_t fits = infop->b->toobig - infop->b->leng - infop->nstage - 1;
1344
0
      if(fits) {
1345
0
        memcpy(&infop->stage[infop->nstage], buf + accepted, fits);
1346
0
        infop->nstage += fits;
1347
0
        accepted += fits;
1348
0
      }
1349
0
      if(alloc_flush(infop))
1350
0
        return accepted;
1351
0
      if(alloc_addn(infop, buf + accepted, 1))
1352
0
        return accepted; /* accepted < len aborts formatting */
1353
0
      accepted++;
1354
0
      continue;
1355
0
    }
1356
1.03M
    memcpy(&infop->stage[infop->nstage], buf + accepted, n);
1357
1.03M
    infop->nstage += n;
1358
1.03M
    accepted += n;
1359
1.03M
  }
1360
1.00M
  return accepted;
1361
1.00M
}
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
151k
{
1366
151k
  struct asprintf info;
1367
151k
  info.b = dyn;
1368
151k
  info.merr = MERR_OK;
1369
151k
  info.nstage = 0;
1370
1371
151k
  (void)formatf(&info, alloc_addbyter, alloc_addrun, alloc_flush,
1372
151k
                format, args);
1373
151k
  if(!info.merr)
1374
151k
    (void)alloc_flush(&info);
1375
151k
  if(info.merr) {
1376
0
    curlx_dyn_free(info.b);
1377
0
    return info.merr;
1378
0
  }
1379
151k
  return 0;
1380
151k
}
1381
1382
char *curl_mvaprintf(const char *format, va_list args)
1383
165k
{
1384
165k
  struct asprintf info;
1385
165k
  struct dynbuf dyn;
1386
165k
  info.b = &dyn;
1387
165k
  curlx_dyn_init(info.b, DYN_APRINTF);
1388
165k
  info.merr = MERR_OK;
1389
165k
  info.nstage = 0;
1390
1391
165k
  (void)formatf(&info, alloc_addbyter, alloc_addrun, alloc_flush,
1392
165k
                format, args);
1393
165k
  if(!info.merr)
1394
165k
    (void)alloc_flush(&info);
1395
165k
  if(info.merr) {
1396
0
    curlx_dyn_free(info.b);
1397
0
    return NULL;
1398
0
  }
1399
165k
  if(curlx_dyn_len(info.b))
1400
165k
    return curlx_dyn_ptr(info.b);
1401
0
  return curlx_strdup("");
1402
165k
}
1403
1404
char *curl_maprintf(const char *format, ...)
1405
64.8k
{
1406
64.8k
  va_list args;
1407
64.8k
  char *s;
1408
64.8k
  va_start(args, format);
1409
64.8k
  s = curl_mvaprintf(format, args);
1410
64.8k
  va_end(args);
1411
64.8k
  return s;
1412
64.8k
}
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
1.70k
{
1453
1.70k
  FILE *s = f;
1454
1.70k
  return fwrite(buf, 1, len, s);
1455
1.70k
}
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
852
{
1469
852
  int retcode;
1470
852
  va_list args; /* argument pointer */
1471
852
  va_start(args, format);
1472
852
  retcode = formatf(fd, fputc_wrapper, fwrite_wrapper, NULL, format, args);
1473
852
  va_end(args);
1474
852
  return retcode;
1475
852
}
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
}