blob: bb51f5dfa42ac3d3de4f2403191e8eaa591e0bd4 [file] [log] [blame]
/* 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;
}