sfvprintf.c revision 3e14f97f673e8a630f076077de35afdd43dc1587
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/***********************************************************************
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* This software is part of the ast package *
3e14f97f673e8a630f076077de35afdd43dc1587Roger A. Faulkner* Copyright (c) 1985-2010 AT&T Intellectual Property *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* and is licensed under the *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* Common Public License, Version 1.0 *
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin* by AT&T Intellectual Property *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* A copy of the License is available at *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* Information and Software Systems Research *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* AT&T Research *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* Florham Park NJ *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* Glenn Fowler <gsf@research.att.com> *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* David Korn <dgk@research.att.com> *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin* Phong Vo <kpv@research.att.com> *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin***********************************************************************/
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/* The engine for formatting data.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** 1. Argument positioning is done in sftable.c so any changes
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** made here should be reflected in sftable.c as well.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** 2. For internationalization, Sfio only supports I/O of multibyte strings.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** However, this code does provide minimal support so that Stdio functions
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** such as fwprintf/swprintf can be emulated (see stdvwprintf()).
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** Written by Kiem-Phong Vo.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SFFMT_PREFIX (SFFMT_MINUS|SFFMT_SIGN|SFFMT_BLANK)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/* characters when using ebcdic or ascii */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#endif /* _chr_ebcdic */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#endif /* _PACKAGE_ast */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/* On some platform(s), large functions are not compilable.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** In such a case, the below macro should be defined non-zero so that
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin** some in-lined macros will be made smaller, trading time for space.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinint sfvprintf(Sfio_t* f, const char* form, va_list args)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin char buf[SF_MAXDIGITS+SLACK], tmp[SF_MAXDIGITS+1], data[SF_GRAIN];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* local io system */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SMputc(f,c) { if((o = SFFLSBUF(f,c)) >= 0 ) n_output += 1; \
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SMnputc(f,c,n) { if((o = SFNPUTC(f,c,n)) > 0 ) n_output += 1; \
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SMwrite(f,s,n) { if((o = SFWRITE(f,(Void_t*)s,n)) > 0 ) n_output += o; \
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#if _sffmt_small /* these macros are made smaller at some performance cost */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SFEND(f) ((n_output += d - f->next), (f->next = d))
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SFputc(f,c) { if(d < endd) { *d++ = (uchar)c; } \
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SFnputc(f,c,n) { if(d+n <= endd) { while(n--) *d++ = (uchar)(c); } \
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SFwrite(f,s,n) { if(d+n <= endd) { while(n--) *d++ = (uchar)(*s++); } \
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#endif /* _sffmt_small */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* make sure stream is in write mode and buffer is not NULL */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin SFMBCLR(&fmbs); /* clear multibyte states to parse the format string */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin while((n = *form) )
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin loop_flags: /* LOOP FOR \0, %, FLAGS, WIDTH, PRECISION, BASE, TYPE */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '\0':
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '%' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case LEFTP : /* get the type enclosed in balanced parens */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin for(v = 1;;)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { switch(*form++)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 0 : /* not balancable, retract */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((v -= 1) != 0)
3e14f97f673e8a630f076077de35afdd43dc1587Roger A. Faulkner !(fp = (*_Sffmtposf)(f,oform,oargs,ft,0)) )
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin LEFTP, 0, 0, 0,0,0,
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin NIL(char*),0);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '-' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin flags = (flags & ~(SFFMT_CENTER|SFFMT_ZERO)) | SFFMT_LEFT;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '0' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case ' ' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '+' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '=' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin flags = (flags & ~(SFFMT_LEFT|SFFMT_ZERO)) | SFFMT_CENTER;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '#' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '.' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* so base can be defined without setting precis */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case '*' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { FMTSET(ft, form,args, '.',dot, 0, 0,0,0, NIL(char*), 0);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin flags = (flags&~SFFMT_TYPES) | (ft->flags&SFFMT_TYPES);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if((width = v) < 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin flags = (flags & ~(SFFMT_CENTER|SFFMT_ZERO)) | SFFMT_LEFT;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = -1; flags = (flags & ~SFFMT_TYPES) | SFFMT_IFLAG;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin NIL(char*), 0);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 'l' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 'h' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 'L' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = -1; flags = (flags & ~SFFMT_TYPES) | SFFMT_LDOUBLE;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 'j' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 'z' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 't' :
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* set object size for scalars */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if((_Sftype[fmt]&(SFFMT_INT|SFFMT_UINT)) || fmt == 'n')
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(long);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(short);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(char);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(size <= 0 ||
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(size < 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(int);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(double);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(size <= 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(size < 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(float);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin sizeof(wchar_t) : sizeof(int);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin size = sizeof(int);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(ft && ft->extf && fp[argp].ft.fmt != fp[argp].fmt)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { FMTSET(ft, form,args, fmt, size,flags, width,precis,base,
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(v < 0) /* no further processing */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(v > 0) /* extf output v bytes */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else /* extf did not output */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { FMTGET(ft, form,args, fmt, size,flags, width,precis,base);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(size == sizeof(short))
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(size == sizeof(char))
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(size == sizeof(float) )
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(size == sizeof(long) )
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin default: /* unknown pattern */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin default : /* unknown directive */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(!argv.ft->form && ft ) /* change extension functions */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else /* stack a new environment */
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin flags = (flags & ~(SFFMT_TYPES|SFFMT_LDOUBLE)) | SFFMT_LONG;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* v: number of bytes w: print width of those v bytes */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if((v = size) < 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin for(v = 0; sp[v]; ++v)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { n -= (k = n/2);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(base > 0)
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin flags = (flags & ~(SFFMT_TYPES|SFFMT_LDOUBLE)) | SFFMT_LONG;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(base >= 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if(base >= 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin while(size > 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { n -= (k = n/2);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin v = precis; /* need this because SFnputc may clear it */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { for(; v > 0; --v)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { for(; v > 0; --v)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(size == sizeof(long))
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(size == sizeof(short) )
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin flags = (flags&~(SFFMT_SIGN|SFFMT_BLANK|SFFMT_ZERO))|SFFMT_ALTER;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#if !_ast_intmax_long || _more_long_int || _more_void_int
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(sizeof(long) < sizeof(Sflong_t) && size == sizeof(long))
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else /* general base */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(sizeof(short) < sizeof(int) && size == sizeof(short) )
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin v = (int)((short)argv.i);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(size == sizeof(char))
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin v = (int)((signed char)argv.i);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin v = -((int)((char)(-argv.i)));
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else v = ((int)((char)( argv.i)));
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(v == 0 && precis == 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else v = -v;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin } while((v = ((uint)v) >> n) );
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else /* n_s == 0, general base */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if(n_s < 0 && (flags&SFFMT_THOUSAND) && (n = endsp-sp) > 3)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { if((n %= 3) == 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* zero padding for precision if have room in buffer */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin while(precis-- > 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* do 0 padding first */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin while(n-- > 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* base#value notation */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin case 'g': case 'G': /* these ultimately become %e or %f */
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin ep = _sfcvt(valp,tmp+1,sizeof(tmp)-1, min(n,SF_FDIGITS),
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(fmt == 'f' || fmt == 'F' && (v |= SFFMT_UPPER))
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin ep = _sfcvt(valp,tmp+1,sizeof(tmp)-1, min(precis,SF_FDIGITS),
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(fmt == 'a' || fmt == 'A' && (v |= SFFMT_UPPER))
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin ep = _sfcvt(valp,tmp+1,sizeof(tmp)-1, min(n,SF_FDIGITS),
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else /* 'g' or 'G' format */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { precis = precis < 0 ? FPRECIS : precis == 0 ? 1 : precis;
7c2fbfb345896881c631598ee3852ce9ce33fb07April Chin ep = _sfcvt(valp,tmp+1,sizeof(tmp)-1, min(precis,SF_FDIGITS),
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* zap trailing 0s */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* build the exponent */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin while(n > 9)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { v = n; n /= 10;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else n = 0;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* the e/Exponent separator and sign */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((n = -decpt) > 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* output zeros for negative exponent */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin fmt = (flags&SFFMT_MINUS) ? '-' : (flags&SFFMT_SIGN) ? '+' : ' ';
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin n = (endsp-sp) + (endep-ep) + (precis <= 0 ? 0 : precis) +
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((v = width-n) <= 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if(flags&SFFMT_PREFIX) /* blank padding, output prefix now */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((n = v) > 0) /* left padding */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* padding for integer precision */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin { /* SFFMT_FLOAT: right padding for float precision */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((n = precis) > 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* SFFMT_FLOAT: the exponent of %eE */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* SFFMT_LEFT: right padding */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((n = -v) > 0)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin while((fm = fmstk) ) /* pop the format stack and continue */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if((((flags = f->flags)&SF_SHARE) && !(flags&SF_PUBLIC) ) ||
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else f->next += n;