* 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 = "from: @(#)vfprintf.c 5.50 (Berkeley) 12/16/92";*/
static char *rcsid
= "$Id: vfprintf.c,v 1.15 1994/01/27 06:12:37 proven Exp $";
#endif /* LIBC_SCCS and not lint */
* This code is large and complicated...
/* Define FLOATING_POINT to get floating point. */
* 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
._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 */
static char *cvt
__P((double, int, int, char *, int *, int, int *));
static int exponent
__P((char *, int, int));
#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 ALT 0x001 /* alternate form */
#define HEXPREFIX 0x002 /* add 0x or 0X prefix */
#define LADJUST 0x004 /* left adjustment */
#define LONGDBL 0x008 /* long double; unimplemented */
#define LONGINT 0x010 /* long integer */
#define QUADINT 0x020 /* quad integer */
#define SHORTINT 0x040 /* short integer */
#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
#define FPT 0x100 /* Floating point number */
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 expt
; /* integer value of exponent */
int expsize
; /* character count for expstr */
int ndig
; /* actual number of digits returned by cvt */
char expstr
[7]; /* buffer for exponent string */
#ifdef __GNUC__ /* gcc has builtin quad type (long long) SOS */
#define u_quad_t unsigned long long
u_quad_t _uquad
; /* 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
#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&QUADINT ? va_arg(ap, quad_t) : \
flags&LONGINT ? va_arg(ap, long) : \
flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
(flags&QUADINT ? va_arg(ap, u_quad_t) : \
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
) &&
ret
= (__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);
if ((quad_t
)_uquad
< 0) {
} else if ((ch
== 'g' || ch
== 'G') && prec
== 0) {
_double
= (double) va_arg(ap
, long double);
_double
= va_arg(ap
, double);
/* do this before tricky precision changes */
cp
= cvt(_double
, prec
, flags
, &softsign
,
if (ch
== 'g' || ch
== 'G') {
if (expt
<= -4 || expt
> prec
)
ch
= (ch
== 'g') ? 'e' : 'E';
if (ch
<= 'e') { /* 'e' or 'E' fmt */
expsize
= exponent(expstr
, expt
, ch
);
if (ndig
> 1 || flags
& ALT
)
} else if (ch
== 'f') { /* f fmt */
} else if (expt
>= ndig
) { /* fixed g fmt */
size
= ndig
+ (expt
> 0 ?
#endif /* FLOATING_POINT */
*va_arg(ap
, quad_t
*) = ret
;
else if (flags
& LONGINT
)
*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-
_uquad
= (u_quad_t
)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
&& _uquad
!= 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 (_uquad
!= 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(_uquad
& 7);
/* handle octal leading 0 */
if (flags
& ALT
&& *cp
!= '0')
/* many numbers are 1 digit */
*--cp
= to_char(_uquad
% 10);
*--cp
= xdigs
[_uquad
& 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, pad with blanks.
* 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 */
if ((flags
& FPT
) == 0) {
} else { /* glue together f_p fragments */
if (ch
>= 'f') { /* 'f' or 'g' */
/* kludge for __dtoa irregularity */
if (expt
>= ndig
&& (flags
& ALT
) == 0) {
} else if (expt
>= ndig
) {
PAD(expt
- ndig
, zeroes
);
} else { /* 'e' or 'E' */
if (ndig
> 1 || flags
& ALT
) {
if (_double
|| flags
& ALT
== 0) {
/* __dtoa irregularity */
/* left-adjusting padding (always blank) */
PAD(width
- realsz
, blanks
);
/* finally, adjust ret */
ret
+= width
> realsz
? width
: realsz
;
FLUSH(); /* copy out the I/O vectors */
extern char *__dtoa
__P((double, int, int, int *, int *, char **));
cvt(value
, ndigits
, flags
, sign
, decpt
, ch
, length
)
int ndigits
, flags
, *decpt
, ch
, *length
;
mode
= 3; /* ndigits after the decimal point */
/* To obtain ndigits after the decimal point for the 'e'
* and 'E' formats, round to ndigits + 1 significant
if (ch
== 'e' || ch
== 'E') {
mode
= 2; /* ndigits significant digits */
digits
= __dtoa(value
, mode
, ndigits
, decpt
, &dsgn
, &rve
);
if ((ch
!= 'g' && ch
!= 'G') || flags
& ALT
) { /* Print trailing zeros */
if (*digits
== '0' && value
)
if (value
== 0) /* kludge for __dtoa irregularity */
*--t
= to_char(exp
% 10);
} while ((exp
/= 10) > 9);
for (; t
< expbuf
+ MAXEXP
; *p
++ = *t
++);
#endif /* FLOATING_POINT */