blob: 0d7b5868ac3b25c3ce6806ce1a11c449da0288bf [file] [log] [blame]
//===- PrintfSupport.cpp - Secure printf() replacement --------------------===//
//
// The SAFECode Compiler
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a secure runtime replacement for printf() and similar
// functions.
//
//===----------------------------------------------------------------------===//
//
// This code is derived from OpenBSD's vfprintf.c; original license follows:
//
// $OpenBSD: vfprintf.c,v 1.60 2010/12/22 14:54:44 millert Exp
//
// 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.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 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. 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
// SUCH DAMAGE.
//
//
// Actual printf innards.
//
// This code is large and complicated...
//
#include "safecode/Config/config.h"
#include "FormatStrings.h"
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#include <algorithm>
#include <iostream>
using std::cerr;
using std::endl;
using std::min;
using std::max;
using std::map;
//
// This structure holds a single buffer to be printed.
//
struct siov
{
// Start of buffer
const char *iov_base;
// Number of bytes to print
size_t iov_len;
};
//
// This structure holds an array of buffers which are printed at one time.
//
struct suio
{
// Array of buffers
struct siov *uio_iov;
// Number of buffers
int uio_iovcnt;
// Total number of bytes to print
size_t uio_resid;
};
//
// do_output()
//
// Flushes out all the vectors defined by the given uio, then resets it so that
// it can be reused.
//
// Inputs:
// c - a pointer to the relevant call_info structure
// p - a pointer to the output_parameter structure describing the output
// location
// uio - a pointer to an suio structure that contains the buffers to be
// written
//
// Returns nonzero on error, zero on success.
//
static int
do_output(call_info *c, output_parameter *p, struct suio *uio)
{
//
// Do a write to an output file if that's the output destination.
//
if (p->output_kind == output_parameter::OUTPUT_TO_FILE)
{
FILE *out = p->output.file;
for (int i = 0; i < uio->uio_iovcnt; ++i)
{
size_t amt, sz = uio->uio_iov[i].iov_len;
//
// Call fwrite_unlocked() for performance since the output stream should
// already be locked by this thread.
//
// Use fwrite() on platforms without fwrite_unlocked().
//
#ifndef HAVE_FWRITE_UNLOCKED
#define fwrite_unlocked fwrite
#endif
amt = fwrite_unlocked(&uio->uio_iov[i].iov_base[0], 1, sz, out);
if (amt < sz)
return 1; // Output error
}
uio->uio_resid = 0;
uio->uio_iovcnt = 0;
return 0;
}
//
// Write into a (fixed) buffer.
//
else if (p->output_kind == output_parameter::OUTPUT_TO_STRING)
{
char *dest = p->output.string.string;
size_t &pos = p->output.string.pos;
size_t n = p->output.string.n;
size_t msz = p->output.string.maxsz;
size_t amt;
if (pos > n) // Don't write anything if we've reached the limit.
{
uio->uio_resid = 0;
uio->uio_iovcnt = 0;
return 0;
}
for (int i = 0; i < uio->uio_iovcnt; ++i)
{
//
// Get the amount of data that we would *like* to write into the buffer.
//
amt = uio->uio_iov[i].iov_len;
//
// If the user-imposed limit is within the memory bounds limit, then
// check to see if we reach the user-imposed limit. If so, fill the
// string to the allowed capacity and return.
//
if ((n - pos) <= (msz - pos))
{
if (amt > n - pos)
{
memcpy(&dest[pos], &uio->uio_iov[i].iov_base[0], n - pos);
pos = n;
uio->uio_resid = 0;
uio->uio_iovcnt = 0;
return 0;
}
}
//
// Check for out of bounds write. Report an out of bounds write only once.
//
if (pos < msz && amt > msz - pos)
{
pointer_info *info = p->output.string.info;
cerr << "Destination string not long enough!" << endl;
write_out_of_bounds_error(c, info, msz, pos + amt);
}
//
// Check to see if the we'll reach the user imposed size.
// If so, fill the string to the allowed capacity and return.
//
if (amt > n - pos)
{
memcpy(&dest[pos], &uio->uio_iov[i].iov_base[0], n - pos);
pos = n;
uio->uio_resid = 0;
uio->uio_iovcnt = 0;
return 0;
}
//
// Otherwise, copy over the buffer and continue.
//
memcpy(&dest[pos], &uio->uio_iov[i].iov_base[0], amt);
pos += amt;
}
uio->uio_resid = 0;
uio->uio_iovcnt = 0;
return 0;
}
//
// Write into a buffer that's dynamically allocated to fit the entire write.
//
else // if (p->output_kind == output_parameter::OUTPUT_TO_ALLOCATED_STRING)
{
char *&dest = p->output.alloced_string.string;
size_t &pos = p->output.alloced_string.pos;
size_t &bufsz = p->output.alloced_string.bufsz;
size_t amt;
if (dest == 0)
return 1; // Output error
//
// Allocate a new string if the old one isn't large enough.
//
if (uio->uio_resid > bufsz - pos)
{
do bufsz *= 2; while (uio->uio_resid > bufsz - pos);
dest = (char *) realloc(dest, bufsz);
if (dest == 0)
return 1; // Output error
}
//
// Copy the characters over.
//
for (int i = 0; i < uio->uio_iovcnt; ++i)
{
amt = uio->uio_iov[i].iov_len;
memcpy(&dest[pos], &uio->uio_iov[i].iov_base[0], amt);
pos += amt;
}
uio->uio_resid = 0;
uio->uio_iovcnt = 0;
return 0;
}
}
//
// An element to hold the value of a positional argument in the positional
// argument table.
//
union arg
{
int intarg;
unsigned int uintarg;
long longarg;
unsigned long ulongarg;
long long longlongarg;
unsigned long long ulonglongarg;
ptrdiff_t ptrdiffarg;
size_t sizearg;
ssize_t ssizearg;
intmax_t intmaxarg;
uintmax_t uintmaxarg;
void *pvoidarg;
wint_t wintarg;
#ifdef FLOATING_POINT
double doublearg;
long double longdoublearg;
#endif
};
static inline int
find_arguments(const char *fmt0,
va_list ap,
union arg **argtable,
size_t *argtablesiz,
unsigned vargc);
static inline int
grow_type_table(unsigned char **typetable, size_t *tablesize, size_t minsz);
#ifdef FLOATING_POINT
#include "../include/FloatConversion.h"
// The default floating point precision
#define DEFPREC 6
static int exponent(char *, int, int);
#endif // FLOATING_POINT
//
// The size of the buffer we use as scratch space for integer
// conversions, among other things. Technically, we would need the
// most space for base 10 conversions with thousands' grouping
// characters between each pair of digits. 100 bytes is a
// conservative overestimate even for a 128-bit uintmax_t.
//
#define BUF 100
#define STATIC_ARG_TBL_SIZE 32 // 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) ((n) + '0')
//
// Flags used during conversion.
//
#define ALT 0x0001 // alternate form
#define LADJUST 0x0004 // left adjustment
#define LONGDBL 0x0008 // long double
#define LONGINT 0x0010 // long integer
#define LLONGINT 0x0020 // long long integer
#define SHORTINT 0x0040 // short integer
#define ZEROPAD 0x0080 // zero (as opposed to blank) pad
#define FPT 0x0100 // Floating point number
#define PTRINT 0x0200 // (unsigned) ptrdiff_t
#define SIZEINT 0x0400 // (signed) size_t
#define CHARINT 0x0800 // 8 bit integer
#define MAXINT 0x1000 // largest integer size (intmax_t)
//
// handle_s_directive()
//
// Prepare a string for printing.
//
// Inputs:
// ci - a pointer to the relevant call_info structure
// options - options passed to the format string function
// p - a pointer to the pointer_info structure that contains the
// string to print
// flags - the relevant flags from the parsed format string
// cp - a pointer into which is written the location of the string to
// print
// len - a pointer to a size_t object, into which is written the number
// of bytes to print
// mbstr - a pointer into which will be written the location of any string
// allocated for purposes of converting a multibyte sequence
// prec - an integer representing the precision
//
// Returns:
// The function returns no value. Instead, *cp is set to point to the buffer
// containing the string of which the first *len bytes should be written.
// If this string is the result of a wide character to multibyte conversion,
// it is an allocated string and *mbstr is also set to point to it. Otherwise
// *mbstr is set to NULL.
//
static inline void
handle_s_directive(call_info *ci,
options_t options,
pointer_info *p,
int flags,
const char **cp,
size_t *len,
char **mbstr,
int prec)
{
//
// Free any previously allocated buffers.
//
if (*mbstr)
free(*mbstr);
//
// Load the object boundaries.
//
find_object(ci, p);
//
// A negative precision indicates the precision has not been set. Set it to
// the maximum allowed value and continue.
//
if (prec < 0)
prec = INT_MAX;
if (!(flags & LONGINT))
{
//
// Print out a regular string.
//
// maxbytes is the maximum number of bytes that can be read safely from
// the input while respecting the precision requirements.
//
size_t maxbytes = (p->flags & HAVEBOUNDS) ?
min((size_t) prec, object_len(p)) :
(size_t) prec;
char *str = (char *) p->ptr;
char *r = (char *) memchr(str, 0, maxbytes);
*cp = str;
*mbstr = 0;
if (r)
*len = r - str;
else if ((unsigned)prec <= maxbytes)
*len = prec;
else
{
*len = maxbytes;
cerr << "Reading string out of bounds!" << endl;
out_of_bounds_error(ci, p, maxbytes);
}
return;
}
else
{
//
// Print out a wide character string converted into multibyte
// characters.
//
// We can't tell how long the resulting multibyte character string will
// be without converting it character by character.
//
// maxbytes represents the maximum number of bytes we can read from the
// wide character string in a safe manner.
//
size_t maxbytes = (p->flags & HAVEBOUNDS) ? object_len(p) : SIZE_MAX;
mbstate_t ps;
wchar_t *input = (wchar_t *) p->ptr;
size_t bytesread = 0; // the current number of bytes read from the wide
// character array
bool overread = false; // whether the wide character array is read out
// of bounds
size_t destpos = 0; // the current index of the converted character
// string
size_t destsize; // the current size of the converted character
// string object
size_t convlen = 0; // the length of the most recent converted
// character
wchar_t nextch; // the next character to convert
char buffer[MB_LEN_MAX]; // buffer for holding character conversions
//
// Set up the destination.
//
*mbstr = (char *) malloc((destsize = 64));
memset(&ps, 0, sizeof(mbstate_t));
bytesread = 0;
if (*mbstr == 0)
{
*cp = "(error)";
*len = 7;
return;
}
//
// Read the wide character string and convert it to a multibyte character
// string using wcrtomb().
//
while (1)
{
//
// If the next wide character to be read will cause us to read out of
// bounds of the array, issue a SAFECode error.
//
if (sizeof(wchar_t) > (maxbytes - bytesread) && overread == false)
{
overread = true;
cerr << "Reading wide character string out of bounds!" << endl;
out_of_bounds_error(ci, p, maxbytes);
}
//
// Read the next wide character from the input. If it's the string
// terminator, terminate the conversion.
//
nextch = input[bytesread / sizeof(wchar_t)];
if (nextch == L'\0')
break;
bytesread += sizeof(wchar_t);
//
// Convert the wide character into a multibyte character.
//
convlen = wcrtomb(&buffer[0], nextch, &ps);
//
// Check for conversion error. If so, terminate the conversion
// at this point.
//
if (convlen == (size_t) -1)
break;
//
// Check if 1) appending the destination character keeps us within the
// allowed precision, and 2) if appending the destination character
// requires making the conversion buffer larger.
//
else if (convlen > (size_t) prec - destpos)
break;
else if (convlen > destsize - destpos)
{
do destsize *= 2; while (convlen > destsize - destpos);
*mbstr = (char *) realloc(*mbstr, destsize);
if (*mbstr == 0)
{
*cp = "(error)";
*len = 7;
return;
}
}
//
// Finally, append the converted character.
//
memcpy(&(*mbstr)[destpos], &buffer[0], convlen);
destpos += convlen;
if (destpos == (size_t) prec)
break;
}
*cp = *mbstr;
*len = destpos;
return;
}
}
//
// The main logic for printf() style functions
//
// Inputs:
// options - options controlling some aspects of execution
// output - a reference to the output_parameter structure describing
// where to do the write
// cinfo - a reference to the call_info structure which contains
// information about the va_list
// fmt0 - the format string
// ap - the variable argument list
//
// Returns:
// This function returns the number of characters that would have been
// written had the output been unbounded on success, and a negative number on
// failure.
//
// IMPORTANT IMPLEMENTATION LIMITATIONS
// - Floating point number printing is not thread safe
// - No support for (nonstandard) locale-defined thousands grouping
// (the "'" flag)
//
int
internal_printf(const options_t options,
output_parameter &output,
call_info &cinfo,
const char *fmt0,
va_list ap)
{
const char *fmt; // format string
int ch; // character from fmt
int n, n2; // handy integers (short term usage)
const char *cp; // handy const char pointer (short term usage)
char *bp; // handy char pointer
struct siov *iovp; // for PRINT macro
int flags; // flags as above
int ret; // return value accumulator
int width; // width from format (%8d), or 0
int prec; // precision from format; <0 for N/A
char sign; // sign prefix (' ', '+', '-', or \0)
#ifdef FLOATING_POINT
//
// We can decompose the printed representation of floating
// point numbers into several parts, some of which may be empty:
//
// [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
// A B ---C--- D E F
//
// A: 'sign' holds this value if present; '\0' otherwise
// B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
// C: cp points to the string MMMNNN. Leading and trailing
// zeros are not in the string and must be added.
// D: expchar holds this character; '\0' if no exponent, e.g. %f
// F: at least two digits for decimal, at least one digit for hex
//
char *decimal_point = localeconv()->decimal_point;
int signflag; // true if float is negative
union // floating point arguments %[aAeEfFgG]
{
double dbl;
long double ldbl;
} fparg;
int expt; // integer value of exponent
char expchar = 0; // exponent character: [eEpP\0]
char *dtoaend; // pointer to end of converted digits
int expsize = 0; // character count for expstr
int lead = 0; // sig figs before decimal or group sep
int ndig = 0; // actual number of digits returned by dtoa
// Set this to the maximum number of digits in an exponent.
// This is a large overestimate.
#define MAXEXPDIG 32
char expstr[MAXEXPDIG+2]; // buffer for exponent string: e+ZZZ
char *dtoaresult = 0;
#endif
uintmax_t _umax; // 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
const char *xdigs; // digits for %[xX] conversion
const int NIOV = 8;
struct suio uio; // output information: summary
struct siov iov[NIOV]; // ... and individual io vectors
char buf[BUF]; // buffer with space for digits of uintmax_t
char ox[2]; // space for 0x; ox[1] is either x, X, or \0
union arg *argtable; // args, built due to positional arg
union arg statargtable[STATIC_ARG_TBL_SIZE]; // initial argument table
size_t argtablesiz; // number of elements in the positional arg table
int nextarg; // 1-based argument index
va_list orgap; // original argument pointer
pointer_info *p; // handy pointer_info structure
wchar_t wc; // the input character to process
char *mbstr; // a string that is a result of multibyte conversion
mbstate_t ps; // conversion state
//
// The next four strings are used by the printing macros.
//
// 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 + 1] = " ";
static char zeroes[PADSIZE + 1] = "0000000000000000";
static char xdigs_lower[] = "0123456789abcdef";
static char xdigs_upper[] = "0123456789ABCDEF";
xdigs = 0;
const unsigned vargc = cinfo.vargc; // number of arguments in the va_list
//
// This map is used if POINTERS_UNWRAPPED is set in the options, to wrap
// pointers in the va_list that do not have a pointer_info structure around
// them.
//
map<void *, pointer_info *> ptr_infos;
//
// Printing macros
//
// BEWARE, these `goto error' on error, and PAD uses `n'.
//
//
// Add the first 'len' bytes of 'ptr' to the output queue.
//
#define PRINT(ptr, len) do { \
iovp->iov_base = (ptr); \
iovp->iov_len = (len); \
uio.uio_resid += (len); \
iovp++; \
if (++uio.uio_iovcnt >= NIOV) \
{ \
if (do_output(&cinfo, &output, &uio)) \
goto error; \
iovp = iov; \
} \
} while (0)
//
// Output 'howmany' bytes from a pad chunk 'with'.
//
#define PAD(howmany, with) do { \
if ((n = (howmany)) > 0) \
{ \
while (n > PADSIZE) \
{ \
PRINT((with), PADSIZE); \
n -= PADSIZE; \
} \
PRINT((with), n); \
} \
} while (0)
//
// Output a string of exactly len bytes, consisting of the string found in the
// range [p, ep), right-padded by the pad characters in 'with', if necessary.
//
#define PRINTANDPAD(p, ep, len, with) do { \
n2 = (ep) - (p); \
if (n2 > (len)) \
n2 = (len); \
if (n2 > 0) \
PRINT((p), n2); \
PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
} while(0)
//
// Flush any output buffers yet to be printed.
//
#define FLUSH() do { \
if (uio.uio_resid && do_output(&cinfo, &output, &uio)) \
goto error; \
uio.uio_iovcnt = 0; \
iovp = iov; \
} while (0)
//
// Integer retrieval macros
//
// To extend shorts properly, we need both signed and unsigned
// argument extraction methods.
//
//
// Retrieve a signed argument.
//
#define SARG() ((intmax_t) \
((flags&MAXINT ? GETARG(intmax_t) : \
flags&LLONGINT ? GETARG(long long) : \
flags&LONGINT ? GETARG(long) : \
flags&PTRINT ? GETARG(ptrdiff_t) : \
flags&SIZEINT ? GETARG(ssize_t) : \
flags&SHORTINT ? (short)GETARG(int) : \
flags&CHARINT ? (signed char)GETARG(int) : \
GETARG(int))))
//
// Retrieve an unsigned argument.
//
#define UARG() ((uintmax_t) \
((flags&MAXINT ? GETARG(uintmax_t) : \
flags&LLONGINT ? GETARG(unsigned long long) : \
flags&LONGINT ? GETARG(unsigned long) : \
flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
flags&SIZEINT ? GETARG(size_t) : \
flags&SHORTINT ? (unsigned short)GETARG(int) : \
flags&CHARINT ? (unsigned char)GETARG(int) : \
GETARG(unsigned int))))
//
// Append a digit to a value and check for overflow.
//
#define APPEND_DIGIT(val, dig) do { \
if ((val) > INT_MAX / 10) \
goto overflow; \
(val) *= 10; \
if ((val) > INT_MAX - to_digit((dig))) \
goto overflow; \
(val) += to_digit((dig)); \
} while (0)
//
// Macros for getting arguments
//
//
// Get * arguments, including the form *nn$, into val. 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)) \
{ \
APPEND_DIGIT(n2, *cp); \
cp++; \
} \
if (*cp == '$') \
{ \
int hold = nextarg; \
if (argtable == 0) \
{ \
argtable = statargtable; \
if (find_arguments(fmt0, orgap, &argtable, &argtablesiz, vargc) == -1) \
goto error; \
} \
nextarg = n2; \
val = GETARG(int); \
nextarg = hold; \
fmt = ++cp; \
} \
else \
val = GETARG(int);
//
// Get the actual 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).
//
#define GETARG(type) ( \
varg_check(&cinfo, options, nextarg++), \
(argtable != 0) ? *((type *) &argtable[nextarg-1]) : va_arg(ap, type) )
//
// Get the pointer_info structure associated with the next argument.
//
#define GETPTRARG() ( \
(pointer_info *) wrap_pointer(options, GETARG(void *), ptr_infos) )
//
// Write the current number of bytes that have been written into the next
// argument, which should be a pointer to 'type'.
//
#define WRITECOUNTAS(type) \
do { \
p = GETPTRARG(); \
write_check(&cinfo, options, p, sizeof(type)); \
*(type *)(p->ptr) = ret; \
} while (0)
fmt = fmt0;
argtable = 0;
nextarg = 1;
va_copy(orgap, ap);
uio.uio_iov = iovp = iov;
uio.uio_resid = 0;
uio.uio_iovcnt = 0;
ret = 0;
mbstr = 0;
memset(&ps, 0, sizeof(mbstate_t));
//
// Scan the format for conversions (`%' character).
//
for (;;)
{
cp = fmt;
while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0)
{
fmt += n;
if (wc == '%')
{
fmt--;
break;
}
}
if (fmt != cp)
{
ptrdiff_t m = fmt - cp;
if (m < 0 || m > INT_MAX - ret)
goto overflow;
PRINT(cp, m);
ret += m;
}
if (n <= 0)
goto done;
fmt++; // skip over '%'
flags = 0;
dprec = 0;
width = 0;
prec = -1;
sign = '\0';
ox[1] = '\0';
//
// This is the main directive parsing section.
//
rflag: ch = *fmt++;
reswitch:
switch (ch)
{
//
// This section parses flags, precision, and field width values.
//
// ' ' flag
//
case ' ':
//
// ``If the space and + flags both appear, the space
// flag will be ignored.''
// -- ANSI X3J11
//
if (!sign)
sign = ' ';
goto rflag;
//
// '#' flag
//
case '#':
flags |= ALT;
goto rflag;
case '\'':
//
// The grouping flag is recognized but is not implemented.
//
goto rflag;
//
// Parse field width given in an argument
//
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;
if (width == INT_MIN)
goto overflow;
width = -width;
// FALLTHROUGH
case '-':
flags |= LADJUST;
goto rflag;
case '+':
sign = '+';
goto rflag;
//
// Parse the precision.
//
case '.':
if ((ch = *fmt++) == '*')
{
GETASTER(n);
prec = n < 0 ? -1 : n;
goto rflag;
}
n = 0;
while (is_digit(ch))
{
APPEND_DIGIT(n, ch);
ch = *fmt++;
}
//
// If the number ends with a $, this indicates a positional argument.
// So parse the whole format string for positional arguments.
//
if (ch == '$')
{
nextarg = n;
if (argtable == 0)
{
argtable = statargtable;
find_arguments(fmt0, orgap, &argtable, &argtablesiz, vargc);
}
goto rflag;
}
prec = n;
goto reswitch;
//
// '0' flag
//
case '0':
//
// ``Note that 0 is taken as a flag, not as the
// beginning of a field width.''
// -- ANSI X3J11
//
flags |= ZEROPAD;
goto rflag;
//
// Reading a number here indicates either a field width or a positional
// argument. They are distinguished since positional arguments end in $.
//
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
do
{
APPEND_DIGIT(n, ch);
ch = *fmt++;
} while (is_digit(ch));
//
// If the number ends with a $, this indicates a positional argument.
// So parse the whole format string for positional arguments.
//
if (ch == '$')
{
nextarg = n;
if (argtable == 0)
{
argtable = statargtable;
find_arguments(fmt0, orgap, &argtable, &argtablesiz, vargc);
}
goto rflag;
}
width = n;
goto reswitch;
//
// This section parses length modifiers.
//
#ifdef FLOATING_POINT
case 'L':
flags |= LONGDBL;
goto rflag;
#endif
case 'h':
if (*fmt == 'h')
{
fmt++;
flags |= CHARINT;
}
else
flags |= SHORTINT;
goto rflag;
case 'j':
flags |= MAXINT;
goto rflag;
case 'l':
if (*fmt == 'l')
{
fmt++;
flags |= LLONGINT;
}
else
flags |= LONGINT;
goto rflag;
case 'q':
flags |= LLONGINT;
goto rflag;
case 't':
flags |= PTRINT;
goto rflag;
case 'z':
flags |= SIZEINT;
goto rflag;
//
// This section parses conversion specifiers and gives instructions on how
// to print the output.
//
//
// 'c' specifier
//
case 'c':
sign = '\0';
if (!(flags & LONGINT))
{
buf[0] = GETARG(int);
cp = buf;
size = 1;
}
//
// Handle wide characters correctly.
//
else
{
wint_t wc = GETARG(wint_t);
mbstate_t st;
size_t sz;
if (mbstr)
free(mbstr);
mbstr = (char *) malloc(MB_LEN_MAX);
if (mbstr == 0)
{
cp = "(error)";
size = 8;
break;
}
else if ((wchar_t) wc == L'\0')
{
//
// Print nothing.
//
cp = "";
size = 0;
break;
}
memset(&st, 0, sizeof(mbstate_t));
sz = wcrtomb(&mbstr[0], (wchar_t) wc, &st);
//
// Handle printing errors.
//
if (sz == (size_t) -1)
{
cp = "";
size = 0;
break;
}
cp = mbstr;
size = (int) sz;
}
break;
//
// %D, %d, %i specifiers
//
case 'D':
flags |= LONGINT;
// FALLTHROUGH
case 'd':
case 'i':
_umax = SARG();
if ((intmax_t)_umax < 0)
{
_umax = -_umax;
sign = '-';
}
base = DEC;
goto number;
#ifdef FLOATING_POINT
//
// %a, %A specifiers
//
case 'a':
case 'A':
if (ch == 'a')
{
ox[1] = 'x';
xdigs = xdigs_lower;
expchar = 'p';
}
else
{
ox[1] = 'X';
xdigs = xdigs_upper;
expchar = 'P';
}
if (prec >= 0)
prec++;
if (dtoaresult)
__freedtoa(dtoaresult);
if (flags & LONGDBL)
{
fparg.ldbl = GETARG(long double);
cp = dtoaresult =
__hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
if (dtoaresult == 0)
{
errno = ENOMEM;
goto error;
}
}
else
{
fparg.dbl = GETARG(double);
cp = dtoaresult =
__hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
if (dtoaresult == 0)
{
errno = ENOMEM;
goto error;
}
}
if (prec < 0)
prec = dtoaend - cp;
if (expt == INT_MAX)
ox[1] = '\0';
goto fp_common;
//
// %e, %E specifiers
//
// This is the form [-]d.ddde[+/-]dd.
// The number of digits after the decimal point is the precision.
//
case 'e':
case 'E':
expchar = ch;
if (prec < 0) // account for digit before decpt
prec = DEFPREC + 1;
else
prec++;
goto fp_begin;
//
// %f, %F specifiers
//
// This is the form ddd.dddd. The number of digits after the decimal point
// is the precision.
//
case 'f':
case 'F':
expchar = '\0';
goto fp_begin;
//
// %g, %G specifiers
//
// 'e' or 'f' style, depending on the precision and exponent.
//
case 'g':
case 'G':
expchar = ch - ('g' - 'e');
if (prec == 0)
prec = 1;
fp_begin:
if (prec < 0)
prec = DEFPREC;
if (dtoaresult)
__freedtoa(dtoaresult);
if (flags & LONGDBL)
{
fparg.ldbl = GETARG(long double);
cp = dtoaresult =
__ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
if (dtoaresult == 0)
{
errno = ENOMEM;
goto error;
}
}
else
{
fparg.dbl = GETARG(double);
//
// There is very sparse documentation for this function call. I'll
// attempt to explain what is going on.
//
// char *
// dtoa(double v, int mode, int prec, int *expt, int *sign, char **end);
//
// The dtoa function returns a (char *) pointer of internally allocated
// memory which is the double value converted into a decimal string.
// The value should be free'd with freedtoa.
//
// In mode 2, which is needed for the form [-]d.ddde[+/-]dd, the
// function returns a string with 'prec' significant digits.
//
// In this mode if XXXXX is the converted string, the number is equal to
// 0.XXXXX * (10 ^ *expt).
//
// In mode 3, the function returns a string which is the decimal
// representation going as far as 'prec' digits beyond the decimal
// point, with trailing zeroes suppressed.
//
// In this mode *expt can be interpreted as the position of the
// decimal point.
//
// Other arguments:
// - signflag is set to true when the number is negative, 0 if not
// - dtoaend is set to point to the end of the returned string.
//
// The calls to similar floating point conversion functions, ie.
// __ldtoa(), __hdtoa(), and __hldtoa() are more or less analogous to
// the call to __dtoa().
//
cp = dtoaresult =
__dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
if (dtoaresult == 0)
{
errno = ENOMEM;
goto error;
}
//
// If the number was bad, expt is set to 9999.
//
if (expt == 9999)
expt = INT_MAX;
}
fp_common:
if (signflag)
sign = '-';
if (expt == INT_MAX) // INF or NaN
{
if (*cp == 'N')
{
cp = (ch >= 'a') ? "nan" : "NAN";
sign = '\0';
}
else
cp = (ch >= 'a') ? "inf" : "INF";
size = 3;
flags &= ~ZEROPAD;
break;
}
flags |= FPT;
ndig = dtoaend - cp;
if (ch == 'g' || ch == 'G')
{
if (expt > -4 && expt <= prec)
{
// Make %[gG] smell like %[fF]
expchar = '\0';
if (flags & ALT)
prec -= expt;
else
prec = ndig - expt;
if (prec < 0)
prec = 0;
}
else
{
// Make %[gG] smell like %[eE], but trim trailing zeroes if no # flag.
if (!(flags & ALT))
prec = ndig;
}
}
if (expchar)
{
expsize = exponent(expstr, expt - 1, expchar);
size = expsize + prec;
if (prec > 1 || flags & ALT)
++size;
}
else
{
// space for digits before decimal point
if (expt > 0)
size = expt;
else // "0"
size = 1;
// space for decimal pt and following digits
if (prec || flags & ALT)
size += prec + 1;
lead = expt;
}
break;
#endif // FLOATING_POINT
//
// %n specifier
//
case 'n':
if (flags & LLONGINT)
WRITECOUNTAS(long long);
else if (flags & LONGINT)
WRITECOUNTAS(long);
else if (flags & SHORTINT)
WRITECOUNTAS(short);
else if (flags & CHARINT)
WRITECOUNTAS(signed char);
else if (flags & PTRINT)
WRITECOUNTAS(ptrdiff_t);
else if (flags & SIZEINT)
WRITECOUNTAS(ssize_t);
else if (flags & MAXINT)
WRITECOUNTAS(intmax_t);
else
WRITECOUNTAS(int);
continue; // no output
//
// %O, %o specifiers
//
case 'O':
flags |= LONGINT;
// FALLTHROUGH
case 'o':
_umax = UARG();
base = OCT;
goto nosign;
//
// %p specifier
//
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
//
p = GETPTRARG();
_umax = (uintmax_t) unwrap_pointer(&cinfo, options, (void *) p);
base = HEX;
xdigs = xdigs_lower;
ox[1] = 'x';
goto nosign;
//
// %s specifier
//
case 's':
sign = '\0';
//
// Get the pointer_info structure associated with the next argument.
//
p = GETPTRARG();
//
// If the structure is NULL or not found in the whitelist, then the
// current argument is not a string.
//
if (p == 0 || !is_in_whitelist(&cinfo, options, p))
{
cp = "(not a string)";
size = 14;
break;
}
//
// Handle printing null pointers.
//
else if (p->ptr == 0)
{
cp = "(null)";
size = 6;
break;
}
//
// Handle printing a string.
//
else
{
size_t sz;
handle_s_directive(&cinfo, options, p, flags, &cp, &sz, &mbstr, prec);
if (sz > INT_MAX)
goto overflow;
else
size = (int) sz;
}
break;
//
// %U, %u specifiers
//
case 'U':
flags |= LONGINT;
// FALLTHROUGH
case 'u':
_umax = UARG();
base = DEC;
goto nosign;
//
// %X, %x specifiers
//
case 'X':
xdigs = xdigs_upper;
goto hex;
case 'x':
xdigs = xdigs_lower;
hex:
_umax = UARG();
base = HEX;
// leading 0x/X only if non-zero
if (flags & ALT && _umax != 0)
ox[1] = ch;
nosign:
// unsigned conversions
sign = '\0';
number:
//
// ``... diouXx conversions ... if a precision is
// specified, the 0 flag will be ignored.''
// -- ANSI X3J11
//
if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
//
// ``The result of converting a zero value with an
// explicit precision of zero is no characters.''
// -- ANSI X3J11
//
bp = buf + BUF;
if (_umax != 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
{
*--bp = to_char(_umax & 7);
_umax >>= 3;
} while (_umax);
// handle octal leading 0
if (flags & ALT && *bp != '0')
*--bp = '0';
break;
case DEC:
// many numbers are 1 digit
while (_umax >= 10)
{
*--bp = to_char(_umax % 10);
_umax /= 10;
}
*--bp = to_char(_umax);
break;
case HEX:
do
{
*--bp = xdigs[_umax & 15];
_umax >>= 4;
} while (_umax);
break;
default:
cp = "bug in *printf: bad base";
size = 24;
goto skipsize;
}
}
cp = bp;
size = buf + BUF - bp;
if (size > BUF) // should never happen
abort();
skipsize:
break;
default: // "%?" prints ?, unless ? is NUL
//
// %m specifier
// (syslog() includes a %m flag which prints sterror(errno).)
//
if (ch == 'm' && options & USE_M_DIRECTIVE)
{
cp = (char *) strerror(errno);
size = strlen(cp);
break;
}
else if (ch == '\0')
goto done;
// pretend it was %c with argument ch
buf[0] = ch;
cp = buf;
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++;
if (ox[1])
realsz+= 2;
// right-adjusting blank padding
if ((flags & (LADJUST|ZEROPAD)) == 0)
PAD(width - realsz, blanks);
// prefix
if (sign)
PRINT(&sign, 1);
if (ox[1])
{
// ox[1] is either x, X, or \0
ox[0] = '0';
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
#ifdef FLOATING_POINT
if ((flags & FPT) == 0)
PRINT(cp, size);
else
{
// glue together f_p fragments
if (!expchar)
{
// %[fF] or sufficiently short %[gG]
if (expt <= 0)
{
PRINT(zeroes, 1);
if (prec || flags & ALT)
PRINT(decimal_point, 1);
PAD(-expt, zeroes);
// already handled initial 0's
prec += expt;
}
else
{
PRINTANDPAD(cp, dtoaend, lead, zeroes);
cp += lead;
if (prec || flags & ALT)
PRINT(decimal_point, 1);
}
PRINTANDPAD(cp, dtoaend, prec, zeroes);
}
else
{
// %[eE] or sufficiently long %[gG]
if (prec > 1 || flags & ALT)
{
buf[0] = *cp++;
buf[1] = *decimal_point;
PRINT(buf, 2);
PRINT(cp, ndig-1);
PAD(prec - ndig, zeroes);
} else
{
// XeYYY
PRINT(cp, 1);
}
PRINT(expstr, expsize);
}
}
#else
PRINT(cp, size);
#endif
// left-adjusting padding (always blank)
if (flags & LADJUST)
PAD(width - realsz, blanks);
// finally, adjust ret
if (width < realsz)
width = realsz;
if (width > INT_MAX - ret)
goto overflow;
ret += width;
FLUSH(); // copy out the I/O vectors
}
done:
FLUSH();
error:
va_end(orgap);
//if (__sferror(fp))
// ret = -1;
goto finish;
overflow:
errno = ENOMEM;
ret = -1;
finish:
#ifdef FLOATING_POINT
//
// Free the buffers allocated for floating point number conversion.
//
if (dtoaresult)
__freedtoa(dtoaresult);
#endif
//
// Free the buffers allocated for multibyte character string conversion.
//
if (mbstr != 0)
free(mbstr);
//
// If the argument table had to be expanded, free it as well.
//
if (argtable != statargtable)
free(argtable);
//
// Free all contents of ptr_infos, if necessary.
//
for (map<void *, pointer_info *>::iterator pos = ptr_infos.begin(),
end = ptr_infos.end();
pos != end;
++pos)
{
delete pos->second;
}
return ret;
}
//
// 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_LLONG 10
#define T_U_LLONG 11
#define TP_LLONG 12
#define T_DOUBLE 13
#define T_LONG_DOUBLE 14
#define TP_CHAR 15
#define TP_VOID 16
#define T_PTRINT 17
#define TP_PTRINT 18
#define T_SIZEINT 19
#define T_SSIZEINT 20
#define TP_SSIZEINT 21
#define T_MAXINT 22
#define T_MAXUINT 23
#define TP_MAXINT 24
#define T_CHAR 25
#define T_U_CHAR 26
#define T_WINT 27
//
// find_arguments()
//
// 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.
//
// Inputs:
// fmt0 - a pointer to the format string
// ap - the list of arguments
// argtable - a pointer to the initial argument table
// argtablesz - a pointer to a parameter which will be filled with the size
// of the argument table
// vargc - the maximum number of arguments to access
//
static int
find_arguments(const char *fmt0,
va_list ap,
union arg **argtable,
size_t *argtablesiz,
unsigned vargc)
{
const char *fmt; // format string
int ch; // character from fmt
int n, n2; // handy integer (short term usage)
const char *cp; // handy char pointer (short term usage)
int flags; // flags as above
unsigned char *typetable; // table of types
unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
size_t tablesize; // current size of type table
unsigned tablemax; // largest used index in table
unsigned nextarg; // 1-based argument index
unsigned i; // handy unsigned integer
int ret = 0; // return value
wchar_t wc;
mbstate_t ps;
//
// Add an argument type to the table, expanding if necessary.
//
#define ADDTYPE(type) \
((nextarg >= tablesize) ? \
grow_type_table(&typetable, &tablesize, 1 + nextarg) : 0), \
((nextarg > tablemax) ? tablemax = nextarg : 0), \
(typetable != 0 ? typetable[nextarg++] = type : 0)
#define ADDSARG() \
((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \
((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \
((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
((flags&LONGINT) ? ADDTYPE(T_LONG) : \
((flags&SHORTINT) ? ADDTYPE(T_SHORT) : \
((flags&CHARINT) ? ADDTYPE(T_CHAR) : ADDTYPE(T_INT))))))))
#define ADDUARG() \
((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \
((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \
((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : \
((flags&CHARINT) ? ADDTYPE(T_U_CHAR) : ADDTYPE(T_U_INT))))))))
//
// Add * arguments to the type array.
//
#define ADDASTER() \
n2 = 0; \
cp = fmt; \
while (is_digit(*cp)) \
{ \
APPEND_DIGIT(n2, *cp); \
cp++; \
} \
if (*cp == '$') \
{ \
unsigned hold = nextarg; \
nextarg = n2; \
ADDTYPE(T_INT); \
nextarg = hold; \
fmt = ++cp; \
} \
else \
ADDTYPE(T_INT);
fmt = fmt0;
typetable = stattypetable;
tablesize = STATIC_ARG_TBL_SIZE;
tablemax = 0;
nextarg = 1;
memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
memset(&ps, 0, sizeof(mbstate_t));
//
// Scan the format for conversions (`%' character).
//
for (;;)
{
cp = fmt;
while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0)
{
fmt += n;
if (wc == '%')
{
fmt--;
break;
}
}
if (n <= 0)
goto done;
fmt++; // skip over '%'
flags = 0;
//
// Scan the format directive for argument type information.
//
rflag: ch = *fmt++;
reswitch:
switch (ch)
{
case ' ':
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
{
APPEND_DIGIT(n ,ch);
ch = *fmt++;
} while (is_digit(ch));
if (ch == '$')
{
nextarg = n;
goto rflag;
}
goto reswitch;
#ifdef FLOATING_POINT
case 'L':
flags |= LONGDBL;
goto rflag;
#endif
case 'h':
if (*fmt == 'h')
{
fmt++;
flags |= CHARINT;
} else
flags |= SHORTINT;
goto rflag;
case 'l':
if (*fmt == 'l')
{
fmt++;
flags |= LLONGINT;
}
else
flags |= LONGINT;
goto rflag;
case 'q':
flags |= LLONGINT;
goto rflag;
case 't':
flags |= PTRINT;
goto rflag;
case 'z':
flags |= SIZEINT;
goto rflag;
case 'c':
//
// Handle wide character arguments correctly.
//
if (flags & LONGINT)
ADDTYPE(T_WINT);
else
ADDTYPE(T_INT);
break;
case 'D':
flags |= LONGINT;
// FALLTHROUGH
case 'd':
case 'i':
ADDSARG();
break;
#ifdef FLOATING_POINT
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
if (flags & LONGDBL)
ADDTYPE(T_LONG_DOUBLE);
else
ADDTYPE(T_DOUBLE);
break;
#endif // FLOATING_POINT
case 'n':
if (flags & LLONGINT)
ADDTYPE(TP_LLONG);
else if (flags & LONGINT)
ADDTYPE(TP_LONG);
else if (flags & SHORTINT)
ADDTYPE(TP_SHORT);
else if (flags & PTRINT)
ADDTYPE(TP_PTRINT);
else if (flags & SIZEINT)
ADDTYPE(TP_SSIZEINT);
else if (flags & MAXINT)
ADDTYPE(TP_MAXINT);
else
ADDTYPE(TP_INT);
continue; // no output
case 'O':
flags |= LONGINT;
// FALLTHROUGH
case 'o':
ADDUARG();
break;
case 'p':
ADDTYPE(TP_VOID);
break;
case 's':
ADDTYPE(TP_CHAR);
break;
case 'U':
flags |= LONGINT;
// FALLTHROUGH
case 'u':
case 'X':
case 'x':
ADDUARG();
break;
default: // "%?" prints ?, unless ? is NUL
if (ch == '\0')
goto done;
break;
}
}
done:
//
// A NULL type table indicates a malloc() failure.
//
if (typetable == 0)
{
ret = -1;
goto finish;
}
//
// Allocate the argument table, if necessary.
//
if (tablemax >= STATIC_ARG_TBL_SIZE)
{
*argtablesiz = sizeof(union arg) * (tablemax + 1);
*argtable = (union arg *) malloc(*argtablesiz);
if (argtable == 0)
{
ret = -1;
goto finish;
}
}
//
// Fill the argument table based on the entries of the type table.
//
for (i = 1; i <= min(vargc, tablemax); i++)
{
switch (typetable[i])
{
case T_UNUSED:
case T_CHAR:
case T_U_CHAR:
case T_SHORT:
case T_U_SHORT:
case T_INT:
(*argtable)[i].intarg = va_arg(ap, int);
break;
case T_WINT:
(*argtable)[i].wintarg = va_arg(ap, wint_t);
break;
case TP_SHORT:
case TP_INT:
case TP_LONG:
case TP_LLONG:
case TP_CHAR:
case TP_VOID:
case TP_PTRINT:
case TP_SSIZEINT:
case TP_MAXINT:
(*argtable)[i].pvoidarg = va_arg(ap, void *);
break;
case T_U_INT:
(*argtable)[i].uintarg = va_arg(ap, unsigned int);
break;
case T_LONG:
(*argtable)[i].longarg = va_arg(ap, long);
break;
case T_U_LONG:
(*argtable)[i].ulongarg = va_arg(ap, unsigned long);
break;
case T_LLONG:
(*argtable)[i].longlongarg = va_arg(ap, long long);
break;
case T_U_LLONG:
(*argtable)[i].ulonglongarg = va_arg(ap, unsigned long long);
break;
#ifdef FLOATING_POINT
case T_DOUBLE:
(*argtable)[i].doublearg = va_arg(ap, double);
break;
case T_LONG_DOUBLE:
(*argtable)[i].longdoublearg = va_arg(ap, long double);
break;
#endif
case T_PTRINT:
(*argtable)[i].ptrdiffarg = va_arg(ap, ptrdiff_t);
break;
case T_SIZEINT:
(*argtable)[i].sizearg = va_arg(ap, size_t);
break;
case T_SSIZEINT:
(*argtable)[i].ssizearg = va_arg(ap, ssize_t);
break;
}
}
overflow:
errno = ENOMEM;
ret = -1;
finish:
// Free the type table, if necessary.
if (typetable != stattypetable)
free(typetable);
return ret;
}
//
// grow_type_table()
//
// Expand the internal type table to be at least of size minsz.
//
// Inputs:
// typetable - a pointer to the current type table
// tablesize - a pointer to a value holding the current table size
// minsz - the minimum new size of the table
//
// Returns:
// This function returns 0 on success and -1 on failure.
//
static inline int
grow_type_table(unsigned char **typetable, size_t *tablesize, size_t minsz)
{
//
// Allocate the new table on the heap.
//
const size_t newsize = max(*tablesize * 2, minsz);
if (*tablesize == STATIC_ARG_TBL_SIZE)
*typetable = (unsigned char *) malloc(newsize);
else
*typetable = (unsigned char *) realloc(*typetable, newsize);
if (*typetable == 0)
return -1;
//
// Fill the rest of the table with empty entries.
//
memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
*tablesize = newsize;
return 0;
}
#ifdef FLOATING_POINT
//
// Convert the exponent into a string, of the form fNNN, where f is the format
// directive and NNN a digit string.
//
// Inputs:
// p0 - the destination buffer
// exp - the value to convert
// fmtch - the associated format directive
//
// Returns:
// This function returns the length of the converted string.
//
static int
exponent(char *p0, int exp, int fmtch)
{
char *p, *t;
char expbuf[MAXEXPDIG];
p = p0;
*p++ = fmtch;
if (exp < 0)
{
exp = -exp;
*p++ = '-';
}
else
*p++ = '+';
t = expbuf + MAXEXPDIG;
if (exp > 9)
{
do *--t = to_char(exp % 10); while ((exp /= 10) > 9);
*--t = to_char(exp);
for (; t < expbuf + MAXEXPDIG; *p++ = *t++)
/* nothing */;
}
else
{
//
// Exponents for decimal floating point conversions
// (%[eEgG]) must be at least two characters long,
// whereas exponents for hexadecimal conversions can
// be only one character long.
//
if (fmtch == 'e' || fmtch == 'E')
*p++ = '0';
*p++ = to_char(exp);
}
return (p - p0);
}
#endif // FLOATING_POINT