|
@@ -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: */
|