xref: /freebsd/lib/libc/stdlib/strfrom.c (revision d15f2551b25f79ddcbe289faa95e655100b952da)
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