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