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:
parent
f69d4d5fa8
commit
9b8cbdad18
@ -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
155
include/printf.h
Normal 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 */
|
@ -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 \
|
||||
|
@ -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
674
lib/libc/stdio/xprintf.c
Normal 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);
|
||||
}
|
||||
|
425
lib/libc/stdio/xprintf_float.c
Normal file
425
lib/libc/stdio/xprintf_float.c
Normal 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);
|
||||
}
|
97
lib/libc/stdio/xprintf_hexdump.c
Normal file
97
lib/libc/stdio/xprintf_hexdump.c
Normal 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);
|
||||
}
|
471
lib/libc/stdio/xprintf_int.c
Normal file
471
lib/libc/stdio/xprintf_int.c
Normal 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));
|
||||
}
|
187
lib/libc/stdio/xprintf_str.c
Normal file
187
lib/libc/stdio/xprintf_str.c
Normal 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);
|
||||
}
|
114
lib/libc/stdio/xprintf_time.c
Normal file
114
lib/libc/stdio/xprintf_time.c
Normal 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));
|
||||
}
|
76
lib/libc/stdio/xprintf_vis.c
Normal file
76
lib/libc/stdio/xprintf_vis.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user