cons: Support epolling /dev/null
[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         return 1;
110 }
111
112 int printf_ipmask(FILE *stream, const struct printf_info *info,
113                   const void *const *args)
114 {
115         enum {
116                 Isprefix = 16,
117         };
118         static uint8_t prefixvals[256] = {
119                 [0x00] 0 | Isprefix,
120                 [0x80] 1 | Isprefix,
121                 [0xC0] 2 | Isprefix,
122                 [0xE0] 3 | Isprefix,
123                 [0xF0] 4 | Isprefix,
124                 [0xF8] 5 | Isprefix,
125                 [0xFC] 6 | Isprefix,
126                 [0xFE] 7 | Isprefix,
127                 [0xFF] 8 | Isprefix,
128         };
129
130         uint8_t *ipmask = *(uint8_t**)args[0];
131         int i, j, n;
132         /* look for a prefix mask */
133         for (i = 0; i < 16; i++)
134                 if (ipmask[i] != 0xff)
135                         break;
136         if (i < 16) {
137                 if ((prefixvals[ipmask[i]] & Isprefix) == 0)
138                         return __printf_ipaddr(stream, ipmask);
139                 for (j = i + 1; j < 16; j++)
140                         if (ipmask[j] != 0)
141                                 return __printf_ipaddr(stream, ipmask);
142                 n = 8 * i + (prefixvals[ipmask[i]] & ~Isprefix);
143         } else
144                 n = 8 * 16;
145         /* got one, use /xx format */
146         return fprintf(stream, "/%d", n);
147 }
148
149 int printf_ipmask_info(const struct printf_info* info, size_t n, int *argtypes,
150                        int *size)
151 {
152         if (n > 0) {
153                 argtypes[0] = PA_POINTER;
154                 size[0] = sizeof(uint8_t*);
155         }
156         return 1;
157 }
158
159 int printf_ethaddr(FILE *stream, const struct printf_info *info,
160                    const void *const *args)
161 {
162         uint8_t *e = *(uint8_t**)args[0];
163         if (!e)
164                 e = "\0\0\0\0\0\0";
165         return fprintf(stream, "%02x:%02x:%02x:%02x:%02x:%02x", e[0], e[1], e[2],
166                        e[3], e[4], e[5]);
167 }
168
169 int printf_ethaddr_info(const struct printf_info* info, size_t n, int *argtypes,
170                         int *size)
171 {
172         if (n > 0) {
173                 argtypes[0] = PA_POINTER;
174                 size[0] = sizeof(uint8_t*);
175         }
176         return 1;
177 }
178
179 int printf_errstr(FILE *stream, const struct printf_info *info,
180                   const void *const *args)
181 {
182         return fprintf(stream, "%s", errstr());
183 }
184
185 int printf_errstr_info(const struct printf_info* info, size_t n, int *argtypes,
186                        int *size)
187 {
188         /* errstr consumes no arguments */
189         return 0;
190 }
191
192 static char num_to_nibble(unsigned int x)
193 {
194         return "0123456789abcdef"[x & 0xf];
195 }
196
197 int printf_hexdump(FILE *stream, const struct printf_info *info,
198                    const void *const *args)
199 {
200         uint8_t *arg = *(uint8_t**)args[0];
201         char *buf, *p;
202         int ret;
203
204         /* 3 chars per byte, one for the space */
205         buf = malloc(3 * info->prec);
206         p = buf;
207         for (int i = 0; i < info->prec; i++) {
208                 if (i)
209                         *p++ = ' ';
210                 *p++ = num_to_nibble(*arg >> 4);
211                 *p++ = num_to_nibble(*arg);
212                 arg++;
213         }
214         ret =  fwrite(buf, 1, p - buf, stream);
215         free(buf);
216         return ret;
217 }
218
219 int printf_hexdump_info(const struct printf_info *info, size_t n, int *argtypes,
220                         int *size)
221 {
222         if (n > 0) {
223                 argtypes[0] = PA_POINTER;
224                 size[0] = sizeof(uint8_t*);
225         }
226         return 1;
227 }