Coverage Report

Created: 2026-03-12 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/printf/vfprintf.c
Line
Count
Source
1
/*-
2
 * SPDX-License-Identifier: BSD-3-Clause
3
 *
4
 * Copyright (c) 1990, 1993
5
 *  The Regents of the University of California.  All rights reserved.
6
 *
7
 * This code is derived from software contributed to Berkeley by
8
 * Chris Torek.
9
 *
10
 * Copyright (c) 2011 The FreeBSD Foundation
11
 * All rights reserved.
12
 * Portions of this software were developed by David Chisnall
13
 * under sponsorship from the FreeBSD Foundation.
14
 *
15
 * Redistribution and use in source and binary forms, with or without
16
 * modification, are permitted provided that the following conditions
17
 * are met:
18
 * 1. Redistributions of source code must retain the above copyright
19
 *    notice, this list of conditions and the following disclaimer.
20
 * 2. Redistributions in binary form must reproduce the above copyright
21
 *    notice, this list of conditions and the following disclaimer in the
22
 *    documentation and/or other materials provided with the distribution.
23
 * 3. Neither the name of the University nor the names of its contributors
24
 *    may be used to endorse or promote products derived from this software
25
 *    without specific prior written permission.
26
 *
27
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37
 * SUCH DAMAGE.
38
 */
39
40
#ifdef HAVE_CONFIG_H
41
#include "config.h"
42
#endif
43
44
#ifdef HAVE_SYS_CDEFS_H
45
#include <sys/cdefs.h>
46
#endif
47
48
/*
49
 * Actual printf innards.
50
 *
51
 * This code is large and complicated...
52
 */
53
54
#include <sys/types.h>
55
#include <sys/uio.h>
56
57
#include <ctype.h>
58
#include <errno.h>
59
#include <limits.h>
60
#include <stddef.h>
61
#include <stdint.h>
62
#include <stdio.h>
63
#include <stdlib.h>
64
#include <string.h>
65
#include <wchar.h>
66
67
#include <stdarg.h>
68
69
#include "printflocal.h"
70
71
7.13k
#define CHAR  char
72
#include "printfcommon.h"
73
74
#ifdef WCHAR_SUPPORT
75
/*
76
 * Convert a wide character string argument for the %ls format to a multibyte
77
 * string representation. If not -1, prec specifies the maximum number of
78
 * bytes to output, and also means that we can't assume that the wide char.
79
 * string ends is null-terminated.
80
 */
81
static char *
82
__wcsconv(wchar_t *wcsarg, int prec)
83
{
84
  static const mbstate_t initial;
85
  mbstate_t mbs;
86
  char buf[MB_LEN_MAX];
87
  wchar_t *p;
88
  char *convbuf;
89
  size_t clen, nbytes;
90
91
  /* Allocate space for the maximum number of bytes we could output. */
92
  if (prec < 0) {
93
    p = wcsarg;
94
    mbs = initial;
95
    nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
96
    if (nbytes == (size_t)-1)
97
      return NULL;
98
  } else {
99
    /*
100
     * Optimisation: if the output precision is small enough,
101
     * just allocate enough memory for the maximum instead of
102
     * scanning the string.
103
     */
104
    if (prec < 128)
105
      nbytes = prec;
106
    else {
107
      nbytes = 0;
108
      p = wcsarg;
109
      mbs = initial;
110
      for (;;) {
111
        clen = wcrtomb(buf, *p++, &mbs);
112
        if (clen == 0 || clen == (size_t)-1 ||
113
            nbytes + clen > (size_t)prec)
114
          break;
115
        nbytes += clen;
116
      }
117
    }
118
  }
119
  if ((convbuf = malloc(nbytes + 1)) == NULL)
120
    return NULL;
121
122
  /* Fill the output buffer. */
123
  p = wcsarg;
124
  mbs = initial;
125
  if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
126
      nbytes, &mbs)) == (size_t)-1) {
127
    free(convbuf);
128
    return NULL;
129
  }
