Add an extensible version of our *printf(3) implementation to libc

on probationary terms:  it may go away again if it transpires it is
a bad idea.

This extensible printf version will only be used if either
    environment variable USE_XPRINTF is defined
or
    one of the extension functions are called.
or
    the global variable __use_xprintf is set greater than zero.

In all other cases our traditional printf implementation will
be used.

The extensible version is slower than the default printf, mostly
because less opportunity for combining I/O operation exists when
faced with extensions.  The default printf on the other hand
is a bad case of spaghetti code.

The extension API has a GLIBC compatible part and a FreeBSD version
of same.  The FreeBSD version exists because the GLIBC version may
run afoul of our FILE * locking in multithreaded programs and it
even further eliminate the opportunities for combining I/O operations.

Include three demo extensions which can be enabled if desired: time
(%T), hexdump (%H) and strvis (%V).

%T can format time_t (%T), struct timeval (%lT) and struct timespec (%llT)
   in one of two human readable duration formats:
	"%.3llT" -> "20349.245"
	"%#.3llT" -> "5h39m9.245"

%H will hexdump a sequence of bytes and takes a pointer and a length
   argument.  The width specifies number of bytes per line.
	"%4H" -> "65 72 20 65"
	"%+4H" -> "0000 65 72 20 65"
	"%#4H" -> "65 72 20 65  |er e|"
	"%+#4H" -> "0000 65 72 20 65  |er e|"

%V will dump a string in strvis format.
	"%V" -> "Hello\tWor\377ld"	(C-style)
	"%0V" -> "Hello\011Wor\377ld"	(octal)
	"%+V" -> "Hello%09Wor%FFld"	(http-style)

Tests, comments, bugreports etc are most welcome.
This commit is contained in:
phk 2005-12-16 18:56:39 +00:00
parent f69d4d5fa8
commit 9b8cbdad18
11 changed files with 2210 additions and 1 deletions

View File

@ -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 \

155
include/printf.h Normal file
View File

@ -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 */

View File

@ -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 \

View File

@ -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

674
lib/libc/stdio/xprintf.c Normal file
View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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);
}