Fri Dec 6 19:36:22 2019 UTC ()
Formalize that the printf formats should be uintmax_t so we can
uniformly use 'j' in the user-provided formatting strings instead
of depending on _LP64 to use 'll' or 'l' (and the PRI macros). The
alternative is to parse the printf format manually to determine
which modifier to apply which would make this transparent to the
user (they could still always use '%u' or '%x'), but that's too
painful.


(christos)
diff -r1.21 -r1.22 src/common/lib/libutil/snprintb.c

cvs diff -r1.21 -r1.22 src/common/lib/libutil/snprintb.c (switch to unified diff)

--- src/common/lib/libutil/snprintb.c 2019/06/17 17:03:58 1.21
+++ src/common/lib/libutil/snprintb.c 2019/12/06 19:36:22 1.22
@@ -1,298 +1,298 @@ @@ -1,298 +1,298 @@
1/* $NetBSD: snprintb.c,v 1.21 2019/06/17 17:03:58 christos Exp $ */ 1/* $NetBSD: snprintb.c,v 1.22 2019/12/06 19:36:22 christos Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
15 * 15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 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 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 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 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 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 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 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29/* 29/*
30 * snprintb: print an interpreted bitmask to a buffer 30 * snprintb: print an interpreted bitmask to a buffer
31 * 31 *
32 * => returns the length of the buffer that would be required to print the 32 * => returns the length of the buffer that would be required to print the
33 * string minus the terminating NUL. 33 * string minus the terminating NUL.
34 */ 34 */
35#ifndef _STANDALONE 35#ifndef _STANDALONE
36# ifndef _KERNEL 36# ifndef _KERNEL
37 37
38# if HAVE_NBTOOL_CONFIG_H 38# if HAVE_NBTOOL_CONFIG_H
39# include "nbtool_config.h" 39# include "nbtool_config.h"
40# endif 40# endif
41 41
42# include <sys/cdefs.h> 42# include <sys/cdefs.h>
43# if defined(LIBC_SCCS) && !defined(lint) 43# if defined(LIBC_SCCS) && !defined(lint)
44__RCSID("$NetBSD: snprintb.c,v 1.21 2019/06/17 17:03:58 christos Exp $"); 44__RCSID("$NetBSD: snprintb.c,v 1.22 2019/12/06 19:36:22 christos Exp $");
45# endif 45# endif
46 46
47# include <sys/types.h> 47# include <sys/types.h>
48# include <inttypes.h> 48# include <inttypes.h>
49# include <stdio.h> 49# include <stdio.h>
50# include <util.h> 50# include <util.h>
51# include <errno.h> 51# include <errno.h>
52# else /* ! _KERNEL */ 52# else /* ! _KERNEL */
53# include <sys/cdefs.h> 53# include <sys/cdefs.h>
54__KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.21 2019/06/17 17:03:58 christos Exp $"); 54__KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.22 2019/12/06 19:36:22 christos Exp $");
55# include <sys/param.h> 55# include <sys/param.h>
56# include <sys/inttypes.h> 56# include <sys/inttypes.h>
57# include <sys/systm.h> 57# include <sys/systm.h>
58# include <lib/libkern/libkern.h> 58# include <lib/libkern/libkern.h>
59# endif /* ! _KERNEL */ 59# endif /* ! _KERNEL */
60 60
61# ifndef HAVE_SNPRINTB_M 61# ifndef HAVE_SNPRINTB_M
62int 62int
63snprintb_m(char *buf, size_t buflen, const char *bitfmt, uint64_t val, 63snprintb_m(char *buf, size_t buflen, const char *bitfmt, uint64_t val,
64 size_t l_max) 64 size_t l_max)
65{ 65{
66 char *bp = buf, *s_bp = NULL; 66 char *bp = buf, *s_bp = NULL;
67 const char *c_fmt, *s_fmt = NULL, *cur_fmt; 67 const char *c_fmt, *s_fmt = NULL, *cur_fmt;
68 const char *sbase; 68 const char *sbase;
69 int bit, ch, t_len, s_len = 0, l_len, f_len, v_len, sep; 69 int bit, ch, t_len, s_len = 0, l_len, f_len, v_len, sep;
70 int restart = 0, matched = 1; 70 int restart = 0, matched = 1;
71 uint64_t field; 71 uint64_t field;
72 72
73#ifdef _KERNEL 73#ifdef _KERNEL
74 /* 74 /*
75 * For safety; no other *s*printf() do this, but in the kernel 75 * For safety; no other *s*printf() do this, but in the kernel
76 * we don't usually check the return value 76 * we don't usually check the return value
77 */ 77 */
78 (void)memset(buf, 0, buflen); 78 (void)memset(buf, 0, buflen);
79#endif /* _KERNEL */ 79#endif /* _KERNEL */
80 80
81 ch = *bitfmt++; 81 ch = *bitfmt++;
82 switch (ch != '\177' ? ch : *bitfmt++) { 82 switch (ch != '\177' ? ch : *bitfmt++) {
83 case 8: 83 case 8:
84 sbase = "0%" PRIo64; 84 sbase = "%#jo";
85 break; 85 break;
86 case 10: 86 case 10:
87 sbase = "%" PRId64; 87 sbase = "%ju";
88 break; 88 break;
89 case 16: 89 case 16:
90 sbase = "0x%" PRIx64; 90 sbase = "%#jx";
91 break; 91 break;
92 default: 92 default:
93 goto internal; 93 goto internal;
94 } 94 }
95 95
96 /* Reserve space for trailing blank line if needed */ 96 /* Reserve space for trailing blank line if needed */
97 if (l_max > 0) 97 if (l_max > 0)
98 buflen--; 98 buflen--;
99 99
100 t_len = snprintf(bp, buflen, sbase, val); 100 t_len = snprintf(bp, buflen, sbase, (uintmax_t)val);
101 if (t_len < 0) 101 if (t_len < 0)
102 goto internal; 102 goto internal;
103 103
104 v_len = l_len = t_len; 104 v_len = l_len = t_len;
105 105
106 if ((size_t)t_len < buflen) 106 if ((size_t)t_len < buflen)
107 bp += t_len; 107 bp += t_len;
108 else 108 else
109 bp += buflen - 1; 109 bp += buflen - 1;
110 110
111 /* 111 /*
112 * If the value we printed was 0 and we're using the old-style format, 112 * If the value we printed was 0 and we're using the old-style format,
113 * we're done. 113 * we're done.
114 */ 114 */
115 if ((val == 0) && (ch != '\177')) 115 if ((val == 0) && (ch != '\177'))
116 goto terminate; 116 goto terminate;
117 117
118#define STORE(c) do { l_len++; \ 118#define STORE(c) do { l_len++; \
119 if ((size_t)(++t_len) < buflen) \ 119 if ((size_t)(++t_len) < buflen) \
120 *bp++ = (c); \ 120 *bp++ = (c); \
121 } while ( /* CONSTCOND */ 0) 121 } while ( /* CONSTCOND */ 0)
122 122
123#define BACKUP do { if (s_bp != NULL) { \ 123#define BACKUP do { if (s_bp != NULL) { \
124 bp = s_bp; s_bp = NULL; \ 124 bp = s_bp; s_bp = NULL; \
125 t_len -= l_len - s_len; \ 125 t_len -= l_len - s_len; \
126 restart = 1; \ 126 restart = 1; \
127 bitfmt = s_fmt; \ 127 bitfmt = s_fmt; \
128 } \ 128 } \
129 STORE('>'); STORE('\0'); \ 129 STORE('>'); STORE('\0'); \
130 if ((size_t)t_len < buflen) \ 130 if ((size_t)t_len < buflen) \
131 snprintf(bp, buflen - t_len, sbase, val); \ 131 snprintf(bp, buflen - t_len, sbase, (uintmax_t)val);\
132 t_len += v_len; l_len = v_len; bp += v_len; \ 132 t_len += v_len; l_len = v_len; bp += v_len; \
133 } while ( /* CONSTCOND */ 0) 133 } while ( /* CONSTCOND */ 0)
134 134
135#define PUTSEP do { \ 135#define PUTSEP do { \
136 if (l_max > 0 && (size_t)l_len >= l_max) { \ 136 if (l_max > 0 && (size_t)l_len >= l_max) { \
137 BACKUP; \ 137 BACKUP; \
138 STORE('<'); \ 138 STORE('<'); \
139 } else { \ 139 } else { \
140 /* Remember separator location */ \ 140 /* Remember separator location */ \
141 if (l_max > 0 && sep != '<') { \ 141 if (l_max > 0 && sep != '<') { \
142 s_len = l_len; \ 142 s_len = l_len; \
143 s_bp = bp; \ 143 s_bp = bp; \
144 s_fmt = cur_fmt; \ 144 s_fmt = cur_fmt; \
145 } \ 145 } \
146 STORE(sep); \ 146 STORE(sep); \
147 restart = 0; \ 147 restart = 0; \
148 } \ 148 } \
149 } while ( /* CONSTCOND */ 0) 149 } while ( /* CONSTCOND */ 0)
150 150
151#define PUTCHR(c) do { \ 151#define PUTCHR(c) do { \
152 if (l_max > 0 && (size_t)l_len >= (l_max - 1)) {\ 152 if (l_max > 0 && (size_t)l_len >= (l_max - 1)) {\
153 BACKUP; \ 153 BACKUP; \
154 if (restart == 0) \ 154 if (restart == 0) \
155 STORE(c); \ 155 STORE(c); \
156 else \ 156 else \
157 sep = '<'; \ 157 sep = '<'; \
158 } else { \ 158 } else { \
159 STORE(c); \ 159 STORE(c); \
160 restart = 0; \ 160 restart = 0; \
161 } \ 161 } \
162 } while ( /* CONSTCOND */ 0) 162 } while ( /* CONSTCOND */ 0)
163 163
164#define PUTS(s) while ((ch = *(s)++) != 0) { \ 164#define PUTS(s) while ((ch = *(s)++) != 0) { \
165 PUTCHR(ch); \ 165 PUTCHR(ch); \
166 if (restart) \ 166 if (restart) \
167 break; \ 167 break; \
168 } 168 }
169#define FMTSTR(sb, f) \ 169#define FMTSTR(sb, f) \
170 do { \ 170 do { \
171 f_len = snprintf(bp, buflen - t_len, sb, f); \ 171 f_len = snprintf(bp, buflen - t_len, sb, (uintmax_t)f); \
172 if (f_len < 0) \ 172 if (f_len < 0) \
173 goto internal; \ 173 goto internal; \
174 t_len += f_len; \ 174 t_len += f_len; \
175 l_len += f_len; \ 175 l_len += f_len; \
176 if ((size_t)t_len < buflen) \ 176 if ((size_t)t_len < buflen) \
177 bp += f_len; \ 177 bp += f_len; \
178 } while (/*CONSTCOND*/0) 178 } while (/*CONSTCOND*/0)
179 179
180 /* 180 /*
181 * Chris Torek's new bitmask format is identified by a leading \177 181 * Chris Torek's new bitmask format is identified by a leading \177
182 */ 182 */
183 sep = '<'; 183 sep = '<';
184 if (ch != '\177') { 184 if (ch != '\177') {
185 /* old (standard) format. */ 185 /* old (standard) format. */
186 for (;(bit = *bitfmt) != 0;) { 186 for (;(bit = *bitfmt) != 0;) {
187 cur_fmt = bitfmt++; 187 cur_fmt = bitfmt++;
188 if (val & (1U << (bit - 1))) { 188 if (val & (1U << (bit - 1))) {
189 PUTSEP; 189 PUTSEP;
190 if (restart) 190 if (restart)
191 continue; 191 continue;
192 sep = ','; 192 sep = ',';
193 for (; (ch = *bitfmt) > ' '; ++bitfmt) { 193 for (; (ch = *bitfmt) > ' '; ++bitfmt) {
194 PUTCHR(ch);  194 PUTCHR(ch);
195 if (restart) 195 if (restart)
196 break; 196 break;
197 } 197 }
198 } else 198 } else
199 for (; *bitfmt > ' '; ++bitfmt) 199 for (; *bitfmt > ' '; ++bitfmt)
200 continue; 200 continue;
201 } 201 }
202 } else { 202 } else {
203 /* new quad-capable format; also does fields. */ 203 /* new quad-capable format; also does fields. */
204 field = val; 204 field = val;
205 while (c_fmt = bitfmt, (ch = *bitfmt++) != '\0') { 205 while (c_fmt = bitfmt, (ch = *bitfmt++) != '\0') {
206 bit = *bitfmt++; /* now 0-origin */ 206 bit = *bitfmt++; /* now 0-origin */
207 switch (ch) { 207 switch (ch) {
208 case 'b': 208 case 'b':
209 if (((unsigned int)(val >> bit) & 1) == 0) 209 if (((unsigned int)(val >> bit) & 1) == 0)
210 goto skip; 210 goto skip;
211 cur_fmt = c_fmt; 211 cur_fmt = c_fmt;
212 PUTSEP; 212 PUTSEP;
213 if (restart) 213 if (restart)
214 break; 214 break;
215 PUTS(bitfmt); 215 PUTS(bitfmt);
216 if (restart == 0) 216 if (restart == 0)
217 sep = ','; 217 sep = ',';
218 break; 218 break;
219 case 'f': 219 case 'f':
220 case 'F': 220 case 'F':
221 matched = 0; 221 matched = 0;
222 cur_fmt = c_fmt; 222 cur_fmt = c_fmt;
223 f_len = *bitfmt++; /* field length */ 223 f_len = *bitfmt++; /* field length */
224 field = (val >> bit) & 224 field = (val >> bit) &
225 (((uint64_t)1 << f_len) - 1); 225 (((uint64_t)1 << f_len) - 1);
226 PUTSEP; 226 PUTSEP;
227 if (restart == 0) 227 if (restart == 0)
228 sep = ','; 228 sep = ',';
229 if (ch == 'F') { /* just extract */ 229 if (ch == 'F') { /* just extract */
230 /* duplicate PUTS() effect on bitfmt */ 230 /* duplicate PUTS() effect on bitfmt */
231 while (*bitfmt++ != '\0') 231 while (*bitfmt++ != '\0')
232 continue; 232 continue;
233 break; 233 break;
234 } 234 }
235 if (restart == 0) 235 if (restart == 0)
236 PUTS(bitfmt); 236 PUTS(bitfmt);
237 if (restart == 0) 237 if (restart == 0)
238 PUTCHR('='); 238 PUTCHR('=');
239 if (restart == 0) { 239 if (restart == 0) {
240 FMTSTR(sbase, field); 240 FMTSTR(sbase, field);
241 if (l_max > 0 && (size_t)l_len > l_max) 241 if (l_max > 0 && (size_t)l_len > l_max)
242 PUTCHR('#'); 242 PUTCHR('#');
243 } 243 }
244 break; 244 break;
245 case '=': 245 case '=':
246 case ':': 246 case ':':
247 /* 247 /*
248 * Here "bit" is actually a value instead, 248 * Here "bit" is actually a value instead,
249 * to be compared against the last field. 249 * to be compared against the last field.
250 * This only works for values in [0..255], 250 * This only works for values in [0..255],
251 * of course. 251 * of course.
252 */ 252 */
253 if ((int)field != bit) 253 if ((int)field != bit)
254 goto skip; 254 goto skip;
255 matched = 1; 255 matched = 1;
256 if (ch == '=') 256 if (ch == '=')
257 PUTCHR('='); 257 PUTCHR('=');
258 PUTS(bitfmt); 258 PUTS(bitfmt);
259 break; 259 break;
260 case '*': 260 case '*':
261 bitfmt--; 261 bitfmt--;
262 if (!matched) { 262 if (!matched) {
263 matched = 1; 263 matched = 1;
264 FMTSTR(bitfmt, field); 264 FMTSTR(bitfmt, field);
265 } 265 }
266 /*FALLTHROUGH*/ 266 /*FALLTHROUGH*/
267 default: 267 default:
268 skip: 268 skip:
269 while (*bitfmt++ != '\0') 269 while (*bitfmt++ != '\0')
270 continue; 270 continue;
271 break; 271 break;
272 } 272 }
273 } 273 }
274 } 274 }
275 l_len++; 275 l_len++;
276 if (sep != '<' && (size_t)(++t_len) < buflen) 276 if (sep != '<' && (size_t)(++t_len) < buflen)
277 *bp++ = '>'; 277 *bp++ = '>';
278terminate: 278terminate:
279 *bp++ = '\0'; 279 *bp++ = '\0';
280 if (l_max != 0) { 280 if (l_max != 0) {
281 t_len++; 281 t_len++;
282 *bp = '\0'; 282 *bp = '\0';
283 } 283 }
284 return t_len; 284 return t_len;
285internal: 285internal:
286#ifndef _KERNEL 286#ifndef _KERNEL
287 errno = EINVAL; 287 errno = EINVAL;
288#endif 288#endif
289 return -1; 289 return -1;
290} 290}
291 291
292int 292int
293snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val) 293snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val)
294{ 294{
295 return snprintb_m(buf, buflen, bitfmt, val, 0); 295 return snprintb_m(buf, buflen, bitfmt, val, 0);
296} 296}
297# endif /* ! HAVE_SNPRINTB_M */ 297# endif /* ! HAVE_SNPRINTB_M */
298#endif /* ! _STANDALONE */ 298#endif /* ! _STANDALONE */