summaryrefslogtreecommitdiff
path: root/libc/stdio
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2003-08-01 20:08:59 +0000
committerManuel Novoa III <mjn3@codepoet.org>2003-08-01 20:08:59 +0000
commit1217289737588e65b088b3535428b27c7287d699 (patch)
tree6a292ac767d219702e26a6a2111737f84a96900c /libc/stdio
parent32b76c5ec3c257b7287913d0d1a96e0cbb2e9c6a (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/Makefile12
-rw-r--r--libc/stdio/old_vfprintf.c9
-rw-r--r--libc/stdio/printf.c1151
-rw-r--r--libc/stdio/scanf.c2370
-rw-r--r--libc/stdio/stdio.c200
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)) {
*sign_str = ' ';
}
-/* *sign_str = flag[FLAG_PLUS]; */
+
*(sign_str+1) = 0;
- if (isnan(x)) { /* nan check */
- pdrvr = drvr + 1;
- *pdrvr++ = 5 + (mode < 'a');
- pc_fwi[2] = 3;
- info->pad = ' ';
-/* flag[FLAG_0_PAD] = 0; */
- goto EXIT_SPECIAL;
+ pc_fwi[5] = INF_OFFSET;
+ if (isnan(x)) { /* First, check for nan. */
+ pc_fwi[5] = NAN_OFFSET;
+ goto INF_NAN;
}
- if (x == 0) { /* handle 0 now to avoid false positive */
+ if (x == 0) { /* Handle 0 now to avoid false positive. */
+#if 1
+ if (zeroisnegative(x)) { /* Handle 'signed' zero. */
+ *sign_str = '-';
+ }
+#endif
exp = -1;
goto GENERATE_DIGITS;
}
- if (x < 0) { /* convert negatives to positives */
+ if (x < 0) { /* Convert negatives to positives. */
*sign_str = '-';
x = -x;
}
- if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */
- pdrvr = drvr + 1;
- *pdrvr++ = 3 + + (mode < 'a');
- pc_fwi[2] = 3;
+ if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */
+ INF_NAN:
info->pad = ' ';
-/* flag[FLAG_0_PAD] = 0; */
+ ppc = pc_fwi + 6;
+ pc_fwi[3] = FPO_STR_PREC;
+ pc_fwi[4] = 3;
+ if (mode < 'a') {
+ pc_fwi[5] += 4;
+ }
+ pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]);
goto EXIT_SPECIAL;
}
- /* need to build the scaling table */
- for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) {
- exp_table[i] = p10;
- p10 *= p10;
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning TODO: Clean up defines when hexadecimal float notation is unsupported.
+#endif /* __UCLIBC_MJN3_ONLY__ */
+
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+
+ if ((mode|0x20) == 'a') {
+ lower_bnd = 0x1.0p31L;
+ upper_bnd = 0x1.0p32L;
+ power_table = exp16_table;
+ exp = HEX_DIGITS_PER_BLOCK - 1;
+ i = EXP16_TABLE_SIZE;
+ j = EXP16_TABLE_MAX;
+ dpb = HEX_DIGITS_PER_BLOCK;
+ ndb = NUM_HEX_DIGIT_BLOCKS;
+ nd = NUM_HEX_DIGITS;
+ base = 16;
+ } else {
+ lower_bnd = 1e8;
+/* upper_bnd = 1e9; */
+ power_table = exp10_table;
+ exp = DIGITS_PER_BLOCK - 1;
+ i = EXP10_TABLE_SIZE;
+ j = EXP10_TABLE_MAX;
+/* dpb = DIGITS_PER_BLOCK; */
+/* ndb = NUM_DIGIT_BLOCKS; */
+/* base = 10; */
}
+
+
+#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+
+#define lower_bnd 1e8
+#define upper_bnd 1e9
+#define power_table exp10_table
+#define dpb DIGITS_PER_BLOCK
+#define base 10
+#define ndb NUM_DIGIT_BLOCKS
+#define nd DECIMAL_DIG
+
+ exp = DIGITS_PER_BLOCK - 1;
+ i = EXP10_TABLE_SIZE;
+ j = EXP10_TABLE_MAX;
+
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+
exp_neg = 0;
- if (x < 1e8) { /* do we need to scale up or down? */
+ if (x < lower_bnd) { /* Do we need to scale up or down? */
exp_neg = 1;
}
- exp = DIGITS_PER_BLOCK - 1;
-
- i = EXP_TABLE_SIZE;
- j = EXP_TABLE_MAX;
- while ( i-- ) { /* scale x such that 1e8 <= x < 1e9 */
+ do {
+ --i;
if (exp_neg) {
- if (x * exp_table[i] < 1e9) {
- x *= exp_table[i];
+ if (x * power_table[i] < upper_bnd) {
+ x *= power_table[i];
exp -= j;
}
} else {
- if (x / exp_table[i] >= 1e8) {
- x /= exp_table[i];
+ if (x / power_table[i] >= lower_bnd) {
+ x /= power_table[i];
exp += j;
}
}
j >>= 1;
- }
- if (x >= 1e9) { /* handle bad rounding case */
- x /= 10;
+ } while (i);
+ if (x >= upper_bnd) { /* Handle bad rounding case. */
+ x /= power_table[0];
++exp;
}
- assert(x < 1e9);
+ assert(x < upper_bnd);
GENERATE_DIGITS:
- s = buf + 2; /* leave space for '\0' and '0' */
-#if 1
-#define ONE_E_NINE 1000000000L
-#else
-#define ONE_E_NINE 1e9
-#endif
- for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) {
- digit_block = (DIGIT_BLOCK_TYPE) x;
- x = (x - digit_block) * ONE_E_NINE;
- s += sprintf(s, DB_FMT, DIGITS_PER_BLOCK, digit_block);
- }
+ s = buf + 2; /* Leave space for '\0' and '0'. */
+ i = 0;
+ do {
+ digit_block = (uint_fast32_t) x;
+ assert(digit_block < upper_bnd);
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Can rounding be a problem?
+#endif /* __UCLIBC_MJN3_ONLY__ */
+ x = (x - digit_block) * upper_bnd;
+ s += dpb;
+ j = 0;
+ do {
+ s[- ++j] = '0' + (digit_block % base);
+ digit_block /= base;
+ } while (j < dpb);
+ } while (++i < ndb);
/*************************************************************************/
- *exp_buf = 'e';
if (mode < 'a') {
- *exp_buf = 'E';
+ *exp_buf -= ('a' - 'A'); /* e->E and p->P */
mode += ('a' - 'A');
}
o_mode = mode;
-
- round = preci;
-
- if ((mode == 'g') && (round > 0)){
- --round;
+ if ((mode == 'g') && (preci > 0)){
+ --preci;
}
+ round = preci;
if (mode == 'f') {
round += exp;
if (round < -1) {
- memset(buf, '0', MAX_DIGITS);
+ memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */
exp = -1;
round = -1;
}
}
s = buf;
- *s++ = 0; /* terminator for rounding and 0-triming */
- *s = '0'; /* space to round */
+ *s++ = 0; /* Terminator for rounding and 0-triming. */
+ *s = '0'; /* Space to round. */
i = 0;
- e = s + MAX_DIGITS + 1;
- if (round < MAX_DIGITS) {
+ e = s + nd + 1;
+ if (round < nd) {
e = s + round + 2;
- if (*e >= '5') {
+ if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */
i = 1;
}
}
- do { /* handle rounding and trim trailing 0s */
- *--e += i; /* add the carry */
- } while ((*e == '0') || (*e > '9'));
+ do { /* Handle rounding and trim trailing 0s. */
+ *--e += i; /* Add the carry. */
+ } while ((*e == '0') || (*e > '0' - 1 + base));
+
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ if ((mode|0x20) == 'a') {
+ char *q;
+
+ for (q = e ; *q ; --q) {
+ if (*q > '9') {
+ *q += (*exp_buf - ('p' - 'a') - '9' - 1);
+ }
+ }
+
+ if (e > s) {
+ exp *= 4; /* Change from base 16 to base 2. */
+ }
+ }
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
o_exp = exp;
- if (e <= s) { /* we carried into extra digit */
+ if (e <= s) { /* We carried into an extra digit. */
++o_exp;
- e = s; /* needed if all 0s */
+ e = s; /* Needed if all 0s. */
} else {
++s;
}
- *++e = 0; /* ending nul char */
+ *++e = 0; /* Terminating nul char. */
if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) {
mode = 'f';
@@ -1871,34 +2117,73 @@ size_t _dtostr(FILE * fp, long double x, struct printf_info *info)
o_exp = 0;
}
- if (o_exp < 0) {
- *--s = '0'; /* fake the first digit */
+ if (o_exp < 0) { /* Exponent is < 0, so */
+ *--s = '0'; /* fake the first 0 digit. */
}
- pdrvr = drvr+1;
- ppc = pc_fwi+2;
-
- *pdrvr++ = 0;
- *ppc++ = 1;
- *ppc++ = (INT_OR_PTR)(*s++ - '0');
+ pc_fwi[3] = FPO_ZERO_PAD;
+ pc_fwi[4] = 1;
+ pc_fwi[5] = (intptr_t)(sign_str + 4);
+ sign_str[4] = *s++;
+ sign_str[5] = 0;
+ ppc = pc_fwi + 6;
- i = e - s; /* total digits */
+ i = e - s; /* Total digits is 'i'. */
if (o_exp >= 0) {
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+
+ const char *p;
+
+ if (PRINT_INFO_FLAG_VAL(info,group)
+ && *(p = __UCLIBC_CURLOCALE_DATA.grouping)
+ ) {
+ int nblk1;
+
+ nblk2 = nblk1 = *p;
+ if (*++p) {
+ nblk2 = *p;
+ assert(!*++p);
+ }
+
+ if (o_exp >= nblk1) {
+ num_groups = (o_exp - nblk1) / nblk2 + 1;
+ initial_group = (o_exp - nblk1) % nblk2;
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ if (PRINT_INFO_FLAG_VAL(info,wide)) {
+ /* _fp_out_wide() will fix this up. */
+ ts = fmt + THOUSEP_OFFSET;
+ tslen = 1;
+ } else {
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ ts = __UCLIBC_CURLOCALE_DATA.thousands_sep;
+ tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len;
+#ifdef __UCLIBC_HAS_WCHAR__
+ }
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+ width -= num_groups * tslen;
+ }
+ }
+
+
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+ ppc[0] = FPO_STR_PREC;
+ ppc[2] = (intptr_t)(s);
if (o_exp >= i) { /* all digit(s) left of decimal */
- *pdrvr++ = 1;
- *ppc++ = i;
- *ppc++ = (INT_OR_PTR)(s);
+ ppc[1] = i;
+ ppc += 3;
o_exp -= i;
i = 0;
if (o_exp>0) { /* have 0s left of decimal */
- *pdrvr++ = 0;
- *ppc++ = o_exp;
- *ppc++ = 0;
+ ppc[0] = FPO_ZERO_PAD;
+ ppc[1] = o_exp;
+ ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
+ ppc += 3;
}
} else if (o_exp > 0) { /* decimal between digits */
- *pdrvr++ = 1;
- *ppc++ = o_exp;
- *ppc++ = (INT_OR_PTR)(s);
+ ppc[1] = o_exp;
+ ppc += 3;
s += o_exp;
i -= o_exp;
}
@@ -1906,105 +2191,221 @@ size_t _dtostr(FILE * fp, long double x, struct printf_info *info)
}
if (PRINT_INFO_FLAG_VAL(info,alt)
-/* flag[FLAG_HASH] */
- || (i) || ((o_mode != 'g') && (preci > 0))) {
+ || (i)
+ || ((o_mode != 'g')
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ && (o_mode != 'a')
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+ && (preci > 0))
+ ) {
+ ppc[0] = FPO_STR_PREC;
#ifdef __LOCALE_C_ONLY
- *pdrvr++ = 2; /* need decimal */
- *ppc++ = 1; /* needed for width calc */
- ppc++;
+ ppc[1] = 1;
+ ppc[2] = (intptr_t)(fmt + DECPT_OFFSET);
#else /* __LOCALE_C_ONLY */
- *pdrvr++ = 1;
- *ppc++ = strlen(CUR_LOCALE.decimal_point);
- *ppc++ = (INT_OR_PTR)(CUR_LOCALE.decimal_point);
+#ifdef __UCLIBC_HAS_WCHAR__
+ if (PRINT_INFO_FLAG_VAL(info,wide)) {
+ /* _fp_out_wide() will fix this up. */
+ ppc[1] = 1;
+ ppc[2] = (intptr_t)(fmt + DECPT_OFFSET);
+ } else {
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ ppc[1] = __UCLIBC_CURLOCALE_DATA.decimal_point_len;
+ ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE_DATA.decimal_point);
+#ifdef __UCLIBC_HAS_WCHAR__
+ }
+#endif /* __UCLIBC_HAS_WCHAR__ */
#endif /* __LOCALE_C_ONLY */
+ ppc += 3;
}
- if (++o_exp < 0) { /* have 0s right of decimal */
- *pdrvr++ = 0;
- *ppc++ = -o_exp;
- *ppc++ = 0;
+ if (++o_exp < 0) { /* Have 0s right of decimal. */
+ ppc[0] = FPO_ZERO_PAD;
+ ppc[1] = -o_exp;
+ ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
+ ppc += 3;
}
- if (i) { /* have digit(s) right of decimal */
- *pdrvr++ = 1;
- *ppc++ = i;
- *ppc++ = (INT_OR_PTR)(s);
+ if (i) { /* Have digit(s) right of decimal. */
+ ppc[0] = FPO_STR_PREC;
+ ppc[1] = i;
+ ppc[2] = (intptr_t)(s);
+ ppc += 3;
}
- if (o_mode != 'g') {
+ if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt))
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ && !sufficient_precision
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+ ) {
i -= o_exp;
- if (i < preci) { /* have 0s right of digits */
+ if (i < preci) { /* Have 0s right of digits. */
i = preci - i;
- *pdrvr++ = 0;
- *ppc++ = i;
- *ppc++ = 0;
+ ppc[0] = FPO_ZERO_PAD;
+ ppc[1] = i;
+ ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
+ ppc += 3;
}
}
- /* build exponent string */
+ /* Build exponent string. */
if (mode != 'f') {
- *pdrvr++ = 1;
- *ppc++ = sprintf(exp_buf,"%c%+.2d", *exp_buf, exp);
- *ppc++ = (INT_OR_PTR) exp_buf;
+ char *p = exp_buf + sizeof(exp_buf);
+ char exp_char = *exp_buf;
+ char exp_sign = '+';
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1));
+#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+#define min_exp_dig_plus_2 (2+2)
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+
+ if (exp < 0) {
+ exp_sign = '-';
+ exp = -exp;
+ }
+
+ *--p = 0; /* nul-terminate */
+ j = 2; /* Count exp_char and exp_sign. */
+ do {
+ *--p = '0' + (exp % 10);
+ exp /= 10;
+ } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */
+ *--p = exp_sign;
+ *--p = exp_char;
+
+ ppc[0] = FPO_STR_PREC;
+ ppc[1] = j;
+ ppc[2] = (intptr_t)(p);
+ ppc += 3;
}
EXIT_SPECIAL:
- npc = pdrvr - drvr;
- ppc = pc_fwi + 2;
- for (i=1 ; i< npc ; i++) {
- width -= *(ppc++);
- ppc++;
- }
- i = 0;
- if (*sign_str) {
- i = 1;
- }
- width -= i;
- if (width <= 0) {
- width = 0;
- } else {
- if (PRINT_INFO_FLAG_VAL(info,left)) { /* padding on right */
-/* flag[FLAG_MINUS_LJUSTIFY] */
- ++npc;
- *pdrvr++ = 7;
- *ppc = width;
- *++ppc = (INT_OR_PTR)("");
- width = 0;
+ ppc_last = ppc;
+ ppc = pc_fwi + 4; /* Need width fields starting with second. */
+ do {
+ width -= *ppc;
+ ppc += 3;
+ } while (ppc < ppc_last);
+
+ ppc = pc_fwi;
+ ppc[0] = FPO_STR_WIDTH;
+ ppc[1] = i = ((*sign_str) != 0);
+ ppc[2] = (intptr_t) sign_str;
+
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */
+ /* Hex and not inf or nan, so prefix with 0x. */
+ char *h = sign_str + i;
+ *h = '0';
+ *++h = 'x' - 'p' + *exp_buf;
+ *++h = 0;
+ ppc[1] = (i += 2);
+ }
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+
+ if ((width -= i) > 0) {
+ if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */
+ ppc_last[0] = FPO_STR_WIDTH;
+ ppc_last[1] = width;
+ ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
+ ppc_last += 3;
} else if (info->pad == '0') { /* 0 padding */
-/* (flag[FLAG_0_PAD] == '0') */
- pc_fwi[2] += width;
- width = 0;
+ ppc[4] += width; /* Pad second field. */
+ } else {
+ ppc[1] += width; /* Pad first (sign) field. */
}
}
- *drvr = 7;
- ppc = pc_fwi;
- *ppc++ = width + i;
- *ppc = (INT_OR_PTR) sign_str;
- pdrvr = drvr;
- ppc = pc_fwi;
cnt = 0;
- for (i=0 ; i<npc ; i++) {
-#if 1
- fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)),
- (INT_OR_PTR)(*(ppc+1)));
-#else
- j = fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)),
- (INT_OR_PTR)(*(ppc+1)));
- assert(j == *ppc);
-#endif
-/* if (size > *ppc) { */
-/* size -= *ppc; */
-/* } */
- cnt += *ppc; /* to avoid problems if j == -1 */
- ppc += 2;
- }
+
+ do {
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+
+ if ((ppc == pc_fwi + 6) && num_groups) {
+ const char *gp = (const char *) ppc[2];
+ int len = ppc[1];
+ int blk = initial_group;
+
+ cnt += num_groups * tslen; /* Adjust count now for sep chars. */
+
+/* printf("\n"); */
+ do {
+ if (!blk) { /* Initial group could be 0 digits long! */
+ blk = nblk2;
+ } else if (len >= blk) { /* Enough digits for a group. */
+/* printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */
+ fp_outfunc(fp, *ppc, blk, (intptr_t) gp);
+ assert(gp);
+ if (*gp) {
+ gp += blk;
+ }
+ len -= blk;
+ } else { /* Transition to 0s. */
+/* printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */
+ if (len) {
+/* printf("len\n"); */
+ fp_outfunc(fp, *ppc, len, (intptr_t) gp);
+ gp += len;
+ }
+
+ if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */
+/* printf("zeropad\n"); */
+ cnt += ppc[1];
+ ppc += 3;
+ gp = (const char *) ppc[2];
+ blk -= len; /* blk > len, so blk still > 0. */
+ len = ppc[1];
+ continue; /* Don't decrement num_groups here. */
+ } else {
+ assert(num_groups == 0);
+ break;
+ }
+ }
+
+ if (num_groups <= 0) {
+ break;
+ }
+ --num_groups;
+
+ fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts);
+ blk = nblk2;
+
+/* printf("num_groups=%d blk=%d\n", num_groups, blk); */
+
+ } while (1);
+ } else
+
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+
+ fp_outfunc(fp, *ppc, ppc[1], ppc[2]); /* NOTE: Remember 'else' above! */
+
+ cnt += ppc[1];
+ ppc += 3;
+ } while (ppc < ppc_last);
return cnt;
}
+
#endif
/**********************************************************************/
#ifdef L__store_inttype
-/* TODO -- right now, assumes intmax_t is either long or long long */
+
+/* Right now, we assume intmax_t is either long or long long */
+
+#ifdef INTMAX_MAX
+
+#ifdef LLONG_MAX
+
+#if INTMAX_MAX > LLONG_MAX
+#error INTMAX_MAX > LLONG_MAX! The printf code needs to be updated!
+#endif
+
+#elif INTMAX_MAX > LONG_MAX
+
+#error No LLONG_MAX and INTMAX_MAX > LONG_MAX! The printf code needs to be updated!
+
+#endif /* LLONG_MAX */
+
+#endif /* INTMAX_MAX */
/* We assume int may be short or long, but short and long are different. */
@@ -2107,6 +2508,7 @@ extern uintmax_t _load_inttype(int desttype, register const void *src,
* In other words, we don't currently support glibc's 'I' flag.
* We do accept it, but it is currently ignored. */
+static void _charpad(FILE * __restrict stream, int padchar, size_t numpad);
#ifdef L_vfprintf
@@ -2117,6 +2519,23 @@ extern uintmax_t _load_inttype(int desttype, register const void *src,
#define _PPFS_init _ppfs_init
#define OUTPUT(F,S) fputs(S,F)
#define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream)
+#define FP_OUT _fp_out_narrow
+
+#ifdef __STDIO_PRINTF_FLOAT
+
+static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
+{
+ if (type & 0x80) { /* Some type of padding needed. */
+ int buflen = strlen((const char *) buf);
+ if ((len -= buflen) > 0) {
+ _charpad(fp, (type & 0x7f), len);
+ }
+ len = buflen;
+ }
+ OUTNSTR(fp, (const char *) buf, len);
+}
+
+#endif /* __STDIO_PRINTF_FLOAT */
#else /* L_vfprintf */
@@ -2127,6 +2546,7 @@ extern uintmax_t _load_inttype(int desttype, register const void *src,
#define _PPFS_init _ppwfs_init
#define OUTPUT(F,S) fputws(S,F)
#define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream)
+#define FP_OUT _fp_out_wide
static void _outnstr(FILE *stream, const char *s, size_t wclen)
{
@@ -2139,13 +2559,69 @@ static void _outnstr(FILE *stream, const char *s, size_t wclen)
todo = wclen;
while (todo) {
- r = mbsrtowcs(wbuf, &s, sizeof(wbuf)/sizeof(wbuf[0]), &mbstate);
+ r = mbsrtowcs(wbuf, &s,
+ ((todo <= sizeof(wbuf)/sizeof(wbuf[0]))
+ ? todo
+ : sizeof(wbuf)/sizeof(wbuf[0])),
+ &mbstate);
assert(((ssize_t)r) > 0);
_outnwcs(stream, wbuf, r);
todo -= r;
}
}
+#ifdef __STDIO_PRINTF_FLOAT
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning TODO: Move defines from _fpmaxtostr. Put them in a common header.
+#endif
+
+/* The following defines are from _fpmaxtostr.*/
+#define DIGITS_PER_BLOCK 9
+#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
+#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
+
+static void _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
+{
+ wchar_t wbuf[BUF_SIZE];
+ const char *s = (const char *) buf;
+ int i;
+
+ if (type & 0x80) { /* Some type of padding needed */
+ int buflen = strlen(s);
+ if ((len -= buflen) > 0) {
+ _charpad(fp, (type & 0x7f), len);
+ }
+ len = buflen;
+ }
+
+ if (len > 0) {
+ i = 0;
+ do {
+#ifdef __LOCALE_C_ONLY
+ wbuf[i] = s[i];
+#else /* __LOCALE_C_ONLY */
+
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ if (s[i] == ',') {
+ wbuf[i] = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc;
+ } else
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+ if (s[i] == '.') {
+ wbuf[i] = __UCLIBC_CURLOCALE_DATA.decimal_point_wc;
+ } else {
+ wbuf[i] = s[i];
+ }
+#endif /* __LOCALE_C_ONLY */
+
+ } while (++i < len);
+
+ OUTNSTR(fp, wbuf, len);
+ }
+}
+
+#endif /* __STDIO_PRINTF_FLOAT */
+
static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0)
{
static const wchar_t invalid_wcs[] = L"Invalid wide format string.";
@@ -2153,7 +2629,9 @@ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0)
/* 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 = (const char *) fmt0;
ppfs->info._flags = FLAG_WIDESTREAM;
@@ -2210,6 +2688,7 @@ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0)
ppfs->fmtpos = (const char *) fmt0; /* rewind */
}
+#ifdef NL_ARGMAX
/* If we have positional args, make sure we know all the types. */
{
register int *p = ppfs->argtype;
@@ -2221,6 +2700,7 @@ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0)
++p;
}
}
+#endif /* NL_ARGMAX */
return 0;
}
@@ -2283,8 +2763,8 @@ static int _do_one_spec(FILE * __restrict stream,
int prefix_num = PREFIX_NONE;
char padchar = ' ';
#ifdef __UCLIBC_MJN3_ONLY__
-#warning REMINDER: buf size
-#endif
+#warning TODO: Determine appropriate buf size.
+#endif /* __UCLIBC_MJN3_ONLY__ */
/* TODO: buf needs to be big enough for any possible error return strings
* and also for any locale-grouped long long integer strings generated.
* This should be large enough for any of the current archs/locales, but
@@ -2304,24 +2784,28 @@ static int _do_one_spec(FILE * __restrict stream,
/* Deal with the argptr vs argvalue issue. */
#ifdef __va_arg_ptr
argptr = (const void * const *) ppfs->argptr;
+#ifdef NL_ARGMAX
if (ppfs->maxposarg > 0) { /* Using positional args... */
argptr += ppfs->argnumber[2] - 1;
}
+#endif /* NL_ARGMAX */
#else
/* Need to build a local copy... */
{
register argvalue_t *p = ppfs->argvalue;
int i;
+#ifdef NL_ARGMAX
if (ppfs->maxposarg > 0) { /* Using positional args... */
p += ppfs->argnumber[2] - 1;
}
+#endif /* NL_ARGMAX */
for (i = 0 ; i < ppfs->num_data_args ; i++ ) {
argptr[i] = (void *) p++;
}
}
#endif
{
- register char *s; /* TODO: Should s be unsigned char * ? */
+ register char *s = NULL; /* TODO: Should s be unsigned char * ? */
if (ppfs->conv_num == CONV_n) {
_store_inttype(*(void **)*argptr,
@@ -2331,7 +2815,12 @@ static int _do_one_spec(FILE * __restrict stream,
}
if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */
alphacase = __UIM_LOWER;
-#ifndef __LOCALE_C_ONLY
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#ifdef L_vfprintf
+#warning CONSIDER: Should we ignore these flags if stub locale? What about custom specs?
+#endif
+#endif /* __UCLIBC_MJN3_ONLY__ */
if ((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10) {
if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) {
alphacase = __UIM_GROUP;
@@ -2340,7 +2829,7 @@ static int _do_one_spec(FILE * __restrict stream,
alphacase |= 0x80;
}
}
-#endif /* __LOCALE_C_ONLY */
+
if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */
if (ppfs->conv_num == CONV_X) {
alphacase = __UIM_UPPER;
@@ -2356,8 +2845,10 @@ static int _do_one_spec(FILE * __restrict stream,
padchar = ppfs->info.pad;
}
#ifdef __UCLIBC_MJN3_ONLY__
-#warning if using outdigits and/or grouping, how should we interpret precision?
+#ifdef L_vfprintf
+#warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision?
#endif
+#endif /* __UCLIBC_MJN3_ONLY__ */
s = _uintmaxtostr(buf + sizeof(buf) - 1,
(uintmax_t)
_load_inttype(*argtype & __PA_INTMASK,
@@ -2413,23 +2904,18 @@ static int _do_one_spec(FILE * __restrict stream,
}
numfill = ((numfill > SLEN) ? numfill - SLEN : 0);
} else if (ppfs->conv_num <= CONV_A) { /* floating point */
-#ifdef L_vfwprintf
-#ifdef __UCLIBC_MJN3_ONLY__
-#warning fix dtostr
-#endif
- return -1;
-#else /* L_vfwprintf */
#ifdef __STDIO_PRINTF_FLOAT
- *count += _dtostr(stream,
- (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double)
- ? *(long double *) *argptr
- : (long double) (* (double *) *argptr)),
- &ppfs->info);
+ *count +=
+ _fpmaxtostr(stream,
+ (__fpmax_t)
+ (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double)
+ ? *(long double *) *argptr
+ : (long double) (* (double *) *argptr)),
+ &ppfs->info, FP_OUT );
return 0;
#else /* __STDIO_PRINTF_FLOAT */
return -1; /* TODO -- try to continue? */
#endif /* __STDIO_PRINTF_FLOAT */
-#endif /* L_vfwprintf */
} else if (ppfs->conv_num <= CONV_S) { /* wide char or string */
#ifdef L_vfprintf
@@ -2467,7 +2953,9 @@ static int _do_one_spec(FILE * __restrict stream,
if (ppfs->conv_num == CONV_s) { /* string */
s = *((char **) (*argptr));
if (s) {
+#ifdef __STDIO_PRINTF_M_SUPPORT
SET_STRING_LEN:
+#endif
slen = strnlen(s, ((ppfs->info.prec >= 0)
? ppfs->info.prec : SIZE_MAX));
} else {
@@ -2505,11 +2993,13 @@ static int _do_one_spec(FILE * __restrict stream,
if (ppfs->conv_num == CONV_s) { /* string */
#ifdef __UCLIBC_MJN3_ONLY__
-#warning Fix %s for vfwprintf... output upto illegal sequence?
-#endif
+#warning TODO: Fix %s for vfwprintf... output upto illegal sequence?
+#endif /* __UCLIBC_MJN3_ONLY__ */
s = *((char **) (*argptr));
if (s) {
+#ifdef __STDIO_PRINTF_M_SUPPORT
SET_STRING_LEN:
+#endif
/* We use an awful uClibc-specific hack here, passing
* (wchar_t*) &mbstate as the conversion destination.
* This signals uClibc's mbsrtowcs that we want a
@@ -2544,6 +3034,7 @@ static int _do_one_spec(FILE * __restrict stream,
goto SET_STRING_LEN;
#endif
} else {
+#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
assert(ppfs->conv_num == CONV_custom0);
s = _custom_printf_spec;
@@ -2561,13 +3052,16 @@ static int _do_one_spec(FILE * __restrict stream,
return 0;
}
} while (++s < (_custom_printf_spec + MAX_USER_SPEC));
+#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
assert(0);
return -1;
}
#ifdef __UCLIBC_MJN3_ONLY__
-#warning if using outdigits and/or grouping, how should we pad?
+#ifdef L_vfprintf
+#warning CONSIDER: If using outdigits and/or grouping, how should we pad?
#endif
+#endif /* __UCLIBC_MJN3_ONLY__ */
{
size_t t;
@@ -2595,6 +3089,7 @@ static int _do_one_spec(FILE * __restrict stream,
#ifdef __UCLIBC_HAS_WCHAR__
if (!ws) {
+ assert(s);
_outnstr(stream, s, slen);
} else { /* wide string */
size_t t;
@@ -2606,7 +3101,6 @@ static int _do_one_spec(FILE * __restrict stream,
_outnstr(stream, buf, t);
slen -= t;
}
- ws = NULL; /* Reset ws. */
}
#else /* __UCLIBC_HAS_WCHAR__ */
_outnstr(stream, s, slen);
@@ -2615,10 +3109,10 @@ static int _do_one_spec(FILE * __restrict stream,
#else /* L_vfprintf */
if (!ws) {
+ assert(s);
_outnstr(stream, s, SLEN);
} else {
_outnwcs(stream, ws, SLEN);
- ws = NULL; /* Reset ws. */
}
#endif /* L_vfprintf */
@@ -2644,6 +3138,9 @@ int VFPRINTF (FILE * __restrict stream,
if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */
OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos,
STRLEN((const FMT_TYPE *)(ppfs.fmtpos)));
+#if defined(L_vfprintf) && !defined(NDEBUG)
+ fprintf(stderr,"\nIMbS: \"%s\"\n\n", format);
+#endif
count = -1;
} else {
_ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */
diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c
index 9ac3d3c9c..48e9344c0 100644
--- a/libc/stdio/scanf.c
+++ b/libc/stdio/scanf.c
@@ -1,130 +1,178 @@
-
-/*
- * Modified by Manuel Novoa III Mar 13, 2001
- *
- * The vfscanf routine was completely rewritten to add features and remove
- * bugs. The function __strtold, based on my strtod code in stdlib, was
- * added to provide floating point support for the scanf functions.
- *
- * So far they pass the test cases from glibc-2.1.3, except in two instances.
- * In one case, the test appears to be broken. The other case is something
- * I need to research further. This version of scanf assumes it can only
- * peek one character ahead. Apparently, glibc looks further. The difference
- * can be seen when parsing a floating point value in the character
- * sequence "100ergs". glibc is able to back up before the 'e' and return
- * a value of 100, whereas this scanf reports a bad match with the stream
- * pointer at 'r'. A similar situation can also happen when parsing hex
- * values prefixed by 0x or 0X; a failure would occur for "0xg". In order to
- * fix this, I need to rework the "ungetc" machinery in stdio.c again.
- * I do have one reference though, that seems to imply scanf has a single
- * character of lookahead.
- *
- * May 20, 2001
+/* Copyright (C) 2002, 2003 Manuel Novoa III
*
- * Quote from ANSI/ISO C99 standard:
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
*
- * fscanf pushes back at most one input character onto the input stream.
- * Therefore, some sequences that are acceptable to strtod, strtol, etc.,
- * are unacceptable to fscanf.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
*
- * So uClibc's *scanf functions conform to the standard, and glibc's
- * implementation doesn't for the "100ergs" case mentioned above.
- *
- * Sep 6, 2002
- * Patch from Tero_Lyytikäinen <tero@paravant.fi> to fix bug in matchchar case.
- *
- * May 15, 2003
- * Hopefully fix handling of 0 bytes with %s, %c, and %[ specifiers.
- *
- * July 17, 2003
- * Bug fix from Peter Kjellerstedt <peter.kjellerstedt@axis.com>. vfscanf was
- * not setting the FILE bufread member to flag the end of the buffer.
- * Also, do not set bufgetc member if getc macro support is disabled.
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Aug 1, 2003
+ * New *scanf implementation with lots of bug fixes and *wscanf support.
+ * Also now optionally supports hexadecimal float notation, positional
+ * args, and glibc locale-specific digit grouping. Should now be
+ * standards compliant.
*/
+
#define _ISOC99_SOURCE /* for LLONG_MAX primarily... */
#define _GNU_SOURCE
#define _STDIO_UTILITY
+#include <features.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
+#include <stdint.h>
+#include <errno.h>
+#include <printf.h>
+
+#ifdef __UCLIBC_HAS_WCHAR__
+#include <bits/uClibc_uwchar.h>
+#include <wchar.h>
+#include <wctype.h>
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+#include <langinfo.h>
+#include <locale.h>
+
+#include <assert.h>
+#include <limits.h>
#ifdef __STDIO_THREADSAFE
#include <stdio_ext.h>
#include <pthread.h>
#endif /* __STDIO_THREADSAFE */
-#ifdef L_scanf
-#ifdef __STDC__
-int scanf(const char *fmt, ...)
+#ifdef __UCLIBC_HAS_FLOATS__
+#include <float.h>
+#include <bits/uClibc_fpmax.h>
+#endif /* __UCLIBC_HAS_FLOATS__ */
+
+#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
+#ifdef L_vfscanf
+/* only emit this once */
+#warning Forcing undef of __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ until implemented!
+#endif
+#undef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
+#endif
+
+extern void _store_inttype(void *dest, int desttype, uintmax_t val);
+
+#ifdef LLONG_MAX
+
+extern unsigned long long
+_stdlib_strto_ll(register const char * __restrict str,
+ char ** __restrict endptr, int base, int sflag);
+#if (ULLONG_MAX == UINTMAX_MAX)
+#define STRTOUIM(s,e,b,sf) _stdlib_strto_ll(s,e,b,sf)
+#endif
+
+#else /* LLONG_MAX */
+
+extern unsigned long
+_stdlib_strto_l(register const char * __restrict str,
+ char ** __restrict endptr, int base, int sflag);
+
+#if (ULONG_MAX == UINTMAX_MAX)
+#define STRTOUIM(s,e,b,sf) _stdlib_strto_l(s,e,b,sf)
+#endif
+
+#endif /* LLONG_MAX */
+
+#ifndef STRTOUIM
+#error STRTOUIM conversion function is undefined!
+#endif
+
+/**********************************************************************/
+
+/* The standards require EOF < 0. */
+#if EOF >= CHAR_MIN
+#define __isdigit_char_or_EOF(C) __isdigit_char((C))
#else
-int scanf(fmt, va_alist)
-__const char *fmt;
-va_dcl
+#define __isdigit_char_or_EOF(C) __isdigit_int((C))
#endif
+
+/**********************************************************************/
+#ifdef L_fscanf
+
+int fscanf(FILE * __restrict stream, const char * __restrict format, ...)
{
- va_list ptr;
+ va_list arg;
int rv;
- va_start(ptr, fmt);
- rv = vfscanf(stdin, fmt, ptr);
- va_end(ptr);
+ va_start(arg, format);
+ rv = vfscanf(stream, format, arg);
+ va_end(arg);
+
return rv;
}
-#endif
-#ifdef L_sscanf
-#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
-#warning skipping sscanf since no buffering and no custom streams!
-#else
+#endif
+/**********************************************************************/
+#ifdef L_scanf
-int sscanf(const char *sp, const char *fmt, ...)
+int scanf(const char * __restrict format, ...)
{
- va_list ptr;
+ va_list arg;
int rv;
- va_start(ptr, fmt);
- rv = vsscanf(sp, fmt, ptr);
- va_end(ptr);
+ va_start(arg, format);
+ rv = vfscanf(stdin, format, arg);
+ va_end(arg);
+
return rv;
}
#endif
-#endif
+/**********************************************************************/
+#ifdef L_sscanf
-#ifdef L_fscanf
-#ifdef __STDC__
-int fscanf(FILE * fp, const char *fmt, ...)
-#else
-int fscanf(fp, fmt, va_alist)
-FILE *fp;
-__const char *fmt;
-va_dcl
-#endif
+#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+
+int sscanf(const char * __restrict str, const char * __restrict format, ...)
{
- va_list ptr;
+ va_list arg;
int rv;
- va_start(ptr, fmt);
- rv = vfscanf(fp, fmt, ptr);
- va_end(ptr);
+ va_start(arg, format);
+ rv = vsscanf(str, format, arg);
+ va_end(arg);
+
return rv;
}
-#endif
+#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
+#warning Skipping sscanf since no buffering and no custom streams!
+#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
+
+#endif
+/**********************************************************************/
#ifdef L_vscanf
-int vscanf(fmt, ap)
-__const char *fmt;
-va_list ap;
+
+int vscanf(const char * __restrict format, va_list arg)
{
- return vfscanf(stdin, fmt, ap);
+ return vfscanf(stdin, format, arg);
}
-#endif
+#endif
+/**********************************************************************/
#ifdef L_vsscanf
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning WISHLIST: Implement vsscanf for non-buffered and no custom stream case.
+#endif /* __UCLIBC_MJN3_ONLY__ */
+
#ifdef __STDIO_BUFFERS
int vsscanf(__const char *sp, __const char *fmt, va_list ap)
{
@@ -165,598 +213,1866 @@ int vsscanf(__const char *sp, __const char *fmt, va_list ap)
return rv;
}
#else /* __STDIO_GLIBC_CUSTOM_STREAMS */
-#warning skipping vsscanf since no buffering and no custom streams!
+#warning Skipping vsscanf since no buffering and no custom streams!
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
#endif /* __STDIO_BUFFERS */
+
#endif
+/**********************************************************************/
+#ifdef L_fwscanf
-#ifdef L_vfscanf
+int fwscanf(FILE * __restrict stream, const wchar_t * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
-#include <assert.h>
-#include <ctype.h>
-#include <limits.h>
+ va_start(arg, format);
+ rv = vfwscanf(stream, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_wscanf
-static int valid_digit(char c, char base)
+int wscanf(const wchar_t * __restrict format, ...)
{
- if (base == 16) {
- return isxdigit(c);
- } else {
- return (__isdigit(c) && (c < '0' + base));
- }
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vfwscanf(stdin, format, arg);
+ va_end(arg);
+
+ return rv;
}
-extern unsigned long
-_stdlib_strto_l(register const char * __restrict str,
- char ** __restrict endptr, int base, int sflag);
-#ifdef LLONG_MAX
-extern unsigned long long
-_stdlib_strto_ll(register const char * __restrict str,
- char ** __restrict endptr, int base, int sflag);
+#endif
+/**********************************************************************/
+#ifdef L_swscanf
+
+#ifdef __STDIO_BUFFERS
+
+int swscanf(const wchar_t * __restrict str, const wchar_t * __restrict format,
+ ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vswscanf(str, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+#else /* __STDIO_BUFFERS */
+#warning Skipping swscanf since no buffering!
+#endif /* __STDIO_BUFFERS */
+
+#endif
+/**********************************************************************/
+#ifdef L_vwscanf
+
+int vwscanf(const wchar_t * __restrict format, va_list arg)
+{
+ return vfwscanf(stdin, format, arg);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_vswscanf
+
+#ifdef __STDIO_BUFFERS
+
+int vswscanf(const wchar_t * __restrict str, const wchar_t * __restrict format,
+ va_list arg)
+{
+ FILE f;
+
+ f.filedes = -3; /* FAKE STREAM TO SUPPORT *wscanf! */
+ f.modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING);
+ f.bufpos = (char *) str;
+ f.bufend = (char *)(str + wcslen(str));
+ f.ungot_width[0] = 0;
+#ifdef __STDIO_THREADSAFE
+ f.user_locking = 0;
+ __stdio_init_mutex(&f.lock);
+#endif
+
+
+ return vfwscanf(&f, format, arg);
+}
+#else /* __STDIO_BUFFERS */
+#warning Skipping vswscanf since no buffering!
+#endif /* __STDIO_BUFFERS */
+
+#endif
+/**********************************************************************/
+/**********************************************************************/
+
+
+
+/* float layout 0123456789012345678901 repeat n for "l[" */
+#define SPEC_CHARS "npxXoudifFeEgGaACSncs["
+/* npxXoudif eEgG CS cs[ */
+
+/* NOTE: Ordering is important! In particular, CONV_LEFTBRACKET
+ * must immediately precede CONV_c. */
+
+enum {
+ CONV_n = 0,
+ CONV_p,
+ CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i,
+ CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A,
+ CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_c, CONV_s, CONV_leftbracket,
+ CONV_percent, CONV_whitespace /* not in SPEC_* and no flags */
+};
+
+#ifdef __UCLIBC_HAS_FLOATS__
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+/* p x X o u d i f F e E g G a A */
+#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+#else
+/* p x X o u d i f F e E g G a A */
+#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10 }
+#endif
+#else /* __UCLIBC_HAS_FLOATS__ */
+/* p x X o u d i f F e E g G a A */
+#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0 }
+#endif /* __UCLIBC_HAS_FLOATS__ */
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#ifdef L_vfscanf
+/* emit once */
+#warning CONSIDER: Add a '0' flag to eat 0 padding when grouping?
+#endif
+#endif /* __UCLIBC_MJN3_ONLY__ */
+
+#define SPEC_FLAGS "*'I"
+
+enum {
+ FLAG_SURPRESS = 0x10, /* MUST BE 1ST!! See DO_FLAGS. */
+ FLAG_THOUSANDS = 0x20,
+ FLAG_I18N = 0x40, /* only works for d, i, u */
+ FLAG_MALLOC = 0x80, /* only works for s, S, and [ (and l[)*/
+};
+
+
+#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \
+ CONV_C, CONV_LEFTBRACKET, \
+ CONV_c, CONV_leftbracket }
+
+/* Note: We treat L and ll as synonymous... for ints and floats. */
+
+#define SPEC_ALLOWED_FLAGS { \
+ /* n */ (0x0f|FLAG_SURPRESS), \
+ /* p */ ( 0|FLAG_SURPRESS), \
+ /* oxXudi */ (0x0f|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \
+ /* fFeEgGaA */ (0x0c|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \
+ /* C */ ( 0|FLAG_SURPRESS), \
+ /* S and l[ */ ( 0|FLAG_SURPRESS|FLAG_MALLOC), \
+ /* c */ (0x04|FLAG_SURPRESS), \
+ /* s and [ */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \
+}
+
+
+/**********************************************************************/
+/*
+ * In order to ease translation to what arginfo and _print_info._flags expect,
+ * we map: 0:int 1:char 2:longlong 4:long 8:short
+ * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
+ */
+
+/* TODO -- Fix the table below to take into account stdint.h. */
+/* #ifndef LLONG_MAX */
+/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */
+/* #else */
+/* #if LLONG_MAX != INTMAX_MAX */
+/* #error fix QUAL_CHARS intmax_t entry 'j'! */
+/* #endif */
+/* #endif */
+
+#ifdef PDS
+#error PDS already defined!
+#endif
+#ifdef SS
+#error SS already defined!
+#endif
+#ifdef IMS
+#error IMS already defined!
+#endif
+
+#if PTRDIFF_MAX == INT_MAX
+#define PDS 0
+#elif PTRDIFF_MAX == LONG_MAX
+#define PDS 4
+#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
+#define PDS 8
+#else
+#error fix QUAL_CHARS ptrdiff_t entry 't'!
+#endif
+
+#if SIZE_MAX == UINT_MAX
+#define SS 0
+#elif SIZE_MAX == ULONG_MAX
+#define SS 4
+#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
+#define SS 8
+#else
+#error fix QUAL_CHARS size_t entries 'z', 'Z'!
+#endif
+
+#if INTMAX_MAX == INT_MAX
+#define IMS 0
+#elif INTMAX_MAX == LONG_MAX
+#define IMS 4
+#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
+#define IMS 8
+#else
+#error fix QUAL_CHARS ptrdiff_t entry 't'!
+#endif
+
+#define QUAL_CHARS { \
+ /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int q:long_long */ \
+ 'h', 'l', 'L', 'j', 'z', 't', 'q', 0, \
+ 2, 4, 8, IMS, SS, PDS, 8, 0, /* TODO -- fix!!! */\
+ 1, 8 }
+
+
+/**********************************************************************/
+
+#ifdef L_vfwscanf
+#if WINT_MIN > EOF
+#error Unfortunately, we currently need wint_t to be able to store EOF. Sorry.
+#endif
+#define W_EOF WEOF
+#define Wint wint_t
+#define Wchar wchar_t
+#define Wuchar __uwchar_t
+#define ISSPACE(C) iswspace((C))
+#define VFSCANF vfwscanf
+#define GETC(SC) (SC)->sc_getc((SC))
+#else
+typedef unsigned char __uchar_t;
+#define W_EOF EOF
+#define Wint int
+#define Wchar char
+#define Wuchar __uchar_t
+#define ISSPACE(C) isspace((C))
+#define VFSCANF vfscanf
+#ifdef __UCLIBC_HAS_WCHAR__
+#define GETC(SC) (SC)->sc_getc((SC))
+#else /* __UCLIBC_HAS_WCHAR__ */
+#define GETC(SC) getc_unlocked((SC)->fp)
+#endif /* __UCLIBC_HAS_WCHAR__ */
#endif
struct scan_cookie {
+ Wint cc;
+ Wint ungot_char;
FILE *fp;
int nread;
int width;
- int width_flag;
- int ungot_char;
- int ungot_flag;
- int app_ungot;
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ wchar_t app_ungot; /* Match FILE struct member type. */
+ unsigned char ungot_wchar_width;
+#else /* __UCLIBC_HAS_WCHAR__ */
+ unsigned char app_ungot; /* Match FILE struct member type. */
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+ char ungot_flag;
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ char ungot_wflag; /* vfwscanf */
+ char mb_fail; /* vfscanf */
+ mbstate_t mbstate; /* vfscanf */
+ wint_t wc;
+ wint_t ungot_wchar; /* to support __scan_getc */
+ int (*sc_getc)(struct scan_cookie *);
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ const char *grouping;
+ const unsigned char *thousands_sep;
+ int tslen;
+#ifdef __UCLIBC_HAS_WCHAR__
+ wchar_t thousands_sep_wc;
+#endif /* __UCLIBC_HAS_WCHAR__ */
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+
+#ifdef __UCLIBC_HAS_FLOATS__
+ const unsigned char *decpt;
+ int decpt_len;
+#ifdef __UCLIBC_HAS_WCHAR__
+ wchar_t decpt_wc;
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ const unsigned char *fake_decpt;
+#endif /* __UCLIBC_HAS_FLOATS__ */
+
};
-static const char qual[] = "hl" /* "jtz" */ "Lq";
-/* char = -2, short = -1, int = 0, long = 1, long long = 2 */
-static const char qsz[] = { -1, 1, 2, 2 };
+typedef struct {
+#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
+#if NL_ARGMAX > 10
+#warning NL_ARGMAX > 10, and space is allocated on the stack for positional args.
+#endif
+ void *pos_args[NL_ARGMAX];
+ int num_pos_args; /* Must start at -1. */
+ int cur_pos_arg;
+#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
+ void *cur_ptr;
+ const unsigned char *fmt;
+ int cnt, dataargtype, conv_num, max_width;
+ unsigned char store, flags;
+} psfs_t; /* parse scanf format state */
+
+
+/**********************************************************************/
+/**********************************************************************/
+
+extern void __init_scan_cookie(register struct scan_cookie *sc,
+ register FILE *fp);
+extern int __scan_getc(register struct scan_cookie *sc);
+extern void __scan_ungetc(register struct scan_cookie *sc);
#ifdef __UCLIBC_HAS_FLOATS__
-static int __strtold(long double *ld, struct scan_cookie *sc);
- /*01234567890123456 */
-static const char spec[] = "%n[csoupxXidfeEgG";
-#else
-static const char spec[] = "%n[csoupxXid";
+extern int __scan_strtold(long double *ld, struct scan_cookie *sc);
+#endif /* __UCLIBC_HAS_FLOATS__ */
+
+extern int __psfs_parse_spec(psfs_t *psfs);
+extern int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc);
+
+/**********************************************************************/
+#ifdef L___scan_cookie
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning TODO: Remove dependence on decpt_str and fake_decpt in stub locale mode.
+#endif
+#ifndef __UCLIBC_HAS_LOCALE__
+static const char decpt_str[] = ".";
#endif
-/* radix[i] <-> spec[i+5] o u p x X i d */
-static const char radix[] = { 8, 10, 16, 16, 16, 0, 10 };
-static void init_scan_cookie(register struct scan_cookie *sc,
- register FILE *fp)
+void __init_scan_cookie(register struct scan_cookie *sc,
+ register FILE *fp)
{
sc->fp = fp;
sc->nread = 0;
- sc->width_flag = 0;
sc->ungot_flag = 0;
sc->app_ungot = ((fp->modeflags & __MASK_UNGOT) ? fp->ungot[1] : 0);
-}
+#ifdef __UCLIBC_HAS_WCHAR__
+ sc->ungot_wflag = 0; /* vfwscanf */
+ sc->mb_fail = 0;
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ if (*(sc->grouping = __UCLIBC_CURLOCALE_DATA.grouping)) {
+ sc->thousands_sep = __UCLIBC_CURLOCALE_DATA.thousands_sep;
+ sc->tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len;
+#ifdef __UCLIBC_HAS_WCHAR__
+ sc->thousands_sep_wc = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc;
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ }
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+
+#ifdef __UCLIBC_HAS_FLOATS__
+#ifdef __UCLIBC_HAS_LOCALE__
+ sc->decpt = __UCLIBC_CURLOCALE_DATA.decimal_point;
+ sc->decpt_len = __UCLIBC_CURLOCALE_DATA.decimal_point_len;
+#else /* __UCLIBC_HAS_LOCALE__ */
+ sc->fake_decpt = sc->decpt = decpt_str;
+ sc->decpt_len = 1;
+#endif /* __UCLIBC_HAS_LOCALE__ */
+#ifdef __UCLIBC_HAS_WCHAR__
+#ifdef __UCLIBC_HAS_LOCALE__
+ sc->decpt_wc = __UCLIBC_CURLOCALE_DATA.decimal_point_wc;
+#else
+ sc->decpt_wc = '.';
+#endif
+#endif /* __UCLIBC_HAS_WCHAR__ */
+#endif /* __UCLIBC_HAS_FLOATS__ */
-/* TODO -- what about literal '\0' chars in a file??? */
+}
-static int scan_getc_nw(register struct scan_cookie *sc)
+int __scan_getc(register struct scan_cookie *sc)
{
+ int c;
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ assert(!sc->mb_fail);
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+ sc->cc = EOF;
+
+ if (--sc->width < 0) {
+ sc->ungot_flag |= 2;
+ return -1;
+ }
+
if (sc->ungot_flag == 0) {
- sc->ungot_char = getc(sc->fp);
+ if ((c = GETC(sc)) == EOF) {
+ sc->ungot_flag |= 2;
+ return -1;
+ }
+ sc->ungot_char = c;
} else {
+ assert(sc->ungot_flag == 1);
sc->ungot_flag = 0;
}
- if (sc->ungot_char > 0) {
- ++sc->nread;
+
+ ++sc->nread;
+ return sc->cc = sc->ungot_char;
+}
+
+void __scan_ungetc(register struct scan_cookie *sc)
+{
+ ++sc->width;
+ if (sc->ungot_flag == 2) { /* last was EOF */
+ sc->ungot_flag = 0;
+ sc->cc = sc->ungot_char;
+ } else if (sc->ungot_flag == 0) {
+ sc->ungot_flag = 1;
+ --sc->nread;
+ } else {
+ assert(0);
}
- sc->width_flag = 0;
- return sc->ungot_char;
}
-static int scan_getc(register struct scan_cookie *sc)
+#endif
+/**********************************************************************/
+#ifdef L___psfs_parse_spec
+
+#ifdef SPEC_FLAGS
+static const unsigned char spec_flags[] = SPEC_FLAGS;
+#endif /* SPEC_FLAGS */
+static const unsigned char spec_chars[] = SPEC_CHARS;
+static const unsigned char qual_chars[] = QUAL_CHARS;
+static const unsigned char spec_ranges[] = SPEC_RANGES;
+static const unsigned short spec_allowed[] = SPEC_ALLOWED_FLAGS;
+
+int __psfs_parse_spec(register psfs_t *psfs)
{
- if (sc->ungot_flag == 0) {
- sc->ungot_char = getc(sc->fp);
+ const unsigned char *p;
+ const unsigned char *fmt0 = psfs->fmt;
+ int i;
+#ifdef SPEC_FLAGS
+ int j;
+#endif
+#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
+ unsigned char fail = 0;
+
+ i = 0; /* Do this here to avoid a warning. */
+
+ if (!__isdigit_char(*psfs->fmt)) { /* Not a positional arg. */
+ fail = 1;
+ goto DO_FLAGS;
+ }
+
+ /* parse the positional arg (or width) value */
+ do {
+ if (i <= ((INT_MAX - 9)/10)) {
+ i = (i * 10) + (*psfs->fmt++ - '0');
+ }
+ } while (__isdigit_char(*psfs->fmt));
+
+ if (*psfs->fmt != '$') { /* This is a max field width. */
+ if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */
+ goto ERROR_EINVAL;
+ }
+ psfs->max_width = i;
+ psfs->num_pos_args = -2;
+ goto DO_QUALIFIER;
+ }
+ ++psfs->fmt; /* Advance past '$'. */
+#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
+
+#if defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0))
+ DO_FLAGS:
+#endif /* defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0)) */
+#ifdef SPEC_FLAGS
+ p = spec_flags;
+ j = FLAG_SURPRESS;
+ do {
+ if (*p == *psfs->fmt) {
+ ++psfs->fmt;
+ psfs->flags |= j;
+ goto DO_FLAGS;
+ }
+ j += j;
+ } while (*++p);
+
+ if (psfs->flags & FLAG_SURPRESS) { /* Suppress assignment. */
+ psfs->store = 0;
+ goto DO_WIDTH;
+ }
+#else /* SPEC_FLAGS */
+ if (*psfs->fmt == '*') { /* Suppress assignment. */
+ ++psfs->fmt;
+ psfs->store = 0;
+ goto DO_WIDTH;
+ }
+#endif /* SPEC_FLAGS */
+
+
+#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
+ if (fail) {
+ /* Must be a non-positional arg */
+ if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */
+ goto ERROR_EINVAL;
+ }
+ psfs->num_pos_args = -2;
+ } else {
+ if ((psfs->num_pos_args == -2) || (((unsigned int)(--i)) >= NL_ARGMAX)) {
+ /* Already saw a non-pos arg or (0-based) num too large. */
+ goto ERROR_EINVAL;
+ }
+ psfs->cur_pos_arg = i;
}
- sc->width_flag = 1;
+#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
+
+ DO_WIDTH:
+ for (i = 0 ; __isdigit_char(*psfs->fmt) ; ) {
+ if (i <= ((INT_MAX - 9)/10)) {
+ i = (i * 10) + (*psfs->fmt++ - '0');
+ psfs->max_width = i;
+ }
+ }
+
+#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
+ DO_QUALIFIER:
+#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
+ p = qual_chars;
+ do {
+ if (*psfs->fmt == *p) {
+ ++psfs->fmt;
+ break;
+ }
+ } while (*++p);
+ if ((p - qual_chars < 2) && (*psfs->fmt == *p)) {
+ p += ((sizeof(qual_chars)-2) / 2);
+ ++psfs->fmt;
+ }
+ psfs->dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Should we validate that psfs->max_width > 0 in __psfs_parse_spec()? It would avoid whitespace consumption...
+#warning CONSIDER: Should INT_MAX be a valid width (%c/%C)? See __psfs_parse_spec().
+#endif /* __UCLIBC_MJN3_ONLY__ */
+
+ p = spec_chars;
+ do {
+ if (*psfs->fmt == *p) {
+ int p_m_spec_chars = p - spec_chars;
+
+#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
+#error implement gnu a flag
+ if ((*p == 'a')
+ && ((psfs->fmt[1] == '[') || ((psfs->fmt[1]|0x20) == 's'))
+ ) { /* Assumes ascii for 's' and 'S' test. */
+ psfs->flags |= FLAG_MALLOC;
+ ++psfs->fmt;
+ ++p;
+ continue; /* The related conversions follow 'a'. */
+ }
+#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */
+
+ for (p = spec_ranges; p_m_spec_chars > *p ; ++p) {}
+ if (((psfs->dataargtype >> 8) | psfs->flags)
+ & ~spec_allowed[(int)(p - spec_ranges)]
+ ) {
+ goto ERROR_EINVAL;
+ }
+
+ if ((p_m_spec_chars >= CONV_c)
+ && (psfs->dataargtype & PA_FLAG_LONG)) {
+ p_m_spec_chars -= 3; /* lc -> C, ls -> S, l[ -> ?? */
+ }
+
+ psfs->conv_num = p_m_spec_chars;
+ return psfs->fmt - fmt0;
+ }
+ if (!*++p) {
+ ERROR_EINVAL:
+ __set_errno(EINVAL);
+ return -1;
+ }
+ } while(1);
+
+ assert(0);
+}
+
+#endif
+/**********************************************************************/
+#if defined(L_vfscanf) || defined(L_vfwscanf)
+
+#ifdef __UCLIBC_HAS_WCHAR__
+#ifdef L_vfscanf
+static int sc_getc(register struct scan_cookie *sc)
+{
+ return getc(sc->fp);
+}
+
+static int scan_getwc(register struct scan_cookie *sc)
+{
+ size_t r;
+ int width;
+ wchar_t wc[1];
+ char b[1];
+
if (--sc->width < 0) {
- sc->ungot_flag = 1;
+ sc->ungot_flag |= 2;
return -1;
}
- sc->ungot_flag = 0;
- if (sc->ungot_char > 0) {
- ++sc->nread;
+
+ width = sc->width; /* Preserve width. */
+ sc->width = INT_MAX; /* MB_CUR_MAX can invoke a function. */
+
+ r = (size_t)(-1);
+ while (__scan_getc(sc) >= 0) {
+ *b = sc->cc;
+
+ r = mbrtowc(wc, b, 1, &sc->mbstate);
+ if (((ssize_t) r) >= 0) { /* Successful completion of a wc. */
+ sc->wc = *wc;
+ goto SUCCESS;
+ } else if (r == ((size_t) -2)) {
+ /* Potentially valid but incomplete. */
+ continue;
+ }
+ break;
}
- return sc->ungot_char;
+
+ /* If we reach here, either r == ((size_t)-1) and
+ * mbrtowc set errno to EILSEQ, or r == ((size_t)-2)
+ * and stream is in an error state or at EOF with a
+ * partially complete wchar. */
+ __set_errno(EILSEQ); /* In case of incomplete conversion. */
+ sc->mb_fail = 1;
+
+ SUCCESS:
+ sc->width = width; /* Restore width. */
+
+ return (int)((ssize_t) r);
}
-static void scan_ungetc(register struct scan_cookie *sc)
+#endif /* L_vfscanf */
+
+#ifdef L_vfwscanf
+
+/* This gets called by __scan_getc. __scan_getc is called by vfwscanf
+ * when the next wide char is expected to be valid ascii (digits).
+ */
+static int sc_getc(register struct scan_cookie *sc)
{
- if (sc->ungot_flag != 0) {
- assert(sc->width < 0);
- return;
+ wint_t wc;
+
+ if (sc->fp->filedes == -3) {
+ if (sc->fp->bufpos < sc->fp->bufend) {
+ wc = *((wchar_t *)(sc->fp->bufpos));
+ sc->fp->bufpos += sizeof(wchar_t);
+ } else {
+ sc->fp->modeflags |= __FLAG_EOF;
+ return EOF;
+ }
+ } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) {
+ return EOF;
}
- if (sc->width_flag) {
- ++sc->width;
+
+ sc->ungot_wflag = 1;
+ sc->ungot_wchar = wc;
+ sc->ungot_wchar_width = sc->fp->ungot_width[0];
+
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ if (wc == sc->thousands_sep_wc) {
+ wc = ',';
+ } else
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+#ifdef __UCLIBC_HAS_FLOATS__
+ if (wc == sc->decpt_wc) {
+ wc = '.';
+ } else
+#endif /* __UCLIBC_HAS_FLOATS__ */
+ if (!__isascii(wc)) {
+ wc = '?';
}
- sc->ungot_flag = 1;
- if (sc->ungot_char > 0) { /* not EOF or EOS */
- --sc->nread;
+ sc->wc = sc->ungot_char = wc;
+
+ return (int) wc;
+}
+
+static int scan_getwc(register struct scan_cookie *sc)
+{
+ wint_t wc;
+
+ sc->wc = WEOF;
+
+ if (--sc->width < 0) {
+ sc->ungot_flag |= 2;
+ return -1;
}
+
+ if (sc->ungot_flag == 0) {
+
+ if (sc->fp->filedes == -3) {
+ if (sc->fp->bufpos < sc->fp->bufend) {
+ wc = *((wchar_t *)(sc->fp->bufpos));
+ sc->fp->bufpos += sizeof(wchar_t);
+ } else {
+ sc->ungot_flag |= 2;
+ return -1;
+ }
+ } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) {
+ sc->ungot_flag |= 2;
+ return -1;
+ }
+ sc->ungot_wflag = 1;
+ sc->ungot_char = wc;
+ sc->ungot_wchar_width = sc->fp->ungot_width[0];
+ } else {
+ assert(sc->ungot_flag == 1);
+ sc->ungot_flag = 0;
+ }
+
+ ++sc->nread;
+ sc->wc = sc->ungot_char;
+
+ return 0;
}
-static void kill_scan_cookie(register struct scan_cookie *sc)
+
+#endif /* L_vfwscanf */
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+static __inline void kill_scan_cookie(register struct scan_cookie *sc)
{
- if (sc->ungot_flag) {
- ungetc(sc->ungot_char,sc->fp);
+#ifdef L_vfscanf
+
+ if (sc->ungot_flag & 1) {
+ ungetc(sc->ungot_char, sc->fp);
+ /* Deal with distiction between user and scanf ungots. */
+ if (sc->nread == 0) { /* Only one char was read... app ungot? */
+ sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */
+ } else {
+ sc->fp->ungot[1] = 0;
+ }
+ }
+
+#else
+
+ if ((sc->ungot_wflag & 1) && (sc->fp->filedes != -3) && (sc->fp->state.mask == 0)) {
+ ungetwc(sc->ungot_char, sc->fp);
/* Deal with distiction between user and scanf ungots. */
if (sc->nread == 0) { /* Only one char was read... app ungot? */
sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */
+ } else {
+ sc->fp->ungot[1] = 0;
}
+ sc->fp->ungot_width[1] = sc->ungot_wchar_width;
}
+
+#endif
}
-int vfscanf(FILE *fp, const char *format, va_list ap)
-{
-#define STRTO_L_(s,e,b,sf) _stdlib_strto_ll(s,e,b,sf)
-#define MAX_DIGITS 64
-#define UV_TYPE unsigned long long
-#define V_TYPE long long
+#ifdef L_vfwscanf
#ifdef __UCLIBC_HAS_FLOATS__
- long double ld;
+static const char fake_decpt_str[] = ".";
+#endif
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+static const char fake_thousands_sep_str[] = ",";
#endif
- UV_TYPE uv;
+#endif /* L_vfwscanf */
+
+
+int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
+{
+ const Wuchar *fmt;
+ unsigned char *b;
+
+
+#ifdef L_vfwscanf
+ wchar_t wbuf[1];
+ wchar_t *wb;
+#endif /* L_vfwscanf */
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ mbstate_t mbstate;
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
struct scan_cookie sc;
- register unsigned const char *fmt;
- const char *p;
- register unsigned char *b;
- void *vp;
- int cc, i, cnt;
- signed char lval;
- unsigned char store, usflag, base, invert, r0, r1;
+ psfs_t psfs;
+
+ int i;
+
+#warning fix MAX_DIGITS. we do not do binary, so...!
+#define MAX_DIGITS 65 /* Allow one leading 0. */
unsigned char buf[MAX_DIGITS+2];
+#ifdef L_vfscanf
unsigned char scanset[UCHAR_MAX + 1];
+ unsigned char invert; /* Careful! Meaning changes. */
+#endif /* L_vfscanf */
+ unsigned char fail;
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning TODO: Make checking of the format string in C locale an option.
+#endif
+ /* To support old programs, don't check mb validity if in C locale. */
+#if defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf)
+ /* ANSI/ISO C99 requires format string to be a valid multibyte string
+ * beginning and ending in its initial shift state. */
+ if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) {
+ mbstate.mask = 0; /* Initialize the mbstate. */
+ const char *p = format;
+ if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
+ __set_errno(EINVAL); /* Format string is invalid. */
+ return 0;
+ }
+ }
+#endif /* defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) */
+
+#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
+ psfs.num_pos_args = -1; /* Must start at -1. */
+ /* Initialize positional arg ptrs to NULL. */
+ memset(psfs.pos_args, 0, sizeof(psfs.pos_args));
+#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
__STDIO_THREADLOCK(fp);
- init_scan_cookie(&sc,fp);
+ __init_scan_cookie(&sc,fp);
+#ifdef __UCLIBC_HAS_WCHAR__
+ sc.sc_getc = sc_getc;
+ sc.ungot_wchar_width = sc.fp->ungot_width[1];
- fmt = (unsigned const char *) format;
- cnt = 0;
+#ifdef L_vfwscanf
- while (*fmt) {
- store = 1;
- lval = 0;
- sc.width = INT_MAX;
- if (*fmt == '%') { /* Conversion specification. */
- ++fmt;
- if (*fmt == '*') { /* Suppress assignment. */
- store = 0;
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ if (*sc.grouping) {
+ sc.thousands_sep = fake_thousands_sep_str;
+ sc.tslen = 1;
+ }
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+
+#ifdef __UCLIBC_HAS_FLOATS__
+ sc.fake_decpt = fake_decpt_str;
+#endif /* __UCLIBC_HAS_FLOATS__ */
+
+#else /* L_vfwscanf */
+
+#ifdef __UCLIBC_HAS_FLOATS__
+ sc.fake_decpt = sc.decpt;
+#endif /* __UCLIBC_HAS_FLOATS__ */
+
+#endif /* L_vfwscanf */
+
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ psfs.cnt = 0;
+
+ /* Note: If we ever wanted to support non-nice codesets, we
+ * would really need to do a mb->wc conversion here in the
+ * vfscanf case. Related changes would have to be made in
+ * the code that follows... basicly wherever fmt appears. */
+ for (fmt = (const Wuchar *) format ; *fmt ; /* ++fmt */) {
+
+ psfs.store = 1;
+ psfs.flags = 0;
+#ifndef NDEBUG
+ psfs.cur_ptr = NULL; /* Debugging aid. */
+#endif /* NDEBUG */
+
+
+ sc.ungot_flag &= 1; /* Clear (possible fake) EOF. */
+ sc.width = psfs.max_width = INT_MAX;
+
+ /* Note: According to the standards, vfscanf does use isspace
+ * here. So, if we did a mb->wc conversion, we would have to do
+ * something like
+ * ((((__uwchar_t)wc) < UCHAR_MAX) && isspace(wc))
+ * because wc might not be in the allowed domain. */
+ if (ISSPACE(*fmt)) {
+ do {
++fmt;
+ } while (ISSPACE(*fmt));
+ --fmt;
+ psfs.conv_num = CONV_whitespace;
+ goto DO_WHITESPACE;
+ }
+
+ if (*fmt == '%') { /* Conversion specification. */
+ if (*++fmt == '%') { /* Remember, '%' eats whitespace too. */
+ psfs.conv_num = CONV_percent;
+ goto DO_CONVERSION;
}
- for (i = 0 ; __isdigit(*fmt) ; sc.width = i) {
- i = (i * 10) + (*fmt++ - '0'); /* Get specified width. */
+
+
+#ifdef L_vfscanf
+ psfs.fmt = fmt;
+#else /* L_vfscanf */
+ {
+ const __uwchar_t *wf = fmt;
+ psfs.fmt = b = buf;
+
+ while (*wf && __isascii(*wf) && (b < buf + sizeof(buf) - 1)) {
+ *b++ = *wf++;
+ }
+#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
+#error this is wrong... we need to ched in __psfs_parse_spec instead since this checks last char in buffer and conversion my have stopped before it.
+ if ((*b == 'a') && ((*wf == '[') || ((*wf|0x20) == 's'))) {
+ goto DONE; /* Spec was excessively long. */
+ }
+#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */
+ *b = 0;
+ if (b == buf) { /* Bad conversion specifier! */
+ goto DONE;
+ }
}
- for (i = 0 ; i < sizeof(qual) ; i++) { /* Optional qualifier. */
- if (qual[i] == *fmt) {
- ++fmt;
- lval += qsz[i];
- if ((i < 2) && (qual[i] == *fmt)) { /* Double h or l. */
- ++fmt;
- lval += qsz[i];
+#endif /* L_vfscanf */
+ if ((i = __psfs_parse_spec(&psfs)) < 0) { /* Bad conversion specifier! */
+ goto DONE;
+ }
+ fmt += i;
+
+#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
+ if (psfs.store) {
+ if (psfs.num_pos_args == -2) {
+ psfs.cur_ptr = va_arg(arg, void *);
+ } else {
+ while (psfs.cur_pos_arg > psfs.num_pos_args) {
+ psfs.pos_args[++psfs.num_pos_args] = va_arg(arg, void *);
}
- break;
+ psfs.cur_ptr = psfs.pos_args[psfs.cur_pos_arg];
+ }
+ }
+#else /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
+ psfs.cur_ptr = va_arg(arg, void *);
+#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
+
+ DO_CONVERSION:
+ /* First, consume white-space if not n, c, [, C, or l[. */
+ if ((((1L << CONV_n)|(1L << CONV_C)|(1L << CONV_c)
+ |(1L << CONV_LEFTBRACKET)|(1L << CONV_leftbracket))
+ & (1L << psfs.conv_num)) == 0
+ ) {
+ DO_WHITESPACE:
+ while ((__scan_getc(&sc) >= 0)
+#ifdef L_vfscanf
+ && isspace(sc.cc)
+#else /* L_vfscanf */
+ && iswspace(sc.wc)
+#endif /* L_vfscanf */
+ ) {}
+ __scan_ungetc(&sc);
+ if (psfs.conv_num == CONV_whitespace) {
+ goto NEXT_FMT;
+ }
+ }
+
+ sc.width = psfs.max_width; /* Now limit the max width. */
+
+ if (sc.width == 0) { /* 0 width is forbidden. */
+ goto DONE;
+ }
+
+
+ if (psfs.conv_num == CONV_percent) {
+ goto MATCH_CHAR;
+ }
+
+ if (psfs.conv_num == CONV_n) {
+ if (psfs.store) {
+ _store_inttype(psfs.cur_ptr, psfs.dataargtype,
+ (uintmax_t) sc.nread);
}
+ goto NEXT_FMT;
}
- for (p = spec ; *p ; p++) { /* Process format specifier. */
- if (*fmt != *p) continue;
- if (p-spec < 1) { /* % - match a '%'*/
- goto matchchar;
+
+ if (psfs.conv_num <= CONV_A) { /* pointer, integer, or float spec */
+#ifdef L_vfscanf
+ if (__psfs_do_numeric(&psfs, &sc) < 0) { /* Num conv failed! */
+ goto DONE;
}
- if (p-spec < 2) { /* n - store number of chars read */
- *(va_arg(ap, int *)) = sc.nread;
- scan_getc_nw(&sc);
- goto nextfmt;
+ goto NEXT_FMT;
+#else
+ int r = __psfs_do_numeric(&psfs, &sc);
+ if (sc.ungot_wflag == 1) { /* fix up '?', '.', and ',' hacks */
+ sc.cc = sc.ungot_char = sc.ungot_wchar;
+ }
+ if (r < 0) {
+ goto DONE;
}
- if (p-spec > 3) { /* skip white space if not c or [ */
- do {
- i = scan_getc_nw(&sc);
- } while (__isspace(i));
- scan_ungetc(&sc);
+ goto NEXT_FMT;
+#endif
+ }
+
+ /* Do string conversions here since they are not common code. */
+
+
+#ifdef L_vfscanf
+
+ if
+#ifdef __UCLIBC_HAS_WCHAR__
+ (psfs.conv_num >= CONV_LEFTBRACKET)
+#else /* __UCLIBC_HAS_WCHAR__ */
+ (psfs.conv_num >= CONV_c)
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ {
+ b = (psfs.store ? ((unsigned char *) psfs.cur_ptr) : buf);
+ fail = 1;
+
+
+ if (psfs.conv_num == CONV_c) {
+ if (sc.width == INT_MAX) {
+ sc.width = 1;
+ }
+
+ while (__scan_getc(&sc) >= 0) {
+ *b = sc.cc;
+ b += psfs.store;
+ }
+ __scan_ungetc(&sc);
+ if (sc.width > 0) { /* Failed to read all required. */
+ goto DONE;
+ }
+ psfs.cnt += psfs.store;
+ goto NEXT_FMT;
}
- if (p-spec < 5) { /* [,c,s - string conversions */
+
+ if (psfs.conv_num == CONV_s) {
+ /* Yes, believe it or not, a %s conversion can store nuls. */
+ while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) {
+ *b = sc.cc;
+ b += psfs.store;
+ fail = 0;
+ }
+ } else {
+#ifdef __UCLIBC_HAS_WCHAR__
+ assert((psfs.conv_num == CONV_LEFTBRACKET) || \
+ (psfs.conv_num == CONV_leftbracket));
+#else /* __UCLIBC_HAS_WCHAR__ */
+ assert((psfs.conv_num == CONV_leftbracket));
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
invert = 0;
- if (*p == 'c') {
+
+ if (*++fmt == '^') {
+ ++fmt;
invert = 1;
- if (sc.width == INT_MAX) {
- sc.width = 1;
- }
- }
- for (i=0 ; i<= UCHAR_MAX ; i++) {
- scanset[i] = ((*p == 's') ? (__isspace(i) == 0) : 0);
+ }
+ memset(scanset, invert, sizeof(scanset));
+ invert = 1-invert;
+
+ if (*fmt == ']') {
+ scanset[(int)(']')] = invert;
+ ++fmt;
}
- if (*p == '[') { /* need to build a scanset */
- if (*++fmt == '^') {
- invert = 1;
- ++fmt;
- }
- if (*fmt == ']') {
- scanset[(int)']'] = 1;
- ++fmt;
+
+ while (*fmt != ']') {
+ if (!*fmt) { /* No closing ']'. */
+ goto DONE;
}
- r0 = 0;
- while (*fmt && *fmt !=']') { /* build scanset */
- if ((*fmt == '-') && r0 && (fmt[1] != ']')) {
- /* range */
- ++fmt;
- if (*fmt < r0) {
- r1 = r0;
- r0 = *fmt;
- } else {
- r1 = *fmt;
- }
- for (i=r0 ; i<= r1 ; i++) {
- scanset[i] = 1;
- }
- r0 = 0;
- } else {
- r0 = *fmt;
- scanset[r0] = 1;
- }
+ if ((*fmt == '-') && (fmt[1] != ']')
+ && (fmt[-1] < fmt[1]) /* sorted? */
+ ) { /* range */
++fmt;
+ i = fmt[-2];
+ /* Note: scanset[i] should already have been done
+ * in the previous iteration. */
+ do {
+ scanset[++i] = invert;
+ } while (i < *fmt);
+ /* Safe to fall through, and a bit smaller. */
}
- if (!*fmt) { /* format string exhausted! */
- goto done;
- }
+ /* literal char */
+ scanset[(int) *fmt] = invert;
+ ++fmt;
}
- /* ok -- back to common work */
- if (sc.width <= 0) {
- goto done;
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ if (psfs.conv_num == CONV_LEFTBRACKET) {
+ goto DO_LEFTBRACKET;
}
- if (store) {
- b = va_arg(ap, unsigned char *);
- } else {
- b = buf;
+#endif /* __UCLIBC_HAS_WCHAR__ */
+
+
+ while (__scan_getc(&sc) >= 0) {
+ if (!scanset[sc.cc]) {
+ break;
+ }
+ *b = sc.cc;
+ b += psfs.store;
+ fail = 0;
}
- cc = scan_getc(&sc);
- if (cc < 0) {
- scan_ungetc(&sc);
- goto done; /* return EOF if cnt == 0 */
+ }
+ /* Common tail for processing of %s and %[. */
+
+ __scan_ungetc(&sc);
+ if (fail) { /* nothing stored! */
+ goto DONE;
+ }
+ *b = 0; /* Nul-terminate string. */
+ psfs.cnt += psfs.store;
+ goto NEXT_FMT;
+ }
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ DO_LEFTBRACKET: /* Need to do common wide init. */
+ if (psfs.conv_num >= CONV_C) {
+ wchar_t wbuf[1];
+ wchar_t *wb;
+
+ sc.mbstate.mask = 0;
+
+ wb = (psfs.store ? ((wchar_t *) psfs.cur_ptr) : wbuf);
+ fail = 1;
+
+ if (psfs.conv_num == CONV_C) {
+ if (sc.width == INT_MAX) {
+ sc.width = 1;
}
- if (*p == 'c') {
- goto c_spec;
+
+ while (scan_getwc(&sc) >= 0) {
+ assert(sc.width >= 0);
+ *wb = sc.wc;
+ wb += psfs.store;
}
- i = 0;
- while ((cc>=0) && (scanset[cc] != invert)) {
- c_spec:
- i = 1; /* yes, we stored something */
- *b = cc;
- b += store;
- cc = scan_getc(&sc);
+
+ __scan_ungetc(&sc);
+ if (sc.width > 0) { /* Failed to read all required. */
+ goto DONE;
}
- if (i==0) {
- scan_ungetc(&sc);
- goto done; /* return cnt */
+ psfs.cnt += psfs.store;
+ goto NEXT_FMT;
+ }
+
+
+ if (psfs.conv_num == CONV_S) {
+ /* Yes, believe it or not, a %s conversion can store nuls. */
+ while ((scan_getwc(&sc) >= 0)
+ && ((((__uwchar_t)(sc.wc)) > UCHAR_MAX)
+ || !isspace(sc.wc))
+ ) {
+ *wb = sc.wc;
+ wb += psfs.store;
+ fail = 0;
}
- if (*p != 'c') { /* nul-terminate the stored string */
- *b = 0;
+ } else {
+ assert(psfs.conv_num == CONV_LEFTBRACKET);
+
+ while (scan_getwc(&sc) >= 0) {
+ if (((__uwchar_t) sc.wc) <= UCHAR_MAX) {
+ if (!scanset[sc.wc]) {
+ break;
+ }
+ } else if (invert) {
+ break;
+ }
+ *wb = sc.wc;
+ wb += psfs.store;
+ fail = 0;
}
- cnt += store;
- goto nextfmt;
}
- if (p-spec < 12) { /* o,u,p,x,X,i,d - (un)signed integer */
- if (*p == 'p') {
- /* assume pointer same size as int or long. */
- lval = (sizeof(char *) == sizeof(long));
+ /* Common tail for processing of %ls and %l[. */
+
+ __scan_ungetc(&sc);
+ if (fail || sc.mb_fail) { /* Nothing stored or mb error. */
+ goto DONE;
+ }
+ *wb = 0; /* Nul-terminate string. */
+ psfs.cnt += psfs.store;
+ goto NEXT_FMT;
+
+ }
+
+#endif /* __UCLIBC_HAS_WCHAR__ */
+#else /* L_vfscanf */
+
+ if (psfs.conv_num >= CONV_C) {
+ b = buf;
+ wb = wbuf;
+ if (psfs.conv_num >= CONV_c) {
+ mbstate.mask = 0; /* Initialize the mbstate. */
+ if (psfs.store) {
+ b = (unsigned char *) psfs.cur_ptr;
}
- usflag = ((p-spec) < 10); /* (1)0 if (un)signed */
- base = radix[(int)(p-spec) - 5];
- b = buf;
- if (sc.width <= 0) {
- goto done;
+ } else {
+ if (psfs.store) {
+ wb = (wchar_t *) psfs.cur_ptr;
}
- cc = scan_getc(&sc);
- if ((cc == '+') || (cc == '-')) { /* Handle leading sign.*/
- *b++ = cc;
- cc = scan_getc(&sc);
+ }
+ fail = 1;
+
+
+ if ((psfs.conv_num == CONV_C) || (psfs.conv_num == CONV_c)) {
+ if (sc.width == INT_MAX) {
+ sc.width = 1;
}
- if (cc == '0') { /* Possibly set base and handle prefix. */
- if ((base == 0) || (base == 16)) {
- cc = scan_getc(&sc);
- if ((cc == 'x') || (cc == 'X')) {
- /* We're committed to base 16 now. */
- base = 16;
- cc = scan_getc(&sc);
- } else { /* oops... back up */
- scan_ungetc(&sc);
- cc = '0';
- if (base == 0) {
- base = 8;
- }
+
+ while (scan_getwc(&sc) >= 0) {
+ if (psfs.conv_num == CONV_C) {
+ *wb = sc.wc;
+ wb += psfs.store;
+ } else {
+ i = wcrtomb(b, sc.wc, &mbstate);
+ if (i < 0) { /* Conversion failure. */
+ goto DONE_DO_UNGET;
+ }
+ if (psfs.store) {
+ b += i;
}
}
}
- if (base == 0) { /* Default to base 10 */
- base = 10;
+ __scan_ungetc(&sc);
+ if (sc.width > 0) { /* Failed to read all required. */
+ goto DONE;
}
- /* At this point, we're ready to start reading digits. */
- if (cc == '0') {
- *b++ = cc; /* Store first leading 0 */
- do { /* but ignore others. */
- cc = scan_getc(&sc);
- } while (cc == '0');
- }
- while (valid_digit(cc,base)) { /* Now for nonzero digits.*/
- if (b - buf < MAX_DIGITS) {
- *b++ = cc;
+ psfs.cnt += psfs.store;
+ goto NEXT_FMT;
+ }
+
+ if ((psfs.conv_num == CONV_S) || (psfs.conv_num == CONV_s)) {
+ /* Yes, believe it or not, a %s conversion can store nuls. */
+ while ((scan_getwc(&sc) >= 0) && !iswspace(sc.wc)) {
+ if (psfs.conv_num == CONV_S) {
+ *wb = sc.wc;
+ wb += psfs.store;
+ } else {
+ i = wcrtomb(b, sc.wc, &mbstate);
+ if (i < 0) { /* Conversion failure. */
+ goto DONE_DO_UNGET;
+ }
+ if (psfs.store) {
+ b += i;
+ }
}
- cc = scan_getc(&sc);
+ fail = 0;
+ }
+ } else {
+ const wchar_t *sss;
+ const wchar_t *ssp;
+ unsigned char invert = 0;
+
+ assert((psfs.conv_num == CONV_LEFTBRACKET)
+ || (psfs.conv_num == CONV_leftbracket));
+
+ if (*++fmt == '^') {
+ ++fmt;
+ invert = 1;
}
- *b = 0; /* null-terminate */
- if ((b == buf) || (*--b == '+') || (*b == '-')) {
- scan_ungetc(&sc);
- goto done; /* No digits! */
+ sss = (const wchar_t *) fmt;
+ if (*fmt == ']') {
+ ++fmt;
}
- if (store) {
- if (*buf == '-') {
- usflag = 0;
+ while (*fmt != ']') {
+ if (!*fmt) { /* No closing ']'. */
+ goto DONE;
}
- uv = STRTO_L_(buf, NULL, base, 1-usflag);
- vp = va_arg(ap, void *);
- switch (lval) {
- case 2: /* If no long long, treat as long . */
- *((unsigned long long *)vp) = uv;
- break;
- case 1:
-#if ULONG_MAX == UINT_MAX
- case 0: /* int and long int are the same */
-#endif
- if (usflag) {
- if (uv > ULONG_MAX) {
- uv = ULONG_MAX;
- }
- } else if (((V_TYPE)uv) > LONG_MAX) {
- uv = LONG_MAX;
- } else if (((V_TYPE)uv) < LONG_MIN) {
- uv = (UV_TYPE) LONG_MIN;
- }
- *((unsigned long *)vp) = (unsigned long)uv;
- break;
-#if ULONG_MAX != UINT_MAX
- case 0: /* int and long int are different */
- if (usflag) {
- if (uv > UINT_MAX) {
- uv = UINT_MAX;
- }
- } else if (((V_TYPE)uv) > INT_MAX) {
- uv = INT_MAX;
- } else if (((V_TYPE)uv) < INT_MIN) {
- uv = (UV_TYPE) INT_MIN;
- }
- *((unsigned int *)vp) = (unsigned int)uv;
- break;
-#endif
- case (signed char)(-1):
- if (usflag) {
- if (uv > USHRT_MAX) {
- uv = USHRT_MAX;
- }
- } else if (((V_TYPE)uv) > SHRT_MAX) {
- uv = SHRT_MAX;
- } else if (((V_TYPE)uv) < SHRT_MIN) {
- uv = (UV_TYPE) SHRT_MIN;
- }
- *((unsigned short *)vp) = (unsigned short)uv;
- break;
- case (signed char)(-2):
- if (usflag) {
- if (uv > UCHAR_MAX) {
- uv = UCHAR_MAX;
+ if ((*fmt == '-') && (fmt[1] != ']')
+ && (fmt[-1] < fmt[1]) /* sorted? */
+ ) { /* range */
+ ++fmt;
+ }
+ ++fmt;
+ }
+ /* Ok... a valid scanset spec. */
+
+ while (scan_getwc(&sc) >= 0) {
+ ssp = sss;
+ do { /* We know sss < fmt. */
+ if (*ssp == '-') { /* possible range... */
+ /* Note: We accept a-c-e (ordered) as
+ * equivalent to a-e. */
+ if (ssp > sss) {
+ if ((++ssp < (const wchar_t *) fmt)
+ && (ssp[-2] < *ssp) /* sorted? */
+ ) { /* yes */
+ if ((sc.wc >= ssp[-2])
+ && (sc.wc <= *ssp)) {
+ break;
+ }
+ continue; /* not in range */
}
- } else if (((V_TYPE)uv) > CHAR_MAX) {
- uv = CHAR_MAX;
- } else if (((V_TYPE)uv) < CHAR_MIN) {
- uv = (UV_TYPE) CHAR_MIN;
+ --ssp; /* oops... '-' at end, so back up */
}
- *((unsigned char *)vp) = (unsigned char) uv;
+ /* false alarm... a literal '-' */
+ }
+ if (sc.wc == *ssp) { /* Matched literal char. */
break;
- default:
- assert(0);
+ }
+ } while (++ssp < (const wchar_t *) fmt);
+
+ if ((ssp == (const wchar_t *) fmt) ^ invert) {
+ /* no match and not inverting
+ * or match and inverting */
+ break;
}
- ++cnt;
- }
- goto nextfmt;
- }
-#ifdef __UCLIBC_HAS_FLOATS__
- else { /* floating point */
- if (sc.width <= 0) {
- goto done;
- }
- if (__strtold(&ld, &sc)) { /* Success! */
- if (store) {
- vp = va_arg(ap, void *);
- switch (lval) {
- case 2:
- *((long double *)vp) = ld;
- break;
- case 1:
- *((double *)vp) = (double) ld;
- break;
- case 0:
- *((float *)vp) = (float) ld;
- break;
- default: /* Illegal qualifier! */
- assert(0);
- goto done;
+ if (psfs.conv_num == CONV_LEFTBRACKET) {
+ *wb = sc.wc;
+ wb += psfs.store;
+ } else {
+ i = wcrtomb(b, sc.wc, &mbstate);
+ if (i < 0) { /* Conversion failure. */
+ goto DONE_DO_UNGET;
+ }
+ if (psfs.store) {
+ b += i;
}
- ++cnt;
}
- goto nextfmt;
+ fail = 0;
}
}
-#else
- assert(0);
-#endif
- goto done;
- }
- /* Unrecognized specifier! */
- goto RETURN_cnt;
- } if (__isspace(*fmt)) { /* Consume all whitespace. */
- do {
- i = scan_getc_nw(&sc);
- } while (__isspace(i));
- } else { /* Match the current fmt char. */
- matchchar:
- if (scan_getc_nw(&sc) != *fmt) {
- scan_ungetc(&sc);
- goto done;
+ /* Common tail for processing of %s and %[. */
+
+ __scan_ungetc(&sc);
+ if (fail) { /* nothing stored! */
+ goto DONE;
+ }
+ *wb = 0; /* Nul-terminate string. */
+ *b = 0;
+ psfs.cnt += psfs.store;
+ goto NEXT_FMT;
}
- scan_getc_nw(&sc);
+
+#endif /* L_vfscanf */
+
+ assert(0);
+ goto DONE;
+ } /* conversion specification */
+
+ MATCH_CHAR:
+ if (__scan_getc(&sc) != *fmt) {
+#ifdef L_vfwscanf
+ DONE_DO_UNGET:
+#endif /* L_vfwscanf */
+ __scan_ungetc(&sc);
+ goto DONE;
}
- nextfmt:
- scan_ungetc(&sc);
+
+ NEXT_FMT:
++fmt;
}
- done: /* end of scan */
- kill_scan_cookie(&sc);
-
- if ((sc.ungot_char <= 0) && (cnt == 0) && (*fmt)) {
- cnt = EOF;
+ DONE:
+ if ((psfs.cnt == 0) && (*fmt) && __FEOF_OR_FERROR(fp)) {
+ psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */
}
- RETURN_cnt:
+ kill_scan_cookie(&sc);
+
+/* RETURN_cnt: */
__STDIO_THREADUNLOCK(fp);
- return (cnt);
+ return psfs.cnt;
}
+#endif
+/**********************************************************************/
+#ifdef L___psfs_do_numeric
-/*****************************************************************************/
+static const unsigned char spec_base[] = SPEC_BASE;
+static const unsigned char nil_string[] = "(nil)";
+
+int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
+{
+ unsigned char *b;
+ const unsigned char *p;
#ifdef __UCLIBC_HAS_FLOATS__
+ int exp_adjust = 0;
+#endif
+#warning fix MAX_DIGITS. we do not do binary, so...!
+#define MAX_DIGITS 65 /* Allow one leading 0. */
+#warning fix buf!
+ unsigned char buf[MAX_DIGITS+2+ 100];
+ unsigned char usflag, base;
+ unsigned char nonzero = 0;
+ unsigned char seendigit = 0;
+
-#include <float.h>
+#ifndef __UCLIBC_HAS_FLOATS__
+ if (psfs->conv_num > CONV_i) { /* floating point */
+ goto DONE;
+ }
+#endif
-#define MAX_SIG_DIGITS 20
-#define MAX_IGNORED_DIGITS 2000
-#define MAX_ALLOWED_EXP (MAX_SIG_DIGITS + MAX_IGNORED_DIGITS + LDBL_MAX_10_EXP)
+ base = spec_base[psfs->conv_num - CONV_p];
+ usflag = (psfs->conv_num <= CONV_u); /* (1)0 if (un)signed */
+ b = buf;
-#if LDBL_DIG > MAX_SIG_DIGITS
-#error need to adjust MAX_SIG_DIGITS
-#endif
-#include <limits.h>
-#if MAX_ALLOWED_EXP > INT_MAX
-#error size assumption violated for MAX_ALLOWED_EXP
+ if (psfs->conv_num == CONV_p) { /* Pointer */
+ p = nil_string;
+ do {
+ if ((__scan_getc(sc) < 0) || (*p != sc->cc)) {
+ __scan_ungetc(sc);
+ if (p > nil_string) { /* failed */
+ /* We matched at least the '(' so even if we
+ * are at eof, we can not match a pointer. */
+ goto DONE;
+ }
+ break;
+ }
+ if (!*++p) { /* Matched (nil), so no unget necessary. */
+ if (psfs->store) {
+ ++psfs->cnt;
+ _store_inttype(psfs->cur_ptr, psfs->dataargtype,
+ (uintmax_t) NULL);
+ }
+ return 0;
+ }
+ } while (1);
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Should we require a 0x prefix and disallow +/- for pointer %p?
+#endif /* __UCLIBC_MJN3_ONLY__ */
+ }
+
+ __scan_getc(sc);
+ if ((sc->cc == '+') || (sc->cc == '-')) { /* Handle leading sign.*/
+ *b++ = sc->cc;
+ __scan_getc(sc);
+ }
+
+ if ((base & 0xef) == 0) { /* 0xef is ~16, so 16 or 0. */
+ if (sc->cc == '0') { /* Possibly set base and handle prefix. */
+ __scan_getc(sc);
+ if ((sc->cc|0x20) == 'x') { /* Assumes ascii.. x or X. */
+ if ((__scan_getc(sc) < 0)
+#ifdef __UCLIBC_HAS_WCHAR__
+ && !sc->ungot_wflag /* wc outside char range */
+#endif /* __UCLIBC_HAS_WCHAR__ */
+ ) {
+ /* Note! 'x' at end of file|field is special.
+ * While this looks like I'm 'unget'ing twice,
+ * EOF and end of field are handled specially
+ * by the scan_* funcs. */
+ __scan_ungetc(sc);
+ goto DO_NO_0X;
+ }
+ base = 16; /* Base 16 for sure now. */
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ /* The prefix is required for hexadecimal floats. */
+ *b++ = '0';
+ *b++ = 'x';
+#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
+ } else { /* oops... back up */
+ DO_NO_0X:
+ __scan_ungetc(sc);
+ sc->cc = '0'; /* NASTY HACK! */
+
+ base = (base >> 1) + 8; /* 0->8, 16->16. no 'if' */
+#ifdef __UCLIBC_HAS_FLOATS__
+ if (psfs->conv_num > CONV_i) { /* floating point */
+ base = 10;
+ }
#endif
+ }
+ } else if (!base) {
+ base = 10;
+ }
+ }
-int __strtold(long double *ld, struct scan_cookie *sc)
-{
- long double number;
- long double p10;
- int exponent_power;
- int exponent_temp;
- int negative;
- int num_digits;
- int since_decimal;
- int c;
+ /***************** digit grouping **********************/
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+
+ if ((psfs->flags & FLAG_THOUSANDS) && (base == 10)
+ && *(p = sc->grouping)
+ ) {
+
+ int nblk1, nblk2, nbmax, lastblock, pass, i;
- c = scan_getc(sc); /* Decrements width. */
-
- negative = 0;
- switch(c) { /* Handle optional sign. */
- case '-': negative = 1; /* Fall through to get next char. */
- case '+': c = scan_getc(sc);
- }
-
- number = 0.;
- num_digits = -1;
- exponent_power = 0;
- since_decimal = INT_MIN;
-
- LOOP:
- while (__isdigit(c)) { /* Process string of digits. */
- ++since_decimal;
- if (num_digits < 0) { /* First time through? */
- ++num_digits; /* We've now seen a digit. */
- }
- if (num_digits || (c != '0')) { /* had/have nonzero */
- ++num_digits;
- if (num_digits <= MAX_SIG_DIGITS) { /* Is digit significant? */
- number = number * 10. + (c - '0');
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Should we initalize the grouping blocks in __init_scan_cookie()?
+#endif /* __UCLIBC_MJN3_ONLY__ */
+ nbmax = nblk2 = nblk1 = *p;
+ if (*++p) {
+ nblk2 = *p;
+ if (nbmax < nblk2) {
+ nbmax = nblk2;
}
+ assert(!*++p);
}
- c = scan_getc(sc);
- }
-
- if ((c == '.') && (since_decimal < 0)) { /* If no previous decimal pt, */
- since_decimal = 0; /* save position of decimal point */
- c = scan_getc(sc); /* and process rest of digits */
- goto LOOP;
- }
-
- if (num_digits<0) { /* Must have at least one digit. */
- goto FAIL;
- }
-
- if (num_digits > MAX_SIG_DIGITS) { /* Adjust exp for skipped digits. */
- exponent_power += num_digits - MAX_SIG_DIGITS;
- }
-
- if (since_decimal >= 0) { /* Adjust exponent for decimal point. */
- exponent_power -= since_decimal;
- }
-
- if (negative) { /* Correct for sign. */
- number = -number;
- negative = 0; /* Reset for exponent processing below. */
- }
-
- /* Process an exponent string. */
- if (c == 'e' || c == 'E') {
- c = scan_getc(sc);
- switch(c) { /* Handle optional sign. */
- case '-': negative = 1; /* Fall through to get next char. */
- case '+': c = scan_getc(sc);
- }
-
- num_digits = 0;
- exponent_temp = 0;
- while (__isdigit(c)) { /* Process string of digits. */
- if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */
- exponent_temp = exponent_temp * 10 + (c - '0');
+
+ /* Note: for printf, if 0 and \' flags appear then
+ * grouping is done before 0-padding. Should we
+ * strip leading 0's first? Or add a 0 flag? */
+
+ /* For vfwscanf, sc_getc translates, so the value of sc->cc is
+ * either EOF or a char. */
+
+ if (!__isdigit_char_or_EOF(sc->cc)) { /* No starting digit! */
+#ifdef __UCLIBC_HAS_FLOATS__
+ if (psfs->conv_num > CONV_i) { /* floating point */
+ goto NO_STARTING_DIGIT;
}
- c = scan_getc(sc);
- ++num_digits;
+#endif
+ goto DONE_DO_UNGET;
}
- if (num_digits == 0) { /* Were there no exp digits? */
- goto FAIL;
- } /* else */
- if (negative) {
- exponent_power -= exponent_temp;
- } else {
- exponent_power += exponent_temp;
+ if (sc->cc == '0') {
+ seendigit = 1;
+ *b++ = '0'; /* Store the first 0. */
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Should leading 0s be skipped before digit grouping? (printf 0 pad)
+#endif /* __UCLIBC_MJN3_ONLY__ */
+#if 0
+ do { /* But ignore all subsequent 0s. */
+ __scan_getc(sc);
+ } while (sc->cc == '0');
+#endif
}
- }
+ pass = 0;
+ lastblock = 0;
+ do {
+ i = 0;
+ while (__isdigit_char_or_EOF(sc->cc)) {
+ seendigit = 1;
+ if (i == nbmax) { /* too many digits for a block */
+#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__
+ if (!pass) { /* treat as nongrouped */
+ if (nonzero) {
+ goto DO_NO_GROUP;
+ }
+ goto DO_TRIM_LEADING_ZEROS;
+ }
+#endif
+ if (nbmax > nblk1) {
+ goto DONE_DO_UNGET;
+ }
+ goto DONE_GROUPING_DO_UNGET; /* nbmax == nblk1 */
+ }
+ ++i;
+
+ if (nonzero || (sc->cc != '0')) {
+ if (b < buf + MAX_DIGITS) {
+ *b++ = sc->cc;
+ nonzero = 1;
+#ifdef __UCLIBC_HAS_FLOATS__
+ } else {
+ ++exp_adjust;
+#endif
+ }
+ }
- if (number != 0.) {
- /* Now scale the result. */
- exponent_temp = exponent_power;
- p10 = 10.;
+ __scan_getc(sc);
+ }
+
+ if (i) { /* we saw digits digits */
+ if ((i == nblk2) || ((i < nblk2) && !pass)) {
+ /* (possible) outer grp */
+ p = sc->thousands_sep;
+ if (*p == sc->cc) { /* first byte matches... */
+ /* so check if grouping mb char */
+ /* Since 1st matched, either match or fail now
+ * unless EOF (yuk) */
+ __scan_getc(sc);
+ MBG_LOOP:
+ if (!*++p) { /* is a grouping mb char */
+ lastblock = i;
+ ++pass;
+ continue;
+ }
+ if (*p == sc->cc) {
+ __scan_getc(sc);
+ goto MBG_LOOP;
+ }
+ /* bad grouping mb char! */
+ __scan_ungetc(sc);
+ if ((sc->cc >= 0) || (p > sc->thousands_sep + 1)) {
+#ifdef __UCLIBC_HAS_FLOATS__
+ /* We failed to match a thousep mb char, and
+ * we've read too much to recover. But if
+ * this is a floating point conversion and
+ * the initial portion of the decpt mb char
+ * matches, then we may still be able to
+ * recover. */
+ int k = p - sc->thousands_sep - 1;
- if (exponent_temp < 0) {
- exponent_temp = -exponent_temp;
+ if ((psfs->conv_num > CONV_i) /* float conversion */
+ && (!pass || (i == nblk1)) /* possible last */
+ && !memcmp(sc->thousands_sep, sc->fake_decpt, k)
+ /* and prefix matched, so could be decpt */
+ ) {
+ __scan_getc(sc);
+ p = sc->fake_decpt + k;
+ do {
+ if (!*++p) {
+ strcpy(b, sc->decpt);
+ b += sc->decpt_len;
+ goto GOT_DECPT;
+ }
+ if (*p != sc->cc) {
+ __scan_ungetc(sc);
+ break; /* failed */
+ }
+ __scan_getc(sc);
+ } while (1);
+ }
+#endif /* __UCLIBC_HAS_FLOATS__ */
+ goto DONE;
+ }
+ /* was EOF and 1st, so recoverable. */
+ }
+ }
+ if ((i == nblk1) || ((i < nblk1) && !pass)) {
+ /* got an inner group */
+ goto DONE_GROUPING_DO_UNGET;
+ }
+ if (i > nblk1) {
+ /* An inner group if we can back up a bit. */
+ if ((i - nblk1) <= (sc->ungot_flag ^ 1)) {
+ assert(sc->cc < 0);
+ --b;
+ goto DO_RECOVER_GROUP;
+ }
+ }
+
+ /* (0 < i < nblk1) && (pass > 0) so prev group char
+ * So we have an unrecoverable situation. */
+ goto DONE_DO_UNGET;
+ } /* i != 0 */
+
+ assert(pass);
+
+ /* No next group. Can we back up past grouping mb char? */
+ if ((pass == 1) || (nblk1 == nblk2)) {
+ if (!i && (sc->tslen == 1) && (sc->cc < 0)) {
+ /* No digits, grouping mb char is len 1, and EOF*/
+ DO_RECOVER_GROUP:
+ if (sc->ungot_flag & 2) {
+ __scan_ungetc(sc);
+ }
+ goto DONE_GROUPING_DO_UNGET;
+ }
+ }
+ goto DONE_DO_UNGET;
+ } while (1);
+
+ assert(0); /* Should never get here. */
+ }
+
+#endif /***************** digit grouping **********************/
+
+ /* Not grouping so first trim all but one leading 0. */
+#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__
+ DO_TRIM_LEADING_ZEROS:
+#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */
+ if (sc->cc == '0') {
+ seendigit = 1;
+ *b++ = '0'; /* Store the first 0. */
+ do { /* But ignore all subsequent 0s. */
+ __scan_getc(sc);
+ } while (sc->cc == '0');
+ }
+
+#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__
+ DO_NO_GROUP:
+#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */
+ /* At this point, we're ready to start reading digits. */
+
+#define valid_digit(cc,base) (isxdigit(cc) && ((base == 16) || (cc - '0' < base)))
+
+ while (valid_digit(sc->cc,base)) { /* Now for significant digits.*/
+ if (b - buf < MAX_DIGITS) {
+ nonzero = seendigit = 1; /* Set nonzero too 0s trimmed above. */
+ *b++ = sc->cc;
+#ifdef __UCLIBC_HAS_FLOATS__
+ } else {
+ ++exp_adjust;
+#endif
}
+ __scan_getc(sc);
+ }
- while (exponent_temp) {
- if (exponent_temp & 1) {
- if (exponent_power < 0) {
- number /= p10;
- } else {
- number *= p10;
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ DONE_GROUPING_DO_UNGET:
+#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
+ if (psfs->conv_num <= CONV_i) { /* integer conversion */
+ __scan_ungetc(sc);
+ *b = 0; /* null-terminate */
+ if (!seendigit) {
+ goto DONE; /* No digits! */
+ }
+ if (psfs->store) {
+ if (*buf == '-') {
+ usflag = 0;
+ }
+ ++psfs->cnt;
+ _store_inttype(psfs->cur_ptr, psfs->dataargtype,
+ (uintmax_t) STRTOUIM(buf, NULL, base, 1-usflag));
+ }
+ return 0;
+ }
+
+#ifdef __UCLIBC_HAS_FLOATS__
+
+ /* At this point, we have everything left of the decimal point or exponent. */
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ NO_STARTING_DIGIT:
+#endif
+ p = sc->fake_decpt;
+ do {
+ if (!*p) {
+ strcpy(b, sc->decpt);
+ b += sc->decpt_len;
+ break;
+ }
+ if (*p != sc->cc) {
+ if (p > sc->fake_decpt) {
+ if ((sc->cc >= 0) && (p > sc->fake_decpt + 1)) {
+ goto DONE_DO_UNGET; /* failed */
}
+
+ __scan_ungetc(sc);
+
}
- exponent_temp >>= 1;
- p10 *= p10;
+ goto DO_DIGIT_CHECK;
+ }
+ ++p;
+ __scan_getc(sc);
+ } while (1);
+
+#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
+ GOT_DECPT:
+#endif
+ if (!nonzero) {
+ if (sc->cc == '0') {
+ assert(exp_adjust == 0);
+ *b++ = '0';
+ ++exp_adjust;
+ seendigit = 1;
+ do {
+ --exp_adjust;
+ __scan_getc(sc);
+ } while (sc->cc == '0');
}
}
- *ld = number;
- return 1;
- FAIL:
- scan_ungetc(sc);
- return 0;
-}
+ while (valid_digit(sc->cc,base)) { /* Process fractional digits.*/
+ if (b - buf < MAX_DIGITS) {
+ seendigit = 1;
+ *b++ = sc->cc;
+ }
+ __scan_getc(sc);
+ }
+
+ DO_DIGIT_CHECK:
+ /* Hmm... no decimal point. */
+ if (!seendigit) {
+ static const unsigned char nan_inf_str[] = "an\0nfinity";
+
+ if (base == 16) { /* We had a prefix, but no digits! */
+ goto DONE_DO_UNGET;
+ }
+
+ /* Avoid tolower problems for INFINITY in the tr_TR locale. (yuk)*/
+#undef TOLOWER
+#define TOLOWER(C) ((C)|0x20)
+
+ switch (TOLOWER(sc->cc)) {
+ case 'i':
+ p = nan_inf_str + 3;
+ break;
+ case 'n':
+ p = nan_inf_str;
+ break;
+ default:
+ /* No digits and not inf or nan. */
+ goto DONE_DO_UNGET;
+ }
+
+ *b++ = sc->cc;
+
+ do {
+ __scan_getc(sc);
+ if (TOLOWER(sc->cc) == *p) {
+ *b++ = sc->cc;
+ ++p;
+ continue;
+ }
+ if (!*p || (p == nan_inf_str + 5)) { /* match nan/infinity or inf */
+ goto GOT_FLOAT;
+ }
+ /* Unrecoverable. Even if on 1st char, we had no digits. */
+ goto DONE_DO_UNGET;
+ } while (1);
+ }
+
+ /* If we get here, we had some digits. */
+
+ if (
+#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
+ ((base == 16) && (((sc->cc)|0x20) == 'p')) ||
+#endif
+ (((sc->cc)|0x20) == 'e')
+ ) { /* Process an exponent. */
+ *b++ = sc->cc;
+
+ __scan_getc(sc);
+ if (sc->cc < 0) { /* EOF... recoverable */
+ --b;
+ goto GOT_FLOAT;
+ }
+
+ if ((sc->cc == '+') || (sc->cc == '-')) { /* Signed exponent? */
+ *b++ = sc->cc;
+ __scan_getc(sc);
+ }
+
+#warning fix MAX_EXP_DIGITS!
+#define MAX_EXP_DIGITS 20
+ assert(seendigit);
+ seendigit = 0;
+ nonzero = 0;
+
+ if (sc->cc == '0') {
+ seendigit = 1;
+ *b++ = '0';
+ do {
+ __scan_getc(sc);
+ } while (sc->cc == '0');
+ }
+
+ while (__isdigit_char_or_EOF(sc->cc)) { /* Exponent digits (base 10).*/
+ if (seendigit < MAX_EXP_DIGITS) {
+ ++seendigit;
+ *b++ = sc->cc;
+ }
+ __scan_getc(sc);
+ }
+
+ if (!seendigit) { /* No digits. Unrecoverable. */
+ goto DONE_DO_UNGET;
+ }
+ }
+
+
+ GOT_FLOAT:
+
+ *b = 0;
+ {
+ __fpmax_t x;
+ char *e;
+ x = __strtofpmax(buf, &e, exp_adjust);
+ assert(!*e);
+ if (psfs->store) {
+ if (psfs->dataargtype & PA_FLAG_LONG_LONG) {
+ *((long double *)psfs->cur_ptr) = (long double) x;
+ } else if (psfs->dataargtype & PA_FLAG_LONG) {
+ *((double *)psfs->cur_ptr) = (double) x;
+ } else {
+ *((float *)psfs->cur_ptr) = (float) x;
+ }
+ ++psfs->cnt;
+ }
+ __scan_ungetc(sc);
+ return 0;
+ }
#endif /* __UCLIBC_HAS_FLOATS__ */
+
+ DONE_DO_UNGET:
+ __scan_ungetc(sc);
+ DONE:
+ return -1;
+
+}
#endif
+/**********************************************************************/
diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c
index 0d4708d97..40ac693a4 100644
--- a/libc/stdio/stdio.c
+++ b/libc/stdio/stdio.c
@@ -352,7 +352,7 @@ UNLOCKED(int,fileno,(register FILE *stream),(stream))
FILE *fdopen(int filedes, const char *mode)
{
- register char *cur_mode; /* TODO -- replace by intptr_t?? */
+ register char *cur_mode; /* TODO -- use intptr_t?? (also fopencookie) */
return (((int)(cur_mode = (char *) fcntl(filedes, F_GETFL))) != -1)
? _stdio_fopen(cur_mode, mode, NULL, filedes)
@@ -491,7 +491,10 @@ static ssize_t fmo_read(register void *cookie, char *buf, size_t bufsize)
{
size_t count = COOKIE->len - COOKIE->pos;
- /* Note: bufsize < SSIZE_MAX because of _stdio_READ. */
+ /* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */
+ if (!count) { /* EOF! */
+ return 0;
+ }
if (bufsize > count) {
bufsize = count;
@@ -838,8 +841,6 @@ FILE *open_memstream(char **__restrict bufloc, size_t *__restrict sizeloc)
* When compiled without large file support, the offset pointer for the
* cookie_seek function is off_t * and not off64_t * as for glibc. */
-/* TODO: rewrite _stdio_fopen() to avoid the fopencookie() kludge. */
-
/* Currently no real reentrancy issues other than a possible double close(). */
#ifndef __BCC__
@@ -848,12 +849,14 @@ FILE *fopencookie(void * __restrict cookie, const char * __restrict mode,
cookie_io_functions_t io_functions)
{
FILE *stream;
- int fd;
- if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) {
- fd = stream->filedes;
+ /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement
+ * check without an fcntl call. */
+ if ((stream = _stdio_fopen(((char *)(INT_MAX-1)),
+ mode, NULL, INT_MAX)) /* TODO: use intptr_t? */
+ != NULL
+ ) {
stream->filedes = -1;
- close(fd);
stream->gcs = io_functions;
stream->cookie = cookie;
}
@@ -886,10 +889,13 @@ FILE *_fopencookie(void * __restrict cookie, const char * __restrict mode,
{
register FILE *stream;
- if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) {
- int fd = stream->filedes;
+ /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement
+ * check without an fcntl call. */
+ if ((stream = _stdio_fopen(((char *)(INT_MAX-1)),
+ mode, NULL, INT_MAX)) /* TODO: use intptr_t? */
+ != NULL
+ ) {
stream->filedes = -1;
- close(fd);
stream->gcs.read = io_functions->read;
stream->gcs.write = io_functions->write;
stream->gcs.seek = io_functions->seek;
@@ -1019,7 +1025,7 @@ void __fpurge(register FILE * __restrict stream)
/* Not reentrant. */
#ifdef __STDIO_WIDE
-#warning unlike the glibc version, this __fpending returns bytes in buffer for wide streams too!
+#warning Unlike the glibc version, this __fpending returns bytes in buffer for wide streams too!
link_warning(__fpending, "This version of __fpending returns bytes remaining in buffer for both narrow and wide streams. glibc's version returns wide chars in buffer for the wide stream case.")
@@ -1220,12 +1226,17 @@ FILE *_stdio_fsfopen(const char * __restrict filename,
/* stdio internal functions */
/**********************************************************************/
#ifdef L__stdio_adjpos
-/*
- * ANSI/ISO p. 370: The file positioning indicator is unspecified after
- * a successful call to ungetwc.
+
+/* According to the ANSI/ISO C99 definition of ungetwc()
+ * For a text or binary stream, the value of its file position indicator
+ * after a successful call to the ungetwc function is unspecified until
+ * all pushed­back wide characters are read or discarded.
* Note however, that this applies only to _user_ calls to ungetwc. We
- * need to allow for internal calls by scanf. So we store the byte count
- * of the first ungot wide char in ungot0_bytes. If it is 0 (user case)
+ * need to allow for internal calls by scanf.
+
+
+ * So we store the byte count
+ * of the first ungot wide char in ungot_width. If it is 0 (user case)
* then the file position is treated as unknown.
*/
@@ -1236,16 +1247,23 @@ int _stdio_adjpos(register FILE * __restrict stream, register __offmax_t *pos)
__offmax_t r;
int cor = stream->modeflags & __MASK_UNGOT; /* handle ungots */
+ assert(cor <= 2);
+
#ifdef __STDIO_WIDE
/* Assumed narrow stream so correct if wide. */
if (cor && (stream->modeflags & __FLAG_WIDE)) {
- cor = cor - 1 + stream->ungot_width[0];
- if (((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1]) {
+ if ((((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1])) {
return -1; /* App did ungetwc, so position is indeterminate. */
}
- assert(stream->ungot_width[0] > 0);
+ if (stream->modeflags & __MASK_UNGOT) {
+ cor = cor - 1 + stream->ungot_width[1];
+ }
+ if (stream->state.mask > 0) { /* Incomplete character (possible bad) */
+ cor -= stream->ungot_width[0];
+ }
}
#endif /* __STDIO_WIDE */
+
#ifdef __STDIO_BUFFERS
if (stream->modeflags & __FLAG_WRITING) {
cor -= (stream->bufpos - stream->bufstart); /* pending writes */
@@ -1415,9 +1433,14 @@ size_t _stdio_fread(unsigned char *buffer, size_t bytes, register FILE *stream)
*p++ = *stream->bufpos++;
}
- if ((bytes > 0) && (stream->filedes != -2)) {
+ if (bytes > 0) {
ssize_t len;
+ if (stream->filedes == -2) {
+ stream->modeflags |= __FLAG_EOF;
+ goto DONE;
+ }
+
/* The buffer is exhausted, but we still need chars. */
stream->bufpos = stream->bufread = stream->bufstart;
@@ -1449,6 +1472,7 @@ size_t _stdio_fread(unsigned char *buffer, size_t bytes, register FILE *stream)
}
#endif
+ DONE:
__stdio_validate_FILE(stream); /* debugging only */
return (p - (unsigned char *)buffer);
}
@@ -2086,7 +2110,9 @@ int fclose(register FILE *stream)
/* At this point, any dangling refs to the stream are the result of
* a programming bug... so free the unlocked stream. */
if (stream->modeflags & __FLAG_FREEFILE) {
+#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = NULL; /* To aid debugging... */
+#endif
free(stream);
}
@@ -2118,7 +2144,9 @@ int fclose(register FILE *stream)
/* At this point, any dangling refs to the stream are the result of
* a programming bug... so free the unlocked stream. */
if (stream->modeflags & __FLAG_FREEFILE) {
+#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = NULL; /* To aid debugging... */
+#endif
free(stream);
}
@@ -2213,7 +2241,7 @@ int fflush_unlocked(register FILE *stream)
rv = -1; /* Not all chars written. */
}
#ifdef __UCLIBC_MJN3_ONLY__
-#warning add option to test for undefined behavior of fflush
+#warning WISHLIST: Add option to test for undefined behavior of fflush.
#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
} else if (stream->modeflags & (__FLAG_READING|__FLAG_READONLY)) {
@@ -2242,7 +2270,7 @@ int fflush_unlocked(register FILE *stream)
#endif
#ifdef __UCLIBC_MJN3_ONLY__
-#warning add option to test for undefined behavior of fflush
+#warning WISHLIST: Add option to test for undefined behavior of fflush.
#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
return ((stream != NULL)
@@ -2317,16 +2345,18 @@ FILE *_stdio_fopen(const char * __restrict filename,
open_mode |= O_RDWR;
}
-#if defined(__STDIO_GNU_FEATURE) || defined(__STDIO_FOPEN_LARGEFILE_MODE)
- while (*mode) { /* ignore everything else except ... */
+#if defined(__STDIO_FOPEN_EXCLUSIVE_MODE) || defined(__STDIO_FOPEN_LARGEFILE_MODE)
+ for ( ; *mode ; ++mode) { /* ignore everything else except ... */
#ifdef __STDIO_FOPEN_EXCLUSIVE_MODE
- if (*mode++ == 'x') { /* open exclusive -- glibc extension */
+ if (*mode == 'x') { /* open exclusive -- glibc extension */
open_mode |= O_EXCL;
+ continue;
}
#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE */
#ifdef __STDIO_FOPEN_LARGEFILE_MODE
- if (*mode++ == 'F') { /* open large file */
+ if (*mode == 'F') { /* open large file */
open_mode |= O_LARGEFILE;
+ continue;
}
#endif /* __STDIO_FOPEN_LARGEFILE_MODE */
}
@@ -2371,6 +2401,7 @@ FILE *_stdio_fopen(const char * __restrict filename,
* leave it set (glibc compat). */
int i = (open_mode & (O_ACCMODE|O_LARGEFILE)) + 1;
+ /* NOTE: fopencookie needs changing if the basic check changes! */
if (((i & (((int) filename) + 1)) != i) /* Check basic agreement. */
|| (((open_mode & O_APPEND)
&& !(((int) filename) & O_APPEND)
@@ -2445,6 +2476,9 @@ FILE *_stdio_fopen(const char * __restrict filename,
stream->gcs.close = _cs_close;
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
+#ifdef __STDIO_WIDE
+ stream->ungot_width[0] = 0;
+#endif /* __STDIO_WIDE */
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(stream->state));
#endif /* __STDIO_MBSTATE */
@@ -2797,6 +2831,7 @@ UNLOCKED(int,fputs,
/**********************************************************************/
#ifdef L_getc
#undef getc
+#undef getc_unlocked
/* Reentrancy handled by UNLOCKED() macro. */
@@ -2855,6 +2890,7 @@ char *gets(char *s) /* WARNING!!! UNSAFE FUNCTION!!! */
/**********************************************************************/
#ifdef L_putc
#undef putc
+#undef putc_unlocked
/* Reentrancy handled by UNLOCKED() macro. */
@@ -2931,8 +2967,7 @@ int ungetc(int c, register FILE *stream)
stream->modeflags |= __FLAG_NARROW;
#endif /* __STDIO_WIDE */
- /* If can't read or there's been an error, or c == EOF, or ungot slots
- * already filled, then return EOF */
+ /* If can't read or c == EOF or ungot slots already filled, then fail. */
if ((stream->modeflags
& (__MASK_UNGOT2|__FLAG_WRITEONLY
#ifndef __STDIO_AUTO_RW_TRANSITION
@@ -2946,14 +2981,18 @@ int ungetc(int c, register FILE *stream)
}
#ifdef __STDIO_BUFFERS
- /* TODO: shouldn't allow writing??? */
+#ifdef __STDIO_AUTO_RW_TRANSITION
if (stream->modeflags & __FLAG_WRITING) {
fflush_unlocked(stream); /* Commit any write-buffered chars. */
}
+#endif /* __STDIO_AUTO_RW_TRANSITION */
#endif /* __STDIO_BUFFERS */
/* Clear EOF and WRITING flags, and set READING FLAG */
stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING);
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Is setting the reading flag after an ungetc necessary?
+#endif /* __UCLIBC_MJN3_ONLY__ */
stream->modeflags |= __FLAG_READING;
stream->ungot[1] = 1; /* Flag as app ungetc call; scanf fixes up. */
stream->ungot[(stream->modeflags++) & __MASK_UNGOT] = c;
@@ -3047,22 +3086,35 @@ UNLOCKED(size_t,fwrite,
int fgetpos64(FILE * __restrict stream, register fpos64_t * __restrict pos)
{
-#ifdef __STDIO_MBSTATE
- int retval;
+ int retval = -1;
- __STDIO_THREADLOCK(stream);
+#ifdef __STDIO_WIDE
- retval = ((pos != NULL) && ((pos->__pos = ftello64(stream)) >= 0))
- ? (__COPY_MBSTATE(&(pos->__mbstate), &(stream->state)), 0)
- : (__set_errno(EINVAL), -1);
+ if (pos == NULL) {
+ __set_errno(EINVAL);
+ } else {
+ __STDIO_THREADLOCK(stream);
- __STDIO_THREADUNLOCK(stream);
+ if ((pos->__pos = ftello64(stream)) >= 0) {
+ __COPY_MBSTATE(&(pos->__mbstate), &(stream->state));
+ pos->mblen_pending = stream->ungot_width[0];
+ retval = 0;
+ }
+
+ __STDIO_THREADUNLOCK(stream);
+ }
+
+#else /* __STDIO_WIDE */
+
+ if (pos == NULL) {
+ __set_errno(EINVAL);
+ } else if ((pos->__pos = ftello64(stream)) >= 0) {
+ retval = 0;
+ }
+
+#endif /* __STDIO_WIDE */
return retval;
-#else
- return ((pos != NULL) && ((pos->__pos = ftello64(stream)) >= 0))
- ? 0 : (__set_errno(EINVAL), -1);
-#endif
}
#ifndef L_fgetpos64
@@ -3137,10 +3189,19 @@ int fseeko64(register FILE *stream, __off64_t offset, int whence)
stream->modeflags &=
~(__FLAG_READING|__FLAG_WRITING|__FLAG_EOF|__MASK_UNGOT);
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: How do we deal with fseek to an ftell position for incomplete or error wide? Right now, we clear all multibyte state info. If we do not clear, then fix rewind() to do so if fseek() succeeds.
+#endif /* __UCLIBC_MJN3_ONLY__ */
+
+#ifdef __STDIO_WIDE
+ /* TODO: don't clear state if don't move? */
+ stream->ungot_width[0] = 0;
+#endif /* __STDIO_WIDE */
#ifdef __STDIO_MBSTATE
/* TODO: don't clear state if don't move? */
__INIT_MBSTATE(&(stream->state));
#endif /* __STDIO_MBSTATE */
+
__stdio_validate_FILE(stream); /* debugging only */
retval = 0;
@@ -3178,7 +3239,7 @@ int fsetpos64(FILE *stream, register const fpos64_t *pos)
__set_errno(EINVAL);
return EOF;
}
-#ifdef __STDIO_MBSTATE
+#ifdef __STDIO_WIDE
{
int retval;
@@ -3186,15 +3247,19 @@ int fsetpos64(FILE *stream, register const fpos64_t *pos)
if ((retval = fseeko64(stream, pos->__pos, SEEK_SET)) == 0) {
__COPY_MBSTATE(&(stream->state), &(pos->__mbstate));
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Moving mblen_pending into some mbstate field might be useful. But we would need to modify all the mb<->wc funcs.
+#endif /* __UCLIBC_MJN3_ONLY__ */
+ stream->ungot_width[0] = pos->mblen_pending;
}
__STDIO_THREADUNLOCK(stream);
return retval;
}
-#else /* __STDIO_MBSTATE */
+#else /* __STDIO_WIDE */
return fseeko64(stream, pos->__pos, SEEK_SET);
-#endif /* __STDIO_MBSTATE */
+#endif /* __STDIO_WIDE */
}
#ifndef L_fsetpos64
@@ -3257,10 +3322,6 @@ void rewind(register FILE *stream)
__CLEARERR(stream); /* Clear errors first and then seek */
fseek(stream, 0L, SEEK_SET); /* in case there is an error seeking. */
-#ifdef __STDIO_MBSTATE
- /* TODO: Is it correct to re-init the stream's state? I think so... */
- __INIT_MBSTATE(&(stream->state));
-#endif /* __STDIO_MBSTATE */
__STDIO_THREADUNLOCK(stream);
}
@@ -3371,15 +3432,8 @@ void _stdio_fdout(int fd, ...)
#define INTERNAL_DIV_MOD
#endif
-#ifdef __UCLIBC_MJN3_ONLY__
-#warning REMINDER: move _uintmaxtostr to locale.c???
-#endif
#include <locale.h>
-#ifndef __LOCALE_C_ONLY
-#define CUR_LOCALE (__global_locale)
-#endif /* __LOCALE_C_ONLY */
-
char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
int base, __UIM_CASE alphacase)
{
@@ -3410,14 +3464,10 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
grouping = -1;
outdigit = 0x80 & alphacase;
alphacase ^= outdigit;
-#ifdef __UCLIBC_MJN3_ONLY_
-#warning implement outdigit... need digit lengths! (put it in locale struct)
-#endif
if (alphacase == __UIM_GROUP) {
assert(base == 10);
- if (*(g = CUR_LOCALE.grouping)
- && ((gslen = strlen(CUR_LOCALE.thousands_sep)) > 0)
- ) {
+ if (*(g = __UCLIBC_CURLOCALE_DATA.grouping)) {
+ gslen = strlen(__UCLIBC_CURLOCALE_DATA.thousands_sep);
grouping = *g;
}
}
@@ -3430,15 +3480,15 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
#ifndef __LOCALE_C_ONLY
if (!grouping) { /* Finished a group. */
#ifdef __UCLIBC_MJN3_ONLY__
-#warning REMINDER: decide about memcpy in _uintmaxtostr
-#endif
+#warning TODO: Decide about memcpy in _uintmaxtostr.
+#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
bufend -= gslen;
- memcpy(bufend, CUR_LOCALE.thousands_sep, gslen);
+ memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, gslen);
#else
grouping = gslen;
do {
- *--bufend = CUR_LOCALE.thousands_sep[--grouping];
+ *--bufend = __UCLIBC_CURLOCALE_DATA.thousands_sep[--grouping];
} while (grouping);
#endif
if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */
@@ -3456,9 +3506,9 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
#ifndef __LOCALE_C_ONLY
if (outdigit) {
- outdigit = CUR_LOCALE.outdigit_length[digit];
+ outdigit = __UCLIBC_CURLOCALE_DATA.outdigit_length[digit];
do {
- *--bufend = (&CUR_LOCALE.outdigit0_mb)[digit][--outdigit];
+ *--bufend = (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit][--outdigit];
} while (outdigit);
outdigit = 1;
} else
@@ -3483,15 +3533,15 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
#ifndef __LOCALE_C_ONLY
if (!grouping) { /* Finished a group. */
#ifdef __UCLIBC_MJN3_ONLY__
-#warning REMINDER: decide about memcpy in _uintmaxtostr
-#endif
+#warning TODO: Decide about memcpy in _uintmaxtostr
+#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
bufend -= gslen;
- memcpy(bufend, CUR_LOCALE.thousands_sep, gslen);
+ memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, gslen);
#else
grouping = gslen;
do {
- *--bufend = CUR_LOCALE.thousands_sep[--grouping];
+ *--bufend = __UCLIBC_CURLOCALE_DATA.thousands_sep[--grouping];
} while (grouping);
#endif
if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */
@@ -3513,9 +3563,9 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
#ifndef __LOCALE_C_ONLY
if (outdigit) {
- outdigit = CUR_LOCALE.outdigit_length[digit];
+ outdigit = __UCLIBC_CURLOCALE_DATA.outdigit_length[digit];
do {
- *--bufend = (&CUR_LOCALE.outdigit0_mb)[digit][--outdigit];
+ *--bufend = (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit][--outdigit];
} while (outdigit);
outdigit = 1;
} else
@@ -3548,7 +3598,7 @@ size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n,
char buf[64];
const wchar_t *pw;
-#ifdef __STDIO_BUFFERS
+#if defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS)
if (stream->filedes == -3) { /* Special case to support {v}swprintf. */
count = ((wchar_t *)(stream->bufend)) - ((wchar_t *)(stream->bufpos));
if (count > n) {
@@ -3560,7 +3610,7 @@ size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n,
}
return n;
}
-#endif
+#endif /* defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS) */
if (stream->modeflags & __FLAG_NARROW) {
stream->modeflags |= __FLAG_ERROR;