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