diff options
| author | David Lamparter <equinox@diac24.net> | 2021-02-18 22:52:23 +0100 |
|---|---|---|
| committer | David Lamparter <equinox@diac24.net> | 2021-03-27 16:56:55 +0100 |
| commit | 212e04e5a7f682594dab26cd8354bc4fa040b61f (patch) | |
| tree | 59a09a812b868332f073985494ee0a61910da9fd /lib/printf/vfprintf.c | |
| parent | bcf9d7d8aa77c66ac2e99f75ae11e276accecb1d (diff) | |
lib: rework printfrr extensions to output directly
Allowing printfrr extensions to directly write to the output buffer has
a few advantages:
- there is no arbitrary length limit imposed (previously 64)
- the output doesn't need to be copied another time
- the extension can directly use bprintfrr() to put together pieces
The downside is that the theoretical length (regardless of available
buffer space) must be computed correctly.
Extended unit tests to test these paths a bit more thoroughly.
Signed-off-by: David Lamparter <equinox@diac24.net>
Diffstat (limited to 'lib/printf/vfprintf.c')
| -rw-r--r-- | lib/printf/vfprintf.c | 84 |
1 files changed, 67 insertions, 17 deletions
diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index a0634cde4b..3b5bda965a 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -177,6 +177,7 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ char *convbuf; /* wide to multibyte conversion result */ + char *extstart = NULL; /* where printfrr_ext* started printing */ static const char xdigs_lower[16] = "0123456789abcdef"; static const char xdigs_upper[16] = "0123456789ABCDEF"; @@ -438,16 +439,14 @@ reswitch: switch (ch) { ulval = SARG(); if (printfrr_ext_char(fmt[0])) { - n2 = printfrr_exti(buf, sizeof(buf), fmt, prec, + if (cb) + extstart = cb->pos; + + size = printfrr_exti(cb, &fmt, prec, (flags & INTMAX_SIZE) ? ujval : (uintmax_t)ulval); - if (n2 > 0) { - fmt += n2; - cp = buf; - size = strlen(cp); - sign = '\0'; - break; - } + if (size >= 0) + goto ext_printed; } if (flags & INTMAX_SIZE) { if ((intmax_t)ujval < 0) { @@ -551,14 +550,13 @@ reswitch: switch (ch) { * -- ANSI X3J11 */ ptrval = GETARG(void *); - if (printfrr_ext_char(fmt[0]) && - (n2 = printfrr_extp(buf, sizeof(buf), - fmt, prec, ptrval)) > 0) { - fmt += n2; - cp = buf; - size = strlen(cp); - sign = '\0'; - break; + if (printfrr_ext_char(fmt[0])) { + if (cb) + extstart = cb->pos; + + size = printfrr_extp(cb, &fmt, prec, ptrval); + if (size >= 0) + goto ext_printed; } ujval = (uintmax_t)(uintptr_t)ptrval; base = 16; @@ -686,7 +684,7 @@ number: if ((dprec = prec) >= 0) realsz += 2; prsize = width > realsz ? width : realsz; - if ((unsigned)ret + prsize > INT_MAX) { + if ((unsigned int)ret + prsize > INT_MAX) { ret = EOF; errno = EOVERFLOW; goto error; @@ -721,6 +719,58 @@ number: if ((dprec = prec) >= 0) ret += prsize; FLUSH(); /* copy out the I/O vectors */ + continue; + +ext_printed: + /* when we arrive here, a printfrr extension has written to cb + * (if non-NULL), but we still need to handle padding. The + * original cb->pos is in extstart; the return value from the + * ext is in size. + * + * Keep analogous to code above please. + */ + + realsz = size; + prsize = width > realsz ? width : realsz; + if ((unsigned int)ret + prsize > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + + /* right-adjusting blank padding - need to move the chars + * that the extension has already written. Should be very + * rare. + */ + if (cb && width > size && (flags & (LADJUST|ZEROPAD)) == 0) { + size_t nwritten = cb->pos - extstart; + size_t navail = cb->buf + cb->len - extstart; + size_t npad = width - realsz; + size_t nmove; + + if (navail < npad) + navail = 0; + else + navail -= npad; + nmove = MIN(nwritten, navail); + + memmove(extstart + npad, extstart, nmove); + + cb->pos = extstart; + PAD(npad, blanks); + cb->pos += nmove; + } + + io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0; + + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += prsize; + + FLUSH(); /* copy out the I/O vectors */ } done: FLUSH(); |