130
  convbuf[nbytes] = '\0';
131
  return (convbuf);
132
}
133
#endif /* WCHAR_SUPPORT */
134
135
/*
136
 * The size of the buffer we use as scratch space for integer
137
 * conversions, among other things.  We need enough space to
138
 * write a uintmax_t in octal (plus one byte).
139
 */
140
#if UINTMAX_MAX <= UINT64_MAX
141
28.5k
#define BUF 80
142
#else
143
#error "BUF must be large enough to format a uintmax_t"
144
#endif
145
146
/*
147
 * Non-MT-safe version
148
 */
149
ssize_t vbprintfrr(struct fbuf *cb_in, const char *fmt0, va_list ap)
150
  __attribute__((no_sanitize("unsigned-integer-overflow")))
151
417k
{
152
417k
  const char *fmt;  /* format string */
153
417k
  int ch;     /* character from fmt */
154
417k
  int n, n2;    /* handy integer (short term usage) */
155
417k
  const char *cp;   /* handy char pointer (short term usage) */
156
417k
  int flags;    /* flags as above */
157
417k
  int ret;    /* return value accumulator */
158
417k
  int width;    /* width from format (%8d), or 0 */
159
417k
  int prec;   /* precision from format; <0 for N/A */
160
417k
  int saved_errno;
161
417k
  char sign;    /* sign prefix (' ', '+', '-', or \0) */
162
163
417k
  u_long  ulval = 0;  /* integer arguments %[diouxX] */
164
417k
  uintmax_t ujval = 0;  /* %j, %ll, %q, %t, %z integers */
165
417k
  void *ptrval;   /* %p */
166
417k
  int base;   /* base for [diouxX] conversion */
167
417k
  int dprec;    /* a copy of prec if [diouxX], 0 otherwise */
168
417k
  int realsz;   /* field size expanded by dprec, sign, etc */
169
417k
  int size;   /* size of converted field or string */
170
417k
  int prsize;             /* max size of printed field */
171
417k
  const char *xdigs;      /* digits for %[xX] conversion */
172
417k
  struct io_state io; /* I/O buffering state */
173
417k
  char buf[BUF];    /* buffer with space for digits of uintmax_t */
174
417k
  char ox[2];   /* space for 0x; ox[1] is either x, X, or \0 */
175
417k
  union arg *argtable;    /* args, built due to positional arg */
176
417k
  union arg statargtable [STATIC_ARG_TBL_SIZE];
177
417k
  int nextarg;            /* 1-based argument index */
178
417k
  va_list orgap;          /* original argument pointer */
179
417k
  char *convbuf;    /* wide to multibyte conversion result */
180
417k
  char *extstart = NULL; /* where printfrr_ext* started printing */
181
417k
  struct fbuf cb_copy, *cb;
182
417k
  struct fmt_outpos *opos;
183
184
417k
  static const char xdigs_lower[16] = "0123456789abcdef";
185
417k
  static const char xdigs_upper[16] = "0123456789ABCDEF";
186
187
  /* BEWARE, these `goto error' on error. */
188
417k
#define PRINT(ptr, len) { \
189
400k
  if (io_print(&io, (ptr), (len))) \
190
400k
    goto error; \
191
400k
}
192
417k
#define PAD(howmany, with) { \
193
78.4k
  if (io_pad(&io, (howmany), (with))) \
194
78.4k
    goto error; \
195
78.4k
}
196
417k
#define PRINTANDPAD(p, ep, len, with) { \
197
417k
  if (io_printandpad(&io, (p), (ep), (len), (with))) \
198
417k
    goto error; \
199
417k
}
200
956k
#define FLUSH() do { } while (0)
201
202
  /*
203
   * Get the argument indexed by nextarg.   If the argument table is
204
   * built, use it to get the argument.  If its not, get the next
205
   * argument (and arguments must be gotten sequentially).
206
   */
