Coverage Report

Created: 2026-10-22 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/mprintf.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#include "curlx/dynbuf.h"
27
#include "curl_printf.h"
28
#include "curlx/strparse.h"
29
#include "curlx/snprintf.h"  /* for curlx_win32_snprintf() */
30
31
1.84M
#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
32
                        fit negative DBL_MAX (317 letters) */
33
7.35M
#define MAX_PARAMETERS 128 /* number of input arguments */
34
6.16M
#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
82.6k
  do {                                    \
44
82.6k
    if(stream((unsigned char)(x), userp)) \
45
82.6k
      return TRUE;                        \
46
82.6k
    (*donep)++;                           \
47
82.6k
  } 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
1.88M
{
153
1.88M
  curl_off_t num;
154
1.88M
  if(curlx_str_number(&p, &num, MAX_PARAMETERS) ||
155
430k
     curlx_str_single(&p, '$') || !num)
156
1.88M
    return -1;
157
0
  *end = p;
158
0
  return (int)num - 1;
159
1.88M
}
160
161
5.46M
#define is_arg_used(x, y)   ((x)[(y) / 8] & (1 << ((y) & 7)))
162
5.46M
#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
7.26M
#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
5.33M
{
188
5.33M
  const char *fmt = *fmtp;
189
5.33M
  bool loopit = TRUE;
190
5.33M
  unsigned int flags = 0;
191
5.33M
  int width = 0;
192
5.33M
  int precision = 0;
193
194
  /* Handle the flags */
195
6.72M
  do {
196
6.72M
    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
167k
    case '.':
211
167k
      if('*' == *fmt) {
212
        /* The precision is picked from a specified parameter */
213
132k
        flags |= FLAGS_PRECPARAM;
214
132k
        fmt++;
215
216
132k
        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
132k
        else
223
          /* get it from the next argument */
224
132k
          precision = -1;
225
132k
      }
226
35.6k
      else {
227
35.6k
        bool is_neg;
228
35.6k
        curl_off_t num;
229
35.6k
        flags |= FLAGS_PREC;
230
35.6k
        is_neg = ('-' == *fmt);
231
35.6k
        if(is_neg)
232
0
          fmt++;
233
35.6k
        if(curlx_str_number(&fmt, &num, INT_MAX))
234
0
          return PFMT_PREC;
235
35.6k
        precision = (int)num;
236
35.6k
        if(is_neg)
237
0
          precision = -precision;
238
35.6k
      }
239
167k
      if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
240
167k
         (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
167k
      break;
245
167k
    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
700k
    case 'l':
269
700k
      if(flags & FLAGS_LONG)
270
1.48k
        flags |= FLAGS_LONGLONG;
271
698k
      else
272
698k
        flags |= FLAGS_LONG;
273
700k
      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
76.7k
    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
76.7k
      flags |= FLAGS_LONG;
287
76.7k
#endif
288
76.7k
      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
445k
    case '0':
297
445k
      if(!(flags & FLAGS_LEFT))
298
445k
        flags |= FLAGS_PAD_NIL;
299
445k
      FALLTHROUGH();
300
445k
    case '1':
301
445k
    case '2':
302
445k
    case '3':
303
446k
    case '4':
304
446k
    case '5':
305
446k
    case '6':
306
446k
    case '7':
307
446k
    case '8':
308
446k
    case '9': {
309
446k
      curl_off_t num;
310
446k
      flags |= FLAGS_WIDTH;
311
446k
      fmt--;
312
446k
      if(curlx_str_number(&fmt, &num, INT_MAX))
313
0
        return PFMT_WIDTH;
314
446k
      width = (int)num;
315
446k
      break;
316
446k
    }
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
5.33M
    default:
330
5.33M
      loopit = FALSE;
331
5.33M
      fmt--;
332
5.33M
      break;
333
6.72M
    } /* switch */
334
6.72M
  } while(loopit); /* do */
335
5.33M
  *flagsp = flags;
336
5.33M
  *precp = precision;
337
5.33M
  *widthp = width;
338
5.33M
  *fmtp = fmt;
339
5.33M
  return PFMT_OK;
340
5.33M
}
341
342
static bool parse_conversion(const char f, unsigned int *flagp,
343
                             FormatType *typep)
