1570af302Sopenharmony_ci#include "stdio_impl.h"
2570af302Sopenharmony_ci#include <errno.h>
3570af302Sopenharmony_ci#include <ctype.h>
4570af302Sopenharmony_ci#include <limits.h>
5570af302Sopenharmony_ci#include <string.h>
6570af302Sopenharmony_ci#include <stdarg.h>
7570af302Sopenharmony_ci#include <stddef.h>
8570af302Sopenharmony_ci#include <stdlib.h>
9570af302Sopenharmony_ci#include <wchar.h>
10570af302Sopenharmony_ci#include <inttypes.h>
11570af302Sopenharmony_ci#include <math.h>
12570af302Sopenharmony_ci#include <float.h>
13570af302Sopenharmony_ci#include <sys/ioctl.h>
14570af302Sopenharmony_ci
15570af302Sopenharmony_ci/* Some useful macros */
16570af302Sopenharmony_ci
17570af302Sopenharmony_ci#define MAX(a,b) ((a)>(b) ? (a) : (b))
18570af302Sopenharmony_ci#define MIN(a,b) ((a)<(b) ? (a) : (b))
19570af302Sopenharmony_ci
20570af302Sopenharmony_ci/* Convenient bit representation for modifier flags, which all fall
21570af302Sopenharmony_ci * within 31 codepoints of the space character. */
22570af302Sopenharmony_ci
23570af302Sopenharmony_ci#define ALT_FORM   (1U<<('#'-' '))
24570af302Sopenharmony_ci#define ZERO_PAD   (1U<<('0'-' '))
25570af302Sopenharmony_ci#define LEFT_ADJ   (1U<<('-'-' '))
26570af302Sopenharmony_ci#define PAD_POS    (1U<<(' '-' '))
27570af302Sopenharmony_ci#define MARK_POS   (1U<<('+'-' '))
28570af302Sopenharmony_ci#define GROUPED    (1U<<('\''-' '))
29570af302Sopenharmony_ci
30570af302Sopenharmony_ci#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
31570af302Sopenharmony_ci
32570af302Sopenharmony_ci/* Redefine the CHAR type that defined in los_typedef.h and introduced
33570af302Sopenharmony_ci * in by limit.h to avoid conflicts with the following enum definition. */
34570af302Sopenharmony_ci#define CHAR _CHAR
35570af302Sopenharmony_ci
36570af302Sopenharmony_ci/* State machine to accept length modifiers + conversion specifiers.
37570af302Sopenharmony_ci * Result is 0 on failure, or an argument type to pop on success. */
38570af302Sopenharmony_ci
39570af302Sopenharmony_cienum {
40570af302Sopenharmony_ci	BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE,
41570af302Sopenharmony_ci	ZTPRE, JPRE,
42570af302Sopenharmony_ci	STOP,
43570af302Sopenharmony_ci	PTR, INT, UINT, ULLONG,
44570af302Sopenharmony_ci	LONG, ULONG,
45570af302Sopenharmony_ci	SHORT, USHORT, CHAR, UCHAR,
46570af302Sopenharmony_ci	LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR,
47570af302Sopenharmony_ci	DBL, LDBL,
48570af302Sopenharmony_ci	NOARG,
49570af302Sopenharmony_ci	MAXSTATE
50570af302Sopenharmony_ci};
51570af302Sopenharmony_ci
52570af302Sopenharmony_ci#define S(x) [(x)-'A']
53570af302Sopenharmony_ci
54570af302Sopenharmony_cistatic const unsigned char states[]['z'-'A'+1] = {
55570af302Sopenharmony_ci	{ /* 0: bare types */
56570af302Sopenharmony_ci		S('d') = INT, S('i') = INT,
57570af302Sopenharmony_ci		S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT,
58570af302Sopenharmony_ci		S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
59570af302Sopenharmony_ci		S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
60570af302Sopenharmony_ci		S('c') = CHAR, S('C') = INT,
61570af302Sopenharmony_ci		S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR,
62570af302Sopenharmony_ci		S('m') = NOARG,
63570af302Sopenharmony_ci		S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE,
64570af302Sopenharmony_ci		S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
65570af302Sopenharmony_ci	}, { /* 1: l-prefixed */
66570af302Sopenharmony_ci		S('d') = LONG, S('i') = LONG,
67570af302Sopenharmony_ci		S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG,
68570af302Sopenharmony_ci		S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
69570af302Sopenharmony_ci		S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
70570af302Sopenharmony_ci		S('c') = INT, S('s') = PTR, S('n') = PTR,
71570af302Sopenharmony_ci		S('l') = LLPRE,
72570af302Sopenharmony_ci	}, { /* 2: ll-prefixed */
73570af302Sopenharmony_ci		S('d') = LLONG, S('i') = LLONG,
74570af302Sopenharmony_ci		S('o') = ULLONG, S('u') = ULLONG,
75570af302Sopenharmony_ci		S('x') = ULLONG, S('X') = ULLONG,
76570af302Sopenharmony_ci		S('n') = PTR,
77570af302Sopenharmony_ci	}, { /* 3: h-prefixed */
78570af302Sopenharmony_ci		S('d') = SHORT, S('i') = SHORT,
79570af302Sopenharmony_ci		S('o') = USHORT, S('u') = USHORT,
80570af302Sopenharmony_ci		S('x') = USHORT, S('X') = USHORT,
81570af302Sopenharmony_ci		S('n') = PTR,
82570af302Sopenharmony_ci		S('h') = HHPRE,
83570af302Sopenharmony_ci	}, { /* 4: hh-prefixed */
84570af302Sopenharmony_ci		S('d') = CHAR, S('i') = CHAR,
85570af302Sopenharmony_ci		S('o') = UCHAR, S('u') = UCHAR,
86570af302Sopenharmony_ci		S('x') = UCHAR, S('X') = UCHAR,
87570af302Sopenharmony_ci		S('n') = PTR,
88570af302Sopenharmony_ci	}, { /* 5: L-prefixed */
89570af302Sopenharmony_ci		S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL,
90570af302Sopenharmony_ci		S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL,
91570af302Sopenharmony_ci		S('n') = PTR,
92570af302Sopenharmony_ci	}, { /* 6: z- or t-prefixed (assumed to be same size) */
93570af302Sopenharmony_ci		S('d') = PDIFF, S('i') = PDIFF,
94570af302Sopenharmony_ci		S('o') = SIZET, S('u') = SIZET,
95570af302Sopenharmony_ci		S('x') = SIZET, S('X') = SIZET,
96570af302Sopenharmony_ci		S('n') = PTR,
97570af302Sopenharmony_ci	}, { /* 7: j-prefixed */
98570af302Sopenharmony_ci		S('d') = IMAX, S('i') = IMAX,
99570af302Sopenharmony_ci		S('o') = UMAX, S('u') = UMAX,
100570af302Sopenharmony_ci		S('x') = UMAX, S('X') = UMAX,
101570af302Sopenharmony_ci		S('n') = PTR,
102570af302Sopenharmony_ci	}
103570af302Sopenharmony_ci};
104570af302Sopenharmony_ci
105570af302Sopenharmony_ci#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
106570af302Sopenharmony_ci
107570af302Sopenharmony_ciunion arg
108570af302Sopenharmony_ci{
109570af302Sopenharmony_ci	uintmax_t i;
110570af302Sopenharmony_ci	long double f;
111570af302Sopenharmony_ci	void *p;
112570af302Sopenharmony_ci};
113570af302Sopenharmony_ci
114570af302Sopenharmony_cistatic void pop_arg(union arg *arg, int type, va_list *ap)
115570af302Sopenharmony_ci{
116570af302Sopenharmony_ci	switch (type) {
117570af302Sopenharmony_ci	       case PTR:	arg->p = va_arg(*ap, void *);
118570af302Sopenharmony_ci	break; case INT:	arg->i = va_arg(*ap, int);
119570af302Sopenharmony_ci	break; case UINT:	arg->i = va_arg(*ap, unsigned int);
120570af302Sopenharmony_ci	break; case LONG:	arg->i = va_arg(*ap, long);
121570af302Sopenharmony_ci	break; case ULONG:	arg->i = va_arg(*ap, unsigned long);
122570af302Sopenharmony_ci	break; case ULLONG:	arg->i = va_arg(*ap, unsigned long long);
123570af302Sopenharmony_ci	break; case SHORT:	arg->i = (short)va_arg(*ap, int);
124570af302Sopenharmony_ci	break; case USHORT:	arg->i = (unsigned short)va_arg(*ap, int);
125570af302Sopenharmony_ci	break; case CHAR:	arg->i = (signed char)va_arg(*ap, int);
126570af302Sopenharmony_ci	break; case UCHAR:	arg->i = (unsigned char)va_arg(*ap, int);
127570af302Sopenharmony_ci	break; case LLONG:	arg->i = va_arg(*ap, long long);
128570af302Sopenharmony_ci	break; case SIZET:	arg->i = va_arg(*ap, size_t);
129570af302Sopenharmony_ci	break; case IMAX:	arg->i = va_arg(*ap, intmax_t);
130570af302Sopenharmony_ci	break; case UMAX:	arg->i = va_arg(*ap, uintmax_t);
131570af302Sopenharmony_ci	break; case PDIFF:	arg->i = va_arg(*ap, ptrdiff_t);
132570af302Sopenharmony_ci	break; case UIPTR:	arg->i = (uintptr_t)va_arg(*ap, void *);
133570af302Sopenharmony_ci	break; case DBL:	arg->f = va_arg(*ap, double);
134570af302Sopenharmony_ci	break; case LDBL:	arg->f = va_arg(*ap, long double);
135570af302Sopenharmony_ci	}
136570af302Sopenharmony_ci}
137570af302Sopenharmony_ci
138570af302Sopenharmony_cistatic void out(FILE *f, const char *s, size_t l)
139570af302Sopenharmony_ci{
140570af302Sopenharmony_ci	if (!(f->flags & F_ERR)) __fwritex((void *)s, l, f);
141570af302Sopenharmony_ci}
142570af302Sopenharmony_ci
143570af302Sopenharmony_cistatic void pad(FILE *f, char c, int w, int l, int fl)
144570af302Sopenharmony_ci{
145570af302Sopenharmony_ci	char pad[256];
146570af302Sopenharmony_ci	if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
147570af302Sopenharmony_ci	l = w - l;
148570af302Sopenharmony_ci	memset(pad, c, l>sizeof pad ? sizeof pad : l);
149570af302Sopenharmony_ci	for (; l >= sizeof pad; l -= sizeof pad)
150570af302Sopenharmony_ci		out(f, pad, sizeof pad);
151570af302Sopenharmony_ci	out(f, pad, l);
152570af302Sopenharmony_ci}
153570af302Sopenharmony_ci
154570af302Sopenharmony_cistatic const char xdigits[16] = {
155570af302Sopenharmony_ci	"0123456789ABCDEF"
156570af302Sopenharmony_ci};
157570af302Sopenharmony_ci
158570af302Sopenharmony_cistatic char *fmt_x(uintmax_t x, char *s, int lower)
159570af302Sopenharmony_ci{
160570af302Sopenharmony_ci	for (; x; x>>=4) *--s = xdigits[(x&15)]|lower;
161570af302Sopenharmony_ci	return s;
162570af302Sopenharmony_ci}
163570af302Sopenharmony_ci
164570af302Sopenharmony_cistatic char *fmt_o(uintmax_t x, char *s)
165570af302Sopenharmony_ci{
166570af302Sopenharmony_ci	for (; x; x>>=3) *--s = '0' + (x&7);
167570af302Sopenharmony_ci	return s;
168570af302Sopenharmony_ci}
169570af302Sopenharmony_ci
170570af302Sopenharmony_cistatic char *fmt_u(uintmax_t x, char *s)
171570af302Sopenharmony_ci{
172570af302Sopenharmony_ci	unsigned long y;
173570af302Sopenharmony_ci	for (   ; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
174570af302Sopenharmony_ci	for (y=x;           y; y/=10) *--s = '0' + y%10;
175570af302Sopenharmony_ci	return s;
176570af302Sopenharmony_ci}
177570af302Sopenharmony_ci
178570af302Sopenharmony_ci/* Do not override this check. The floating point printing code below
179570af302Sopenharmony_ci * depends on the float.h constants being right. If they are wrong, it
180570af302Sopenharmony_ci * may overflow the stack. */
181570af302Sopenharmony_ci#if LDBL_MANT_DIG == 53
182570af302Sopenharmony_ci// typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
183570af302Sopenharmony_ci#endif
184570af302Sopenharmony_ci
185570af302Sopenharmony_cistatic int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t)
186570af302Sopenharmony_ci{
187570af302Sopenharmony_ci	uint32_t big[(LDBL_MANT_DIG+28)/29 + 1          // mantissa expansion
188570af302Sopenharmony_ci		+ (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
189570af302Sopenharmony_ci	uint32_t *a, *d, *r, *z;
190570af302Sopenharmony_ci	int e2=0, e, i, j, l;
191570af302Sopenharmony_ci	char buf[9+LDBL_MANT_DIG/4], *s;
192570af302Sopenharmony_ci	const char *prefix="-0X+0X 0X-0x+0x 0x";
193570af302Sopenharmony_ci	int pl;
194570af302Sopenharmony_ci	char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)];
195570af302Sopenharmony_ci	char *estr = NULL;
196570af302Sopenharmony_ci
197570af302Sopenharmony_ci	pl=1;
198570af302Sopenharmony_ci	if (signbit(y)) {
199570af302Sopenharmony_ci		y=-y;
200570af302Sopenharmony_ci	} else if (fl & MARK_POS) {
201570af302Sopenharmony_ci		prefix+=3;
202570af302Sopenharmony_ci	} else if (fl & PAD_POS) {
203570af302Sopenharmony_ci		prefix+=6;
204570af302Sopenharmony_ci	} else prefix++, pl=0;
205570af302Sopenharmony_ci
206570af302Sopenharmony_ci	if (!isfinite(y)) {
207570af302Sopenharmony_ci		char *s = (t&32)?"inf":"INF";
208570af302Sopenharmony_ci		if (y!=y) s=(t&32)?"nan":"NAN";
209570af302Sopenharmony_ci		pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
210570af302Sopenharmony_ci		out(f, prefix, pl);
211570af302Sopenharmony_ci		out(f, s, 3);
212570af302Sopenharmony_ci		pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
213570af302Sopenharmony_ci		return MAX(w, 3+pl);
214570af302Sopenharmony_ci	}
215570af302Sopenharmony_ci
216570af302Sopenharmony_ci	y = frexpl(y, &e2) * 2;
217570af302Sopenharmony_ci	if (y) e2--;
218570af302Sopenharmony_ci
219570af302Sopenharmony_ci	if ((t|32)=='a') {
220570af302Sopenharmony_ci		long double round = 8.0;
221570af302Sopenharmony_ci		int re;
222570af302Sopenharmony_ci
223570af302Sopenharmony_ci		if (t&32) prefix += 9;
224570af302Sopenharmony_ci		pl += 2;
225570af302Sopenharmony_ci
226570af302Sopenharmony_ci		if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
227570af302Sopenharmony_ci		else re=LDBL_MANT_DIG/4-1-p;
228570af302Sopenharmony_ci
229570af302Sopenharmony_ci		if (re) {
230570af302Sopenharmony_ci			round *= 1<<(LDBL_MANT_DIG%4);
231570af302Sopenharmony_ci			while (re--) round*=16;
232570af302Sopenharmony_ci			if (*prefix=='-') {
233570af302Sopenharmony_ci				y=-y;
234570af302Sopenharmony_ci				y-=round;
235570af302Sopenharmony_ci				y+=round;
236570af302Sopenharmony_ci				y=-y;
237570af302Sopenharmony_ci			} else {
238570af302Sopenharmony_ci				y+=round;
239570af302Sopenharmony_ci				y-=round;
240570af302Sopenharmony_ci			}
241570af302Sopenharmony_ci		}
242570af302Sopenharmony_ci
243570af302Sopenharmony_ci		estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
244570af302Sopenharmony_ci		if (estr==ebuf) *--estr='0';
245570af302Sopenharmony_ci		*--estr = (e2<0 ? '-' : '+');
246570af302Sopenharmony_ci		*--estr = t+('p'-'a');
247570af302Sopenharmony_ci
248570af302Sopenharmony_ci		s=buf;
249570af302Sopenharmony_ci		do {
250570af302Sopenharmony_ci			int x=y;
251570af302Sopenharmony_ci			*s++=xdigits[x]|(t&32);
252570af302Sopenharmony_ci			y=16*(y-x);
253570af302Sopenharmony_ci			if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
254570af302Sopenharmony_ci		} while (y);
255570af302Sopenharmony_ci
256570af302Sopenharmony_ci		if (p > INT_MAX-2-(ebuf-estr)-pl)
257570af302Sopenharmony_ci			return -1;
258570af302Sopenharmony_ci		if (p && s-buf-2 < p)
259570af302Sopenharmony_ci			l = (p+2) + (ebuf-estr);
260570af302Sopenharmony_ci		else
261570af302Sopenharmony_ci			l = (s-buf) + (ebuf-estr);
262570af302Sopenharmony_ci
263570af302Sopenharmony_ci		pad(f, ' ', w, pl+l, fl);
264570af302Sopenharmony_ci		out(f, prefix, pl);
265570af302Sopenharmony_ci		pad(f, '0', w, pl+l, fl^ZERO_PAD);
266570af302Sopenharmony_ci		out(f, buf, s-buf);
267570af302Sopenharmony_ci		pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
268570af302Sopenharmony_ci		out(f, estr, ebuf-estr);
269570af302Sopenharmony_ci		pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
270570af302Sopenharmony_ci		return MAX(w, pl+l);
271570af302Sopenharmony_ci	}
272570af302Sopenharmony_ci	if (p<0) p=6;
273570af302Sopenharmony_ci
274570af302Sopenharmony_ci	if (y) y *= 0x1p28, e2-=28;
275570af302Sopenharmony_ci
276570af302Sopenharmony_ci	if (e2<0) a=r=z=big;
277570af302Sopenharmony_ci	else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
278570af302Sopenharmony_ci
279570af302Sopenharmony_ci	do {
280570af302Sopenharmony_ci		*z = y;
281570af302Sopenharmony_ci		y = 1000000000*(y-*z++);
282570af302Sopenharmony_ci	} while (y);
283570af302Sopenharmony_ci
284570af302Sopenharmony_ci	while (e2>0) {
285570af302Sopenharmony_ci		uint32_t carry=0;
286570af302Sopenharmony_ci		int sh=MIN(29,e2);
287570af302Sopenharmony_ci		for (d=z-1; d>=a; d--) {
288570af302Sopenharmony_ci			uint64_t x = ((uint64_t)*d<<sh)+carry;
289570af302Sopenharmony_ci			*d = x % 1000000000;
290570af302Sopenharmony_ci			carry = x / 1000000000;
291570af302Sopenharmony_ci		}
292570af302Sopenharmony_ci		if (carry) *--a = carry;
293570af302Sopenharmony_ci		while (z>a && !z[-1]) z--;
294570af302Sopenharmony_ci		e2-=sh;
295570af302Sopenharmony_ci	}
296570af302Sopenharmony_ci	while (e2<0) {
297570af302Sopenharmony_ci		uint32_t carry=0, *b;
298570af302Sopenharmony_ci		int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3U+8)/9;
299570af302Sopenharmony_ci		for (d=a; d<z; d++) {
300570af302Sopenharmony_ci			uint32_t rm = *d & ((1<<sh)-1);
301570af302Sopenharmony_ci			*d = (*d>>sh) + carry;
302570af302Sopenharmony_ci			carry = (1000000000>>sh) * rm;
303570af302Sopenharmony_ci		}
304570af302Sopenharmony_ci		if (!*a) a++;
305570af302Sopenharmony_ci		if (carry) *z++ = carry;
306570af302Sopenharmony_ci		/* Avoid (slow!) computation past requested precision */
307570af302Sopenharmony_ci		b = (t|32)=='f' ? r : a;
308570af302Sopenharmony_ci		if (z-b > need) z = b+need;
309570af302Sopenharmony_ci		e2+=sh;
310570af302Sopenharmony_ci	}
311570af302Sopenharmony_ci
312570af302Sopenharmony_ci	if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
313570af302Sopenharmony_ci	else e=0;
314570af302Sopenharmony_ci
315570af302Sopenharmony_ci	/* Perform rounding: j is precision after the radix (possibly neg) */
316570af302Sopenharmony_ci	j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
317570af302Sopenharmony_ci	if (j < 9*(z-r-1)) {
318570af302Sopenharmony_ci		uint32_t x;
319570af302Sopenharmony_ci		/* We avoid C's broken division of negative numbers */
320570af302Sopenharmony_ci		d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
321570af302Sopenharmony_ci		j += 9*LDBL_MAX_EXP;
322570af302Sopenharmony_ci		j %= 9;
323570af302Sopenharmony_ci		for (i=10, j++; j<9; i*=10, j++);
324570af302Sopenharmony_ci		x = *d % i;
325570af302Sopenharmony_ci		/* Are there any significant digits past j? */
326570af302Sopenharmony_ci		if (x || d+1!=z) {
327570af302Sopenharmony_ci			long double round = 2/LDBL_EPSILON;
328570af302Sopenharmony_ci			long double small;
329570af302Sopenharmony_ci			if ((*d/i & 1) || (i==1000000000 && d>a && (d[-1]&1)))
330570af302Sopenharmony_ci				round += 2;
331570af302Sopenharmony_ci			if (x<i/2) small=0x0.8p0;
332570af302Sopenharmony_ci			else if (x==i/2 && d+1==z) small=0x1.0p0;
333570af302Sopenharmony_ci			else small=0x1.8p0;
334570af302Sopenharmony_ci			if (pl && *prefix=='-') round*=-1, small*=-1;
335570af302Sopenharmony_ci			*d -= x;
336570af302Sopenharmony_ci			/* Decide whether to round by probing round+small */
337570af302Sopenharmony_ci			if (round+small != round) {
338570af302Sopenharmony_ci				*d = *d + i;
339570af302Sopenharmony_ci				while (*d > 999999999) {
340570af302Sopenharmony_ci					*d--=0;
341570af302Sopenharmony_ci					if (d<a) *--a=0;
342570af302Sopenharmony_ci					(*d)++;
343570af302Sopenharmony_ci				}
344570af302Sopenharmony_ci				for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
345570af302Sopenharmony_ci			}
346570af302Sopenharmony_ci		}
347570af302Sopenharmony_ci		if (z>d+1) z=d+1;
348570af302Sopenharmony_ci	}
349570af302Sopenharmony_ci	for (; z>a && !z[-1]; z--);
350570af302Sopenharmony_ci
351570af302Sopenharmony_ci	if ((t|32)=='g') {
352570af302Sopenharmony_ci		if (!p) p++;
353570af302Sopenharmony_ci		if (p>e && e>=-4) {
354570af302Sopenharmony_ci			t--;
355570af302Sopenharmony_ci			p-=e+1;
356570af302Sopenharmony_ci		} else {
357570af302Sopenharmony_ci			t-=2;
358570af302Sopenharmony_ci			p--;
359570af302Sopenharmony_ci		}
360570af302Sopenharmony_ci		if (!(fl&ALT_FORM)) {
361570af302Sopenharmony_ci			/* Count trailing zeros in last place */
362570af302Sopenharmony_ci			if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
363570af302Sopenharmony_ci			else j=9;
364570af302Sopenharmony_ci			if ((t|32)=='f')
365570af302Sopenharmony_ci				p = MIN(p,MAX(0,9*(z-r-1)-j));
366570af302Sopenharmony_ci			else
367570af302Sopenharmony_ci				p = MIN(p,MAX(0,9*(z-r-1)+e-j));
368570af302Sopenharmony_ci		}
369570af302Sopenharmony_ci	}
370570af302Sopenharmony_ci	if (p > INT_MAX-1-(p || (fl&ALT_FORM)))
371570af302Sopenharmony_ci		return -1;
372570af302Sopenharmony_ci	l = 1 + p + (p || (fl&ALT_FORM));
373570af302Sopenharmony_ci	if ((t|32)=='f') {
374570af302Sopenharmony_ci		if (e > INT_MAX-l) return -1;
375570af302Sopenharmony_ci		if (e>0) l+=e;
376570af302Sopenharmony_ci	} else {
377570af302Sopenharmony_ci		estr=fmt_u(e<0 ? -e : e, ebuf);
378570af302Sopenharmony_ci		while(ebuf-estr<2) *--estr='0';
379570af302Sopenharmony_ci		*--estr = (e<0 ? '-' : '+');
380570af302Sopenharmony_ci		*--estr = t;
381570af302Sopenharmony_ci		if (ebuf-estr > INT_MAX-l) return -1;
382570af302Sopenharmony_ci		l += ebuf-estr;
383570af302Sopenharmony_ci	}
384570af302Sopenharmony_ci
385570af302Sopenharmony_ci	if (l > INT_MAX-pl) return -1;
386570af302Sopenharmony_ci	pad(f, ' ', w, pl+l, fl);
387570af302Sopenharmony_ci	out(f, prefix, pl);
388570af302Sopenharmony_ci	pad(f, '0', w, pl+l, fl^ZERO_PAD);
389570af302Sopenharmony_ci
390570af302Sopenharmony_ci	if ((t|32)=='f') {
391570af302Sopenharmony_ci		if (a>r) a=r;
392570af302Sopenharmony_ci		for (d=a; d<=r; d++) {
393570af302Sopenharmony_ci			char *s = fmt_u(*d, buf+9);
394570af302Sopenharmony_ci			if (d!=a) while (s>buf) *--s='0';
395570af302Sopenharmony_ci			else if (s==buf+9) *--s='0';
396570af302Sopenharmony_ci			out(f, s, buf+9-s);
397570af302Sopenharmony_ci		}
398570af302Sopenharmony_ci		if (p || (fl&ALT_FORM)) out(f, ".", 1);
399570af302Sopenharmony_ci		for (; d<z && p>0; d++, p-=9) {
400570af302Sopenharmony_ci			char *s = fmt_u(*d, buf+9);
401570af302Sopenharmony_ci			while (s>buf) *--s='0';
402570af302Sopenharmony_ci			out(f, s, MIN(9,p));
403570af302Sopenharmony_ci		}
404570af302Sopenharmony_ci		pad(f, '0', p+9, 9, 0);
405570af302Sopenharmony_ci	} else {
406570af302Sopenharmony_ci		if (z<=a) z=a+1;
407570af302Sopenharmony_ci		for (d=a; d<z && p>=0; d++) {
408570af302Sopenharmony_ci			char *s = fmt_u(*d, buf+9);
409570af302Sopenharmony_ci			if (s==buf+9) *--s='0';
410570af302Sopenharmony_ci			if (d!=a) while (s>buf) *--s='0';
411570af302Sopenharmony_ci			else {
412570af302Sopenharmony_ci				out(f, s++, 1);
413570af302Sopenharmony_ci				if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
414570af302Sopenharmony_ci			}
415570af302Sopenharmony_ci			out(f, s, MIN(buf+9-s, p));
416570af302Sopenharmony_ci			p -= buf+9-s;
417570af302Sopenharmony_ci		}
418570af302Sopenharmony_ci		pad(f, '0', p+18, 18, 0);
419570af302Sopenharmony_ci		out(f, estr, ebuf-estr);
420570af302Sopenharmony_ci	}
421570af302Sopenharmony_ci
422570af302Sopenharmony_ci	pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
423570af302Sopenharmony_ci
424570af302Sopenharmony_ci	return MAX(w, pl+l);
425570af302Sopenharmony_ci}
426570af302Sopenharmony_ci
427570af302Sopenharmony_cistatic int getint(char **s) {
428570af302Sopenharmony_ci	int i;
429570af302Sopenharmony_ci	for (i=0; isdigit(**s); (*s)++) {
430570af302Sopenharmony_ci		if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1;
431570af302Sopenharmony_ci		else i = 10*i + (**s-'0');
432570af302Sopenharmony_ci	}
433570af302Sopenharmony_ci	return i;
434570af302Sopenharmony_ci}
435570af302Sopenharmony_ci
436570af302Sopenharmony_cistatic int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
437570af302Sopenharmony_ci{
438570af302Sopenharmony_ci	char *a, *z, *s=(char *)fmt;
439570af302Sopenharmony_ci	unsigned l10n=0, fl;
440570af302Sopenharmony_ci	int w, p, xp;
441570af302Sopenharmony_ci	union arg arg;
442570af302Sopenharmony_ci	int argpos;
443570af302Sopenharmony_ci	unsigned st, ps;
444570af302Sopenharmony_ci	int cnt=0, l=0;
445570af302Sopenharmony_ci	size_t i;
446570af302Sopenharmony_ci	char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4];
447570af302Sopenharmony_ci	const char *prefix;
448570af302Sopenharmony_ci	int t, pl;
449570af302Sopenharmony_ci	wchar_t wc[2], *ws;
450570af302Sopenharmony_ci	char mb[4];
451570af302Sopenharmony_ci#ifdef OS_SUPPORT_FS
452570af302Sopenharmony_ci	struct winsize wsz;
453570af302Sopenharmony_ci
454570af302Sopenharmony_ci	if (f && (f->write == __stdout_write)) {
455570af302Sopenharmony_ci		f->lbf = '\n';
456570af302Sopenharmony_ci		f->write = __stdio_write;
457570af302Sopenharmony_ci		f->wpos = f->wbase = f->buf;
458570af302Sopenharmony_ci		f->wend = f->buf + f->buf_size;
459570af302Sopenharmony_ci	}
460570af302Sopenharmony_ci#endif
461570af302Sopenharmony_ci	for (;;) {
462570af302Sopenharmony_ci		/* This error is only specified for snprintf, but since it's
463570af302Sopenharmony_ci		 * unspecified for other forms, do the same. Stop immediately
464570af302Sopenharmony_ci		 * on overflow; otherwise %n could produce wrong results. */
465570af302Sopenharmony_ci		if (l > INT_MAX - cnt) goto overflow;
466570af302Sopenharmony_ci
467570af302Sopenharmony_ci		/* Update output count, end loop when fmt is exhausted */
468570af302Sopenharmony_ci		cnt += l;
469570af302Sopenharmony_ci		if (!*s) break;
470570af302Sopenharmony_ci
471570af302Sopenharmony_ci		/* Handle literal text and %% format specifiers */
472570af302Sopenharmony_ci		for (a=s; *s && *s!='%'; s++);
473570af302Sopenharmony_ci		for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2);
474570af302Sopenharmony_ci		if (z-a > INT_MAX-cnt) goto overflow;
475570af302Sopenharmony_ci		l = z-a;
476570af302Sopenharmony_ci		if (f) out(f, a, l);
477570af302Sopenharmony_ci		if (l) continue;
478570af302Sopenharmony_ci
479570af302Sopenharmony_ci		if (isdigit(s[1]) && s[2]=='$') {
480570af302Sopenharmony_ci			l10n=1;
481570af302Sopenharmony_ci			argpos = s[1]-'0';
482570af302Sopenharmony_ci			s+=3;
483570af302Sopenharmony_ci		} else {
484570af302Sopenharmony_ci			argpos = -1;
485570af302Sopenharmony_ci			s++;
486570af302Sopenharmony_ci		}
487570af302Sopenharmony_ci
488570af302Sopenharmony_ci		/* Read modifier flags */
489570af302Sopenharmony_ci		for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<(*s-' '))); s++)
490570af302Sopenharmony_ci			fl |= 1U<<(*s-' ');
491570af302Sopenharmony_ci
492570af302Sopenharmony_ci		/* Read field width */
493570af302Sopenharmony_ci		if (*s=='*') {
494570af302Sopenharmony_ci			if (isdigit(s[1]) && s[2]=='$') {
495570af302Sopenharmony_ci				l10n=1;
496570af302Sopenharmony_ci				nl_type[s[1]-'0'] = INT;
497570af302Sopenharmony_ci				w = nl_arg[s[1]-'0'].i;
498570af302Sopenharmony_ci				s+=3;
499570af302Sopenharmony_ci			} else if (!l10n) {
500570af302Sopenharmony_ci				w = f ? va_arg(*ap, int) : 0;
501570af302Sopenharmony_ci				s++;
502570af302Sopenharmony_ci			} else goto inval;
503570af302Sopenharmony_ci			if (w<0) fl|=LEFT_ADJ, w=-w;
504570af302Sopenharmony_ci		} else if ((w=getint(&s))<0) goto overflow;
505570af302Sopenharmony_ci
506570af302Sopenharmony_ci		/* Read precision */
507570af302Sopenharmony_ci		if (*s=='.' && s[1]=='*') {
508570af302Sopenharmony_ci			if (isdigit(s[2]) && s[3]=='$') {
509570af302Sopenharmony_ci				nl_type[s[2]-'0'] = INT;
510570af302Sopenharmony_ci				p = nl_arg[s[2]-'0'].i;
511570af302Sopenharmony_ci				s+=4;
512570af302Sopenharmony_ci			} else if (!l10n) {
513570af302Sopenharmony_ci				p = f ? va_arg(*ap, int) : 0;
514570af302Sopenharmony_ci				s+=2;
515570af302Sopenharmony_ci			} else goto inval;
516570af302Sopenharmony_ci			xp = (p>=0);
517570af302Sopenharmony_ci		} else if (*s=='.') {
518570af302Sopenharmony_ci			s++;
519570af302Sopenharmony_ci			p = getint(&s);
520570af302Sopenharmony_ci			xp = 1;
521570af302Sopenharmony_ci		} else {
522570af302Sopenharmony_ci			p = -1;
523570af302Sopenharmony_ci			xp = 0;
524570af302Sopenharmony_ci		}
525570af302Sopenharmony_ci
526570af302Sopenharmony_ci		/* Format specifier state machine */
527570af302Sopenharmony_ci		st=0;
528570af302Sopenharmony_ci		do {
529570af302Sopenharmony_ci			if (OOB(*s)) goto inval;
530570af302Sopenharmony_ci			ps=st;
531570af302Sopenharmony_ci			st=states[st]S(*s++);
532570af302Sopenharmony_ci		} while (st-1<STOP);
533570af302Sopenharmony_ci		if (!st) goto inval;
534570af302Sopenharmony_ci
535570af302Sopenharmony_ci		/* Check validity of argument type (nl/normal) */
536570af302Sopenharmony_ci		if (st==NOARG) {
537570af302Sopenharmony_ci			if (argpos>=0) goto inval;
538570af302Sopenharmony_ci		} else {
539570af302Sopenharmony_ci			if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
540570af302Sopenharmony_ci			else if (f) pop_arg(&arg, st, ap);
541570af302Sopenharmony_ci			else return 0;
542570af302Sopenharmony_ci		}
543570af302Sopenharmony_ci
544570af302Sopenharmony_ci		if (!f) continue;
545570af302Sopenharmony_ci
546570af302Sopenharmony_ci		z = buf + sizeof(buf);
547570af302Sopenharmony_ci		prefix = "-+   0X0x";
548570af302Sopenharmony_ci		pl = 0;
549570af302Sopenharmony_ci		t = s[-1];
550570af302Sopenharmony_ci
551570af302Sopenharmony_ci		/* Transform ls,lc -> S,C */
552570af302Sopenharmony_ci		if (ps && (t&15)==3) t&=~32;
553570af302Sopenharmony_ci
554570af302Sopenharmony_ci		/* - and 0 flags are mutually exclusive */
555570af302Sopenharmony_ci		if (fl & LEFT_ADJ) fl &= ~ZERO_PAD;
556570af302Sopenharmony_ci
557570af302Sopenharmony_ci		switch(t) {
558570af302Sopenharmony_ci		case 'n':
559570af302Sopenharmony_ci			switch(ps) {
560570af302Sopenharmony_ci			case BARE: *(int *)arg.p = cnt; break;
561570af302Sopenharmony_ci			case LPRE: *(long *)arg.p = cnt; break;
562570af302Sopenharmony_ci			case LLPRE: *(long long *)arg.p = cnt; break;
563570af302Sopenharmony_ci			case HPRE: *(unsigned short *)arg.p = cnt; break;
564570af302Sopenharmony_ci			case HHPRE: *(unsigned char *)arg.p = cnt; break;
565570af302Sopenharmony_ci			case ZTPRE: *(size_t *)arg.p = cnt; break;
566570af302Sopenharmony_ci			case JPRE: *(uintmax_t *)arg.p = cnt; break;
567570af302Sopenharmony_ci			}
568570af302Sopenharmony_ci			continue;
569570af302Sopenharmony_ci		case 'p':
570570af302Sopenharmony_ci			p = MAX(p, 2*sizeof(void*));
571570af302Sopenharmony_ci			t = 'x';
572570af302Sopenharmony_ci			fl |= ALT_FORM;
573570af302Sopenharmony_ci		case 'x': case 'X':
574570af302Sopenharmony_ci			a = fmt_x(arg.i, z, t&32);
575570af302Sopenharmony_ci			if (arg.i && (fl & ALT_FORM)) prefix+=(t>>4), pl=2;
576570af302Sopenharmony_ci			if (0) {
577570af302Sopenharmony_ci		case 'o':
578570af302Sopenharmony_ci			a = fmt_o(arg.i, z);
579570af302Sopenharmony_ci			if ((fl&ALT_FORM) && p<z-a+1) p=z-a+1;
580570af302Sopenharmony_ci			} if (0) {
581570af302Sopenharmony_ci		case 'd': case 'i':
582570af302Sopenharmony_ci			pl=1;
583570af302Sopenharmony_ci			if (arg.i>INTMAX_MAX) {
584570af302Sopenharmony_ci				arg.i=-arg.i;
585570af302Sopenharmony_ci			} else if (fl & MARK_POS) {
586570af302Sopenharmony_ci				prefix++;
587570af302Sopenharmony_ci			} else if (fl & PAD_POS) {
588570af302Sopenharmony_ci				prefix+=2;
589570af302Sopenharmony_ci			} else pl=0;
590570af302Sopenharmony_ci		case 'u':
591570af302Sopenharmony_ci			a = fmt_u(arg.i, z);
592570af302Sopenharmony_ci			}
593570af302Sopenharmony_ci			if (xp && p<0) goto overflow;
594570af302Sopenharmony_ci			if (xp) fl &= ~ZERO_PAD;
595570af302Sopenharmony_ci			if (!arg.i && !p) {
596570af302Sopenharmony_ci				a=z;
597570af302Sopenharmony_ci				break;
598570af302Sopenharmony_ci			}
599570af302Sopenharmony_ci			p = MAX(p, z-a + !arg.i);
600570af302Sopenharmony_ci			break;
601570af302Sopenharmony_ci		case 'c':
602570af302Sopenharmony_ci			*(a=z-(p=1))=arg.i;
603570af302Sopenharmony_ci			fl &= ~ZERO_PAD;
604570af302Sopenharmony_ci			break;
605570af302Sopenharmony_ci		case 'm':
606570af302Sopenharmony_ci			if (1) a = strerror(errno); else
607570af302Sopenharmony_ci		case 's':
608570af302Sopenharmony_ci			a = arg.p ? arg.p : "(null)";
609570af302Sopenharmony_ci			z = a + strnlen(a, p<0 ? INT_MAX : p);
610570af302Sopenharmony_ci			if (p<0 && *z) goto overflow;
611570af302Sopenharmony_ci			p = z-a;
612570af302Sopenharmony_ci			fl &= ~ZERO_PAD;
613570af302Sopenharmony_ci			break;
614570af302Sopenharmony_ci		case 'C':
615570af302Sopenharmony_ci			wc[0] = arg.i;
616570af302Sopenharmony_ci			wc[1] = 0;
617570af302Sopenharmony_ci			arg.p = wc;
618570af302Sopenharmony_ci			p = -1;
619570af302Sopenharmony_ci		case 'S':
620570af302Sopenharmony_ci			ws = arg.p;
621570af302Sopenharmony_ci			for (i=l=0; i<p && *ws && (l=wctomb(mb, *ws++))>=0 && l<=p-i; i+=l);
622570af302Sopenharmony_ci			if (l<0) return -1;
623570af302Sopenharmony_ci			if (i > INT_MAX) goto overflow;
624570af302Sopenharmony_ci			p = i;
625570af302Sopenharmony_ci			pad(f, ' ', w, p, fl);
626570af302Sopenharmony_ci			ws = arg.p;
627570af302Sopenharmony_ci			for (i=0; i<0U+p && *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l)
628570af302Sopenharmony_ci				out(f, mb, l);
629570af302Sopenharmony_ci			pad(f, ' ', w, p, fl^LEFT_ADJ);
630570af302Sopenharmony_ci			l = w>p ? w : p;
631570af302Sopenharmony_ci			continue;
632570af302Sopenharmony_ci		case 'e': case 'f': case 'g': case 'a':
633570af302Sopenharmony_ci		case 'E': case 'F': case 'G': case 'A':
634570af302Sopenharmony_ci			if (xp && p<0) goto overflow;
635570af302Sopenharmony_ci			l = fmt_fp(f, arg.f, w, p, fl, t);
636570af302Sopenharmony_ci			if (l<0) goto overflow;
637570af302Sopenharmony_ci			continue;
638570af302Sopenharmony_ci		}
639570af302Sopenharmony_ci
640570af302Sopenharmony_ci		if (p < z-a) p = z-a;
641570af302Sopenharmony_ci		if (p > INT_MAX-pl) goto overflow;
642570af302Sopenharmony_ci		if (w < pl+p) w = pl+p;
643570af302Sopenharmony_ci		if (w > INT_MAX-cnt) goto overflow;
644570af302Sopenharmony_ci
645570af302Sopenharmony_ci		pad(f, ' ', w, pl+p, fl);
646570af302Sopenharmony_ci		out(f, prefix, pl);
647570af302Sopenharmony_ci		pad(f, '0', w, pl+p, fl^ZERO_PAD);
648570af302Sopenharmony_ci		pad(f, '0', p, z-a, 0);
649570af302Sopenharmony_ci		out(f, a, z-a);
650570af302Sopenharmony_ci		pad(f, ' ', w, pl+p, fl^LEFT_ADJ);
651570af302Sopenharmony_ci
652570af302Sopenharmony_ci		l = w;
653570af302Sopenharmony_ci	}
654570af302Sopenharmony_ci
655570af302Sopenharmony_ci	if (f) return cnt;
656570af302Sopenharmony_ci	if (!l10n) return 0;
657570af302Sopenharmony_ci
658570af302Sopenharmony_ci	for (i=1; i<=NL_ARGMAX && nl_type[i]; i++)
659570af302Sopenharmony_ci		pop_arg(nl_arg+i, nl_type[i], ap);
660570af302Sopenharmony_ci	for (; i<=NL_ARGMAX && !nl_type[i]; i++);
661570af302Sopenharmony_ci	if (i<=NL_ARGMAX) goto inval;
662570af302Sopenharmony_ci	return 1;
663570af302Sopenharmony_ci
664570af302Sopenharmony_ciinval:
665570af302Sopenharmony_ci	errno = EINVAL;
666570af302Sopenharmony_ci	return -1;
667570af302Sopenharmony_cioverflow:
668570af302Sopenharmony_ci	errno = EOVERFLOW;
669570af302Sopenharmony_ci	return -1;
670570af302Sopenharmony_ci}
671570af302Sopenharmony_ci
672570af302Sopenharmony_ciint vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
673570af302Sopenharmony_ci{
674570af302Sopenharmony_ci	va_list ap2;
675570af302Sopenharmony_ci	int nl_type[NL_ARGMAX+1] = {0};
676570af302Sopenharmony_ci	union arg nl_arg[NL_ARGMAX+1];
677570af302Sopenharmony_ci	unsigned char internal_buf[80], *saved_buf = 0;
678570af302Sopenharmony_ci	int olderr;
679570af302Sopenharmony_ci	int ret;
680570af302Sopenharmony_ci
681570af302Sopenharmony_ci	/* the copy allows passing va_list* even if va_list is an array */
682570af302Sopenharmony_ci	va_copy(ap2, ap);
683570af302Sopenharmony_ci	if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) {
684570af302Sopenharmony_ci		va_end(ap2);
685570af302Sopenharmony_ci		return -1;
686570af302Sopenharmony_ci	}
687570af302Sopenharmony_ci
688570af302Sopenharmony_ci	FLOCK(f);
689570af302Sopenharmony_ci	olderr = f->flags & F_ERR;
690570af302Sopenharmony_ci	if (f->mode < 1) f->flags &= ~F_ERR;
691570af302Sopenharmony_ci	if (!f->buf_size) {
692570af302Sopenharmony_ci		saved_buf = f->buf;
693570af302Sopenharmony_ci		f->buf = internal_buf;
694570af302Sopenharmony_ci		f->buf_size = sizeof internal_buf;
695570af302Sopenharmony_ci		f->wpos = f->wbase = f->wend = 0;
696570af302Sopenharmony_ci	}
697570af302Sopenharmony_ci	if (!f->wend && __towrite(f)) ret = -1;
698570af302Sopenharmony_ci	else ret = printf_core(f, fmt, &ap2, nl_arg, nl_type);
699570af302Sopenharmony_ci	if (saved_buf) {
700570af302Sopenharmony_ci		f->write(f, 0, 0);
701570af302Sopenharmony_ci		if (!f->wpos) ret = -1;
702570af302Sopenharmony_ci		f->buf = saved_buf;
703570af302Sopenharmony_ci		f->buf_size = 0;
704570af302Sopenharmony_ci		f->wpos = f->wbase = f->wend = 0;
705570af302Sopenharmony_ci	}
706570af302Sopenharmony_ci	if (f->flags & F_ERR) ret = -1;
707570af302Sopenharmony_ci	f->flags |= olderr;
708570af302Sopenharmony_ci	FUNLOCK(f);
709570af302Sopenharmony_ci	va_end(ap2);
710570af302Sopenharmony_ci	return ret;
711570af302Sopenharmony_ci}
712