* Copyright (c) 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)vfprintf.c 5.35 (Berkeley) %G%";
#endif /* LIBC_SCCS and not lint */
/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
#define PUTC(ch) (void) putc(ch, fp)
_ulong = flags&LONGINT ? va_arg(argp, long) : \
flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
#define todigit(c) ((c) - '0')
#define tochar(n) ((n) + '0')
/* have to deal with the negative buffer count kludge */
#define NEGATIVE_COUNT_KLUDGE
#define LONGINT 0x01 /* long integer */
#define LONGDBL 0x02 /* long double; unimplemented */
#define SHORTINT 0x04 /* short integer */
#define ALT 0x08 /* alternate form */
#define LADJUST 0x10 /* left adjustment */
#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */
#define HEXPREFIX 0x40 /* add 0x or 0X prefix */
register u_char
*fmt
; /* format string */
register int ch
; /* character from fmt */
register int cnt
; /* return value accumulator */
register int n
; /* random handy integer */
register char *t
; /* buffer pointer */
double _double
; /* double precision arguments %[eEfgG] */
u_long _ulong
; /* integer arguments %[diouxX] */
int base
; /* base for [diouxX] conversion */
int dprec
; /* decimal precision in [diouxX] */
int fieldsz
; /* field size expanded by sign, etc */
int flags
; /* flags as above */
int fpprec
; /* `extra' floating precision in [eEfgG] */
int prec
; /* precision from format (%.3d), or -1 */
int realsz
; /* field size expanded by decimal precision */
int size
; /* size of converted field or string */
int width
; /* width from format (%8d), or 0 */
char sign
; /* sign prefix (' ', '+', '-', or \0) */
char softsign
; /* temporary negative sign for floats */
char *digs
; /* digits for [diouxX] conversion */
char buf
[BUF
]; /* space for %c, %[diouxX], %[eEfgG] */
fp
->_flag
&= ~(_IOEOF
|_IOREAD
);
if ((fp
->_flag
& _IOWRT
) == 0)
digs
= "0123456789abcdef";
for (t
= (char *)fp
->_ptr
; (ch
= *fmt
) && ch
!= '%';
#ifdef NEGATIVE_COUNT_KLUDGE
&& (!(fp
->_flag
& _IOLBF
) || -n
>= fp
->_bufsiz
)
|| ch
== '\n' && fp
->_flag
& _IOLBF
) {
(void) _flsbuf((u_char
)ch
, fp
);
flags
= 0; dprec
= 0; fpprec
= 0; width
= 0;
* ``If the space and + flags both appear, the space
* flag will be ignored.''
* ``A negative field width argument is taken as a
* - flag followed by a positive field width.''
* They don't exclude field widths read from args.
if ((width
= va_arg(argp
, int)) >= 0)
while (isascii(*fmt
) && isdigit(*fmt
))
n
= 10 * n
+ todigit(*fmt
++);
* ``Note that 0 is taken as a flag, not as the
* beginning of a field width.''
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n
= 10 * n
+ todigit(*fmt
);
} while (isascii(*++fmt
) && isdigit(*fmt
));
*(t
= buf
) = va_arg(argp
, int);
_double
= va_arg(argp
, double);
* don't do unrealistic precision; just pad it with
* zeroes later, so buffer size stays rational.
if (*fmt
!= 'g' && *fmt
!= 'G' || (flags
&ALT
))
fpprec
= prec
- MAXFRACT
;
* softsign avoids negative 0 if _double is < 0 and
* no significant digits will be shown
* cvt may have to round up past the "start" of the
* buffer, i.e. ``intf("%.2f", (double)9.999);'';
* if the first char isn't NULL, it did.
size
= cvt(_double
, prec
, flags
, &softsign
, *fmt
, buf
,
t
= *buf
? buf
: buf
+ 1;
*va_arg(argp
, long *) = cnt
;
else if (flags
& SHORTINT
)
*va_arg(argp
, short *) = cnt
;
*va_arg(argp
, int *) = cnt
;
* ``The argument shall be a pointer to void. The
* value of the pointer is converted to a sequence
* of printable characters, in an implementation-
_ulong
= (u_long
)va_arg(argp
, void *);
if (!(t
= va_arg(argp
, char *)))
* can't use strlen; can only look for the
* NUL in the first `prec' characters, and
* strlen() will go further.
if (p
= memchr(t
, 0, prec
)) {
digs
= "0123456789ABCDEF";
/* leading 0x/X only if non-zero */
if (flags
& ALT
&& _ulong
!= 0)
/* unsigned conversions */
* ``... diouXx conversions ... if a precision is
* specified, the 0 flag will be ignored.''
number
: if ((dprec
= prec
) >= 0)
* ``The result of converting a zero value with an
* explicit precision of zero is no characters.''
if (_ulong
!= 0 || prec
!= 0) {
*--t
= digs
[_ulong
% base
];
digs
= "0123456789abcdef";
if (flags
& ALT
&& base
== 8 && *t
!= '0')
*--t
= '0'; /* octal leading 0 */
* All reasonable formats wind up here. At this point,
* `t' points to a string which (if not flags&LADJUST)
* should be padded out to `width' places. If
* flags&ZEROPAD, it should first be prefixed by any
* sign or other prefix; otherwise, it should be blank
* padded before the prefix is emitted. After any
* left-hand padding and prefixing, emit zeroes
* required by a decimal [diouxX] precision, then print
* the string proper, then emit zeroes required by any
* leftover floating precision; finally, if LADJUST,
* compute actual size, so we know how much to pad
* fieldsz excludes decimal prec; realsz includes it
realsz
= dprec
> fieldsz
? dprec
: fieldsz
;
/* right-adjusting blank padding */
if ((flags
& (LADJUST
|ZEROPAD
)) == 0 && width
)
for (n
= realsz
; n
< width
; n
++)
/* right-adjusting zero padding */
if ((flags
& (LADJUST
|ZEROPAD
)) == ZEROPAD
)
for (n
= realsz
; n
< width
; n
++)
/* leading zeroes from decimal precision */
for (n
= fieldsz
; n
< dprec
; n
++)
/* the string or number proper */
if (fp
->_cnt
- (n
= size
) >= 0 &&
(fp
->_flag
& _IOLBF
) == 0) {
bcopy(t
, (char *)fp
->_ptr
, n
);
/* trailing f.p. zeroes */
/* left-adjusting padding (always blank) */
for (n
= realsz
; n
< width
; n
++)
/* finally, adjust cnt */
cnt
+= width
> realsz
? width
: realsz
;
case '\0': /* "%?" prints ?, unless ? is NULL */
cvt(number
, prec
, flags
, signp
, fmtch
, startp
, endp
)
char *signp
, *startp
, *endp
;
int dotrim
, expcnt
, gformat
;
double integer
, tmp
, modf();
char *exponent(), *round();
dotrim
= expcnt
= gformat
= 0;
fract
= modf(number
, &integer
);
/* get an extra slot for rounding. */
* get integer portion of number; put into the end of the buffer; the
* .01 is added for modf(356.0 / 10, &integer) returning .59999999...
for (p
= endp
- 1; integer
; ++expcnt
) {
tmp
= modf(integer
/ 10, &integer
);
*p
-- = tochar((int)((tmp
+ .01) * 10));
/* reverse integer into beginning of buffer */
for (; ++p
< endp
; *t
++ = *p
);
* if precision required or alternate flag set, add in a
/* if requires more precision and some fraction left */
fract
= modf(fract
* 10, &tmp
);
} while (--prec
&& fract
);
startp
= round(fract
, (int *)NULL
, startp
,
for (; prec
--; *t
++ = '0');
/* if requires more precision and some integer left */
for (; prec
&& ++p
< endp
; --prec
)
* if done precision and more of the integer component,
* round using it; adjust fract so we don't re-round
if (!prec
&& ++p
< endp
) {
startp
= round((double)0, &expcnt
, startp
,
/* adjust expcnt for digit in front of decimal */
/* until first fractional digit, decrement exponent */
/* adjust expcnt for digit in front of decimal */
for (expcnt
= -1;; --expcnt
) {
fract
= modf(fract
* 10, &tmp
);
/* if requires more precision and some fraction left */
fract
= modf(fract
* 10, &tmp
);
} while (--prec
&& fract
);
startp
= round(fract
, &expcnt
, startp
,
/* if requires more precision */
for (; prec
--; *t
++ = '0');
/* unless alternate flag, trim any g/G format trailing 0's */
if (gformat
&& !(flags
&ALT
)) {
while (t
> startp
&& *--t
== '0');
t
= exponent(t
, expcnt
, fmtch
);
/* a precision of 0 is treated as a precision of 1. */
* ``The style used depends on the value converted; style e
* will be used only if the exponent resulting from the
* conversion is less than -4 or greater than the precision.''
if (expcnt
> prec
|| !expcnt
&& fract
&& fract
< .0001) {
* g/G format counts "significant digits, not digits of
* precision; for the e/E format, this just causes an
* off-by-one problem, i.e. g/G considers the digit
* before the decimal point significant and e/E doesn't
fmtch
-= 2; /* G->E, g->e */
* reverse integer into beginning of buffer,
* note, decrement precision
for (; ++p
< endp
; *t
++ = *p
, --prec
);
* if precision required or alternate flag set, add in a
* decimal point. If no digits yet, add in leading 0.
/* if requires more precision and some fraction left */
fract
= modf(fract
* 10, &tmp
);
while (--prec
&& fract
) {
fract
= modf(fract
* 10, &tmp
);
startp
= round(fract
, (int *)NULL
, startp
,
/* alternate format, adds 0's for precision, else trim 0's */
for (; prec
--; *t
++ = '0');
while (t
> startp
&& *--t
== '0');
round(fract
, exp
, start
, end
, ch
, signp
)
register char *start
, *end
;
(void)modf(fract
* 10, &tmp
);
if (exp
) { /* e/E; increment exponent */
else { /* f; add extra digit */
/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
} while ((exp
/= 10) > 9);
for (; t
< expbuf
+ MAXEXP
; *p
++ = *t
++);