344
5.33M
{
345
5.33M
  unsigned int flags = *flagp;
346
5.33M
  FormatType type;
347
5.33M
  switch(f) {
348
0
  case 'S':
349
0
    flags |= FLAGS_ALT;
350
0
    type = MTYPE_STRING;
351
0
    break;
352
3.48M
  case 's':
353
3.48M
    type = MTYPE_STRING;
354
3.48M
    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
940k
  case 'd':
362
940k
  case 'i':
363
940k
    if(flags & FLAGS_LONGLONG)
364
1.48k
      type = MTYPE_LONGLONG;
365
938k
    else if(flags & FLAGS_LONG)
366
697k
      type = MTYPE_LONG;
367
241k
    else
368
241k
      type = MTYPE_INT;
369
940k
    break;
370
354k
  case 'u':
371
354k
    if(flags & FLAGS_LONGLONG)
372
0
      type = MTYPE_LONGLONGU;
373
354k
    else if(flags & FLAGS_LONG)
374
74.6k
      type = MTYPE_LONGU;
375
280k
    else
376
280k
      type = MTYPE_INTU;
377
354k
    flags |= FLAGS_UNSIGNED;
378
354k
    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
185k
  case 'x':
389
185k
    if(flags & FLAGS_LONGLONG)
390
0
      type = MTYPE_LONGLONGU;
391
185k
    else if(flags & FLAGS_LONG)
392
2.14k
      type = MTYPE_LONGU;
393
182k
    else
394
182k
      type = MTYPE_INTU;
395
185k
    flags |= FLAGS_HEX | FLAGS_UNSIGNED;
396
185k
    break;
397
287k
  case 'X':
398
287k
    if(flags & FLAGS_LONGLONG)
399
0
      type = MTYPE_LONGLONGU;
400
287k
    else if(flags & FLAGS_LONG)
401
0
      type = MTYPE_LONGU;
402
287k
    else
403
287k
      type = MTYPE_INTU;
404
287k
    flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED;
405
287k
    break;
406
82.3k
  case 'c':
407
82.3k
    type = MTYPE_INT;
408
82.3k
    flags |= FLAGS_CHAR;
409
82.3k
    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
5.33M
  } /* switch */
437
438
5.33M
  *flagp |= flags;
439
5.33M
  *typep = type;
440
5.33M
  return FALSE;
441
5.33M
}
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
1.93M
{
449
1.93M
  const char *fmt = format;
450
1.93M
  int param_num = 0;
451
1.93M
  int max_param = -1;
452
1.93M
  int i;
453
1.93M
  int ocount = 0;
454
1.93M
  unsigned char usedinput[MAX_PARAMETERS / 8];
455
1.93M
  size_t outlen = 0;
456
1.93M
  struct outsegment *optr;
457
1.93M
  int use_dollar = DOLLAR_UNKNOWN;
458
1.93M
  const char *start = fmt;
459
460
  /* clear, set a bit for each used input */
461
1.93M
  memset(usedinput, 0, sizeof(usedinput));
462
463
18.2M
  while(*fmt) {
464
16.2M
    if(*fmt == '%') {
465
5.62M
      struct va_input *iptr;
466
5.62M
      FormatType type;
467
5.62M
      unsigned int flags = 0;
468
5.62M
      int width = 0;
469
5.62M
      int precision = 0;
470
5.62M
      int param = -1;
471
5.62M
      int rc;
472
5.62M
      fmt++;
473
5.62M
      outlen = (size_t)(fmt - start - 1);
474
5.62M
      if(*fmt == '%') {
475
        /* this means a %% that should be output only as %. Create an output
476
           segment. */
477
290k
        if(outlen) {
478
0
          optr = &out[ocount++];
479
0
          if(ocount > MAX_SEGMENTS)
480
0
            return PFMT_MANYSEGS;
481
0
          optr->input = 0;
482
0
          optr->flags = FLAGS_SUBSTR;
483
0
          optr->start = start;
484
0
          optr->outlen = outlen;
485
0
        }
486
290k
        start = fmt;
487
290k
        fmt++;
488
290k
        continue; /* while */
489
290k
      }
490
491
5.33M
      if(use_dollar != DOLLAR_NOPE) {
492
1.88M
        param = dollarstring(fmt, &fmt);
493
1.88M
        if(param < 0) {
494
1.88M
          if(use_dollar == DOLLAR_USE)
495
            /* illegal combo */
496
0
            return PFMT_DOLLAR;
497
498
          /* we got no positional, get the next arg */
499
1.88M
          param = -1;
500
1.88M
          use_dollar = DOLLAR_NOPE;
501
1.88M
        }
502
0
        else
503
0
          use_dollar = DOLLAR_USE;
504
1.88M
      }
505
506
5.33M
      rc = parse_flags(&fmt, &flags, use_dollar, &precision, &width);
507
5.33M
      if(rc)
508
0
        return rc;
509
510
5.33M
      if(parse_conversion(*fmt, &flags, &type))
511
0
        continue;
512
513
5.33M
      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
5.33M
      if(flags & FLAGS_PRECPARAM) {
532
132k
        if(precision < 0)
533
132k
          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
132k
        if(precision >= MAX_PARAMETERS)
540
0
          return PFMT_MANYARGS;
541
132k
        if(precision >= max_param)
542
132k
          max_param = precision;
543
544
132k
        in[precision].type = MTYPE_PRECISION;
545
132k
        mark_arg_used(usedinput, precision);
546
132k
      }
547
548
      /* Handle the specifier */
549
5.33M
      if(param < 0)
550
5.33M
        param = param_num++;
551
5.33M
      if(param >= MAX_PARAMETERS)
552
0
        return PFMT_MANYARGS;
553
5.33M
      if(param >= max_param)
554
5.33M
        max_param = param;
555
556
5.33M
      iptr = &in[param];
557
5.33M
      iptr->type = type;
558
559
      /* mark this input as used */
560
5.33M
      mark_arg_used(usedinput, param);
561
562
5.33M
      fmt++;
563
5.33M
      optr = &out[ocount++];
564
5.33M
      if(ocount > MAX_SEGMENTS)
565
0
        return PFMT_MANYSEGS;
566
5.33M
      optr->input = (unsigned int)param;
567
5.33M
      optr->flags = flags;
568
5.33M
      optr->width = width;
569
5.33M
      optr->precision = precision;
570
5.33M
      optr->start = start;
571
5.33M
      optr->outlen = outlen;
572
5.33M
      start = fmt;
573
5.33M
    }
574
10.6M
    else
575
10.6M
      fmt++;
576
16.2M
  }
577
578
  /* is there a trailing piece */
579
1.93M
  outlen = (size_t)(fmt - start);
580
1.93M
  if(outlen) {
581
827k
    optr = &out[ocount++];
582
827k
    if(ocount > MAX_SEGMENTS)
583
0
      return PFMT_MANYSEGS;
584
827k
    optr->input = 0;
585
827k
    optr->flags = FLAGS_SUBSTR;
586
827k
    optr->start = start;
587
827k
    optr->outlen = outlen;
588
827k
  }
589
590
  /* Read the arg list parameters into our data list */
591
7.39M
  for(i = 0; i < max_param + 1; i++) {
592
5.46M
    struct va_input *iptr = &in[i];
593
5.46M
    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
5.46M
    switch(iptr->type) {
599
3.48M
    case MTYPE_STRING:
600
3.48M
      iptr->val.str = va_arg(arglist, const char *);
601
3.48M
      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
1.48k
    case MTYPE_LONGLONG:
613
1.48k
      iptr->val.nums = va_arg(arglist, int64_t);
614
1.48k
      break;
615
616
76.8k
    case MTYPE_LONGU:
617
76.8k
      iptr->val.numu = va_arg(arglist, unsigned long);
618
76.8k
      break;
619
620
697k
    case MTYPE_LONG:
621
697k
      iptr->val.nums = va_arg(arglist, long);
622
697k
      break;
623
624
750k
    case MTYPE_INTU:
625
750k
      iptr->val.numu = va_arg(arglist, unsigned int);
626
750k
      break;
627
628
323k
    case MTYPE_INT:
629
323k
    case MTYPE_WIDTH:
630
455k
    case MTYPE_PRECISION:
631
455k
      iptr->val.nums = va_arg(arglist, int);
632
455k
      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
5.46M
    }
646
5.46M
  }
647
1.93M
  *ipieces = max_param + 1;
648
1.93M
  *opieces = ocount;
649
650
1.93M
  return PFMT_OK;
651
1.93M
}
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
8.12M
{
676
8.12M
  if(!len)
677
1.95M
    return FALSE;
678
6.16M
  if(streamn) {
679
6.16M
    size_t n = streamn(buf, len, userp);
680
6.16M
    *donep += (int)n;
681
6.16M
    return n != len;
682
6.16M
  }
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
17.4k
#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
5.33M
{
704
5.33M
  const unsigned char *src;
705
5.33M
  if(num <= 0)
706
5.31M
    return FALSE;
707
17.4k
  if(pad == '0')
708
17.3k
    src = (const unsigned char *)pad_zeros;
709
69
  else
710
69
    src = (const unsigned char *)pad_spaces;
711
17.4k
  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
17.4k
  return stream_run(userp, stream, streamn, src, (size_t)num, donep);
717
17.4k
}
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
2.98M
{
730
2.98M
  size_t n = 0;
731
21.1M
  while(n < len && str[n])
732
18.1M
    n++;
733
2.98M
  return stream_run(userp, stream, streamn,
734
2.98M
                    (const unsigned char *)str, n, donep);
735
2.98M
}
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
1.84M
{
834
1.84M
  const unsigned char *digits = Curl_ldigits;
835
1.84M
  int flags = p->flags;
836
1.84M
  int width = p->width;
837
1.84M
  int prec = p->prec;
838
1.84M
  bool is_alt = flags & FLAGS_ALT;
839
1.84M
  bool is_neg = FALSE;
840
1.84M
  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
1.84M
  char *workend = &work[BUFFSIZE - 2];
846
1.84M
  char *w;
847
848
1.84M
  if(flags & FLAGS_CHAR) {
849
    /* Character. */
850
82.3k
    if(!(flags & FLAGS_LEFT)) {
851
82.3k
      if(stream_pad(userp, stream, streamn, ' ', width - 1, donep))
852
0
        return TRUE;
853
82.3k
      width = 1;
854
82.3k
    }
855
82.3k
    OUTCHAR((char)num);
856
82.3k
    if(flags & FLAGS_LEFT) {
857
0
      if(stream_pad(userp, stream, streamn, ' ', width - 1, donep))
858
0
        return TRUE;
859
0
    }
860
82.3k
    return FALSE;
861
82.3k
  }
862
1.76M
  if(flags & FLAGS_OCTAL)
863
    /* Octal unsigned integer */
864
0
    base = 8;
865
866
1.76M
  else if(flags & FLAGS_HEX) {
867
    /* Hexadecimal unsigned integer */
868
472k
    digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits;
869
472k
    base = 16;
870
472k
  }
871
1.29M
  else if(flags & FLAGS_UNSIGNED)
872
    /* Decimal unsigned integer */
873
354k
    ;
874
875
940k
  else {
876
    /* Decimal integer. */
877
940k
    is_neg = (nums < 0);
878
940k
    if(is_neg) {
879
      /* signed_num might fail to hold absolute negative minimum by 1 */
880
298
      int64_t signed_num; /* Used to convert negative in positive. */
881
298
      signed_num = nums + (int64_t)1;
882
298
      signed_num = -signed_num;
883
298
      num = (uint64_t)signed_num;
884
298
      num += (uint64_t)1;
885
298
    }
886
940k
  }
887
888
  /* Supply a default precision if none was given. */
889
1.76M
  if(prec == -1)
890
1.73M
    prec = 1;
891
892
  /* Put the number in WORK. */
893
1.76M
  w = workend;
894
1.76M
  DEBUGASSERT(base <= 16);
895
1.76M
  switch(base) {
896
1.29M
  case 10:
897
2.26M
    while(num > 0) {
898
965k
      *w-- = (char)('0' + (num % 10));
899
965k
      num /= 10;
900
965k
    }
901
1.29M
    break;
902
472k
  default:
903
1.40M
    while(num > 0) {
904
931k
      *w-- = digits[num % base];
905
931k
      num /= base;
906
931k
    }
907
472k
    break;
908
1.76M
  }
909
1.76M
  width -= (int)(workend - w);
910
1.76M
  prec -= (int)(workend - w);
911
912
1.76M
  if(is_alt && base == 8 && prec <= 0) {
913
0
    *w-- = '0';
914
0
    --width;
915
0
  }
916
917
1.76M
  if(prec > 0) {
918
816k
    width -= prec;
919
1.63M
    while(prec-- > 0 && w >= work)
920
816k
      *w-- = '0';
921
816k
  }
922
923
1.76M
  if(is_alt && base == 16)
924
0
    width -= 2;
925
926
1.76M
  if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
927
298
    --width;
928
929
1.76M
  if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL)) {
930
1.32M
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
931
0
      return TRUE;
932
1.32M
    width = 0;
933
1.32M
  }
