summaryrefslogtreecommitdiff
path: root/lib/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/printf.c')
-rw-r--r--lib/printf.c141
1 files changed, 110 insertions, 31 deletions
diff --git a/lib/printf.c b/lib/printf.c
index 4fd75c9b..533a1300 100644
--- a/lib/printf.c
+++ b/lib/printf.c
@@ -118,16 +118,17 @@ static char * number(char * str, long num, int base, int size, int precision,
* @fmt: format string
* @args: a list of arguments to be formatted
*
- * This functions acts like ordinary sprintf() except that it checks
- * available space to avoid buffer overflows and it allows some more
- * format specifiers: |%I| for formatting of IP addresses (any non-zero
- * width is automatically replaced by standard IP address width which
- * depends on whether we use IPv4 or IPv6; |%#I| gives hexadecimal format),
- * |%R| for Router / Network ID (u32 value printed as IPv4 address)
- * |%lR| for 64bit Router / Network ID (u64 value printed as eight :-separated octets)
- * and |%m| resp. |%M| for error messages (uses strerror() to translate @errno code to
- * message text). On the other hand, it doesn't support floating
- * point numbers.
+ * This functions acts like ordinary sprintf() except that it checks available
+ * space to avoid buffer overflows and it allows some more format specifiers:
+ * |%I| for formatting of IP addresses (width of 1 is automatically replaced by
+ * standard IP address width which depends on whether we use IPv4 or IPv6; |%I4|
+ * or |%I6| can be used for explicit ip4_addr / ip6_addr arguments, |%N| for
+ * generic network addresses (net_addr *), |%R| for Router / Network ID (u32
+ * value printed as IPv4 address), |%lR| for 64bit Router / Network ID (u64
+ * value printed as eight :-separated octets), |%t| for time values (btime) with
+ * specified subsecond precision, and |%m| resp. |%M| for error messages (uses
+ * strerror() to translate @errno code to message text). On the other hand, it
+ * doesn't support floating point numbers.
*
* Result: number of characters of the output string or -1 if
* the buffer space was insufficient.
@@ -139,9 +140,11 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
int i, base;
u32 x;
u64 X;
+ btime t;
+ s64 t1, t2;
char *str, *start;
const char *s;
- char ipbuf[MAX(STD_ADDRESS_P_LENGTH,ROUTER_ID_64_LENGTH)+1];
+ char ipbuf[NET_MAX_TEXT_LENGTH+1];
struct iface *iface;
int flags; /* flags to number() */
@@ -158,7 +161,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
*str++ = *fmt;
continue;
}
-
+
/* process flags */
flags = 0;
repeat:
@@ -170,7 +173,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
-
+
/* get field width */
field_width = -1;
if (is_digit(*fmt))
@@ -188,7 +191,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* get the precision */
precision = -1;
if (*fmt == '.') {
- ++fmt;
+ ++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
@@ -238,6 +241,14 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
case 'M':
s = strerror(va_arg(args, int));
goto str;
+ case 'N': {
+ net_addr *n = va_arg(args, net_addr *);
+ if (field_width == 1)
+ field_width = net_max_text_length[n->type];
+ net_format(n, ipbuf, sizeof(ipbuf));
+ s = ipbuf;
+ goto str;
+ }
case 's':
s = va_arg(args, char *);
if (!s)
@@ -271,7 +282,6 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
return -1;
continue;
-
case 'n':
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
@@ -284,14 +294,35 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* IP address */
case 'I':
- if (flags & SPECIAL)
- ipa_ntox(va_arg(args, ip_addr), ipbuf);
- else {
- ipa_ntop(va_arg(args, ip_addr), ipbuf);
- if (field_width == 1)
- field_width = STD_ADDRESS_P_LENGTH;
+ if (fmt[1] == '4') {
+ /* Explicit IPv4 address */
+ ip4_addr a = va_arg(args, ip4_addr);
+ ip4_ntop(a, ipbuf);
+ i = IP4_MAX_TEXT_LENGTH;
+ fmt++;
+ } else if (fmt[1] == '6') {
+ /* Explicit IPv6 address */
+ ip6_addr a = va_arg(args, ip6_addr);
+ ip6_ntop(a, ipbuf);
+ i = IP6_MAX_TEXT_LENGTH;
+ fmt++;
+ } else {
+ /* Just IP address */
+ ip_addr a = va_arg(args, ip_addr);
+
+ if (ipa_is_ip4(a)) {
+ ip4_ntop(ipa_to_ip4(a), ipbuf);
+ i = IP4_MAX_TEXT_LENGTH;
+ } else {
+ ip6_ntop(ipa_to_ip6(a), ipbuf);
+ i = IP6_MAX_TEXT_LENGTH;
+ }
}
+
s = ipbuf;
+ if (field_width == 1)
+ field_width = i;
+
goto str;
/* Interface scope after link-local IP address */
@@ -311,7 +342,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* Router/Network ID - essentially IPv4 address in u32 value */
case 'R':
- if(qualifier == 'l') {
+ if (qualifier == 'l') {
X = va_arg(args, u64);
bsprintf(ipbuf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
((X >> 56) & 0xff),
@@ -326,15 +357,55 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
else
{
x = va_arg(args, u32);
- bsprintf(ipbuf, "%d.%d.%d.%d",
- ((x >> 24) & 0xff),
- ((x >> 16) & 0xff),
- ((x >> 8) & 0xff),
- (x & 0xff));
+ ip4_ntop(ip4_from_u32(x), ipbuf);
}
s = ipbuf;
goto str;
+ case 't':
+ t = va_arg(args, btime);
+ t1 = t TO_S;
+ t2 = t - t1 S;
+
+ if (precision < 0)
+ precision = 3;
+
+ if (precision > 6)
+ precision = 6;
+
+ /* Compute field_width for second part */
+ if ((precision > 0) && (field_width > 0))
+ field_width -= (1 + precision);
+
+ if (field_width < 0)
+ field_width = 0;
+
+ /* Print seconds */
+ flags |= SIGN;
+ str = number(str, t1, 10, field_width, 0, flags, size);
+ if (!str)
+ return -1;
+
+ if (precision > 0)
+ {
+ size -= (str-start);
+ start = str;
+
+ if ((1 + precision) > size)
+ return -1;
+
+ /* Convert microseconds to requested precision */
+ for (i = precision; i < 6; i++)
+ t2 /= 10;
+
+ /* Print sub-seconds */
+ *str++ = '.';
+ str = number(str, t2, 10, precision, 0, ZEROPAD, size - 1);
+ if (!str)
+ return -1;
+ }
+ goto done;
+
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
@@ -376,6 +447,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
str = number(str, num, base, field_width, precision, flags, size);
if (!str)
return -1;
+ done: ;
}
if (!size)
return -1;
@@ -442,6 +514,10 @@ int
buffer_vprint(buffer *buf, const char *fmt, va_list args)
{
int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
+
+ if ((i < 0) && (buf->pos < buf->end))
+ *buf->pos = 0;
+
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i;
}
@@ -453,9 +529,12 @@ buffer_print(buffer *buf, const char *fmt, ...)
int i;
va_start(args, fmt);
- i=bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
+ i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
va_end(args);
+ if ((i < 0) && (buf->pos < buf->end))
+ *buf->pos = 0;
+
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i;
}
@@ -464,13 +543,13 @@ void
buffer_puts(buffer *buf, const char *str)
{
byte *bp = buf->pos;
- byte *be = buf->end;
+ byte *be = buf->end - 1;
while (bp < be && *str)
*bp++ = *str++;
- if (bp < be)
+ if (bp <= be)
*bp = 0;
- buf->pos = bp;
+ buf->pos = (bp < be) ? bp : buf->end;
}