Coverage Report

Created: 2026-10-15 06:17

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