934
935
1.76M
  if(is_neg)
936
298
    OUTCHAR('-');
937
1.76M
  else if(flags & FLAGS_SHOWSIGN)
938
0
    OUTCHAR('+');
939
1.76M
  else if(flags & FLAGS_SPACE)
940
0
    OUTCHAR(' ');
941
942
1.76M
  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
1.76M
  if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL)) {
951
445k
    if(stream_pad(userp, stream, streamn, '0', width, donep))
952
0
      return TRUE;
953
445k
    width = 0;
954
445k
  }
955
956
  /* Write the number. */
957
1.76M
  if(stream_run(userp, stream, streamn, (const unsigned char *)w + 1,
958
1.76M
                (size_t)(workend - w), donep))
959
120
    return TRUE;
960
961
1.76M
  if(flags & FLAGS_LEFT) {
962
0
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
963
0
      return TRUE;
964
0
  }
965
966
1.76M
  return FALSE;
967
1.76M
}
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
3.48M
{
978
3.48M
  int flags = p->flags;
979
3.48M
  int width = p->width;
980
3.48M
  int prec = p->prec;
981
3.48M
  size_t len;
982
983
3.48M
  if(!str) {
984
    /* Write null string if there is space. */
985
1.12k
    if(prec == -1 || prec >= (int)CURL_CSTRLEN(nilstr)) {
986
1.12k
      str = nilstr;
987
1.12k
      len = CURL_CSTRLEN(nilstr);
988
      /* Disable quotes around (nil) */
989
1.12k
      flags &= ~(unsigned int)FLAGS_ALT;
990
1.12k
    }
991
0
    else {
992
0
      str = "";
993
0
      len = 0;
994
0
    }
995
1.12k
  }
996
3.48M
  else if(prec != -1)
997
132k
    len = (size_t)prec;
998
3.35M
  else if(*str == '\0')
999
1.95M
    len = 0;
1000
1.39M
  else
1001
1.39M
    len = strlen(str);
1002
1003
3.48M
  width -= (len > INT_MAX) ? INT_MAX : (int)len;
1004
1005
3.48M
  if(flags & FLAGS_ALT)
1006
0
    OUTCHAR('"');
1007
1008
3.48M
  if(!(flags & FLAGS_LEFT)) {
1009
3.48M
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
1010
0
      return TRUE;
1011
3.48M
    width = 0;
1012
3.48M
  }
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
3.48M
  if(prec == -1) {
1018
3.35M
    if(stream_run(userp, stream, streamn,
1019
3.35M
                  (const unsigned char *)str, len, donep))
1020
788
      return TRUE;
1021
3.35M
  }
1022
132k
  else if(stream_zrun(userp, stream, streamn, str, len, donep))
1023
404
    return TRUE;
1024
3.48M
  if(flags & FLAGS_LEFT) {
1025
0
    if(stream_pad(userp, stream, streamn, ' ', width, donep))
1026
0
      return TRUE;
1027
0
  }
1028
1029
3.48M
  if(flags & FLAGS_ALT)
1030
0
    OUTCHAR('"');
1031
1032
3.48M
  return FALSE;
1033
3.48M
}
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
1.93M
{
1100
1.93M
  int done = 0;   /* number of characters written */
1101
1.93M
  int i;
1102
1.93M
  int ocount = 0; /* number of output segments */
1103
1.93M
  int icount = 0; /* number of input arguments */
1104
1105
1.93M
  struct outsegment output[MAX_SEGMENTS];
1106
1.93M
  struct va_input input[MAX_PARAMETERS];
1107
1.93M
  char work[BUFFSIZE + 2];
1108
1109
  /* Parse the format string */
1110
1.93M
  if(parsefmt(format, output, input, &ocount, &icount, ap_save))
1111
0
    return 0;
1112
1113
8.09M
  for(i = 0; i < ocount; i++) {
1114
6.16M
    struct outsegment *optr = &output[i];
1115
6.16M
    struct va_input *iptr = &input[optr->input];
1116
6.16M
    struct mproperty p;
1117
6.16M
    size_t outlen = optr->outlen;
1118
1119
6.16M
    if(outlen) {
1120
2.85M
      if(stream_zrun(userp, stream, streamn, optr->start, outlen, &done))
1121
82
        return done;
1122
2.85M
      if(optr->flags & FLAGS_SUBSTR)
1123
        /* this is a substring */
1124
827k
        continue;
1125
2.85M
    }
1126
1127
5.33M
    p.flags = optr->flags;
1128
1129
    /* pick up the specified width */
1130
5.33M
    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
5.33M
    else
1144
5.33M
      p.width = optr->width;
1145
1146
    /* pick up the specified precision */
1147
5.33M
    if(p.flags & FLAGS_PRECPARAM) {
1148
132k
      p.prec = (int)input[optr->precision].val.nums;
1149
132k
      if(p.prec < 0)
1150
        /* "A negative precision is taken as if the precision were
1151
           omitted." */
1152
0
        p.prec = -1;
1153
132k
    }
1154
5.20M
    else if(p.flags & FLAGS_PREC)
1155
35.6k
      p.prec = optr->precision;
1156
5.16M
    else
1157
5.16M
      p.prec = -1;
1158
1159
5.33M
    switch(iptr->type) {
1160
750k
    case MTYPE_INTU:
1161
827k
    case MTYPE_LONGU:
1162
827k
    case MTYPE_LONGLONGU:
1163
827k
      p.flags |= FLAGS_UNSIGNED;
1164
827k
      if(out_number(userp, stream, streamn, &p, iptr->val.numu, 0, work,
1165
827k
                    &done))
1166
116
        return done;
1167
827k
      break;
1168
1169
827k
    case MTYPE_INT:
1170
1.02M
    case MTYPE_LONG:
1171
1.02M
    case MTYPE_LONGLONG:
1172
1.02M
      if(out_number(userp, stream, streamn, &p, iptr->val.numu,
1173
1.02M
                    iptr->val.nums, work, &done))
1174
4
        return done;
1175
1.02M
      break;
1176
1177
3.48M
    case MTYPE_STRING:
1178
3.48M
      if(out_string(userp, stream, streamn, &p, iptr->val.str, &done))
1179
1.19k
        return done;
1180
3.48M
      break;
1181
1182
3.48M
    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
5.33M
    }
1215
5.33M
  }
1216
1.93M
  return done;
1217
1.93M
}
1218
1219
/* fputc() look-alike */
1220
static int addbyter(unsigned char outc, void *f)
1221
82.5k
{
1222
82.5k
  struct nsprintf *infop = f;
1223
82.5k
  if(infop->length < infop->max) {
1224
    /* only do this if we have not reached max length yet */
1225
82.5k
    *infop->buffer++ = (char)outc; /* store */
1226
82.5k
    infop->length++; /* we are now one byte larger */
1227
82.5k
    return 0;     /* fputc() returns like this on success */
1228
82.5k
  }
1229
0
  return 1;
1230
82.5k
}
1231
1232
/* block variant of addbyter */
1233
static size_t addrun(const unsigned char *buf, size_t len, void *f)
1234
3.32M
{
1235
3.32M
  struct nsprintf *infop = f;
1236
3.32M
  size_t fit = infop->max - infop->length;
1237
3.32M
  if(len > fit)
1238
1.38k
    len = fit;
1239
3.32M
  if(len) {
1240
3.32M
    memcpy(infop->buffer, buf, len);
1241
3.32M
    infop->buffer += len;
1242
3.32M
    infop->length += len;
1243
3.32M
  }
1244
3.32M
  return len;
1245
3.32M
}
1246
1247
int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1248
                    va_list args)
