lib: record output positions in printfrr

This replaces `%n` with a safe, out-of-band option that simply records
the start and end offset of the output produced for each `%...`
specifier.

The old `%n` code is removed.

Signed-off-by: David Lamparter <equinox@diac24.net>
This commit is contained in:
David Lamparter 2021-03-02 20:16:18 +01:00
parent eba599a397
commit 487eefcfbe
3 changed files with 64 additions and 32 deletions

View file

@ -147,7 +147,7 @@ __wcsconv(wchar_t *wcsarg, int prec)
* Non-MT-safe version
*/
ssize_t
vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
vbprintfrr(struct fbuf *cb_in, const char *fmt0, va_list ap)
{
const char *fmt; /* format string */
int ch; /* character from fmt */
@ -178,6 +178,8 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
va_list orgap; /* original argument pointer */
char *convbuf; /* wide to multibyte conversion result */
char *extstart = NULL; /* where printfrr_ext* started printing */
struct fbuf cb_copy, *cb;
struct fmt_outpos *opos;
static const char xdigs_lower[16] = "0123456789abcdef";
static const char xdigs_upper[16] = "0123456789ABCDEF";
@ -269,6 +271,16 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
argtable = NULL;
nextarg = 1;
va_copy(orgap, ap);
if (cb_in) {
/* prevent printfrr exts from polluting cb->outpos */
cb_copy = *cb_in;
cb_copy.outpos = NULL;
cb_copy.outpos_n = cb_copy.outpos_i = 0;
cb = &cb_copy;
} else
cb = NULL;
io_init(&io, cb);
ret = 0;
@ -298,6 +310,11 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
sign = '\0';
ox[1] = '\0';
if (cb_in && cb_in->outpos_i < cb_in->outpos_n)
opos = &cb_in->outpos[cb_in->outpos_i];
else
opos = NULL;
rflag: ch = *fmt++;
reswitch: switch (ch) {
case ' ':
@ -502,35 +519,6 @@ reswitch: switch (ch) {
size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
sign = '\0';
break;
#ifdef DANGEROUS_PERCENT_N
/* FRR does not use %n in printf formats. This is just left
* here in case someone tries to use %n and starts debugging
* why the f* it doesn't work
*/
case 'n':
/*
* Assignment-like behavior is specified if the
* value overflows or is otherwise unrepresentable.
* C99 says to use `signed char' for %hhn conversions.
*/
if (flags & LLONGINT)
*GETARG(long long *) = ret;
else if (flags & SIZET)
*GETARG(ssize_t *) = (ssize_t)ret;
else if (flags & PTRDIFFT)
*GETARG(ptrdiff_t *) = ret;
else if (flags & INTMAXT)
*GETARG(intmax_t *) = ret;
else if (flags & LONGINT)
*GETARG(long *) = ret;
else if (flags & SHORTINT)
*GETARG(short *) = ret;
else if (flags & CHARINT)
*GETARG(signed char *) = ret;
else
*GETARG(int *) = ret;
continue; /* no output */
#endif
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
@ -660,6 +648,7 @@ number: if ((dprec = prec) >= 0)
cp = buf;
size = 1;
sign = '\0';
opos = NULL;
break;
}
@ -694,6 +683,9 @@ number: if ((dprec = prec) >= 0)
if ((flags & (LADJUST|ZEROPAD)) == 0)
PAD(width - realsz, blanks);
if (opos)
opos->off_start = cb->pos - cb->buf;
/* prefix */
if (sign)
PRINT(&sign, 1);
@ -711,6 +703,12 @@ number: if ((dprec = prec) >= 0)
/* leading zeroes from decimal precision */
PAD(dprec - size, zeroes);
PRINT(cp, size);
if (opos) {
opos->off_end = cb->pos - cb->buf;
cb_in->outpos_i++;
}
/* left-adjusting padding (always blank) */
if (flags & LADJUST)
PAD(width - realsz, blanks);
@ -759,10 +757,17 @@ ext_printed:
cb->pos = extstart;
PAD(npad, blanks);
cb->pos += nmove;
extstart += npad;
}
io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0;
if (opos && extstart <= cb->pos) {
opos->off_start = extstart - cb->buf;
opos->off_end = cb->pos - cb->buf;
cb_in->outpos_i++;
}
/* left-adjusting padding (always blank) */
if (flags & LADJUST)
PAD(width - realsz, blanks);
@ -780,6 +785,8 @@ error:
free(convbuf);
if ((argtable != NULL) && (argtable != statargtable))
free (argtable);
if (cb_in)
cb_in->pos = cb->pos;
return (ret);
/* NOTREACHED */
}

View file

@ -28,10 +28,17 @@
extern "C" {
#endif
struct fmt_outpos {
unsigned int off_start, off_end;
};
struct fbuf {
char *buf;
char *pos;
size_t len;
struct fmt_outpos *outpos;
size_t outpos_n, outpos_i;
};
#define at(a, b) PRINTFRR(a, b)

View file

@ -95,11 +95,21 @@ static void printchk(const char *ref, const char *fmt, ...)
errors++;
}
struct fmt_outpos outpos[16];
struct fbuf fb = {
.buf = bufrr,
.pos = bufrr,
.len = sizeof(bufrr) - 1,
.outpos = outpos,
.outpos_n = array_size(outpos),
};
va_start(ap, fmt);
vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
vbprintfrr(&fb, fmt, ap);
fb.pos[0] = '\0';
va_end(ap);
printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n\n",
printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n",
fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
if (strcmp(ref, bufrr))
errors++;
@ -107,6 +117,14 @@ static void printchk(const char *ref, const char *fmt, ...)
printf("return value <> length mismatch\n");
errors++;
}
for (size_t i = 0; i < fb.outpos_i; i++)
printf("\t[%zu: %u..%u] = \"%.*s\"\n", i,
outpos[i].off_start,
outpos[i].off_end,
(int)(outpos[i].off_end - outpos[i].off_start),
bufrr + outpos[i].off_start);
printf("\n");
}
int main(int argc, char **argv)