1 /* 2 * Copyright (c) 2026 Faraz Vahedi <kfv@kfv.io> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 /* 8 * Shared helpers for strfromd, strfromf, and strfroml (C23 §7.24.1.3). 9 */ 10 11 #include <ctype.h> 12 #include <errno.h> 13 #include <locale.h> 14 #include <stdlib.h> 15 16 #include "strfrom.h" 17 18 struct sf_buf { 19 char *s; 20 size_t n; 21 int pos; 22 }; 23 24 /* 25 * Write c to b->s[b->pos] if within the n-1 writable bytes; always advance 26 * b->pos. n is the total buffer capacity, including the null slot. 27 */ 28 static void 29 sf_putc(struct sf_buf *b, char c) 30 { 31 if (b->n > 0 && (size_t)b->pos < b->n - 1) 32 b->s[b->pos] = c; 33 b->pos++; 34 } 35 36 /* 37 * Write len bytes from src. 38 */ 39 static void 40 sf_write(struct sf_buf *b, const char *src, int len) 41 { 42 int i; 43 44 for (i = 0; i < len; i++) 45 sf_putc(b, src[i]); 46 } 47 48 /* 49 * Write a null terminated string. 50 */ 51 static void 52 sf_puts(struct sf_buf *b, const char *src) 53 { 54 while (*src != '\0') 55 sf_putc(b, *src++); 56 } 57 58 /* 59 * Write count copies of c. 60 */ 61 static void 62 sf_padc(struct sf_buf *b, char c, int count) 63 { 64 int i; 65 66 for (i = 0; i < count; i++) 67 sf_putc(b, c); 68 } 69 70 /* 71 * Seal the buffer: null terminate at min(pos, n-1). 72 */ 73 static void 74 sf_seal(struct sf_buf *b) 75 { 76 if (b->n > 0) 77 b->s[(size_t)b->pos < b->n ? b->pos : b->n - 1] = '\0'; 78 } 79 80 /* 81 * Emit the radix. Interfaces are defined as snprintf(3) (C23 §7.24.1.3p2), 82 * whose '.' is the LC_NUMERIC decimal point; localeconv() resolves the same 83 * per-thread locale as vfprintf(3). The radix may be multibyte. 84 */ 85 static void 86 sf_putdp(struct sf_buf *b) 87 { 88 sf_puts(b, localeconv()->decimal_point); 89 } 90 91 /* 92 * Emit "[eEpP][+-]ddd": exponent sign and decimal magnitude, zero-padded to at 93 * least mindig digits (2 for %e/%E and %g/%G, 1 for %a/%A per C23 §7.23.6.1). 94 * 95 * Handles the full exponent range of every supported type. 96 */ 97 static void 98 sf_emit_exp(struct sf_buf *b, char ec, int exp, int mindig) 99 { 100 char buf[16]; 101 int len; 102 103 sf_putc(b, ec); 104 if (exp < 0) { 105 sf_putc(b, '-'); 106 exp = -exp; 107 } else { 108 sf_putc(b, '+'); 109 } 110 111 len = 0; 112 do { 113 buf[len++] = '0' + exp % 10; 114 exp /= 10; 115 } while (exp != 0); 116 while (len < mindig) 117 buf[len++] = '0'; 118 while (len > 0) 119 sf_putc(b, buf[--len]); 120 } 121 122 /* 123 * Parse "%[.prec]conv" per C23 §7.24.1.3. 124 * 125 * Returns the conversion specifier character, or '\0' with errno set to 126 * EDOOFUS if fmt is not one of the forms the standard permits; *prec is 127 * -1 when the precision is absent. 128 */ 129 char 130 __sf_parse_fmt(const char *fmt, int *prec) 131 { 132 const char *p; 133 134 *prec = -1; 135 if (*fmt != '%') 136 return ('\0'); 137 p = fmt + 1; 138 139 if (*p == '.') { 140 *prec = 0; 141 while (*++p >= '0' && *p <= '9') 142 *prec = *prec * 10 + (*p - '0'); 143 } 144 145 switch (*p) { 146 case 'a': case 'A': 147 case 'e': case 'E': 148 case 'f': case 'F': 149 case 'g': case 'G': 150 break; 151 default: 152 return ('\0'); 153 } 154 155 return (p[1] == '\0' ? *p : '\0'); 156 } 157 158 /* 159 * Report a format string the standard does not permit. 160 * 161 * Sets errno and renders "EDOOFUS" under the ordinary snprintf(3) truncation 162 * rules, which keeps a faulty caller alive and its output obviously wrong. 163 */ 164 int 165 __sf_edoofus(char *s, size_t n) 166 { 167 struct sf_buf b; 168 169 errno = EDOOFUS; 170 b.s = s; 171 b.n = n; 172 b.pos = 0; 173 sf_puts(&b, "EDOOFUS"); 174 sf_seal(&b); 175 return (b.pos); 176 } 177 178 /* 179 * Render Inf or NaN into b->s[0..n-1]. 180 * 181 * is_nan: non-zero if digits[0] == 'N' (NaN), zero for Infinity. 182 */ 183 static int 184 sf_special(struct sf_buf *b, char conv, int signflag, int is_nan) 185 { 186 int upper; 187 188 b->pos = 0; 189 upper = isupper((unsigned char)conv); 190 if (!is_nan && signflag) 191 sf_putc(b, '-'); 192 sf_puts(b, is_nan ? (upper ? "NAN" : "nan") : (upper ? "INF" : "inf")); 193 sf_seal(b); 194 return (b->pos); 195 } 196 197 /* 198 * Render %e / %E. 199 * 200 * dtoa was called with mode=2, ndigits=prec+1. decpt is the position of the 201 * first significant digit relative to the decimal point (= exponent + 1), as 202 * returned by dtoa. 203 */ 204 static int 205 sf_efmt(struct sf_buf *b, int prec, char conv, 206 const char *digits, int ndig, int decpt, int signflag) 207 { 208 int avail, copy; 209 210 b->pos = 0; 211 if (signflag) 212 sf_putc(b, '-'); 213 214 /* 215 * Leading significant digit. 216 */ 217 sf_putc(b, ndig > 0 ? digits[0] : '0'); 218 219 if (prec > 0) { 220 sf_putdp(b); 221 avail = ndig > 1 ? ndig - 1 : 0; 222 copy = avail < prec ? avail : prec; 223 sf_write(b, digits + 1, copy); 224 sf_padc(b, '0', prec - copy); 225 } 226 sf_emit_exp(b, isupper((unsigned char)conv) ? 'E' : 'e', decpt - 1, 2); 227 228 sf_seal(b); 229 return (b->pos); 230 } 231 232 /* 233 * Render %f / %F. 234 * 235 * dtoa was called with mode=3, ndigits=prec. decpt gives the number of digits 236 * before the decimal point (may be <= 0). 237 */ 238 static int 239 sf_ffmt(struct sf_buf *b, int prec, int signflag, 240 const char *digits, int ndig, int decpt) 241 { 242 int avail, copy, rem, zc; 243 244 b->pos = 0; 245 if (signflag) 246 sf_putc(b, '-'); 247 248 if (decpt <= 0) { 249 sf_putc(b, '0'); 250 if (prec > 0) { 251 sf_putdp(b); 252 zc = -decpt < prec ? -decpt : prec; 253 rem = prec - zc; 254 copy = ndig < rem ? ndig : rem; 255 sf_padc(b, '0', zc); 256 sf_write(b, digits, copy); 257 sf_padc(b, '0', rem - copy); 258 } 259 } else { 260 /* 261 * decpt digits (or zeros) before the point. 262 */ 263 copy = ndig < decpt ? ndig : decpt; 264 sf_write(b, digits, copy); 265 sf_padc(b, '0', decpt - copy); 266 if (prec > 0) { 267 sf_putdp(b); 268 avail = ndig - decpt; 269 if (avail < 0) 270 avail = 0; 271 copy = avail < prec ? avail : prec; 272 sf_write(b, digits + decpt, copy); 273 sf_padc(b, '0', prec - copy); 274 } 275 } 276 277 sf_seal(b); 278 return (b->pos); 279 } 280 281 /* 282 * Render %g / %G. 283 * 284 * dtoa was called with mode=2, ndigits=max(1, prec). 285 */ 286 static int 287 sf_gfmt(struct sf_buf *b, int prec, char conv, 288 const char *digits, int ndig, int decpt, int signflag) 289 { 290 int ep, copy, frac; 291 292 b->pos = 0; 293 /* 294 * Precision 0 is treated as 1 per C23 §7.23.6.1. 295 */ 296 ep = prec == 0 ? 1 : prec; 297 298 /* 299 * Strip trailing zeros (no ALT flag is accepted). 300 */ 301 while (ndig > 1 && digits[ndig - 1] == '0') 302 ndig--; 303 304 if (signflag) 305 sf_putc(b, '-'); 306 307 /* 308 * Standard says −4 ≤ exponent < P test: exponent = decpt − 1 309 */ 310 if (decpt > -4 && decpt <= ep) { 311 /* 312 * %f style, per C23 §7.23.6.1. 313 */ 314 if (decpt <= 0) { 315 sf_putc(b, '0'); 316 if (ndig > 0) { 317 sf_putdp(b); 318 sf_padc(b, '0', -decpt); 319 sf_write(b, digits, ndig); 320 } 321 } else { 322 copy = ndig < decpt ? ndig : decpt; 323 sf_write(b, digits, copy); 324 sf_padc(b, '0', decpt - copy); 325 frac = ndig - decpt; 326 if (frac > 0) { 327 sf_putdp(b); 328 sf_write(b, digits + decpt, frac); 329 } 330 } 331 } else { 332 /* 333 * %e style, per C23 §7.23.6.1. 334 */ 335 sf_putc(b, ndig > 0 ? digits[0] : '0'); 336 if (ndig > 1) { 337 sf_putdp(b); 338 sf_write(b, digits + 1, ndig - 1); 339 } 340 sf_emit_exp(b, isupper((unsigned char)conv) ? 'E' : 'e', 341 decpt - 1, 2); 342 } 343 344 sf_seal(b); 345 return (b->pos); 346 } 347 348 /* 349 * Render %a / %A. 350 * 351 * digits / ndig: hex significand digits from __hdtoa / __hldtoa; the first 352 * digit represents the integer part of the mantissa (normally '1'). 353 * 354 * decpt: binary exponent such that p-exponent = decpt - 1. 355 * 356 * user_prec: digits after the hex point (-1 for shortest-exact). 357 */ 358 static int 359 sf_afmt(struct sf_buf *b, int user_prec, char conv, 360 const char *digits, int ndig, int decpt, int signflag) 361 { 362 int after, copy; 363 364 b->pos = 0; 365 if (signflag) 366 sf_putc(b, '-'); 367 sf_putc(b, '0'); 368 sf_putc(b, isupper((unsigned char)conv) ? 'X' : 'x'); 369 370 /* 371 * Integer part of the significand. 372 */ 373 sf_putc(b, ndig > 0 ? digits[0] : '0'); 374 375 after = ndig - 1; 376 if (user_prec < 0) { 377 /* 378 * Shortest-exact: emit only the non-zero significant tail. 379 */ 380 if (after > 0) { 381 sf_putdp(b); 382 sf_write(b, digits + 1, after); 383 } 384 } else if (user_prec > 0) { 385 sf_putdp(b); 386 copy = after < user_prec ? after : user_prec; 387 sf_write(b, digits + 1, copy); 388 sf_padc(b, '0', user_prec - copy); 389 } 390 sf_emit_exp(b, isupper((unsigned char)conv) ? 'P' : 'p', decpt - 1, 1); 391 392 sf_seal(b); 393 return (b->pos); 394 } 395 396 /* 397 * Hex digit table for %a / %A, selected by specifier case. 398 */ 399 const char * 400 __sf_xdigits(char conv) 401 { 402 return (isupper((unsigned char)conv) ? "0123456789ABCDEF" : 403 "0123456789abcdef"); 404 } 405 406 /* 407 * Map a decimal specifier (lowercased) to its gdtoa mode and digit count. 408 */ 409 void 410 __sf_decimal_mode(char lc, int prec, int *mode, int *ndig_req) 411 { 412 switch (lc) { 413 case 'e': 414 *mode = 2; 415 *ndig_req = prec + 1; 416 break; 417 case 'f': 418 *mode = 3; 419 *ndig_req = prec; 420 break; 421 case 'g': 422 *mode = 2; 423 *ndig_req = prec == 0 ? 1 : prec; 424 break; 425 default: 426 __unreachable(); 427 } 428 } 429 430 /* 431 * Render a finished %a / %A conversion. __hdtoa / __hldtoa always flag 432 * Inf/NaN with decpt == INT_MAX. 433 */ 434 int 435 __sf_render_hex(char *s, size_t n, char conv, int prec, 436 char *digits, char *dend, int decpt, int signflag) 437 { 438 struct sf_buf b; 439 int ndig, usprec; 440 441 b.s = s; 442 b.n = n; 443 if (decpt == INT_MAX) 444 return (sf_special(&b, conv, signflag, digits[0] == 'N')); 445 ndig = (int)(dend - digits); 446 usprec = prec >= 0 ? prec : ndig - 1; 447 return (sf_afmt(&b, usprec, conv, digits, ndig, decpt, signflag)); 448 } 449 450 /* 451 * Render a finished %e/%f/%g conversion. is_special is set when decpt holds 452 * the caller's Inf/NaN sentinel (dtoa: 9999, __ldtoa: INT_MAX). 453 */ 454 int 455 __sf_render_decimal(char *s, size_t n, char conv, char lc, int prec, 456 char *digits, char *dend, int decpt, int signflag, int is_special) 457 { 458 struct sf_buf b; 459 int ndig; 460 461 b.s = s; 462 b.n = n; 463 if (is_special) 464 return (sf_special(&b, conv, signflag, digits[0] == 'N')); 465 ndig = (int)(dend - digits); 466 switch (lc) { 467 case 'e': 468 return (sf_efmt(&b, prec, conv, digits, ndig, decpt, signflag)); 469 case 'f': 470 return (sf_ffmt(&b, prec, signflag, digits, ndig, decpt)); 471 case 'g': 472 return (sf_gfmt(&b, prec, conv, digits, ndig, decpt, signflag)); 473 default: 474 __unreachable(); 475 } 476 } 477