1249
1.04M
{
1250
1.04M
  int retcode;
1251
1.04M
  struct nsprintf info;
1252
1253
1.04M
  info.buffer = buffer;
1254
1.04M
  info.length = 0;
1255
1.04M
  info.max = maxlength;
1256
1257
1.04M
  retcode = formatf(&info, addbyter, addrun, NULL, format, args);
1258
1.04M
  if(info.max) {
1259
    /* we null-terminate this */
1260
1.04M
    if(info.max == info.length) {
1261
      /* we are at maximum, scrap the last letter */
1262
1.38k
      info.buffer[-1] = 0;
1263
1.38k
      DEBUGASSERT(retcode);
1264
1.38k
      retcode--; /* do not count the NUL byte */
1265
1.38k
    }
1266
1.04M
    else
1267
1.04M
      info.buffer[0] = 0;
1268
1.04M
  }
1269
1.04M
  return retcode;
1270
1.04M
}
1271
1272
int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1273
750k
{
1274
750k
  int retcode;
1275
750k
  va_list args; /* argument pointer */
1276
750k
  va_start(args, format);
1277
750k
  retcode = curl_mvsnprintf(buffer, maxlength, format, args);
1278
750k
  va_end(args);
1279
750k
  return retcode;
1280
750k
}
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
997k
{
1286
997k
  struct asprintf *infop = f;
1287
997k
  if(infop->nstage) {
1288
997k
    CURLcode result = curlx_dyn_addn(infop->b, infop->stage, infop->nstage);
1289
997k
    infop->nstage = 0;
1290
997k
    if(result) {
1291
128
      infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
1292
128
      return 1; /* fail */
1293
128
    }
1294
997k
  }
1295
997k
  return 0;
1296
997k
}
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
4
{
1302
4
  CURLcode result = curlx_dyn_addn(infop->b, buf, len);
1303
4
  if(result) {
1304
4
    infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
1305
4
    return 1; /* fail */
1306
4
  }
1307
0
  return 0;
1308
4
}
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
2.89M
{
1315
2.89M
  return (infop->b->leng + infop->nstage + more + 1) <= infop->b->toobig;
1316
2.89M
}
1317
1318
/* fputc() look-alike */
1319
static int alloc_addbyter(unsigned char outc, void *f)
1320
91
{
1321
91
  struct asprintf *infop = f;
1322
91
  if(infop->nstage == sizeof(infop->stage) && alloc_flush(infop))
1323
0
    return 1; /* fail */
1324
91
  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
91
  infop->stage[infop->nstage++] = outc;
1333
91
  return 0;
1334
91
}
1335
1336
/* block variant of alloc_addbyter */
1337
static size_t alloc_addrun(const unsigned char *buf, size_t len, void *f)
1338
2.77M
{
1339
2.77M
  struct asprintf *infop = f;
1340
2.77M
  size_t accepted = 0;
1341
5.67M
  while(accepted < len) {
1342
2.89M
    size_t room = sizeof(infop->stage) - infop->nstage;
1343
2.89M
    size_t n;
1344
2.89M
    if(!room) {
1345
112k
      if(alloc_flush(infop))
1346
5
        return accepted; /* accepted < len aborts formatting */
1347
112k
      room = sizeof(infop->stage);
1348
112k
    }
1349
2.89M
    n = len - accepted;
1350
2.89M
    if(n > room)
1351
112k
      n = room;
1352
2.89M
    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
4
      size_t fits = infop->b->toobig - infop->b->leng - infop->nstage - 1;
1357
4
      if(fits) {
1358
1
        memcpy(&infop->stage[infop->nstage], buf + accepted, fits);
1359
1
        infop->nstage += fits;
1360
1
        accepted += fits;
1361
1
      }
1362
4
      if(alloc_flush(infop))
1363
0
        return accepted;
1364
4
      if(alloc_addn(infop, buf + accepted, 1))
1365
4
        return accepted; /* accepted < len aborts formatting */
1366
0
      accepted++;
1367
0
      continue;
1368
4
    }
1369
2.89M
    memcpy(&infop->stage[infop->nstage], buf + accepted, n);
1370
2.89M
    infop->nstage += n;
1371
2.89M
    accepted += n;
1372
2.89M
  }
