diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 7e3aa2a48a..50123c608b 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -201,18 +201,17 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table, } 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); } diff --git a/lib/nexthop.c b/lib/nexthop.c index 17ef95c687..64b4f3adb1 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -730,80 +730,99 @@ int nexthop_str2backups(const char *str, int *num_backups, * 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; } diff --git a/lib/prefix.c b/lib/prefix.c index afc4d3d5c2..169e9ed572 100644 --- a/lib/prefix.c +++ b/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) -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]; - 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) -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]; - 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) -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]; - return 2; + if (!ptr) + return bputs(buf, "NULL"); + + 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) + return bputs(buf, "NULL"); - if (sg->grp.s_addr == INADDR_ANY) - bprintfrr(&fb, "*)"); - else - bprintfrr(&fb, "%pI4)", &sg->grp); + if (sg->src.s_addr == INADDR_ANY) + ret += bputs(buf, "(*,"); + else + 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 { - strlcpy(buf, "NULL", bsz); - } - - return 3; + return ret; } diff --git a/lib/printf/glue.c b/lib/printf/glue.c index 29ca26ad5d..477fc0d384 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, 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]; @@ -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); + *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]; @@ -247,7 +250,8 @@ 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); + *fmtp += strlen(ext->match); + return ext->print_int(buf, fmtp, prec, num); } - return 0; + return -1; } diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h index 335e09872e..df962fc043 100644 --- a/lib/printf/printflocal.h +++ b/lib/printf/printflocal.h @@ -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; #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; 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; @@ -720,6 +718,58 @@ number: if ((dprec = prec) >= 0) /* 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: diff --git a/lib/printfrr.h b/lib/printfrr.h index 418e839d97..549334ba5b 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -112,20 +112,21 @@ struct printfrr_ext { /* 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); }; @@ -136,7 +137,7 @@ struct printfrr_ext { 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, \ @@ -149,7 +150,7 @@ void printfrr_ext_reg(const struct printfrr_ext *); /* 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, \ diff --git a/lib/sockunion.c b/lib/sockunion.c index d65235b41c..25de8f7dd0 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -664,54 +664,51 @@ void sockunion_init(union sockunion *su) } 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; + ssize_t ret = 0; + char cbuf[INET6_ADDRSTRLEN]; - if (su) { - while (!endflags) { - switch (fmt[consumed++]) { - case 'p': - include_port = true; - break; - default: - consumed--; - endflags = true; - break; - } - }; + if (!su) + return bputs(buf, "NULL"); - 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); + 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); } - 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) diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index a115507192..ca48d56e4d 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -307,20 +307,20 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) } 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) diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 24de3fa88d..9ffea8b978 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -64,8 +64,37 @@ static void printchk(const char *ref, const char *fmt, ...) { 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); @@ -74,6 +103,10 @@ static void printchk(const char *ref, const char *fmt, ...) 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) @@ -112,6 +145,7 @@ 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);