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