1373
2.77M
  return accepted;
1374
2.77M
}
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
553k
{
1379
553k
  struct asprintf info;
1380
553k
  info.b = dyn;
1381
553k
  info.merr = MERR_OK;
1382
553k
  info.nstage = 0;
1383
1384
553k
  (void)formatf(&info, alloc_addbyter, alloc_addrun, alloc_flush,
1385
553k
                format, args);
1386
553k
  if(!info.merr)
1387
553k
    (void)alloc_flush(&info);
1388
553k
  if(info.merr) {
1389
47
    curlx_dyn_free(info.b);
1390
47
    return info.merr;
1391
47
  }
1392
553k
  return 0;
1393
553k
}
1394
1395
char *curl_mvaprintf(const char *format, va_list args)
1396
331k
{
1397
331k
  struct asprintf info;
1398
331k
  struct dynbuf dyn;
1399
331k
  info.b = &dyn;
1400
331k
  curlx_dyn_init(info.b, DYN_APRINTF);
1401
331k
  info.merr = MERR_OK;
1402
331k
  info.nstage = 0;
1403
1404
331k
  (void)formatf(&info, alloc_addbyter, alloc_addrun, alloc_flush,
1405
331k
                format, args);
1406
331k
  if(!info.merr)
1407
331k
    (void)alloc_flush(&info);
1408
331k
  if(info.merr) {
1409
85
    curlx_dyn_free(info.b);
1410
85
    return NULL;
1411
85
  }
1412
331k
  if(curlx_dyn_len(info.b))
1413
331k
    return curlx_dyn_ptr(info.b);
1414
0
  return curlx_strdup("");
1415
331k
}
1416
1417
char *curl_maprintf(const char *format, ...)
1418
271k
{
1419
271k
  va_list args;
1420
271k
  char *s;
1421
271k
  va_start(args, format);
1422
271k
  s = curl_mvaprintf(format, args);
1423
271k
  va_end(args);
1424
271k
  return s;
1425
271k
}
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
62.3k
{
1468
62.3k
  struct ioprintf *i = f;
1469
62.3k
  size_t n;
1470
62.3k
  FILE *s = i->fd;
1471
62.3k
  n = fwrite(buf, 1, len, s);
1472
62.3k
  i->error = (n != len);
1473
62.3k
  return n;
1474
62.3k
}
1475
1476
int curl_mvfprintf(FILE *fd, const char *format, va_list args)
1477
6.39k
{
1478
6.39k
  struct ioprintf info;
1479
6.39k
  int n;
1480
6.39k
  info.fd = fd;
1481
6.39k
  info.error = FALSE;
1482
6.39k
  n = formatf(&info, fputc_wrapper, fwrite_wrapper, NULL, format, args);
1483
6.39k
  if(info.error)
1484
0
    return -1;
1485
6.39k
  return n;
1486
6.39k
}
1487
1488
int curl_mfprintf(FILE *fd, const char *format, ...)
1489
6.39k
{
1490
6.39k
  va_list args; /* argument pointer */
1491
6.39k
  int n;
1492
6.39k
  va_start(args, format);
1493
6.39k
  n = curl_mvfprintf(fd, format, args);
1494
6.39k
  va_end(args);
1495
6.39k
  return n;
1496
6.39k
}
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
}