xref: /minix3/common/lib/libutil/snprintb.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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