diff options
-rw-r--r-- | include/Makefile | 2 | ||||
-rw-r--r-- | include/printf.h | 155 | ||||
-rw-r--r-- | lib/libc/stdio/Makefile.inc | 3 | ||||
-rw-r--r-- | lib/libc/stdio/vfprintf.c | 7 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf.c | 674 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf_float.c | 425 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf_hexdump.c | 97 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf_int.c | 471 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf_str.c | 187 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf_time.c | 114 | ||||
-rw-r--r-- | lib/libc/stdio/xprintf_vis.c | 76 |
11 files changed, 2210 insertions, 1 deletions
diff --git a/include/Makefile b/include/Makefile index 89554f0..e488788 100644 --- a/include/Makefile +++ b/include/Makefile @@ -14,7 +14,7 @@ INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h _ctype.h ctype.h \ locale.h malloc.h memory.h monetary.h mpool.h mqueue.h \ ndbm.h netconfig.h \ netdb.h nl_types.h nlist.h nss.h nsswitch.h objformat.h paths.h \ - proc_service.h pthread.h \ + printf.h proc_service.h pthread.h \ pthread_np.h pwd.h ranlib.h readpassphrase.h regex.h regexp.h \ resolv.h runetype.h search.h setjmp.h sgtty.h \ signal.h stab.h \ diff --git a/include/printf.h b/include/printf.h new file mode 100644 index 0000000..d16ad6e --- /dev/null +++ b/include/printf.h @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$ + */ + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +union arg; + +/* + * The API defined by glibc allows a renderer to take multiple arguments + * This is obviously usable for things like (ptr+len) pairs etc. + * But the do not actually provide support for it at the end of the day, + * they offer only one argument to the arginfo function, but do accept + * >1 returns, although the do not check the types of those arguments + * argument + * Be compatible for now. + */ +#define __PRINTFMAXARG 2 + +struct printf_info { + /* GLIBC compatible */ + int prec; + int width; + wchar_t spec; + unsigned is_long_double; + unsigned is_char; + unsigned is_short; + unsigned is_long; + unsigned alt; + unsigned space; + unsigned left; + unsigned showsign; + unsigned group; + unsigned extra; + unsigned wide; + wchar_t pad; + + /* FreeBSD extensions */ + + unsigned is_quad; + unsigned is_intmax; + unsigned is_ptrdiff; + unsigned is_size; + + /* private */ + int sofar; + unsigned get_width; + unsigned get_prec; + const char *begin; + const char *end; + void *arg[__PRINTFMAXARG]; +}; + +enum { + PA_INT = (1 << 0), /* int */ + PA_CHAR = (1 << 1), /* int, cast to char */ + PA_WCHAR = (1 << 2), /* wide char */ + PA_STRING = (1 << 3), /* const char * (with '\0') */ + PA_WSTRING = (1 << 4), /* const wchar_t * */ + PA_POINTER = (1 << 5), /* void * */ + PA_FLOAT = (1 << 6), /* float */ + PA_DOUBLE = (1 << 7) /* double */ +}; + +#define PA_FLAG_MASK 0xff0000 +#define PA_FLAG_LONG_LONG (1 << 16) +#define PA_FLAG_LONG (1 << 17) +#define PA_FLAG_SHORT (1 << 18) +#define PA_FLAG_PTR (1 << 19) +#define PA_FLAG_QUAD (1 << 20) +#define PA_FLAG_INTMAX (1 << 21) +#define PA_FLAG_SIZE (1 << 22) +#define PA_FLAG_PTRDIFF (1 << 23) +#define PA_FLAG_LONG_DOUBLE PA_FLAG_LONG_LONG + +typedef int printf_arginfo_function(const struct printf_info *, size_t, int *); +typedef int printf_function(FILE *, const struct printf_info *, const void *const *); + +/* FreeBSD extension */ +struct __printf_io; +typedef int printf_render(struct __printf_io *, const struct printf_info *, const void *const *); + +/* vprintf.c */ +extern const char __lowercase_hex[17]; +extern const char __uppercase_hex[17]; + +void __printf_flush(struct __printf_io *io); +int __printf_puts(struct __printf_io *io, const void *ptr, int len); +int __printf_pad(struct __printf_io *io, int n, int zero); +int __printf_out(struct __printf_io *io, const struct printf_info *pi, const void *ptr, int len); + +int __xvprintf(FILE *fp, const char *fmt0, va_list ap); +extern int __use_xprintf; + +/* GLIBC compat */ +int register_printf_function(int spec, printf_function *render, printf_arginfo_function *arginfo); + +/* FreeBSD */ +int register_printf_render(int spec, printf_render *render, printf_arginfo_function *arginfo); +int register_printf_render_std(const unsigned char *specs); + +/* vprintf_float.c */ +printf_arginfo_function __printf_arginfo_float; +printf_render __printf_render_float; + +/* vprintf_int.c */ +printf_arginfo_function __printf_arginfo_ptr; +printf_arginfo_function __printf_arginfo_int; +printf_render __printf_render_ptr; +printf_render __printf_render_int; + +/* vprintf_str.c */ +printf_arginfo_function __printf_arginfo_chr; +printf_render __printf_render_chr; +printf_arginfo_function __printf_arginfo_str; +printf_render __printf_render_str; + +/* vprintf_time.c */ +printf_arginfo_function __printf_arginfo_time; +printf_render __printf_render_time; + +/* vprintf_hexdump.c */ +printf_arginfo_function __printf_arginfo_hexdump; +printf_render __printf_render_hexdump; + +/* vprintf_vis.c */ +printf_arginfo_function __printf_arginfo_vis; +printf_render __printf_render_vis; + +#endif /* !_PRINTF_H */ diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index f8a90f8..495c85f 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -23,6 +23,9 @@ SRCS+= _flock_stub.c asprintf.c clrerr.c fclose.c fdopen.c feof.c ferror.c \ vswprintf.c vswscanf.c vwprintf.c vwscanf.c wbuf.c wprintf.c wscanf.c \ wsetup.c +SRCS+= xprintf.c xprintf_float.c xprintf_int.c xprintf_str.c +SRCS+= xprintf_hexdump.c xprintf_time.c xprintf_vis.c + MAN+= fclose.3 ferror.3 fflush.3 fgetln.3 fgets.3 fgetwln.3 fgetws.3 \ flockfile.3 \ fopen.3 fputs.3 \ diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c index 25ac2d7..34e6bf0 100644 --- a/lib/libc/stdio/vfprintf.c +++ b/lib/libc/stdio/vfprintf.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <wchar.h> +#include <printf.h> #include <stdarg.h> #include "un-namespace.h" @@ -466,6 +467,12 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) char sign; /* sign prefix (' ', '+', '-', or \0) */ char thousands_sep; /* locale specific thousands separator */ const char *grouping; /* locale specific numeric grouping rules */ + + if (__use_xprintf == 0 && getenv("USE_XPRINTF")) + __use_xprintf = 1; + if (__use_xprintf > 0) + return (__xvprintf(fp, fmt0, ap)); + #ifndef NO_FLOATING_POINT /* * We can decompose the printed representation of floating diff --git a/lib/libc/stdio/xprintf.c b/lib/libc/stdio/xprintf.c new file mode 100644 index 0000000..3b27782 --- /dev/null +++ b/lib/libc/stdio/xprintf.c @@ -0,0 +1,674 @@ +/*- + * 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 "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, const 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: + args[ch].doublearg = va_arg (ap, double); + break; + case PA_DOUBLE | PA_FLAG_LONG_DOUBLE: + args[ch].longdoublearg = va_arg (ap, long double); + 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; + 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._extra = fp->_extra; + + /* 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 unsigned char *specs) +{ + + for (; *specs != '\0'; specs++) { + switch (*specs) { + case 'H': + register_printf_render(*specs, + __printf_render_hexdump, + __printf_arginfo_hexdump); + 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); +} + diff --git a/lib/libc/stdio/xprintf_float.c b/lib/libc/stdio/xprintf_float.c new file mode 100644 index 0000000..b719aac --- /dev/null +++ b/lib/libc/stdio/xprintf_float.c @@ -0,0 +1,425 @@ +/*- + * 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 <stdio.h> +#include <wchar.h> +#include <assert.h> +#include <locale.h> +#include <limits.h> + +#define dtoa __dtoa +#define freedtoa __freedtoa + +#include <float.h> +#include <math.h> +#include "gdtoa.h" +#include "floatio.h" +#include "printf.h" +#include <un-namespace.h> + +/* + * 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 DEFPREC 6 /* Default FP precision */ + + +/* various globals ---------------------------------------------------*/ + + +/* padding function---------------------------------------------------*/ + +#define PRINTANDPAD(p, ep, len, with) do { \ + n2 = (ep) - (p); \ + if (n2 > (len)) \ + n2 = (len); \ + if (n2 > 0) \ + ret += __printf_puts(io, (p), n2); \ + ret += __printf_pad(io, (len) - (n2 > 0 ? n2 : 0), (with)); \ +} while(0) + +/* misc --------------------------------------------------------------*/ + +#define to_char(n) ((n) + '0') + +static int +exponent(char *p0, int expo, int fmtch) +{ + char *p, *t; + char expbuf[MAXEXPDIG]; + + p = p0; + *p++ = fmtch; + if (expo < 0) { + expo = -expo; + *p++ = '-'; + } + else + *p++ = '+'; + t = expbuf + MAXEXPDIG; + if (expo > 9) { + do { + *--t = to_char(expo % 10); + } while ((expo /= 10) > 9); + *--t = to_char(expo); + for (; t < expbuf + MAXEXPDIG; *p++ = *t++) + ; + } + 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(expo); + } + return (p - p0); +} + +/* 'f' ---------------------------------------------------------------*/ + +int +__printf_arginfo_float(const struct printf_info *pi, size_t n, int *argt) +{ + assert (n > 0); + argt[0] = PA_DOUBLE; + if (pi->is_long_double) + argt[0] |= PA_FLAG_LONG_DOUBLE; + return (1); +} + +/* + * 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 + */ + +int +__printf_render_float(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + int prec; /* precision from format; <0 for N/A */ + char *dtoaresult; /* buffer allocated by dtoa */ + char expchar; /* exponent character: [eEpP\0] */ + char *cp; + int expt; /* integer value of exponent */ + int signflag; /* true if float is negative */ + char *dtoaend; /* pointer to end of converted digits */ + char sign; /* sign prefix (' ', '+', '-', or \0) */ + int size; /* size of converted field or string */ + int ndig; /* actual number of digits returned by dtoa */ + int expsize; /* character count for expstr */ + char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ + int nseps; /* number of group separators with ' */ + int nrepeats; /* number of repeats of the last group */ + const char *grouping; /* locale specific numeric grouping rules */ + int lead; /* sig figs before decimal or group sep */ + long double ld; + double d; + int realsz; /* field size expanded by dprec, sign, etc */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ + int prsize; /* max size of printed field */ + int ret; /* return value accumulator */ + char *decimal_point; /* locale specific decimal point */ + int n2; /* XXX: for PRINTANDPAD */ + char thousands_sep; /* locale specific thousands separator */ + char buf[BUF]; /* buffer with space for digits of uintmax_t */ + const char *xdigs; + int flag; + + prec = pi->prec; + ox[1] = '\0'; + sign = pi->showsign; + flag = 0; + ret = 0; + + thousands_sep = *(localeconv()->thousands_sep); + grouping = NULL; + if (pi->alt) + grouping = localeconv()->grouping; + decimal_point = localeconv()->decimal_point; + dprec = -1; + + switch(pi->spec) { + case 'a': + case 'A': + if (pi->spec == 'a') { + ox[1] = 'x'; + xdigs = __lowercase_hex; + expchar = 'p'; + } else { + ox[1] = 'X'; + xdigs = __uppercase_hex; + expchar = 'P'; + } + if (prec >= 0) + prec++; + if (pi->is_long_double) { + ld = *((long double *)arg[0]); + dtoaresult = cp = + __hldtoa(ld, xdigs, prec, + &expt, &signflag, &dtoaend); + } else { + d = *((double *)arg[0]); + dtoaresult = cp = + __hdtoa(d, xdigs, prec, + &expt, &signflag, &dtoaend); + } + if (prec < 0) + prec = dtoaend - cp; + if (expt == INT_MAX) + ox[1] = '\0'; + goto fp_common; + case 'e': + case 'E': + expchar = pi->spec; + if (prec < 0) /* account for digit before decpt */ + prec = DEFPREC + 1; + else + prec++; + break; + case 'f': + case 'F': + expchar = '\0'; + break; + case 'g': + case 'G': + expchar = pi->spec - ('g' - 'e'); + if (prec == 0) + prec = 1; + break; + default: + assert(pi->spec == 'f'); + } + + if (prec < 0) + prec = DEFPREC; + if (pi->is_long_double) { + ld = *((long double *)arg[0]); + dtoaresult = cp = + __ldtoa(&ld, expchar ? 2 : 3, prec, + &expt, &signflag, &dtoaend); + } else { + d = *((double *)arg[0]); + dtoaresult = cp = + dtoa(d, expchar ? 2 : 3, prec, + &expt, &signflag, &dtoaend); + if (expt == 9999) + expt = INT_MAX; + } +fp_common: + if (signflag) + sign = '-'; + if (expt == INT_MAX) { /* inf or nan */ + if (*cp == 'N') { + cp = (pi->spec >= 'a') ? "nan" : "NAN"; + sign = '\0'; + } else + cp = (pi->spec >= 'a') ? "inf" : "INF"; + size = 3; + flag = 1; + goto here; + } + ndig = dtoaend - cp; + if (pi->spec == 'g' || pi->spec == 'G') { + if (expt > -4 && expt <= prec) { + /* Make %[gG] smell like %[fF] */ + expchar = '\0'; + if (pi->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 (!pi->alt) + prec = ndig; + } + } + if (expchar) { + expsize = exponent(expstr, expt - 1, expchar); + size = expsize + prec; + if (prec > 1 || pi->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 || pi->alt) + size += prec + 1; + if (grouping && expt > 0) { + /* space for thousands' grouping */ + nseps = nrepeats = 0; + lead = expt; + while (*grouping != CHAR_MAX) { + if (lead <= *grouping) + break; + lead -= *grouping; + if (*(grouping+1)) { + nseps++; + grouping++; + } else + nrepeats++; + } + size += nseps + nrepeats; + } else + lead = expt; + } + +here: + /* + * 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; + + prsize = pi->width > realsz ? pi->width : realsz; + + /* right-adjusting blank padding */ + if (pi->pad != '0' && pi->left == 0) + ret += __printf_pad(io, pi->width - realsz, 0); + + /* prefix */ + if (sign) + ret += __printf_puts(io, &sign, 1); + + if (ox[1]) { /* ox[1] is either x, X, or \0 */ + ox[0] = '0'; + ret += __printf_puts(io, ox, 2); + } + + /* right-adjusting zero padding */ + if (pi->pad == '0' && pi->left == 0) + ret += __printf_pad(io, pi->width - realsz, 1); + + /* leading zeroes from decimal precision */ + ret += __printf_pad(io, dprec - size, 1); + + if (flag) + ret += __printf_puts(io, cp, size); + else { + /* glue together f_p fragments */ + if (!expchar) { /* %[fF] or sufficiently short %[gG] */ + if (expt <= 0) { + ret += __printf_puts(io, "0", 1); + if (prec || pi->alt) + ret += __printf_puts(io, decimal_point, 1); + ret += __printf_pad(io, -expt, 1); + /* already handled initial 0's */ + prec += expt; + } else { + PRINTANDPAD(cp, dtoaend, lead, 1); + cp += lead; + if (grouping) { + while (nseps>0 || nrepeats>0) { + if (nrepeats > 0) + nrepeats--; + else { + grouping--; + nseps--; + } + ret += __printf_puts(io, &thousands_sep, 1); + PRINTANDPAD(cp,dtoaend, + *grouping, 1); + cp += *grouping; + } + if (cp > dtoaend) + cp = dtoaend; + } + if (prec || pi->alt) + ret += __printf_puts(io, decimal_point,1); + } + PRINTANDPAD(cp, dtoaend, prec, 1); + } else { /* %[eE] or sufficiently long %[gG] */ + if (prec > 1 || pi->alt) { + buf[0] = *cp++; + buf[1] = *decimal_point; + ret += __printf_puts(io, buf, 2); + ret += __printf_puts(io, cp, ndig-1); + ret += __printf_pad(io, prec - ndig, 1); + } else /* XeYYY */ + ret += __printf_puts(io, cp, 1); + ret += __printf_puts(io, expstr, expsize); + } + } + /* left-adjusting padding (always blank) */ + if (pi->left) + ret += __printf_pad(io, pi->width - realsz, 0); + + __printf_flush(io); + if (dtoaresult != NULL) + freedtoa(dtoaresult); + + return (ret); +} diff --git a/lib/libc/stdio/xprintf_hexdump.c b/lib/libc/stdio/xprintf_hexdump.c new file mode 100644 index 0000000..a2956ba --- /dev/null +++ b/lib/libc/stdio/xprintf_hexdump.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <stdio.h> +#include <wchar.h> +#include <stdint.h> +#include <assert.h> +#include <sys/time.h> +#include "printf.h" + +int +__printf_arginfo_hexdump(const struct printf_info *pi, size_t n, int *argt) +{ + + assert(n >= 2); + argt[0] = PA_POINTER; + argt[1] = PA_INT; + return (2); +} + +int +__printf_render_hexdump(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + unsigned char *p; + unsigned u, l, j, a; + char buf[100], *q; + int ret; + + if (pi->width > 0 && pi->width < 16) + l = pi->width; + else + l = 16; + p = *((unsigned char **)arg[0]); + u = *((unsigned *)arg[1]); + + ret = 0; + a = 0; + while (u > 0) { + q = buf; + if (pi->showsign) + q += sprintf(q, " %04x", a); + for (j = 0; j < l && j < u; j++) + q += sprintf(q, " %02x", p[j]); + if (pi->alt) { + for (; j < l; j++) + q += sprintf(q, " "); + q += sprintf(q, " |"); + for (j = 0; j < l && j < u; j++) { + if (p[j] < ' ' || p[j] > '~') + *q++ = '.'; + else + *q++ = p[j]; + } + for (; j < l; j++) + *q++ = ' '; + *q++ = '|'; + } + if (l < u) + j = l; + else + j = u; + p += j; + u -= j; + a += j; + if (u > 0) + *q++ = '\n'; + ret += __printf_puts(io, buf + 1, q - (buf + 1)); + __printf_flush(io); + } + return (ret); +} diff --git a/lib/libc/stdio/xprintf_int.c b/lib/libc/stdio/xprintf_int.c new file mode 100644 index 0000000..641690c --- /dev/null +++ b/lib/libc/stdio/xprintf_int.c @@ -0,0 +1,471 @@ +/*- + * 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 <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <locale.h> +#include <stdint.h> +#include <assert.h> +#include <namespace.h> +#include <string.h> +#include <wchar.h> +#include <un-namespace.h> + +#include "printf.h" + +/* private stuff -----------------------------------------------------*/ + +union arg { + int intarg; + u_int uintarg; + long longarg; + u_long ulongarg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; +}; + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_char(n) ((n) + '0') + +/* various globals ---------------------------------------------------*/ + +/* + * The size of the buffer we use for integer conversions. + * Technically, we would need the most space for base 10 + * conversions with thousands' grouping characters between + * each pair of digits: 60 digits for 128 bit intmax_t. + * Use a bit more for better alignment of stuff. + */ +#define BUF 64 + +/* misc --------------------------------------------------------------*/ + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static char * +__ultoa(u_long val, char *endp, int base, const char *xdigs, + int needgrp, char thousep, const char *grp) +{ + char *cp = endp; + long sval; + int ndig; + + /* + * Handle the three cases separately, in the hope of getting + * better/faster code. + */ + switch (base) { + case 10: + if (val < 10) { /* many numbers are 1 digit */ + *--cp = to_char(val); + return (cp); + } + ndig = 0; + /* + * On many machines, unsigned arithmetic is harder than + * signed arithmetic, so we do at most one unsigned mod and + * divide; this is sufficient to reduce the range of + * the incoming value to where signed arithmetic works. + */ + if (val > LONG_MAX) { + *--cp = to_char(val % 10); + ndig++; + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + ndig++; + /* + * If (*grp == CHAR_MAX) then no more grouping + * should be performed. + */ + if (needgrp && ndig == *grp && *grp != CHAR_MAX + && sval > 9) { + *--cp = thousep; + ndig = 0; + /* + * If (*(grp+1) == '\0') then we have to + * use *grp character (last grouping rule) + * for all next cases + */ + if (*(grp+1) != '\0') + grp++; + } + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: /* oops */ + assert(base == 16); + } + return (cp); +} + + +/* Identical to __ultoa, but for intmax_t. */ +static char * +__ujtoa(uintmax_t val, char *endp, int base, const char *xdigs, + int needgrp, char thousep, const char *grp) +{ + char *cp = endp; + intmax_t sval; + int ndig; + + switch (base) { + case 10: + if (val < 10) { + *--cp = to_char(val % 10); + return (cp); + } + ndig = 0; + if (val > INTMAX_MAX) { + *--cp = to_char(val % 10); + ndig++; + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + ndig++; + /* + * If (*grp == CHAR_MAX) then no more grouping + * should be performed. + */ + if (needgrp && *grp != CHAR_MAX && ndig == *grp + && sval > 9) { + *--cp = thousep; + ndig = 0; + /* + * If (*(grp+1) == '\0') then we have to + * use *grp character (last grouping rule) + * for all next cases + */ + if (*(grp+1) != '\0') + grp++; + } + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: + abort(); + } + return (cp); +} + + +/* 'd' ---------------------------------------------------------------*/ + +int +__printf_arginfo_int(const struct printf_info *pi, size_t n, int *argt) +{ + assert (n > 0); + argt[0] = PA_INT; + if (pi->is_ptrdiff) + argt[0] |= PA_FLAG_PTRDIFF; + else if (pi->is_size) + argt[0] |= PA_FLAG_SIZE; + else if (pi->is_long) + argt[0] |= PA_FLAG_LONG; + else if (pi->is_intmax) + argt[0] |= PA_FLAG_INTMAX; + else if (pi->is_quad) + argt[0] |= PA_FLAG_QUAD; + else if (pi->is_long_double) + argt[0] |= PA_FLAG_LONG_LONG; + else if (pi->is_short) + argt[0] |= PA_FLAG_SHORT; + else if (pi->is_char) + argt[0] = PA_CHAR; + return (1); +} + +int +__printf_render_int(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + const union arg *argp; + char buf[BUF]; + char *p, *pe; + char ns, l; + int rdx, sign, zext, ngrp; + const char *nalt, *digit; + char thousands_sep; /* locale specific thousands separator */ + const char *grouping; /* locale specific numeric grouping rules */ + uintmax_t uu; + int ret; + + ret = 0; + nalt = NULL; + digit = __lowercase_hex; + ns = '\0'; + pe = buf + sizeof buf - 1; + + if (pi->group) { + thousands_sep = *(localeconv()->thousands_sep); + grouping = localeconv()->grouping; + ngrp = 1; + } else { + thousands_sep = 0; + grouping = NULL; + ngrp = 0; + } + + switch(pi->spec) { + case 'd': + case 'i': + rdx = 10; + sign = 1; + break; + case 'X': + digit = __uppercase_hex; + /*FALLTHOUGH*/ + case 'x': + rdx = 16; + sign = 0; + break; + case 'u': + case 'U': + rdx = 10; + sign = 0; + break; + case 'o': + case 'O': + rdx = 8; + sign = 0; + break; + default: + fprintf(stderr, "pi->spec = '%c'\n", pi->spec); + assert(1 == 0); + } + argp = arg[0]; + + if (sign) + ns = pi->showsign; + + if (pi->is_long_double || pi->is_quad || pi->is_intmax || + pi->is_size || pi->is_ptrdiff) { + if (sign && argp->intmaxarg < 0) { + uu = -argp->intmaxarg; + ns = '-'; + } else + uu = argp->uintmaxarg; + } else if (pi->is_long) { + if (sign && argp->longarg < 0) { + uu = (u_long)-argp->longarg; + ns = '-'; + } else + uu = argp->ulongarg; + } else if (pi->is_short) { + if (sign && (short)argp->intarg < 0) { + uu = -(short)argp->intarg; + ns = '-'; + } else + uu = (unsigned short)argp->uintarg; + } else if (pi->is_char) { + if (sign && (char)argp->intarg < 0) { + uu = -(char)argp->intarg; + ns = '-'; + } else + uu = (unsigned char)argp->uintarg; + } else { + if (sign && argp->intarg < 0) { + uu = (unsigned)-argp->intarg; + ns = '-'; + } else + uu = argp->uintarg; + } + if (uu <= ULONG_MAX) + p = __ultoa(uu, pe, rdx, digit, ngrp, thousands_sep, grouping); + else + p = __ujtoa(uu, pe, rdx, digit, ngrp, thousands_sep, grouping); + + l = 0; + if (uu == 0) { + /*- + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + * + * ``The C Standard is clear enough as is. The call + * printf("%#.0o", 0) should print 0.'' + * -- Defect Report #151 + */ + ; + if (pi->prec == 0 && !(pi->alt && rdx == 8)) + p = pe; + } else if (pi->alt) { + if (rdx == 8) + *--p = '0'; + if (rdx == 16) { + if (pi->spec == 'x') + nalt = "0x"; + else + nalt = "0X"; + l += 2; + } + } + l += pe - p; + if (ns) + l++; + + /*- + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ + if (pi->prec > (pe - p)) + zext = pi->prec - (pe - p); + else if (pi->prec != -1) + zext = 0; + else if (pi->pad == '0' && pi->width > l && !pi->left) + zext = pi->width - l; + else + zext = 0; + + l += zext; + + while (zext > 0 && p > buf) { + *--p = '0'; + zext--; + } + + if (l < BUF) { + if (ns) { + *--p = ns; + } else if (nalt != NULL) { + *--p = nalt[1]; + *--p = nalt[0]; + } + if (pi->width > (pe - p) && !pi->left) { + l = pi->width - (pe - p); + while (l > 0 && p > buf) { + *--p = ' '; + l--; + } + if (l) + ret += __printf_pad(io, l, 0); + } + } else { + if (!pi->left && pi->width > l) + ret += __printf_pad(io, pi->width - l, 0); + if (ns != '\0') + ret += __printf_puts(io, &ns, 1); + else if (nalt != NULL) + ret += __printf_puts(io, nalt, 2); + if (zext > 0) + ret += __printf_pad(io, zext, 1); + } + + ret += __printf_puts(io, p, pe - p); + if (pi->width > ret && pi->left) + ret += __printf_pad(io, pi->width - ret, 0); + __printf_flush(io); + return (ret); +} + +/* 'p' ---------------------------------------------------------------*/ + +int +__printf_arginfo_ptr(const struct printf_info *pi __unused, size_t n, int *argt) +{ + + assert (n > 0); + argt[0] = PA_POINTER; + return (1); +} + +int +__printf_render_ptr(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + struct printf_info p2; + uintmax_t u; + const void *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 + */ + u = (uintmax_t)(uintptr_t) *((void **)arg[0]); + p2 = *pi; + + p2.spec = 'x'; + p2.alt = 1; + p2.is_long_double = 1; + p = &u; + return (__printf_render_int(io, &p2, &p)); +} diff --git a/lib/libc/stdio/xprintf_str.c b/lib/libc/stdio/xprintf_str.c new file mode 100644 index 0000000..d46fa85 --- /dev/null +++ b/lib/libc/stdio/xprintf_str.c @@ -0,0 +1,187 @@ +/*- + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <stdint.h> +#include <assert.h> +#include <wchar.h> +#include "printf.h" + +/* + * 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. + * string ends is null-terminated. + */ +static char * +__wcsconv(wchar_t *wcsarg, int prec) +{ + static const mbstate_t initial; + mbstate_t mbs; + char buf[MB_LEN_MAX]; + wchar_t *p; + char *convbuf; + size_t clen, nbytes; + + /* Allocate space for the maximum number of bytes we could output. */ + if (prec < 0) { + p = wcsarg; + mbs = initial; + nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); + if (nbytes == (size_t)-1) + return (NULL); + } else { + /* + * Optimisation: if the output precision is small enough, + * just allocate enough memory for the maximum instead of + * scanning the string. + */ + if (prec < 128) + nbytes = prec; + else { + nbytes = 0; + p = wcsarg; + mbs = initial; + for (;;) { + clen = wcrtomb(buf, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1 || + (int)(nbytes + clen) > prec) + break; + nbytes += clen; + } + } + } + if ((convbuf = malloc(nbytes + 1)) == NULL) + return (NULL); + + /* Fill the output buffer. */ + p = wcsarg; + mbs = initial; + if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, + nbytes, &mbs)) == (size_t)-1) { + free(convbuf); + return (NULL); + } + convbuf[nbytes] = '\0'; + return (convbuf); +} + + +/* 's' ---------------------------------------------------------------*/ + +int +__printf_arginfo_str(const struct printf_info *pi, size_t n, int *argt) +{ + + assert (n > 0); + if (pi->is_long || pi->spec == 'C') + argt[0] = PA_WSTRING; + else + argt[0] = PA_STRING; + return (1); +} + +int +__printf_render_str(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + const char *p; + wchar_t *wcp; + char *convbuf; + int l; + + if (pi->is_long || pi->spec == 'S') { + wcp = *((wint_t **)arg[0]); + if (wcp == NULL) + return (__printf_out(io, pi, "(null)", 6)); + convbuf = __wcsconv(wcp, pi->prec); + if (convbuf == NULL) + return (-1); + l = __printf_out(io, pi, convbuf, strlen(convbuf)); + free(convbuf); + return (l); + } + p = *((char **)arg[0]); + if (p == NULL) + return (__printf_out(io, pi, "(null)", 6)); + l = strlen(p); + if (pi->prec >= 0 && pi->prec < l) + l = pi->prec; + return (__printf_out(io, pi, p, l)); +} + +/* 'c' ---------------------------------------------------------------*/ + +int +__printf_arginfo_chr(const struct printf_info *pi, size_t n, int *argt) +{ + + assert (n > 0); + if (pi->is_long || pi->spec == 'C') + argt[0] = PA_WCHAR; + else + argt[0] = PA_INT; + return (1); +} + +int +__printf_render_chr(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + int i; + wint_t ii; + unsigned char c; + static const mbstate_t initial; /* XXX: this is bogus! */ + mbstate_t mbs; + size_t mbseqlen; + char buf[MB_CUR_MAX]; + + if (pi->is_long || pi->spec == 'C') { + ii = *((wint_t *)arg[0]); + + mbs = initial; + mbseqlen = wcrtomb(buf, (wchar_t)ii, &mbs); + if (mbseqlen == (size_t) -1) + return (-1); + return (__printf_out(io, pi, buf, mbseqlen)); + } + i = *((int *)arg[0]); + c = i; + i = __printf_out(io, pi, &c, 1); + __printf_flush(io); + return (i); +} diff --git a/lib/libc/stdio/xprintf_time.c b/lib/libc/stdio/xprintf_time.c new file mode 100644 index 0000000..b566aa0 --- /dev/null +++ b/lib/libc/stdio/xprintf_time.c @@ -0,0 +1,114 @@ +/*- + * 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 <stdio.h> +#include <wchar.h> +#include <stdint.h> +#include <assert.h> +#include <sys/time.h> +#include "printf.h" + +int +__printf_arginfo_time(const struct printf_info *pi, size_t n, int *argt) +{ + + assert(n >= 1); + argt[0] = PA_POINTER; + return (1); +} +#define MINUTE 60 +#define HOUR (60 * MINUTE) +#define DAY (24 * HOUR) +#define YEAR (365 * DAY) + +int +__printf_render_time(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + char buf[100]; + char *p; + struct timeval *tv; + struct timespec *ts; + time_t *tp; + intmax_t t; + int i, prec, nsec; + + prec = 0; + if (pi->is_long) { + tv = *((struct timeval **)arg[0]); + t = tv->tv_sec; + nsec = tv->tv_usec * 1000; + prec = 6; + } else if (pi->is_long_double) { + ts = *((struct timespec **)arg[0]); + t = ts->tv_sec; + nsec = ts->tv_nsec; + prec = 9; + } else { + tp = *((time_t **)arg[0]); + t = *tp; + } + + p = buf; + if (pi->alt) { + if (t >= YEAR) { + p += sprintf(p, "%jdy", t / YEAR); + t %= YEAR; + } + if (t >= DAY && t != 0) { + p += sprintf(p, "%jdd", t / DAY); + t %= DAY; + } + if (t >= HOUR && t != 0) { + p += sprintf(p, "%jdh", t / HOUR); + t %= HOUR; + } + if (t >= MINUTE && t != 0) { + p += sprintf(p, "%jdm", t / MINUTE); + t %= MINUTE; + } + if (t != 0) + p += sprintf(p, "%jd", t); + } else { + p += sprintf(p, "%jd", (intmax_t)t); + } + if (pi->is_long || pi->is_long_double) { + if (pi->prec >= 0) + prec = pi->prec; + for (i = prec; i < 9; i++) + nsec /= 10; + p += sprintf(p, ".%.*d", prec, nsec); + } + return(__printf_out(io, pi, buf, p - buf)); +} diff --git a/lib/libc/stdio/xprintf_vis.c b/lib/libc/stdio/xprintf_vis.c new file mode 100644 index 0000000..2f8d63e --- /dev/null +++ b/lib/libc/stdio/xprintf_vis.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <wchar.h> +#include <vis.h> +#include <assert.h> +#include <sys/time.h> +#include "printf.h" + +int +__printf_arginfo_vis(const struct printf_info *pi, size_t n, int *argt) +{ + + assert(n >= 1); + argt[0] = PA_POINTER; + return (1); +} + +int +__printf_render_vis(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + char *p, *buf; + unsigned l; + int ret; + + ret = 0; + p = *((char **)arg[0]); + if (pi->prec >= 0) + l = pi->prec; + else + l = strlen(p); + buf = malloc(l * 4 + 1); + if (buf == NULL) + return (-1); + if (pi->showsign) + ret = strvisx(buf, p, l, VIS_WHITE | VIS_HTTPSTYLE); + else if (pi->pad == '0') + ret = strvisx(buf, p, l, VIS_WHITE | VIS_OCTAL); + else if (pi->alt) + ret = strvisx(buf, p, l, VIS_WHITE); + else + ret = strvisx(buf, p, l, VIS_WHITE | VIS_CSTYLE | VIS_OCTAL); + ret += __printf_out(io, pi, buf, ret); + __printf_flush(io); + free(buf); + return(ret); +} |