|  | /* Copyright (c) 2013-14 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); | 
|  | * | 
|  | * __printf_ipaddr, printf_ipmask, and printf_ethaddr adapted from Inferno's | 
|  | * eipconvtest.c.  Their copyright: | 
|  | * | 
|  | * Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved. | 
|  | * Portions Copyright © 1997-1999 Vita Nuova Limited | 
|  | * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited | 
|  | *                                (www.vitanuova.com) | 
|  | * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
|  | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
|  | * SOFTWARE. */ | 
|  |  | 
|  | #include <parlib/printf-ext.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <stdlib.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; | 
|  | } | 
|  |  | 
|  | static char num_to_nibble(unsigned int x) | 
|  | { | 
|  | return "0123456789abcdef"[x & 0xf]; | 
|  | } | 
|  |  | 
|  | int printf_hexdump(FILE *stream, const struct printf_info *info, | 
|  | const void *const *args) | 
|  | { | 
|  | uint8_t *arg = *(uint8_t**)args[0]; | 
|  | char *buf, *p; | 
|  | int ret; | 
|  |  | 
|  | /* 3 chars per byte, one for the space */ | 
|  | buf = malloc(3 * info->prec); | 
|  | p = buf; | 
|  | for (int i = 0; i < info->prec; i++) { | 
|  | if (i) | 
|  | *p++ = ' '; | 
|  | *p++ = num_to_nibble(*arg >> 4); | 
|  | *p++ = num_to_nibble(*arg); | 
|  | arg++; | 
|  | } | 
|  | ret =  fwrite(buf, 1, p - buf, stream); | 
|  | free(buf); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int printf_hexdump_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; | 
|  | } |