Adds custom printf formats
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 15 Oct 2013 20:52:18 +0000 (13:52 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 22 Jan 2014 22:22:53 +0000 (14:22 -0800)
The specifier %r will print the contents of errstr, installed by
default.  Any others need to be manually installed.

For example:
register_printf_specifier('i', printf_ipaddr, printf_ipaddr_info);

See tests/printf-ext.c for some examples.

If you want to add your own, add them to parlib/printf-ext.c.  Check out
the comments for ipaddr and ipaddr_info for details.  Once you get the
hang of it, it's pretty simple.

Also, some letters can't have a specifier installed onto it, such as %I
and other special chars.

tests/printf-ext.c [new file with mode: 0644]
user/eipconvtest.c [deleted file]
user/parlib/include/printf-ext.h [new file with mode: 0644]
user/parlib/printf-ext.c [new file with mode: 0644]
user/parlib/vcore.c

diff --git a/tests/printf-ext.c b/tests/printf-ext.c
new file mode 100644 (file)
index 0000000..4dbe086
--- /dev/null
@@ -0,0 +1,63 @@
+/* Basic tests for custom printf strings, such as %i (ipaddr) and %r */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <parlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <printf-ext.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+uint8_t v4addr[] = {
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0xff, 0xff,
+       192, 168, 0, 2
+};
+
+uint8_t v6addr[] = {
+       0xfe, 0x80, 0, 0,
+       0, 0, 0, 0,
+       0x20, 0x0, 0x0a, 0xff,
+       0xfe, 0xa7, 0x0f, 0x7c
+};
+
+uint8_t v6mask[] = {
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff,
+       255, 255, 192, 0
+};
+
+uint8_t ethaddr[] = {
+       0x90, 0xe6, 0xba, 0x12, 0x48, 0x64
+};
+
+int main(int argc, char **argv)
+{
+       int ret;
+       if (register_printf_specifier('i', printf_ipaddr, printf_ipaddr_info))
+               printf("Failed to register 'i'\n");
+       if (register_printf_specifier('M', printf_ipmask, printf_ipmask_info))
+               printf("Failed to register 'M'\n");
+       if (register_printf_specifier('E', printf_ethaddr, printf_ethaddr_info))
+               printf("Failed to register 'E'\n");
+
+       printf("IPv4 addr %i\n", v4addr);
+       printf("IPv6 addr %i\n", v6addr);
+       printf("IPv6 mask %M\n", v6mask);
+       printf("IPv4 addr as mask %M\n", v4addr);
+       printf("IPv6 addr as mask %M\n", v6addr);
+       printf("ethaddr %E\n", ethaddr);
+       printf("ethaddr null %E\n", 0);
+
+       ret = open("/9/proc/no/such/file", 0, 0);
+       printf("Open ret %d, errstr: %r\n", ret);
+       printf("%s %i %M %d %s %r %E\n", "testing a few,", v6addr, v6mask, 1337,
+              "more cowbell", ethaddr);
+
+       printf("Done\n");
+       return 0;
+}
diff --git a/user/eipconvtest.c b/user/eipconvtest.c
deleted file mode 100644 (file)
index 03a5e5c..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-// INFERNO
-#if 0
-#include <vfs.h>
-#include <kfs.h>
-#include <slab.h>
-#include <kmalloc.h>
-#include <kref.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include <error.h>
-#include <cpio.h>
-#include <pmap.h>
-#include <smp.h>
-#include <ip.h>
-#endif
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdarg.h>
-#include <string.h>
-
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned long uint32_t;
-
-enum {
-       Isprefix = 16,
-};
-
-uint8_t prefixvals[256] = {
-       [0x00] 0 | Isprefix,
-       [0x80] 1 | Isprefix,
-       [0xC0] 2 | Isprefix,
-       [0xE0] 3 | Isprefix,
-       [0xF0] 4 | Isprefix,
-       [0xF8] 5 | Isprefix,
-       [0xFC] 6 | Isprefix,
-       [0xFE] 7 | Isprefix,
-       [0xFF] 8 | Isprefix,
-};
-
-uint8_t v4prefix[16] = {
-       0, 0, 0, 0,
-       0, 0, 0, 0,
-       0, 0, 0xff, 0xff,
-       0, 0, 0, 0
-};
-
-void hnputl(void *p, uint32_t v)
-{
-       uint8_t *a;
-
-       a = p;
-       a[0] = v >> 24;
-       a[1] = v >> 16;
-       a[2] = v >> 8;
-       a[3] = v;
-}
-
-char *eipconv(int fmt, ...)
-{
-       va_list ap;
-       static char buf[8 * 5];
-       static char *efmt = "0x%.2lx0x%.2lx0x%.2lx0x%.2lx0x%.2lx0x%.2lx";
-       static char *ifmt = "%d.%d.%d.%d";
-       uint8_t *p, ip[16];
-       uint32_t *lp;
-       uint16_t s;
-       int i, j, n, eln, eli;
-       va_start(ap, fmt);
-       switch (fmt) {
-               case 'E':       /* Ethernet address */
-                       p = va_arg(ap, uint8_t *);
-                       snprintf(buf, sizeof(buf), efmt, p[0], p[1], p[2], p[3], p[4],
-                                        p[5]);
-                       break;
-               case 'I':       /* Ip address */
-                       p = va_arg(ap, uint8_t *);
-common:
-                       if (memcmp(p, v4prefix, 12) == 0)
-                               snprintf(buf, sizeof(buf), ifmt, p[12], p[13], p[14], p[15]);
-                       else {
-                               /* find longest elision */
-                               eln = eli = -1;
-                               for (i = 0; i < 16; i += 2) {
-                                       for (j = i; j < 16; j += 2)
-                                               if (p[j] != 0 || p[j + 1] != 0)
-                                                       break;
-                                       if (j > i && j - i > eln) {
-                                               eli = i;
-                                               eln = j - i;
-                                       }
-                               }
-
-                               /* print with possible elision */
-                               n = 0;
-                               for (i = 0; i < 16; i += 2) {
-                                       if (i == eli) {
-                                               n += snprintf(buf, sizeof(buf) + n, "::");
-                                               i += eln;
-                                               if (i >= 16)
-                                                       break;
-                                       } else if (i != 0)
-                                               n += snprintf(buf, sizeof(buf) + n, ":");
-                                       s = (p[i] << 8) + p[i + 1];
-                                       n += snprintf(buf, sizeof(buf) + n, "0x%x", s);
-                               }
-                       }
-                       break;
-               case 'i':       /* v6 address as 4 longs */
-                       lp = va_arg(ap, uint32_t *);
-                       for (i = 0; i < 4; i++)
-                               hnputl(ip + 4 * i, *lp++);
-                       p = ip;
-                       goto common;
-               case 'V':       /* v4 ip address */
-                       p = va_arg(ap, uint8_t *);
-                       snprintf(buf, sizeof(buf), ifmt, p[0], p[1], p[2], p[3]);
-                       break;
-               case 'M':       /* ip mask */
-                       p = va_arg(ap, uint8_t *);
-
-                       /* look for a prefix mask */
-                       for (i = 0; i < 16; i++)
-                               if (p[i] != 0xff)
-                                       break;
-                       if (i < 16) {
-                               if ((prefixvals[p[i]] & Isprefix) == 0)
-                                       goto common;
-                               for (j = i + 1; j < 16; j++)
-                                       if (p[j] != 0)
-                                               goto common;
-                               n = 8 * i + (prefixvals[p[i]] & ~Isprefix);
-                       } else
-                               n = 8 * 16;
-
-                       /* got one, use /xx format */
-                       snprintf(buf, sizeof(buf), "/%d", n);
-                       break;
-               default:
-                       strncpy(buf, "(eipconv)", sizeof(buf));
-       }
-
-       return buf;
-}
-
-uint8_t testvec[11][16] = {
-       {0,0,0,0,0,0,0,0,0,0,0xff,0xff,1,3,4,5,} ,
-       {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,} ,
-       {0xff,0xff,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,} ,
-       {0xff,0xff,0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,} ,
-       {0xff,0xff,0xff,0xff,0xe0,0,0,0,0,0,0,0,0,0,0,0,} ,
-       {0xff,0xff,0xff,0xff,0xff,0xf0,0,0,0,0,0,0,0,0,0,0,} ,
-       {0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0,0,0,0,0,0,0,0,0,} ,
-       {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,} ,
-       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,} ,
-       {0,0,0,0,0,0x11,0,0,0,0,0,0,0,0,0,0,} ,
-       {0,0,0,0x11,0,0,0,0,0,0,0,0,0,0,0,0x12,} ,
-};
-
-void main(void)
-{
-       int i;
-       for (i = 0; i < 11; i++)
-               printf("%s\n", eipconv('I', &testvec[i]));
-
-}
diff --git a/user/parlib/include/printf-ext.h b/user/parlib/include/printf-ext.h
new file mode 100644 (file)
index 0000000..d4c96f0
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright (c) 2013 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Common printf format extensions.  For now, %r is installed by default
+ * (in early init code), and the others need to be requested.
+ *
+ * To register, for example %i for ipaddr, call:
+ *             register_printf_specifier('i', printf_ipaddr, printf_ipaddr_info);
+ */
+
+#ifndef _PRINTF_EXT_H
+#define _PRINTF_EXT_H
+
+#include <ros/common.h>
+#include <printf.h>
+
+/* Commonly used as %i, will print out a 16-byte plan9 IP address */
+int printf_ipaddr(FILE *stream, const struct printf_info *info,
+                  const void *const *args);
+int printf_ipaddr_info(const struct printf_info* info, size_t n, int *argtypes,
+                       int *size);
+
+/* Commonly used as %M, will print out a plan9 IPv6 mask, preferably as /xx */
+int printf_ipmask(FILE *stream, const struct printf_info *info,
+                  const void *const *args);
+int printf_ipmask_info(const struct printf_info* info, size_t n, int *argtypes,
+                       int *size);
+
+/* Commonly used as %E, will print out an ethernet address */
+int printf_ethaddr(FILE *stream, const struct printf_info *info,
+                   const void *const *args);
+int printf_ethaddr_info(const struct printf_info* info, size_t n, int *argtypes,
+                        int *size);
+
+/* Installed by default, will print the errstr for %r */
+int printf_errstr(FILE *stream, const struct printf_info *info,
+                  const void *const *args);
+int printf_errstr_info(const struct printf_info* info, size_t n, int *argtypes,
+                       int *size);
+
+#endif /* _PRINTF_EXT_H */
diff --git a/user/parlib/printf-ext.c b/user/parlib/printf-ext.c
new file mode 100644 (file)
index 0000000..aad8594
--- /dev/null
@@ -0,0 +1,167 @@
+/* Copyright (c) 2013-14 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Parts Copyright //INFERNO
+ *
+ * Common printf format extensions.  For now, %r is installed by default
+ * (in early init code), and the others need to be requested.
+ *
+ * To register, for example %i for ipaddr, call:
+ *             register_printf_specifier('i', printf_ipaddr, printf_ipaddr_info);
+ *
+ * __printf_ipaddr, printf_ipmask, and printf_ethaddr adapted from INFERNO's
+ * eipconvtest.c. */
+
+#include <printf-ext.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static bool is_ipv4(uint8_t *ipaddr)
+{
+       uint8_t v4prefix[] = {
+               0, 0, 0, 0,
+               0, 0, 0, 0,
+               0, 0, 0xff, 0xff
+       };
+       return memcmp(ipaddr, v4prefix, sizeof(v4prefix)) == 0;
+}
+
+/* Helper, prints a formatted ipaddr to stream. */
+static int __printf_ipaddr(FILE *stream, uint8_t *ipaddr)
+{
+       int i, j, eln, eli;
+       int ret = 0;
+       uint16_t s;
+
+       if (is_ipv4(ipaddr))
+               return fprintf(stream, "%d.%d.%d.%d", ipaddr[12], ipaddr[13],
+                              ipaddr[14], ipaddr[15]);
+       /* find longest elision */
+       eln = eli = -1;
+       for (i = 0; i < 16; i += 2) {
+               for (j = i; j < 16; j += 2)
+                       if (ipaddr[j] != 0 || ipaddr[j + 1] != 0)
+                               break;
+               if (j > i && j - i > eln) {
+                       eli = i;
+                       eln = j - i;
+               }
+       }
+       /* print with possible elision */
+       for (i = 0; i < 16; i += 2) {
+               if (i == eli) {
+                       ret += fprintf(stream, "::");
+                       i += eln;
+                       if (i >= 16)
+                               break;
+               } else if (i != 0)
+                       ret += fprintf(stream, ":");
+               s = (ipaddr[i] << 8) + ipaddr[i + 1];
+               ret += fprintf(stream, "%x", s);
+       }
+       return ret;
+}
+
+int printf_ipaddr(FILE *stream, const struct printf_info *info,
+                  const void *const *args)
+{
+       /* args is an array of pointers, each of which points to an arg.
+        * to extract: TYPE x = *(TYPE*)args[n]. */
+       uint8_t *ipaddr = *(uint8_t**)args[0];
+       return __printf_ipaddr(stream, ipaddr);
+}
+
+int printf_ipaddr_info(const struct printf_info* info, size_t n, int *argtypes,
+                       int *size)
+{
+       /* seems like this is how many va_args we will use, and how big each was
+        * we're supposed to fill up to n, i think.  we're only doing one */
+       if (n > 0) {
+               argtypes[0] = PA_POINTER;
+               size[0] = sizeof(uint8_t*);
+       }
+       /* returns the nr of args required by the format string, no matter what */
+       return 1;
+}
+
+int printf_ipmask(FILE *stream, const struct printf_info *info,
+                  const void *const *args)
+{
+       enum {
+               Isprefix = 16,
+       };
+       static uint8_t prefixvals[256] = {
+               [0x00] 0 | Isprefix,
+               [0x80] 1 | Isprefix,
+               [0xC0] 2 | Isprefix,
+               [0xE0] 3 | Isprefix,
+               [0xF0] 4 | Isprefix,
+               [0xF8] 5 | Isprefix,
+               [0xFC] 6 | Isprefix,
+               [0xFE] 7 | Isprefix,
+               [0xFF] 8 | Isprefix,
+       };
+
+       uint8_t *ipmask = *(uint8_t**)args[0];
+       int i, j, n;
+       /* look for a prefix mask */
+       for (i = 0; i < 16; i++)
+               if (ipmask[i] != 0xff)
+                       break;
+       if (i < 16) {
+               if ((prefixvals[ipmask[i]] & Isprefix) == 0)
+                       return __printf_ipaddr(stream, ipmask);
+               for (j = i + 1; j < 16; j++)
+                       if (ipmask[j] != 0)
+                               return __printf_ipaddr(stream, ipmask);
+               n = 8 * i + (prefixvals[ipmask[i]] & ~Isprefix);
+       } else
+               n = 8 * 16;
+       /* got one, use /xx format */
+       return fprintf(stream, "/%d", n);
+}
+
+int printf_ipmask_info(const struct printf_info* info, size_t n, int *argtypes,
+                       int *size)
+{
+       if (n > 0) {
+               argtypes[0] = PA_POINTER;
+               size[0] = sizeof(uint8_t*);
+       }
+       return 1;
+}
+
+int printf_ethaddr(FILE *stream, const struct printf_info *info,
+                   const void *const *args)
+{
+       uint8_t *e = *(uint8_t**)args[0];
+       if (!e)
+               e = "\0\0\0\0\0\0";
+       return fprintf(stream, "%02x:%02x:%02x:%02x:%02x:%02x", e[0], e[1], e[2],
+                      e[3], e[4], e[5]);
+}
+
+int printf_ethaddr_info(const struct printf_info* info, size_t n, int *argtypes,
+                        int *size)
+{
+       if (n > 0) {
+               argtypes[0] = PA_POINTER;
+               size[0] = sizeof(uint8_t*);
+       }
+       return 1;
+}
+
+int printf_errstr(FILE *stream, const struct printf_info *info,
+                  const void *const *args)
+{
+       return fprintf(stream, "%s", errstr());
+}
+
+int printf_errstr_info(const struct printf_info* info, size_t n, int *argtypes,
+                       int *size)
+{
+       /* errstr consumes no arguments */
+       return 0;
+}
index 0b94ec2..45e9ee5 100644 (file)
@@ -13,6 +13,7 @@
 #include <uthread.h>
 #include <ucq.h>
 #include <ros/arch/membar.h>
 #include <uthread.h>
 #include <ucq.h>
 #include <ros/arch/membar.h>
+#include <printf-ext.h>
 
 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
 static size_t _max_vcores_ever_wanted = 1;
 
 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
 static size_t _max_vcores_ever_wanted = 1;
@@ -144,6 +145,7 @@ void vcore_reenter(void (*entry_func)(void))
  * go to vcore 0. */
 void vcore_event_init(void)
 {
  * go to vcore 0. */
 void vcore_event_init(void)
 {
+       register_printf_specifier('r', printf_errstr, printf_errstr_info);
        /* set up our thread0 as a uthread */
        uthread_slim_init();
        /* TODO: register for other kevents/signals and whatnot (can probably reuse
        /* set up our thread0 as a uthread */
        uthread_slim_init();
        /* TODO: register for other kevents/signals and whatnot (can probably reuse