diff options
Diffstat (limited to 'libc/stdio/vfprintf.cpp')
-rw-r--r-- | libc/stdio/vfprintf.cpp | 667 |
1 files changed, 2 insertions, 665 deletions
diff --git a/libc/stdio/vfprintf.cpp b/libc/stdio/vfprintf.cpp index fe2e338e7..a60b862ba 100644 --- a/libc/stdio/vfprintf.cpp +++ b/libc/stdio/vfprintf.cpp @@ -33,59 +33,7 @@ #define CHAR_TYPE char #define FUNCTION_NAME __vfprintf - -#include <sys/mman.h> -#include <sys/types.h> - -#include <errno.h> -#include <float.h> -#include <langinfo.h> -#include <limits.h> -#include <locale.h> -#include <math.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <wchar.h> - -#include "fvwrite.h" -#include "gdtoa.h" -#include "local.h" - -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; - char* pchararg; - signed char* pschararg; - short* pshortarg; - int* pintarg; - long* plongarg; - long long* plonglongarg; - ptrdiff_t* pptrdiffarg; - ssize_t* pssizearg; - intmax_t* pintmaxarg; - double doublearg; - long double longdoublearg; - wint_t wintarg; - wchar_t* pwchararg; -}; - -static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz); -static int __grow_type_table(unsigned char** typetable, int* tablesize); +#include "printf_common.h" /* * Flush out all the vectors defined by the given uio, @@ -105,35 +53,6 @@ static int __sprint(FILE* fp, struct __suio* uio) { } /* - * Helper function for `fprintf to unbuffered unix file': creates a - * temporary buffer. We only work on write-only files; this avoids - * worries about ungetc buffers and so forth. - */ -static int __sbprintf(FILE* fp, const CHAR_TYPE* fmt, va_list ap) { - FILE fake; - struct __sfileext fakeext; - unsigned char buf[BUFSIZ]; - - _FILEEXT_SETUP(&fake, &fakeext); - /* copy the important variables */ - fake._flags = fp->_flags & ~__SNBF; - fake._file = fp->_file; - fake._cookie = fp->_cookie; - fake._write = fp->_write; - - /* set up the buffer */ - fake._bf._base = fake._p = buf; - fake._bf._size = fake._w = sizeof(buf); - fake._lbfsize = 0; /* not actually used, but Just In Case */ - - /* do the work, then copy any error status */ - int ret = FUNCTION_NAME(&fake, fmt, ap); - if (ret >= 0 && __sflush(&fake)) ret = EOF; - if (fake._flags & __SERR) fp->_flags |= __SERR; - return (ret); -} - -/* * Convert a wide character string argument for the %ls format to a multibyte * string representation. If not -1, prec specifies the maximum number of * bytes to output, and also means that we can't assume that the wide char @@ -185,72 +104,7 @@ static char* __wcsconv(wchar_t* wcsarg, int prec) { return (convbuf); } -#define DEFPREC 6 - -#define to_digit(c) ((c) - '0') -#define is_digit(c) ((unsigned)to_digit(c) <= 9) -#define to_char(n) ((CHAR_TYPE)((n) + '0')) - -template <typename CharT> -static int exponent(CharT* p0, int exp, int fmtch) { - CharT* p = p0; - *p++ = fmtch; - if (exp < 0) { - exp = -exp; - *p++ = '-'; - } else { - *p++ = '+'; - } - - CharT expbuf[MAXEXPDIG]; - CharT* 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); -} - -/* - * 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 8 /* Size of static argument table. */ - -/* - * 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) */ - -int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, __va_list ap) { +int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) { int ch; /* character from fmt */ int n, n2; /* handy integers (short term usage) */ CHAR_TYPE* cp; /* handy char pointer (short term usage) */ @@ -337,23 +191,6 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, __va_list ap) { 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 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) #define FLUSH() \ do { \ if (uio.uio_resid && __sprint(fp, &uio)) goto error; \ @@ -361,87 +198,6 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, __va_list ap) { iovp = iov; \ } while (0) - /* - * To extend shorts properly, we need both signed and unsigned - * argument extraction methods. - */ -#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))) -#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) - - /* - * 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)) { \ - APPEND_DIGIT(n2, *cp); \ - cp++; \ - } \ - if (*cp == '$') { \ - int hold = nextarg; \ - if (argtable == NULL) { \ - argtable = statargtable; \ - if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { \ - ret = -1; \ - goto error; \ - } \ - } \ - 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). - */ -#define GETARG(type) \ - ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type))) - _SET_ORIENTATION(fp, -1); // Writing "" to a read only file returns EOF, not 0. @@ -1009,422 +765,3 @@ finish: } 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 -#define TP_WCHAR 28 - -/* - * 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 mmap-ed one if it overflows (malloc cannot be - * used since we are attempting to make snprintf thread safe, and alloca is - * problematic since we have nested functions..) - */ -static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, - size_t* argtablesiz) { - int ch; /* character from fmt */ - int n, n2; /* handy integer (short term usage) */ - 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 */ - int ret = 0; /* return value */ - - /* - * Add an argument type to the table, expanding if necessary. - */ -#define ADDTYPE(type) \ - ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \ - (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type) - -#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 == '$') { \ - int hold = nextarg; \ - nextarg = n2; \ - ADDTYPE(T_INT); \ - nextarg = hold; \ - fmt = ++cp; \ - } else { \ - ADDTYPE(T_INT); \ - } - CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0); - CHAR_TYPE* cp; - typetable = stattypetable; - tablesize = STATIC_ARG_TBL_SIZE; - tablemax = 0; - nextarg = 1; - memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); - - /* - * Scan the format for conversions (`%' character). - */ - for (;;) { - for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; - if (ch == '\0') goto done; - fmt++; /* skip over '%' */ - - flags = 0; - - 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; - case 'L': - flags |= LONGDBL; - goto rflag; - 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; - case 'C': - flags |= LONGINT; - /*FALLTHROUGH*/ - case 'c': - if (flags & LONGINT) - ADDTYPE(T_WINT); - else - ADDTYPE(T_INT); - break; - case 'D': - flags |= LONGINT; - /*FALLTHROUGH*/ - case 'd': - case 'i': - ADDSARG(); - break; - 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; -#ifndef NO_PRINTF_PERCENT_N - 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 */ -#endif /* NO_PRINTF_PERCENT_N */ - case 'O': - flags |= LONGINT; - /*FALLTHROUGH*/ - case 'o': - ADDUARG(); - break; - case 'p': - ADDTYPE(TP_VOID); - break; - case 'S': - flags |= LONGINT; - /*FALLTHROUGH*/ - case 's': - ADDTYPE((flags & LONGINT) ? TP_WCHAR : 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: - /* - * Build the argument table. - */ - if (tablemax >= STATIC_ARG_TBL_SIZE) { - *argtablesiz = sizeof(union arg) * (tablemax + 1); - *argtable = static_cast<arg*>(mmap(NULL, *argtablesiz, - PROT_WRITE | PROT_READ, - MAP_ANON | MAP_PRIVATE, -1, 0)); - if (*argtable == MAP_FAILED) return -1; - } - -#if 0 - /* XXX is this required? */ - (*argtable)[0].intarg = 0; -#endif - for (n = 1; n <= tablemax; n++) { - switch (typetable[n]) { - case T_UNUSED: - case T_CHAR: - case T_U_CHAR: - case T_SHORT: - case T_U_SHORT: - case T_INT: - (*argtable)[n].intarg = va_arg(ap, int); - break; - case TP_SHORT: - (*argtable)[n].pshortarg = va_arg(ap, short*); - break; - case T_U_INT: - (*argtable)[n].uintarg = va_arg(ap, unsigned int); - break; - case TP_INT: - (*argtable)[n].pintarg = va_arg(ap, int*); - break; - case T_LONG: - (*argtable)[n].longarg = va_arg(ap, long); - break; - case T_U_LONG: - (*argtable)[n].ulongarg = va_arg(ap, unsigned long); - break; - case TP_LONG: - (*argtable)[n].plongarg = va_arg(ap, long*); - break; - case T_LLONG: - (*argtable)[n].longlongarg = va_arg(ap, long long); - break; - case T_U_LLONG: - (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long); - break; - case TP_LLONG: - (*argtable)[n].plonglongarg = va_arg(ap, long long*); - break; - case T_DOUBLE: - (*argtable)[n].doublearg = va_arg(ap, double); - break; - case T_LONG_DOUBLE: - (*argtable)[n].longdoublearg = va_arg(ap, long double); - break; - case TP_CHAR: - (*argtable)[n].pchararg = va_arg(ap, char*); - break; - case TP_VOID: - (*argtable)[n].pvoidarg = va_arg(ap, void*); - break; - case T_PTRINT: - (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t); - break; - case TP_PTRINT: - (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*); - break; - case T_SIZEINT: - (*argtable)[n].sizearg = va_arg(ap, size_t); - break; - case T_SSIZEINT: - (*argtable)[n].ssizearg = va_arg(ap, ssize_t); - break; - case TP_SSIZEINT: - (*argtable)[n].pssizearg = va_arg(ap, ssize_t*); - break; - case T_MAXINT: - (*argtable)[n].intmaxarg = va_arg(ap, intmax_t); - break; - case T_MAXUINT: - (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t); - break; - case TP_MAXINT: - (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*); - break; - case T_WINT: - (*argtable)[n].wintarg = va_arg(ap, wint_t); - break; - case TP_WCHAR: - (*argtable)[n].pwchararg = va_arg(ap, wchar_t*); - break; - } - } - goto finish; - -overflow: - errno = ENOMEM; - ret = -1; - -finish: - if (typetable != NULL && typetable != stattypetable) { - munmap(typetable, *argtablesiz); - typetable = NULL; - } - return (ret); -} - -/* - * Increase the size of the type table. - */ -static int __grow_type_table(unsigned char** typetable, int* tablesize) { - unsigned char* old_table = *typetable; - int new_size = *tablesize * 2; - - if (new_size < getpagesize()) new_size = getpagesize(); - - if (*tablesize == STATIC_ARG_TBL_SIZE) { - *typetable = static_cast<unsigned char*>(mmap(NULL, new_size, - PROT_WRITE | PROT_READ, - MAP_ANON | MAP_PRIVATE, -1, 0)); - if (*typetable == MAP_FAILED) return -1; - bcopy(old_table, *typetable, *tablesize); - } else { - unsigned char* new_table = static_cast<unsigned char*>(mmap(NULL, new_size, - PROT_WRITE | PROT_READ, - MAP_ANON | MAP_PRIVATE, -1, 0)); - if (new_table == MAP_FAILED) return -1; - memmove(new_table, *typetable, *tablesize); - munmap(*typetable, *tablesize); - *typetable = new_table; - } - memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize)); - - *tablesize = new_size; - return 0; -} |