Adds custom printf formats
[akaros.git] / user / parlib / printf-ext.c
1 /* Copyright (c) 2013-14 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Parts Copyright //INFERNO
6  *
7  * Common printf format extensions.  For now, %r is installed by default
8  * (in early init code), and the others need to be requested.
9  *
10  * To register, for example %i for ipaddr, call:
11  *              register_printf_specifier('i', printf_ipaddr, printf_ipaddr_info);
12  *
13  * __printf_ipaddr, printf_ipmask, and printf_ethaddr adapted from INFERNO's
14  * eipconvtest.c. */
15
16 #include <printf-ext.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20
21 static bool is_ipv4(uint8_t *ipaddr)
22 {
23         uint8_t v4prefix[] = {
24                 0, 0, 0, 0,
25                 0, 0, 0, 0,
26                 0, 0, 0xff, 0xff
27         };
28         return memcmp(ipaddr, v4prefix, sizeof(v4prefix)) == 0;
29 }
30
31 /* Helper, prints a formatted ipaddr to stream. */
32 static int __printf_ipaddr(FILE *stream, uint8_t *ipaddr)
33 {
34         int i, j, eln, eli;
35         int ret = 0;
36         uint16_t s;
37
38         if (is_ipv4(ipaddr))
39                 return fprintf(stream, "%d.%d.%d.%d", ipaddr[12], ipaddr[13],
40                                ipaddr[14], ipaddr[15]);
41         /* find longest elision */
42         eln = eli = -1;
43         for (i = 0; i < 16; i += 2) {
44                 for (j = i; j < 16; j += 2)
45                         if (ipaddr[j] != 0 || ipaddr[j + 1] != 0)
46                                 break;
47                 if (j > i && j - i > eln) {
48                         eli = i;
49                         eln = j - i;
50                 }
51         }
52         /* print with possible elision */
53         for (i = 0; i < 16; i += 2) {
54                 if (i == eli) {
55                         ret += fprintf(stream, "::");
56                         i += eln;
57                         if (i >= 16)
58                                 break;
59                 } else if (i != 0)
60                         ret += fprintf(stream, ":");
61                 s = (ipaddr[i] << 8) + ipaddr[i + 1];
62                 ret += fprintf(stream, "%x", s);
63         }
64         return ret;
65 }
66
67 int printf_ipaddr(FILE *stream, const struct printf_info *info,
68                   const void *const *args)
69 {
70         /* args is an array of pointers, each of which points to an arg.
71          * to extract: TYPE x = *(TYPE*)args[n]. */
72         uint8_t *ipaddr = *(uint8_t**)args[0];
73         return __printf_ipaddr(stream, ipaddr);
74 }
75
76 int printf_ipaddr_info(const struct printf_info* info, size_t n, int *argtypes,
77                        int *size)
78 {
79         /* seems like this is how many va_args we will use, and how big each was
80          * we're supposed to fill up to n, i think.  we're only doing one */
81         if (n > 0) {
82                 argtypes[0] = PA_POINTER;
83                 size[0] = sizeof(uint8_t*);
84         }
85         /* returns the nr of args required by the format string, no matter what */
86         return 1;
87 }
88
89 int printf_ipmask(FILE *stream, const struct printf_info *info,
90                   const void *const *args)
91 {
92         enum {
93                 Isprefix = 16,
94         };
95         static uint8_t prefixvals[256] = {
96                 [0x00] 0 | Isprefix,
97                 [0x80] 1 | Isprefix,
98                 [0xC0] 2 | Isprefix,
99                 [0xE0] 3 | Isprefix,
100                 [0xF0] 4 | Isprefix,
101                 [0xF8] 5 | Isprefix,
102                 [0xFC] 6 | Isprefix,
103                 [0xFE] 7 | Isprefix,
104                 [0xFF] 8 | Isprefix,
105         };
106
107         uint8_t *ipmask = *(uint8_t**)args[0];
108         int i, j, n;
109         /* look for a prefix mask */
110         for (i = 0; i < 16; i++)
111                 if (ipmask[i] != 0xff)
112                         break;
113         if (i < 16) {
114                 if ((prefixvals[ipmask[i]] & Isprefix) == 0)
115                         return __printf_ipaddr(stream, ipmask);
116                 for (j = i + 1; j < 16; j++)
117                         if (ipmask[j] != 0)
118                                 return __printf_ipaddr(stream, ipmask);
119                 n = 8 * i + (prefixvals[ipmask[i]] & ~Isprefix);
120         } else
121                 n = 8 * 16;
122         /* got one, use /xx format */
123         return fprintf(stream, "/%d", n);
124 }
125
126 int printf_ipmask_info(const struct printf_info* info, size_t n, int *argtypes,
127                        int *size)
128 {
129         if (n > 0) {
130                 argtypes[0] = PA_POINTER;
131                 size[0] = sizeof(uint8_t*);
132         }
133         return 1;
134 }
135
136 int printf_ethaddr(FILE *stream, const struct printf_info *info,
137                    const void *const *args)
138 {
139         uint8_t *e = *(uint8_t**)args[0];
140         if (!e)
141                 e = "\0\0\0\0\0\0";
142         return fprintf(stream, "%02x:%02x:%02x:%02x:%02x:%02x", e[0], e[1], e[2],
143                        e[3], e[4], e[5]);
144 }
145
146 int printf_ethaddr_info(const struct printf_info* info, size_t n, int *argtypes,
147                         int *size)
148 {
149         if (n > 0) {
150                 argtypes[0] = PA_POINTER;
151                 size[0] = sizeof(uint8_t*);
152         }
153         return 1;
154 }
155
156 int printf_errstr(FILE *stream, const struct printf_info *info,
157                   const void *const *args)
158 {
159         return fprintf(stream, "%s", errstr());
160 }
161
162 int printf_errstr_info(const struct printf_info* info, size_t n, int *argtypes,
163                        int *size)
164 {
165         /* errstr consumes no arguments */
166         return 0;
167 }