207
417k
#define GETARG(type) \
208
538k
  ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
209
538k
      (nextarg++, va_arg(ap, type)))
210
211
  /*
212
   * To extend shorts properly, we need both signed and unsigned
213
   * argument extraction methods.
214
   */
215
417k
#define SARG() \
216
417k
  (flags&LONGINT ? GETARG(long) : \
217
5.28k
      flags&SHORTINT ? (long)(short)GETARG(int) : \
218
5.27k
      flags&CHARINT ? (long)(signed char)GETARG(int) : \
219
5.27k
      (long)GETARG(int))
220
417k
#define UARG() \
221
417k
  (flags&LONGINT ? GETARG(u_long) : \
222
1.84k
      flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
223
86
      flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
224
86
      (u_long)GETARG(u_int))
225
417k
#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT|LONGDBL)
226
417k
#define SJARG() \
227
417k
  (flags&LONGDBL ? GETARG(int64_t) : \
228
4
      flags&INTMAXT ? GETARG(intmax_t) : \
229
4
      flags&SIZET ? (intmax_t)GETARG(ssize_t) : \
230
0
      flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
231
0
      (intmax_t)GETARG(long long))
232
417k
#define UJARG() \
233
417k
  (flags&LONGDBL ? GETARG(uint64_t) : \
234
0
      flags&INTMAXT ? GETARG(uintmax_t) : \
235
0
      flags&SIZET ? (uintmax_t)GETARG(size_t) : \
236
0
      flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
237
0
      (uintmax_t)GETARG(unsigned long long))
238
239
  /*
240
   * Get * arguments, including the form *nn$.  Preserve the nextarg
241
   * that the argument can be gotten once the type is determined.
242
   */
243
417k
#define GETASTER(val) \
244
417k
  n2 = 0; \
245
0
  cp = fmt; \
246
0
  while (is_digit(*cp)) { \
247
0
    n2 = 10 * n2 + to_digit(*cp); \
248
0
    cp++; \
249
0
  } \
250
0
  if (*cp == '$') { \
251
0
    int hold = nextarg; \
252
0
    if (argtable == NULL) { \
253
0
      argtable = statargtable; \
254
0
      if (_frr_find_arguments (fmt0, orgap, &argtable)) { \
255
0
        ret = EOF; \
256
0
        goto error; \
257
0
      } \
258
0
    } \
259
0
    nextarg = n2; \
260
0
    val = GETARG (int); \
261
0
    nextarg = hold; \
262
0
    fmt = ++cp; \
263
0
  } else { \
264
0
    val = GETARG (int); \
265
0
  }
266
267
417k
  xdigs = xdigs_lower;
268
417k
  saved_errno = errno;
269
417k
  convbuf = NULL;
270
417k
  fmt = (char *)fmt0;
271
417k
  argtable = NULL;
272
417k
  nextarg = 1;
273
417k
  va_copy(orgap, ap);
274
275
417k
  if (cb_in) {
276
    /* prevent printfrr exts from polluting cb->outpos */
277
417k
    cb_copy = *cb_in;
278
417k
    cb_copy.outpos = NULL;
279
417k
    cb_copy.outpos_n = cb_copy.outpos_i = 0;
280
417k
    cb = &cb_copy;
281
417k
  } else
282
0
    cb = NULL;
283
284
417k
  io_init(&io, cb);
285
417k
  ret = 0;
286
287
  /*
288
   * Scan the format for conversions (`%' character).
289
   */
