Browse Source

Add snprintf and remove our sprintf

philip 3 years ago
parent
commit
60f0150736
5 changed files with 2152 additions and 1074 deletions
  1. 1 1
      app/libc/Makefile
  2. 39 0
      app/libc/snp_config.h
  3. 2110 0
      app/libc/snprintf.c
  4. 0 1072
      app/libc/stdio.c
  5. 2 1
      sdk-overrides/include/stdio.h

+ 1 - 1
app/libc/Makefile

@@ -22,7 +22,7 @@ endif
 #   makefile at its root level - these are then overridden
 #   for a subtree within the makefile rooted therein
 #
-#DEFINES +=
+DEFINES += -DHAVE_CONFIG_H 
 
 #############################################################
 # Recursion Magic - Don't touch this!!

+ 39 - 0
app/libc/snp_config.h

@@ -0,0 +1,39 @@
+#ifndef SNP_CONFIG_H
+#define SNP_CONFIG_H
+
+#define TEST_SNPRINTF 0
+#define HAVE_SNPRINTF 0
+#define HAVE_VSNPRINTF 0
+#define HAVE_ASPRINTF 0
+#define HAVE_VASPRINTF 0
+
+#define HAVE_STDARG_H 1
+#define HAVE_STDLIB_H 1
+
+#define HAVE_VA_COPY 0
+#define HAVE___VA_COPY 0
+#define HAVE_FLOAT_H 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LOCALE_H 0
+#define HAVE_STDDEF_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+#define HAVE_UINTMAX_T 1
+#define HAVE_LONG_DOUBLE 0
+#define HAVE_LONG_LONG_INT 1
+#define HAVE_INTMAX_T 1
+#define HAVE_UINTPTR_T 1
+#define HAVE_PTRDIFF_T 1
+
+#define HAVE_LOCALECONV 0 
+#define HAVE_LCONV_DECIMAL_POINT 0
+#define HAVE_LCONV_THOUSANDS_SEP 0
+#define LDOUBLE_MIN_10_EXP DBL_MIN_10_EXP
+#define LDOUBLE_MAX_10_EXP DBL_MAX_10_EXP
+
+#define rpl_snprintf snprintf
+#define rpl_vsnprintf vsnprintf
+#define rpl_vasprintf vasprintf
+#define rpl_asprintf asprintf
+
+#endif	/* SNP_CONFIG_H */

+ 2110 - 0
app/libc/snprintf.c

