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