525cd732f9
Remove the const qualifier from ap argument for __v2printf, that induced that breakage, and seems to be the real reason for bad code. ap is modified inside the __v2printf body by va_arg macro. Pointy hat to: kib Approved by: pjd (mentor)
689 lines
17 KiB
C
689 lines
17 KiB
C
/*-
|
|
* 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, va_list ap)
|
|
{
|
|
struct printf_info *pi, *pil;
|
|
const char *fmt;
|
|
int ch;
|
|
struct printf_info pia[pct + 10];
|
|
int argt[pct + 10];
|
|
union arg args[pct + 10];
|
|
int nextarg;
|
|
int maxarg;
|
|
int ret = 0;
|
|
int n;
|
|
struct __printf_io io;
|
|
|
|
__printf_init(&io);
|
|
io.fp = fp;
|
|
|
|
fmt = fmt0;
|
|
maxarg = 0;
|
|
nextarg = 1;
|
|
memset(argt, 0, sizeof argt);
|
|
for (pi = pia; ; pi++) {
|
|
memset(pi, 0, sizeof *pi);
|
|
pil = pi;
|
|
if (*fmt == '\0')
|
|
break;
|
|
pil = pi + 1;
|
|
pi->prec = -1;
|
|
pi->pad = ' ';
|
|
pi->begin = pi->end = fmt;
|
|
while (*fmt != '\0' && *fmt != '%')
|
|
pi->end = ++fmt;
|
|
if (*fmt == '\0')
|
|
break;
|
|
fmt++;
|
|
for (;;) {
|
|
pi->spec = *fmt;
|
|
switch (pi->spec) {
|
|
case ' ':
|
|
/*-
|
|
* ``If the space and + flags both appear, the space
|
|
* flag will be ignored.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
if (pi->showsign == 0)
|
|
pi->showsign = ' ';
|
|
fmt++;
|
|
continue;
|
|
case '#':
|
|
pi->alt = 1;
|
|
fmt++;
|
|
continue;
|
|
case '.':
|
|
pi->prec = 0;
|
|
fmt++;
|
|
if (*fmt == '*') {
|
|
fmt++;
|
|
pi->get_prec = nextarg;
|
|
argt[nextarg++] = PA_INT;
|
|
continue;
|
|
}
|
|
while (*fmt != '\0' && is_digit(*fmt)) {
|
|
pi->prec *= 10;
|
|
pi->prec += to_digit(*fmt);
|
|
fmt++;
|
|
}
|
|
continue;
|
|
case '-':
|
|
pi->left = 1;
|
|
fmt++;
|
|
continue;
|
|
case '+':
|
|
pi->showsign = '+';
|
|
fmt++;
|
|
continue;
|
|
case '*':
|
|
fmt++;
|
|
pi->get_width = nextarg;
|
|
argt[nextarg++] = PA_INT;
|
|
continue;
|
|
case '%':
|
|
fmt++;
|
|
break;
|
|
case '\'':
|
|
pi->group = 1;
|
|
fmt++;
|
|
continue;
|
|
case '0':
|
|
/*-
|
|
* ``Note that 0 is taken as a flag, not as the
|
|
* beginning of a field width.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
pi->pad = '0';
|
|
fmt++;
|
|
continue;
|
|
case '1': case '2': case '3':
|
|
case '4': case '5': case '6':
|
|
case '7': case '8': case '9':
|
|
n = 0;
|
|
while (*fmt != '\0' && is_digit(*fmt)) {
|
|
n *= 10;
|
|
n += to_digit(*fmt);
|
|
fmt++;
|
|
}
|
|
if (*fmt == '$') {
|
|
if (nextarg > maxarg)
|
|
maxarg = nextarg;
|
|
nextarg = n;
|
|
fmt++;
|
|
} else
|
|
pi->width = n;
|
|
continue;
|
|
case 'D':
|
|
case 'O':
|
|
case 'U':
|
|
pi->spec += ('a' - 'A');
|
|
pi->is_intmax = 0;
|
|
if (pi->is_long_double || pi->is_quad) {
|
|
pi->is_long = 0;
|
|
pi->is_long_double = 1;
|
|
} else {
|
|
pi->is_long = 1;
|
|
pi->is_long_double = 0;
|
|
}
|
|
fmt++;
|
|
break;
|
|
case 'j':
|
|
pi->is_intmax = 1;
|
|
fmt++;
|
|
continue;
|
|
case 'q':
|
|
pi->is_long = 0;
|
|
pi->is_quad = 1;
|
|
fmt++;
|
|
continue;
|
|
case 'L':
|
|
pi->is_long_double = 1;
|
|
fmt++;
|
|
continue;
|
|
case 'h':
|
|
fmt++;
|
|
if (*fmt == 'h') {
|
|
fmt++;
|
|
pi->is_char = 1;
|
|
} else {
|
|
pi->is_short = 1;
|
|
}
|
|
continue;
|
|
case 'l':
|
|
fmt++;
|
|
if (*fmt == 'l') {
|
|
fmt++;
|
|
pi->is_long_double = 1;
|
|
pi->is_quad = 0;
|
|
} else {
|
|
pi->is_quad = 0;
|
|
pi->is_long = 1;
|
|
}
|
|
continue;
|
|
case 't':
|
|
pi->is_ptrdiff = 1;
|
|
fmt++;
|
|
continue;
|
|
case 'z':
|
|
pi->is_size = 1;
|
|
fmt++;
|
|
continue;
|
|
default:
|
|
fmt++;
|
|
break;
|
|
}
|
|
if (printf_tbl[pi->spec].arginfo == NULL)
|
|
errx(1, "arginfo[%c] = NULL", pi->spec);
|
|
ch = printf_tbl[pi->spec].arginfo(
|
|
pi, __PRINTFMAXARG, &argt[nextarg]);
|
|
if (ch > 0)
|
|
pi->arg[0] = &args[nextarg];
|
|
if (ch > 1)
|
|
pi->arg[1] = &args[nextarg + 1];
|
|
nextarg += ch;
|
|
break;
|
|
}
|
|
}
|
|
if (nextarg > maxarg)
|
|
maxarg = nextarg;
|
|
#if 0
|
|
fprintf(stderr, "fmt0 <%s>\n", fmt0);
|
|
fprintf(stderr, "pil %p\n", pil);
|
|
#endif
|
|
for (ch = 1; ch < maxarg; ch++) {
|
|
#if 0
|
|
fprintf(stderr, "arg %d %x\n", ch, argt[ch]);
|
|
#endif
|
|
switch(argt[ch]) {
|
|
case PA_CHAR:
|
|
args[ch].intarg = (char)va_arg (ap, int);
|
|
break;
|
|
case PA_INT:
|
|
args[ch].intarg = va_arg (ap, int);
|
|
break;
|
|
case PA_INT | PA_FLAG_SHORT:
|
|
args[ch].intarg = (short)va_arg (ap, int);
|
|
break;
|
|
case PA_INT | PA_FLAG_LONG:
|
|
args[ch].longarg = va_arg (ap, long);
|
|
break;
|
|
case PA_INT | PA_FLAG_INTMAX:
|
|
args[ch].intmaxarg = va_arg (ap, intmax_t);
|
|
break;
|
|
case PA_INT | PA_FLAG_QUAD:
|
|
args[ch].intmaxarg = va_arg (ap, quad_t);
|
|
break;
|
|
case PA_INT | PA_FLAG_LONG_LONG:
|
|
args[ch].intmaxarg = va_arg (ap, long long);
|
|
break;
|
|
case PA_INT | PA_FLAG_SIZE:
|
|
args[ch].intmaxarg = va_arg (ap, size_t);
|
|
break;
|
|
case PA_INT | PA_FLAG_PTRDIFF:
|
|
args[ch].intmaxarg = va_arg (ap, ptrdiff_t);
|
|
break;
|
|
case PA_WCHAR:
|
|
args[ch].wintarg = va_arg (ap, wint_t);
|
|
break;
|
|
case PA_POINTER:
|
|
args[ch].pvoidarg = va_arg (ap, void *);
|
|
break;
|
|
case PA_STRING:
|
|
args[ch].pchararg = va_arg (ap, char *);
|
|
break;
|
|
case PA_WSTRING:
|
|
args[ch].pwchararg = va_arg (ap, wchar_t *);
|
|
break;
|
|
case PA_DOUBLE:
|
|
#ifndef NO_FLOATING_POINT
|
|
args[ch].doublearg = va_arg (ap, double);
|
|
#endif
|
|
break;
|
|
case PA_DOUBLE | PA_FLAG_LONG_DOUBLE:
|
|
#ifndef NO_FLOATING_POINT
|
|
args[ch].longdoublearg = va_arg (ap, long double);
|
|
#endif
|
|
break;
|
|
default:
|
|
errx(1, "argtype = %x (fmt = \"%s\")\n",
|
|
argt[ch], fmt0);
|
|
}
|
|
}
|
|
for (pi = pia; pi < pil; pi++) {
|
|
#if 0
|
|
fprintf(stderr, "pi %p", pi);
|
|
fprintf(stderr, " spec '%c'", pi->spec);
|
|
fprintf(stderr, " args %d",
|
|
((uintptr_t)pi->arg[0] - (uintptr_t)args) / sizeof args[0]);
|
|
if (pi->width) fprintf(stderr, " width %d", pi->width);
|
|
if (pi->pad) fprintf(stderr, " pad 0x%x", pi->pad);
|
|
if (pi->left) fprintf(stderr, " left");
|
|
if (pi->showsign) fprintf(stderr, " showsign");
|
|
if (pi->prec != -1) fprintf(stderr, " prec %d", pi->prec);
|
|
if (pi->is_char) fprintf(stderr, " char");
|
|
if (pi->is_short) fprintf(stderr, " short");
|
|
if (pi->is_long) fprintf(stderr, " long");
|
|
if (pi->is_long_double) fprintf(stderr, " long_double");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "\t\"%.*s\"\n", pi->end - pi->begin, pi->begin);
|
|
#endif
|
|
if (pi->get_width) {
|
|
pi->width = args[pi->get_width].intarg;
|
|
/*-
|
|
* ``A negative field width argument is taken as a
|
|
* - flag followed by a positive field width.''
|
|
* -- ANSI X3J11
|
|
* They don't exclude field widths read from args.
|
|
*/
|
|
if (pi->width < 0) {
|
|
pi->left = 1;
|
|
pi->width = -pi->width;
|
|
}
|
|
}
|
|
if (pi->get_prec)
|
|
pi->prec = args[pi->get_prec].intarg;
|
|
ret += __printf_puts(&io, pi->begin, pi->end - pi->begin);
|
|
if (printf_tbl[pi->spec].gnurender != NULL) {
|
|
__printf_flush(&io);
|
|
pi->sofar = ret;
|
|
ret += printf_tbl[pi->spec].gnurender(
|
|
fp, pi, (const void *)pi->arg);
|
|
} else if (printf_tbl[pi->spec].render != NULL) {
|
|
pi->sofar = ret;
|
|
n = printf_tbl[pi->spec].render(
|
|
&io, pi, (const void *)pi->arg);
|
|
if (n < 0)
|
|
io.fp->_flags |= __SERR;
|
|
else
|
|
ret += n;
|
|
} else if (pi->begin == pi->end)
|
|
errx(1, "render[%c] = NULL", *fmt);
|
|
}
|
|
__printf_flush(&io);
|
|
return (ret);
|
|
}
|
|
|
|
extern int __fflush(FILE *fp);
|
|
|
|
/*
|
|
* Helper function for `fprintf to unbuffered unix file': creates a
|
|
* temporary buffer. We only work on write-only files; this avoids
|
|
* worries about ungetc buffers and so forth.
|
|
*/
|
|
static int
|
|
__v3printf(FILE *fp, const char *fmt, int pct, va_list ap)
|
|
{
|
|
int ret;
|
|
FILE fake;
|
|
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 '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);
|
|
}
|
|
|