290
956k
  for (;;) {
291
1.84M
    for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
292
886k
      /* void */;
293
956k
    if ((n = fmt - cp) != 0) {
294
359k
      if ((unsigned)ret + n > INT_MAX) {
295
0
        ret = EOF;
296
0
        errno = EOVERFLOW;
297
0
        goto error;
298
0
      }
299
359k
      PRINT(cp, n);
300
359k
      ret += n;
301
359k
    }
302
956k
    if (ch == '\0')
303
417k
      goto done;
304
538k
    fmt++;    /* skip over '%' */
305
306
538k
    flags = 0;
307
538k
    dprec = 0;
308
538k
    width = -1;
309
538k
    prec = -1;
310
538k
    sign = '\0';
311
538k
    ox[1] = '\0';
312
313
538k
    if (cb_in && cb_in->outpos_i < cb_in->outpos_n)
314
3.37k
      opos = &cb_in->outpos[cb_in->outpos_i];
315
534k
    else
316
534k
      opos = NULL;
317
318
547k
rflag:    ch = *fmt++;
319
552k
reswitch: switch (ch) {
320
0
    case ' ':
321
      /*-
322
       * ``If the space and + flags both appear, the space
323
       * flag will be ignored.''
324
       *  -- ANSI X3J11
325
       */
326
0
      if (!sign)
327
0
        sign = ' ';
328
0
      goto rflag;
329
0
    case '#':
330
0
      flags |= ALT;
331
0
      goto rflag;
332
0
    case '*':
333
      /*-
334
       * ``A negative field width argument is taken as a
335
       * - flag followed by a positive field width.''
336
       *  -- ANSI X3J11
337
       * They don't exclude field widths read from args.
338
       */
339
0
      GETASTER (width);
340
0
      if (width >= 0)
341
0
        goto rflag;
342
0
      width = -width;
343
      /* FALLTHROUGH */
344
0
    case '-':
345
0
      flags |= LADJUST;
346
0
      goto rflag;
347
1.75k
    case '+':
348
1.75k
      sign = '+';
349
1.75k
      goto rflag;
350
0
    case '\'':
351
0
      flags |= GROUPING;
352
0
      goto rflag;
353
0
    case '.':
354
0
      if ((ch = *fmt++) == '*') {
355
0
        GETASTER (prec);
356
0
        goto rflag;
357
0
      }
358
0
      prec = 0;
359
0
      while (is_digit(ch)) {
360
0
        prec = 10 * prec + to_digit(ch);
361
0
        ch = *fmt++;
362
0
      }
363
0
      goto reswitch;
364
5.27k
    case '0':
365
      /*-
366
       * ``Note that 0 is taken as a flag, not as the
367
       * beginning of a field width.''
368
       *  -- ANSI X3J11
369
       */
370
5.27k
      flags |= ZEROPAD;
371
5.27k
      goto rflag;
372
3.51k
    case '1': case '2': case '3': case '4':
373
5.27k
    case '5': case '6': case '7': case '8': case '9':
374
5.27k
      n = 0;
375
5.27k
      do {
376
5.27k
        n = 10 * n + to_digit(ch);
377
5.27k
        ch = *fmt++;
378
5.27k
      } while (is_digit(ch));
379
5.27k
      if (ch == '$') {
380
0
        nextarg = n;
381
0
        if (argtable == NULL) {
382
0
          argtable = statargtable;
383
0
          if (_frr_find_arguments (fmt0, orgap,
384
0
                    &argtable)) {
385
0
            ret = EOF;
386
0
            goto error;
387
0
          }
388
0
        }
389
0
        goto rflag;
390
0
      }
391
5.27k
      width = n;
392
5.27k
      goto reswitch;
393
0
    case 'L':
394
0
      flags |= LONGDBL;
395
0
      goto rflag;
396
0
    case 'h':
397
0
      if (flags & SHORTINT) {
398
0
        flags &= ~SHORTINT;
399
0
        flags |= CHARINT;
400
0
      } else
401
0
        flags |= SHORTINT;
402
0
      goto rflag;
403
4
    case 'j':
404
4
      flags |= INTMAXT;
405
4
      goto rflag;
406
1.76k
    case 'l':
407
1.76k
      if (flags & LONGINT) {
408
0
        flags &= ~LONGINT;
409
0
        flags |= LLONGINT;
410
0
      } else
411
1.76k
        flags |= LONGINT;
412
1.76k
      goto rflag;
413
0
    case 'q':
414
0
      flags |= LLONGINT; /* not necessarily */
415
0
      goto rflag;
416
0
    case 't':
417
0
      flags |= PTRDIFFT;
418
0
      goto rflag;
419
0
    case 'z':
420
0
      flags |= SIZET;
421
0
      goto rflag;
422
0
    case 'C':
423
0
      flags |= LONGINT;
424
      /*FALLTHROUGH*/
425
0
    case 'c':
426
#ifdef WCHAR_SUPPORT
427
      if (flags & LONGINT) {
428
        static const mbstate_t initial;
429
        mbstate_t mbs;
430
        size_t mbseqlen;
431
432
        mbs = initial;
433
        mbseqlen = wcrtomb(cp = buf,
434
            (wchar_t)GETARG(wint_t), &mbs);
435
        if (mbseqlen == (size_t)-1) {
436
          goto error;
437
        }
438
        size = (int)mbseqlen;
439
      } else
440
#endif /* WCHAR_SUPPORT */
441
0
      {
442
0
        buf[0] = GETARG(int);
443
0
        cp = buf;
444
0
        size = 1;
445
0
      }
446
0
      sign = '\0';
447
0
      break;
448
0
    case 'D':
449
0
      flags |= LONGINT;
450
      /*FALLTHROUGH*/
451
5.28k
    case 'd':
452
5.28k
    case 'i':
453
5.28k
      if (flags & INTMAX_SIZE)
454
4
        ujval = SJARG();
455
5.28k
      else
456
5.28k
        ulval = (u_long)SARG();
457
458
5.28k
      if (printfrr_ext_char(fmt[0])) {
459
0
        struct printfrr_eargs ea = {
460
0
          .fmt = fmt,
461
0
          .precision = prec,
462
0
          .width = width,
463
0
          .alt_repr = !!(flags & ALT),
464
0
          .leftadj = !!(flags & LADJUST),
465
0
        };
466
467
0
        if (cb)
468
0
          extstart = cb->pos;
469
470
0
        size = printfrr_exti(cb, &ea,
471
0
            (flags & INTMAX_SIZE) ? ujval
472
0
            : (uintmax_t)ulval);
473
0
        if (size >= 0) {
474
0
          fmt = ea.fmt;
475
0
          width = ea.width;
476
0
          goto ext_printed;
477
0
        }
478
0
      }
479
5.28k
      if (flags & INTMAX_SIZE) {
480
4
        if ((intmax_t)ujval < 0) {
481
0
          ujval = -ujval;
482
0
          sign = '-';
483
0
        }
484
5.28k
      } else {
485
5.28k
        if ((long)ulval < 0) {
486
0
          ulval = (~ulval) + 1;
487
0
          sign = '-';
488
0
        }
489
5.28k
      }
490
5.28k
      base = 10;
491
5.28k
      goto number;
492
0
#ifndef NO_FLOATING_POINT
493
0
    case 'a':
494
0
    case 'A':
495
0
    case 'e':
496
0
    case 'E':
497
0
    case 'f':
498
0
    case 'F':
499
0
    case 'g':
500
0
    case 'G':
501
0
      if (flags & LONGDBL) {
502
0
        long double arg = GETARG(long double);
503
0
        char fmt[6] = "%.*L";
504
0
        fmt[4] = ch;
505
0
        fmt[5] = '\0';
506
507
0
#pragma GCC diagnostic push
508
0
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
509
0
        snprintf(buf, sizeof(buf), fmt, prec, arg);
510
0
#pragma GCC diagnostic pop
511
0
      } else {
512
0
        double arg = GETARG(double);
513
0
        char fmt[5] = "%.*";
514
0
        fmt[3] = ch;
515
0
        fmt[4] = '\0';
516
517
0
#pragma GCC diagnostic push
518
0
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
519
0
        snprintf(buf, sizeof(buf), fmt, prec, arg);
520
0
#pragma GCC diagnostic pop
521
0
      }
522
0
      cp = buf;
523
      /* for proper padding */
524
0
      if (*cp == '-') {
525
0
        cp++;
526
0
        sign = '-';
527
0
      }
528
      /* "inf" */
529
0
      if (!is_digit(*cp) && *cp != '.')
530
0
        flags &= ~ZEROPAD;
531
0
      size = strlen(buf);
532
0
      break;
533
0
#endif
534
0
    case 'm':
535
0
      cp = strerror(saved_errno);
536
0
      size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
537
0
      sign = '\0';
538
0
      break;
539
0
    case 'O':
540
0
      flags |= LONGINT;
541
      /*FALLTHROUGH*/
542
0
    case 'o':
543
0
      if (flags & INTMAX_SIZE)
544
0
        ujval = UJARG();
545
0
      else
546
0
        ulval = UARG();
547
0
      base = 8;
548
0
      goto nosign;
549
499k
    case 'p':
550
      /*-
551
       * ``The argument shall be a pointer to void.  The
552
       * value of the pointer is converted to a sequence
553
       * of printable characters, in an implementation-
554
       * defined manner.''
555
       *  -- ANSI X3J11
556
       */
557
499k
      ptrval = GETARG(void *);
558
499k
      if (printfrr_ext_char(fmt[0])) {
559
499k
        struct printfrr_eargs ea = {
560
499k
          .fmt = fmt,
561
499k
          .precision = prec,
562
499k
          .width = width,
563
499k
          .alt_repr = !!(flags & ALT),
564
499k
          .leftadj = !!(flags & LADJUST),
565
499k
        };
566
567
499k
        if (cb)
568
499k
          extstart = cb->pos;
569
570
499k
        size = printfrr_extp(cb, &ea, ptrval);
571
499k
        if (size >= 0) {
572
499k
          fmt = ea.fmt;
573
499k
          width = ea.width;
574
499k
          goto ext_printed;
575
499k
        }
576
499k
      }
577
0
      ujval = (uintmax_t)(uintptr_t)ptrval;
578
0
      base = 16;
579
0
      xdigs = xdigs_lower;
580
0
      flags = flags | INTMAXT;
581
0
      ox[1] = 'x';
582
0
      goto nosign;
583
0
    case 'S':
584
0
      flags |= LONGINT;
585
      /*FALLTHROUGH*/
586
32.1k
    case 's':
587
#ifdef WCHAR_SUPPORT
588
      if (flags & LONGINT) {
589
        wchar_t *wcp;
590
591
        if (convbuf != NULL)
592
          free(convbuf);
593
        if ((wcp = GETARG(wchar_t *)) == NULL)
594
          cp = "(null)";
595
        else {
596
          convbuf = __wcsconv(wcp, prec);
597
          if (convbuf == NULL) {
598
            goto error;
599
          }
600
          cp = convbuf;
601
        }
602
      } else
603
#endif
604
32.1k
      if ((cp = GETARG(char *)) == NULL)
605
0
        cp = "(null)";
606
32.1k
      size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
607
32.1k
      sign = '\0';
608
32.1k
      break;
609
0
    case 'U':
610
0
      flags |= LONGINT;
611
      /*FALLTHROUGH*/
612
1.84k
    case 'u':
613
1.84k
      if (flags & INTMAX_SIZE)
614
0
        ujval = UJARG();
615
1.84k
      else
616
1.84k
        ulval = UARG();
617
1.84k
      base = 10;
618
1.84k
      goto nosign;
619
0
    case 'X':
620
0
      xdigs = xdigs_upper;
621
0
      goto hex;
622
0
    case 'x':
623
0
      xdigs = xdigs_lower;
624
0
hex:
625
0
      if (flags & INTMAX_SIZE)
626
0
        ujval = UJARG();
627
0
      else
628
0
        ulval = UARG();
629
0
      base = 16;
630
      /* leading 0x/X only if non-zero */
631
0
      if (flags & ALT &&
632
0
          (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
633
0
        ox[1] = ch;
634
635
0
      flags &= ~GROUPING;
636
      /* unsigned conversions */
637
1.84k
nosign:     sign = '\0';
638
      /*-
639
       * ``... diouXx conversions ... if a precision is
640
       * specified, the 0 flag will be ignored.''
641
       *  -- ANSI X3J11
642
       */
643
7.12k
number:     if ((dprec = prec) >= 0)
644
0
        flags &= ~ZEROPAD;
645
646
      /*-
647
       * ``The result of converting a zero value with an
648
       * explicit precision of zero is no characters.''
649
       *  -- ANSI X3J11
650
       *
651
       * ``The C Standard is clear enough as is.  The call
652
       * printf("%#.0o", 0) should print 0.''
653
       *  -- Defect Report #151
654
       */
655
7.12k
      cp = buf + BUF;
656
7.12k
      if (flags & INTMAX_SIZE) {
657
4
        if (ujval != 0 || prec != 0 ||
658
0
            (flags & ALT && base == 8))
659
4
          cp = __ujtoa(ujval, buf + BUF, base,
660
4
              flags & ALT, xdigs);
661
7.12k
      } else {
662
7.12k
        if (ulval != 0 || prec != 0 ||
663
0
            (flags & ALT && base == 8))
664
7.12k
          cp = __ultoa(ulval, buf + BUF, base,
665
7.12k
              flags & ALT, xdigs);
666
7.12k
      }
667
7.12k
      size = buf + BUF - cp;
668
7.12k
      if (size > BUF)  /* should never happen */
669
0
        abort();
670
7.12k
      break;
671
7.12k
    default:  /* "%?" prints ?, unless ? is NUL */
672
0
      if (ch == '\0')
673
0
        goto done;
674
      /* pretend it was %c with argument ch */
675
0
      buf[0] = ch;
676
0
      cp = buf;
677
0
      size = 1;
678
0
      sign = '\0';
679
0
      opos = NULL;
680
0
      break;
681
552k
    }
682
683
    /*
684
     * All reasonable formats wind up here.  At this point, `cp'
685
     * points to a string which (if not flags&LADJUST) should be
686
     * padded out to `width' places.  If flags&ZEROPAD, it should
687
     * first be prefixed by any sign or other prefix; otherwise,
688
     * it should be blank padded before the prefix is emitted.
689
     * After any left-hand padding and prefixing, emit zeroes
690
     * required by a decimal [diouxX] precision, then print the
691
     * string proper, then emit zeroes required by any leftover
692
     * floating precision; finally, if LADJUST, pad with blanks.
693
     *
694
     * Compute actual size, so we know how much to pad.
695
     * size excludes decimal prec; realsz includes it.
696
     */
697
39.2k
    if (width < 0)
698
33.9k
      width = 0;
699
700
39.2k
    realsz = dprec > size ? dprec : size;
701
39.2k
    if (sign)
702
1.75k
      realsz++;
703
39.2k
    if (ox[1])
704
0
      realsz += 2;
705
706
39.2k
    prsize = width > realsz ? width : realsz;
707
39.2k
    if ((unsigned int)ret + prsize > INT_MAX) {
708
0
      ret = EOF;
709
0
      errno = EOVERFLOW;
710
0
      goto error;
711
0
    }
712
713
    /* right-adjusting blank padding */
714
39.2k
    if ((flags & (LADJUST|ZEROPAD)) == 0)
715
39.2k
      PAD(width - realsz, blanks);
716
717
39.2k
    if (opos)
718
3.37k
      opos->off_start = cb->pos - cb->buf;
719
720
    /* prefix */
721
39.2k
    if (sign)
722
39.2k
      PRINT(&sign, 1);
723
724
39.2k
    if (ox[1]) { /* ox[1] is either x, X, or \0 */
725
0
      ox[0] = '0';
726
0
      PRINT(ox, 2);
727
0
    }
728
729
    /* right-adjusting zero padding */
730
39.2k
    if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
731
39.2k
      PAD(width - realsz, zeroes);
732
733
    /* the string or number proper */
734
    /* leading zeroes from decimal precision */
735
39.2k
    PAD(dprec - size, zeroes);
736
39.2k
    PRINT(cp, size);
737
738
39.2k
    if (opos) {
739
3.37k
      opos->off_end = cb->pos - cb->buf;
740
3.37k
      cb_in->outpos_i++;
741
3.37k
    }
742
743
    /* left-adjusting padding (always blank) */
744
39.2k
    if (flags & LADJUST)
745
39.2k
      PAD(width - realsz, blanks);
746
747
    /* finally, adjust ret */
748
39.2k
    ret += prsize;
749
750
39.2k
    FLUSH(); /* copy out the I/O vectors */
751
39.2k
    continue;
752
753
499k
ext_printed:
754
    /* when we arrive here, a printfrr extension has written to cb
755
     * (if non-NULL), but we still need to handle padding.  The
756
     * original cb->pos is in extstart;  the return value from the
757
     * ext is in size.
758
     *
759
     * Keep analogous to code above please.
760
     */
761
762
499k
    if (width < 0)
763
499k
      width = 0;
764
765
499k
    realsz = size;
766
499k
    prsize = width > realsz ? width : realsz;
767
499k
    if ((unsigned int)ret + prsize > INT_MAX) {
768
0
      ret = EOF;
769
0
      errno = EOVERFLOW;
770
0
      goto error;
771
0
    }
772
773
    /* right-adjusting blank padding - need to move the chars
774
     * that the extension has already written.  Should be very
775
     * rare.
776
     */
777
499k
    if (cb && width > size && (flags & (LADJUST|ZEROPAD)) == 0) {
778
0
      size_t nwritten = cb->pos - extstart;
779
0
      size_t navail = cb->buf + cb->len - extstart;
780
0
      size_t npad = width - realsz;
781
0
      size_t nmove;
782
783
0
      if (navail < npad)
784
0
        navail = 0;
785
0
      else
786
0
        navail -= npad;
787
0
      nmove = MIN(nwritten, navail);
788
789
0
      memmove(extstart + npad, extstart, nmove);
790
791
0
      cb->pos = extstart;
792
0
      PAD(npad, blanks);
793
0
      cb->pos += nmove;
794
0
      extstart += npad;
795
0
    }
796
797
499k
    io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0;
798
799
499k
    if (opos && extstart <= cb->pos) {
800
0
      opos->off_start = extstart - cb->buf;
801
0
      opos->off_end = cb->pos - cb->buf;
802
0
      cb_in->outpos_i++;
803
0
    }
804
805
    /* left-adjusting padding (always blank) */
806
499k
    if (flags & LADJUST)
807
499k
      PAD(width - realsz, blanks);
808
809
    /* finally, adjust ret */
810
499k
    ret += prsize;
811
812
499k
    FLUSH(); /* copy out the I/O vectors */
813
499k
  }
814
417k
done:
815
417k
  FLUSH();
816
417k
error:
817
417k
  va_end(orgap);
818
417k
  if (convbuf != NULL)
819
0
    free(convbuf);
820
417k
  if ((argtable != NULL) && (argtable != statargtable))
821
0
    free (argtable);
822
417k
  if (cb_in)
823
417k
    cb_in->pos = cb->pos;
824
417k
  return (ret);
825
  /* NOTREACHED */
826
417k
}
827