@@ -0,0 +1,2110 @@
+/*
+ * Copyright (c) 1995 Patrick Powell.
+ *
+ * This code is based on code written by Patrick Powell <papowell@astart.com>.
+ * It may be used for any purpose as long as this notice remains intact on all
+ * source code distributions.
+ */
+
+/*
+ * Copyright (c) 2008 Holger Weiss.
+ *
+ * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
+ * My changes to the code may freely be used, modified and/or redistributed for
+ * any purpose.  It would be nice if additions and fixes to this file (including
+ * trivial code cleanups) would be sent back in order to let me include them in
+ * the version available at <http://www.jhweiss.de/software/snprintf.html>.
+ * However, this is not a requirement for using or redistributing (possibly
+ * modified) versions of this file, nor is leaving this notice intact mandatory.
+ */
+
+/*
+ * History
+ *
+ * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
+ *
+ * 	Fixed the detection of infinite floating point values on IRIX (and
+ * 	possibly other systems) and applied another few minor cleanups.
+ *
+ * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
+ *
+ * 	Added a lot of new features, fixed many bugs, and incorporated various
+ * 	improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
+ * 	<rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
+ * 	<djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
+ * 	projects.  The additions include: support the "e", "E", "g", "G", and
+ * 	"F" conversion specifiers (and use conversion style "f" or "F" for the
+ * 	still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
+ * 	"t", and "z" length modifiers; support the "#" flag and the (non-C99)
+ * 	"'" flag; use localeconv(3) (if available) to get both the current
+ * 	locale's decimal point character and the separator between groups of
+ * 	digits; fix the handling of various corner cases of field width and
+ * 	precision specifications; fix various floating point conversion bugs;
+ * 	handle infinite and NaN floating point values; don't attempt to write to
+ * 	the output buffer (which may be NULL) if a size of zero was specified;
+ * 	check for integer overflow of the field width, precision, and return
+ * 	values and during the floating point conversion; use the OUTCHAR() macro
+ * 	instead of a function for better performance; provide asprintf(3) and
+ * 	vasprintf(3) functions; add new test cases.  The replacement functions
+ * 	have been renamed to use an "rpl_" prefix, the function calls in the
+ * 	main project (and in this file) must be redefined accordingly for each
+ * 	replacement function which is needed (by using Autoconf or other means).
+ * 	Various other minor improvements have been applied and the coding style
+ * 	was cleaned up for consistency.
+ *
+ * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
+ *
+ * 	C99 compliant snprintf(3) and vsnprintf(3) functions return the number
+ * 	of characters that would have been written to a sufficiently sized
+ * 	buffer (excluding the '\0').  The original code simply returned the
+ * 	length of the resulting output string, so that's been fixed.
+ *
+ * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
+ *
+ * 	The original code assumed that both snprintf(3) and vsnprintf(3) were
+ * 	missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
+ * 	the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
+ *
+ * 	The PGP code was using unsigned hexadecimal formats.  Unfortunately,
+ * 	unsigned formats simply didn't work.
+ *
+ * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
+ *
+ * 	Ok, added some minimal floating point support, which means this probably
+ * 	requires libm on most operating systems.  Don't yet support the exponent
+ * 	(e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
+ * 	wasn't being exercised in ways which showed it, so that's been fixed.
+ * 	Also, formatted the code to Mutt conventions, and removed dead code left
+ * 	over from the original.  Also, there is now a builtin-test, run with:
+ * 	gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
+ *
+ * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
+ *
+ * 	This was ugly.  It is still ugly.  I opted out of floating point
+ * 	numbers, but the formatter understands just about everything from the
+ * 	normal C string format, at least as far as I can tell from the Solaris
+ * 	2.5 printf(3S) man page.
+ */
+
+/*
+ * ToDo
+ *
+ * - Add wide character support.
+ * - Add support for "%a" and "%A" conversions.
+ * - Create test routines which predefine the expected results.  Our test cases
+ *   usually expose bugs in system implementations rather than in ours :-)
+ */
+
+/*
+ * Usage
+ *
+ * 1) The following preprocessor macros should be defined to 1 if the feature or
+ *    file in question is available on the target system (by using Autoconf or
+ *    other means), though basic functionality should be available as long as
+ *    HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
+ *
+ *    	HAVE_VSNPRINTF
+ *    	HAVE_SNPRINTF
+ *    	HAVE_VASPRINTF
+ *    	HAVE_ASPRINTF
+ *    	HAVE_STDARG_H
+ *    	HAVE_STDDEF_H
+ *    	HAVE_STDINT_H
+ *    	HAVE_STDLIB_H
+ *    	HAVE_FLOAT_H
+ *    	HAVE_INTTYPES_H
+ *    	HAVE_LOCALE_H
+ *    	HAVE_LOCALECONV
+ *    	HAVE_LCONV_DECIMAL_POINT
+ *    	HAVE_LCONV_THOUSANDS_SEP
+ *    	HAVE_LONG_DOUBLE
+ *    	HAVE_LONG_LONG_INT
+ *    	HAVE_UNSIGNED_LONG_LONG_INT
+ *    	HAVE_INTMAX_T
+ *    	HAVE_UINTMAX_T
+ *    	HAVE_UINTPTR_T
+ *    	HAVE_PTRDIFF_T
+ *    	HAVE_VA_COPY
+ *    	HAVE___VA_COPY
+ *
+ * 2) The calls to the functions which should be replaced must be redefined
+ *    throughout the project files (by using Autoconf or other means):
+ *
+ *    	#define vsnprintf rpl_vsnprintf
+ *    	#define snprintf rpl_snprintf
+ *    	#define vasprintf rpl_vasprintf
+ *    	#define asprintf rpl_asprintf
+ *
+ * 3) The required replacement functions should be declared in some header file
+ *    included throughout the project files:
+ *
+ *    	#if HAVE_CONFIG_H
+ *    	#include <config.h>
+ *    	#endif
+ *    	#if HAVE_STDARG_H
+ *    	#include <stdarg.h>
+ *    	#if !HAVE_VSNPRINTF
+ *    	int rpl_vsnprintf(char *, size_t, const char *, va_list);
+ *    	#endif
+ *    	#if !HAVE_SNPRINTF
+ *    	int rpl_snprintf(char *, size_t, const char *, ...);
+ *    	#endif
+ *    	#if !HAVE_VASPRINTF
+ *    	int rpl_vasprintf(char **, const char *, va_list);
+ *    	#endif
+ *    	#if !HAVE_ASPRINTF
+ *    	int rpl_asprintf(char **, const char *, ...);
+ *    	#endif
+ *    	#endif
+ *
+ * Autoconf macros for handling step 1 and step 2 are available at
+ * <http://www.jhweiss.de/software/snprintf.html>.
+ */
+
+#if HAVE_CONFIG_H
+#include "snp_config.h"
+#endif	/* HAVE_CONFIG_H */
+
+#if TEST_SNPRINTF
+#include <math.h>	/* For pow(3), NAN, and INFINITY. */
+#include <string.h>	/* For strcmp(3). */
+#if defined(__NetBSD__) || \
+    defined(__FreeBSD__) || \
+    defined(__OpenBSD__) || \
+    defined(__NeXT__) || \
+    defined(__bsd__)
+#define OS_BSD 1
+#elif defined(sgi) || defined(__sgi)
+#ifndef __c99
+#define __c99	/* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
+#endif	/* !defined(__c99) */
+#define OS_IRIX 1
+#define OS_SYSV 1
+#elif defined(__svr4__)
+#define OS_SYSV 1
+#elif defined(__linux__)
+#define OS_LINUX 1
+#endif	/* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
+#if HAVE_CONFIG_H	/* Undefine definitions possibly done in config.h. */
+#ifdef HAVE_SNPRINTF
+#undef HAVE_SNPRINTF
+#endif	/* defined(HAVE_SNPRINTF) */
+#ifdef HAVE_VSNPRINTF
+#undef HAVE_VSNPRINTF
+#endif	/* defined(HAVE_VSNPRINTF) */
+#ifdef HAVE_ASPRINTF
+#undef HAVE_ASPRINTF
+#endif	/* defined(HAVE_ASPRINTF) */
+#ifdef HAVE_VASPRINTF
+#undef HAVE_VASPRINTF
+#endif	/* defined(HAVE_VASPRINTF) */
+#ifdef snprintf
+#undef snprintf
+#endif	/* defined(snprintf) */
+#ifdef vsnprintf
+#undef vsnprintf
+#endif	/* defined(vsnprintf) */
+#ifdef asprintf
+#undef asprintf
+#endif	/* defined(asprintf) */
+#ifdef vasprintf
+#undef vasprintf
+#endif	/* defined(vasprintf) */
+#else	/* By default, we assume a modern system for testing. */
+#ifndef HAVE_STDARG_H
+#define HAVE_STDARG_H 1
+#endif	/* HAVE_STDARG_H */
+#ifndef HAVE_STDDEF_H
+#define HAVE_STDDEF_H 1
+#endif	/* HAVE_STDDEF_H */
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H 1
+#endif	/* HAVE_STDINT_H */
+#ifndef HAVE_STDLIB_H
+#define HAVE_STDLIB_H 1
+#endif	/* HAVE_STDLIB_H */
+#ifndef HAVE_FLOAT_H
+#define HAVE_FLOAT_H 1
+#endif	/* HAVE_FLOAT_H */
+#ifndef HAVE_INTTYPES_H
+#define HAVE_INTTYPES_H 1
+#endif	/* HAVE_INTTYPES_H */
+#ifndef HAVE_LOCALE_H
+#define HAVE_LOCALE_H 1
+#endif	/* HAVE_LOCALE_H */
+#ifndef HAVE_LOCALECONV
+#define HAVE_LOCALECONV 1
+#endif	/* !defined(HAVE_LOCALECONV) */
+#ifndef HAVE_LCONV_DECIMAL_POINT
+#define HAVE_LCONV_DECIMAL_POINT 1
+#endif	/* HAVE_LCONV_DECIMAL_POINT */
+#ifndef HAVE_LCONV_THOUSANDS_SEP
+#define HAVE_LCONV_THOUSANDS_SEP 1
+#endif	/* HAVE_LCONV_THOUSANDS_SEP */
+#ifndef HAVE_LONG_DOUBLE
+#define HAVE_LONG_DOUBLE 1
+#endif	/* !defined(HAVE_LONG_DOUBLE) */
+#ifndef HAVE_LONG_LONG_INT
+#define HAVE_LONG_LONG_INT 1
+#endif	/* !defined(HAVE_LONG_LONG_INT) */
+#ifndef HAVE_UNSIGNED_LONG_LONG_INT
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+#endif	/* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
+#ifndef HAVE_INTMAX_T
+#define HAVE_INTMAX_T 1
+#endif	/* !defined(HAVE_INTMAX_T) */
+#ifndef HAVE_UINTMAX_T
+#define HAVE_UINTMAX_T 1
+#endif	/* !defined(HAVE_UINTMAX_T) */
+#ifndef HAVE_UINTPTR_T
+#define HAVE_UINTPTR_T 1
+#endif	/* !defined(HAVE_UINTPTR_T) */
+#ifndef HAVE_PTRDIFF_T
+#define HAVE_PTRDIFF_T 1
+#endif	/* !defined(HAVE_PTRDIFF_T) */
+#ifndef HAVE_VA_COPY
+#define HAVE_VA_COPY 1
+#endif	/* !defined(HAVE_VA_COPY) */
+#ifndef HAVE___VA_COPY
+#define HAVE___VA_COPY 1
+#endif	/* !defined(HAVE___VA_COPY) */
+#endif	/* HAVE_CONFIG_H */
+#define snprintf rpl_snprintf
+#define vsnprintf rpl_vsnprintf
+#define asprintf rpl_asprintf
+#define vasprintf rpl_vasprintf
+#endif	/* TEST_SNPRINTF */
+
+#if !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || !HAVE_VASPRINTF
+#include <stdio.h>	/* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
+#ifdef VA_START
+#undef VA_START
+#endif	/* defined(VA_START) */
+#ifdef VA_SHIFT
+#undef VA_SHIFT
+#endif	/* defined(VA_SHIFT) */
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#define VA_START(ap, last) va_start(ap, last)
+#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
+#else	/* Assume <varargs.h> is available. */
+#include <varargs.h>
+#define VA_START(ap, last) va_start(ap)	/* "last" is ignored. */
+#define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
+#endif	/* HAVE_STDARG_H */
+
+#if !HAVE_VASPRINTF
+#if HAVE_STDLIB_H
+#include <stdlib.h>	/* For malloc(3). */
+#endif	/* HAVE_STDLIB_H */
+#ifdef VA_COPY
+#undef VA_COPY
+#endif	/* defined(VA_COPY) */
+#ifdef VA_END_COPY
+#undef VA_END_COPY
+#endif	/* defined(VA_END_COPY) */
+#if HAVE_VA_COPY
+#define VA_COPY(dest, src) va_copy(dest, src)
+#define VA_END_COPY(ap) va_end(ap)
+#elif HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#define VA_END_COPY(ap) va_end(ap)
+#else
+#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
+#define VA_END_COPY(ap) /* No-op. */
+#define NEED_MYMEMCPY 1
+static void *mymemcpy(void *, void *, size_t);
+#endif	/* HAVE_VA_COPY */
+#endif	/* !HAVE_VASPRINTF */
+
+#if !HAVE_VSNPRINTF
+#include <errno.h>	/* For ERANGE and errno. */
+#include <limits.h>	/* For *_MAX. */
+#if HAVE_FLOAT_H
+#include <float.h>	/* For *DBL_{MIN,MAX}_10_EXP. */
+#endif	/* HAVE_FLOAT_H */
+#if HAVE_INTTYPES_H
+#include <inttypes.h>	/* For intmax_t (if not defined in <stdint.h>). */
+#endif	/* HAVE_INTTYPES_H */
+#if HAVE_LOCALE_H
+#include <locale.h>	/* For localeconv(3). */
+#endif	/* HAVE_LOCALE_H */
+#if HAVE_STDDEF_H
+#include <stddef.h>	/* For ptrdiff_t. */
+#endif	/* HAVE_STDDEF_H */
+#if HAVE_STDINT_H
+#include <stdint.h>	/* For intmax_t. */
+#endif	/* HAVE_STDINT_H */
+
+/* Support for unsigned long long int.  We may also need ULLONG_MAX. */
+#ifndef ULONG_MAX	/* We may need ULONG_MAX as a fallback. */
+#ifdef UINT_MAX
+#define ULONG_MAX UINT_MAX
+#else
+#define ULONG_MAX INT_MAX
+#endif	/* defined(UINT_MAX) */
+#endif	/* !defined(ULONG_MAX) */
+#ifdef ULLONG
+#undef ULLONG
+#endif	/* defined(ULLONG) */
+#if HAVE_UNSIGNED_LONG_LONG_INT
+#define ULLONG unsigned long long int
+#ifndef ULLONG_MAX
+#define ULLONG_MAX ULONG_MAX
+#endif	/* !defined(ULLONG_MAX) */
+#else
+#define ULLONG unsigned long int
+#ifdef ULLONG_MAX
+#undef ULLONG_MAX
+#endif	/* defined(ULLONG_MAX) */
+#define ULLONG_MAX ULONG_MAX
+#endif	/* HAVE_LONG_LONG_INT */
+
+/* Support for uintmax_t.  We also need UINTMAX_MAX. */
+#ifdef UINTMAX_T
+#undef UINTMAX_T
+#endif	/* defined(UINTMAX_T) */
+#if HAVE_UINTMAX_T || defined(uintmax_t)
+#define UINTMAX_T uintmax_t
+#ifndef UINTMAX_MAX
+#define UINTMAX_MAX ULLONG_MAX
+#endif	/* !defined(UINTMAX_MAX) */
+#else
+#define UINTMAX_T ULLONG
+#ifdef UINTMAX_MAX
+#undef UINTMAX_MAX
+#endif	/* defined(UINTMAX_MAX) */
+#define UINTMAX_MAX ULLONG_MAX
+#endif	/* HAVE_UINTMAX_T || defined(uintmax_t) */
+
+/* Support for long double. */
+#ifndef LDOUBLE
+#if HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#define LDOUBLE_MIN_10_EXP LDBL_MIN_10_EXP
+#define LDOUBLE_MAX_10_EXP LDBL_MAX_10_EXP
+#else
+#define LDOUBLE double
+#define LDOUBLE_MIN_10_EXP DBL_MIN_10_EXP
+#define LDOUBLE_MAX_10_EXP DBL_MAX_10_EXP
+#endif	/* HAVE_LONG_DOUBLE */
+#endif	/* !defined(LDOUBLE) */
+
+/* Support for long long int. */
+#ifndef LLONG
+#if HAVE_LONG_LONG_INT
+#define LLONG long long int
+#else
+#define LLONG long int
+#endif	/* HAVE_LONG_LONG_INT */
+#endif	/* !defined(LLONG) */
+
+/* Support for intmax_t. */
+#ifndef INTMAX_T
+#if HAVE_INTMAX_T || defined(intmax_t)
+#define INTMAX_T intmax_t
+#else
+#define INTMAX_T LLONG
+#endif	/* HAVE_INTMAX_T || defined(intmax_t) */
+#endif	/* !defined(INTMAX_T) */
+
+/* Support for uintptr_t. */
+#ifndef UINTPTR_T
+#if HAVE_UINTPTR_T || defined(uintptr_t)
+#define UINTPTR_T uintptr_t
+#else
+#define UINTPTR_T unsigned long int
+#endif	/* HAVE_UINTPTR_T || defined(uintptr_t) */
+#endif	/* !defined(UINTPTR_T) */
+
+/* Support for ptrdiff_t. */
+#ifndef PTRDIFF_T
+#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
+#define PTRDIFF_T ptrdiff_t
+#else
+#define PTRDIFF_T long int
+#endif	/* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
+#endif	/* !defined(PTRDIFF_T) */
+
+/*
+ * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
+ * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
+ * unsigned type if necessary.  This should work just fine in practice.
+ */
+#ifndef UPTRDIFF_T
+#define UPTRDIFF_T PTRDIFF_T
+#endif	/* !defined(UPTRDIFF_T) */
+
+/*
+ * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
+ * However, we'll simply use size_t and convert it to a signed type if
+ * necessary.  This should work just fine in practice.
+ */
+#ifndef SSIZE_T
+#define SSIZE_T size_t
+#endif	/* !defined(SSIZE_T) */
+
+/* Either ERANGE or E2BIG should be available everywhere. */
+#ifndef ERANGE
+#define ERANGE E2BIG
+#endif	/* !defined(ERANGE) */
+#ifndef EOVERFLOW
+#define EOVERFLOW ERANGE
+#endif	/* !defined(EOVERFLOW) */
+
+/*
+ * Buffer size to hold the octal string representation of UINT128_MAX without
+ * nul-termination ("3777777777777777777777777777777777777777777").
+ */
+#ifdef MAX_CONVERT_LENGTH
+#undef MAX_CONVERT_LENGTH
+#endif	/* defined(MAX_CONVERT_LENGTH) */
+#define MAX_CONVERT_LENGTH      43
+
+/* Format read states. */
+#define PRINT_S_DEFAULT         0
+#define PRINT_S_FLAGS           1
+#define PRINT_S_WIDTH           2
+#define PRINT_S_DOT             3
+#define PRINT_S_PRECISION       4
+#define PRINT_S_MOD             5
+#define PRINT_S_CONV            6
+
+/* Format flags. */
+#define PRINT_F_MINUS           (1 << 0)
+#define PRINT_F_PLUS            (1 << 1)
+#define PRINT_F_SPACE           (1 << 2)
+#define PRINT_F_NUM             (1 << 3)
+#define PRINT_F_ZERO            (1 << 4)
+#define PRINT_F_QUOTE           (1 << 5)
+#define PRINT_F_UP              (1 << 6)
+#define PRINT_F_UNSIGNED        (1 << 7)
+#define PRINT_F_TYPE_G          (1 << 8)
+#define PRINT_F_TYPE_E          (1 << 9)
+
+/* Conversion flags. */
+#define PRINT_C_CHAR            1
+#define PRINT_C_SHORT           2
+#define PRINT_C_LONG            3
+#define PRINT_C_LLONG           4
+#define PRINT_C_LDOUBLE         5
+#define PRINT_C_SIZE            6
+#define PRINT_C_PTRDIFF         7
+#define PRINT_C_INTMAX          8
+
+#ifndef MAX
+#define MAX(x, y) ((x >= y) ? x : y)
+#endif	/* !defined(MAX) */
+#ifndef CHARTOINT
+#define CHARTOINT(ch) (ch - '0')
+#endif	/* !defined(CHARTOINT) */
+#ifndef ISDIGIT
+#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
+#endif	/* !defined(ISDIGIT) */
+#ifndef ISNAN
+#define ISNAN(x) (x != x)
+#endif	/* !defined(ISNAN) */
+#ifndef ISINF
+#define ISINF(x) ((x < -1 || x > 1) && x + x == x)
+#endif	/* !defined(ISINF) */
+
+#ifdef OUTCHAR
+#undef OUTCHAR
+#endif	/* defined(OUTCHAR) */
+#define OUTCHAR(str, len, size, ch)                                          \
+do {                                                                         \
+	if (len + 1 < size)                                                  \
+		str[len] = ch;                                               \
+	(len)++;                                                             \
+} while (/* CONSTCOND */ 0)
+
+static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
+static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
+static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
+static void printsep(char *, size_t *, size_t);
+static int getnumsep(int);
+static int getexponent(LDOUBLE);
+static int convert(UINTMAX_T, char *, size_t, int, int);
+static UINTMAX_T cast(LDOUBLE);
+static UINTMAX_T myround(LDOUBLE);
+static LDOUBLE mypow10(int);
+
+extern int errno;
+
+int
+rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+	LDOUBLE fvalue;
+	INTMAX_T value;
+	unsigned char cvalue;
+	const char *strvalue;
+	INTMAX_T *intmaxptr;
+	PTRDIFF_T *ptrdiffptr;
+	SSIZE_T *sizeptr;
+	LLONG *llongptr;
+	long int *longptr;
+	int *intptr;
+	short int *shortptr;
+	signed char *charptr;
+	size_t len = 0;
+	int overflow = 0;
+	int base = 0;
+	int cflags = 0;
+	int flags = 0;
+	int width = 0;
+	int precision = -1;
+	int state = PRINT_S_DEFAULT;
+	char ch = *format++;
+
+	/*
+	 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
+	 * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
+	 * even if a size larger than zero was specified.  At least NetBSD's
+	 * snprintf(3) does the same, as well as other versions of this file.
+	 * (Though some of these versions will write to a non-NULL buffer even
+	 * if a size of zero was specified, which violates the standard.)
+	 */
+	if (str == NULL && size != 0)
+		size = 0;
+
+	while (ch != '\0')
+		switch (state) {
+		case PRINT_S_DEFAULT:
+			if (ch == '%')
+				state = PRINT_S_FLAGS;
+			else
+				OUTCHAR(str, len, size, ch);
+			ch = *format++;
+			break;
+		case PRINT_S_FLAGS:
+			switch (ch) {
+			case '-':
+				flags |= PRINT_F_MINUS;
+				ch = *format++;
+				break;
+			case '+':
+				flags |= PRINT_F_PLUS;
+				ch = *format++;
+				break;
+			case ' ':
+				flags |= PRINT_F_SPACE;
+				ch = *format++;
+				break;
+			case '#':
+				flags |= PRINT_F_NUM;
+				ch = *format++;
+				break;
+			case '0':
+				flags |= PRINT_F_ZERO;
+				ch = *format++;
+				break;
+			case '\'':	/* SUSv2 flag (not in C99). */
+				flags |= PRINT_F_QUOTE;
+				ch = *format++;
+				break;
+			default:
+				state = PRINT_S_WIDTH;
+				break;
+			}
+			break;
+		case PRINT_S_WIDTH:
+			if (ISDIGIT(ch)) {
+				ch = CHARTOINT(ch);
+				if (width > (INT_MAX - ch) / 10) {
+					overflow = 1;
+					goto out;
+				}
+				width = 10 * width + ch;
+				ch = *format++;
+			} else if (ch == '*') {
+				/*
+				 * C99 says: "A negative field width argument is
+				 * taken as a `-' flag followed by a positive
+				 * field width." (7.19.6.1, 5)
+				 */
+				if ((width = va_arg(args, int)) < 0) {
+					flags |= PRINT_F_MINUS;
+					width = -width;
+				}
+				ch = *format++;
+				state = PRINT_S_DOT;
+			} else
+				state = PRINT_S_DOT;
+			break;
+		case PRINT_S_DOT:
+			if (ch == '.') {
+				state = PRINT_S_PRECISION;
+				ch = *format++;
+			} else
+				state = PRINT_S_MOD;
+			break;
+		case PRINT_S_PRECISION:
+			if (precision == -1)
+				precision = 0;
+			if (ISDIGIT(ch)) {
+				ch = CHARTOINT(ch);
+				if (precision > (INT_MAX - ch) / 10) {
+					overflow = 1;
+					goto out;
+				}
+				precision = 10 * precision + ch;
+				ch = *format++;
+			} else if (ch == '*') {
+				/*
+				 * C99 says: "A negative precision argument is
+				 * taken as if the precision were omitted."
+				 * (7.19.6.1, 5)
+				 */
+				if ((precision = va_arg(args, int)) < 0)
+					precision = -1;
+				ch = *format++;
+				state = PRINT_S_MOD;
+			} else
+				state = PRINT_S_MOD;
+			break;
+		case PRINT_S_MOD:
+			switch (ch) {
+			case 'h':
+				ch = *format++;
+				if (ch == 'h') {	/* It's a char. */
+					ch = *format++;
+					cflags = PRINT_C_CHAR;
+				} else
+					cflags = PRINT_C_SHORT;
+				break;
+			case 'l':
+				ch = *format++;
+				if (ch == 'l') {	/* It's a long long. */
+					ch = *format++;
+					cflags = PRINT_C_LLONG;
+				} else
+					cflags = PRINT_C_LONG;
+				break;
+			case 'L':
+				cflags = PRINT_C_LDOUBLE;
+				ch = *format++;
+				break;
+			case 'j':
+				cflags = PRINT_C_INTMAX;
+				ch = *format++;
+				break;
+			case 't':
+				cflags = PRINT_C_PTRDIFF;
+				ch = *format++;
+				break;
+			case 'z':
+				cflags = PRINT_C_SIZE;
+				ch = *format++;
+				break;
+			}
+			state = PRINT_S_CONV;
+			break;
+		case PRINT_S_CONV:
+			switch (ch) {
+			case 'd':
+				/* FALLTHROUGH */
+			case 'i':
+				switch (cflags) {
+				case PRINT_C_CHAR:
+					value = (signed char)va_arg(args, int);
+					break;
+				case PRINT_C_SHORT:
+					value = (short int)va_arg(args, int);
+					break;
+				case PRINT_C_LONG:
+					value = va_arg(args, long int);
+					break;
+				case PRINT_C_LLONG:
+					value = va_arg(args, LLONG);
+					break;
+				case PRINT_C_SIZE:
+					value = va_arg(args, SSIZE_T);
+					break;
+				case PRINT_C_INTMAX:
+					value = va_arg(args, INTMAX_T);
+					break;
+				case PRINT_C_PTRDIFF:
+					value = va_arg(args, PTRDIFF_T);
+					break;
+				default:
+					value = va_arg(args, int);
+					break;
+				}
+				fmtint(str, &len, size, value, 10, width,
+				    precision, flags);
+				break;
+			case 'X':
+				flags |= PRINT_F_UP;
+				/* FALLTHROUGH */
+			case 'x':
+				base = 16;
+				/* FALLTHROUGH */
+			case 'o':
+				if (base == 0)
+					base = 8;
+				/* FALLTHROUGH */
+			case 'u':
+				if (base == 0)
+					base = 10;
+				flags |= PRINT_F_UNSIGNED;
+				switch (cflags) {
+				case PRINT_C_CHAR:
+					value = (unsigned char)va_arg(args,
+					    unsigned int);
+					break;
+				case PRINT_C_SHORT:
+					value = (unsigned short int)va_arg(args,
+					    unsigned int);
+					break;
+				case PRINT_C_LONG:
+					value = va_arg(args, unsigned long int);
+					break;
+				case PRINT_C_LLONG:
+					value = va_arg(args, ULLONG);
+					break;
+				case PRINT_C_SIZE:
+					value = va_arg(args, size_t);
+					break;
+				case PRINT_C_INTMAX:
+					value = va_arg(args, UINTMAX_T);
+					break;
+				case PRINT_C_PTRDIFF:
+					value = va_arg(args, UPTRDIFF_T);
+					break;
+				default:
+					value = va_arg(args, unsigned int);
+					break;
+				}
+				fmtint(str, &len, size, value, base, width,
+				    precision, flags);
+				break;
+			case 'A':
+				/* Not yet supported, we'll use "%F". */
+				/* FALLTHROUGH */
+			case 'E':
+				if (ch == 'E')
+					flags |= PRINT_F_TYPE_E;
+				/* FALLTHROUGH */
+			case 'G':
+				if (ch == 'G')
+					flags |= PRINT_F_TYPE_G;
+				/* FALLTHROUGH */
+			case 'F':
+				flags |= PRINT_F_UP;
+				/* FALLTHROUGH */
+			case 'a':
+				/* Not yet supported, we'll use "%f". */
+				/* FALLTHROUGH */
+			case 'e':
+				if (ch == 'e')
+					flags |= PRINT_F_TYPE_E;
+				/* FALLTHROUGH */
+			case 'g':
+				if (ch == 'g')
+					flags |= PRINT_F_TYPE_G;
+				/* FALLTHROUGH */
+			case 'f':
+				if (cflags == PRINT_C_LDOUBLE)
+					fvalue = va_arg(args, LDOUBLE);
+				else
+					fvalue = va_arg(args, double);
+				fmtflt(str, &len, size, fvalue, width,
+				    precision, flags, &overflow);
+				if (overflow)
+					goto out;
+				break;
+			case 'c':
+				cvalue = va_arg(args, int);
+				OUTCHAR(str, len, size, cvalue);
+				break;
+			case 's':
+				strvalue = va_arg(args, char *);
+				fmtstr(str, &len, size, strvalue, width,
+				    precision, flags);
+				break;
+			case 'p':
+				/*
+				 * C99 says: "The value of the pointer is
+				 * converted to a sequence of printing
+				 * characters, in an implementation-defined
+				 * manner." (C99: 7.19.6.1, 8)
+				 */
+				if ((strvalue = va_arg(args, void *)) == NULL)
+					/*
+					 * We use the glibc format.  BSD prints
+					 * "0x0", SysV "0".
+					 */
+					fmtstr(str, &len, size, "(nil)", width,
+					    -1, flags);
+				else {
+					/*
+					 * We use the BSD/glibc format.  SysV
+					 * omits the "0x" prefix (which we emit
+					 * using the PRINT_F_NUM flag).
+					 */
+					flags |= PRINT_F_NUM;
+					flags |= PRINT_F_UNSIGNED;
+					fmtint(str, &len, size,
+					    (UINTPTR_T)strvalue, 16, width,
+					    precision, flags);
+				}
+				break;
+			case 'n':
+				switch (cflags) {
+				case PRINT_C_CHAR:
+					charptr = va_arg(args, signed char *);
+					*charptr = len;
+					break;
+				case PRINT_C_SHORT:
+					shortptr = va_arg(args, short int *);
+					*shortptr = len;
+					break;
+				case PRINT_C_LONG:
+					longptr = va_arg(args, long int *);
+					*longptr = len;
+					break;
+				case PRINT_C_LLONG:
+					llongptr = va_arg(args, LLONG *);
+					*llongptr = len;
+					break;
+				case PRINT_C_SIZE:
+					/*
+					 * C99 says that with the "z" length
+					 * modifier, "a following `n' conversion
+					 * specifier applies to a pointer to a
+					 * signed integer type corresponding to
+					 * size_t argument." (7.19.6.1, 7)
+					 */
+					sizeptr = va_arg(args, SSIZE_T *);
+					*sizeptr = len;
+					break;
+				case PRINT_C_INTMAX:
+					intmaxptr = va_arg(args, INTMAX_T *);
+					*intmaxptr = len;
+					break;
+				case PRINT_C_PTRDIFF:
+					ptrdiffptr = va_arg(args, PTRDIFF_T *);
+					*ptrdiffptr = len;
+					break;
+				default:
+					intptr = va_arg(args, int *);
+					*intptr = len;
+					break;
+				}
+				break;
+			case '%':	/* Print a "%" character verbatim. */
+				OUTCHAR(str, len, size, ch);
+				break;
+			default:	/* Skip other characters. */
+				break;
+			}
+			ch = *format++;
+			state = PRINT_S_DEFAULT;
+			base = cflags = flags = width = 0;
+			precision = -1;
+			break;
+		}
+out:
+	if (len < size)
+		str[len] = '\0';
+	else if (size > 0)
+		str[size - 1] = '\0';
+
+	if (overflow || len > INT_MAX) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	return (int)len;
+}
+
+static void
+fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
+       int precision, int flags)
+{
+	int padlen, strln;	/* Amount to pad. */
+	int noprecision = (precision == -1);
+
+	if (value == NULL)	/* We're forgiving. */
+		value = "(null)";
+
+	/* If a precision was specified, don't read the string past it. */
+	for (strln = 0; value[strln] != '\0' &&
+	    (noprecision || strln < precision); strln++)
+		continue;
+
+	if ((padlen = width - strln) < 0)
+		padlen = 0;
+	if (flags & PRINT_F_MINUS)	/* Left justify. */
+		padlen = -padlen;
+
+	while (padlen > 0) {	/* Leading spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen--;
+	}
+	while (*value != '\0' && (noprecision || precision-- > 0)) {
+		OUTCHAR(str, *len, size, *value);
+		value++;
+	}
+	while (padlen < 0) {	/* Trailing spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen++;
+	}
+}
+
+static void
+fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
+       int precision, int flags)
+{
+	UINTMAX_T uvalue;
+	char iconvert[MAX_CONVERT_LENGTH];
+	char sign = 0;
+	char hexprefix = 0;
+	int spadlen = 0;	/* Amount to space pad. */
+	int zpadlen = 0;	/* Amount to zero pad. */
+	int pos;
+	int separators = (flags & PRINT_F_QUOTE);
+	int noprecision = (precision == -1);
+
+	if (flags & PRINT_F_UNSIGNED)
+		uvalue = value;
+	else {
+		uvalue = (value >= 0) ? value : -value;
+		if (value < 0)
+			sign = '-';
+		else if (flags & PRINT_F_PLUS)	/* Do a sign. */
+			sign = '+';
+		else if (flags & PRINT_F_SPACE)
+			sign = ' ';
+	}
+
+	pos = convert(uvalue, iconvert, sizeof(iconvert), base,
+	    flags & PRINT_F_UP);
+
+	if (flags & PRINT_F_NUM && uvalue != 0) {
+		/*
+		 * C99 says: "The result is converted to an `alternative form'.
+		 * For `o' conversion, it increases the precision, if and only
+		 * if necessary, to force the first digit of the result to be a
+		 * zero (if the value and precision are both 0, a single 0 is
+		 * printed).  For `x' (or `X') conversion, a nonzero result has
+		 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
+		 */
+		switch (base) {
+		case 8:
+			if (precision <= pos)
+				precision = pos + 1;
+			break;
+		case 16:
+			hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
+			break;
+		}
+	}
+
+	if (separators)	/* Get the number of group separators we'll print. */
+		separators = getnumsep(pos);
+
+	zpadlen = precision - pos - separators;
+	spadlen = width                         /* Minimum field width. */
+	    - separators                        /* Number of separators. */
+	    - MAX(precision, pos)               /* Number of integer digits. */
+	    - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
+	    - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
+
+	if (zpadlen < 0)
+		zpadlen = 0;
+	if (spadlen < 0)
+		spadlen = 0;
+
+	/*
+	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+	 * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
+	 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
+	 */
+	if (flags & PRINT_F_MINUS)	/* Left justify. */
+		spadlen = -spadlen;
+	else if (flags & PRINT_F_ZERO && noprecision) {
+		zpadlen += spadlen;
+		spadlen = 0;
+	}
+	while (spadlen > 0) {	/* Leading spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		spadlen--;
+	}
+	if (sign != 0)	/* Sign. */
+		OUTCHAR(str, *len, size, sign);
+	if (hexprefix != 0) {	/* A "0x" or "0X" prefix. */
+		OUTCHAR(str, *len, size, '0');
+		OUTCHAR(str, *len, size, hexprefix);
+	}
+	while (zpadlen > 0) {	/* Leading zeros. */
+		OUTCHAR(str, *len, size, '0');
+		zpadlen--;
+	}
+	while (pos > 0) {	/* The actual digits. */
+		pos--;
+		OUTCHAR(str, *len, size, iconvert[pos]);
+		if (separators > 0 && pos > 0 && pos % 3 == 0)
+			printsep(str, len, size);
+	}
+	while (spadlen < 0) {	/* Trailing spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		spadlen++;
+	}
+}
+
+static void
+fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
+       int precision, int flags, int *overflow)
+{
+	LDOUBLE ufvalue;
+	UINTMAX_T intpart;
+	UINTMAX_T fracpart;
+	UINTMAX_T mask;
+	const char *infnan = NULL;
+	char iconvert[MAX_CONVERT_LENGTH];
+	char fconvert[MAX_CONVERT_LENGTH];
+	char econvert[5];	/* "e-300" (without nul-termination). */
+	char esign = 0;
+	char sign = 0;
+	int leadfraczeros = 0;
+	int exponent = 0;
+	int emitpoint = 0;
+	int omitzeros = 0;
+	int omitcount = 0;
+	int padlen = 0;
+	int epos = 0;
+	int fpos = 0;
+	int ipos = 0;
+	int separators = (flags & PRINT_F_QUOTE);
+	int estyle = (flags & PRINT_F_TYPE_E);
+#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
+	struct lconv *lc = localeconv();
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
+
+	/*
+	 * AIX' man page says the default is 0, but C99 and at least Solaris'
+	 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
+	 * defaults to 6.
+	 */
+	if (precision == -1)
+		precision = 6;
+
+	if (fvalue < 0.0)
+		sign = '-';
+	else if (flags & PRINT_F_PLUS)	/* Do a sign. */
+		sign = '+';
+	else if (flags & PRINT_F_SPACE)
+		sign = ' ';
+
+	if (ISNAN(fvalue))
+		infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
+	else if (ISINF(fvalue))
+		infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
+
+	if (infnan != NULL) {
+		if (sign != 0)
+			iconvert[ipos++] = sign;
+		while (*infnan != '\0')
+			iconvert[ipos++] = *infnan++;
+		fmtstr(str, len, size, iconvert, width, ipos, flags);
+		return;
+	}
+
+	/* "%e" (or "%E") or "%g" (or "%G") conversion. */
+	if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
+		if (flags & PRINT_F_TYPE_G) {
+			/*
+			 * If the precision is zero, it is treated as one (cf.
+			 * C99: 7.19.6.1, 8).
+			 */
+			if (precision == 0)
+				precision = 1;
+			/*
+			 * For "%g" (and "%G") conversions, the precision
+			 * specifies the number of significant digits, which
+			 * includes the digits in the integer part.  The
+			 * conversion will or will not be using "e-style" (like
+			 * "%e" or "%E" conversions) depending on the precision
+			 * and on the exponent.  However, the exponent can be
+			 * affected by rounding the converted value, so we'll
+			 * leave this decision for later.  Until then, we'll
+			 * assume that we're going to do an "e-style" conversion
+			 * (in order to get the exponent calculated).  For
+			 * "e-style", the precision must be decremented by one.
+			 */
+			precision--;
+			/*
+			 * For "%g" (and "%G") conversions, trailing zeros are
+			 * removed from the fractional portion of the result
+			 * unless the "#" flag was specified.
+			 */
+			if (!(flags & PRINT_F_NUM))
+				omitzeros = 1;
+		}
+		exponent = getexponent(fvalue);
+		estyle = 1;
+	}
+
+again:
+	/*
+	 * Sorry, we only support 9, 19, or 38 digits (that is, the number of
+	 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
+	 * minus one) past the decimal point due to our conversion method.
+	 */
+	switch (sizeof(UINTMAX_T)) {
+	case 16:
+		if (precision > 38)
+			precision = 38;
+		break;
+	case 8:
+		if (precision > 19)
+			precision = 19;
+		break;
+	default:
+		if (precision > 9)
+			precision = 9;
+		break;
+	}
+
+	ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
+	if (estyle)	/* We want exactly one integer digit. */
+		ufvalue /= mypow10(exponent);
+
+	if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
+		*overflow = 1;
+		return;
+	}
+
+	/*
+	 * Factor of ten with the number of digits needed for the fractional
+	 * part.  For example, if the precision is 3, the mask will be 1000.
+	 */
+	mask = mypow10(precision);
+	/*
+	 * We "cheat" by converting the fractional part to integer by
+	 * multiplying by a factor of ten.
+	 */
+	if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
+		/*
+		 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
+		 * (because precision = 3).  Now, myround(1000 * 0.99962) will
+		 * return 1000.  So, the integer part must be incremented by one
+		 * and the fractional part must be set to zero.
+		 */
+		intpart++;
+		fracpart = 0;
+		if (estyle && intpart == 10) {
+			/*
+			 * The value was rounded up to ten, but we only want one
+			 * integer digit if using "e-style".  So, the integer
+			 * part must be set to one and the exponent must be
+			 * incremented by one.
+			 */
+			intpart = 1;
+			exponent++;
+		}
+	}
+
+	/*
+	 * Now that we know the real exponent, we can check whether or not to
+	 * use "e-style" for "%g" (and "%G") conversions.  If we don't need
+	 * "e-style", the precision must be adjusted and the integer and
+	 * fractional parts must be recalculated from the original value.
+	 *
+	 * C99 says: "Let P equal the precision if nonzero, 6 if the precision
+	 * is omitted, or 1 if the precision is zero.  Then, if a conversion
+	 * with style `E' would have an exponent of X:
+	 *
+	 * - if P > X >= -4, the conversion is with style `f' (or `F') and
+	 *   precision P - (X + 1).
+	 *
+	 * - otherwise, the conversion is with style `e' (or `E') and precision
+	 *   P - 1." (7.19.6.1, 8)
+	 *
+	 * Note that we had decremented the precision by one.
+	 */
+	if (flags & PRINT_F_TYPE_G && estyle &&
+	    precision + 1 > exponent && exponent >= -4) {
+		precision -= exponent;
+		estyle = 0;
+		goto again;
+	}
+
+	if (estyle) {
+		if (exponent < 0) {
+			exponent = -exponent;
+			esign = '-';
+		} else
+			esign = '+';
+
+		/*
+		 * Convert the exponent.  The sizeof(econvert) is 5.  So, the
+		 * econvert buffer can hold e.g. "e+999" and "e-999".  We don't
+		 * support an exponent which contains more than three digits.
+		 * Therefore, the following stores are safe.
+		 */
+		epos = convert(exponent, econvert, 3, 10, 0);
+		/*
+		 * C99 says: "The exponent always contains at least two digits,
+		 * and only as many more digits as necessary to represent the
+		 * exponent." (7.19.6.1, 8)
+		 */
+		if (epos == 1)
+			econvert[epos++] = '0';
+		econvert[epos++] = esign;
+		econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
+	}
+
+	/* Convert the integer part and the fractional part. */
+	ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
+	if (fracpart != 0)	/* convert() would return 1 if fracpart == 0. */
+		fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
+
+	leadfraczeros = precision - fpos;
+
+	if (omitzeros) {
+		if (fpos > 0)	/* Omit trailing fractional part zeros. */
+			while (omitcount < fpos && fconvert[omitcount] == '0')
+				omitcount++;
+		else {	/* The fractional part is zero, omit it completely. */
+			omitcount = precision;
+			leadfraczeros = 0;
+		}
+		precision -= omitcount;
+	}
+
+	/*
+	 * Print a decimal point if either the fractional part is non-zero
+	 * and/or the "#" flag was specified.
+	 */
+	if (precision > 0 || flags & PRINT_F_NUM)
+		emitpoint = 1;
+	if (separators)	/* Get the number of group separators we'll print. */
+		separators = getnumsep(ipos);
+
+	padlen = width                  /* Minimum field width. */
+	    - ipos                      /* Number of integer digits. */
+	    - epos                      /* Number of exponent characters. */
+	    - precision                 /* Number of fractional digits. */
+	    - separators                /* Number of group separators. */
+	    - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
+	    - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
+
+	if (padlen < 0)
+		padlen = 0;
+
+	/*
+	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+	 * ignored." (7.19.6.1, 6)
+	 */
+	if (flags & PRINT_F_MINUS)	/* Left justifty. */
+		padlen = -padlen;
+	else if (flags & PRINT_F_ZERO && padlen > 0) {
+		if (sign != 0) {	/* Sign. */
+			OUTCHAR(str, *len, size, sign);
+			sign = 0;
+		}
+		while (padlen > 0) {	/* Leading zeros. */
+			OUTCHAR(str, *len, size, '0');
+			padlen--;
+		}
+	}
+	while (padlen > 0) {	/* Leading spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen--;
+	}
+	if (sign != 0)	/* Sign. */
+		OUTCHAR(str, *len, size, sign);
+	while (ipos > 0) {	/* Integer part. */
+		ipos--;
+		OUTCHAR(str, *len, size, iconvert[ipos]);
+		if (separators > 0 && ipos > 0 && ipos % 3 == 0)
+			printsep(str, len, size);
+	}
+	if (emitpoint) {	/* Decimal point. */
+#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
+		if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
+			OUTCHAR(str, *len, size, *lc->decimal_point);
+		else	/* We'll always print some decimal point character. */
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
+			OUTCHAR(str, *len, size, '.');
+	}
+	while (leadfraczeros > 0) {	/* Leading fractional part zeros. */
+		OUTCHAR(str, *len, size, '0');
+		leadfraczeros--;
+	}
+	while (fpos > omitcount) {	/* The remaining fractional part. */
+		fpos--;
+		OUTCHAR(str, *len, size, fconvert[fpos]);
+	}
+	while (epos > 0) {	/* Exponent. */
+		epos--;
+		OUTCHAR(str, *len, size, econvert[epos]);
+	}
+	while (padlen < 0) {	/* Trailing spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen++;
+	}
+}
+
+static void
+printsep(char *str, size_t *len, size_t size)
+{
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+	struct lconv *lc = localeconv();
+	int i;
+
+	if (lc->thousands_sep != NULL)
+		for (i = 0; lc->thousands_sep[i] != '\0'; i++)
+			OUTCHAR(str, *len, size, lc->thousands_sep[i]);
+	else
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
+		OUTCHAR(str, *len, size, ',');
+}
+
+static int
+getnumsep(int digits)
+{
+	int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+	int strln;
+	struct lconv *lc = localeconv();
+
+	/* We support an arbitrary separator length (including zero). */
+	if (lc->thousands_sep != NULL) {
+		for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
+			continue;
+		separators *= strln;
+	}
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
+	return separators;
+}
+
+static int
+getexponent(LDOUBLE value)
+{
+	LDOUBLE tmp = (value >= 0.0) ? value : -value;
+	int exponent = 0;
+
+	/*
+	 * We check for LDOUBLE_MAX_10_EXP >= exponent >= LDOUBLE_MIN_10_EXP in
+	 * order to work around possible endless loops which could happen (at
+	 * least) in the second loop (at least) if we're called with an infinite
+	 * value.  However, we checked for infinity before calling this function
+	 * using our ISINF() macro, so this might be somewhat paranoid.
+	 */
+	while (tmp < 1.0 && tmp > 0.0 && --exponent >= LDOUBLE_MIN_10_EXP)
+		tmp *= 10;
+	while (tmp >= 10.0 && ++exponent <= LDOUBLE_MAX_10_EXP)
+		tmp /= 10;
+
+	return exponent;
+}
+
+static int
+convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
+{
+	const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
+	size_t pos = 0;
+
+	/* We return an unterminated buffer with the digits in reverse order. */
+	do {
+		buf[pos++] = digits[value % base];
+		value /= base;
+	} while (value != 0 && pos < size);
+
+	return (int)pos;
+}
+
+static UINTMAX_T
+cast(LDOUBLE value)
+{
+	UINTMAX_T result;
+
+	/*
+	 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
+	 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
+	 * it may be increased to the nearest higher representable value for the
+	 * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
+	 * value although converting the latter to UINTMAX_T would overflow.
+	 */
+	if (value >= UINTMAX_MAX)
+		return UINTMAX_MAX;
+
+	result = value;
+	/*
+	 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
+	 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
+	 * the standard).  Sigh.
+	 */
+	return (result <= value) ? result : result - 1;
+}
+
+static UINTMAX_T
+myround(LDOUBLE value)
+{
+	UINTMAX_T intpart = cast(value);
+
+	return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
+}
+
+static LDOUBLE
+mypow10(int exponent)
+{
+	LDOUBLE result = 1;
+
+	while (exponent > 0) {
+		result *= 10;
+		exponent--;
+	}
+	while (exponent < 0) {
+		result /= 10;
+		exponent++;
+	}
+	return result;
+}
+#endif	/* !HAVE_VSNPRINTF */
+
+#if !HAVE_VASPRINTF
+#if NEED_MYMEMCPY
+void *
+mymemcpy(void *dst, void *src, size_t len)
+{
+	const char *from = src;
+	char *to = dst;
+
+	/* No need for optimization, we use this only to replace va_copy(3). */
+	while (len-- > 0)
+		*to++ = *from++;
+	return dst;
+}
+#endif	/* NEED_MYMEMCPY */
+
+int
+rpl_vasprintf(char **ret, const char *format, va_list ap)
+{
+	size_t size;
+	int len;
+	va_list aq;
+
+	VA_COPY(aq, ap);
+	len = vsnprintf(NULL, 0, format, aq);
+	VA_END_COPY(aq);
+	if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
+		return -1;
+	return vsnprintf(*ret, size, format, ap);
+}
+#endif	/* !HAVE_VASPRINTF */
+
+#if !HAVE_SNPRINTF
+#if HAVE_STDARG_H
+int
+rpl_snprintf(char *str, size_t size, const char *format, ...)
+#else
+int
+rpl_snprintf(va_alist) va_dcl
+#endif	/* HAVE_STDARG_H */
+{
+#if !HAVE_STDARG_H
+	char *str;
+	size_t size;
+	char *format;
+#endif	/* HAVE_STDARG_H */
+	va_list ap;
+	int len;
+
+	VA_START(ap, format);
+	VA_SHIFT(ap, str, char *);
+	VA_SHIFT(ap, size, size_t);
+	VA_SHIFT(ap, format, const char *);
+	len = vsnprintf(str, size, format, ap);
+	va_end(ap);
+	return len;
+}
+#endif	/* !HAVE_SNPRINTF */
+
+#if !HAVE_ASPRINTF
+#if HAVE_STDARG_H
+int
+rpl_asprintf(char **ret, const char *format, ...)
+#else
+int
+rpl_asprintf(va_alist) va_dcl
+#endif	/* HAVE_STDARG_H */
+{
+#if !HAVE_STDARG_H
+	char **ret;
+	char *format;
+#endif	/* HAVE_STDARG_H */
+	va_list ap;
+	int len;
+
+	VA_START(ap, format);
+	VA_SHIFT(ap, ret, char **);
+	VA_SHIFT(ap, format, const char *);
+	len = vasprintf(ret, format, ap);
+	va_end(ap);
+	return len;
+}
+#endif	/* !HAVE_ASPRINTF */
+#else	/* Dummy declaration to avoid empty translation unit warnings. */
+int main(int argc, char **argv);
+#endif	/* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */
+
+int sprintf(char *s, const char *fmt, ...)
+{
+  int n;
+  va_list arg;
+  va_start(arg, fmt);
+  n = vsnprintf(s, 1024, fmt, arg);
+  va_end(arg);
+  return n;
+}
+
+int vsprintf (char *d, const char *s, va_list ap)
+{
+  return vsnprintf(d, 1024, s, ap);
+}
+
+#if TEST_SNPRINTF
+int
+main(void)
+{
+	const char *float_fmt[] = {
+		/* "%E" and "%e" formats. */
+#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
+		"%.16e",
+		"%22.16e",
+		"%022.16e",
+		"%-22.16e",
+		"%#+'022.16e",
+#endif	/* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
+		"foo|%#+0123.9E|bar",
+		"%-123.9e",
+		"%123.9e",
+		"%+23.9e",
+		"%+05.8e",
+		"%-05.8e",
+		"%05.8e",
+		"%+5.8e",
+		"%-5.8e",
+		"% 5.8e",
+		"%5.8e",
+		"%+4.9e",
+#if !OS_LINUX	/* glibc sometimes gets these wrong. */
+		"%+#010.0e",
+		"%#10.1e",
+		"%10.5e",
+		"% 10.5e",
+		"%5.0e",
+		"%5.e",
+		"%#5.0e",
+		"%#5.e",
+		"%3.2e",
+		"%3.1e",
+		"%-1.5e",
+		"%1.5e",
+		"%01.3e",
+		"%1.e",
+		"%.1e",
+		"%#.0e",
+		"%+.0e",
+		"% .0e",
+		"%.0e",
+		"%#.e",
+		"%+.e",
+		"% .e",
+		"%.e",
+		"%4e",
+		"%e",
+		"%E",
+#endif	/* !OS_LINUX */
+		/* "%F" and "%f" formats. */
+#if !OS_BSD && !OS_IRIX
+		"% '022f",
+		"%+'022f",
+		"%-'22f",
+		"%'22f",
+#if HAVE_LONG_LONG_INT
+		"%.16f",
+		"%22.16f",
+		"%022.16f",
+		"%-22.16f",
+		"%#+'022.16f",
+#endif	/* HAVE_LONG_LONG_INT */
+#endif	/* !OS_BSD && !OS_IRIX */
+		"foo|%#+0123.9F|bar",
+		"%-123.9f",
+		"%123.9f",
+		"%+23.9f",
+		"%+#010.0f",
+		"%#10.1f",
+		"%10.5f",
+		"% 10.5f",
+		"%+05.8f",
+		"%-05.8f",
+		"%05.8f",
+		"%+5.8f",
+		"%-5.8f",
+		"% 5.8f",
+		"%5.8f",
+		"%5.0f",
+		"%5.f",
+		"%#5.0f",
+		"%#5.f",
+		"%+4.9f",
+		"%3.2f",
+		"%3.1f",
+		"%-1.5f",
+		"%1.5f",
+		"%01.3f",
+		"%1.f",
+		"%.1f",
+		"%#.0f",
+		"%+.0f",
+		"% .0f",
+		"%.0f",
+		"%#.f",
+		"%+.f",
+		"% .f",
+		"%.f",
+		"%4f",
+		"%f",
+		"%F",
+		/* "%G" and "%g" formats. */
+#if !OS_BSD && !OS_IRIX && !OS_LINUX
+		"% '022g",
+		"%+'022g",
+		"%-'22g",
+		"%'22g",
+#if HAVE_LONG_LONG_INT
+		"%.16g",
+		"%22.16g",
+		"%022.16g",
+		"%-22.16g",
+		"%#+'022.16g",
+#endif	/* HAVE_LONG_LONG_INT */
+#endif	/* !OS_BSD && !OS_IRIX && !OS_LINUX */
+		"foo|%#+0123.9G|bar",
+		"%-123.9g",
+		"%123.9g",
+		"%+23.9g",
+		"%+05.8g",
+		"%-05.8g",
+		"%05.8g",
+		"%+5.8g",
+		"%-5.8g",
+		"% 5.8g",
+		"%5.8g",
+		"%+4.9g",
+#if !OS_LINUX	/* glibc sometimes gets these wrong. */
+		"%+#010.0g",
+		"%#10.1g",
+		"%10.5g",
+		"% 10.5g",
+		"%5.0g",
+		"%5.g",
+		"%#5.0g",
+		"%#5.g",
+		"%3.2g",
+		"%3.1g",
+		"%-1.5g",
+		"%1.5g",
+		"%01.3g",
+		"%1.g",
+		"%.1g",
+		"%#.0g",
+		"%+.0g",
+		"% .0g",
+		"%.0g",
+		"%#.g",
+		"%+.g",
+		"% .g",
+		"%.g",
+		"%4g",
+		"%g",
+		"%G",
+#endif	/* !OS_LINUX */
+		NULL
+	};
+	double float_val[] = {
+		-4.136,
+		-134.52,
+		-5.04030201,
+		-3410.01234,
+		-999999.999999,
+		-913450.29876,
+		-913450.2,
+		-91345.2,
+		-9134.2,
+		-913.2,
+		-91.2,
+		-9.2,
+		-9.9,
+		4.136,
+		134.52,
+		5.04030201,
+		3410.01234,
+		999999.999999,
+		913450.29876,
+		913450.2,
+		91345.2,
+		9134.2,
+		913.2,
+		91.2,
+		9.2,
+		9.9,
+		9.96,
+		9.996,
+		9.9996,
+		9.99996,
+		9.999996,
+		9.9999996,
+		9.99999996,
+		0.99999996,
+		0.99999999,
+		0.09999999,
+		0.00999999,
+		0.00099999,
+		0.00009999,
+		0.00000999,
+		0.00000099,
+		0.00000009,
+		0.00000001,
+		0.0000001,
+		0.000001,
+		0.00001,
+		0.0001,
+		0.001,
+		0.01,
+		0.1,
+		1.0,
+		1.5,
+		-1.5,
+		-1.0,
+		-0.1,
+#if !OS_BSD	/* BSD sometimes gets these wrong. */
+#ifdef INFINITY
+		INFINITY,
+		-INFINITY,
+#endif	/* defined(INFINITY) */
+#ifdef NAN
+		NAN,
+#endif	/* defined(NAN) */
+#endif	/* !OS_BSD */
+		0
+	};
+	const char *long_fmt[] = {
+		"foo|%0123ld|bar",
+#if !OS_IRIX
+		"% '0123ld",
+		"%+'0123ld",
+		"%-'123ld",
+		"%'123ld",
+#endif	/* !OS_IRiX */
+		"%123.9ld",
+		"% 123.9ld",
+		"%+123.9ld",
+		"%-123.9ld",
+		"%0123ld",
+		"% 0123ld",
+		"%+0123ld",
+		"%-0123ld",
+		"%10.5ld",
+		"% 10.5ld",
+		"%+10.5ld",
+		"%-10.5ld",
+		"%010ld",
+		"% 010ld",
+		"%+010ld",
+		"%-010ld",
+		"%4.2ld",
+		"% 4.2ld",
+		"%+4.2ld",
+		"%-4.2ld",
+		"%04ld",
+		"% 04ld",
+		"%+04ld",
+		"%-04ld",
+		"%5.5ld",
+		"%+22.33ld",
+		"%01.3ld",
+		"%1.5ld",
+		"%-1.5ld",
+		"%44ld",
+		"%4ld",
+		"%4.0ld",
+		"%4.ld",
+		"%.44ld",
+		"%.4ld",
+		"%.0ld",
+		"%.ld",
+		"%ld",
+		NULL
+	};
+	long int long_val[] = {
+#ifdef LONG_MAX
+		LONG_MAX,
+#endif	/* LONG_MAX */
+#ifdef LONG_MIN
+		LONG_MIN,
+#endif	/* LONG_MIN */
+		-91340,
+		91340,
+		341,
+		134,
+		0203,
+		-1,
+		1,
+		0
+	};
+	const char *ulong_fmt[] = {
+		/* "%u" formats. */
+		"foo|%0123lu|bar",
+#if !OS_IRIX
+		"% '0123lu",
+		"%+'0123lu",
+		"%-'123lu",
+		"%'123lu",
+#endif	/* !OS_IRiX */
+		"%123.9lu",
+		"% 123.9lu",
+		"%+123.9lu",
+		"%-123.9lu",
+		"%0123lu",
+		"% 0123lu",
+		"%+0123lu",
+		"%-0123lu",
+		"%5.5lu",
+		"%+22.33lu",
+		"%01.3lu",
+		"%1.5lu",
+		"%-1.5lu",
+		"%44lu",
+		"%lu",
+		/* "%o" formats. */
+		"foo|%#0123lo|bar",
+		"%#123.9lo",
+		"%# 123.9lo",
+		"%#+123.9lo",
+		"%#-123.9lo",
+		"%#0123lo",
+		"%# 0123lo",
+		"%#+0123lo",
+		"%#-0123lo",
+		"%#5.5lo",
+		"%#+22.33lo",
+		"%#01.3lo",
+		"%#1.5lo",
+		"%#-1.5lo",
+		"%#44lo",
+		"%#lo",
+		"%123.9lo",
+		"% 123.9lo",
+		"%+123.9lo",
+		"%-123.9lo",
+		"%0123lo",
+		"% 0123lo",
+		"%+0123lo",
+		"%-0123lo",
+		"%5.5lo",
+		"%+22.33lo",
+		"%01.3lo",
+		"%1.5lo",
+		"%-1.5lo",
+		"%44lo",
+		"%lo",
+		/* "%X" and "%x" formats. */
+		"foo|%#0123lX|bar",
+		"%#123.9lx",
+		"%# 123.9lx",
+		"%#+123.9lx",
+		"%#-123.9lx",
+		"%#0123lx",
+		"%# 0123lx",
+		"%#+0123lx",
+		"%#-0123lx",
+		"%#5.5lx",
+		"%#+22.33lx",
+		"%#01.3lx",
+		"%#1.5lx",
+		"%#-1.5lx",
+		"%#44lx",
+		"%#lx",
+		"%#lX",
+		"%123.9lx",
+		"% 123.9lx",
+		"%+123.9lx",
+		"%-123.9lx",
+		"%0123lx",
+		"% 0123lx",
+		"%+0123lx",
+		"%-0123lx",
+		"%5.5lx",
+		"%+22.33lx",
+		"%01.3lx",
+		"%1.5lx",
+		"%-1.5lx",
+		"%44lx",
+		"%lx",
+		"%lX",
+		NULL
+	};
+	unsigned long int ulong_val[] = {
+#ifdef ULONG_MAX
+		ULONG_MAX,
+#endif	/* ULONG_MAX */
+		91340,
+		341,
+		134,
+		0203,
+		1,
+		0
+	};
+	const char *llong_fmt[] = {
+		"foo|%0123lld|bar",
+		"%123.9lld",
+		"% 123.9lld",
+		"%+123.9lld",
+		"%-123.9lld",
+		"%0123lld",
+		"% 0123lld",
+		"%+0123lld",
+		"%-0123lld",
+		"%5.5lld",
+		"%+22.33lld",
+		"%01.3lld",
+		"%1.5lld",
+		"%-1.5lld",
+		"%44lld",
+		"%lld",
+		NULL
+	};
+	LLONG llong_val[] = {
+#ifdef LLONG_MAX
+		LLONG_MAX,
+#endif	/* LLONG_MAX */
+#ifdef LLONG_MIN
+		LLONG_MIN,
+#endif	/* LLONG_MIN */
+		-91340,
+		91340,
+		341,
+		134,
+		0203,
+		-1,
+		1,
+		0
+	};
+	const char *string_fmt[] = {
+		"foo|%10.10s|bar",
+		"%-10.10s",
+		"%10.10s",
+		"%10.5s",
+		"%5.10s",
+		"%10.1s",
+		"%1.10s",
+		"%10.0s",
+		"%0.10s",
+		"%-42.5s",
+		"%2.s",
+		"%.10s",
+		"%.1s",
+		"%.0s",
+		"%.s",
+		"%4s",
+		"%s",
+		NULL
+	};
+	const char *string_val[] = {
+		"Hello",
+		"Hello, world!",
+		"Sound check: One, two, three.",
+		"This string is a little longer than the other strings.",
+		"1",
+		"",
+		NULL
+	};
+#if !OS_SYSV	/* SysV uses a different format than we do. */
+	const char *pointer_fmt[] = {
+		"foo|%p|bar",
+		"%42p",
+		"%p",
+		NULL
+	};
+	const char *pointer_val[] = {
+		*pointer_fmt,
+		*string_fmt,
+		*string_val,
+		NULL
+	};
+#endif	/* !OS_SYSV */
+	char buf1[1024], buf2[1024];
+	double value, digits = 9.123456789012345678901234567890123456789;
+	int i, j, r1, r2, failed = 0, num = 0;
+
+/*
+ * Use -DTEST_NILS in order to also test the conversion of nil values.  Might
+ * segfault on systems which don't support converting a NULL pointer with "%s"
+ * and lets some test cases fail against BSD and glibc due to bugs in their
+ * implementations.
+ */
+#ifndef TEST_NILS
+#define TEST_NILS 0
+#elif TEST_NILS
+#undef TEST_NILS
+#define TEST_NILS 1
+#endif	/* !defined(TEST_NILS) */
+#ifdef TEST
+#undef TEST
+#endif	/* defined(TEST) */
+#define TEST(fmt, val)                                                         \
+do {                                                                           \
+	for (i = 0; fmt[i] != NULL; i++)                                       \
+		for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) {          \
+			r1 = sprintf(buf1, fmt[i], val[j]);                    \
+			r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]);     \
+			if (strcmp(buf1, buf2) != 0 || r1 != r2) {             \
+				(void)printf("Results don't match, "           \
+				    "format string: %s\n"                      \
+				    "\t sprintf(3): [%s] (%d)\n"               \
+				    "\tsnprintf(3): [%s] (%d)\n",              \
+				    fmt[i], buf1, r1, buf2, r2);               \
+				failed++;                                      \
+			}                                                      \
+			num++;                                                 \
+		}                                                              \
+} while (/* CONSTCOND */ 0)
+
+#if HAVE_LOCALE_H
+	(void)setlocale(LC_ALL, "");
+#endif	/* HAVE_LOCALE_H */
+
+	(void)puts("Testing our snprintf(3) against your system's sprintf(3).");
+	TEST(float_fmt, float_val);
+	TEST(long_fmt, long_val);
+	TEST(ulong_fmt, ulong_val);
+	TEST(llong_fmt, llong_val);
+	TEST(string_fmt, string_val);
+#if !OS_SYSV	/* SysV uses a different format than we do. */
+	TEST(pointer_fmt, pointer_val);
+#endif	/* !OS_SYSV */
+	(void)printf("Result: %d out of %d tests failed.\n", failed, num);
+
+	(void)fputs("Checking how many digits we support: ", stdout);
+	for (i = 0; i < 100; i++) {
+		value = pow(10, i) * digits;
+		(void)sprintf(buf1, "%.1f", value);
+		(void)snprintf(buf2, sizeof(buf2), "%.1f", value);
+		if (strcmp(buf1, buf2) != 0) {
+			(void)printf("apparently %d.\n", i);
+			break;
+		}
+	}
+	return (failed == 0) ? 0 : 1;
+}
+#endif	/* TEST_SNPRINTF */
+
+/* vim: set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */

