summaryrefslogtreecommitdiff
path: root/lib/printf
diff options
context:
space:
mode:
authorMark Stapp <mjs@voltanet.io>2021-03-31 09:10:30 -0400
committerGitHub <noreply@github.com>2021-03-31 09:10:30 -0400
commite2efe13327adefb655811bdb03bb76b95db2407c (patch)
treeb9fa898013c23be16c8a50ee477af017ab52220a /lib/printf
parentfb639375cb2ca062f350c56c51367f2d8d5b2514 (diff)
parent19b1a1c6a975f772b22dda9c5c42c6288e2ce459 (diff)
Merge pull request #8350 from opensourcerouting/printfrr-revamp
lib: `printfrr()` care package
Diffstat (limited to 'lib/printf')
-rw-r--r--lib/printf/glue.c60
-rw-r--r--lib/printf/printflocal.h8
-rw-r--r--lib/printf/vfprintf.c179
3 files changed, 189 insertions, 58 deletions
diff --git a/lib/printf/glue.c b/lib/printf/glue.c
index 29ca26ad5d..1147901236 100644
--- a/lib/printf/glue.c
+++ b/lib/printf/glue.c
@@ -210,15 +210,16 @@ void printfrr_ext_reg(const struct printfrr_ext *ext)
exts[i] = ext;
}
-ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
+ssize_t printfrr_extp(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
+ const char *fmt = ea->fmt;
const struct printfrr_ext *ext;
size_t i;
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
- return 0;
+ return -1;
if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
continue;
ext = exts[i];
@@ -226,20 +227,22 @@ ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
continue;
if (strncmp(ext->match, fmt, strlen(ext->match)))
continue;
- return ext->print_ptr(buf, sz, fmt, prec, ptr);
+ ea->fmt += strlen(ext->match);
+ return ext->print_ptr(buf, ea, ptr);
}
- return 0;
+ return -1;
}
-ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
+ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea,
uintmax_t num)
{
+ const char *fmt = ea->fmt;
const struct printfrr_ext *ext;
size_t i;
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
- return 0;
+ return -1;
if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
continue;
ext = exts[i];
@@ -247,7 +250,48 @@ ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
continue;
if (strncmp(ext->match, fmt, strlen(ext->match)))
continue;
- return ext->print_int(buf, sz, fmt, prec, num);
+ ea->fmt += strlen(ext->match);
+ return ext->print_int(buf, ea, num);
}
- return 0;
+ return -1;
+}
+
+printfrr_ext_autoreg_p("FB", printfrr_fb)
+static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ const struct fbuf *in = ptr;
+ ptrdiff_t copy_len;
+
+ if (!in)
+ return bputs(out, "NULL");
+
+ if (out) {
+ copy_len = MIN(in->pos - in->buf,
+ out->buf + out->len - out->pos);
+ if (copy_len > 0) {
+ memcpy(out->pos, in->buf, copy_len);
+ out->pos += copy_len;
+ }
+ }
+
+ return in->pos - in->buf;
+}
+
+printfrr_ext_autoreg_p("VA", printfrr_va)
+static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ const struct va_format *vaf = ptr;
+ va_list ap;
+
+ if (!vaf || !vaf->fmt || !vaf->va)
+ return bputs(buf, "NULL");
+
+ /* make sure we don't alter the data passed in - especially since
+ * bprintfrr (and thus this) might be called on the same format twice,
+ * when allocating a larger buffer in asnprintfrr()
+ */
+ va_copy(ap, *vaf->va);
+ return vbprintfrr(buf, vaf->fmt, ap);
}
diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h
index 335e09872e..bac80e801c 100644
--- a/lib/printf/printflocal.h
+++ b/lib/printf/printflocal.h
@@ -100,6 +100,8 @@ int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL;
int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL;
#endif
-/* returns number of bytes consumed for extended specifier */
-ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL;
-ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL;
+/* returns number of bytes needed for full output, or -1 */
+ssize_t printfrr_extp(struct fbuf *, struct printfrr_eargs *ea, const void *)
+ DSO_LOCAL;
+ssize_t printfrr_exti(struct fbuf *, struct printfrr_eargs *ea, uintmax_t)
+ DSO_LOCAL;
diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c
index 8c7a8a58c4..49fa2b718f 100644
--- a/lib/printf/vfprintf.c
+++ b/lib/printf/vfprintf.c
@@ -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 */
@@ -177,6 +177,9 @@ 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 */
+ struct fbuf cb_copy, *cb;
+ struct fmt_outpos *opos;
static const char xdigs_lower[16] = "0123456789abcdef";
static const char xdigs_upper[16] = "0123456789ABCDEF";
@@ -268,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;
@@ -292,11 +305,16 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
flags = 0;
dprec = 0;
- width = 0;
+ width = -1;
prec = -1;
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 ' ':
@@ -438,15 +456,24 @@ reswitch: switch (ch) {
ulval = SARG();
if (printfrr_ext_char(fmt[0])) {
- n2 = printfrr_exti(buf, sizeof(buf), fmt, prec,
+ struct printfrr_eargs ea = {
+ .fmt = fmt,
+ .precision = prec,
+ .width = width,
+ .alt_repr = !!(flags & ALT),
+ .leftadj = !!(flags & LADJUST),
+ };
+
+ if (cb)
+ extstart = cb->pos;
+
+ size = printfrr_exti(cb, &ea,
(flags & INTMAX_SIZE) ? ujval
: (uintmax_t)ulval);
- if (n2 > 0) {
- fmt += n2;
- cp = buf;
- size = strlen(cp);
- sign = '\0';
- break;
+ if (size >= 0) {
+ fmt = ea.fmt;
+ width = ea.width;
+ goto ext_printed;
}
}
if (flags & INTMAX_SIZE) {
@@ -503,35 +530,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*/
@@ -551,14 +549,24 @@ 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])) {
+ struct printfrr_eargs ea = {
+ .fmt = fmt,
+ .precision = prec,
+ .width = width,
+ .alt_repr = !!(flags & ALT),
+ .leftadj = !!(flags & LADJUST),
+ };
+
+ if (cb)
+ extstart = cb->pos;
+
+ size = printfrr_extp(cb, &ea, ptrval);
+ if (size >= 0) {
+ fmt = ea.fmt;
+ width = ea.width;
+ goto ext_printed;
+ }
}
ujval = (uintmax_t)(uintptr_t)ptrval;
base = 16;
@@ -662,6 +670,7 @@ number: if ((dprec = prec) >= 0)
cp = buf;
size = 1;
sign = '\0';
+ opos = NULL;
break;
}
@@ -679,6 +688,9 @@ number: if ((dprec = prec) >= 0)
* Compute actual size, so we know how much to pad.
* size excludes decimal prec; realsz includes it.
*/
+ if (width < 0)
+ width = 0;
+
realsz = dprec > size ? dprec : size;
if (sign)
realsz++;
@@ -686,7 +698,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;
@@ -696,6 +708,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);
@@ -713,6 +728,74 @@ 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);
+
+ /* finally, adjust ret */
+ 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.
+ */
+
+ if (width < 0)
+ width = 0;
+
+ 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;
+ 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);
@@ -730,6 +813,8 @@ error:
free(convbuf);
if ((argtable != NULL) && (argtable != statargtable))
free (argtable);
+ if (cb_in)
+ cb_in->pos = cb->pos;
return (ret);
/* NOTREACHED */
}