diff options
Diffstat (limited to 'sendmail/libsm/vfprintf.c')
-rw-r--r-- | sendmail/libsm/vfprintf.c | 1118 |
1 files changed, 1118 insertions, 0 deletions
diff --git a/sendmail/libsm/vfprintf.c b/sendmail/libsm/vfprintf.c new file mode 100644 index 0000000..c4ca9bb --- /dev/null +++ b/sendmail/libsm/vfprintf.c @@ -0,0 +1,1118 @@ +/* + * 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. + */ + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.54 2005/05/16 03:52:00 ca 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 int sm_bprintf __P((SM_FILE_T *, const char *, va_list)); +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) + 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) +#if HASSNPRINTF + snprintf(out, sizeof(out), fmt, width, + prec, val); +#else /* HASSNPRINTF */ + sprintf(out, fmt, width, prec, val); +#endif /* HASSNPRINTF */ + else +#if HASSNPRINTF + snprintf(out, sizeof(out), fmt, width, + val); +#else /* HASSNPRINTF */ + sprintf(out, fmt, width, val); +#endif /* HASSNPRINTF */ + 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; +} |