}
printfrr_ext_autoreg_p("BD", printfrr_bd)
-static ssize_t printfrr_bd(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_bd(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const struct bgp_dest *dest = ptr;
- const struct prefix *p;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+ char cbuf[PREFIX_STRLEN];
- if (dest) {
- p = bgp_dest_get_prefix(dest);
- prefix2str(p, buf, bsz);
- } else {
- strlcpy(buf, "NULL", bsz);
- }
+ if (!dest)
+ return bputs(buf, "NULL");
- return 2;
+ /* need to get the real length even if buffer too small */
+ prefix2str(p, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
* nexthop2str()
*/
printfrr_ext_autoreg_p("NH", printfrr_nh)
-static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_nh(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const struct nexthop *nexthop = ptr;
- struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
bool do_ifi = false;
- const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
- ssize_t ret = 3;
+ const char *v_is = "", *v_via = "", *v_viaif = "via ";
+ ssize_t ret = 0;
- /* NULL-check */
- if (nexthop == NULL) {
- if (fmt[2] == 'v' && fmt[3] == 'v')
- ret++;
-
- strlcpy(buf, "NULL", bsz);
-
- return ret;
- }
-
- switch (fmt[2]) {
+ switch (**fmt) {
case 'v':
- if (fmt[3] == 'v') {
+ (*fmt)++;
+ if (**fmt == 'v') {
v_is = "is ";
v_via = "via ";
v_viaif = "";
- ret++;
+ (*fmt)++;
}
+ if (!nexthop)
+ return bputs(buf, "NULL");
+
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
- bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4);
+ ret += bprintfrr(buf, "%s%pI4", v_via,
+ &nexthop->gate.ipv4);
do_ifi = true;
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
- bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6);
+ ret += bprintfrr(buf, "%s%pI6", v_via,
+ &nexthop->gate.ipv6);
do_ifi = true;
break;
case NEXTHOP_TYPE_IFINDEX:
- bprintfrr(&fb, "%sdirectly connected, %s", v_is,
- ifindex2ifname(nexthop->ifindex,
- nexthop->vrf_id));
+ ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
+ ret += bputs(buf, "unreachable");
+
switch (nexthop->bh_type) {
case BLACKHOLE_REJECT:
- s = " (ICMP unreachable)";
+ ret += bputs(buf, " (ICMP unreachable)");
break;
case BLACKHOLE_ADMINPROHIB:
- s = " (ICMP admin-prohibited)";
+ ret += bputs(buf, " (ICMP admin-prohibited)");
break;
case BLACKHOLE_NULL:
- s = " (blackhole)";
+ ret += bputs(buf, " (blackhole)");
break;
default:
- s = "";
break;
}
- bprintfrr(&fb, "unreachable%s", s);
break;
default:
break;
}
if (do_ifi && nexthop->ifindex)
- bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
- nexthop->ifindex,
- nexthop->vrf_id));
+ ret += bprintfrr(buf, ", %s%s", v_viaif,
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
- *fb.pos = '\0';
return ret;
case 's':
- nexthop2str(nexthop, buf, bsz);
- return 3;
+ (*fmt)++;
+
+ if (!nexthop)
+ return bputs(buf, "NULL");
+
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ ret += bprintfrr(buf, "if %u", nexthop->ifindex);
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4,
+ nexthop->ifindex);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6,
+ nexthop->ifindex);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ ret += bputs(buf, "blackhole");
+ break;
+ default:
+ ret += bputs(buf, "unknown");
+ break;
+ }
+ return ret;
}
- return 0;
+ return -1;
}
}
printfrr_ext_autoreg_p("EA", printfrr_ea)
-static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_ea(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const struct ethaddr *mac = ptr;
+ char cbuf[ETHER_ADDR_STRLEN];
- if (mac)
- prefix_mac2str(mac, buf, bsz);
- else
- strlcpy(buf, "NULL", bsz);
+ if (!mac)
+ return bputs(buf, "NULL");
- return 2;
+ /* need real length even if buffer is too short */
+ prefix_mac2str(mac, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("IA", printfrr_ia)
-static ssize_t printfrr_ia(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_ia(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const struct ipaddr *ipa = ptr;
+ char cbuf[INET6_ADDRSTRLEN];
- if (ipa)
- ipaddr2str(ipa, buf, bsz);
- else
- strlcpy(buf, "NULL", bsz);
+ if (!ipa)
+ return bputs(buf, "NULL");
- return 2;
+ ipaddr2str(ipa, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("I4", printfrr_i4)
-static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_i4(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
- if (ptr)
- inet_ntop(AF_INET, ptr, buf, bsz);
- else
- strlcpy(buf, "NULL", bsz);
+ char cbuf[INET_ADDRSTRLEN];
+
+ if (!ptr)
+ return bputs(buf, "NULL");
- return 2;
+ inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("I6", printfrr_i6)
-static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_i6(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
- if (ptr)
- inet_ntop(AF_INET6, ptr, buf, bsz);
- else
- strlcpy(buf, "NULL", bsz);
+ char cbuf[INET6_ADDRSTRLEN];
+
+ if (!ptr)
+ return bputs(buf, "NULL");
- return 2;
+ inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("FX", printfrr_pfx)
-static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_pfx(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
- if (ptr)
- prefix2str(ptr, buf, bsz);
- else
- strlcpy(buf, "NULL", bsz);
+ char cbuf[PREFIX_STRLEN];
+
+ if (!ptr)
+ return bputs(buf, "NULL");
- return 2;
+ prefix2str(ptr, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
printfrr_ext_autoreg_p("SG4", printfrr_psg)
-static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_psg(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const struct prefix_sg *sg = ptr;
- struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
+ ssize_t ret = 0;
- if (sg) {
- if (sg->src.s_addr == INADDR_ANY)
- bprintfrr(&fb, "(*,");
- else
- bprintfrr(&fb, "(%pI4,", &sg->src);
-
- if (sg->grp.s_addr == INADDR_ANY)
- bprintfrr(&fb, "*)");
- else
- bprintfrr(&fb, "%pI4)", &sg->grp);
+ if (!sg)
+ return bputs(buf, "NULL");
- fb.pos[0] = '\0';
+ if (sg->src.s_addr == INADDR_ANY)
+ ret += bputs(buf, "(*,");
+ else
+ ret += bprintfrr(buf, "(%pI4,", &sg->src);
- } else {
- strlcpy(buf, "NULL", bsz);
- }
+ if (sg->grp.s_addr == INADDR_ANY)
+ ret += bputs(buf, "*)");
+ else
+ ret += bprintfrr(buf, "%pI4)", &sg->grp);
- return 3;
+ return ret;
}
exts[i] = ext;
}
-ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
+ssize_t printfrr_extp(struct fbuf *buf, const char **fmtp, int prec,
const void *ptr)
{
+ const char *fmt = *fmtp;
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];
continue;
if (strncmp(ext->match, fmt, strlen(ext->match)))
continue;
- return ext->print_ptr(buf, sz, fmt, prec, ptr);
+ *fmtp += strlen(ext->match);
+ return ext->print_ptr(buf, fmtp, prec, 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, const char **fmtp, int prec,
uintmax_t num)
{
+ const char *fmt = *fmtp;
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];
continue;
if (strncmp(ext->match, fmt, strlen(ext->match)))
continue;
- return ext->print_int(buf, sz, fmt, prec, num);
+ *fmtp += strlen(ext->match);
+ return ext->print_int(buf, fmtp, prec, num);
}
- return 0;
+ return -1;
}
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 *, const char **, int, const void *)
+ DSO_LOCAL;
+ssize_t printfrr_exti(struct fbuf *, const char **, int, uintmax_t) DSO_LOCAL;
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";
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) {
* -- 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;
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;
/* 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.
+ */
+
+ 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:
/* both can be given, if not the code continues searching
* (you can do %pX and %dX in 2 different entries)
*
- * return value: number of bytes consumed from the format string, so
- * you can consume extra flags (e.g. register for "%pX", consume
- * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags
- * lowercase letters or numbers.
+ * return value: number of bytes that would be printed if the buffer
+ * was large enough. be careful about not under-reporting this;
+ * otherwise asnprintf() & co. will get broken. Returning -1 means
+ * something went wrong & default %p/%d handling should be executed.
*
- * bsz is a compile-time constant in printf; it's gonna be relatively
- * small. This isn't designed to print Shakespeare from a pointer.
+ * to consume extra input flags after %pXY, increment *fmt. It points
+ * at the first character after %pXY at entry. Convention is to make
+ * those flags lowercase letters or numbers.
*
* prec is the precision specifier (the 999 in "%.999p") -1 means
* none given (value in the format string cannot be negative)
*/
- ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec,
+ ssize_t (*print_ptr)(struct fbuf *buf, const char **fmt, int prec,
const void *);
- ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec,
+ ssize_t (*print_int)(struct fbuf *buf, const char **fmt, int prec,
uintmax_t);
};
void printfrr_ext_reg(const struct printfrr_ext *);
#define printfrr_ext_autoreg_p(matchs, print_fn) \
- static ssize_t print_fn(char *, size_t, const char *, int, \
+ static ssize_t print_fn(struct fbuf *, const char **, int, \
const void *); \
static const struct printfrr_ext _printext_##print_fn = { \
.match = matchs, \
/* end */
#define printfrr_ext_autoreg_i(matchs, print_fn) \
- static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \
+ static ssize_t print_fn(struct fbuf *, const char **, int, uintmax_t); \
static const struct printfrr_ext _printext_##print_fn = { \
.match = matchs, \
.print_int = print_fn, \
}
printfrr_ext_autoreg_p("SU", printfrr_psu)
-static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_psu(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const union sockunion *su = ptr;
- struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
bool include_port = false;
bool endflags = false;
- ssize_t consumed = 2;
-
- if (su) {
- while (!endflags) {
- switch (fmt[consumed++]) {
- case 'p':
- include_port = true;
- break;
- default:
- consumed--;
- endflags = true;
- break;
- }
- };
-
- switch (sockunion_family(su)) {
- case AF_UNSPEC:
- bprintfrr(&fb, "(unspec)");
- break;
- case AF_INET:
- inet_ntop(AF_INET, &su->sin.sin_addr, buf, bsz);
- fb.pos += strlen(fb.buf);
- if (include_port)
- bprintfrr(&fb, ":%d", su->sin.sin_port);
- break;
- case AF_INET6:
- inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, bsz);
- fb.pos += strlen(fb.buf);
- if (include_port)
- bprintfrr(&fb, ":%d", su->sin6.sin6_port);
+ ssize_t ret = 0;
+ char cbuf[INET6_ADDRSTRLEN];
+
+ if (!su)
+ return bputs(buf, "NULL");
+
+ while (!endflags) {
+ switch (**fmt) {
+ case 'p':
+ (*fmt)++;
+ include_port = true;
break;
default:
- bprintfrr(&fb, "(af %d)", sockunion_family(su));
+ endflags = true;
+ break;
}
+ }
- fb.pos[0] = '\0';
- } else {
- strlcpy(buf, "NULL", bsz);
+ switch (sockunion_family(su)) {
+ case AF_UNSPEC:
+ ret += bputs(buf, "(unspec)");
+ break;
+ case AF_INET:
+ inet_ntop(AF_INET, &su->sin.sin_addr, cbuf, sizeof(cbuf));
+ ret += bputs(buf, cbuf);
+ if (include_port)
+ ret += bprintfrr(buf, ":%d", su->sin.sin_port);
+ break;
+ case AF_INET6:
+ inet_ntop(AF_INET6, &su->sin6.sin6_addr, cbuf, sizeof(cbuf));
+ ret += bputs(buf, cbuf);
+ if (include_port)
+ ret += bprintfrr(buf, ":%d", su->sin6.sin6_port);
+ break;
+ default:
+ ret += bprintfrr(buf, "(af %d)", sockunion_family(su));
}
- return consumed;
+ return ret;
}
int sockunion_is_null(const union sockunion *su)
}
printfrr_ext_autoreg_p("RN", printfrr_rn)
-static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_rn(struct fbuf *buf, const char **fmt,
int prec, const void *ptr)
{
const struct route_node *rn = ptr;
const struct prefix *dst_p, *src_p;
+ char cbuf[PREFIX_STRLEN * 2 + 6];
- if (rn) {
- srcdest_rnode_prefixes(rn, &dst_p, &src_p);
- srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz);
- } else {
- strlcpy(buf, "NULL", bsz);
- }
+ if (!rn)
+ return bputs(buf, "NULL");
- return 2;
+ srcdest_rnode_prefixes(rn, &dst_p, &src_p);
+ srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p,
+ cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
}
struct route_table *srcdest_srcnode_table(struct route_node *rn)
{
va_list ap;
char bufrr[256];
+ bool truncfail = false;
+ size_t i;
+ size_t expectlen;
+
memset(bufrr, 0xcc, sizeof(bufrr));
+ va_start(ap, fmt);
+ expectlen = vsnprintfrr(NULL, 0, fmt, ap);
+ va_end(ap);
+
+ va_start(ap, fmt);
+ vsnprintfrr(bufrr, 7, fmt, ap);
+ va_end(ap);
+
+ if (strnlen(bufrr, 7) == 7)
+ truncfail = true;
+ if (strnlen(bufrr, 7) < 7 && strncmp(ref, bufrr, 6) != 0)
+ truncfail = true;
+ for (i = 7; i < sizeof(bufrr); i++)
+ if (bufrr[i] != (char)0xcc) {
+ truncfail = true;
+ break;
+ }
+
+ if (truncfail) {
+ printf("truncation test FAILED:\n"
+ "fmt: \"%s\"\nref: \"%s\"\nfrr[:7]: \"%s\"\n%s\n\n",
+ fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
+ errors++;
+ }
+
va_start(ap, fmt);
vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
va_end(ap);
fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
if (strcmp(ref, bufrr))
errors++;
+ if (strlen(bufrr) != expectlen) {
+ printf("return value <> length mismatch\n");
+ errors++;
+ }
}
int main(int argc, char **argv)
inet_aton("192.168.1.2", &ip);
printchk("192.168.1.2", "%pI4", &ip);
printchk(" 192.168.1.2", "%20pI4", &ip);
+ printchk("192.168.1.2 ", "%-20pI4", &ip);
printcmp("%p", &ip);