* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)vfprintf.c 5.47 (Berkeley) 3/22/91";
#endif /* LIBC_SCCS and not lint */
* This code is large and complicated...
* Define FLOATING_POINT to get floating point.
* Define CSH to get a csh-specific version (grr).
/* end of configuration stuff */
* C shell hacks. Ick, gag.
printf(const char *fmt
, ...) {
ret
= vfprintf(&f
, fmt
, ap
);
return (vfprintf(&f
, fmt
, &args
));
register struct __suio
*uio
;
register int n
, ch
, iovcnt
;
register struct __siov
*iov
;
/* must allow sprintf to work, might as well allow others too */
if (fp
->_write
|| fp
->_flags
& __SSTR
) {
if (uio
->uio_resid
== 0) {
for (iovcnt
= uio
->uio_iovcnt
; --iovcnt
>= 0; iov
++) {
for (p
= iov
->iov_base
, n
= iov
->iov_len
; --n
>= 0;) {
CSHPUTCHAR
; /* this horrid macro uses `ch' */
* Flush out all the vectors defined by the given uio,
* then reset it so that it can be reused.
register struct __suio
*uio
;
if (uio
->uio_resid
== 0) {
err
= __sfvwrite(fp
, uio
);
* Helper function for `fprintf to unbuffered unix file': creates a
* temporary buffer. We only work on write-only files; this avoids
* worries about ungetc buffers and so forth.
unsigned char buf
[BUFSIZ
];
/* copy the important variables */
fake
._flags
= fp
->_flags
& ~__SNBF
;
fake
._cookie
= fp
->_cookie
;
fake
._write
= fp
->_write
;
fake
._bf
._base
= fake
._p
= buf
;
fake
._bf
._size
= fake
._w
= sizeof(buf
);
fake
._lbfsize
= 0; /* not actually used, but Just In Case */
/* do the work, then copy any error status */
ret
= vfprintf(&fake
, fmt
, ap
);
if (ret
>= 0 && fflush(&fake
))
if (fake
._flags
& __SERR
)
#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
#else /* no FLOATING_POINT */
#endif /* FLOATING_POINT */
* Macros for converting digits to letters and vice versa
#define to_digit(c) ((c) - '0')
#define is_digit(c) ((unsigned)to_digit(c) <= 9)
#define to_char(n) ((n) + '0')
* Flags used during conversion.
#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 /* technically illegal, since we do not know what type va_list is */
register char *fmt
; /* format string */
register int ch
; /* character from fmt */
register int n
; /* handy integer (short term usage) */
register char *cp
; /* handy char pointer (short term usage) */
register struct __siov
*iovp
;/* for PRINT macro */
register int flags
; /* flags as above */
int ret
; /* return value accumulator */
int width
; /* width from format (%8d), or 0 */
int prec
; /* precision from format (%.3d), or -1 */
char sign
; /* sign prefix (' ', '+', '-', or \0) */
char softsign
; /* temporary negative sign for floats */
double _double
; /* double precision arguments %[eEfgG] */
int fpprec
; /* `extra' floating precision in [eEfgG] */
u_long _ulong
; /* integer arguments %[diouxX] */
enum { OCT
, DEC
, HEX
} base
;/* base for [diouxX] conversion */
int dprec
; /* a copy of prec if [diouxX], 0 otherwise */
int fieldsz
; /* field size expanded by sign, etc */
int realsz
; /* field size expanded by dprec */
int size
; /* size of converted field or string */
char *xdigs
; /* digits for [xX] conversion */
struct __suio uio
; /* output information: summary */
struct __siov iov
[NIOV
];/* ... and individual io vectors */
char buf
[BUF
]; /* space for %c, %[diouxX], %[eEfgG] */
char ox
[2]; /* space for 0x hex-prefix */
* Choose PADSIZE to trade efficiency vs size. If larger
* printf fields occur frequently, increase PADSIZE (and make
* the initialisers below longer).
#define PADSIZE 16 /* pad chunk size */
static char blanks
[PADSIZE
] =
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
static char zeroes
[PADSIZE
] =
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
* BEWARE, these `goto error' on error, and PAD uses `n'.
#define PRINT(ptr, len) { \
iovp->iov_base = (ptr); \
uio.uio_resid += (len); \
if (++uio.uio_iovcnt >= NIOV) { \
if (__sprint(fp, &uio)) \
#define PAD(howmany, with) { \
if ((n = (howmany)) > 0) { \
if (uio.uio_resid && __sprint(fp, &uio)) \
* To extend shorts properly, we need both signed and unsigned
* argument extraction methods.
(flags&LONGINT ? va_arg(ap, long) : \
flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
(flags&LONGINT ? va_arg(ap, u_long) : \
flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
(u_long)va_arg(ap, u_int))
/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
/* optimise fprintf(stderr) (and other unbuffered Unix files) */
if ((fp
->_flags
& (__SNBF
|__SWR
|__SRW
)) == (__SNBF
|__SWR
) &&
return (__sbprintf(fp
, fmt0
, ap
));
uio
.uio_iov
= iovp
= iov
;
* Scan the format for conversions (`%' character).
for (cp
= fmt
; (ch
= *fmt
) != '\0' && ch
!= '%'; fmt
++)
if ((n
= fmt
- cp
) != 0) {
fmt
++; /* skip over '%' */
* ``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(ap
, int)) >= 0)
if ((ch
= *fmt
++) == '*') {
n
= 10 * n
+ to_digit(ch
);
* ``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
+ to_digit(ch
);
*(cp
= buf
) = va_arg(ap
, int);
_double
= va_arg(ap
, double);
/* do this before tricky precision changes */
* don't do unrealistic precision; just pad it with
* zeroes later, so buffer size stays rational.
if (ch
!= 'g' && ch
!= 'G' || (flags
&ALT
))
fpprec
= prec
- MAXFRACT
;
* cvt may have to round up before the "start" of
* its buffer, i.e. ``intf("%.2f", (double)9.999);'';
* if the first character is still NUL, it did.
* softsign avoids negative 0 if _double < 0 but
* no significant digits will be shown.
size
= cvt(_double
, prec
, flags
, &softsign
, ch
,
#endif /* FLOATING_POINT */
*va_arg(ap
, long *) = ret
;
else if (flags
& SHORTINT
)
*va_arg(ap
, short *) = ret
;
*va_arg(ap
, int *) = ret
;
continue; /* no output */
* ``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(ap
, void *);
xdigs
= "0123456789abcdef";
if ((cp
= va_arg(ap
, char *)) == NULL
)
* can't use strlen; can only look for the
* NUL in the first `prec' characters, and
* strlen() will go further.
char *p
= memchr(cp
, 0, prec
);
xdigs
= "0123456789ABCDEF";
xdigs
= "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) {
* unsigned mod is hard, and unsigned mod
* by a constant is easier than that by
* a variable; hence this switch.
*--cp
= to_char(_ulong
& 7);
/* handle octal leading 0 */
if (flags
& ALT
&& *cp
!= '0')
/* many numbers are 1 digit */
*--cp
= to_char(_ulong
% 10);
*--cp
= xdigs
[_ulong
& 15];
cp
= "bug in vfprintf: bad base";
default: /* "%?" prints ?, unless ? is NUL */
/* pretend it was %c with argument ch */
* All reasonable formats wind up here. At this point,
* `cp' 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
else if (flags
& HEXPREFIX
)
realsz
= dprec
> fieldsz
? dprec
: fieldsz
;
/* right-adjusting blank padding */
if ((flags
& (LADJUST
|ZEROPAD
)) == 0)
PAD(width
- realsz
, blanks
);
} else if (flags
& HEXPREFIX
) {
/* right-adjusting zero padding */
if ((flags
& (LADJUST
|ZEROPAD
)) == ZEROPAD
)
PAD(width
- realsz
, zeroes
);
/* leading zeroes from decimal precision */
PAD(dprec
- fieldsz
, zeroes
);
/* the string or number proper */
/* trailing f.p. zeroes */
/* left-adjusting padding (always blank) */
PAD(width
- realsz
, blanks
);
/* finally, adjust ret */
ret
+= width
> realsz
? width
: realsz
;
FLUSH(); /* copy out the I/O vectors */
return (__sferror(fp
) ? EOF
: ret
);
cvt(number
, prec
, flags
, signp
, fmtch
, startp
, endp
)
int dotrim
, expcnt
, gformat
;
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
-- = to_char((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
);
*t
++ = to_char((int)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
);
*t
++ = to_char((int)tmp
);
/* if requires more precision and some fraction left */
fract
= modf(fract
* 10, &tmp
);
*t
++ = to_char((int)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
);
*t
++ = to_char((int)tmp
);
while (--prec
&& fract
) {
fract
= modf(fract
* 10, &tmp
);
*t
++ = to_char((int)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. */
*--t
= to_char(exp
% 10);
} while ((exp
/= 10) > 9);
for (; t
< expbuf
+ MAXEXP
; *p
++ = *t
++);
#endif /* FLOATING_POINT */