1 /* $NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2024 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 #ifndef _STANDALONE
30 # ifndef _KERNEL
31
32 # if HAVE_NBTOOL_CONFIG_H
33 # include "nbtool_config.h"
34 # endif
35
36 # include <sys/cdefs.h>
37 # if defined(LIBC_SCCS)
38 __RCSID("$NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $");
39 # endif
40
41 # include <sys/types.h>
42 # include <inttypes.h>
43 # include <stdio.h>
44 # include <string.h>
45 # include <util.h>
46 # include <errno.h>
47 # else /* ! _KERNEL */
48 # include <sys/cdefs.h>
49 __KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $");
50 # include <sys/param.h>
51 # include <sys/inttypes.h>
52 # include <sys/systm.h>
53 # include <lib/libkern/libkern.h>
54 # endif /* ! _KERNEL */
55
56 # ifndef HAVE_SNPRINTB_M
57 typedef struct {
58 char *const buf;
59 size_t const bufsize;
60 const char *bitfmt;
61 uint64_t const val;
62 size_t const line_max;
63
64 char num_fmt[5];
65 size_t total_len;
66 size_t line_pos;
67 size_t comma_pos;
68 int in_angle_brackets;
69 } state;
70
71 static void
store(state * s,char c)72 store(state *s, char c)
73 {
74 if (s->total_len < s->bufsize)
75 s->buf[s->total_len] = c;
76 s->total_len++;
77 }
78
79 static int
store_num(state * s,const char * fmt,uintmax_t num)80 store_num(state *s, const char *fmt, uintmax_t num)
81 {
82 int num_len = s->total_len < s->bufsize
83 ? snprintf(s->buf + s->total_len, s->bufsize - s->total_len,
84 fmt, num)
85 : snprintf(NULL, 0, fmt, num);
86 if (num_len > 0)
87 s->total_len += num_len;
88 return num_len;
89 }
90
91 static void
store_eol(state * s)92 store_eol(state *s)
93 {
94 if (s->total_len - s->line_pos > s->line_max) {
95 s->total_len = s->line_pos + s->line_max - 1;
96 store(s, '#');
97 }
98 store(s, '\0');
99 s->line_pos = s->total_len;
100 s->comma_pos = 0;
101 s->in_angle_brackets = 0;
102 }
103
104 static void
store_delimiter(state * s)105 store_delimiter(state *s)
106 {
107 if (s->in_angle_brackets) {
108 s->comma_pos = s->total_len;
109 store(s, ',');
110 } else {
111 store(s, '<');
112 s->in_angle_brackets = 1;
113 }
114 }
115
116 static void
maybe_wrap_line(state * s,const char * bitfmt)117 maybe_wrap_line(state *s, const char *bitfmt)
118 {
119 if (s->line_max > 0
120 && s->comma_pos > 0
121 && s->total_len - s->line_pos >= s->line_max) {
122 s->total_len = s->comma_pos;
123 store(s, '>');
124 store_eol(s);
125 store_num(s, s->num_fmt, s->val);
126 s->bitfmt = bitfmt;
127 }
128 }
129
130 static int
old_style(state * s)131 old_style(state *s)
132 {
133 while (*s->bitfmt != '\0') {
134 const char *cur_bitfmt = s->bitfmt;
135 uint8_t bit = *s->bitfmt;
136 if (bit > 32)
137 return -1;
138 if ((uint8_t)cur_bitfmt[1] <= 32)
139 return -1;
140 if (s->val & (1U << (bit - 1))) {
141 store_delimiter(s);
142 while ((uint8_t)*++s->bitfmt > 32)
143 store(s, *s->bitfmt);
144 maybe_wrap_line(s, cur_bitfmt);
145 } else
146 while ((uint8_t)*++s->bitfmt > 32)
147 continue;
148 }
149 return 0;
150 }
151
152 static int
new_style(state * s)153 new_style(state *s)
154 {
155 uint8_t field_kind = 0; // 0 or 'f' or 'F'
156 uint64_t field = 0; // valid if field_kind != '\0'
157 int matched = 1;
158 const char *prev_bitfmt = s->bitfmt;
159 while (*s->bitfmt != '\0') {
160 const char *cur_bitfmt = s->bitfmt;
161 uint8_t kind = cur_bitfmt[0];
162 switch (kind) {
163 case 'b':
164 field_kind = 0;
165 prev_bitfmt = cur_bitfmt;
166 uint8_t b_bit = cur_bitfmt[1];
167 if (b_bit >= 64)
168 return -1;
169 if (cur_bitfmt[2] == '\0')
170 return -1;
171 s->bitfmt += 2;
172 if (((s->val >> b_bit) & 1) == 0)
173 goto skip_description;
174 store_delimiter(s);
175 while (*s->bitfmt++ != '\0')
176 store(s, s->bitfmt[-1]);
177 maybe_wrap_line(s, cur_bitfmt);
178 break;
179 case 'f':
180 case 'F':
181 field_kind = kind;
182 prev_bitfmt = cur_bitfmt;
183 matched = 0;
184 uint8_t f_lsb = cur_bitfmt[1];
185 if (f_lsb >= 64)
186 return -1;
187 uint8_t f_width = cur_bitfmt[2];
188 if (f_width > 64)
189 return -1;
190 if (kind == 'f' && cur_bitfmt[3] == '\0')
191 return -1;
192 field = s->val >> f_lsb;
193 if (f_width < 64)
194 field &= ((uint64_t) 1 << f_width) - 1;
195 s->bitfmt += 3;
196 store_delimiter(s);
197 if (kind == 'F')
198 goto skip_description;
199 while (*s->bitfmt++ != '\0')
200 store(s, s->bitfmt[-1]);
201 store(s, '=');
202 store_num(s, s->num_fmt, field);
203 maybe_wrap_line(s, cur_bitfmt);
204 break;
205 case '=':
206 case ':':
207 s->bitfmt += 2;
208 if (kind == '=' && field_kind != 'f')
209 return -1;
210 if (kind == ':' && field_kind != 'F')
211 return -1;
212 uint8_t cmp = cur_bitfmt[1];
213 if (cur_bitfmt[2] == '\0')
214 return -1;
215 if (field != cmp)
216 goto skip_description;
217 matched = 1;
218 if (kind == '=')
219 store(s, '=');
220 while (*s->bitfmt++ != '\0')
221 store(s, s->bitfmt[-1]);
222 maybe_wrap_line(s, prev_bitfmt);
223 break;
224 case '*':
225 if (field_kind == 0)
226 return -1;
227 field_kind = 0;
228 if (cur_bitfmt[1] == '\0')
229 return -1;
230 s->bitfmt++;
231 if (matched)
232 goto skip_description;
233 matched = 1;
234 if (store_num(s, s->bitfmt, field) < 0)
235 return -1;
236 maybe_wrap_line(s, prev_bitfmt);
237 goto skip_description;
238 default:
239 return -1;
240 skip_description:
241 while (*s->bitfmt++ != '\0')
242 continue;
243 break;
244 }
245 }
246 return 0;
247 }
248
249 static void
finish_buffer(state * s)250 finish_buffer(state *s)
251 {
252 if (s->line_max > 0) {
253 store_eol(s);
254 store(s, '\0');
255 if (s->total_len <= s->bufsize)
256 return;
257 if (s->bufsize >= 3)
258 s->buf[s->bufsize - 3] = '#';
259 if (s->bufsize >= 2)
260 s->buf[s->bufsize - 2] = '\0';
261 if (s->bufsize >= 1)
262 s->buf[s->bufsize - 1] = '\0';
263 } else {
264 store(s, '\0');
265 if (s->total_len <= s->bufsize)
266 return;
267 if (s->bufsize >= 2)
268 s->buf[s->bufsize - 2] = '#';
269 if (s->bufsize >= 1)
270 s->buf[s->bufsize - 1] = '\0';
271 }
272 }
273
274 int
snprintb_m(char * buf,size_t bufsize,const char * bitfmt,uint64_t val,size_t line_max)275 snprintb_m(char *buf, size_t bufsize, const char *bitfmt, uint64_t val,
276 size_t line_max)
277 {
278 int old = *bitfmt != '\177';
279 if (!old)
280 bitfmt++;
281
282 state s = {
283 .buf = buf,
284 .bufsize = bufsize,
285 .bitfmt = bitfmt,
286 .val = val,
287 .line_max = line_max,
288 };
289 int had_error = 0;
290
291 switch (*s.bitfmt++) {
292 case 8:
293 memcpy(s.num_fmt, "%#jo", 4);
294 break;
295 case 10:
296 memcpy(s.num_fmt, "%ju", 4);
297 break;
298 case 16:
299 memcpy(s.num_fmt, "%#jx", 4);
300 break;
301 default:
302 goto had_error;
303 }
304
305 store_num(&s, s.num_fmt, val);
306
307 if ((old ? old_style(&s) : new_style(&s)) < 0) {
308 had_error:
309 #ifndef _KERNEL
310 errno = EINVAL;
311 #endif
312 had_error = 1;
313 store(&s, '#');
314 } else if (s.in_angle_brackets)
315 store(&s, '>');
316 finish_buffer(&s);
317 return had_error ? -1 : (int)(s.total_len - 1);
318 }
319
320 int
snprintb(char * buf,size_t bufsize,const char * bitfmt,uint64_t val)321 snprintb(char *buf, size_t bufsize, const char *bitfmt, uint64_t val)
322 {
323 return snprintb_m(buf, bufsize, bitfmt, val, 0);
324 }
325 # endif /* ! HAVE_SNPRINTB_M */
326 #endif /* ! _STANDALONE */
327