1*0a6a1f1dSLionel Sambuc /* $NetBSD: snprintb.c,v 1.16 2014/08/02 11:19:01 ryo Exp $ */
20c3983b2SBen Gras
30c3983b2SBen Gras /*-
40c3983b2SBen Gras * Copyright (c) 2002 The NetBSD Foundation, Inc.
50c3983b2SBen Gras * All rights reserved.
60c3983b2SBen Gras *
70c3983b2SBen Gras * Redistribution and use in source and binary forms, with or without
80c3983b2SBen Gras * modification, are permitted provided that the following conditions
90c3983b2SBen Gras * are met:
100c3983b2SBen Gras * 1. Redistributions of source code must retain the above copyright
110c3983b2SBen Gras * notice, this list of conditions and the following disclaimer.
120c3983b2SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
130c3983b2SBen Gras * notice, this list of conditions and the following disclaimer in the
140c3983b2SBen Gras * documentation and/or other materials provided with the distribution.
150c3983b2SBen Gras *
160c3983b2SBen Gras * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
170c3983b2SBen Gras * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
180c3983b2SBen Gras * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
190c3983b2SBen Gras * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
200c3983b2SBen Gras * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
210c3983b2SBen Gras * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
220c3983b2SBen Gras * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
230c3983b2SBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
240c3983b2SBen Gras * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
250c3983b2SBen Gras * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
260c3983b2SBen Gras * POSSIBILITY OF SUCH DAMAGE.
270c3983b2SBen Gras */
280c3983b2SBen Gras
290c3983b2SBen Gras /*
300c3983b2SBen Gras * snprintb: print an interpreted bitmask to a buffer
310c3983b2SBen Gras *
320c3983b2SBen Gras * => returns the length of the buffer that would be required to print the
330c3983b2SBen Gras * string minus the terminating NUL.
340c3983b2SBen Gras */
350c3983b2SBen Gras #ifndef _STANDALONE
360c3983b2SBen Gras # ifndef _KERNEL
370c3983b2SBen Gras
380c3983b2SBen Gras # if HAVE_NBTOOL_CONFIG_H
390c3983b2SBen Gras # include "nbtool_config.h"
400c3983b2SBen Gras # endif
410c3983b2SBen Gras
420c3983b2SBen Gras # include <sys/cdefs.h>
430c3983b2SBen Gras # if defined(LIBC_SCCS) && !defined(lint)
44*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: snprintb.c,v 1.16 2014/08/02 11:19:01 ryo Exp $");
450c3983b2SBen Gras # endif
460c3983b2SBen Gras
470c3983b2SBen Gras # include <sys/types.h>
4884d9c625SLionel Sambuc # include <inttypes.h>
490c3983b2SBen Gras # include <stdio.h>
500c3983b2SBen Gras # include <util.h>
510c3983b2SBen Gras # include <errno.h>
5284d9c625SLionel Sambuc # else /* ! _KERNEL */
530c3983b2SBen Gras # include <sys/cdefs.h>
54*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.16 2014/08/02 11:19:01 ryo Exp $");
550c3983b2SBen Gras # include <sys/param.h>
560c3983b2SBen Gras # include <sys/inttypes.h>
570c3983b2SBen Gras # include <sys/systm.h>
580c3983b2SBen Gras # include <lib/libkern/libkern.h>
5984d9c625SLionel Sambuc # endif /* ! _KERNEL */
600c3983b2SBen Gras
6184d9c625SLionel Sambuc # ifndef HAVE_SNPRINTB_M
620c3983b2SBen Gras int
snprintb_m(char * buf,size_t buflen,const char * bitfmt,uint64_t val,size_t l_max)630c3983b2SBen Gras snprintb_m(char *buf, size_t buflen, const char *bitfmt, uint64_t val,
640c3983b2SBen Gras size_t l_max)
650c3983b2SBen Gras {
660c3983b2SBen Gras char *bp = buf, *s_bp = NULL;
670c3983b2SBen Gras const char *c_fmt, *s_fmt = NULL, *cur_fmt;
680c3983b2SBen Gras const char *sbase;
690c3983b2SBen Gras int bit, ch, t_len, s_len = 0, l_len, f_len, v_len, sep;
700c3983b2SBen Gras int restart = 0;
710c3983b2SBen Gras uint64_t field;
720c3983b2SBen Gras
730c3983b2SBen Gras #ifdef _KERNEL
740c3983b2SBen Gras /*
750c3983b2SBen Gras * For safety; no other *s*printf() do this, but in the kernel
760c3983b2SBen Gras * we don't usually check the return value
770c3983b2SBen Gras */
780c3983b2SBen Gras (void)memset(buf, 0, buflen);
790c3983b2SBen Gras #endif /* _KERNEL */
800c3983b2SBen Gras
810c3983b2SBen Gras ch = *bitfmt++;
820c3983b2SBen Gras switch (ch != '\177' ? ch : *bitfmt++) {
830c3983b2SBen Gras case 8:
840c3983b2SBen Gras sbase = "0%" PRIo64;
850c3983b2SBen Gras break;
860c3983b2SBen Gras case 10:
870c3983b2SBen Gras sbase = "%" PRId64;
880c3983b2SBen Gras break;
890c3983b2SBen Gras case 16:
900c3983b2SBen Gras sbase = "0x%" PRIx64;
910c3983b2SBen Gras break;
920c3983b2SBen Gras default:
930c3983b2SBen Gras goto internal;
940c3983b2SBen Gras }
950c3983b2SBen Gras
960c3983b2SBen Gras /* Reserve space for trailing blank line if needed */
970c3983b2SBen Gras if (l_max > 0)
980c3983b2SBen Gras buflen--;
990c3983b2SBen Gras
1000c3983b2SBen Gras t_len = snprintf(bp, buflen, sbase, val);
1010c3983b2SBen Gras if (t_len < 0)
1020c3983b2SBen Gras goto internal;
1030c3983b2SBen Gras
1040c3983b2SBen Gras v_len = l_len = t_len;
1050c3983b2SBen Gras
1060c3983b2SBen Gras if ((size_t)t_len < buflen)
1070c3983b2SBen Gras bp += t_len;
1080c3983b2SBen Gras else
1090c3983b2SBen Gras bp += buflen - 1;
1100c3983b2SBen Gras
1110c3983b2SBen Gras /*
1120c3983b2SBen Gras * If the value we printed was 0 and we're using the old-style format,
1130c3983b2SBen Gras * we're done.
1140c3983b2SBen Gras */
1150c3983b2SBen Gras if ((val == 0) && (ch != '\177'))
1160c3983b2SBen Gras goto terminate;
1170c3983b2SBen Gras
11884d9c625SLionel Sambuc #define STORE(c) do { l_len++; \
1190c3983b2SBen Gras if ((size_t)(++t_len) < buflen) \
1200c3983b2SBen Gras *bp++ = (c); \
1210c3983b2SBen Gras } while ( /* CONSTCOND */ 0)
1220c3983b2SBen Gras
12384d9c625SLionel Sambuc #define BACKUP do { if (s_bp != NULL) { \
1240c3983b2SBen Gras bp = s_bp; s_bp = NULL; \
1250c3983b2SBen Gras t_len -= l_len - s_len; \
1260c3983b2SBen Gras restart = 1; \
1270c3983b2SBen Gras bitfmt = s_fmt; \
1280c3983b2SBen Gras } \
1290c3983b2SBen Gras STORE('>'); STORE('\0'); \
1300c3983b2SBen Gras if ((size_t)t_len < buflen) \
1310c3983b2SBen Gras snprintf(bp, buflen - t_len, sbase, val); \
1320c3983b2SBen Gras t_len += v_len; l_len = v_len; bp += v_len; \
1330c3983b2SBen Gras } while ( /* CONSTCOND */ 0)
1340c3983b2SBen Gras
13584d9c625SLionel Sambuc #define PUTSEP do { \
1360c3983b2SBen Gras if (l_max > 0 && (size_t)l_len >= l_max) { \
1370c3983b2SBen Gras BACKUP; \
1380c3983b2SBen Gras STORE('<'); \
1390c3983b2SBen Gras } else { \
1400c3983b2SBen Gras /* Remember separator location */ \
1410c3983b2SBen Gras if (l_max > 0 && sep != '<') { \
1420c3983b2SBen Gras s_len = l_len; \
1430c3983b2SBen Gras s_bp = bp; \
1440c3983b2SBen Gras s_fmt = cur_fmt; \
1450c3983b2SBen Gras } \
1460c3983b2SBen Gras STORE(sep); \
1470c3983b2SBen Gras restart = 0; \
1480c3983b2SBen Gras } \
14984d9c625SLionel Sambuc } while ( /* CONSTCOND */ 0)
1500c3983b2SBen Gras
15184d9c625SLionel Sambuc #define PUTCHR(c) do { \
1520c3983b2SBen Gras if (l_max > 0 && (size_t)l_len >= (l_max - 1)) {\
1530c3983b2SBen Gras BACKUP; \
15484d9c625SLionel Sambuc if (restart == 0) \
1550c3983b2SBen Gras STORE(c); \
15684d9c625SLionel Sambuc else \
1570c3983b2SBen Gras sep = '<'; \
1580c3983b2SBen Gras } else { \
1590c3983b2SBen Gras STORE(c); \
1600c3983b2SBen Gras restart = 0; \
1610c3983b2SBen Gras } \
16284d9c625SLionel Sambuc } while ( /* CONSTCOND */ 0)
1630c3983b2SBen Gras
1640c3983b2SBen Gras #define PUTS(s) while ((ch = *(s)++) != 0) { \
1650c3983b2SBen Gras PUTCHR(ch); \
1660c3983b2SBen Gras if (restart) \
1670c3983b2SBen Gras break; \
1680c3983b2SBen Gras }
1690c3983b2SBen Gras
1700c3983b2SBen Gras /*
1710c3983b2SBen Gras * Chris Torek's new bitmask format is identified by a leading \177
1720c3983b2SBen Gras */
1730c3983b2SBen Gras sep = '<';
1740c3983b2SBen Gras if (ch != '\177') {
1750c3983b2SBen Gras /* old (standard) format. */
1760c3983b2SBen Gras for (;(bit = *bitfmt) != 0;) {
1770c3983b2SBen Gras cur_fmt = bitfmt++;
1780c3983b2SBen Gras if (val & (1 << (bit - 1))) {
1790c3983b2SBen Gras PUTSEP;
1800c3983b2SBen Gras if (restart)
1810c3983b2SBen Gras continue;
1820c3983b2SBen Gras sep = ',';
1830c3983b2SBen Gras for (; (ch = *bitfmt) > ' '; ++bitfmt) {
1840c3983b2SBen Gras PUTCHR(ch);
1850c3983b2SBen Gras if (restart)
1860c3983b2SBen Gras break;
1870c3983b2SBen Gras }
1880c3983b2SBen Gras } else
1890c3983b2SBen Gras for (; *bitfmt > ' '; ++bitfmt)
1900c3983b2SBen Gras continue;
1910c3983b2SBen Gras }
1920c3983b2SBen Gras } else {
1930c3983b2SBen Gras /* new quad-capable format; also does fields. */
1940c3983b2SBen Gras field = val;
1950c3983b2SBen Gras while (c_fmt = bitfmt, (ch = *bitfmt++) != '\0') {
1960c3983b2SBen Gras bit = *bitfmt++; /* now 0-origin */
1970c3983b2SBen Gras switch (ch) {
1980c3983b2SBen Gras case 'b':
19984d9c625SLionel Sambuc if (((unsigned int)(val >> bit) & 1) == 0)
2000c3983b2SBen Gras goto skip;
2010c3983b2SBen Gras cur_fmt = c_fmt;
2020c3983b2SBen Gras PUTSEP;
2030c3983b2SBen Gras if (restart)
2040c3983b2SBen Gras break;
2050c3983b2SBen Gras PUTS(bitfmt);
2060c3983b2SBen Gras if (restart == 0)
2070c3983b2SBen Gras sep = ',';
2080c3983b2SBen Gras break;
2090c3983b2SBen Gras case 'f':
2100c3983b2SBen Gras case 'F':
2110c3983b2SBen Gras cur_fmt = c_fmt;
2120c3983b2SBen Gras f_len = *bitfmt++; /* field length */
2130c3983b2SBen Gras field = (val >> bit) &
2140c3983b2SBen Gras (((uint64_t)1 << f_len) - 1);
215f14fb602SLionel Sambuc PUTSEP;
216f14fb602SLionel Sambuc if (restart == 0)
217f14fb602SLionel Sambuc sep = ',';
2180c3983b2SBen Gras if (ch == 'F') /* just extract */
2190c3983b2SBen Gras break;
22084d9c625SLionel Sambuc if (restart == 0)
2210c3983b2SBen Gras PUTS(bitfmt);
22284d9c625SLionel Sambuc if (restart == 0)
2230c3983b2SBen Gras PUTCHR('=');
2240c3983b2SBen Gras if (restart == 0) {
2250c3983b2SBen Gras f_len = snprintf(bp, buflen - t_len,
2260c3983b2SBen Gras sbase, field);
2270c3983b2SBen Gras if (f_len < 0)
2280c3983b2SBen Gras goto internal;
2290c3983b2SBen Gras t_len += f_len;
2300c3983b2SBen Gras l_len += f_len;
2310c3983b2SBen Gras if ((size_t)t_len < buflen)
2320c3983b2SBen Gras bp += f_len;
23384d9c625SLionel Sambuc if (l_max > 0 && (size_t)l_len > l_max)
2340c3983b2SBen Gras PUTCHR('#');
2350c3983b2SBen Gras }
2360c3983b2SBen Gras break;
2370c3983b2SBen Gras case '=':
2380c3983b2SBen Gras case ':':
2390c3983b2SBen Gras /*
2400c3983b2SBen Gras * Here "bit" is actually a value instead,
2410c3983b2SBen Gras * to be compared against the last field.
2420c3983b2SBen Gras * This only works for values in [0..255],
2430c3983b2SBen Gras * of course.
2440c3983b2SBen Gras */
2450c3983b2SBen Gras if ((int)field != bit)
2460c3983b2SBen Gras goto skip;
24784d9c625SLionel Sambuc if (ch == '=')
2480c3983b2SBen Gras PUTCHR('=');
2490c3983b2SBen Gras PUTS(bitfmt);
2500c3983b2SBen Gras break;
2510c3983b2SBen Gras default:
2520c3983b2SBen Gras skip:
2530c3983b2SBen Gras while (*bitfmt++ != '\0')
2540c3983b2SBen Gras continue;
2550c3983b2SBen Gras break;
2560c3983b2SBen Gras }
2570c3983b2SBen Gras }
2580c3983b2SBen Gras }
2590c3983b2SBen Gras l_len++;
260*0a6a1f1dSLionel Sambuc if (sep != '<' && (size_t)(++t_len) < buflen)
2610c3983b2SBen Gras *bp++ = '>';
2620c3983b2SBen Gras terminate:
2630c3983b2SBen Gras *bp++ = '\0';
2640c3983b2SBen Gras if (l_max != 0) {
2650c3983b2SBen Gras t_len++;
2660c3983b2SBen Gras *bp = '\0';
2670c3983b2SBen Gras }
2680c3983b2SBen Gras return t_len;
2690c3983b2SBen Gras internal:
2700c3983b2SBen Gras #ifndef _KERNEL
2710c3983b2SBen Gras errno = EINVAL;
2720c3983b2SBen Gras #endif
2730c3983b2SBen Gras return -1;
2740c3983b2SBen Gras }
2750c3983b2SBen Gras
2760c3983b2SBen Gras int
snprintb(char * buf,size_t buflen,const char * bitfmt,uint64_t val)2770c3983b2SBen Gras snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val)
2780c3983b2SBen Gras {
2790c3983b2SBen Gras return snprintb_m(buf, buflen, bitfmt, val, 0);
2800c3983b2SBen Gras }
28184d9c625SLionel Sambuc # endif /* ! HAVE_SNPRINTB_M */
28284d9c625SLionel Sambuc #endif /* ! _STANDALONE */
283