Coverage Report

Created: 2026-09-04 07:15

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