bb51f5dfa42ac3d3de4f2403191e8eaa591e0bd4
[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  * Common printf format extensions.  For now, %r is installed by default
6  * (in early init code), and the others need to be requested.
7  *
8  * To register, for example %i for ipaddr, call:
9  *      register_printf_specifier('i', printf_ipaddr, printf_ipaddr_info);
10  *
11  * __printf_ipaddr, printf_ipmask, and printf_ethaddr adapted from Inferno's
12  * eipconvtest.c.  Their copyright:
13  *
14  * Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
15  * Portions Copyright © 1997-1999 Vita Nuova Limited
16  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
17  *                                (www.vitanuova.com)
18  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining a copy
21  * of this software and associated documentation files (the "Software"), to deal
22  * in the Software without restriction, including without limitation the rights
23  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24  * copies of the Software, and to permit persons to whom the Software is
25  * furnished to do so, subject to the following conditions:
26  *
27  * The above copyright notice and this permission notice shall be included in
28  * all copies or substantial portions of the Software.
29  *
30  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
33  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36  * SOFTWARE. */
37
38 #include <parlib/printf-ext.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <stdlib.h>
43
44 static bool is_ipv4(uint8_t *ipaddr)
45 {
46         uint8_t v4prefix[] = {
47                 0, 0, 0, 0,
48                 0, 0, 0, 0,
49                 0, 0, 0xff, 0xff
50         };
51         return memcmp(ipaddr, v4prefix, sizeof(v4prefix)) == 0;
52 }
53
54 /* Helper, prints a formatted ipaddr to stream. */
55 static int __printf_ipaddr(FILE *stream, uint8_t *ipaddr)
56 {
57         int i, j, eln, eli;
58         int ret = 0;
59         uint16_t s;
60
61         if (is_ipv4(ipaddr))
62                 return fprintf(stream, "%d.%d.%d.%d", ipaddr[12], ipaddr[13],
63                                ipaddr[14], ipaddr[15]);
64         /* find longest elision */
65         eln = eli = -1;
66         for (i = 0; i < 16; i += 2) {
67                 for (j = i; j < 16; j += 2)
68                         if (ipaddr[j] != 0 || ipaddr[j + 1] != 0)
69                                 break;
70                 if (j > i && j - i > eln) {
71                         eli = i;
72                         eln = j - i;
73                 }
74         }
75         /* print with possible elision */
76         for (i = 0; i < 16; i += 2) {
77                 if (i == eli) {
78                         ret += fprintf(stream, "::");
79                         i += eln;
80                         if (i >= 16)
81                                 break;
82                 } else if (i != 0)
83                         ret += fprintf(stream, ":");
84                 s = (ipaddr[i] << 8) + ipaddr[i + 1];
85                 ret += fprintf(stream, "%x", s);
86         }
87         return ret;
88 }
89
90 int printf_ipaddr(FILE *stream, const struct printf_info *info,
91                   const void *const *args)
92 {
93         /* args is an array of pointers, each of which points to an arg.
94          * to extract: TYPE x = *(TYPE*)args[n]. */
95         uint8_t *ipaddr = *(uint8_t**)args[0];
96         return __printf_ipaddr(stream, ipaddr);
97 }
98
99 int printf_ipaddr_info(const struct printf_info* info, size_t n, int *argtypes,
100                        int *size)
101 {
102         /* seems like this is how many va_args we will use, and how big each was
103          * we're supposed to fill up to n, i think.  we're only doing one */
104         if (n > 0) {
105                 argtypes[0] = PA_POINTER;
106                 size[0] = sizeof(uint8_t*);
107         }
108         /* returns the nr of args required by the format string, no matter what
109          */
110         return 1;
111 }
112
113 int printf_ipmask(FILE *stream, const struct printf_info *info,
114                   const void *const *args)
115 {
116         enum {
117                 Isprefix = 16,
118         };
119         static uint8_t prefixvals[256] = {
120                 [0x00] 0 | Isprefix,
121                 [0x80] 1 | Isprefix,
122                 [0xC0] 2 | Isprefix,
123                 [0xE0] 3 | Isprefix,
124                 [0xF0] 4 | Isprefix,
125                 [0xF8] 5 | Isprefix,
126                 [0xFC] 6 | Isprefix,
127                 [0xFE] 7 | Isprefix,
128                 [0xFF] 8 | Isprefix,
129         };
130
131         uint8_t *ipmask = *(uint8_t**)args[0];
132         int i, j, n;
133         /* look for a prefix mask */
134         for (i = 0; i < 16; i++)
135                 if (ipmask[i] != 0xff)
136                         break;
137         if (i < 16) {
138                 if ((prefixvals[ipmask[i]] & Isprefix) == 0)
139                         return __printf_ipaddr(stream, ipmask);
140                 for (j = i + 1; j < 16; j++)
141                         if (ipmask[j] != 0)
142                                 return __printf_ipaddr(stream, ipmask);
143                 n = 8 * i + (prefixvals[ipmask[i]] & ~Isprefix);
144         } else
145                 n = 8 * 16;
146         /* got one, use /xx format */
147         return fprintf(stream, "/%d", n);
148 }
149
150 int printf_ipmask_info(const struct printf_info* info, size_t n, int *argtypes,
151                        int *size)
152 {
153         if (n > 0) {
154                 argtypes[0] = PA_POINTER;
155                 size[0] = sizeof(uint8_t*);
156         }
157         return 1;
158 }
159
160 int printf_ethaddr(FILE *stream, const struct printf_info *info,
161                    const void *const *args)
162 {
163         uint8_t *e = *(uint8_t**)args[0];
164
165         if (!e)
166                 e = "\0\0\0\0\0\0";
167         return fprintf(stream, "%02x:%02x:%02x:%02x:%02x:%02x",
168                        e[0], e[1], e[2], e[3], e[4], e[5]);
169 }
170
171 int printf_ethaddr_info(const struct printf_info* info, size_t n, int *argtypes,
172                         int *size)
173 {
174         if (n > 0) {
175                 argtypes[0] = PA_POINTER;
176                 size[0] = sizeof(uint8_t*);
177         }
178         return 1;
179 }
180
181 int printf_errstr(FILE *stream, const struct printf_info *info,
182                   const void *const *args)
183 {
184         return fprintf(stream, "%s", errstr());
185 }
186
187 int printf_errstr_info(const struct printf_info* info, size_t n, int *argtypes,
188                        int *size)
189 {
190         /* errstr consumes no arguments */
191         return 0;
192 }
193
194 static char num_to_nibble(unsigned int x)
195 {
196         return "0123456789abcdef"[x & 0xf];
197 }
198
199 int printf_hexdump(FILE *stream, const struct printf_info *info,
200                    const void *const *args)
201 {
202         uint8_t *arg = *(uint8_t**)args[0];
203         char *buf, *p;
204         int ret;
205
206         /* 3 chars per byte, one for the space */
207         buf = malloc(3 * info->prec);
208         p = buf;
209         for (int i = 0; i < info->prec; i++) {
210                 if (i)
211                         *p++ = ' ';
212                 *p++ = num_to_nibble(*arg >> 4);
213                 *p++ = num_to_nibble(*arg);
214                 arg++;
215         }
216         ret =  fwrite(buf, 1, p - buf, stream);
217         free(buf);
218         return ret;
219 }
220
221 int printf_hexdump_info(const struct printf_info *info, size_t n, int *argtypes,
222                         int *size)
223 {
224         if (n > 0) {
225                 argtypes[0] = PA_POINTER;
226                 size[0] = sizeof(uint8_t*);
227         }
228         return 1;
229 }