1 1.49 rillig /* $NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.41 rillig * Copyright (c) 2002, 2024 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * Redistribution and use in source and binary forms, with or without 8 1.1 christos * modification, are permitted provided that the following conditions 9 1.1 christos * are met: 10 1.1 christos * 1. Redistributions of source code must retain the above copyright 11 1.1 christos * notice, this list of conditions and the following disclaimer. 12 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 christos * notice, this list of conditions and the following disclaimer in the 14 1.1 christos * documentation and/or other materials provided with the distribution. 15 1.1 christos * 16 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 27 1.1 christos */ 28 1.1 christos 29 1.2 christos #ifndef _STANDALONE 30 1.2 christos # ifndef _KERNEL 31 1.1 christos 32 1.2 christos # if HAVE_NBTOOL_CONFIG_H 33 1.2 christos # include "nbtool_config.h" 34 1.2 christos # endif 35 1.2 christos 36 1.2 christos # include <sys/cdefs.h> 37 1.41 rillig # if defined(LIBC_SCCS) 38 1.49 rillig __RCSID("$NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $"); 39 1.2 christos # endif 40 1.2 christos 41 1.2 christos # include <sys/types.h> 42 1.13 agc # include <inttypes.h> 43 1.2 christos # include <stdio.h> 44 1.43 rillig # include <string.h> 45 1.2 christos # include <util.h> 46 1.2 christos # include <errno.h> 47 1.9 apb # else /* ! _KERNEL */ 48 1.2 christos # include <sys/cdefs.h> 49 1.49 rillig __KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.49 2024/06/16 19:41:39 rillig Exp $"); 50 1.3 pooka # include <sys/param.h> 51 1.2 christos # include <sys/inttypes.h> 52 1.2 christos # include <sys/systm.h> 53 1.2 christos # include <lib/libkern/libkern.h> 54 1.9 apb # endif /* ! _KERNEL */ 55 1.1 christos 56 1.9 apb # ifndef HAVE_SNPRINTB_M 57 1.35 rillig typedef struct { 58 1.35 rillig char *const buf; 59 1.35 rillig size_t const bufsize; 60 1.35 rillig const char *bitfmt; 61 1.35 rillig uint64_t const val; 62 1.35 rillig size_t const line_max; 63 1.35 rillig 64 1.43 rillig char num_fmt[5]; 65 1.41 rillig size_t total_len; 66 1.41 rillig size_t line_pos; 67 1.41 rillig size_t comma_pos; 68 1.41 rillig int in_angle_brackets; 69 1.35 rillig } state; 70 1.35 rillig 71 1.35 rillig static void 72 1.35 rillig store(state *s, char c) 73 1.35 rillig { 74 1.35 rillig if (s->total_len < s->bufsize) 75 1.35 rillig s->buf[s->total_len] = c; 76 1.35 rillig s->total_len++; 77 1.35 rillig } 78 1.35 rillig 79 1.37 rillig static int 80 1.37 rillig store_num(state *s, const char *fmt, uintmax_t num) 81 1.35 rillig { 82 1.37 rillig int num_len = s->total_len < s->bufsize 83 1.37 rillig ? snprintf(s->buf + s->total_len, s->bufsize - s->total_len, 84 1.37 rillig fmt, num) 85 1.37 rillig : snprintf(NULL, 0, fmt, num); 86 1.37 rillig if (num_len > 0) 87 1.37 rillig s->total_len += num_len; 88 1.37 rillig return num_len; 89 1.35 rillig } 90 1.35 rillig 91 1.35 rillig static void 92 1.41 rillig store_eol(state *s) 93 1.35 rillig { 94 1.37 rillig if (s->total_len - s->line_pos > s->line_max) { 95 1.41 rillig s->total_len = s->line_pos + s->line_max - 1; 96 1.37 rillig store(s, '#'); 97 1.35 rillig } 98 1.37 rillig store(s, '\0'); 99 1.37 rillig s->line_pos = s->total_len; 100 1.37 rillig s->comma_pos = 0; 101 1.41 rillig s->in_angle_brackets = 0; 102 1.35 rillig } 103 1.35 rillig 104 1.35 rillig static void 105 1.41 rillig store_delimiter(state *s) 106 1.35 rillig { 107 1.41 rillig if (s->in_angle_brackets) { 108 1.37 rillig s->comma_pos = s->total_len; 109 1.37 rillig store(s, ','); 110 1.35 rillig } else { 111 1.37 rillig store(s, '<'); 112 1.41 rillig s->in_angle_brackets = 1; 113 1.35 rillig } 114 1.35 rillig } 115 1.35 rillig 116 1.35 rillig static void 117 1.41 rillig maybe_wrap_line(state *s, const char *bitfmt) 118 1.35 rillig { 119 1.37 rillig if (s->line_max > 0 120 1.37 rillig && s->comma_pos > 0 121 1.37 rillig && s->total_len - s->line_pos >= s->line_max) { 122 1.37 rillig s->total_len = s->comma_pos; 123 1.37 rillig store(s, '>'); 124 1.41 rillig store_eol(s); 125 1.37 rillig store_num(s, s->num_fmt, s->val); 126 1.37 rillig s->bitfmt = bitfmt; 127 1.35 rillig } 128 1.35 rillig } 129 1.35 rillig 130 1.35 rillig static int 131 1.35 rillig old_style(state *s) 132 1.35 rillig { 133 1.37 rillig while (*s->bitfmt != '\0') { 134 1.37 rillig const char *cur_bitfmt = s->bitfmt; 135 1.37 rillig uint8_t bit = *s->bitfmt; 136 1.48 rillig if (bit > 32) 137 1.48 rillig return -1; 138 1.48 rillig if ((uint8_t)cur_bitfmt[1] <= 32) 139 1.37 rillig return -1; 140 1.35 rillig if (s->val & (1U << (bit - 1))) { 141 1.41 rillig store_delimiter(s); 142 1.48 rillig while ((uint8_t)*++s->bitfmt > 32) 143 1.37 rillig store(s, *s->bitfmt); 144 1.41 rillig maybe_wrap_line(s, cur_bitfmt); 145 1.35 rillig } else 146 1.48 rillig while ((uint8_t)*++s->bitfmt > 32) 147 1.35 rillig continue; 148 1.35 rillig } 149 1.37 rillig return 0; 150 1.35 rillig } 151 1.35 rillig 152 1.35 rillig static int 153 1.35 rillig new_style(state *s) 154 1.35 rillig { 155 1.47 rillig uint8_t field_kind = 0; // 0 or 'f' or 'F' 156 1.47 rillig uint64_t field = 0; // valid if field_kind != '\0' 157 1.35 rillig int matched = 1; 158 1.37 rillig const char *prev_bitfmt = s->bitfmt; 159 1.35 rillig while (*s->bitfmt != '\0') { 160 1.37 rillig const char *cur_bitfmt = s->bitfmt; 161 1.38 rillig uint8_t kind = cur_bitfmt[0]; 162 1.35 rillig switch (kind) { 163 1.35 rillig case 'b': 164 1.47 rillig field_kind = 0; 165 1.37 rillig prev_bitfmt = cur_bitfmt; 166 1.38 rillig uint8_t b_bit = cur_bitfmt[1]; 167 1.38 rillig if (b_bit >= 64) 168 1.38 rillig return -1; 169 1.46 rillig if (cur_bitfmt[2] == '\0') 170 1.46 rillig return -1; 171 1.38 rillig s->bitfmt += 2; 172 1.38 rillig if (((s->val >> b_bit) & 1) == 0) 173 1.37 rillig goto skip_description; 174 1.41 rillig store_delimiter(s); 175 1.37 rillig while (*s->bitfmt++ != '\0') 176 1.37 rillig store(s, s->bitfmt[-1]); 177 1.41 rillig maybe_wrap_line(s, cur_bitfmt); 178 1.35 rillig break; 179 1.35 rillig case 'f': 180 1.35 rillig case 'F': 181 1.47 rillig field_kind = kind; 182 1.37 rillig prev_bitfmt = cur_bitfmt; 183 1.35 rillig matched = 0; 184 1.38 rillig uint8_t f_lsb = cur_bitfmt[1]; 185 1.38 rillig if (f_lsb >= 64) 186 1.38 rillig return -1; 187 1.38 rillig uint8_t f_width = cur_bitfmt[2]; 188 1.38 rillig if (f_width > 64) 189 1.38 rillig return -1; 190 1.46 rillig if (kind == 'f' && cur_bitfmt[3] == '\0') 191 1.46 rillig return -1; 192 1.38 rillig field = s->val >> f_lsb; 193 1.38 rillig if (f_width < 64) 194 1.38 rillig field &= ((uint64_t) 1 << f_width) - 1; 195 1.38 rillig s->bitfmt += 3; 196 1.41 rillig store_delimiter(s); 197 1.35 rillig if (kind == 'F') 198 1.37 rillig goto skip_description; 199 1.37 rillig while (*s->bitfmt++ != '\0') 200 1.37 rillig store(s, s->bitfmt[-1]); 201 1.37 rillig store(s, '='); 202 1.37 rillig store_num(s, s->num_fmt, field); 203 1.41 rillig maybe_wrap_line(s, cur_bitfmt); 204 1.35 rillig break; 205 1.35 rillig case '=': 206 1.35 rillig case ':': 207 1.38 rillig s->bitfmt += 2; 208 1.47 rillig if (kind == '=' && field_kind != 'f') 209 1.47 rillig return -1; 210 1.47 rillig if (kind == ':' && field_kind != 'F') 211 1.47 rillig return -1; 212 1.38 rillig uint8_t cmp = cur_bitfmt[1]; 213 1.46 rillig if (cur_bitfmt[2] == '\0') 214 1.46 rillig return -1; 215 1.38 rillig if (field != cmp) 216 1.37 rillig goto skip_description; 217 1.35 rillig matched = 1; 218 1.35 rillig if (kind == '=') 219 1.37 rillig store(s, '='); 220 1.37 rillig while (*s->bitfmt++ != '\0') 221 1.37 rillig store(s, s->bitfmt[-1]); 222 1.41 rillig maybe_wrap_line(s, prev_bitfmt); 223 1.35 rillig break; 224 1.35 rillig case '*': 225 1.47 rillig if (field_kind == 0) 226 1.47 rillig return -1; 227 1.48 rillig field_kind = 0; 228 1.46 rillig if (cur_bitfmt[1] == '\0') 229 1.46 rillig return -1; 230 1.38 rillig s->bitfmt++; 231 1.37 rillig if (matched) 232 1.37 rillig goto skip_description; 233 1.37 rillig matched = 1; 234 1.37 rillig if (store_num(s, s->bitfmt, field) < 0) 235 1.37 rillig return -1; 236 1.41 rillig maybe_wrap_line(s, prev_bitfmt); 237 1.39 rillig goto skip_description; 238 1.35 rillig default: 239 1.42 rillig return -1; 240 1.37 rillig skip_description: 241 1.35 rillig while (*s->bitfmt++ != '\0') 242 1.35 rillig continue; 243 1.35 rillig break; 244 1.35 rillig } 245 1.35 rillig } 246 1.35 rillig return 0; 247 1.35 rillig } 248 1.35 rillig 249 1.39 rillig static void 250 1.39 rillig finish_buffer(state *s) 251 1.39 rillig { 252 1.39 rillig if (s->line_max > 0) { 253 1.41 rillig store_eol(s); 254 1.44 rillig store(s, '\0'); 255 1.49 rillig if (s->total_len <= s->bufsize) 256 1.49 rillig return; 257 1.49 rillig if (s->bufsize >= 3) 258 1.44 rillig s->buf[s->bufsize - 3] = '#'; 259 1.49 rillig if (s->bufsize >= 2) 260 1.39 rillig s->buf[s->bufsize - 2] = '\0'; 261 1.49 rillig if (s->bufsize >= 1) 262 1.44 rillig s->buf[s->bufsize - 1] = '\0'; 263 1.44 rillig } else { 264 1.44 rillig store(s, '\0'); 265 1.49 rillig if (s->total_len <= s->bufsize) 266 1.49 rillig return; 267 1.49 rillig if (s->bufsize >= 2) 268 1.44 rillig s->buf[s->bufsize - 2] = '#'; 269 1.49 rillig if (s->bufsize >= 1) 270 1.44 rillig s->buf[s->bufsize - 1] = '\0'; 271 1.39 rillig } 272 1.39 rillig } 273 1.39 rillig 274 1.1 christos int 275 1.24 rillig snprintb_m(char *buf, size_t bufsize, const char *bitfmt, uint64_t val, 276 1.27 rillig size_t line_max) 277 1.1 christos { 278 1.35 rillig int old = *bitfmt != '\177'; 279 1.35 rillig if (!old) 280 1.28 rillig bitfmt++; 281 1.35 rillig 282 1.43 rillig state s = { 283 1.43 rillig .buf = buf, 284 1.43 rillig .bufsize = bufsize, 285 1.43 rillig .bitfmt = bitfmt, 286 1.43 rillig .val = val, 287 1.43 rillig .line_max = line_max, 288 1.43 rillig }; 289 1.43 rillig int had_error = 0; 290 1.43 rillig 291 1.43 rillig switch (*s.bitfmt++) { 292 1.1 christos case 8: 293 1.43 rillig memcpy(s.num_fmt, "%#jo", 4); 294 1.1 christos break; 295 1.1 christos case 10: 296 1.43 rillig memcpy(s.num_fmt, "%ju", 4); 297 1.1 christos break; 298 1.1 christos case 16: 299 1.43 rillig memcpy(s.num_fmt, "%#jx", 4); 300 1.1 christos break; 301 1.1 christos default: 302 1.43 rillig goto had_error; 303 1.1 christos } 304 1.1 christos 305 1.43 rillig store_num(&s, s.num_fmt, val); 306 1.5 pgoyette 307 1.43 rillig if ((old ? old_style(&s) : new_style(&s)) < 0) { 308 1.43 rillig had_error: 309 1.1 christos #ifndef _KERNEL 310 1.43 rillig errno = EINVAL; 311 1.1 christos #endif 312 1.43 rillig had_error = 1; 313 1.43 rillig store(&s, '#'); 314 1.43 rillig } else if (s.in_angle_brackets) 315 1.43 rillig store(&s, '>'); 316 1.39 rillig finish_buffer(&s); 317 1.43 rillig return had_error ? -1 : (int)(s.total_len - 1); 318 1.1 christos } 319 1.5 pgoyette 320 1.5 pgoyette int 321 1.24 rillig snprintb(char *buf, size_t bufsize, const char *bitfmt, uint64_t val) 322 1.5 pgoyette { 323 1.24 rillig return snprintb_m(buf, bufsize, bitfmt, val, 0); 324 1.5 pgoyette } 325 1.9 apb # endif /* ! HAVE_SNPRINTB_M */ 326 1.9 apb #endif /* ! _STANDALONE */ 327