1 /* $NetBSD: snprintb.c,v 1.44 2024/03/25 20:39:26 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.44 2024/03/25 20:39:26 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.44 2024/03/25 20:39:26 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 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 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 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 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 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 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 > ' ') 137 return -1; 138 if (s->val & (1U << (bit - 1))) { 139 store_delimiter(s); 140 while ((uint8_t)*++s->bitfmt > ' ') 141 store(s, *s->bitfmt); 142 maybe_wrap_line(s, cur_bitfmt); 143 } else 144 while ((uint8_t)*++s->bitfmt > ' ') 145 continue; 146 } 147 return 0; 148 } 149 150 static int 151 new_style(state *s) 152 { 153 uint64_t field = s->val; 154 int matched = 1; 155 const char *prev_bitfmt = s->bitfmt; 156 while (*s->bitfmt != '\0') { 157 const char *cur_bitfmt = s->bitfmt; 158 uint8_t kind = cur_bitfmt[0]; 159 switch (kind) { 160 case 'b': 161 prev_bitfmt = cur_bitfmt; 162 uint8_t b_bit = cur_bitfmt[1]; 163 if (b_bit >= 64) 164 return -1; 165 s->bitfmt += 2; 166 if (((s->val >> b_bit) & 1) == 0) 167 goto skip_description; 168 store_delimiter(s); 169 while (*s->bitfmt++ != '\0') 170 store(s, s->bitfmt[-1]); 171 maybe_wrap_line(s, cur_bitfmt); 172 break; 173 case 'f': 174 case 'F': 175 prev_bitfmt = cur_bitfmt; 176 matched = 0; 177 uint8_t f_lsb = cur_bitfmt[1]; 178 if (f_lsb >= 64) 179 return -1; 180 uint8_t f_width = cur_bitfmt[2]; 181 if (f_width > 64) 182 return -1; 183 field = s->val >> f_lsb; 184 if (f_width < 64) 185 field &= ((uint64_t) 1 << f_width) - 1; 186 s->bitfmt += 3; 187 store_delimiter(s); 188 if (kind == 'F') 189 goto skip_description; 190 while (*s->bitfmt++ != '\0') 191 store(s, s->bitfmt[-1]); 192 store(s, '='); 193 store_num(s, s->num_fmt, field); 194 maybe_wrap_line(s, cur_bitfmt); 195 break; 196 case '=': 197 case ':': 198 s->bitfmt += 2; 199 uint8_t cmp = cur_bitfmt[1]; 200 if (field != cmp) 201 goto skip_description; 202 matched = 1; 203 if (kind == '=') 204 store(s, '='); 205 while (*s->bitfmt++ != '\0') 206 store(s, s->bitfmt[-1]); 207 maybe_wrap_line(s, prev_bitfmt); 208 break; 209 case '*': 210 s->bitfmt++; 211 if (matched) 212 goto skip_description; 213 matched = 1; 214 if (store_num(s, s->bitfmt, field) < 0) 215 return -1; 216 maybe_wrap_line(s, prev_bitfmt); 217 goto skip_description; 218 default: 219 return -1; 220 skip_description: 221 while (*s->bitfmt++ != '\0') 222 continue; 223 break; 224 } 225 } 226 return 0; 227 } 228 229 static void 230 finish_buffer(state *s) 231 { 232 if (s->line_max > 0) { 233 store_eol(s); 234 store(s, '\0'); 235 if (s->bufsize >= 3 && s->total_len > s->bufsize) 236 s->buf[s->bufsize - 3] = '#'; 237 if (s->bufsize >= 2 && s->total_len > s->bufsize) 238 s->buf[s->bufsize - 2] = '\0'; 239 if (s->bufsize >= 1 && s->total_len > s->bufsize) 240 s->buf[s->bufsize - 1] = '\0'; 241 } else { 242 store(s, '\0'); 243 if (s->bufsize >= 2 && s->total_len > s->bufsize) 244 s->buf[s->bufsize - 2] = '#'; 245 if (s->bufsize >= 1 && s->total_len > s->bufsize) 246 s->buf[s->bufsize - 1] = '\0'; 247 } 248 } 249 250 int 251 snprintb_m(char *buf, size_t bufsize, const char *bitfmt, uint64_t val, 252 size_t line_max) 253 { 254 #ifdef _KERNEL 255 /* 256 * For safety; no other *s*printf() do this, but in the kernel 257 * we don't usually check the return value. 258 */ 259 if (bufsize > 0) 260 (void)memset(buf, 0, bufsize); 261 #endif /* _KERNEL */ 262 263 int old = *bitfmt != '\177'; 264 if (!old) 265 bitfmt++; 266 267 state s = { 268 .buf = buf, 269 .bufsize = bufsize, 270 .bitfmt = bitfmt, 271 .val = val, 272 .line_max = line_max, 273 }; 274 int had_error = 0; 275 276 switch (*s.bitfmt++) { 277 case 8: 278 memcpy(s.num_fmt, "%#jo", 4); 279 break; 280 case 10: 281 memcpy(s.num_fmt, "%ju", 4); 282 break; 283 case 16: 284 memcpy(s.num_fmt, "%#jx", 4); 285 break; 286 default: 287 goto had_error; 288 } 289 290 store_num(&s, s.num_fmt, val); 291 292 if ((old ? old_style(&s) : new_style(&s)) < 0) { 293 had_error: 294 #ifndef _KERNEL 295 errno = EINVAL; 296 #endif 297 had_error = 1; 298 store(&s, '#'); 299 } else if (s.in_angle_brackets) 300 store(&s, '>'); 301 finish_buffer(&s); 302 return had_error ? -1 : (int)(s.total_len - 1); 303 } 304 305 int 306 snprintb(char *buf, size_t bufsize, const char *bitfmt, uint64_t val) 307 { 308 return snprintb_m(buf, bufsize, bitfmt, val, 0); 309 } 310 # endif /* ! HAVE_SNPRINTB_M */ 311 #endif /* ! _STANDALONE */ 312