| /* 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 <parlib/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; | 
 | } |