1/* $NetBSD: snprintb.c,v 1.48 2024/04/07 15:20:16 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.48 2024/04/07 15:20:16 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.48 2024/04/07 15:20:16 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 57typedef 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 71static void 72store(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 79static int 80store_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 91static void 92store_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 104static void 105store_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 116static void 117maybe_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 130static int 131old_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 152static int 153new_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 249static void 250finish_buffer(state *s) 251{ 252 if (s->line_max > 0) { 253 store_eol(s); 254 store(s, '\0'); 255 if (s->bufsize >= 3 && s->total_len > s->bufsize) 256 s->buf[s->bufsize - 3] = '#'; 257 if (s->bufsize >= 2 && s->total_len > s->bufsize) 258 s->buf[s->bufsize - 2] = '\0'; 259 if (s->bufsize >= 1 && s->total_len > s->bufsize) 260 s->buf[s->bufsize - 1] = '\0'; 261 } else { 262 store(s, '\0'); 263 if (s->bufsize >= 2 && s->total_len > s->bufsize) 264 s->buf[s->bufsize - 2] = '#'; 265 if (s->bufsize >= 1 && s->total_len > s->bufsize) 266 s->buf[s->bufsize - 1] = '\0'; 267 } 268} 269 270int 271snprintb_m(char *buf, size_t bufsize, const char *bitfmt, uint64_t val, 272 size_t line_max) 273{ 274 int old = *bitfmt != '\177'; 275 if (!old) 276 bitfmt++; 277 278 state s = { 279 .buf = buf, 280 .bufsize = bufsize, 281 .bitfmt = bitfmt, 282 .val = val, 283 .line_max = line_max, 284 }; 285 int had_error = 0; 286 287 switch (*s.bitfmt++) { 288 case 8: 289 memcpy(s.num_fmt, "%#jo", 4); 290 break; 291 case 10: 292 memcpy(s.num_fmt, "%ju", 4); 293 break; 294 case 16: 295 memcpy(s.num_fmt, "%#jx", 4); 296 break; 297 default: 298 goto had_error; 299 } 300 301 store_num(&s, s.num_fmt, val); 302 303 if ((old ? old_style(&s) : new_style(&s)) < 0) { 304had_error: 305#ifndef _KERNEL 306 errno = EINVAL; 307#endif 308 had_error = 1; 309 store(&s, '#'); 310 } else if (s.in_angle_brackets) 311 store(&s, '>'); 312 finish_buffer(&s); 313 return had_error ? -1 : (int)(s.total_len - 1); 314} 315 316int 317snprintb(char *buf, size_t bufsize, const char *bitfmt, uint64_t val) 318{ 319 return snprintb_m(buf, bufsize, bitfmt, val, 0); 320} 321# endif /* ! HAVE_SNPRINTB_M */ 322#endif /* ! _STANDALONE */ 323