1 /* $NetBSD: snprintb.c,v 1.4 2009/01/18 12:05:49 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * snprintb: print an interpreted bitmask to a buffer 31 * 32 * => returns the length of the buffer that would be required to print the 33 * string minus the terminating NUL. 34 */ 35 #ifndef _STANDALONE 36 # ifndef _KERNEL 37 38 # if HAVE_NBTOOL_CONFIG_H 39 # include "nbtool_config.h" 40 # endif 41 42 # include <sys/cdefs.h> 43 # if defined(LIBC_SCCS) && !defined(lint) 44 __RCSID("$NetBSD: snprintb.c,v 1.4 2009/01/18 12:05:49 lukem Exp $"); 45 # endif 46 47 # include <sys/types.h> 48 # include <sys/inttypes.h> 49 # include <stdio.h> 50 # include <util.h> 51 # include <errno.h> 52 # else 53 # include <sys/cdefs.h> 54 __KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.4 2009/01/18 12:05:49 lukem Exp $"); 55 # include <sys/param.h> 56 # include <sys/inttypes.h> 57 # include <sys/systm.h> 58 # include <lib/libkern/libkern.h> 59 # endif 60 61 int 62 snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val) 63 { 64 char *bp = buf; 65 const char *sbase; 66 int bit, ch, len, sep, flen; 67 uint64_t field; 68 69 #ifdef _KERNEL 70 /* 71 * For safety; no other *s*printf() do this, but in the kernel 72 * we don't usually check the return value 73 */ 74 (void)memset(buf, 0, buflen); 75 #endif /* _KERNEL */ 76 77 ch = *bitfmt++; 78 switch (ch != '\177' ? ch : *bitfmt++) { 79 case 8: 80 sbase = "0%" PRIo64; 81 break; 82 case 10: 83 sbase = "%" PRId64; 84 break; 85 case 16: 86 sbase = "0x%" PRIx64; 87 break; 88 default: 89 goto internal; 90 } 91 92 len = snprintf(bp, buflen, sbase, val); 93 if (len < 0) 94 goto internal; 95 96 if ((size_t)len < buflen) 97 bp += len; 98 else 99 bp += buflen - 1; 100 101 /* 102 * If the value we printed was 0 and we're using the old-style format, 103 * we're done. 104 */ 105 if ((val == 0) && (ch != '\177')) 106 goto terminate; 107 108 #define PUTC(c) if ((size_t)(++len) < buflen) *bp++ = (c) 109 #define PUTS(s) while ((ch = *(s)++) != 0) PUTC(ch) 110 111 /* 112 * Chris Torek's new bitmask format is identified by a leading \177 113 */ 114 sep = '<'; 115 if (ch != '\177') { 116 /* old (standard) format. */ 117 for (;(bit = *bitfmt++) != 0;) { 118 if (val & (1 << (bit - 1))) { 119 PUTC(sep); 120 for (; (ch = *bitfmt) > ' '; ++bitfmt) 121 PUTC(ch); 122 sep = ','; 123 } else 124 for (; *bitfmt > ' '; ++bitfmt) 125 continue; 126 } 127 } else { 128 /* new quad-capable format; also does fields. */ 129 field = val; 130 while ((ch = *bitfmt++) != '\0') { 131 bit = *bitfmt++; /* now 0-origin */ 132 switch (ch) { 133 case 'b': 134 if (((u_int)(val >> bit) & 1) == 0) 135 goto skip; 136 PUTC(sep); 137 PUTS(bitfmt); 138 sep = ','; 139 break; 140 case 'f': 141 case 'F': 142 flen = *bitfmt++; /* field length */ 143 field = (val >> bit) & 144 (((uint64_t)1 << flen) - 1); 145 if (ch == 'F') /* just extract */ 146 break; 147 PUTC(sep); 148 sep = ','; 149 PUTS(bitfmt); 150 PUTC('='); 151 flen = snprintf(bp, buflen - len, sbase, field); 152 if (flen < 0) 153 goto internal; 154 len += flen; 155 if ((size_t)len < buflen) 156 bp += flen; 157 break; 158 case '=': 159 case ':': 160 /* 161 * Here "bit" is actually a value instead, 162 * to be compared against the last field. 163 * This only works for values in [0..255], 164 * of course. 165 */ 166 if ((int)field != bit) 167 goto skip; 168 if (ch == '=') { 169 PUTC('='); 170 } 171 PUTS(bitfmt); 172 break; 173 default: 174 skip: 175 while (*bitfmt++ != '\0') 176 continue; 177 break; 178 } 179 } 180 } 181 if (sep != '<') { 182 PUTC('>'); 183 } 184 terminate: 185 *bp = '\0'; 186 return len; 187 internal: 188 #ifndef _KERNEL 189 errno = EINVAL; 190 #endif 191 return -1; 192 } 193 #endif 194