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