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 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 > 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 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 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 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 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