snprintb.c revision 1.2 1 /* $NetBSD: snprintb.c,v 1.2 2008/12/17 16:02:27 christos 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.2 2008/12/17 16:02:27 christos 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.2 2008/12/17 16:02:27 christos Exp $");
55 # include <sys/types.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 (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 (++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 (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