diff options
Diffstat (limited to 'lib/libc/stdio/xprintf.c')
-rw-r--r-- | lib/libc/stdio/xprintf.c | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/lib/libc/stdio/xprintf.c b/lib/libc/stdio/xprintf.c new file mode 100644 index 0000000..b00b4cb --- /dev/null +++ b/lib/libc/stdio/xprintf.c @@ -0,0 +1,690 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * Copyright (c) 1990, 1993 + * 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. + * + * $FreeBSD$ + */ + +#include "namespace.h" +#include <err.h> +#include <sys/types.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <locale.h> +#include <stdint.h> +#include <assert.h> +#include <stdarg.h> +#include <namespace.h> +#include <string.h> +#include <wchar.h> +#include "un-namespace.h" + +#include "local.h" +#include "printf.h" +#include "fvwrite.h" + +int __use_xprintf = -1; + +/* private stuff -----------------------------------------------------*/ + +union arg { + int intarg; + long longarg; + intmax_t intmaxarg; +#ifndef NO_FLOATING_POINT + double doublearg; + long double longdoublearg; +#endif + wint_t wintarg; + char *pchararg; + wchar_t *pwchararg; + void *pvoidarg; +}; + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) (((unsigned)to_digit(c)) <= 9) + +/* various globals ---------------------------------------------------*/ + +const char __lowercase_hex[17] = "0123456789abcdef?"; /*lint !e784 */ +const char __uppercase_hex[17] = "0123456789ABCDEF?"; /*lint !e784 */ + +#define PADSIZE 16 +static char blanks[PADSIZE] = + {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +static char zeroes[PADSIZE] = + {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +/* printing and padding functions ------------------------------------*/ + +#define NIOV 8 + +struct __printf_io { + FILE *fp; + struct __suio uio; + struct __siov iov[NIOV]; + struct __siov *iovp; +}; + +static void +__printf_init(struct __printf_io *io) +{ + + io->uio.uio_iov = io->iovp = &io->iov[0]; + io->uio.uio_resid = 0; + io->uio.uio_iovcnt = 0; +} + +void +__printf_flush(struct __printf_io *io) +{ + + __sfvwrite(io->fp, &io->uio); + __printf_init(io); +} + +int +__printf_puts(struct __printf_io *io, const void *ptr, int len) +{ + + + if (io->fp->_flags & __SERR) + return (0); + if (len == 0) + return (0); + io->iovp->iov_base = __DECONST(void *, ptr); + io->iovp->iov_len = len; + io->uio.uio_resid += len; + io->iovp++; + io->uio.uio_iovcnt++; + if (io->uio.uio_iovcnt >= NIOV) + __printf_flush(io); + return (len); +} + +int +__printf_pad(struct __printf_io *io, int howmany, int zero) +{ + int n; + const char *with; + int ret = 0; + + if (zero) + with = zeroes; + else + with = blanks; + + if ((n = (howmany)) > 0) { + while (n > PADSIZE) { + ret += __printf_puts(io, with, PADSIZE); + n -= PADSIZE; + } + ret += __printf_puts(io, with, n); + } + return (ret); +} + +int +__printf_out(struct __printf_io *io, const struct printf_info *pi, const void *ptr, int len) +{ + int ret = 0; + + if ((!pi->left) && pi->width > len) + ret += __printf_pad(io, pi->width - len, pi->pad == '0'); + ret += __printf_puts(io, ptr, len); + if (pi->left && pi->width > len) + ret += __printf_pad(io, pi->width - len, pi->pad == '0'); + return (ret); +} + + +/* percent handling -------------------------------------------------*/ + +static int +__printf_arginfo_pct(const struct printf_info *pi __unused, size_t n __unused, int *argt __unused) +{ + + return (0); +} + +static int +__printf_render_pct(struct __printf_io *io, const struct printf_info *pi __unused, const void *const *arg __unused) +{ + + return (__printf_puts(io, "%", 1)); +} + +/* 'n' ---------------------------------------------------------------*/ + +static int +__printf_arginfo_n(const struct printf_info *pi, size_t n, int *argt) +{ + + assert(n >= 1); + argt[0] = PA_POINTER; + return (1); +} + +/* + * This is a printf_render so that all output has been flushed before it + * gets called. + */ + +static int +__printf_render_n(FILE *io __unused, const struct printf_info *pi, const void *const *arg) +{ + + if (pi->is_char) + **((signed char **)arg[0]) = (signed char)pi->sofar; + else if (pi->is_short) + **((short **)arg[0]) = (short)pi->sofar; + else if (pi->is_long) + **((long **)arg[0]) = pi->sofar; + else if (pi->is_long_double) + **((long long **)arg[0]) = pi->sofar; + else if (pi->is_intmax) + **((intmax_t **)arg[0]) = pi->sofar; + else if (pi->is_ptrdiff) + **((ptrdiff_t **)arg[0]) = pi->sofar; + else if (pi->is_quad) + **((quad_t **)arg[0]) = pi->sofar; + else if (pi->is_size) + **((size_t **)arg[0]) = pi->sofar; + else + **((int **)arg[0]) = pi->sofar; + + return (0); +} + +/* table -------------------------------------------------------------*/ + +/*lint -esym(785, printf_tbl) */ +static struct { + printf_arginfo_function *arginfo; + printf_function *gnurender; + printf_render *render; +} printf_tbl[256] = { + ['%'] = { __printf_arginfo_pct, NULL, __printf_render_pct }, + ['A'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['C'] = { __printf_arginfo_chr, NULL, __printf_render_chr }, + ['E'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['F'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['G'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['S'] = { __printf_arginfo_str, NULL, __printf_render_str }, + ['X'] = { __printf_arginfo_int, NULL, __printf_render_int }, + ['a'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['c'] = { __printf_arginfo_chr, NULL, __printf_render_chr }, + ['d'] = { __printf_arginfo_int, NULL, __printf_render_int }, + ['e'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['f'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['g'] = { __printf_arginfo_float, NULL, __printf_render_float }, + ['i'] = { __printf_arginfo_int, NULL, __printf_render_int }, + ['n'] = { __printf_arginfo_n, __printf_render_n, NULL }, + ['o'] = { __printf_arginfo_int, NULL, __printf_render_int }, + ['p'] = { __printf_arginfo_ptr, NULL, __printf_render_ptr }, + ['q'] = { __printf_arginfo_int, NULL, __printf_render_int }, + ['s'] = { __printf_arginfo_str, NULL, __printf_render_str }, + ['u'] = { __printf_arginfo_int, NULL, __printf_render_int }, + ['x'] = { __printf_arginfo_int, NULL, __printf_render_int }, +}; + + +static int +__v2printf(FILE *fp, const char *fmt0, unsigned pct, va_list ap) +{ + struct printf_info *pi, *pil; + const char *fmt; + int ch; + struct printf_info pia[pct + 10]; + int argt[pct + 10]; + union arg args[pct + 10]; + int nextarg; + int maxarg; + int ret = 0; + int n; + struct __printf_io io; + + __printf_init(&io); + io.fp = fp; + + fmt = fmt0; + maxarg = 0; + nextarg = 1; + memset(argt, 0, sizeof argt); + for (pi = pia; ; pi++) { + memset(pi, 0, sizeof *pi); + pil = pi; + if (*fmt == '\0') + break; + pil = pi + 1; + pi->prec = -1; + pi->pad = ' '; + pi->begin = pi->end = fmt; + while (*fmt != '\0' && *fmt != '%') + pi->end = ++fmt; + if (*fmt == '\0') + break; + fmt++; + for (;;) { + pi->spec = *fmt; + switch (pi->spec) { + case ' ': + /*- + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (pi->showsign == 0) + pi->showsign = ' '; + fmt++; + continue; + case '#': + pi->alt = 1; + fmt++; + continue; + case '.': + pi->prec = 0; + fmt++; + if (*fmt == '*') { + fmt++; + pi->get_prec = nextarg; + argt[nextarg++] = PA_INT; + continue; + } + while (*fmt != '\0' && is_digit(*fmt)) { + pi->prec *= 10; + pi->prec += to_digit(*fmt); + fmt++; + } + continue; + case '-': + pi->left = 1; + fmt++; + continue; + case '+': + pi->showsign = '+'; + fmt++; + continue; + case '*': + fmt++; + pi->get_width = nextarg; + argt[nextarg++] = PA_INT; + continue; + case '%': + fmt++; + break; + case '\'': + pi->group = 1; + fmt++; + continue; + case '0': + /*- + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + pi->pad = '0'; + fmt++; + continue; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + n = 0; + while (*fmt != '\0' && is_digit(*fmt)) { + n *= 10; + n += to_digit(*fmt); + fmt++; + } + if (*fmt == '$') { + if (nextarg > maxarg) + maxarg = nextarg; + nextarg = n; + fmt++; + } else + pi->width = n; + continue; + case 'D': + case 'O': + case 'U': + pi->spec += ('a' - 'A'); + pi->is_intmax = 0; + if (pi->is_long_double || pi->is_quad) { + pi->is_long = 0; + pi->is_long_double = 1; + } else { + pi->is_long = 1; + pi->is_long_double = 0; + } + fmt++; + break; + case 'j': + pi->is_intmax = 1; + fmt++; + continue; + case 'q': + pi->is_long = 0; + pi->is_quad = 1; + fmt++; + continue; + case 'L': + pi->is_long_double = 1; + fmt++; + continue; + case 'h': + fmt++; + if (*fmt == 'h') { + fmt++; + pi->is_char = 1; + } else { + pi->is_short = 1; + } + continue; + case 'l': + fmt++; + if (*fmt == 'l') { + fmt++; + pi->is_long_double = 1; + pi->is_quad = 0; + } else { + pi->is_quad = 0; + pi->is_long = 1; + } + continue; + case 't': + pi->is_ptrdiff = 1; + fmt++; + continue; + case 'z': + pi->is_size = 1; + fmt++; + continue; + default: + fmt++; + break; + } + if (printf_tbl[pi->spec].arginfo == NULL) + errx(1, "arginfo[%c] = NULL", pi->spec); + ch = printf_tbl[pi->spec].arginfo( + pi, __PRINTFMAXARG, &argt[nextarg]); + if (ch > 0) + pi->arg[0] = &args[nextarg]; + if (ch > 1) + pi->arg[1] = &args[nextarg + 1]; + nextarg += ch; + break; + } + } + if (nextarg > maxarg) + maxarg = nextarg; +#if 0 + fprintf(stderr, "fmt0 <%s>\n", fmt0); + fprintf(stderr, "pil %p\n", pil); +#endif + for (ch = 1; ch < maxarg; ch++) { +#if 0 + fprintf(stderr, "arg %d %x\n", ch, argt[ch]); +#endif + switch(argt[ch]) { + case PA_CHAR: + args[ch].intarg = (char)va_arg (ap, int); + break; + case PA_INT: + args[ch].intarg = va_arg (ap, int); + break; + case PA_INT | PA_FLAG_SHORT: + args[ch].intarg = (short)va_arg (ap, int); + break; + case PA_INT | PA_FLAG_LONG: + args[ch].longarg = va_arg (ap, long); + break; + case PA_INT | PA_FLAG_INTMAX: + args[ch].intmaxarg = va_arg (ap, intmax_t); + break; + case PA_INT | PA_FLAG_QUAD: + args[ch].intmaxarg = va_arg (ap, quad_t); + break; + case PA_INT | PA_FLAG_LONG_LONG: + args[ch].intmaxarg = va_arg (ap, long long); + break; + case PA_INT | PA_FLAG_SIZE: + args[ch].intmaxarg = va_arg (ap, size_t); + break; + case PA_INT | PA_FLAG_PTRDIFF: + args[ch].intmaxarg = va_arg (ap, ptrdiff_t); + break; + case PA_WCHAR: + args[ch].wintarg = va_arg (ap, wint_t); + break; + case PA_POINTER: + args[ch].pvoidarg = va_arg (ap, void *); + break; + case PA_STRING: + args[ch].pchararg = va_arg (ap, char *); + break; + case PA_WSTRING: + args[ch].pwchararg = va_arg (ap, wchar_t *); + break; + case PA_DOUBLE: +#ifndef NO_FLOATING_POINT + args[ch].doublearg = va_arg (ap, double); +#endif + break; + case PA_DOUBLE | PA_FLAG_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT + args[ch].longdoublearg = va_arg (ap, long double); +#endif + break; + default: + errx(1, "argtype = %x (fmt = \"%s\")\n", + argt[ch], fmt0); + } + } + for (pi = pia; pi < pil; pi++) { +#if 0 + fprintf(stderr, "pi %p", pi); + fprintf(stderr, " spec '%c'", pi->spec); + fprintf(stderr, " args %d", + ((uintptr_t)pi->arg[0] - (uintptr_t)args) / sizeof args[0]); + if (pi->width) fprintf(stderr, " width %d", pi->width); + if (pi->pad) fprintf(stderr, " pad 0x%x", pi->pad); + if (pi->left) fprintf(stderr, " left"); + if (pi->showsign) fprintf(stderr, " showsign"); + if (pi->prec != -1) fprintf(stderr, " prec %d", pi->prec); + if (pi->is_char) fprintf(stderr, " char"); + if (pi->is_short) fprintf(stderr, " short"); + if (pi->is_long) fprintf(stderr, " long"); + if (pi->is_long_double) fprintf(stderr, " long_double"); + fprintf(stderr, "\n"); + fprintf(stderr, "\t\"%.*s\"\n", pi->end - pi->begin, pi->begin); +#endif + if (pi->get_width) { + pi->width = args[pi->get_width].intarg; + /*- + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + if (pi->width < 0) { + pi->left = 1; + pi->width = -pi->width; + } + } + if (pi->get_prec) + pi->prec = args[pi->get_prec].intarg; + ret += __printf_puts(&io, pi->begin, pi->end - pi->begin); + if (printf_tbl[pi->spec].gnurender != NULL) { + __printf_flush(&io); + pi->sofar = ret; + ret += printf_tbl[pi->spec].gnurender( + fp, pi, (const void *)pi->arg); + } else if (printf_tbl[pi->spec].render != NULL) { + pi->sofar = ret; + n = printf_tbl[pi->spec].render( + &io, pi, (const void *)pi->arg); + if (n < 0) + io.fp->_flags |= __SERR; + else + ret += n; + } else if (pi->begin == pi->end) + errx(1, "render[%c] = NULL", *fmt); + } + __printf_flush(&io); + return (ret); +} + +extern int __fflush(FILE *fp); + +/* + * 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 +__v3printf(FILE *fp, const char *fmt, int pct, va_list ap) +{ + int ret; + FILE fake = FAKE_FILE; + unsigned char buf[BUFSIZ]; + + /* copy the important variables */ + fake._flags = fp->_flags & ~__SNBF; + fake._file = fp->_file; + fake._cookie = fp->_cookie; + fake._write = fp->_write; + fake._orientation = fp->_orientation; + fake._mbstate = fp->_mbstate; + + /* 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 */ + ret = __v2printf(&fake, fmt, pct, ap); + if (ret >= 0 && __fflush(&fake)) + ret = EOF; + if (fake._flags & __SERR) + fp->_flags |= __SERR; + return (ret); +} + +int +__xvprintf(FILE *fp, const char *fmt0, va_list ap) +{ + unsigned u; + const char *p; + + /* Count number of '%' signs handling double '%' signs */ + for (p = fmt0, u = 0; *p; p++) { + if (*p != '%') + continue; + u++; + if (p[1] == '%') + p++; + } + + /* optimise fprintf(stderr) (and other unbuffered Unix files) */ + if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && + fp->_file >= 0) + return (__v3printf(fp, fmt0, u, ap)); + else + return (__v2printf(fp, fmt0, u, ap)); +} + +/* extending ---------------------------------------------------------*/ + +int +register_printf_function(int spec, printf_function *render, printf_arginfo_function *arginfo) +{ + + if (spec > 255 || spec < 0) + return (-1); + printf_tbl[spec].gnurender = render; + printf_tbl[spec].arginfo = arginfo; + __use_xprintf = 1; + return (0); +} + +int +register_printf_render(int spec, printf_render *render, printf_arginfo_function *arginfo) +{ + + if (spec > 255 || spec < 0) + return (-1); + printf_tbl[spec].render = render; + printf_tbl[spec].arginfo = arginfo; + __use_xprintf = 1; + return (0); +} + +int +register_printf_render_std(const char *specs) +{ + + for (; *specs != '\0'; specs++) { + switch (*specs) { + case 'H': + register_printf_render(*specs, + __printf_render_hexdump, + __printf_arginfo_hexdump); + break; + case 'M': + register_printf_render(*specs, + __printf_render_errno, + __printf_arginfo_errno); + break; + case 'Q': + register_printf_render(*specs, + __printf_render_quote, + __printf_arginfo_quote); + break; + case 'T': + register_printf_render(*specs, + __printf_render_time, + __printf_arginfo_time); + break; + case 'V': + register_printf_render(*specs, + __printf_render_vis, + __printf_arginfo_vis); + break; + default: + return (-1); + } + } + return (0); +} + |