mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-15 07:21:59 +00:00
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>
This commit is contained in:
parent
bcf9d7d8aa
commit
212e04e5a7
@ -201,18 +201,17 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table,
|
|||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("BD", printfrr_bd)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const struct bgp_dest *dest = 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) {
|
if (!dest)
|
||||||
p = bgp_dest_get_prefix(dest);
|
return bputs(buf, "NULL");
|
||||||
prefix2str(p, buf, bsz);
|
|
||||||
} else {
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 2;
|
/* need to get the real length even if buffer too small */
|
||||||
|
prefix2str(p, cbuf, sizeof(cbuf));
|
||||||
|
return bputs(buf, cbuf);
|
||||||
}
|
}
|
||||||
|
@ -730,80 +730,99 @@ int nexthop_str2backups(const char *str, int *num_backups,
|
|||||||
* nexthop2str()
|
* nexthop2str()
|
||||||
*/
|
*/
|
||||||
printfrr_ext_autoreg_p("NH", printfrr_nh)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const struct nexthop *nexthop = ptr;
|
const struct nexthop *nexthop = ptr;
|
||||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
|
|
||||||
bool do_ifi = false;
|
bool do_ifi = false;
|
||||||
const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
|
const char *v_is = "", *v_via = "", *v_viaif = "via ";
|
||||||
ssize_t ret = 3;
|
ssize_t ret = 0;
|
||||||
|
|
||||||
/* NULL-check */
|
switch (**fmt) {
|
||||||
if (nexthop == NULL) {
|
|
||||||
if (fmt[2] == 'v' && fmt[3] == 'v')
|
|
||||||
ret++;
|
|
||||||
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (fmt[2]) {
|
|
||||||
case 'v':
|
case 'v':
|
||||||
if (fmt[3] == 'v') {
|
(*fmt)++;
|
||||||
|
if (**fmt == 'v') {
|
||||||
v_is = "is ";
|
v_is = "is ";
|
||||||
v_via = "via ";
|
v_via = "via ";
|
||||||
v_viaif = "";
|
v_viaif = "";
|
||||||
ret++;
|
(*fmt)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!nexthop)
|
||||||
|
return bputs(buf, "NULL");
|
||||||
|
|
||||||
switch (nexthop->type) {
|
switch (nexthop->type) {
|
||||||
case NEXTHOP_TYPE_IPV4:
|
case NEXTHOP_TYPE_IPV4:
|
||||||
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
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;
|
do_ifi = true;
|
||||||
break;
|
break;
|
||||||
case NEXTHOP_TYPE_IPV6:
|
case NEXTHOP_TYPE_IPV6:
|
||||||
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
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;
|
do_ifi = true;
|
||||||
break;
|
break;
|
||||||
case NEXTHOP_TYPE_IFINDEX:
|
case NEXTHOP_TYPE_IFINDEX:
|
||||||
bprintfrr(&fb, "%sdirectly connected, %s", v_is,
|
ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
|
||||||
ifindex2ifname(nexthop->ifindex,
|
ifindex2ifname(nexthop->ifindex,
|
||||||
nexthop->vrf_id));
|
nexthop->vrf_id));
|
||||||
break;
|
break;
|
||||||
case NEXTHOP_TYPE_BLACKHOLE:
|
case NEXTHOP_TYPE_BLACKHOLE:
|
||||||
|
ret += bputs(buf, "unreachable");
|
||||||
|
|
||||||
switch (nexthop->bh_type) {
|
switch (nexthop->bh_type) {
|
||||||
case BLACKHOLE_REJECT:
|
case BLACKHOLE_REJECT:
|
||||||
s = " (ICMP unreachable)";
|
ret += bputs(buf, " (ICMP unreachable)");
|
||||||
break;
|
break;
|
||||||
case BLACKHOLE_ADMINPROHIB:
|
case BLACKHOLE_ADMINPROHIB:
|
||||||
s = " (ICMP admin-prohibited)";
|
ret += bputs(buf, " (ICMP admin-prohibited)");
|
||||||
break;
|
break;
|
||||||
case BLACKHOLE_NULL:
|
case BLACKHOLE_NULL:
|
||||||
s = " (blackhole)";
|
ret += bputs(buf, " (blackhole)");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s = "";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bprintfrr(&fb, "unreachable%s", s);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (do_ifi && nexthop->ifindex)
|
if (do_ifi && nexthop->ifindex)
|
||||||
bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
|
ret += bprintfrr(buf, ", %s%s", v_viaif,
|
||||||
nexthop->ifindex,
|
ifindex2ifname(nexthop->ifindex,
|
||||||
nexthop->vrf_id));
|
nexthop->vrf_id));
|
||||||
|
|
||||||
*fb.pos = '\0';
|
|
||||||
return ret;
|
return ret;
|
||||||
case 's':
|
case 's':
|
||||||
nexthop2str(nexthop, buf, bsz);
|
(*fmt)++;
|
||||||
return 3;
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
94
lib/prefix.c
94
lib/prefix.c
@ -1361,92 +1361,92 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("EA", printfrr_ea)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const struct ethaddr *mac = ptr;
|
const struct ethaddr *mac = ptr;
|
||||||
|
char cbuf[ETHER_ADDR_STRLEN];
|
||||||
|
|
||||||
if (mac)
|
if (!mac)
|
||||||
prefix_mac2str(mac, buf, bsz);
|
return bputs(buf, "NULL");
|
||||||
else
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
|
|
||||||
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)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const struct ipaddr *ipa = ptr;
|
const struct ipaddr *ipa = ptr;
|
||||||
|
char cbuf[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
if (ipa)
|
if (!ipa)
|
||||||
ipaddr2str(ipa, buf, bsz);
|
return bputs(buf, "NULL");
|
||||||
else
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
|
|
||||||
return 2;
|
ipaddr2str(ipa, cbuf, sizeof(cbuf));
|
||||||
|
return bputs(buf, cbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("I4", printfrr_i4)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
if (ptr)
|
char cbuf[INET_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, ptr, buf, bsz);
|
|
||||||
else
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
|
|
||||||
return 2;
|
if (!ptr)
|
||||||
|
return bputs(buf, "NULL");
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf));
|
||||||
|
return bputs(buf, cbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("I6", printfrr_i6)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
if (ptr)
|
char cbuf[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET6, ptr, buf, bsz);
|
|
||||||
else
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
|
|
||||||
return 2;
|
if (!ptr)
|
||||||
|
return bputs(buf, "NULL");
|
||||||
|
|
||||||
|
inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf));
|
||||||
|
return bputs(buf, cbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("FX", printfrr_pfx)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
if (ptr)
|
char cbuf[PREFIX_STRLEN];
|
||||||
prefix2str(ptr, buf, bsz);
|
|
||||||
else
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
|
|
||||||
return 2;
|
if (!ptr)
|
||||||
|
return bputs(buf, "NULL");
|
||||||
|
|
||||||
|
prefix2str(ptr, cbuf, sizeof(cbuf));
|
||||||
|
return bputs(buf, cbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("SG4", printfrr_psg)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const struct prefix_sg *sg = 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)
|
||||||
if (sg->src.s_addr == INADDR_ANY)
|
return bputs(buf, "NULL");
|
||||||
bprintfrr(&fb, "(*,");
|
|
||||||
else
|
|
||||||
bprintfrr(&fb, "(%pI4,", &sg->src);
|
|
||||||
|
|
||||||
if (sg->grp.s_addr == INADDR_ANY)
|
if (sg->src.s_addr == INADDR_ANY)
|
||||||
bprintfrr(&fb, "*)");
|
ret += bputs(buf, "(*,");
|
||||||
else
|
else
|
||||||
bprintfrr(&fb, "%pI4)", &sg->grp);
|
ret += bprintfrr(buf, "(%pI4,", &sg->src);
|
||||||
|
|
||||||
fb.pos[0] = '\0';
|
if (sg->grp.s_addr == INADDR_ANY)
|
||||||
|
ret += bputs(buf, "*)");
|
||||||
|
else
|
||||||
|
ret += bprintfrr(buf, "%pI4)", &sg->grp);
|
||||||
|
|
||||||
} else {
|
return ret;
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 3;
|
|
||||||
}
|
}
|
||||||
|
@ -210,15 +210,16 @@ void printfrr_ext_reg(const struct printfrr_ext *ext)
|
|||||||
exts[i] = 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, const char **fmtp, int prec,
|
||||||
const void *ptr)
|
const void *ptr)
|
||||||
{
|
{
|
||||||
|
const char *fmt = *fmtp;
|
||||||
const struct printfrr_ext *ext;
|
const struct printfrr_ext *ext;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
|
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
|
||||||
if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
|
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])
|
if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
|
||||||
continue;
|
continue;
|
||||||
ext = exts[i];
|
ext = exts[i];
|
||||||
@ -226,20 +227,22 @@ ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
|
|||||||
continue;
|
continue;
|
||||||
if (strncmp(ext->match, fmt, strlen(ext->match)))
|
if (strncmp(ext->match, fmt, strlen(ext->match)))
|
||||||
continue;
|
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)
|
uintmax_t num)
|
||||||
{
|
{
|
||||||
|
const char *fmt = *fmtp;
|
||||||
const struct printfrr_ext *ext;
|
const struct printfrr_ext *ext;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
|
for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
|
||||||
if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
|
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])
|
if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
|
||||||
continue;
|
continue;
|
||||||
ext = exts[i];
|
ext = exts[i];
|
||||||
@ -247,7 +250,8 @@ ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
|
|||||||
continue;
|
continue;
|
||||||
if (strncmp(ext->match, fmt, strlen(ext->match)))
|
if (strncmp(ext->match, fmt, strlen(ext->match)))
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,7 @@ 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;
|
int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* returns number of bytes consumed for extended specifier */
|
/* returns number of bytes needed for full output, or -1 */
|
||||||
ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL;
|
ssize_t printfrr_extp(struct fbuf *, const char **, int, const void *)
|
||||||
ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL;
|
DSO_LOCAL;
|
||||||
|
ssize_t printfrr_exti(struct fbuf *, const char **, int, uintmax_t) DSO_LOCAL;
|
||||||
|
@ -177,6 +177,7 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
|
|||||||
int nextarg; /* 1-based argument index */
|
int nextarg; /* 1-based argument index */
|
||||||
va_list orgap; /* original argument pointer */
|
va_list orgap; /* original argument pointer */
|
||||||
char *convbuf; /* wide to multibyte conversion result */
|
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_lower[16] = "0123456789abcdef";
|
||||||
static const char xdigs_upper[16] = "0123456789ABCDEF";
|
static const char xdigs_upper[16] = "0123456789ABCDEF";
|
||||||
@ -438,16 +439,14 @@ reswitch: switch (ch) {
|
|||||||
ulval = SARG();
|
ulval = SARG();
|
||||||
|
|
||||||
if (printfrr_ext_char(fmt[0])) {
|
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
|
(flags & INTMAX_SIZE) ? ujval
|
||||||
: (uintmax_t)ulval);
|
: (uintmax_t)ulval);
|
||||||
if (n2 > 0) {
|
if (size >= 0)
|
||||||
fmt += n2;
|
goto ext_printed;
|
||||||
cp = buf;
|
|
||||||
size = strlen(cp);
|
|
||||||
sign = '\0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (flags & INTMAX_SIZE) {
|
if (flags & INTMAX_SIZE) {
|
||||||
if ((intmax_t)ujval < 0) {
|
if ((intmax_t)ujval < 0) {
|
||||||
@ -551,14 +550,13 @@ reswitch: switch (ch) {
|
|||||||
* -- ANSI X3J11
|
* -- ANSI X3J11
|
||||||
*/
|
*/
|
||||||
ptrval = GETARG(void *);
|
ptrval = GETARG(void *);
|
||||||
if (printfrr_ext_char(fmt[0]) &&
|
if (printfrr_ext_char(fmt[0])) {
|
||||||
(n2 = printfrr_extp(buf, sizeof(buf),
|
if (cb)
|
||||||
fmt, prec, ptrval)) > 0) {
|
extstart = cb->pos;
|
||||||
fmt += n2;
|
|
||||||
cp = buf;
|
size = printfrr_extp(cb, &fmt, prec, ptrval);
|
||||||
size = strlen(cp);
|
if (size >= 0)
|
||||||
sign = '\0';
|
goto ext_printed;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
ujval = (uintmax_t)(uintptr_t)ptrval;
|
ujval = (uintmax_t)(uintptr_t)ptrval;
|
||||||
base = 16;
|
base = 16;
|
||||||
@ -686,7 +684,7 @@ number: if ((dprec = prec) >= 0)
|
|||||||
realsz += 2;
|
realsz += 2;
|
||||||
|
|
||||||
prsize = width > realsz ? width : realsz;
|
prsize = width > realsz ? width : realsz;
|
||||||
if ((unsigned)ret + prsize > INT_MAX) {
|
if ((unsigned int)ret + prsize > INT_MAX) {
|
||||||
ret = EOF;
|
ret = EOF;
|
||||||
errno = EOVERFLOW;
|
errno = EOVERFLOW;
|
||||||
goto error;
|
goto error;
|
||||||
@ -720,6 +718,58 @@ number: if ((dprec = prec) >= 0)
|
|||||||
/* finally, adjust ret */
|
/* finally, adjust ret */
|
||||||
ret += prsize;
|
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 */
|
FLUSH(); /* copy out the I/O vectors */
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
@ -112,20 +112,21 @@ struct printfrr_ext {
|
|||||||
/* both can be given, if not the code continues searching
|
/* both can be given, if not the code continues searching
|
||||||
* (you can do %pX and %dX in 2 different entries)
|
* (you can do %pX and %dX in 2 different entries)
|
||||||
*
|
*
|
||||||
* return value: number of bytes consumed from the format string, so
|
* return value: number of bytes that would be printed if the buffer
|
||||||
* you can consume extra flags (e.g. register for "%pX", consume
|
* was large enough. be careful about not under-reporting this;
|
||||||
* "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags
|
* otherwise asnprintf() & co. will get broken. Returning -1 means
|
||||||
* lowercase letters or numbers.
|
* something went wrong & default %p/%d handling should be executed.
|
||||||
*
|
*
|
||||||
* bsz is a compile-time constant in printf; it's gonna be relatively
|
* to consume extra input flags after %pXY, increment *fmt. It points
|
||||||
* small. This isn't designed to print Shakespeare from a pointer.
|
* 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
|
* prec is the precision specifier (the 999 in "%.999p") -1 means
|
||||||
* none given (value in the format string cannot be negative)
|
* 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 *);
|
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);
|
uintmax_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ struct printfrr_ext {
|
|||||||
void printfrr_ext_reg(const struct printfrr_ext *);
|
void printfrr_ext_reg(const struct printfrr_ext *);
|
||||||
|
|
||||||
#define printfrr_ext_autoreg_p(matchs, print_fn) \
|
#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 *); \
|
const void *); \
|
||||||
static const struct printfrr_ext _printext_##print_fn = { \
|
static const struct printfrr_ext _printext_##print_fn = { \
|
||||||
.match = matchs, \
|
.match = matchs, \
|
||||||
@ -149,7 +150,7 @@ void printfrr_ext_reg(const struct printfrr_ext *);
|
|||||||
/* end */
|
/* end */
|
||||||
|
|
||||||
#define printfrr_ext_autoreg_i(matchs, print_fn) \
|
#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 = { \
|
static const struct printfrr_ext _printext_##print_fn = { \
|
||||||
.match = matchs, \
|
.match = matchs, \
|
||||||
.print_int = print_fn, \
|
.print_int = print_fn, \
|
||||||
|
@ -664,54 +664,51 @@ void sockunion_init(union sockunion *su)
|
|||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("SU", printfrr_psu)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const union sockunion *su = ptr;
|
const union sockunion *su = ptr;
|
||||||
struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
|
|
||||||
bool include_port = false;
|
bool include_port = false;
|
||||||
bool endflags = false;
|
bool endflags = false;
|
||||||
ssize_t consumed = 2;
|
ssize_t ret = 0;
|
||||||
|
char cbuf[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
if (su) {
|
if (!su)
|
||||||
while (!endflags) {
|
return bputs(buf, "NULL");
|
||||||
switch (fmt[consumed++]) {
|
|
||||||
case 'p':
|
|
||||||
include_port = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
consumed--;
|
|
||||||
endflags = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (sockunion_family(su)) {
|
while (!endflags) {
|
||||||
case AF_UNSPEC:
|
switch (**fmt) {
|
||||||
bprintfrr(&fb, "(unspec)");
|
case 'p':
|
||||||
break;
|
(*fmt)++;
|
||||||
case AF_INET:
|
include_port = true;
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
bprintfrr(&fb, "(af %d)", sockunion_family(su));
|
endflags = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fb.pos[0] = '\0';
|
|
||||||
} else {
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
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 ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sockunion_is_null(const union sockunion *su)
|
int sockunion_is_null(const union sockunion *su)
|
||||||
|
@ -307,20 +307,20 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
printfrr_ext_autoreg_p("RN", printfrr_rn)
|
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)
|
int prec, const void *ptr)
|
||||||
{
|
{
|
||||||
const struct route_node *rn = ptr;
|
const struct route_node *rn = ptr;
|
||||||
const struct prefix *dst_p, *src_p;
|
const struct prefix *dst_p, *src_p;
|
||||||
|
char cbuf[PREFIX_STRLEN * 2 + 6];
|
||||||
|
|
||||||
if (rn) {
|
if (!rn)
|
||||||
srcdest_rnode_prefixes(rn, &dst_p, &src_p);
|
return bputs(buf, "NULL");
|
||||||
srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz);
|
|
||||||
} else {
|
|
||||||
strlcpy(buf, "NULL", bsz);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
struct route_table *srcdest_srcnode_table(struct route_node *rn)
|
||||||
|
@ -64,8 +64,37 @@ static void printchk(const char *ref, const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char bufrr[256];
|
char bufrr[256];
|
||||||
|
bool truncfail = false;
|
||||||
|
size_t i;
|
||||||
|
size_t expectlen;
|
||||||
|
|
||||||
memset(bufrr, 0xcc, sizeof(bufrr));
|
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);
|
va_start(ap, fmt);
|
||||||
vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
|
vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
@ -74,6 +103,10 @@ static void printchk(const char *ref, const char *fmt, ...)
|
|||||||
fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
|
fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
|
||||||
if (strcmp(ref, bufrr))
|
if (strcmp(ref, bufrr))
|
||||||
errors++;
|
errors++;
|
||||||
|
if (strlen(bufrr) != expectlen) {
|
||||||
|
printf("return value <> length mismatch\n");
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@ -112,6 +145,7 @@ int main(int argc, char **argv)
|
|||||||
inet_aton("192.168.1.2", &ip);
|
inet_aton("192.168.1.2", &ip);
|
||||||
printchk("192.168.1.2", "%pI4", &ip);
|
printchk("192.168.1.2", "%pI4", &ip);
|
||||||
printchk(" 192.168.1.2", "%20pI4", &ip);
|
printchk(" 192.168.1.2", "%20pI4", &ip);
|
||||||
|
printchk("192.168.1.2 ", "%-20pI4", &ip);
|
||||||
|
|
||||||
printcmp("%p", &ip);
|
printcmp("%p", &ip);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user