vfprintf.c revision 49218d4f8e4d84d1c08aeb267bcf6e451f2056dc
/*
* Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1990
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
** Overall:
** Actual printing innards.
** This code is large and complicated...
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "local.h"
#include "fvwrite.h"
static void sm_grow_type_table_x __P((unsigned char **, int *));
/*
**
** Flush out all the vectors defined by the given uio,
** then reset it so that it can be reused.
**
** Parameters:
** fp -- file pointer
** timeout -- time to complete operation (milliseconds)
** uio -- vector list of memory locations of data for printing
**
** Results:
** Success: 0 (zero)
** Failure:
*/
static int
int timeout;
{
register int err;
{
uio->uio_iovcnt = 0;
return 0;
}
uio->uio_iovcnt = 0;
return err;
}
/*
** SM_BPRINTF -- allow formating to an unbuffered file.
**
** Helper function for `fprintf to unbuffered unix file': creates a
** temporary buffer (via a "fake" file pointer).
** We only work on write-only files; this avoids
** worries about ungetc buffers and so forth.
**
** Parameters:
** fp -- the file to send the o/p to
** fmt -- format instructions for the o/p
** ap -- vectors of data units used for formating
**
** Results:
** Failure: SM_IO_EOF and errno set
** Success: number of data units used in the formating
**
** Side effects:
** formatted o/p can be SM_IO_BUFSIZ length maximum
*/
static int
const char *fmt;
{
int ret;
unsigned char buf[SM_IO_BUFSIZ];
extern const char SmFileMagic[];
/* copy the important variables */
/* set up the buffer */
/* do the work, then copy any error status */
return ret;
}
#define BUF 40
/* Macros for converting digits to letters and vice versa */
#define to_digit(c) ((c) - '0')
#define to_char(n) ((char) (n) + '0')
/* Flags used during conversion. */
/*
** SM_IO_VPRINTF -- performs actual formating for o/p
**
** Parameters:
** fp -- file pointer for o/p
** timeout -- time to complete the print
** fmt0 -- formating directives
** ap -- vectors with data units for formating
**
** Results:
** Success: number of data units used for formatting
** Failure: SM_IO_EOF and sets errno
*/
int
int timeout;
const char *fmt0;
{
register char *fmt; /* format string */
register int ch; /* character from fmt */
register int n, m, n2; /* handy integers (short term usage) */
register char *cp; /* handy char pointer (short term usage) */
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) */
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
int realsz; /* field size expanded by dprec */
int size; /* size of converted field or string */
#define NIOV 8
int nextarg; /* 1-based argument index */
/*
** Choose PADSIZE to trade efficiency vs. size. If larger printf
** fields occur frequently, increase PADSIZE and make the initialisers
** below longer.
*/
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
{'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'.
*/
iovp++; \
{ \
goto error; \
} \
} while (0)
{ \
if ((n = (howmany)) > 0) \
{ \
while (n > PADSIZE) { \
n -= PADSIZE; \
} \
} \
} while (0)
#define FLUSH() do \
{ \
goto error; \
uio.uio_iovcnt = 0; \
} while (0)
/*
** To extend shorts properly, we need both signed and unsigned
** argument extraction methods.
*/
#define SARG() \
(long) GETARG(int))
#define UARG() \
(unsigned long) GETARG(unsigned int))
/*
** Get * arguments, including the form *nn$. Preserve the nextarg
** that the argument can be gotten once the type is determined.
*/
n2 = 0; \
{ \
cp++; \
} \
if (*cp == '$') \
{ \
{ \
argtable = statargtable; \
} \
} \
else \
{ \
}
/*
** Get the argument indexed by nextarg. If the argument table is
** built, use it to get the argument. If its not, get the next
** argument (and arguments must be gotten sequentially).
*/
#if SM_VA_STD
#else /* SM_VA_STD */
#endif /* SM_VA_STD */
/* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
{
return SM_IO_EOF;
}
/* optimise fprintf(stderr) (and other unbuffered Unix files) */
nextarg = 1;
uio.uio_iovcnt = 0;
ret = 0;
/* Scan the format for conversions (`%' character). */
for (;;)
{
n = 0;
{
if (wc == '%')
{
n = 1;
break;
}
fmt++;
}
{
ret += m;
}
if (n <= 0)
goto done;
fmt++; /* skip over '%' */
flags = 0;
dprec = 0;
width = 0;
prec = -1;
sign = '\0';
{
case ' ':
/*
** ``If the space and + flags both appear, the space
** flag will be ignored.''
** -- ANSI X3J11
*/
if (!sign)
sign = ' ';
goto rflag;
case '#':
goto rflag;
case '*':
/*
** ``A negative field width argument is taken as a
** - flag followed by a positive field width.''
** -- ANSI X3J11
** They don't exclude field widths read from args.
*/
if (width >= 0)
goto rflag;
/* FALLTHROUGH */
case '-':
goto rflag;
case '+':
sign = '+';
goto rflag;
case '.':
{
GETASTER(n);
prec = n < 0 ? -1 : n;
goto rflag;
}
n = 0;
{
}
if (ch == '$')
{
nextarg = n;
{
&argtable);
}
goto rflag;
}
prec = n < 0 ? -1 : n;
goto reswitch;
case '0':
/*
** ``Note that 0 is taken as a flag, not as the
** beginning of a field width.''
** -- ANSI X3J11
*/
goto rflag;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
do
{
if (ch == '$')
{
nextarg = n;
{
&argtable);
}
goto rflag;
}
width = n;
goto reswitch;
case 'h':
goto rflag;
case 'l':
if (*fmt == 'l')
{
fmt++;
}
else
{
}
goto rflag;
case 'q':
goto rflag;
case 'c':
size = 1;
sign = '\0';
break;
case 'D':
/*FALLTHROUGH*/
case 'd':
case 'i':
if ((LONGLONG_T) _uquad < 0)
{
sign = '-';
}
goto number;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
{
double val;
char *p;
char fmt[16];
char out[150];
/*
** This code implements floating point output
** in the most portable manner possible,
** relying only on 'sprintf' as defined by
** the 1989 ANSI C standard.
** We silently cap width and precision
** at 120, to avoid buffer overflow.
*/
p = fmt;
*p++ = '%';
if (sign)
*p++ = sign;
*p++ = '#';
*p++ = '-';
*p++ = '0';
*p++ = '*';
if (prec >= 0)
{
*p++ = '.';
*p++ = '*';
}
*p++ = ch;
*p = '\0';
if (width > 120)
width = 120;
if (prec > 120)
prec = 120;
if (prec >= 0)
#if HASSNPRINTF
#else /* HASSNPRINTF */
#endif /* HASSNPRINTF */
else
#if HASSNPRINTF
val);
#else /* HASSNPRINTF */
#endif /* HASSNPRINTF */
FLUSH();
continue;
}
case 'n':
else
continue; /* no output */
case 'O':
/*FALLTHROUGH*/
case 'o':
goto nosign;
case 'p':
/*
** ``The argument shall be a pointer to void. The
** value of the pointer is converted to a sequence
** of printable characters, in an implementation-
** defined manner.''
** -- ANSI X3J11
*/
/* NOSTRICT */
{
union
{
void *p;
unsigned long l;
unsigned i;
} u;
u.p = GETARG(void *);
if (sizeof(void *) == sizeof(ULONGLONG_T))
else if (sizeof(void *) == sizeof(long))
_uquad = u.l;
else
_uquad = u.i;
}
xdigs = "0123456789abcdef";
ch = 'x';
goto nosign;
case 's':
cp = "(null)";
if (prec >= 0)
{
/*
** can't use strlen; can only look for the
** NUL in the first `prec' characters, and
** strlen() will go further.
*/
if (p != NULL)
{
}
else
}
else
sign = '\0';
break;
case 'U':
/*FALLTHROUGH*/
case 'u':
goto nosign;
case 'X':
xdigs = "0123456789ABCDEF";
goto hex;
case 'x':
xdigs = "0123456789abcdef";
/* leading 0x/X only if non-zero */
/* unsigned conversions */
/*
** ``... diouXx conversions ... if a precision is
** specified, the 0 flag will be ignored.''
** -- ANSI X3J11
*/
/*
** ``The result of converting a zero value with an
** explicit precision of zero is no characters.''
** -- ANSI X3J11
*/
{
/*
** Unsigned mod is hard, and unsigned mod
** by a constant is easier than that by
** a variable; hence this switch.
*/
switch (base)
{
case OCT:
do
{
_uquad >>= 3;
} while (_uquad);
/* handle octal leading 0 */
*--cp = '0';
break;
case DEC:
/* many numbers are 1 digit */
while (_uquad >= 10)
{
_uquad /= 10;
}
break;
case HEX:
do
{
_uquad >>= 4;
} while (_uquad);
break;
default:
cp = "bug in sm_io_vfprintf: bad base";
goto skipsize;
}
}
break;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0')
goto done;
/* pretend it was %c with argument ch */
size = 1;
sign = '\0';
break;
}
/*
** 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.
** size excludes decimal prec; realsz includes it.
*/
if (sign)
realsz++;
realsz+= 2;
/* right-adjusting blank padding */
/* prefix */
if (sign)
{
}
{
ox[0] = '0';
}
/* right-adjusting zero padding */
/* leading zeroes from decimal precision */
/* the string or number proper */
/* left-adjusting padding (always blank) */
/* finally, adjust ret */
FLUSH(); /* copy out the I/O vectors */
}
done:
FLUSH();
/* NOTREACHED */
}
/* Type ids for argument type table. */
#define T_UNUSED 0
#define T_SHORT 1
#define T_U_SHORT 2
#define TP_SHORT 3
#define T_INT 4
#define T_U_INT 5
#define TP_INT 6
#define T_LONG 7
#define T_U_LONG 8
#define TP_LONG 9
#define T_QUAD 10
#define T_U_QUAD 11
#define TP_QUAD 12
#define T_DOUBLE 13
#define TP_CHAR 15
#define TP_VOID 16
/*
** SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
**
** Find all arguments when a positional parameter is encountered. Returns a
** table, indexed by argument number, of pointers to each arguments. The
** initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
** It will be replaced with a malloc-ed one if it overflows.
**
** Parameters:
** fmt0 -- formating directives
** ap -- vector list of data unit for formating consumption
** argtable -- an indexable table (returned) of 'ap'
**
** Results:
** none.
*/
static void
const char *fmt0;
{
register char *fmt; /* format string */
register int ch; /* character from fmt */
register int n, n2; /* handy integer (short term usage) */
register char *cp; /* handy char pointer (short term usage) */
register int flags; /* flags as above */
unsigned char *typetable; /* table of types */
unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
int tablesize; /* current size of type table */
int tablemax; /* largest used index in table */
int nextarg; /* 1-based argument index */
/* Add an argument type to the table, expanding if necessary. */
#define ADDSARG() \
#define ADDUARG() \
/* Add * arguments to the type array. */
#define ADDASTER() \
n2 = 0; \
{ \
cp++; \
} \
if (*cp == '$') \
{ \
} \
else \
{ \
}
tablemax = 0;
nextarg = 1;
/* Scan the format for conversions (`%' character). */
for (;;)
{
/* void */;
if (ch == '\0')
goto done;
fmt++; /* skip over '%' */
flags = 0;
{
case ' ':
case '#':
goto rflag;
case '*':
ADDASTER();
goto rflag;
case '-':
case '+':
goto rflag;
case '.':
{
ADDASTER();
goto rflag;
}
{
}
goto reswitch;
case '0':
goto rflag;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
do
{
if (ch == '$')
{
nextarg = n;
goto rflag;
}
goto reswitch;
case 'h':
goto rflag;
case 'l':
goto rflag;
case 'q':
goto rflag;
case 'c':
break;
case 'D':
/*FALLTHROUGH*/
case 'd':
case 'i':
{
}
else
{
ADDSARG();
}
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
break;
case 'n':
else
continue; /* no output */
case 'O':
/*FALLTHROUGH*/
case 'o':
else
ADDUARG();
break;
case 'p':
break;
case 's':
break;
case 'U':
/*FALLTHROUGH*/
case 'u':
else
ADDUARG();
break;
case 'X':
case 'x':
else
ADDUARG();
break;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0')
goto done;
break;
}
}
done:
/* Build the argument table. */
if (tablemax >= STATIC_ARG_TBL_SIZE)
{
}
for (n = 1; n <= tablemax; n++)
{
switch (typetable [n])
{
case T_UNUSED:
break;
case T_SHORT:
break;
case T_U_SHORT:
break;
case TP_SHORT:
break;
case T_INT:
break;
case T_U_INT:
break;
case TP_INT:
break;
case T_LONG:
break;
case T_U_LONG:
break;
case TP_LONG:
break;
case T_QUAD:
break;
case T_U_QUAD:
break;
case TP_QUAD:
break;
case T_DOUBLE:
break;
case TP_CHAR:
break;
case TP_VOID:
break;
}
}
}
/*
** SM_GROW_TYPE_TABLE -- Increase the size of the type table.
**
** Parameters:
** tabletype -- type of table to grow
** tablesize -- requested new table size
**
** Results:
** Raises an exception if can't allocate memory.
*/
static void
unsigned char **typetable;
int *tablesize;
{
if (*tablesize == STATIC_ARG_TBL_SIZE)
{
*typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
* newsize);
}
else
{
sizeof(unsigned char) * newsize);
}
}