Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
b80cfe9f
"vscode:/vscode.git/clone" did not exist on "b80cfe9f1ee5f103cd6e8e1f50bda2ae286b2f1d"
Commit
b80cfe9f
authored
Sep 25, 2020
by
philip
Committed by
Marcel Stör
Nov 07, 2020
Browse files
Now use the original c99-snprintf as a submodule.
parent
78ee67fd
Changes
3
Show whitespace changes
Inline
Side-by-side
app/libc/Makefile
View file @
b80cfe9f
...
@@ -22,7 +22,7 @@ endif
...
@@ -22,7 +22,7 @@ endif
# makefile at its root level - these are then overridden
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
# for a subtree within the makefile rooted therein
#
#
DEFINES
+=
-DHAVE_CONFIG_H
#
DEFINES +=
#############################################################
#############################################################
# Recursion Magic - Don't touch this!!
# Recursion Magic - Don't touch this!!
...
...
c99-snprintf
@
27b59ac1
Subproject commit 27b59ac13b47414000e83f05a246008425c22bb7
app/libc/snprintf.c
View file @
b80cfe9f
/*
/*
* Copyright (c) 1995 Patrick Powell.
* Wrapper around https://github.com/weiss/c99-snprintf
*
* 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.
*/
*/
/*
#define HAVE_CONFIG_H 0
* Copyright (c) 2008 Holger Weiss.
#define TEST_SNPRINTF 0
*
#define HAVE_SNPRINTF 0
* This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
#define HAVE_VSNPRINTF 0
* My changes to the code may freely be used, modified and/or redistributed for
#define HAVE_ASPRINTF 0
* any purpose. It would be nice if additions and fixes to this file (including
#define HAVE_VASPRINTF 0
* 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
#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
#define HAVE_STDLIB_H 1
#endif
/* HAVE_STDLIB_H */
#ifndef HAVE_FLOAT_H
#define HAVE_VA_COPY 0
#define HAVE___VA_COPY 0
#define HAVE_FLOAT_H 1
#define HAVE_FLOAT_H 1
#endif
/* HAVE_FLOAT_H */
#ifndef HAVE_INTTYPES_H
#define HAVE_INTTYPES_H 1
#define HAVE_INTTYPES_H 1
#endif
/* HAVE_INTTYPES_H */
#define HAVE_LOCALE_H 0
#ifndef HAVE_LOCALE_H
#define HAVE_STDDEF_H 1
#define HAVE_LOCALE_H 1
#define HAVE_STDINT_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
#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
#define HAVE_UINTMAX_T 1
#endif
/* !defined(HAVE_UINTMAX_T) */
#define HAVE_LONG_DOUBLE 0
#ifndef HAVE_UINTPTR_T
#define HAVE_LONG_LONG_INT 1
#define HAVE_INTMAX_T 1
#define HAVE_UINTPTR_T 1
#define HAVE_UINTPTR_T 1
#endif
/* !defined(HAVE_UINTPTR_T) */
#ifndef HAVE_PTRDIFF_T
#define HAVE_PTRDIFF_T 1
#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
#define HAVE_LOCALECONV 0
#if HAVE_STDLIB_H
#define HAVE_LCONV_DECIMAL_POINT 0
#include <stdlib.h>
/* For malloc(3). */
#define HAVE_LCONV_THOUSANDS_SEP 0
#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_MIN_10_EXP DBL_MIN_10_EXP
#define LDOUBLE_MAX_10_EXP DBL_MAX_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 rpl_snprintf snprintf
#define MAX(x, y) ((x >= y) ? x : y)
#define rpl_vsnprintf vsnprintf
#endif
/* !defined(MAX) */
#define rpl_vasprintf vasprintf
#ifndef CHARTOINT
#define rpl_asprintf asprintf
#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
#include "c99-snprintf/snprintf.c"
#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
sprintf
(
char
*
s
,
const
char
*
fmt
,
...)
{
{
...
@@ -1568,543 +55,4 @@ int vsprintf (char *d, const char *s, va_list ap)
...
@@ -1568,543 +55,4 @@ int vsprintf (char *d, const char *s, va_list ap)
return
vsnprintf
(
d
,
1024
,
s
,
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
.
00
999999
,
0
.
000
99999
,
0
.
0000
9999
,
0
.
00000
999
,
0
.
000000
99
,
0
.
0000000
9
,
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: */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment