#include "namespace.h"
#include <err.h>
#include <sys/types.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <locale.h>
#include <stdint.h>
#include <assert.h>
#include <stdarg.h>
#include <namespace.h>
#include <string.h>
#include <wchar.h>
#include "un-namespace.h"
#include "local.h"
#include "printf.h"
#include "fvwrite.h"
int __use_xprintf = -1;
union arg {
int intarg;
long longarg;
intmax_t intmaxarg;
double doublearg;
long double longdoublearg;
wint_t wintarg;
char *pchararg;
wchar_t *pwchararg;
void *pvoidarg;
};
#define to_digit(c) ((c) - '0')
#define is_digit(c) (((unsigned)to_digit(c)) <= 9)
const char __lowercase_hex[17] = "0123456789abcdef?";
const char __uppercase_hex[17] = "0123456789ABCDEF?";
#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'};
#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);
}
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));
}
static int
__printf_arginfo_n(const struct printf_info *pi, size_t n, int *argt)
{
assert(n >= 1);
argt[0] = PA_POINTER;
return (1);
}
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);
}
static struct {
printf_arginfo_function *arginfo;
printf_function *gnurender;
printf_render *render;
} printf_tbl[256] = {
['%'] = { __printf_arginfo_pct, NULL, __printf_render_pct },
['A'] = { __printf_arginfo_float, NULL, __printf_render_float },
['C'] = { __printf_arginfo_chr, NULL, __printf_render_chr },
['E'] = { __printf_arginfo_float, NULL, __printf_render_float },
['F'] = { __printf_arginfo_float, NULL, __printf_render_float },
['G'] = { __printf_arginfo_float, NULL, __printf_render_float },
['S'] = { __printf_arginfo_str, NULL, __printf_render_str },
['X'] = { __printf_arginfo_int, NULL, __printf_render_int },
['a'] = { __printf_arginfo_float, NULL, __printf_render_float },
['c'] = { __printf_arginfo_chr, NULL, __printf_render_chr },
['d'] = { __printf_arginfo_int, NULL, __printf_render_int },
['e'] = { __printf_arginfo_float, NULL, __printf_render_float },
['f'] = { __printf_arginfo_float, NULL, __printf_render_float },
['g'] = { __printf_arginfo_float, NULL, __printf_render_float },
['i'] = { __printf_arginfo_int, NULL, __printf_render_int },
['n'] = { __printf_arginfo_n, __printf_render_n, NULL },
['o'] = { __printf_arginfo_int, NULL, __printf_render_int },
['p'] = { __printf_arginfo_ptr, NULL, __printf_render_ptr },
['q'] = { __printf_arginfo_int, NULL, __printf_render_int },
['s'] = { __printf_arginfo_str, NULL, __printf_render_str },
['u'] = { __printf_arginfo_int, NULL, __printf_render_int },
['x'] = { __printf_arginfo_int, NULL, __printf_render_int },
};
static int
__v2printf(FILE *fp, const char *fmt0, unsigned pct, va_list ap)
{
struct printf_info *pi, *pil;
const char *fmt;
int ch;
struct printf_info pia[pct + 10];
int argt[pct + 10];
union arg args[pct + 10];
int nextarg;
int maxarg;
int ret = 0;
int n;
struct __printf_io io;
__printf_init(&io);
io.fp = fp;
fmt = fmt0;
maxarg = 0;
nextarg = 1;
memset(argt, 0, sizeof argt);
for (pi = pia; ; pi++) {
memset(pi, 0, sizeof *pi);
pil = pi;
if (*fmt == '\0')
break;
pil = pi + 1;
pi->prec = -1;
pi->pad = ' ';
pi->begin = pi->end = fmt;
while (*fmt != '\0' && *fmt != '%')
pi->end = ++fmt;
if (*fmt == '\0')
break;
fmt++;
for (;;) {
pi->spec = *fmt;
switch (pi->spec) {
case ' ':
if (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':
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;
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);
static int
__v3printf(FILE *fp, const char *fmt, int pct, va_list ap)
{
int ret;
FILE fake = FAKE_FILE;
unsigned char buf[BUFSIZ];
fake._flags = fp->_flags & ~__SNBF;
fake._file = fp->_file;
fake._cookie = fp->_cookie;
fake._write = fp->_write;
fake._orientation = fp->_orientation;
fake._mbstate = fp->_mbstate;
fake._bf._base = fake._p = buf;
fake._bf._size = fake._w = sizeof(buf);
fake._lbfsize = 0;
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;
for (p = fmt0, u = 0; *p; p++) {
if (*p != '%')
continue;
u++;
if (p[1] == '%')
p++;
}
if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
fp->_file >= 0)
return (__v3printf(fp, fmt0, u, ap));
else
return (__v2printf(fp, fmt0, u, ap));
}
int
register_printf_function(int spec, printf_function *render, printf_arginfo_function *arginfo)
{
if (spec > 255 || spec < 0)
return (-1);
printf_tbl[spec].gnurender = render;
printf_tbl[spec].arginfo = arginfo;
__use_xprintf = 1;
return (0);
}
int
register_printf_render(int spec, printf_render *render, printf_arginfo_function *arginfo)
{
if (spec > 255 || spec < 0)
return (-1);
printf_tbl[spec].render = render;
printf_tbl[spec].arginfo = arginfo;
__use_xprintf = 1;
return (0);
}
int
register_printf_render_std(const char *specs)
{
for (; *specs != '\0'; specs++) {
switch (*specs) {
case 'H':
register_printf_render(*specs,
__printf_render_hexdump,
__printf_arginfo_hexdump);
break;
case 'M':
register_printf_render(*specs,
__printf_render_errno,
__printf_arginfo_errno);
break;
case 'Q':
register_printf_render(*specs,
__printf_render_quote,
__printf_arginfo_quote);
break;
case 'T':
register_printf_render(*specs,
__printf_render_time,
__printf_arginfo_time);
break;
case 'V':
register_printf_render(*specs,
__printf_render_vis,
__printf_arginfo_vis);
break;
default:
return (-1);
}
}
return (0);
}