+ 0 - 1072
app/libc/stdio.c

@@ -1,1072 +0,0 @@
-#include <stdio.h>
-
-#if defined( LUA_NUMBER_INTEGRAL )
-
-#include <stdarg.h>
-
-int sprintf(char *s, const char *fmt, ...)
-{
-    int n;
-    va_list arg;
-    va_start(arg, fmt);
-    n = ets_vsprintf(s, fmt, arg);
-    va_end(arg);
-    return n;
-}
-
-int vsprintf (char *d, const char *s, va_list ap)
-{
-  return ets_vsprintf(d, s, ap);
-}
-
-#else
-
-#define FLOATINGPT 1
-#define NEWFP 1
-#define ENDIAN_LITTLE 1234
-#define ENDIAN_BIG  4321
-#define ENDIAN_PDP  3412
-#define ENDIAN ENDIAN_LITTLE
-
-/* $Id: strichr.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
-
-/*
- * Copyright (c) 2000-2002 Opsycon AB  (www.opsycon.se)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *  This product includes software developed by Opsycon AB.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-#include <string.h>
-
-char *
-strichr(char *p, int c)
-{
-    char *t;
-
-    if (p != NULL) {
-        for(t = p; *t; t++);
-        for (; t >= p; t--) {
-            *(t + 1) = *t;
-        }
-        *p = c;
-    }
-    return (p);
-}
-
-/* $Id: str_fmt.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
-
-/*
- * Copyright (c) 2000-2002 Opsycon AB  (www.opsycon.se)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *  This product includes software developed by Opsycon AB.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-#include <string.h>
-
-#define FMT_RJUST 0
-#define FMT_LJUST 1
-#define FMT_RJUST0 2
-#define FMT_CENTER 3
-
-/*
- *  Format string by inserting blanks.
- */
-
-void
-str_fmt(char *p, int size, int fmt)
-{
-    int n, m, len;
-
-    len = strlen (p);
-    switch (fmt) {
-    case FMT_RJUST:
-        for (n = size - len; n > 0; n--)
-            strichr (p, ' ');
-        break;
-    case FMT_LJUST:
-        for (m = size - len; m > 0; m--)
-            strcat (p, " ");
-        break;
-    case FMT_RJUST0:
-        for (n = size - len; n > 0; n--)
-            strichr (p, '0');
-        break;
-    case FMT_CENTER:
-        m = (size - len) / 2;
-        n = size - (len + m);
-        for (; m > 0; m--)
-            strcat (p, " ");
-        for (; n > 0; n--)
-            strichr (p, ' ');
-        break;
-    }
-}
-
-/* $Id: strtoupp.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
-
-/*
- * Copyright (c) 2000-2002 Opsycon AB  (www.opsycon.se)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *  This product includes software developed by Opsycon AB.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-#include <string.h>
-#include <ctype.h>
-
-void
-strtoupper(char *p)
-{
-    if(!p)
-        return;
-    for (; *p; p++)
-        *p = toupper (*p);
-}
-
-/* $Id: atob.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
-
-/*
- * Copyright (c) 2000-2002 Opsycon AB  (www.opsycon.se)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *  This product includes software developed by Opsycon AB.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-//#include <sys/types.h>
-#include <string.h>
-#include <stdint.h>
-//#include <pmon.h>
-typedef unsigned int u_int;
-typedef unsigned long u_long;
-typedef int32_t register_t;
-typedef long long quad_t;
-typedef unsigned long long u_quad_t;
-typedef double rtype;
-
-#ifndef __P
-#define __P(args) args
-#endif
-
-static char * _getbase __P((char *, int *));
-static int _atob __P((unsigned long long *, char *p, int));
-
-static char *
-_getbase(char *p, int *basep)
-{
-    if (p[0] == '0') {
-        switch (p[1]) {
-        case 'x':
-            *basep = 16;
-            break;
-        case 't': case 'n':
-            *basep = 10;
-            break;
-        case 'o':
-            *basep = 8;
-            break;
-        default:
-            *basep = 10;
-            return (p);
-        }
-        return (p + 2);
-    }
-    *basep = 10;
-    return (p);
-}
-
-
-/*
- *  _atob(vp,p,base)
- */
-static int
-_atob (u_quad_t *vp, char *p, int base)
-{
-    u_quad_t value, v1, v2;
-    char *q, tmp[20];
-    int digit;
-
-    if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
-        base = 16;
-        p += 2;
-    }
-
-    if (base == 16 && (q = strchr (p, '.')) != 0) {
-        if (q - p > sizeof(tmp) - 1)
-            return (0);
-
-        strncpy (tmp, p, q - p);
-        tmp[q - p] = '\0';
-        if (!_atob (&v1, tmp, 16))
-            return (0);
-
-        q++;
-        if (strchr (q, '.'))
-            return (0);
-
-        if (!_atob (&v2, q, 16))
-            return (0);
-        *vp = (v1 << 16) + v2;
-        return (1);
-    }
-
-    value = *vp = 0;
-    for (; *p; p++) {
-        if (*p >= '0' && *p <= '9')
-            digit = *p - '0';
-        else if (*p >= 'a' && *p <= 'f')
-            digit = *p - 'a' + 10;
-        else if (*p >= 'A' && *p <= 'F')
-            digit = *p - 'A' + 10;
-        else
-            return (0);
-
-        if (digit >= base)
-            return (0);
-        value *= base;
-        value += digit;
-    }
-    *vp = value;
-    return (1);
-}
-
-/*
- *  atob(vp,p,base)
- *      converts p to binary result in vp, rtn 1 on success
- */
-int
-atob(uint32_t *vp, char *p, int base)
-{
-    u_quad_t v;
-
-    if (base == 0)
-        p = _getbase (p, &base);
-    if (_atob (&v, p, base)) {
-        *vp = v;
-        return (1);
-    }
-    return (0);
-}
-
-
-/*
- *  llatob(vp,p,base)
- *      converts p to binary result in vp, rtn 1 on success
- */
-int
-llatob(u_quad_t *vp, char *p, int base)
-{
-    if (base == 0)
-        p = _getbase (p, &base);
-    return _atob(vp, p, base);
-}
-
-
-/*
- *  char *btoa(dst,value,base)
- *      converts value to ascii, result in dst
- */
-char *
-btoa(char *dst, u_int value, int base, int plussgn)
-{
-    char buf[34], digit;
-    int i, j, rem, neg;
-
-    if (value == 0) {
-        dst[0] = '0';
-        dst[1] = 0;
-        return (dst);
-    }
-
-    neg = 0;
-    if (base == -10) {
-        base = 10;
-        if (value & (1L << 31)) {
-            value = (~value) + 1;
-            neg = 1;
-        }
-    }
-
-    for (i = 0; value != 0; i++) {
-        rem = value % base;
-        value /= base;
-        if (rem >= 0 && rem <= 9)
-            digit = rem + '0';
-        else if (rem >= 10 && rem <= 36)
-            digit = (rem - 10) + 'a';
-        buf[i] = digit;
-    }
-
-    buf[i] = 0;
-    if (neg)
-        strcat (buf, "-");
-    else if (plussgn)
-        strcat (buf, "+");
-
-    /* reverse the string */
-    for (i = 0, j = strlen (buf) - 1; j >= 0; i++, j--)
-        dst[i] = buf[j];
-    dst[i] = 0;
-    return (dst);
-}
-
-/*
- *  char *btoa(dst,value,base)
- *      converts value to ascii, result in dst
- */
-char *
-llbtoa(char *dst, u_quad_t value, int base, int plussgn)
-{
-    char buf[66], digit;
-    int i, j, rem, neg;
-
-    if (value == 0) {
-        dst[0] = '0';
-        dst[1] = 0;
-        return (dst);
-    }
-
-    neg = 0;
-    if (base == -10) {
-        base = 10;
-        if (value & (1LL << 63)) {
-            value = (~value) + 1;
-            neg = 1;
-        }
-    }
-
-    for (i = 0; value != 0; i++) {
-        rem = value % base;
-        value /= base;
-        if (rem >= 0 && rem <= 9)
-            digit = rem + '0';
-        else if (rem >= 10 && rem <= 36)
-            digit = (rem - 10) + 'a';
-        buf[i] = digit;
-    }
-
-    buf[i] = 0;
-    if (neg)
-        strcat (buf, "-");
-    else if (plussgn)
-        strcat (buf, "+");
-
-    /* reverse the string */
-    for (i = 0, j = strlen (buf) - 1; j >= 0; i++, j--)
-        dst[i] = buf[j];
-    dst[i] = 0;
-    return (dst);
-}
-
-/*
- *  gethex(vp,p,n)
- *      convert n hex digits from p to binary, result in vp,
- *      rtn 1 on success
- */
-int
-gethex(int32_t *vp, char *p, int n)
-{
-    u_long v;
-    int digit;
-
-    for (v = 0; n > 0; n--) {
-        if (*p == 0)
-            return (0);
-        if (*p >= '0' && *p <= '9')
-            digit = *p - '0';
-        else if (*p >= 'a' && *p <= 'f')
-            digit = *p - 'a' + 10;
-        else if (*p >= 'A' && *p <= 'F')
-            digit = *p - 'A' + 10;
-        else
-            return (0);
-
-        v <<= 4;
-        v |= digit;
-        p++;
-    }
-    *vp = v;
-    return (1);
-}
-
-/* $Id: vsprintf.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
-
-/*
- * Copyright (c) 2000-2002 Opsycon AB  (www.opsycon.se)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *  This product includes software developed by Opsycon AB.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-//#include <stdio.h>
-//#include <pmon.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-
-/*
- *  int vsprintf(d,s,ap)
- */
-int
-vsprintf (char *d, const char *s, va_list ap)
-{
-    const char *t;
-    char *p, *dst, tmp[40];
-    unsigned int n;
-    int fmt, trunc, haddot, width, base, longlong, plussgn;
-#ifdef FLOATINGPT
-    double dbl;
-
-#ifndef NEWFP
-    EP ex;
-#endif
-#endif
-
-    dst = d;
-    for (; *s;) {
-        if (*s == '%') {
-            s++;
-            fmt = FMT_RJUST;
-            width = trunc = haddot = longlong = plussgn = 0;
-            for (; *s; s++) {
-                if (strchr("bcdefgilopPrRsuxX%", *s))
-                    break;
-                else if (*s == '-')
-                    fmt = FMT_LJUST;
-                else if (*s == '+')
-                    plussgn = 1;
-                else if (*s == '0')
-                    fmt = FMT_RJUST0;
-                else if (*s == '~')
-                    fmt = FMT_CENTER;
-                else if (*s == '*') {
-                    if (haddot)
-                        trunc = va_arg(ap, int);
-                    else
-                        width = va_arg(ap, int);
-                } else if (*s >= '1' && *s <= '9') {
-                    for (t = s; isdigit(*s); s++);
-                    strncpy(tmp, t, s - t);
-                    tmp[s - t] = '\0';
-                    atob(&n, tmp, 10);
-                    if (haddot)
-                        trunc = n;
-                    else
-                        width = n;
-                    s--;
-                } else if (*s == '.')
-                    haddot = 1;
-            }
-            if (*s == '%') {
-                *d++ = '%';
-                *d = 0;
-            } else if (*s == 's') {
-                p = va_arg(ap, char *);
-
-                if (p)
-                    strcpy(d, p);
-                else
-                    strcpy(d, "(null)");
-            } else if (*s == 'c') {
-                n = va_arg (ap, int);
-                *d = n;
-                d[1] = 0;
-            } else {
-                if (*s == 'l') {
-                    if (*++s == 'l') {
-                        longlong = 1;
-                        ++s;
-                    }
-                }
-                if (strchr("bdiopPrRxXu", *s)) {
-                    if (*s == 'd' || *s == 'i')
-                        base = -10;
-                    else if (*s == 'u')
-                        base = 10;
-                    else if (*s == 'x' || *s == 'X')
-                        base = 16;
-                    else if(*s == 'p' || *s == 'P') {
-                        base = 16;
-                        if (*s == 'p') {
-                            *d++ = '0';
-                            *d++ = 'x';
-                        }
-                        fmt = FMT_RJUST0;
-                        if (sizeof(long) > 4) {
-                            width = 16;
-                            longlong = 1;
-                        } else {
-                            width = 8;
-                        }
-                    }
-                    else if(*s == 'r' || *s == 'R') {
-                        base = 16;
-                        if (*s == 'r') {
-                            *d++ = '0';
-                            *d++ = 'x';
-                        }
-                        fmt = FMT_RJUST0;
-                        if (sizeof(register_t) > 4) {
-                            width = 16;
-                            longlong = 1;
-                        } else {
-                            width = 8;
-                        }
-                    }
-                    else if (*s == 'o')
-                        base = 8;
-                    else if (*s == 'b')
-                        base = 2;
-                    if (longlong)
-                        llbtoa(d, va_arg (ap, quad_t),
-                            base, plussgn);
-                    else
-                        btoa(d, va_arg (ap, int), base, plussgn);
-
-                    if (*s == 'X')
-                        strtoupper(d);
-                }
-#ifdef FLOATINGPT
-                else if (strchr ("eEfgG", *s)) {
-//static void dtoa (char *, double, int, int, int, int);
-void dtoa (char *dbuf, rtype arg, int fmtch, int width, int prec, int plussgn);
-                    dbl = va_arg(ap, double);
-                    if (!haddot) {
-                      trunc = 6;
-                    }
-                    dtoa(d, dbl, *s, width, trunc, plussgn);
-                    trunc = 0;
-                }
-#endif
-            }
-            if (trunc)
-                d[trunc] = 0;
-            if (width)
-                str_fmt (d, width, fmt);
-            for (; *d; d++);
-            s++;
-        } else
-            *d++ = *s++;
-    }
-    *d = 0;
-    return (d - dst);
-}
-
-#ifdef FLOATINGPT
-/*
- * Floating point output, cvt() onward lifted from BSD sources:
- *
- * Copyright (c) 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *  This product includes software developed by the University of
- *  California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-
-#define MAX_FCONVERSION 512 /* largest possible real conversion     */
-#define MAX_EXPT    5   /* largest possible exponent field */
-#define MAX_FRACT   39  /* largest possible fraction field */
-
-#define TESTFLAG(x) 0
-
-
-typedef double rtype;
-
-extern double modf(double, double *);
-#define to_char(n)  ((n) + '0')
-#define to_digit(c) ((c) - '0')
-#define _isNan(arg) ((arg) != (arg))
-
-static int cvt (rtype arg, int prec, char *signp, int fmtch,
-        char *startp, char *endp, int plussgn);
-static char *c_round (double fract, int *exp, char *start, char *end,
-            char ch, char *signp);
-static char *exponent(char *p, int exp, int fmtch);
-
-
-/*
- * _finite arg not Infinity or Nan
- */
-static int _finite(rtype d)
-{
-#if ENDIAN == ENDIAN_LITTLE
-    struct IEEEdp {
-    unsigned manl:32;
-    unsigned manh:20;
-    unsigned exp:11;
-    unsigned sign:1;
-    } *ip;
-#else
-    struct IEEEdp {
-    unsigned sign:1;
-    unsigned exp:11;
-    unsigned manh:20;
-    unsigned manl:32;
-    } *ip;
-#endif
-
-    ip = (struct IEEEdp *)&d;
-    return (ip->exp != 0x7ff);
-}
-
-
-void dtoa (char *dbuf, rtype arg, int fmtch, int width, int prec, int plussgn)
-{
-    char    buf[MAX_FCONVERSION+1], *cp;
-    char    sign;
-    int size;
-
-    if( !_finite(arg) ) {
-        if( _isNan(arg) )
-            strcpy (dbuf, "NaN");
-        else if( arg < 0)
-            strcpy (dbuf, "-Infinity");
-        else
-            strcpy (dbuf, "Infinity");
-        return;
-    }
-
-    if (prec > MAX_FRACT)
-        prec = MAX_FRACT;
-
-    /* leave room for sign at start of buffer */
-    cp = buf + 1;
-
-    /*
-     * cvt may have to round up before the "start" of
-     * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
-     * if the first character is still NUL, it did.
-     * softsign avoids negative 0 if _double < 0 but
-     * no significant digits will be shown.
-     */
-    *cp = '\0';
-    size = cvt (arg, prec, &sign, fmtch, cp, buf + sizeof(buf), plussgn);
-    if (*cp == '\0')
-        cp++;
-
-    if (sign)
-        *--cp = sign, size++;
-
-    cp[size] = 0;
-    memcpy (dbuf, cp, size + 1);
-}
-
-
-static int
-cvt(rtype number, int prec, char *signp, int fmtch, char *startp, char *endp, int plussgn)
-{
-    register char *p, *t;
-    register double fract;
-    double integer, tmp;
-    int dotrim, expcnt, gformat;
-
-    dotrim = expcnt = gformat = 0;
-    if (number < 0) {
-        number = -number;
-        *signp = '-';
-    } else if (plussgn) {
-        *signp = '+';
-    } else
-        *signp = 0;
-
-    fract = modf(number, &integer);
-
-    /* get an extra slot for rounding. */
-    t = ++startp;
-
-    /*
-     * get integer portion of number; put into the end of the buffer; the
-     * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
-     */
-    for (p = endp - 1; integer; ++expcnt) {
-        tmp = modf(integer / 10, &integer);
-        *p-- = to_char((int)((tmp + .01) * 10));
-    }
-    switch (fmtch) {
-    case 'f':
-        /* reverse integer into beginning of buffer */
-        if (expcnt)
-            for (; ++p < endp; *t++ = *p);
-        else
-            *t++ = '0';
-        /*
-         * if precision required or alternate flag set, add in a
-         * decimal point.
-         */
-        if (prec || TESTFLAG(ALTERNATE_FORM))
-            *t++ = '.';
-        /* if requires more precision and some fraction left */
-        if (fract) {
-            if (prec)
-                do {
-                    fract = modf(fract * 10, &tmp);
-                    *t++ = to_char((int)tmp);
-                } while (--prec && fract);
-            if (fract)
-                startp = c_round(fract, (int *)NULL, startp,
-                    t - 1, (char)0, signp);
-        }
-        for (; prec--; *t++ = '0');
-        break;
-    case 'e':
-    case 'E':
-eformat:    if (expcnt) {
-            *t++ = *++p;
-            if (prec || TESTFLAG(ALTERNATE_FORM))
-                *t++ = '.';
-            /* if requires more precision and some integer left */
-            for (; prec && ++p < endp; --prec)
-                *t++ = *p;
-            /*
-             * if done precision and more of the integer component,
-             * round using it; adjust fract so we don't re-round
-             * later.
-             */
-            if (!prec && ++p < endp) {
-                fract = 0;
-                startp = c_round((double)0, &expcnt, startp,
-                    t - 1, *p, signp);
-            }
-            /* adjust expcnt for digit in front of decimal */
-            --expcnt;
-        }
-        /* until first fractional digit, decrement exponent */
-        else if (fract) {
-            /* adjust expcnt for digit in front of decimal */
-            for (expcnt = -1;; --expcnt) {
-                fract = modf(fract * 10, &tmp);
-                if (tmp)
-                    break;
-            }
-            *t++ = to_char((int)tmp);
-            if (prec || TESTFLAG(ALTERNATE_FORM))
-                *t++ = '.';
-        }
-        else {
-            *t++ = '0';
-            if (prec || TESTFLAG(ALTERNATE_FORM))
-                *t++ = '.';
-        }
-        /* if requires more precision and some fraction left */
-        if (fract) {
-            if (prec)
-                do {
-                    fract = modf(fract * 10, &tmp);
-                    *t++ = to_char((int)tmp);
-                } while (--prec && fract);
-            if (fract)
-                startp = c_round(fract, &expcnt, startp,
-                    t - 1, (char)0, signp);
-        }
-        /* if requires more precision */
-        for (; prec--; *t++ = '0');
-
-        /* unless alternate flag, trim any g/G format trailing 0's */
-        if (gformat && !TESTFLAG(ALTERNATE_FORM)) {
-            while (t > startp && *--t == '0');
-            if (*t == '.')
-                --t;
-            ++t;
-        }
-        t = exponent(t, expcnt, fmtch);
-        break;
-    case 'g':
-    case 'G':
-        /* a precision of 0 is treated as a precision of 1. */
-        if (!prec)
-            ++prec;
-        /*
-         * ``The style used depends on the value converted; style e
-         * will be used only if the exponent resulting from the
-         * conversion is less than -4 or greater than the precision.''
-         *  -- ANSI X3J11
-         */
-        if (expcnt > prec || (!expcnt && fract && fract < .0001)) {
-            /*
-             * g/G format counts "significant digits, not digits of
-             * precision; for the e/E format, this just causes an
-             * off-by-one problem, i.e. g/G considers the digit
-             * before the decimal point significant and e/E doesn't
-             * count it as precision.
-             */
-            --prec;
-            fmtch -= 2;     /* G->E, g->e */
-            gformat = 1;
-            goto eformat;
-        }
-        /*
-         * reverse integer into beginning of buffer,
-         * note, decrement precision
-         */
-        if (expcnt)
-            for (; ++p < endp; *t++ = *p, --prec);
-        else
-            *t++ = '0';
-        /*
-         * if precision required or alternate flag set, add in a
-         * decimal point.  If no digits yet, add in leading 0.
-         */
-        if (prec || TESTFLAG(ALTERNATE_FORM)) {
-            dotrim = 1;
-            *t++ = '.';
-        }
-        else
-            dotrim = 0;
-        /* if requires more precision and some fraction left */
-        if (fract) {
-            if (prec) {
-                    do {
-                    fract = modf(fract * 10, &tmp);
-                    *t++ = to_char((int)tmp);
-                } while(!tmp && !expcnt);
-                while (--prec && fract) {
-                    fract = modf(fract * 10, &tmp);
-                    *t++ = to_char((int)tmp);
-                }
-            }
-            if (fract)
-                startp = c_round(fract, (int *)NULL, startp,
-                    t - 1, (char)0, signp);
-        }
-        /* alternate format, adds 0's for precision, else trim 0's */
-        if (TESTFLAG(ALTERNATE_FORM))
-            for (; prec--; *t++ = '0');
-        else if (dotrim) {
-            while (t > startp && *--t == '0');
-            if (*t != '.')
-                ++t;
-        }
-    }
-    return (t - startp);
-}
-
-
-static char *
-c_round(double fract, int *exp, char *start, char *end, char ch, char *signp)
-{
-    double tmp;
-
-    if (fract)
-        (void)modf(fract * 10, &tmp);
-    else
-        tmp = to_digit(ch);
-    if (tmp > 4)
-        for (;; --end) {
-            if (*end == '.')
-                --end;
-            if (++*end <= '9')
-                break;
-            *end = '0';
-            if (end == start) {
-                if (exp) {  /* e/E; increment exponent */
-                    *end = '1';
-                    ++*exp;
-                }
-                else {      /* f; add extra digit */
-                    *--end = '1';
-                    --start;
-                }
-                break;
-            }
-        }
-    /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
-    else if (*signp == '-' || *signp == '+')
-        for (;; --end) {
-            if (*end == '.')
-                --end;
-            if (*end != '0')
-                break;
-            if (end == start)
-                *signp = 0;
-        }
-    return (start);
-}
-
-static char *
-exponent(char *p, int exp, int fmtch)
-{
-    char *t;
-    char expbuf[MAX_FCONVERSION];
-
-    *p++ = fmtch;
-    if (exp < 0) {
-        exp = -exp;
-        *p++ = '-';
-    }
-    else
-        *p++ = '+';
-    t = expbuf + MAX_FCONVERSION;
-    if (exp > 9) {
-        do {
-            *--t = to_char(exp % 10);
-        } while ((exp /= 10) > 9);
-        *--t = to_char(exp);
-        for (; t < expbuf + MAX_FCONVERSION; *p++ = *t++);
-    }
-    else {
-        *p++ = '0';
-        *p++ = to_char(exp);
-    }
-    return (p);
-}
-
-
-int sprintf(char *s, const char *fmt, ...)
-{
-    int n;
-    va_list arg;
-    va_start(arg, fmt);
-    n = vsprintf(s, fmt, arg);
-    va_end(arg);
-    return n;
-}
-
-#endif /* FLOATINGPT */
-
-#endif

+ 2 - 1
sdk-overrides/include/stdio.h

@@ -14,8 +14,9 @@ extern void output_redirect(const char *str, size_t l);
 
 #define printf(...) do { \
   char __printf_buf[BUFSIZ]; \
-  sprintf(__printf_buf, __VA_ARGS__); \
+  snprintf(__printf_buf, BUFSIZ, __VA_ARGS__); \
   puts(__printf_buf); \
 } while(0)
 
+
 #endif