| /* 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); |
| * |
| * The ipmask and ip parsing are adapted from Ron's 9ns code, which was adapted |
| * from plan9/nixip. */ |
| |
| #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, n, 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 */ |
| n = 0; |
| for (i = 0; i < 16; i += 2) { |
| if (i == eli) { |
| ret += fprintf(stream, ":"); |
| ret += fprintf(stream, ":"); |
| n += 2; |
| i += eln; |
| if (i >= 16) |
| break; |
| } else if (i != 0) |
| ret += fprintf(stream, ":"); |
| n++; |
| s = (ipaddr[i] << 8) + ipaddr[i + 1]; |
| ret += fprintf(stream, "%x", s); |
| n += 4; |
| } |
| 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 *up = *(uint8_t**)args[0]; |
| return __printf_ipaddr(stream, up); |
| } |
| |
| 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 *up = *(uint8_t**)args[0]; |
| int i, j, n; |
| /* look for a prefix mask */ |
| for (i = 0; i < 16; i++) |
| if (up[i] != 0xff) |
| break; |
| if (i < 16) { |
| if ((prefixvals[up[i]] & Isprefix) == 0) |
| return __printf_ipaddr(stream, up); |
| for (j = i + 1; j < 16; j++) |
| if (up[j] != 0) |
| return __printf_ipaddr(stream, up); |
| n = 8 * i + (prefixvals[up[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 *up = *(uint8_t**)args[0]; |
| if (!up) |
| up = "\0\0\0\0\0\0"; |
| return fprintf(stream, "%02x:%02x:%02x:%02x:%02x:%02x", up[0], up[1], up[2], |
| up[3], up[4], up[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; |
| } |
| |