diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2003-08-01 20:08:59 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2003-08-01 20:08:59 +0000 |
commit | 1217289737588e65b088b3535428b27c7287d699 (patch) | |
tree | 6a292ac767d219702e26a6a2111737f84a96900c /libc/stdio | |
parent | 32b76c5ec3c257b7287913d0d1a96e0cbb2e9c6a (diff) |
Add a new *scanf implementation, includeing the *wscanf functions.
Should be standards compliant and with several optional features,
including support for hexadecimal float notation, locale awareness,
glibc-like locale-specific digit grouping with the `'' flag, and
positional arg support. I tested it pretty well (finding several
bugs in glibc's scanf in the process), but it is brand new so be
aware.
The *wprintf functions now support floating point output. Also, a
couple of bugs were squashed. Finally, %a/%A conversions are
now implemented.
Implement the glibc xlocale interface for thread-specific locale
support. Also add the various *_l(args, locale_t loc_arg) funcs.
NOTE!!! setlocale() is NOT threadsafe! NOTE!!!
The strto{floating point} conversion functions are now locale aware.
The also now support hexadecimal floating point notation.
Add the wcsto{floating point} conversion functions.
Fix a bug in mktime() related to dst. Note that unlike glibc's mktime,
uClibc's version always normalizes the struct tm before attempting
to determine the correct dst setting if tm_isdst == -1 on entry.
Add a stub version of the libintl functions. (untested)
Fixed a known memory leak in setlocale() related to the collation data.
Add lots of new config options (which Erik agreed to sort out :-),
including finally exposing some of the stripped down stdio configs.
Be careful with those though, as they haven't been tested in a
long time.
(temporary) GOTCHAs...
The ctype functions are currently incorrect for 8-bit locales. They
will be fixed shortly.
The ctype functions are now table-based, resulting in larger staticly
linked binaries. I'll be adding an option to use the old approach
in the stub locale configuration.
Diffstat (limited to 'libc/stdio')
-rw-r--r-- | libc/stdio/Makefile | 12 | ||||
-rw-r--r-- | libc/stdio/old_vfprintf.c | 9 | ||||
-rw-r--r-- | libc/stdio/printf.c | 1151 | ||||
-rw-r--r-- | libc/stdio/scanf.c | 2370 | ||||
-rw-r--r-- | libc/stdio/stdio.c | 200 |
5 files changed, 2806 insertions, 936 deletions
diff --git a/libc/stdio/Makefile b/libc/stdio/Makefile index 9f8af9e4c..271fe7207 100644 --- a/libc/stdio/Makefile +++ b/libc/stdio/Makefile @@ -51,10 +51,15 @@ MSRC2= printf.c MOBJ2= vsnprintf.o vdprintf.o vasprintf.o vprintf.o vsprintf.o \ fprintf.o snprintf.o dprintf.o asprintf.o printf.o sprintf.o +MSRC3=scanf.c +MOBJ3=scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o vfscanf.o \ + __scan_cookie.o __psfs_parse_spec.o __psfs_do_numeric.o + ifeq ($(UCLIBC_HAS_WCHAR),y) MOBJ += _wstdio_fwrite.o MOBJ2 += fwprintf.o wprintf.o swprintf.o vwprintf.o vswprintf.o \ vfwprintf.o + MOBJ3 += wscanf.o swscanf.o fwscanf.o vwscanf.o vswscanf.o vfwscanf.o endif ifneq ($(USE_OLD_VFPRINTF),y) @@ -63,16 +68,11 @@ ifneq ($(USE_OLD_VFPRINTF),y) _store_inttype.o _load_inttype.o \ register_printf_function.o parse_printf_format.o endif -# _do_one_spec.o - ifeq ($(UCLIBC_HAS_FLOATS),y) - MOBJ2 += _dtostr.o + MOBJ2 += _fpmaxtostr.o endif -MSRC3=scanf.c -MOBJ3=scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o vfscanf.o - CSRC=popen.c tmpfile.c tmpnam.c tmpnam_r.c tempnam.c ifeq ($(USE_OLD_VFPRINTF),y) CSRC += old_vfprintf.c diff --git a/libc/stdio/old_vfprintf.c b/libc/stdio/old_vfprintf.c index 9c8970912..77e593490 100644 --- a/libc/stdio/old_vfprintf.c +++ b/libc/stdio/old_vfprintf.c @@ -117,7 +117,8 @@ * strerror and the corresponding string table which together are about 3.8k. */ -#define WANT_GNU_ERRNO 0 +/* Now controlled by uClibc_stdio.h and set below. */ +/* #define WANT_GNU_ERRNO 0 */ /**************************************************************************/ @@ -143,6 +144,12 @@ /* #define __isdigit(c) (((unsigned int)(c - '0')) < 10) */ +#ifdef __STDIO_PRINTF_M_SUPPORT +#define WANT_GNU_ERRNO 1 +#else +#define WANT_GNU_ERRNO 0 +#endif + #if defined(__UCLIBC_HAS_FLOATS__) extern size_t _dtostr(FILE * fp, long double x, struct printf_info *info); #endif diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index 945d3c38d..da0444510 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* Copyright (C) 2002, 2003 Manuel Novoa III * My stdio library for linux and (soon) elks. * * This library is free software; you can redistribute it and/or @@ -63,6 +63,12 @@ * Nov 21, 2002 * Add *wprintf functions. Currently they don't support floating point * conversions. That will wait until the rewrite of _dtostr. + * + * Aug 1, 2003 + * Optional hexadecimal float notation support for %a/%A. + * Floating point output now works for *wprintf. + * Support for glibc locale-specific digit grouping for floats. + * Misc bug fixes. */ /* TODO: @@ -90,6 +96,7 @@ #include <assert.h> #include <stdint.h> #include <errno.h> +#include <locale.h> #define __PRINTF_INFO_NO_BITFIELD #include <printf.h> @@ -103,11 +110,22 @@ #include <wchar.h> #endif /* __UCLIBC_HAS_WCHAR__ */ -/**********************************************************************/ +/* Some older or broken gcc toolchains define LONG_LONG_MAX but not + * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what + * we use. So complain if we do not have it but should. + */ +#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) +#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long. +#endif +/**********************************************************************/ /* These provide some control over printf's feature set */ -#define __STDIO_PRINTF_FLOAT -#define __STDIO_PRINTF_M_SUPPORT + +/* This is undefined below depeding on uClibc's configuration. */ +#define __STDIO_PRINTF_FLOAT 1 + +/* Now controlled by uClibc_stdio.h. */ +/* #define __STDIO_PRINTF_M_SUPPORT */ /**********************************************************************/ @@ -120,28 +138,59 @@ #undef __STDIO_PRINTF_FLOAT #endif -#ifndef __STDIO_PRINTF_FLOAT -#undef L__dtostr -#endif +#ifdef __STDIO_PRINTF_FLOAT +#include <float.h> +#include <bits/uClibc_fpmax.h> +#else /* __STDIO_PRINTF_FLOAT */ +#undef L__fpmaxtostr +#endif /* __STDIO_PRINTF_FLOAT */ /**********************************************************************/ -#define __STDIO_GLIBC_CUSTOM_PRINTF +/* Now controlled by uClibc_stdio.h. */ +/* #define __STDIO_GLIBC_CUSTOM_PRINTF */ /* TODO -- move these to a configuration section? */ #define MAX_FIELD_WIDTH 4095 -#define MAX_USER_SPEC 10 -#define MAX_POS_ARGS 10 -/* TODO - fix the defs below */ -#define MAX_ARGS_PER_SPEC (MAX_POS_ARGS-2) +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_register_printf_function +/* emit only once */ +#warning WISHLIST: Make MAX_USER_SPEC configurable? +#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF + +#define MAX_USER_SPEC 10 +#define MAX_ARGS_PER_SPEC 5 + +#else /* __STDIO_GLIBC_CUSTOM_PRINTF */ -#if MAX_ARGS_PER_SPEC + 2 > MAX_POS_ARGS -#define MAX_ARGS MAX_ARGS_PER_SPEC + 2 +#undef MAX_USER_SPEC +#define MAX_ARGS_PER_SPEC 1 + +#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ + +#if MAX_ARGS_PER_SPEC < 1 +#error MAX_ARGS_PER_SPEC < 1! +#undef MAX_ARGS_PER_SPEC +#define MAX_ARGS_PER_SPEC 1 +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX < 9) +#error NL_ARGMAX < 9! +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2)) +#define MAX_ARGS NL_ARGMAX #else -#define MAX_ARGS MAX_POS_ARGS +/* N for spec itself, plus 1 each for width and precision */ +#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) #endif + /**********************************************************************/ /* Deal with pre-C99 compilers. */ @@ -154,7 +203,7 @@ * to ensure we get the right behavior? Either that or fall back * on the portable (but costly in size) method of using a va_list *. * That means a pointer derefs in the va_arg() invocations... */ -#warning neither va_copy or __va_copy is defined. using a simple copy instead... +#warning Neither va_copy (C99/SUSv3) or __va_copy is defined. Using a simple copy instead. But you should really check that this is appropriate... /* the glibc manual suggests that this will usually suffice when __va_copy doesn't exist. */ #define va_copy(A,B) A = B @@ -167,9 +216,11 @@ #define __PA_FLAG_INTMASK \ (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF extern printf_function _custom_printf_handler[MAX_USER_SPEC]; extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; extern char *_custom_printf_spec; +#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ /**********************************************************************/ @@ -282,7 +333,7 @@ enum { #elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) #define IMS 8 #else -#error fix QUAL_CHARS ptrdiff_t entry 't'! +#error fix QUAL_CHARS intmax_t entry 'j'! #endif #define QUAL_CHARS { \ @@ -340,7 +391,9 @@ typedef union { typedef struct { const char *fmtpos; /* TODO: move below struct?? */ struct printf_info info; +#ifdef NL_ARGMAX int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ +#endif /* NL_ARGMAX */ int num_data_args; /* TODO: use sentinal??? */ unsigned int conv_num; unsigned char argnumber[4]; /* width | prec | 1st data | unused */ @@ -349,6 +402,7 @@ typedef struct { #ifdef __va_arg_ptr void *argptr[MAX_ARGS]; #else +/* if defined(NL_ARGMAX) || defined(__STDIO_GLIBC_CUSTOM_PRINTF) */ /* While this is wasteful of space in the case where pos args aren't * enabled, it is also needed to support custom printf handlers. */ argvalue_t argvalue[MAX_ARGS]; @@ -361,7 +415,11 @@ typedef struct { only returns -1 if sets error indicator for the stream. */ #ifdef __STDIO_PRINTF_FLOAT -extern size_t _dtostr(FILE * fp, long double x, struct printf_info *info); +typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, + intptr_t buf); + +extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc); #endif extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0); /* validates */ @@ -390,6 +448,7 @@ size_t parse_printf_format(register const char *template, size_t count = 0; if (_ppfs_init(&ppfs, template) >= 0) { +#ifdef NL_ARGMAX if (ppfs.maxposarg > 0) { /* Using positional args. */ count = ppfs.maxposarg; if (n > count) { @@ -399,6 +458,7 @@ size_t parse_printf_format(register const char *template, *argtypes++ = ppfs.argtype[i]; } } else { /* Not using positional args. */ +#endif /* NL_ARGMAX */ while (*template) { if ((*template == '%') && (*++template != '%')) { ppfs.fmtpos = template; @@ -431,7 +491,9 @@ size_t parse_printf_format(register const char *template, ++template; } } +#ifdef NL_ARGMAX } +#endif /* NL_ARGMAX */ } return count; @@ -443,17 +505,23 @@ size_t parse_printf_format(register const char *template, int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) { -#ifdef __UCLIBC_HAS_WCHAR__ - static const char invalid_mbs[] = "Invalid multibyte format string."; -#endif /* __UCLIBC_HAS_WCHAR__ */ int r; /* First, zero out everything... argnumber[], argtype[], argptr[] */ memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ +#ifdef NL_ARGMAX --ppfs->maxposarg; /* set to -1 */ +#endif /* NL_ARGMAX */ ppfs->fmtpos = fmt0; -#ifdef __UCLIBC_HAS_WCHAR__ - { +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make checking of the format string in C locale an option. +#endif +#ifdef __UCLIBC_HAS_LOCALE__ + /* To support old programs, don't check mb validity if in C locale. */ + if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { + /* ANSI/ISO C99 requires format string to be a valid multibyte string + * beginning and ending in its initial shift state. */ + static const char invalid_mbs[] = "Invalid multibyte format string."; mbstate_t mbstate; const char *p; mbstate.mask = 0; /* Initialize the mbstate. */ @@ -463,7 +531,7 @@ int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) return -1; } } -#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif /* __UCLIBC_HAS_LOCALE__ */ /* now set all argtypes to no-arg */ { #if 1 @@ -506,6 +574,7 @@ int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) ppfs->fmtpos = fmt0; /* rewind */ } +#ifdef NL_MAX_ARG /* If we have positional args, make sure we know all the types. */ { register int *p = ppfs->argtype; @@ -517,6 +586,7 @@ int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) ++p; } } +#endif /* NL_MAX_ARG */ return 0; } @@ -529,12 +599,14 @@ void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) va_copy(ppfs->arg, arg); +#ifdef NL_ARGMAX if ((i = ppfs->maxposarg) > 0) { /* init for positional args */ ppfs->num_data_args = i; ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0; _ppfs_setargs(ppfs); ppfs->maxposarg = i; } +#endif /* NL_ARGMAX */ } #endif /**********************************************************************/ @@ -549,7 +621,9 @@ void _ppfs_setargs(register ppfs_t *ppfs) #endif int i; +#ifdef NL_ARGMAX if (ppfs->maxposarg == 0) { /* initing for or no pos args */ +#endif /* NL_ARGMAX */ if (ppfs->info.width == INT_MIN) { ppfs->info.width = #ifdef __va_arg_ptr @@ -615,6 +689,7 @@ void _ppfs_setargs(register ppfs_t *ppfs) } ++p; } +#ifdef NL_ARGMAX } else { if (ppfs->info.width == INT_MIN) { ppfs->info.width @@ -625,6 +700,7 @@ void _ppfs_setargs(register ppfs_t *ppfs) = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int); } } +#endif /* NL_ARGMAX */ /* Now we know the width and precision. */ if (ppfs->info.width < 0) { @@ -741,12 +817,14 @@ static int _is_equal_or_bigger_arg(int curtype, int newtype) #endif +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF /* TODO - do this differently? */ static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ char *_custom_printf_spec = _bss_custom_printf_spec; printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; printf_function _custom_printf_handler[MAX_USER_SPEC]; +#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ extern int _ppfs_parsespec(ppfs_t *ppfs) { @@ -758,12 +836,13 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) int dataargtype; int i; int dpoint; +#ifdef NL_ARGMAX int maxposarg; +#endif /* NL_ARGMAX */ int p_m_spec_chars; int n; int argtype[MAX_ARGS_PER_SPEC+2]; int argnumber[3]; /* width, precision, 1st data arg */ - unsigned int conv_num; /* This does not need to be initialized. */ static const char spec_flags[] = SPEC_FLAGS; static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */ static const char spec_ranges[] = SPEC_RANGES; @@ -781,7 +860,10 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) argnumber[1] = 0; argtype[0] = __PA_NOARG; argtype[1] = __PA_NOARG; +#ifdef NL_ARGMAX maxposarg = ppfs->maxposarg; +#endif /* NL_ARGMAX */ + #ifdef __UCLIBC_HAS_WCHAR__ /* This is somewhat lame, but saves a lot of code. If we're dealing with * a wide stream, that means the format is a wchar string. So, copy it @@ -820,7 +902,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) ++fmt; } i = 0; - while (__isdigit(*fmt)) { + while (isdigit(*fmt)) { if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */ i = (i * 10) + (*fmt - '0'); } @@ -830,6 +912,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) /* TODO: if val not in range, then error */ +#ifdef NL_ARGMAX if ((*fmt == '$') && (i > 0)) {/* Positional spec. */ ++fmt; if (maxposarg == 0) { @@ -846,7 +929,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) #warning TODO: Support prec and width for %m when positional args used /* Actually, positional arg processing will fail in general * for specifiers that don't require an arg. */ -#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ if (*fmt == 'm') { goto PREC_WIDTH; } @@ -862,6 +945,18 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) fmt = p; /* Back up for possible '0's flag. */ /* Now fall through to check flags. */ } +#else /* NL_ARGMAX */ + if (*fmt == '$') { /* Positional spec. */ + return -1; + } + + if ((fmt > p) && (*p != '0')) { + goto PREC_WIDTH; + } + + fmt = p; /* Back up for possible '0's flag. */ + /* Now fall through to check flags. */ +#endif /* NL_ARGMAX */ restart_flags: /* Process flags. */ i = 1; @@ -889,13 +984,16 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) } PREC_WIDTH: if (*p == '*') { /* Prec or width takes an arg. */ +#ifdef NL_ARGMAX if (maxposarg) { if ((*fmt++ != '$') || (i <= 0)) { /* Using pos args and no $ or invalid arg number. */ return -1; } argnumber[-dpoint] = i; - } else if (++p != fmt) { + } else +#endif /* NL_ARGMAX */ + if (++p != fmt) { /* Not using pos args but digits followed *. */ return -1; } @@ -943,7 +1041,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) p_m_spec_chars -= 2; /* lc -> C and ls -> S */ } - conv_num = p_m_spec_chars; + ppfs->conv_num = p_m_spec_chars; p = spec_ranges-1; while (p_m_spec_chars > *++p) {} @@ -962,18 +1060,17 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) ppfs->num_data_args = 1; if (!*p) { -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF - /* TODO -- gnu %m support build option. */ #ifdef __STDIO_PRINTF_M_SUPPORT if (*fmt == 'm') { - conv_num = CONV_m; + ppfs->conv_num = CONV_m; ppfs->num_data_args = 0; goto DONE; } #endif +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF /* Handle custom arg -- WARNING -- overwrites p!!! */ - conv_num = CONV_custom0; + ppfs->conv_num = CONV_custom0; p = _custom_printf_spec; do { if (*p == *fmt) { @@ -991,10 +1088,11 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) return -1; } -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF +#if defined(__STDIO_GLIBC_CUSTOM_PRINTF) || defined(__STDIO_PRINTF_M_SUPPORT) DONE: #endif +#ifdef NL_ARGMAX if (maxposarg > 0) { i = 0; do { @@ -1014,12 +1112,14 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) } } while (++i < ppfs->num_data_args + 2); } else { +#endif /* NL_ARGMAX */ ppfs->argnumber[2] = 1; memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); +#ifdef NL_ARGMAX } ppfs->maxposarg = maxposarg; - ppfs->conv_num = conv_num; +#endif /* NL_ARGMAX */ #ifdef __UCLIBC_HAS_WCHAR__ if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { @@ -1039,6 +1139,8 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) /**********************************************************************/ #ifdef L_register_printf_function +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF + int register_printf_function(int spec, printf_function handler, printf_arginfo_function arginfo) { @@ -1076,10 +1178,17 @@ int register_printf_function(int spec, printf_function handler, } return -1; } + +#endif + #endif /**********************************************************************/ #ifdef L_vsnprintf +#ifdef __UCLIBC_MJN3_ONLY__ +#warning WISHLIST: Implement vsnprintf for non-buffered and no custom stream case. +#endif /* __UCLIBC_MJN3_ONLY__ */ + #ifdef __STDIO_BUFFERS int vsnprintf(char *__restrict buf, size_t size, const char * __restrict format, va_list arg) @@ -1206,7 +1315,7 @@ int vsnprintf(char *__restrict buf, size_t size, } #else /* __STDIO_GLIBC_CUSTOM_STREAMS */ -#warning skipping vsnprintf since no buffering and no custom streams! +#warning Skipping vsnprintf since no buffering and no custom streams! #endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ #endif /* __STDIO_BUFFERS */ #endif @@ -1258,7 +1367,7 @@ int vdprintf(int filedes, const char * __restrict format, va_list arg) #ifdef L_vasprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping vasprintf since no buffering and no custom streams! +#warning Skipping vasprintf since no buffering and no custom streams! #else int vasprintf(char **__restrict buf, const char * __restrict format, @@ -1312,7 +1421,7 @@ int vprintf(const char * __restrict format, va_list arg) #ifdef L_vsprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping vsprintf since no buffering and no custom streams! +#warning Skipping vsprintf since no buffering and no custom streams! #else int vsprintf(char *__restrict buf, const char * __restrict format, @@ -1343,7 +1452,7 @@ int fprintf(FILE * __restrict stream, const char * __restrict format, ...) #ifdef L_snprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping snprintf since no buffering and no custom streams! +#warning Skipping snprintf since no buffering and no custom streams! #else int snprintf(char *__restrict buf, size_t size, @@ -1380,7 +1489,7 @@ int dprintf(int filedes, const char * __restrict format, ...) #ifdef L_asprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping asprintf and __asprintf since no buffering and no custom streams! +#warning Skipping asprintf and __asprintf since no buffering and no custom streams! #else weak_alias(__asprintf,asprintf) @@ -1417,7 +1526,7 @@ int printf(const char * __restrict format, ...) #ifdef L_sprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping sprintf since no buffering and no custom streams! +#warning Skipping sprintf since no buffering and no custom streams! #else int sprintf(char *__restrict buf, const char * __restrict format, ...) @@ -1492,7 +1601,7 @@ int vswprintf(wchar_t *__restrict buf, size_t size, return rv; } #else /* __STDIO_BUFFERS */ -#warning skipping vswprintf since no buffering! +#warning Skipping vswprintf since no buffering! #endif /* __STDIO_BUFFERS */ #endif /**********************************************************************/ @@ -1512,7 +1621,7 @@ int swprintf(wchar_t *__restrict buf, size_t size, } #else /* __STDIO_BUFFERS */ -#warning skipping vsWprintf since no buffering! +#warning Skipping vsWprintf since no buffering! #endif /* __STDIO_BUFFERS */ #endif /**********************************************************************/ @@ -1553,174 +1662,249 @@ int wprintf(const wchar_t * __restrict format, ...) } #endif /**********************************************************************/ -#ifdef L__dtostr -/* - * Copyright (C) 2000, 2001 Manuel Novoa III +#ifdef L__fpmaxtostr + +/* Copyright (C) 2000, 2001, 2003 Manuel Novoa III + * + * Function: * - * Function: size_t _dtostr(FILE *fp, long double x, struct printf_info *info) + * size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + * __fp_outfunc_t fp_outfunc); * - * This was written for uClibc to provide floating point support for - * the printf functions. It handles +/- infinity and nan on i386. + * This is derived from the old _dtostr, whic I wrote for uClibc to provide + * floating point support for the printf functions. It handles +/- infinity, + * nan, and signed 0 assuming you have ieee arithmetic. It also now handles + * digit grouping (for the uClibc supported locales) and hexadecimal float + * notation. Finally, via the fp_outfunc parameter, it now supports wide + * output. * * Notes: * - * At most MAX_DIGITS significant digits are kept. Any trailing digits + * At most DECIMAL_DIG significant digits are kept. Any trailing digits * are treated as 0 as they are really just the results of rounding noise * anyway. If you want to do better, use an arbitary precision arithmetic * package. ;-) * - * It should also be fairly portable, as not assumptions are made about the - * bit-layout of doubles. - * - * It should be too difficult to convert this to handle long doubles on i386. - * For information, see the comments below. + * It should also be fairly portable, as no assumptions are made about the + * bit-layout of doubles. Of course, that does make it less efficient than + * it could be. * - * TODO: - * long double and/or float version? (note: for float can trim code some). - * - * Decrease the size. This is really much bigger than I'd like. */ /*****************************************************************************/ /* Don't change anything that follows unless you know what you're doing. */ /*****************************************************************************/ - -/* - * Configuration for the scaling power table. Ignoring denormals, you - * should have 2**EXP_TABLE_SIZE >= LDBL_MAX_EXP >= 2**(EXP_TABLE_SIZE-1). - * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices. - * For long doubles on i386, use 13. +/* Fairly portable nan check. Bitwise for i386 generated larger code. + * If you have a better version, comment this out. */ -#define EXP_TABLE_SIZE 13 +#define isnan(x) ((x) != (x)) -/* - * Set this to the maximum number of digits you want converted. - * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits. - * (20) 17 digits suffices to uniquely determine a (long) double on i386. +/* Without seminumerical functions to examine the sign bit, this is + * about the best we can do to test for '-0'. */ -#define MAX_DIGITS 20 +#define zeroisnegative(x) ((1./(x)) < 0) -/* - * Set this to the smallest integer type capable of storing a pointer. - */ -#define INT_OR_PTR int +/*****************************************************************************/ +/* Don't change anything that follows peroid!!! ;-) */ +/*****************************************************************************/ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ +#if FLT_RADIX != 2 +#error FLT_RADIX != 2 is not currently supported +#endif +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ -/* - * This is really only used to check for infinities. The macro produces - * smaller code for i386 and, since this is tested before any floating point - * calculations, it doesn't appear to suffer from the excess precision problem - * caused by the FPU that strtod had. If it causes problems, call the function - * and compile zoicheck.c with -ffloat-store. - */ -#define _zero_or_inf_check(x) ( x == (x/4) ) +#define NUM_HEX_DIGITS ((FPMAX_MANT_DIG + 3)/ 4) -/* - * Fairly portable nan check. Bitwise for i386 generated larger code. - * If you have a better version, comment this out. +/* WARNING: Adjust _fp_out_wide() below if this changes! */ +/* With 32 bit ints, we can get 9 decimal digits per block. */ +#define DIGITS_PER_BLOCK 9 +#define HEX_DIGITS_PER_BLOCK 8 + +/* Maximum number of subcases to output double is... + * 0 - sign + * 1 - padding and initial digit + * 2 - digits left of the radix + * 3 - 0s left of the radix or radix + * 4 - radix or digits right of the radix + * 5 - 0s right of the radix + * 6 - exponent + * 7 - trailing space padding + * although not all cases may occur. */ -#define isnan(x) (x != x) +#define MAX_CALLS 8 /*****************************************************************************/ -/* Don't change anything that follows peroid!!! ;-) */ -/*****************************************************************************/ -#include <float.h> +#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#define NUM_HEX_DIGIT_BLOCKS \ + ((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK) -/*****************************************************************************/ +/* WARNING: Adjust _fp_out_wide() below if this changes! */ -/* - * Set things up for the scaling power table. - */ +/* extra space for '-', '.', 'e+###', and nul */ +#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) -#if EXP_TABLE_SIZE < 6 -#error EXP_TABLE_SIZE should be at least 6 to comply with standards -#endif +/*****************************************************************************/ -#define EXP_TABLE_MAX (1U<<(EXP_TABLE_SIZE-1)) +static const char fmt[] = "inf\0INF\0nan\0NAN\0.\0,"; -/* - * Only bother checking if this is too small. - */ +#define INF_OFFSET 0 /* must be 1st */ +#define NAN_OFFSET 8 /* must be 2nd.. see hex sign handling */ +#define DECPT_OFFSET 16 +#define THOUSEP_OFFSET 18 -#if LDBL_MAX_10_EXP/2 > EXP_TABLE_MAX -#error larger EXP_TABLE_SIZE needed -#endif +#define EMPTY_STRING_OFFSET 3 -/* - * With 32 bit ints, we can get 9 digits per block. - */ -#define DIGITS_PER_BLOCK 9 +/*****************************************************************************/ +#if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP +#error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP +#endif -#if INT_MAX >= 2147483647L -#define DIGIT_BLOCK_TYPE int -#define DB_FMT "%.*d" -#elif LONG_MAX >= 2147483647L -#define DIGIT_BLOCK_TYPE long -#define DB_FMT "%.*ld" -#else -#warning need at least 32 bit longs +static const __fpmax_t exp10_table[] = +{ + 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, /* floats */ +#if FPMAX_MAX_10_EXP < 32 +#error unsupported FPMAX_MAX_10_EXP (< 32). ANSI/ISO C requires >= 37. +#endif +#if FPMAX_MAX_10_EXP >= 64 + 1e64L, #endif +#if FPMAX_MAX_10_EXP >= 128 + 1e128L, +#endif +#if FPMAX_MAX_10_EXP >= 256 + 1e256L, +#endif +#if FPMAX_MAX_10_EXP >= 512 + 1e512L, +#endif +#if FPMAX_MAX_10_EXP >= 1024 + 1e1024L, +#endif +#if FPMAX_MAX_10_EXP >= 2048 + 1e2048L, +#endif +#if FPMAX_MAX_10_EXP >= 4096 + 1e4096L +#endif +#if FPMAX_MAX_10_EXP >= 8192 +#error unsupported FPMAX_MAX_10_EXP. please increase table +#endif +}; -/* Maximum number of calls to fnprintf to output double. */ -#define MAX_CALLS 8 +#define EXP10_TABLE_SIZE (sizeof(exp10_table)/sizeof(exp10_table[0])) +#define EXP10_TABLE_MAX (1U<<(EXP10_TABLE_SIZE-1)) /*****************************************************************************/ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ -#define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#if FLT_RADIX != 2 +#error FLT_RADIX != 2 is not currently supported +#endif -/* extra space for '-', '.', 'e+###', and nul */ -#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) -/*****************************************************************************/ +#if FPMAX_MAX_EXP < -FPMAX_MIN_EXP +#error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP +#endif -static const char *fmts[] = { - "%0*d", "%.*s", ".", "inf", "INF", "nan", "NAN", "%*s" +static const __fpmax_t exp16_table[] = { + 0x1.0p4L, 0x1.0p8L, 0x1.0p16L, 0x1.0p32L, 0x1.0p64L, +#if FPMAX_MAX_EXP >= 128 + 0x1.0p128L, +#endif +#if FPMAX_MAX_EXP >= 256 + 0x1.0p256L, +#endif +#if FPMAX_MAX_EXP >= 512 + 0x1.0p512L, +#endif +#if FPMAX_MAX_EXP >= 1024 + 0x1.0p1024L, +#endif +#if FPMAX_MAX_EXP >= 2048 + 0x1.0p2048L, +#endif +#if FPMAX_MAX_EXP >= 4096 + 0x1.0p4096L, +#endif +#if FPMAX_MAX_EXP >= 8192 + 0x1.0p8192L, +#endif +#if FPMAX_MAX_EXP >= 16384 + 0x1.0p16384L +#endif +#if FPMAX_MAX_EXP >= 32768 +#error unsupported FPMAX_MAX_EXP. please increase table +#endif }; -/*****************************************************************************/ -#include <locale.h> +#define EXP16_TABLE_SIZE (sizeof(exp16_table)/sizeof(exp16_table[0])) +#define EXP16_TABLE_MAX (1U<<(EXP16_TABLE_SIZE-1)) -#ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: implement grouping for floating point -#endif +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +/*****************************************************************************/ -#ifndef __LOCALE_C_ONLY -#define CUR_LOCALE (__global_locale) -#endif /* __LOCALE_C_ONLY */ +#define FPO_ZERO_PAD (0x80 | '0') +#define FPO_STR_WIDTH (0x80 | ' '); +#define FPO_STR_PREC 'p' -size_t _dtostr(FILE * fp, long double x, struct printf_info *info) +size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc) { - long double exp_table[EXP_TABLE_SIZE]; - long double p10; - DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + __fpmax_t lower_bnd; + __fpmax_t upper_bnd = 1e9; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + uint_fast32_t digit_block; +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + uint_fast32_t base = 10; + const __fpmax_t *power_table; + int dpb = DIGITS_PER_BLOCK; + int ndb = NUM_DIGIT_BLOCKS; + int nd = DECIMAL_DIG; + int sufficient_precision = 0; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + int num_groups = 0; + int initial_group; /* This does not need to be initialized. */ + int tslen; /* This does not need to be initialized. */ + int nblk2; /* This does not need to be initialized. */ + const char *ts; /* This does not need to be initialized. */ +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ int i, j; int round, o_exp; int exp, exp_neg; int width, preci; + int cnt; char *s; char *e; + intptr_t pc_fwi[3*MAX_CALLS]; + intptr_t *ppc; + intptr_t *ppc_last; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: The size of exp_buf[] should really be determined by the float constants. +#endif /* __UCLIBC_MJN3_ONLY__ */ + char exp_buf[16]; char buf[BUF_SIZE]; - INT_OR_PTR pc_fwi[2*MAX_CALLS]; - INT_OR_PTR *ppc; - char exp_buf[8]; - char drvr[8]; - char *pdrvr; - int npc; - int cnt; - char sign_str[2]; + char sign_str[6]; /* Last 2 are for 1st digit + nul. */ char o_mode; char mode; - /* check that INT_OR_PTR is sufficiently large */ - assert( sizeof(INT_OR_PTR) == sizeof(char *) ); width = info->width; preci = info->prec; mode = info->spec; - if (mode == 'a') { - mode = 'g'; /* TODO -- fix */ - } - if (mode == 'A') { - mode = 'G'; /* TODO -- fix */ + + *exp_buf = 'e'; + if ((mode|0x20) == 'a') { +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + *exp_buf = 'p'; + if (preci < 0) { + preci = NUM_HEX_DIGITS; + sufficient_precision = 1; + } +#else + mode += ('g' - 'a'); +#endif } if (preci < 0) { @@ -1733,134 +1917,196 @@ size_t _dtostr(FILE * fp, long double x, struct printf_info *info) } else if (PRINT_INFO_FLAG_VAL(info,space)) { |