1*b215905fSrillig /* $NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $ */
2cec60a58Schristos
3cec60a58Schristos /*-
44734052dSrillig * Copyright (c) 2002, 2024 The NetBSD Foundation, Inc.
5cec60a58Schristos * All rights reserved.
6cec60a58Schristos *
7cec60a58Schristos * Redistribution and use in source and binary forms, with or without
8cec60a58Schristos * modification, are permitted provided that the following conditions
9cec60a58Schristos * are met:
10cec60a58Schristos * 1. Redistributions of source code must retain the above copyright
11cec60a58Schristos * notice, this list of conditions and the following disclaimer.
12cec60a58Schristos * 2. Redistributions in binary form must reproduce the above copyright
13cec60a58Schristos * notice, this list of conditions and the following disclaimer in the
14cec60a58Schristos * documentation and/or other materials provided with the distribution.
15cec60a58Schristos *
16cec60a58Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17cec60a58Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18cec60a58Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19cec60a58Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20cec60a58Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21cec60a58Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22cec60a58Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23cec60a58Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24cec60a58Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25cec60a58Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26cec60a58Schristos * POSSIBILITY OF SUCH DAMAGE.
27cec60a58Schristos */
28cec60a58Schristos
292d2593abSchristos #ifndef _STANDALONE
30cec60a58Schristos # ifndef _KERNEL
31cec60a58Schristos
32cec60a58Schristos # if HAVE_NBTOOL_CONFIG_H
33cec60a58Schristos # include "nbtool_config.h"
34cec60a58Schristos # endif
35cec60a58Schristos
36cec60a58Schristos # include <sys/cdefs.h>
374734052dSrillig # if defined(LIBC_SCCS)
38*b215905fSrillig __RCSID("$NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $");
39cec60a58Schristos # endif
40cec60a58Schristos
41cec60a58Schristos # include <sys/types.h>
4214872396Sagc # include <inttypes.h>
43cec60a58Schristos # include <stdio.h>
44c4ea73f5Srillig # include <string.h>
45cec60a58Schristos # include <util.h>
46cec60a58Schristos # include <errno.h>
473be9df0dSapb # else /* ! _KERNEL */
48cec60a58Schristos # include <sys/cdefs.h>
49*b215905fSrillig __KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $");
50d2196120Spooka # include <sys/param.h>
51cec60a58Schristos # include <sys/inttypes.h>
52cec60a58Schristos # include <sys/systm.h>
53cec60a58Schristos # include <lib/libkern/libkern.h>
543be9df0dSapb # endif /* ! _KERNEL */
55cec60a58Schristos
563be9df0dSapb # ifndef HAVE_SNPRINTB_M
57f48e1c68Srillig typedef struct {
58f48e1c68Srillig char *const buf;
59f48e1c68Srillig size_t const bufsize;
60f48e1c68Srillig const char *bitfmt;
61f48e1c68Srillig uint64_t const val;
62f48e1c68Srillig size_t const line_max;
63f48e1c68Srillig
64c4ea73f5Srillig char num_fmt[5];
654734052dSrillig size_t total_len;
664734052dSrillig size_t line_pos;
674734052dSrillig size_t comma_pos;
684734052dSrillig int in_angle_brackets;
69f48e1c68Srillig } state;
70f48e1c68Srillig
71f48e1c68Srillig static void
store(state * s,char c)72f48e1c68Srillig store(state *s, char c)
73f48e1c68Srillig {
74f48e1c68Srillig if (s->total_len < s->bufsize)
75f48e1c68Srillig s->buf[s->total_len] = c;
76f48e1c68Srillig s->total_len++;
7712034681Srillig }
7812034681Srillig
7912034681Srillig static int
store_num(state * s,const char * fmt,uintmax_t num)8012034681Srillig store_num(state *s, const char *fmt, uintmax_t num)
8112034681Srillig {
8212034681Srillig int num_len = s->total_len < s->bufsize
8312034681Srillig ? snprintf(s->buf + s->total_len, s->bufsize - s->total_len,
8412034681Srillig fmt, num)
8512034681Srillig : snprintf(NULL, 0, fmt, num);
8612034681Srillig if (num_len > 0)
8712034681Srillig s->total_len += num_len;
8812034681Srillig return num_len;
89f48e1c68Srillig }
90f48e1c68Srillig
91f48e1c68Srillig static void
store_eol(state * s)924734052dSrillig store_eol(state *s)
93f48e1c68Srillig {
9412034681Srillig if (s->total_len - s->line_pos > s->line_max) {
954734052dSrillig s->total_len = s->line_pos + s->line_max - 1;
9612034681Srillig store(s, '#');
97f48e1c68Srillig }
98f48e1c68Srillig store(s, '\0');
9912034681Srillig s->line_pos = s->total_len;
10012034681Srillig s->comma_pos = 0;
1014734052dSrillig s->in_angle_brackets = 0;
102f48e1c68Srillig }
103f48e1c68Srillig
104f48e1c68Srillig static void
store_delimiter(state * s)1054734052dSrillig store_delimiter(state *s)
106f48e1c68Srillig {
1074734052dSrillig if (s->in_angle_brackets) {
10812034681Srillig s->comma_pos = s->total_len;
10912034681Srillig store(s, ',');
11012034681Srillig } else {
111f48e1c68Srillig store(s, '<');
1124734052dSrillig s->in_angle_brackets = 1;
113f48e1c68Srillig }
114f48e1c68Srillig }
115f48e1c68Srillig
116f48e1c68Srillig static void
maybe_wrap_line(state * s,const char * bitfmt)1174734052dSrillig maybe_wrap_line(state *s, const char *bitfmt)
118f48e1c68Srillig {
11912034681Srillig if (s->line_max > 0
12012034681Srillig && s->comma_pos > 0
12112034681Srillig && s->total_len - s->line_pos >= s->line_max) {
12212034681Srillig s->total_len = s->comma_pos;
12312034681Srillig store(s, '>');
1244734052dSrillig store_eol(s);
12512034681Srillig store_num(s, s->num_fmt, s->val);
12612034681Srillig s->bitfmt = bitfmt;
127f48e1c68Srillig }
128f48e1c68Srillig }
129f48e1c68Srillig
130f48e1c68Srillig static int
old_style(state * s)131f48e1c68Srillig old_style(state *s)
132f48e1c68Srillig {
13312034681Srillig while (*s->bitfmt != '\0') {
13412034681Srillig const char *cur_bitfmt = s->bitfmt;
13512034681Srillig uint8_t bit = *s->bitfmt;
136b5be0901Srillig if (bit > 32)
137b5be0901Srillig return -1;
138b5be0901Srillig if ((uint8_t)cur_bitfmt[1] <= 32)
13912034681Srillig return -1;
140f48e1c68Srillig if (s->val & (1U << (bit - 1))) {
1414734052dSrillig store_delimiter(s);
142b5be0901Srillig while ((uint8_t)*++s->bitfmt > 32)
14312034681Srillig store(s, *s->bitfmt);
1444734052dSrillig maybe_wrap_line(s, cur_bitfmt);
145f48e1c68Srillig } else
146b5be0901Srillig while ((uint8_t)*++s->bitfmt > 32)
147f48e1c68Srillig continue;
148f48e1c68Srillig }
14912034681Srillig return 0;
150f48e1c68Srillig }
151f48e1c68Srillig
152f48e1c68Srillig static int
new_style(state * s)153f48e1c68Srillig new_style(state *s)
154f48e1c68Srillig {
155442f4c6cSrillig uint8_t field_kind = 0; // 0 or 'f' or 'F'
156442f4c6cSrillig uint64_t field = 0; // valid if field_kind != '\0'
157f48e1c68Srillig int matched = 1;
15812034681Srillig const char *prev_bitfmt = s->bitfmt;
159f48e1c68Srillig while (*s->bitfmt != '\0') {
16012034681Srillig const char *cur_bitfmt = s->bitfmt;
161d101133eSrillig uint8_t kind = cur_bitfmt[0];
162f48e1c68Srillig switch (kind) {
163f48e1c68Srillig case 'b':
164442f4c6cSrillig field_kind = 0;
16512034681Srillig prev_bitfmt = cur_bitfmt;
166d101133eSrillig uint8_t b_bit = cur_bitfmt[1];
167d101133eSrillig if (b_bit >= 64)
168d101133eSrillig return -1;
169864a50a3Srillig if (cur_bitfmt[2] == '\0')
170864a50a3Srillig return -1;
171d101133eSrillig s->bitfmt += 2;
172d101133eSrillig if (((s->val >> b_bit) & 1) == 0)
17312034681Srillig goto skip_description;
1744734052dSrillig store_delimiter(s);
17512034681Srillig while (*s->bitfmt++ != '\0')
17612034681Srillig store(s, s->bitfmt[-1]);
1774734052dSrillig maybe_wrap_line(s, cur_bitfmt);
178f48e1c68Srillig break;
179f48e1c68Srillig case 'f':
180f48e1c68Srillig case 'F':
181442f4c6cSrillig field_kind = kind;
18212034681Srillig prev_bitfmt = cur_bitfmt;
183f48e1c68Srillig matched = 0;
184d101133eSrillig uint8_t f_lsb = cur_bitfmt[1];
185d101133eSrillig if (f_lsb >= 64)
186d101133eSrillig return -1;
187d101133eSrillig uint8_t f_width = cur_bitfmt[2];
188d101133eSrillig if (f_width > 64)
189d101133eSrillig return -1;
190864a50a3Srillig if (kind == 'f' && cur_bitfmt[3] == '\0')
191864a50a3Srillig return -1;
192d101133eSrillig field = s->val >> f_lsb;
193d101133eSrillig if (f_width < 64)
194d101133eSrillig field &= ((uint64_t) 1 << f_width) - 1;
195d101133eSrillig s->bitfmt += 3;
1964734052dSrillig store_delimiter(s);
197f48e1c68Srillig if (kind == 'F')
19812034681Srillig goto skip_description;
19912034681Srillig while (*s->bitfmt++ != '\0')
20012034681Srillig store(s, s->bitfmt[-1]);
20112034681Srillig store(s, '=');
20212034681Srillig store_num(s, s->num_fmt, field);
2034734052dSrillig maybe_wrap_line(s, cur_bitfmt);
204f48e1c68Srillig break;
205f48e1c68Srillig case '=':
206f48e1c68Srillig case ':':
207d101133eSrillig s->bitfmt += 2;
208442f4c6cSrillig if (kind == '=' && field_kind != 'f')
209442f4c6cSrillig return -1;
210442f4c6cSrillig if (kind == ':' && field_kind != 'F')
211442f4c6cSrillig return -1;
212d101133eSrillig uint8_t cmp = cur_bitfmt[1];
213864a50a3Srillig if (cur_bitfmt[2] == '\0')
214864a50a3Srillig return -1;
215d101133eSrillig if (field != cmp)
21612034681Srillig goto skip_description;
217f48e1c68Srillig matched = 1;
218f48e1c68Srillig if (kind == '=')
21912034681Srillig store(s, '=');
22012034681Srillig while (*s->bitfmt++ != '\0')
22112034681Srillig store(s, s->bitfmt[-1]);
2224734052dSrillig maybe_wrap_line(s, prev_bitfmt);
223f48e1c68Srillig break;
224f48e1c68Srillig case '*':
225442f4c6cSrillig if (field_kind == 0)
226442f4c6cSrillig return -1;
227b5be0901Srillig field_kind = 0;
228864a50a3Srillig if (cur_bitfmt[1] == '\0')
229864a50a3Srillig return -1;
230d101133eSrillig s->bitfmt++;
23112034681Srillig if (matched)
23212034681Srillig goto skip_description;
233f48e1c68Srillig matched = 1;
23412034681Srillig if (store_num(s, s->bitfmt, field) < 0)
235f48e1c68Srillig return -1;
2364734052dSrillig maybe_wrap_line(s, prev_bitfmt);
23769c5b3b0Srillig goto skip_description;
238f48e1c68Srillig default:
239e572db38Srillig return -1;
24012034681Srillig skip_description:
241f48e1c68Srillig while (*s->bitfmt++ != '\0')
242f48e1c68Srillig continue;
243f48e1c68Srillig break;
244f48e1c68Srillig }
245f48e1c68Srillig }
246f48e1c68Srillig return 0;
247f48e1c68Srillig }
248f48e1c68Srillig
24969c5b3b0Srillig static void
finish_buffer(state * s)25069c5b3b0Srillig finish_buffer(state *s)
25169c5b3b0Srillig {
25269c5b3b0Srillig if (s->line_max > 0) {
2534734052dSrillig store_eol(s);
25469c5b3b0Srillig store(s, '\0');
255*b215905fSrillig if (s->total_len <= s->bufsize)
256*b215905fSrillig return;
257*b215905fSrillig if (s->bufsize >= 3)
258b050579eSrillig s->buf[s->bufsize - 3] = '#';
259*b215905fSrillig if (s->bufsize >= 2)
260b050579eSrillig s->buf[s->bufsize - 2] = '\0';
261*b215905fSrillig if (s->bufsize >= 1)
26269c5b3b0Srillig s->buf[s->bufsize - 1] = '\0';
263b050579eSrillig } else {
264b050579eSrillig store(s, '\0');
265*b215905fSrillig if (s->total_len <= s->bufsize)
266*b215905fSrillig return;
267*b215905fSrillig if (s->bufsize >= 2)
268b050579eSrillig s->buf[s->bufsize - 2] = '#';
269*b215905fSrillig if (s->bufsize >= 1)
270b050579eSrillig s->buf[s->bufsize - 1] = '\0';
271b050579eSrillig }
27269c5b3b0Srillig }
27369c5b3b0Srillig
274cec60a58Schristos int
snprintb_m(char * buf,size_t bufsize,const char * bitfmt,uint64_t val,size_t line_max)2756f937561Srillig snprintb_m(char *buf, size_t bufsize, const char *bitfmt, uint64_t val,
276798cb1a7Srillig size_t line_max)
277cec60a58Schristos {
278f48e1c68Srillig int old = *bitfmt != '\177';
279f48e1c68Srillig if (!old)
2808548878bSrillig bitfmt++;
281f48e1c68Srillig
282f48e1c68Srillig state s = {
283f48e1c68Srillig .buf = buf,
284f48e1c68Srillig .bufsize = bufsize,
285f48e1c68Srillig .bitfmt = bitfmt,
286f48e1c68Srillig .val = val,
287f48e1c68Srillig .line_max = line_max,
288f48e1c68Srillig };
289c4ea73f5Srillig int had_error = 0;
29069c5b3b0Srillig
291c4ea73f5Srillig switch (*s.bitfmt++) {
292c4ea73f5Srillig case 8:
293c4ea73f5Srillig memcpy(s.num_fmt, "%#jo", 4);
294c4ea73f5Srillig break;
295c4ea73f5Srillig case 10:
296c4ea73f5Srillig memcpy(s.num_fmt, "%ju", 4);
297c4ea73f5Srillig break;
298c4ea73f5Srillig case 16:
299c4ea73f5Srillig memcpy(s.num_fmt, "%#jx", 4);
300c4ea73f5Srillig break;
301c4ea73f5Srillig default:
302c4ea73f5Srillig goto had_error;
303c4ea73f5Srillig }
30428469fc2Spgoyette
305c4ea73f5Srillig store_num(&s, s.num_fmt, val);
30628469fc2Spgoyette
307c4ea73f5Srillig if ((old ? old_style(&s) : new_style(&s)) < 0) {
308c4ea73f5Srillig had_error:
309cec60a58Schristos #ifndef _KERNEL
310cec60a58Schristos errno = EINVAL;
311cec60a58Schristos #endif
312c4ea73f5Srillig had_error = 1;
31369c5b3b0Srillig store(&s, '#');
314c4ea73f5Srillig } else if (s.in_angle_brackets)
315c4ea73f5Srillig store(&s, '>');
31669c5b3b0Srillig finish_buffer(&s);
317c4ea73f5Srillig return had_error ? -1 : (int)(s.total_len - 1);
318cec60a58Schristos }
31928469fc2Spgoyette
32028469fc2Spgoyette int
snprintb(char * buf,size_t bufsize,const char * bitfmt,uint64_t val)3216f937561Srillig snprintb(char *buf, size_t bufsize, const char *bitfmt, uint64_t val)
32228469fc2Spgoyette {
3236f937561Srillig return snprintb_m(buf, bufsize, bitfmt, val, 0);
32428469fc2Spgoyette }
3253be9df0dSapb # endif /* ! HAVE_SNPRINTB_M */
3263be9df0dSapb #endif /* ! _STANDALONE */
327