summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libsm/vfprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/libsm/vfprintf.c')
-rw-r--r--contrib/sendmail/libsm/vfprintf.c1107
1 files changed, 1107 insertions, 0 deletions
diff --git a/contrib/sendmail/libsm/vfprintf.c b/contrib/sendmail/libsm/vfprintf.c
new file mode 100644
index 0000000..e2115bd
--- /dev/null
+++ b/contrib/sendmail/libsm/vfprintf.c
@@ -0,0 +1,1107 @@
+/*
+ * Copyright (c) 2000-2001 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.
+ */
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.52 2001/09/11 04:04:49 gshapiro Exp $")
+
+/*
+** Overall:
+** Actual printing innards.
+** This code is large and complicated...
+*/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sm/config.h>
+#include <sm/varargs.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include <sm/conf.h>
+#include "local.h"
+#include "fvwrite.h"
+
+static void sm_find_arguments __P((const char *, va_list , va_list **));
+static void sm_grow_type_table_x __P((unsigned char **, int *));
+static int sm_print __P((SM_FILE_T *, int, struct sm_uio *));
+
+/*
+** SM_PRINT -- print/flush to the file
+**
+** 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
+sm_print(fp, timeout, uio)
+ SM_FILE_T *fp;
+ int timeout;
+ register struct sm_uio *uio;
+{
+ register int err;
+
+ if (uio->uio_resid == 0)
+ {
+ uio->uio_iovcnt = 0;
+ return 0;
+ }
+ err = sm_fvwrite(fp, timeout, uio);
+ uio->uio_resid = 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
+sm_bprintf(fp, fmt, ap)
+ register SM_FILE_T *fp;
+ const char *fmt;
+ SM_VA_LOCAL_DECL
+{
+ int ret;
+ SM_FILE_T fake;
+ unsigned char buf[SM_IO_BUFSIZ];
+ extern const char SmFileMagic[];
+
+ /* copy the important variables */
+ fake.sm_magic = SmFileMagic;
+ fake.f_timeout = SM_TIME_FOREVER;
+ fake.f_timeoutstate = SM_TIME_BLOCK;
+ fake.f_flags = fp->f_flags & ~SMNBF;
+ fake.f_file = fp->f_file;
+ fake.f_cookie = fp->f_cookie;
+ fake.f_write = fp->f_write;
+ fake.f_close = NULL;
+ fake.f_open = NULL;
+ fake.f_read = NULL;
+ fake.f_seek = NULL;
+ fake.f_setinfo = fake.f_getinfo = NULL;
+ fake.f_type = "sm_bprintf:fake";
+
+ /* set up the buffer */
+ fake.f_bf.smb_base = fake.f_p = buf;
+ fake.f_bf.smb_size = fake.f_w = sizeof(buf);
+ fake.f_lbfsize = 0; /* not actually used, but Just In Case */
+
+ /* do the work, then copy any error status */
+ ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
+ if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
+ ret = SM_IO_EOF; /* errno set by sm_io_flush */
+ if (fake.f_flags & SMERR)
+ fp->f_flags |= SMERR;
+ return ret;
+}
+
+
+#define BUF 40
+
+#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
+
+
+/* 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) ((char) (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 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 */
+
+/*
+** 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
+sm_io_vfprintf(fp, timeout, fmt0, ap)
+ SM_FILE_T *fp;
+ int timeout;
+ const char *fmt0;
+ SM_VA_LOCAL_DECL
+{
+ 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 struct sm_iov *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) */
+ wchar_t wc;
+ ULONGLONG_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 realsz; /* field size expanded by dprec */
+ int size; /* size of converted field or string */
+ char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
+#define NIOV 8
+ struct sm_uio uio; /* output information: summary */
+ struct sm_iov iov[NIOV];/* ... and individual io vectors */
+ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
+ char ox[2]; /* space for 0x hex-prefix */
+ va_list *argtable; /* args, built due to positional arg */
+ va_list statargtable[STATIC_ARG_TBL_SIZE];
+ int nextarg; /* 1-based argument index */
+ va_list orgap; /* original argument pointer */
+
+ /*
+ ** 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) do { \
+ iovp->iov_base = (ptr); \
+ iovp->iov_len = (len); \
+ uio.uio_resid += (len); \
+ iovp++; \
+ if (++uio.uio_iovcnt >= NIOV) \
+ { \
+ if (sm_print(fp, timeout, &uio)) \
+ goto error; \
+ iovp = iov; \
+ } \
+} while (0)
+#define PAD(howmany, with) do \
+{ \
+ if ((n = (howmany)) > 0) \
+ { \
+ while (n > PADSIZE) { \
+ PRINT(with, PADSIZE); \
+ n -= PADSIZE; \
+ } \
+ PRINT(with, n); \
+ } \
+} while (0)
+#define FLUSH() do \
+{ \
+ if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
+ goto error; \
+ uio.uio_iovcnt = 0; \
+ iovp = iov; \
+} while (0)
+
+ /*
+ ** To extend shorts properly, we need both signed and unsigned
+ ** argument extraction methods.
+ */
+#define SARG() \
+ (flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
+ flags&LONGINT ? GETARG(long) : \
+ flags&SHORTINT ? (long) (short) GETARG(int) : \
+ (long) GETARG(int))
+#define UARG() \
+ (flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
+ flags&LONGINT ? GETARG(unsigned long) : \
+ flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
+ (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.
+ */
+#define GETASTER(val) \
+ n2 = 0; \
+ cp = fmt; \
+ while (is_digit(*cp)) \
+ { \
+ n2 = 10 * n2 + to_digit(*cp); \
+ cp++; \
+ } \
+ if (*cp == '$') \
+ { \
+ int hold = nextarg; \
+ if (argtable == NULL) \
+ { \
+ argtable = statargtable; \
+ sm_find_arguments(fmt0, orgap, &argtable); \
+ } \
+ nextarg = n2; \
+ val = GETARG(int); \
+ nextarg = hold; \
+ fmt = ++cp; \
+ } \
+ else \
+ { \
+ val = GETARG(int); \
+ }
+
+/*
+** 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
+# define GETARG(type) \
+ (((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
+ nextarg++, SM_VA_ARG(ap, type))
+#else /* SM_VA_STD */
+# define GETARG(type) \
+ ((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
+ (nextarg++, SM_VA_ARG(ap, type)))
+#endif /* SM_VA_STD */
+
+ /* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
+ if (cantwrite(fp))
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+
+ /* optimise fprintf(stderr) (and other unbuffered Unix files) */
+ if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
+ fp->f_file >= 0)
+ return sm_bprintf(fp, fmt0, ap);
+
+ fmt = (char *) fmt0;
+ argtable = NULL;
+ nextarg = 1;
+ SM_VA_COPY(orgap, ap);
+ uio.uio_iov = iovp = iov;
+ uio.uio_resid = 0;
+ uio.uio_iovcnt = 0;
+ ret = 0;
+
+ /* Scan the format for conversions (`%' character). */
+ for (;;)
+ {
+ cp = fmt;
+ n = 0;
+ while ((wc = *fmt) != '\0')
+ {
+ if (wc == '%')
+ {
+ n = 1;
+ break;
+ }
+ fmt++;
+ }
+ if ((m = fmt - cp) != 0)
+ {
+ PRINT(cp, m);
+ ret += m;
+ }
+ if (n <= 0)
+ goto done;
+ fmt++; /* skip over '%' */
+
+ flags = 0;
+ dprec = 0;
+ width = 0;
+ prec = -1;
+ sign = '\0';
+
+rflag: ch = *fmt++;
+reswitch: switch (ch)
+ {
+ case ' ':
+
+ /*
+ ** ``If the space and + flags both appear, the space
+ ** flag will be ignored.''
+ ** -- ANSI X3J11
+ */
+
+ if (!sign)
+ sign = ' ';
+ goto rflag;
+ case '#':
+ flags |= ALT;
+ 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.
+ */
+
+ GETASTER(width);
+ if (width >= 0)
+ goto rflag;
+ width = -width;
+ /* FALLTHROUGH */
+ case '-':
+ flags |= LADJUST;
+ goto rflag;
+ case '+':
+ sign = '+';
+ goto rflag;
+ case '.':
+ if ((ch = *fmt++) == '*')
+ {
+ GETASTER(n);
+ prec = n < 0 ? -1 : n;
+ goto rflag;
+ }
+ n = 0;
+ while (is_digit(ch))
+ {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ }
+ if (ch == '$')
+ {
+ nextarg = n;
+ if (argtable == NULL)
+ {
+ argtable = statargtable;
+ sm_find_arguments(fmt0, orgap,
+ &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
+ */
+
+ flags |= ZEROPAD;
+ goto rflag;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = 0;
+ do
+ {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ } while (is_digit(ch));
+ if (ch == '$')
+ {
+ nextarg = n;
+ if (argtable == NULL)
+ {
+ argtable = statargtable;
+ sm_find_arguments(fmt0, orgap,
+ &argtable);
+ }
+ goto rflag;
+ }
+ width = n;
+ goto reswitch;
+ case 'h':
+ flags |= SHORTINT;
+ goto rflag;
+ case 'l':
+ if (*fmt == 'l')
+ {
+ fmt++;
+ flags |= QUADINT;
+ }
+ else
+ {
+ flags |= LONGINT;
+ }
+ goto rflag;
+ case 'q':
+ flags |= QUADINT;
+ goto rflag;
+ case 'c':
+ *(cp = buf) = GETARG(int);
+ size = 1;
+ sign = '\0';
+ break;
+ case 'D':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'd':
+ case 'i':
+ _uquad = SARG();
+ if ((LONGLONG_T) _uquad < 0)
+ {
+ _uquad = -(LONGLONG_T) _uquad;
+ sign = '-';
+ }
+ base = DEC;
+ goto number;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ {
+ double val;
+ char *p;
+ char fmt[16];
+ char out[150];
+ size_t len;
+
+ /*
+ ** 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.
+ */
+
+ val = GETARG(double);
+
+ p = fmt;
+ *p++ = '%';
+ if (sign)
+ *p++ = sign;
+ if (flags & ALT)
+ *p++ = '#';
+ if (flags & LADJUST)
+ *p++ = '-';
+ if (flags & ZEROPAD)
+ *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)
+ sprintf(out, fmt, width, prec, val);
+ else
+ sprintf(out, fmt, width, val);
+ len = strlen(out);
+ PRINT(out, len);
+ FLUSH();
+ continue;
+ }
+ case 'n':
+ if (flags & QUADINT)
+ *GETARG(LONGLONG_T *) = ret;
+ else if (flags & LONGINT)
+ *GETARG(long *) = ret;
+ else if (flags & SHORTINT)
+ *GETARG(short *) = ret;
+ else
+ *GETARG(int *) = ret;
+ continue; /* no output */
+ case 'O':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'o':
+ _uquad = UARG();
+ base = OCT;
+ 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;
+ ULONGLONG_T ll;
+ unsigned long l;
+ unsigned i;
+ } u;
+ u.p = GETARG(void *);
+ if (sizeof(void *) == sizeof(ULONGLONG_T))
+ _uquad = u.ll;
+ else if (sizeof(void *) == sizeof(long))
+ _uquad = u.l;
+ else
+ _uquad = u.i;
+ }
+ base = HEX;
+ xdigs = "0123456789abcdef";
+ flags |= HEXPREFIX;
+ ch = 'x';
+ goto nosign;
+ case 's':
+ if ((cp = GETARG(char *)) == NULL)
+ 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.
+ */
+
+ char *p = memchr(cp, 0, prec);
+
+ if (p != NULL)
+ {
+ size = p - cp;
+ if (size > prec)
+ size = prec;
+ }
+ else
+ size = prec;
+ }
+ else
+ size = strlen(cp);
+ sign = '\0';
+ break;
+ case 'U':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'u':
+ _uquad = UARG();
+ base = DEC;
+ goto nosign;
+ case 'X':
+ xdigs = "0123456789ABCDEF";
+ goto hex;
+ case 'x':
+ xdigs = "0123456789abcdef";
+hex: _uquad = UARG();
+ base = HEX;
+ /* leading 0x/X only if non-zero */
+ if (flags & ALT && _uquad != 0)
+ flags |= HEXPREFIX;
+
+ /* unsigned conversions */
+nosign: sign = '\0';
+
+ /*
+ ** ``... diouXx conversions ... if a precision is
+ ** specified, the 0 flag will be ignored.''
+ ** -- ANSI X3J11
+ */
+
+number: if ((dprec = prec) >= 0)
+ flags &= ~ZEROPAD;
+
+ /*
+ ** ``The result of converting a zero value with an
+ ** explicit precision of zero is no characters.''
+ ** -- ANSI X3J11
+ */
+
+ cp = buf + BUF;
+ 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.
+ */
+
+ switch (base)
+ {
+ case OCT:
+ do
+ {
+ *--cp = to_char(_uquad & 7);
+ _uquad >>= 3;
+ } while (_uquad);
+ /* handle octal leading 0 */
+ if (flags & ALT && *cp != '0')
+ *--cp = '0';
+ break;
+
+ case DEC:
+ /* many numbers are 1 digit */
+ while (_uquad >= 10)
+ {
+ *--cp = to_char(_uquad % 10);
+ _uquad /= 10;
+ }
+ *--cp = to_char(_uquad);
+ break;
+
+ case HEX:
+ do
+ {
+ *--cp = xdigs[_uquad & 15];
+ _uquad >>= 4;
+ } while (_uquad);
+ break;
+
+ default:
+ cp = "bug in sm_io_vfprintf: bad base";
+ size = strlen(cp);
+ goto skipsize;
+ }
+ }
+ size = buf + BUF - cp;
+ skipsize:
+ break;
+ default: /* "%?" prints ?, unless ? is NUL */
+ if (ch == '\0')
+ goto done;
+ /* pretend it was %c with argument ch */
+ cp = buf;
+ *cp = 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.
+ */
+
+ realsz = dprec > size ? dprec : size;
+ if (sign)
+ realsz++;
+ else if (flags & HEXPREFIX)
+ realsz+= 2;
+
+ /* right-adjusting blank padding */
+ if ((flags & (LADJUST|ZEROPAD)) == 0)
+ PAD(width - realsz, blanks);
+
+ /* prefix */
+ if (sign)
+ {
+ PRINT(&sign, 1);
+ }
+ else if (flags & HEXPREFIX)
+ {
+ ox[0] = '0';
+ ox[1] = ch;
+ PRINT(ox, 2);
+ }
+
+ /* right-adjusting zero padding */
+ if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
+ PAD(width - realsz, zeroes);
+
+ /* leading zeroes from decimal precision */
+ PAD(dprec - size, zeroes);
+
+ /* the string or number proper */
+ PRINT(cp, size);
+ /* left-adjusting padding (always blank) */
+ if (flags & LADJUST)
+ PAD(width - realsz, blanks);
+
+ /* finally, adjust ret */
+ ret += width > realsz ? width : realsz;
+
+ FLUSH(); /* copy out the I/O vectors */
+ }
+done:
+ FLUSH();
+error:
+ if ((argtable != NULL) && (argtable != statargtable))
+ sm_free(argtable);
+ return sm_error(fp) ? SM_IO_EOF : ret;
+ /* 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
+sm_find_arguments(fmt0, ap, argtable)
+ const char *fmt0;
+ SM_VA_LOCAL_DECL
+ va_list **argtable;
+{
+ 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 ADDTYPE(type) \
+ ((nextarg >= tablesize) ? \
+ (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
+ typetable[nextarg++] = type, \
+ (nextarg > tablemax) ? tablemax = nextarg : 0)
+
+#define ADDSARG() \
+ ((flags & LONGINT) ? ADDTYPE(T_LONG) : \
+ ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
+
+#define ADDUARG() \
+ ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
+ ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
+
+ /* Add * arguments to the type array. */
+#define ADDASTER() \
+ n2 = 0; \
+ cp = fmt; \
+ while (is_digit(*cp)) \
+ { \
+ n2 = 10 * n2 + to_digit(*cp); \
+ cp++; \
+ } \
+ if (*cp == '$') \
+ { \
+ int hold = nextarg; \
+ nextarg = n2; \
+ ADDTYPE (T_INT); \
+ nextarg = hold; \
+ fmt = ++cp; \
+ } \
+ else \
+ { \
+ ADDTYPE (T_INT); \
+ }
+ fmt = (char *) fmt0;
+ typetable = stattypetable;
+ tablesize = STATIC_ARG_TBL_SIZE;
+ tablemax = 0;
+ nextarg = 1;
+ (void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
+
+ /* Scan the format for conversions (`%' character). */
+ for (;;)
+ {
+ for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
+ /* void */;
+ if (ch == '\0')
+ goto done;
+ fmt++; /* skip over '%' */
+
+ flags = 0;
+
+rflag: ch = *fmt++;
+reswitch: switch (ch)
+ {
+ case ' ':
+ case '#':
+ goto rflag;
+ case '*':
+ ADDASTER();
+ goto rflag;
+ case '-':
+ case '+':
+ goto rflag;
+ case '.':
+ if ((ch = *fmt++) == '*')
+ {
+ ADDASTER();
+ goto rflag;
+ }
+ while (is_digit(ch))
+ {
+ ch = *fmt++;
+ }
+ 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
+ {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ } while (is_digit(ch));
+ if (ch == '$')
+ {
+ nextarg = n;
+ goto rflag;
+ }
+ goto reswitch;
+ case 'h':
+ flags |= SHORTINT;
+ goto rflag;
+ case 'l':
+ flags |= LONGINT;
+ goto rflag;
+ case 'q':
+ flags |= QUADINT;
+ goto rflag;
+ case 'c':
+ ADDTYPE(T_INT);
+ break;
+ case 'D':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'd':
+ case 'i':
+ if (flags & QUADINT)
+ {
+ ADDTYPE(T_QUAD);
+ }
+ else
+ {
+ ADDSARG();
+ }
+ break;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ ADDTYPE(T_DOUBLE);
+ break;
+ case 'n':
+ if (flags & QUADINT)
+ ADDTYPE(TP_QUAD);
+ else if (flags & LONGINT)
+ ADDTYPE(TP_LONG);
+ else if (flags & SHORTINT)
+ ADDTYPE(TP_SHORT);
+ else
+ ADDTYPE(TP_INT);
+ continue; /* no output */
+ case 'O':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'o':
+ if (flags & QUADINT)
+ ADDTYPE(T_U_QUAD);
+ else
+ ADDUARG();
+ break;
+ case 'p':
+ ADDTYPE(TP_VOID);
+ break;
+ case 's':
+ ADDTYPE(TP_CHAR);
+ break;
+ case 'U':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'u':
+ if (flags & QUADINT)
+ ADDTYPE(T_U_QUAD);
+ else
+ ADDUARG();
+ break;
+ case 'X':
+ case 'x':
+ if (flags & QUADINT)
+ ADDTYPE(T_U_QUAD);
+ 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)
+ {
+ *argtable = (va_list *)
+ sm_malloc(sizeof(va_list) * (tablemax + 1));
+ }
+
+ for (n = 1; n <= tablemax; n++)
+ {
+ SM_VA_COPY((*argtable)[n], ap);
+ switch (typetable [n])
+ {
+ case T_UNUSED:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case T_SHORT:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case T_U_SHORT:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case TP_SHORT:
+ (void) SM_VA_ARG(ap, short *);
+ break;
+ case T_INT:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case T_U_INT:
+ (void) SM_VA_ARG(ap, unsigned int);
+ break;
+ case TP_INT:
+ (void) SM_VA_ARG(ap, int *);
+ break;
+ case T_LONG:
+ (void) SM_VA_ARG(ap, long);
+ break;
+ case T_U_LONG:
+ (void) SM_VA_ARG(ap, unsigned long);
+ break;
+ case TP_LONG:
+ (void) SM_VA_ARG(ap, long *);
+ break;
+ case T_QUAD:
+ (void) SM_VA_ARG(ap, LONGLONG_T);
+ break;
+ case T_U_QUAD:
+ (void) SM_VA_ARG(ap, ULONGLONG_T);
+ break;
+ case TP_QUAD:
+ (void) SM_VA_ARG(ap, LONGLONG_T *);
+ break;
+ case T_DOUBLE:
+ (void) SM_VA_ARG(ap, double);
+ break;
+ case TP_CHAR:
+ (void) SM_VA_ARG(ap, char *);
+ break;
+ case TP_VOID:
+ (void) SM_VA_ARG(ap, void *);
+ break;
+ }
+ }
+
+ if ((typetable != NULL) && (typetable != stattypetable))
+ sm_free(typetable);
+}
+
+/*
+** 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
+sm_grow_type_table_x(typetable, tablesize)
+ unsigned char **typetable;
+ int *tablesize;
+{
+ unsigned char *oldtable = *typetable;
+ int newsize = *tablesize * 2;
+
+ if (*tablesize == STATIC_ARG_TBL_SIZE)
+ {
+ *typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
+ * newsize);
+ (void) memmove(*typetable, oldtable, *tablesize);
+ }
+ else
+ {
+ *typetable = (unsigned char *) sm_realloc_x(typetable,
+ sizeof(unsigned char) * newsize);
+ }
+ (void) memset(&typetable [*tablesize], T_UNUSED,
+ (newsize - *tablesize));
+
+ *tablesize = newsize;
+}
OpenPOWER on IntegriCloud