xref: /netbsd-src/external/bsd/ntp/dist/libntp/snprintf.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: snprintf.c,v 1.7 2024/08/18 20:47:13 christos Exp $	*/
2abb0f93cSkardel 
38585484eSchristos /*
48585484eSchristos  * Modified by Dave Hart for integration into NTP 4.2.7 <hart@ntp.org>
58585484eSchristos  *
68585484eSchristos  * Changed in a backwards-incompatible way to separate HAVE_SNPRINTF
78585484eSchristos  * from HW_WANT_RPL_SNPRINTF, etc. for each of the four replaced
88585484eSchristos  * functions.
98585484eSchristos  *
108585484eSchristos  * Changed to honor hw_force_rpl_snprintf=yes, etc.  This is used by NTP
118585484eSchristos  * to test rpl_snprintf() and rpl_vsnprintf() on platforms which provide
128585484eSchristos  * C99-compliant implementations.
138585484eSchristos  */
148585484eSchristos 
15cdfa2a7eSchristos /* Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp  */
168585484eSchristos 
178585484eSchristos /*
188585484eSchristos  * Copyright (c) 1995 Patrick Powell.
198585484eSchristos  *
208585484eSchristos  * This code is based on code written by Patrick Powell <papowell@astart.com>.
218585484eSchristos  * It may be used for any purpose as long as this notice remains intact on all
228585484eSchristos  * source code distributions.
238585484eSchristos  */
248585484eSchristos 
258585484eSchristos /*
268585484eSchristos  * Copyright (c) 2008 Holger Weiss.
278585484eSchristos  *
288585484eSchristos  * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
298585484eSchristos  * My changes to the code may freely be used, modified and/or redistributed for
308585484eSchristos  * any purpose.  It would be nice if additions and fixes to this file (including
318585484eSchristos  * trivial code cleanups) would be sent back in order to let me include them in
328585484eSchristos  * the version available at <http://www.jhweiss.de/software/snprintf.html>.
338585484eSchristos  * However, this is not a requirement for using or redistributing (possibly
348585484eSchristos  * modified) versions of this file, nor is leaving this notice intact mandatory.
358585484eSchristos  */
368585484eSchristos 
378585484eSchristos /*
388585484eSchristos  * History
398585484eSchristos  *
408585484eSchristos  * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
418585484eSchristos  *
428585484eSchristos  * 	Fixed the detection of infinite floating point values on IRIX (and
438585484eSchristos  * 	possibly other systems) and applied another few minor cleanups.
448585484eSchristos  *
458585484eSchristos  * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
468585484eSchristos  *
478585484eSchristos  * 	Added a lot of new features, fixed many bugs, and incorporated various
488585484eSchristos  * 	improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
498585484eSchristos  * 	<rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
508585484eSchristos  * 	<djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
518585484eSchristos  * 	projects.  The additions include: support the "e", "E", "g", "G", and
528585484eSchristos  * 	"F" conversion specifiers (and use conversion style "f" or "F" for the
538585484eSchristos  * 	still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
548585484eSchristos  * 	"t", and "z" length modifiers; support the "#" flag and the (non-C99)
558585484eSchristos  * 	"'" flag; use localeconv(3) (if available) to get both the current
568585484eSchristos  * 	locale's decimal point character and the separator between groups of
578585484eSchristos  * 	digits; fix the handling of various corner cases of field width and
588585484eSchristos  * 	precision specifications; fix various floating point conversion bugs;
598585484eSchristos  * 	handle infinite and NaN floating point values; don't attempt to write to
608585484eSchristos  * 	the output buffer (which may be NULL) if a size of zero was specified;
618585484eSchristos  * 	check for integer overflow of the field width, precision, and return
628585484eSchristos  * 	values and during the floating point conversion; use the OUTCHAR() macro
638585484eSchristos  * 	instead of a function for better performance; provide asprintf(3) and
648585484eSchristos  * 	vasprintf(3) functions; add new test cases.  The replacement functions
658585484eSchristos  * 	have been renamed to use an "rpl_" prefix, the function calls in the
668585484eSchristos  * 	main project (and in this file) must be redefined accordingly for each
678585484eSchristos  * 	replacement function which is needed (by using Autoconf or other means).
688585484eSchristos  * 	Various other minor improvements have been applied and the coding style
698585484eSchristos  * 	was cleaned up for consistency.
708585484eSchristos  *
718585484eSchristos  * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
728585484eSchristos  *
738585484eSchristos  * 	C99 compliant snprintf(3) and vsnprintf(3) functions return the number
748585484eSchristos  * 	of characters that would have been written to a sufficiently sized
758585484eSchristos  * 	buffer (excluding the '\0').  The original code simply returned the
768585484eSchristos  * 	length of the resulting output string, so that's been fixed.
778585484eSchristos  *
788585484eSchristos  * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
798585484eSchristos  *
808585484eSchristos  * 	The original code assumed that both snprintf(3) and vsnprintf(3) were
818585484eSchristos  * 	missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
828585484eSchristos  * 	the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
838585484eSchristos  *
848585484eSchristos  * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
858585484eSchristos  *
868585484eSchristos  * 	The PGP code was using unsigned hexadecimal formats.  Unfortunately,
878585484eSchristos  * 	unsigned formats simply didn't work.
888585484eSchristos  *
898585484eSchristos  * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
908585484eSchristos  *
918585484eSchristos  * 	Ok, added some minimal floating point support, which means this probably
928585484eSchristos  * 	requires libm on most operating systems.  Don't yet support the exponent
938585484eSchristos  * 	(e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
948585484eSchristos  * 	wasn't being exercised in ways which showed it, so that's been fixed.
958585484eSchristos  * 	Also, formatted the code to Mutt conventions, and removed dead code left
968585484eSchristos  * 	over from the original.  Also, there is now a builtin-test, run with:
978585484eSchristos  * 	gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
988585484eSchristos  *
998585484eSchristos  * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
1008585484eSchristos  *
1018585484eSchristos  * 	This was ugly.  It is still ugly.  I opted out of floating point
1028585484eSchristos  * 	numbers, but the formatter understands just about everything from the
1038585484eSchristos  * 	normal C string format, at least as far as I can tell from the Solaris
1048585484eSchristos  * 	2.5 printf(3S) man page.
1058585484eSchristos  */
1068585484eSchristos 
1078585484eSchristos /*
1088585484eSchristos  * ToDo
1098585484eSchristos  *
1108585484eSchristos  * - Add wide character support.
1118585484eSchristos  * - Add support for "%a" and "%A" conversions.
1128585484eSchristos  * - Create test routines which predefine the expected results.  Our test cases
1138585484eSchristos  *   usually expose bugs in system implementations rather than in ours :-)
1148585484eSchristos  */
1158585484eSchristos 
1168585484eSchristos /*
1178585484eSchristos  * Usage
1188585484eSchristos  *
1198585484eSchristos  * 1) The following preprocessor macros should be defined to 1 if the feature or
1208585484eSchristos  *    file in question is available on the target system (by using Autoconf or
1218585484eSchristos  *    other means), though basic functionality should be available as long as
1228585484eSchristos  *    HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
1238585484eSchristos  *
1248585484eSchristos  *	HW_WANT_RPL_VSNPRINTF
1258585484eSchristos  *	HW_WANT_RPL_SNPRINTF
1268585484eSchristos  *	HW_WANT_RPL_VASPRINTF
1278585484eSchristos  *	HW_WANT_RPL_ASPRINTF
1288585484eSchristos  *	HAVE_VSNPRINTF	// define to 1 #if HW_WANT_RPL_VSNPRINTF
1298585484eSchristos  *	HAVE_SNPRINTF	// define to 1 #if HW_WANT_RPL_SNPRINTF
1308585484eSchristos  *	HAVE_VASPRINTF	// define to 1 #if HW_WANT_RPL_VASPRINTF
1318585484eSchristos  *	HAVE_ASPRINTF	// define to 1 #if HW_WANT_RPL_ASPRINTF
1328585484eSchristos  *	HAVE_STDARG_H
1338585484eSchristos  *	HAVE_STDDEF_H
1348585484eSchristos  *	HAVE_STDINT_H
1358585484eSchristos  *	HAVE_STDLIB_H
1368585484eSchristos  *	HAVE_INTTYPES_H
1378585484eSchristos  *	HAVE_LOCALE_H
1388585484eSchristos  *	HAVE_LOCALECONV
1398585484eSchristos  *	HAVE_LCONV_DECIMAL_POINT
1408585484eSchristos  *	HAVE_LCONV_THOUSANDS_SEP
1418585484eSchristos  *	HAVE_LONG_DOUBLE
1428585484eSchristos  *	HAVE_LONG_LONG_INT
1438585484eSchristos  *	HAVE_UNSIGNED_LONG_LONG_INT
1448585484eSchristos  *	HAVE_INTMAX_T
1458585484eSchristos  *	HAVE_UINTMAX_T
1468585484eSchristos  *	HAVE_UINTPTR_T
1478585484eSchristos  *	HAVE_PTRDIFF_T
1488585484eSchristos  *	HAVE_VA_COPY
1498585484eSchristos  *	HAVE___VA_COPY
1508585484eSchristos  *
1518585484eSchristos  * 2) The calls to the functions which should be replaced must be redefined
1528585484eSchristos  *    throughout the project files (by using Autoconf or other means):
1538585484eSchristos  *
1548585484eSchristos  *	#if HW_WANT_RPL_VSNPRINTF
1558585484eSchristos  *	#define vsnprintf rpl_vsnprintf
1568585484eSchristos  *	#endif
1578585484eSchristos  *	#if HW_WANT_RPL_SNPRINTF
1588585484eSchristos  *	#define snprintf rpl_snprintf
1598585484eSchristos  *	#endif
1608585484eSchristos  *	#if HW_WANT_RPL_VASPRINTF
1618585484eSchristos  *	#define vasprintf rpl_vasprintf
1628585484eSchristos  *	#endif
1638585484eSchristos  *	#if HW_WANT_RPL_ASPRINTF
1648585484eSchristos  *	#define asprintf rpl_asprintf
1658585484eSchristos  *	#endif
1668585484eSchristos  *
1678585484eSchristos  * 3) The required replacement functions should be declared in some header file
1688585484eSchristos  *    included throughout the project files:
1698585484eSchristos  *
1708585484eSchristos  *	#if HAVE_CONFIG_H
1718585484eSchristos  *	#include <config.h>
1728585484eSchristos  *	#endif
1738585484eSchristos  *	#if HAVE_STDARG_H
1748585484eSchristos  *	#include <stdarg.h>
1758585484eSchristos  *	#if HW_WANT_RPL_VSNPRINTF
1768585484eSchristos  *	int rpl_vsnprintf(char *, size_t, const char *, va_list);
1778585484eSchristos  *	#endif
1788585484eSchristos  *	#if HW_WANT_RPL_SNPRINTF
1798585484eSchristos  *	int rpl_snprintf(char *, size_t, const char *, ...);
1808585484eSchristos  *	#endif
1818585484eSchristos  *	#if HW_WANT_RPL_VASPRINTF
1828585484eSchristos  *	int rpl_vasprintf(char **, const char *, va_list);
1838585484eSchristos  *	#endif
1848585484eSchristos  *	#if HW_WANT_RPL_ASPRINTF
1858585484eSchristos  *	int rpl_asprintf(char **, const char *, ...);
1868585484eSchristos  *	#endif
1878585484eSchristos  *	#endif
1888585484eSchristos  *
1898585484eSchristos  * Autoconf macros for handling step 1 and step 2 are available at
1908585484eSchristos  * <http://www.jhweiss.de/software/snprintf.html>.
1918585484eSchristos  */
1928585484eSchristos 
1938585484eSchristos #if HAVE_CONFIG_H
194abb0f93cSkardel #include <config.h>
1958585484eSchristos #endif	/* HAVE_CONFIG_H */
196abb0f93cSkardel 
1978585484eSchristos #if TEST_SNPRINTF
1988585484eSchristos #include <math.h>	/* For pow(3), NAN, and INFINITY. */
1998585484eSchristos #include <string.h>	/* For strcmp(3). */
2008585484eSchristos #if defined(__NetBSD__) || \
2018585484eSchristos     defined(__FreeBSD__) || \
2028585484eSchristos     defined(__OpenBSD__) || \
2038585484eSchristos     defined(__NeXT__) || \
2048585484eSchristos     defined(__bsd__)
2058585484eSchristos #define OS_BSD 1
2068585484eSchristos #elif defined(sgi) || defined(__sgi)
2078585484eSchristos #ifndef __c99
2088585484eSchristos #define __c99	/* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
2098585484eSchristos #endif	/* !defined(__c99) */
2108585484eSchristos #define OS_IRIX 1
2118585484eSchristos #define OS_SYSV 1
2128585484eSchristos #elif defined(__svr4__)
2138585484eSchristos #define OS_SYSV 1
2148585484eSchristos #elif defined(__linux__)
2158585484eSchristos #define OS_LINUX 1
2168585484eSchristos #endif	/* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
2178585484eSchristos #if HAVE_CONFIG_H	/* Undefine definitions possibly done in config.h. */
2188585484eSchristos #ifdef HAVE_SNPRINTF
2198585484eSchristos #undef HAVE_SNPRINTF
2208585484eSchristos #endif	/* defined(HAVE_SNPRINTF) */
2218585484eSchristos #ifdef HAVE_VSNPRINTF
2228585484eSchristos #undef HAVE_VSNPRINTF
2238585484eSchristos #endif	/* defined(HAVE_VSNPRINTF) */
2248585484eSchristos #ifdef HAVE_ASPRINTF
2258585484eSchristos #undef HAVE_ASPRINTF
2268585484eSchristos #endif	/* defined(HAVE_ASPRINTF) */
2278585484eSchristos #ifdef HAVE_VASPRINTF
2288585484eSchristos #undef HAVE_VASPRINTF
2298585484eSchristos #endif	/* defined(HAVE_VASPRINTF) */
2308585484eSchristos #ifdef snprintf
2318585484eSchristos #undef snprintf
2328585484eSchristos #endif	/* defined(snprintf) */
2338585484eSchristos #ifdef vsnprintf
2348585484eSchristos #undef vsnprintf
2358585484eSchristos #endif	/* defined(vsnprintf) */
2368585484eSchristos #ifdef asprintf
2378585484eSchristos #undef asprintf
2388585484eSchristos #endif	/* defined(asprintf) */
2398585484eSchristos #ifdef vasprintf
2408585484eSchristos #undef vasprintf
2418585484eSchristos #endif	/* defined(vasprintf) */
2428585484eSchristos #else	/* By default, we assume a modern system for testing. */
2438585484eSchristos #ifndef HAVE_STDARG_H
2448585484eSchristos #define HAVE_STDARG_H 1
2458585484eSchristos #endif	/* HAVE_STDARG_H */
2468585484eSchristos #ifndef HAVE_STDDEF_H
2478585484eSchristos #define HAVE_STDDEF_H 1
2488585484eSchristos #endif	/* HAVE_STDDEF_H */
2498585484eSchristos #ifndef HAVE_STDINT_H
2508585484eSchristos #define HAVE_STDINT_H 1
2518585484eSchristos #endif	/* HAVE_STDINT_H */
2528585484eSchristos #ifndef HAVE_STDLIB_H
2538585484eSchristos #define HAVE_STDLIB_H 1
2548585484eSchristos #endif	/* HAVE_STDLIB_H */
2558585484eSchristos #ifndef HAVE_INTTYPES_H
2568585484eSchristos #define HAVE_INTTYPES_H 1
2578585484eSchristos #endif	/* HAVE_INTTYPES_H */
2588585484eSchristos #ifndef HAVE_LOCALE_H
2598585484eSchristos #define HAVE_LOCALE_H 1
2608585484eSchristos #endif	/* HAVE_LOCALE_H */
2618585484eSchristos #ifndef HAVE_LOCALECONV
2628585484eSchristos #define HAVE_LOCALECONV 1
2638585484eSchristos #endif	/* !defined(HAVE_LOCALECONV) */
2648585484eSchristos #ifndef HAVE_LCONV_DECIMAL_POINT
2658585484eSchristos #define HAVE_LCONV_DECIMAL_POINT 1
2668585484eSchristos #endif	/* HAVE_LCONV_DECIMAL_POINT */
2678585484eSchristos #ifndef HAVE_LCONV_THOUSANDS_SEP
2688585484eSchristos #define HAVE_LCONV_THOUSANDS_SEP 1
2698585484eSchristos #endif	/* HAVE_LCONV_THOUSANDS_SEP */
2708585484eSchristos #ifndef HAVE_LONG_DOUBLE
2718585484eSchristos #define HAVE_LONG_DOUBLE 1
2728585484eSchristos #endif	/* !defined(HAVE_LONG_DOUBLE) */
2738585484eSchristos #ifndef HAVE_LONG_LONG_INT
2748585484eSchristos #define HAVE_LONG_LONG_INT 1
2758585484eSchristos #endif	/* !defined(HAVE_LONG_LONG_INT) */
2768585484eSchristos #ifndef HAVE_UNSIGNED_LONG_LONG_INT
2778585484eSchristos #define HAVE_UNSIGNED_LONG_LONG_INT 1
2788585484eSchristos #endif	/* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
2798585484eSchristos #ifndef HAVE_INTMAX_T
2808585484eSchristos #define HAVE_INTMAX_T 1
2818585484eSchristos #endif	/* !defined(HAVE_INTMAX_T) */
2828585484eSchristos #ifndef HAVE_UINTMAX_T
2838585484eSchristos #define HAVE_UINTMAX_T 1
2848585484eSchristos #endif	/* !defined(HAVE_UINTMAX_T) */
2858585484eSchristos #ifndef HAVE_UINTPTR_T
2868585484eSchristos #define HAVE_UINTPTR_T 1
2878585484eSchristos #endif	/* !defined(HAVE_UINTPTR_T) */
2888585484eSchristos #ifndef HAVE_PTRDIFF_T
2898585484eSchristos #define HAVE_PTRDIFF_T 1
2908585484eSchristos #endif	/* !defined(HAVE_PTRDIFF_T) */
2918585484eSchristos #ifndef HAVE_VA_COPY
2928585484eSchristos #define HAVE_VA_COPY 1
2938585484eSchristos #endif	/* !defined(HAVE_VA_COPY) */
2948585484eSchristos #ifndef HAVE___VA_COPY
2958585484eSchristos #define HAVE___VA_COPY 1
2968585484eSchristos #endif	/* !defined(HAVE___VA_COPY) */
2978585484eSchristos #endif	/* HAVE_CONFIG_H */
2988585484eSchristos #define snprintf rpl_snprintf
2998585484eSchristos #define vsnprintf rpl_vsnprintf
3008585484eSchristos #define asprintf rpl_asprintf
3018585484eSchristos #define vasprintf rpl_vasprintf
3028585484eSchristos #endif	/* TEST_SNPRINTF */
303abb0f93cSkardel 
3048585484eSchristos #if HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || HW_WANT_RPL_VASPRINTF
3058585484eSchristos #include <stdio.h>	/* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
3068585484eSchristos #ifdef VA_START
3078585484eSchristos #undef VA_START
3088585484eSchristos #endif	/* defined(VA_START) */
3098585484eSchristos #ifdef VA_SHIFT
3108585484eSchristos #undef VA_SHIFT
3118585484eSchristos #endif	/* defined(VA_SHIFT) */
3128585484eSchristos #if HAVE_STDARG_H
313abb0f93cSkardel #include <stdarg.h>
3148585484eSchristos #define VA_START(ap, last) va_start(ap, last)
3158585484eSchristos #define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
3168585484eSchristos #else	/* Assume <varargs.h> is available. */
317abb0f93cSkardel #include <varargs.h>
3188585484eSchristos #define VA_START(ap, last) va_start(ap)	/* "last" is ignored. */
3198585484eSchristos #define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
3208585484eSchristos #endif	/* HAVE_STDARG_H */
321abb0f93cSkardel 
3228585484eSchristos #if HW_WANT_RPL_VASPRINTF
3238585484eSchristos #if HAVE_STDLIB_H
3248585484eSchristos #include <stdlib.h>	/* For malloc(3). */
3258585484eSchristos #endif	/* HAVE_STDLIB_H */
3268585484eSchristos #ifdef VA_COPY
3278585484eSchristos #undef VA_COPY
3288585484eSchristos #endif	/* defined(VA_COPY) */
3298585484eSchristos #ifdef VA_END_COPY
3308585484eSchristos #undef VA_END_COPY
3318585484eSchristos #endif	/* defined(VA_END_COPY) */
3328585484eSchristos #if HAVE_VA_COPY
3338585484eSchristos #define VA_COPY(dest, src) va_copy(dest, src)
3348585484eSchristos #define VA_END_COPY(ap) va_end(ap)
3358585484eSchristos #elif HAVE___VA_COPY
3368585484eSchristos #define VA_COPY(dest, src) __va_copy(dest, src)
3378585484eSchristos #define VA_END_COPY(ap) va_end(ap)
3388585484eSchristos #else
3398585484eSchristos #define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
3408585484eSchristos #define VA_END_COPY(ap) /* No-op. */
3418585484eSchristos #define NEED_MYMEMCPY 1
3428585484eSchristos static void *mymemcpy(void *, void *, size_t);
3438585484eSchristos #endif	/* HAVE_VA_COPY */
3448585484eSchristos #endif	/* HW_WANT_RPL_VASPRINTF */
345abb0f93cSkardel 
3468585484eSchristos #if HW_WANT_RPL_VSNPRINTF
3478585484eSchristos #include <errno.h>	/* For ERANGE and errno. */
3488585484eSchristos #include <limits.h>	/* For *_MAX. */
3498585484eSchristos #if HAVE_INTTYPES_H
3508585484eSchristos #include <inttypes.h>	/* For intmax_t (if not defined in <stdint.h>). */
3518585484eSchristos #endif	/* HAVE_INTTYPES_H */
3528585484eSchristos #if HAVE_LOCALE_H
3538585484eSchristos #include <locale.h>	/* For localeconv(3). */
3548585484eSchristos #endif	/* HAVE_LOCALE_H */
3558585484eSchristos #if HAVE_STDDEF_H
3568585484eSchristos #include <stddef.h>	/* For ptrdiff_t. */
3578585484eSchristos #endif	/* HAVE_STDDEF_H */
3588585484eSchristos #if HAVE_STDINT_H
3598585484eSchristos #include <stdint.h>	/* For intmax_t. */
3608585484eSchristos #endif	/* HAVE_STDINT_H */
3618585484eSchristos 
3628585484eSchristos /* Support for unsigned long long int.  We may also need ULLONG_MAX. */
3638585484eSchristos #ifndef ULONG_MAX	/* We may need ULONG_MAX as a fallback. */
3648585484eSchristos #ifdef UINT_MAX
3658585484eSchristos #define ULONG_MAX UINT_MAX
366abb0f93cSkardel #else
3678585484eSchristos #define ULONG_MAX INT_MAX
3688585484eSchristos #endif	/* defined(UINT_MAX) */
3698585484eSchristos #endif	/* !defined(ULONG_MAX) */
3708585484eSchristos #ifdef ULLONG
3718585484eSchristos #undef ULLONG
3728585484eSchristos #endif	/* defined(ULLONG) */
3738585484eSchristos #if HAVE_UNSIGNED_LONG_LONG_INT
3748585484eSchristos #define ULLONG unsigned long long int
3758585484eSchristos #ifndef ULLONG_MAX
3768585484eSchristos #define ULLONG_MAX ULONG_MAX
3778585484eSchristos #endif	/* !defined(ULLONG_MAX) */
378abb0f93cSkardel #else
3798585484eSchristos #define ULLONG unsigned long int
3808585484eSchristos #ifdef ULLONG_MAX
3818585484eSchristos #undef ULLONG_MAX
3828585484eSchristos #endif	/* defined(ULLONG_MAX) */
3838585484eSchristos #define ULLONG_MAX ULONG_MAX
3848585484eSchristos #endif	/* HAVE_LONG_LONG_INT */
3858585484eSchristos 
3868585484eSchristos /* Support for uintmax_t.  We also need UINTMAX_MAX. */
3878585484eSchristos #ifdef UINTMAX_T
3888585484eSchristos #undef UINTMAX_T
3898585484eSchristos #endif	/* defined(UINTMAX_T) */
3908585484eSchristos #if HAVE_UINTMAX_T || defined(uintmax_t)
3918585484eSchristos #define UINTMAX_T uintmax_t
3928585484eSchristos #ifndef UINTMAX_MAX
3938585484eSchristos #define UINTMAX_MAX ULLONG_MAX
3948585484eSchristos #endif	/* !defined(UINTMAX_MAX) */
395abb0f93cSkardel #else
3968585484eSchristos #define UINTMAX_T ULLONG
3978585484eSchristos #ifdef UINTMAX_MAX
3988585484eSchristos #undef UINTMAX_MAX
3998585484eSchristos #endif	/* defined(UINTMAX_MAX) */
4008585484eSchristos #define UINTMAX_MAX ULLONG_MAX
4018585484eSchristos #endif	/* HAVE_UINTMAX_T || defined(uintmax_t) */
4028585484eSchristos 
4038585484eSchristos /* Support for long double. */
4048585484eSchristos #ifndef LDOUBLE
4058585484eSchristos #if HAVE_LONG_DOUBLE
4068585484eSchristos #define LDOUBLE long double
4078585484eSchristos #else
4088585484eSchristos #define LDOUBLE double
4098585484eSchristos #endif	/* HAVE_LONG_DOUBLE */
4108585484eSchristos #endif	/* !defined(LDOUBLE) */
4118585484eSchristos 
4128585484eSchristos /* Support for long long int. */
4138585484eSchristos #ifndef LLONG
4148585484eSchristos #if HAVE_LONG_LONG_INT
4158585484eSchristos #define LLONG long long int
4168585484eSchristos #else
4178585484eSchristos #define LLONG long int
4188585484eSchristos #endif	/* HAVE_LONG_LONG_INT */
4198585484eSchristos #endif	/* !defined(LLONG) */
4208585484eSchristos 
4218585484eSchristos /* Support for intmax_t. */
4228585484eSchristos #ifndef INTMAX_T
4238585484eSchristos #if HAVE_INTMAX_T || defined(intmax_t)
4248585484eSchristos #define INTMAX_T intmax_t
4258585484eSchristos #else
4268585484eSchristos #define INTMAX_T LLONG
4278585484eSchristos #endif	/* HAVE_INTMAX_T || defined(intmax_t) */
4288585484eSchristos #endif	/* !defined(INTMAX_T) */
4298585484eSchristos 
4308585484eSchristos /* Support for uintptr_t. */
4318585484eSchristos #ifndef UINTPTR_T
4328585484eSchristos #if HAVE_UINTPTR_T || defined(uintptr_t)
4338585484eSchristos #define UINTPTR_T uintptr_t
4348585484eSchristos #else
4358585484eSchristos #define UINTPTR_T unsigned long int
4368585484eSchristos #endif	/* HAVE_UINTPTR_T || defined(uintptr_t) */
4378585484eSchristos #endif	/* !defined(UINTPTR_T) */
4388585484eSchristos 
4398585484eSchristos /* Support for ptrdiff_t. */
4408585484eSchristos #ifndef PTRDIFF_T
4418585484eSchristos #if HAVE_PTRDIFF_T || defined(ptrdiff_t)
4428585484eSchristos #define PTRDIFF_T ptrdiff_t
4438585484eSchristos #else
4448585484eSchristos #define PTRDIFF_T long int
4458585484eSchristos #endif	/* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
4468585484eSchristos #endif	/* !defined(PTRDIFF_T) */
4478585484eSchristos 
4488585484eSchristos /*
4498585484eSchristos  * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
4508585484eSchristos  * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
4518585484eSchristos  * unsigned type if necessary.  This should work just fine in practice.
4528585484eSchristos  */
4538585484eSchristos #ifndef UPTRDIFF_T
4548585484eSchristos #define UPTRDIFF_T PTRDIFF_T
4558585484eSchristos #endif	/* !defined(UPTRDIFF_T) */
4568585484eSchristos 
4578585484eSchristos /*
4588585484eSchristos  * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
4598585484eSchristos  * However, we'll simply use size_t and convert it to a signed type if
4608585484eSchristos  * necessary.  This should work just fine in practice.
4618585484eSchristos  */
4628585484eSchristos #ifndef SSIZE_T
4638585484eSchristos #define SSIZE_T size_t
4648585484eSchristos #endif	/* !defined(SSIZE_T) */
4658585484eSchristos 
4668585484eSchristos /* Either ERANGE or E2BIG should be available everywhere. */
4678585484eSchristos #ifndef ERANGE
4688585484eSchristos #define ERANGE E2BIG
4698585484eSchristos #endif	/* !defined(ERANGE) */
4708585484eSchristos #ifndef EOVERFLOW
4718585484eSchristos #define EOVERFLOW ERANGE
4728585484eSchristos #endif	/* !defined(EOVERFLOW) */
4738585484eSchristos 
4748585484eSchristos /*
4758585484eSchristos  * Buffer size to hold the octal string representation of UINT128_MAX without
4768585484eSchristos  * nul-termination ("3777777777777777777777777777777777777777777").
4778585484eSchristos  */
4788585484eSchristos #ifdef MAX_CONVERT_LENGTH
4798585484eSchristos #undef MAX_CONVERT_LENGTH
4808585484eSchristos #endif	/* defined(MAX_CONVERT_LENGTH) */
4818585484eSchristos #define MAX_CONVERT_LENGTH      43
4828585484eSchristos 
4838585484eSchristos /* Format read states. */
4848585484eSchristos #define PRINT_S_DEFAULT         0
4858585484eSchristos #define PRINT_S_FLAGS           1
4868585484eSchristos #define PRINT_S_WIDTH           2
4878585484eSchristos #define PRINT_S_DOT             3
4888585484eSchristos #define PRINT_S_PRECISION       4
4898585484eSchristos #define PRINT_S_MOD             5
4908585484eSchristos #define PRINT_S_CONV            6
4918585484eSchristos 
4928585484eSchristos /* Format flags. */
4938585484eSchristos #define PRINT_F_MINUS           (1 << 0)
4948585484eSchristos #define PRINT_F_PLUS            (1 << 1)
4958585484eSchristos #define PRINT_F_SPACE           (1 << 2)
4968585484eSchristos #define PRINT_F_NUM             (1 << 3)
4978585484eSchristos #define PRINT_F_ZERO            (1 << 4)
4988585484eSchristos #define PRINT_F_QUOTE           (1 << 5)
4998585484eSchristos #define PRINT_F_UP              (1 << 6)
5008585484eSchristos #define PRINT_F_UNSIGNED        (1 << 7)
5018585484eSchristos #define PRINT_F_TYPE_G          (1 << 8)
5028585484eSchristos #define PRINT_F_TYPE_E          (1 << 9)
5038585484eSchristos 
5048585484eSchristos /* Conversion flags. */
5058585484eSchristos #define PRINT_C_CHAR            1
5068585484eSchristos #define PRINT_C_SHORT           2
5078585484eSchristos #define PRINT_C_LONG            3
5088585484eSchristos #define PRINT_C_LLONG           4
5098585484eSchristos #define PRINT_C_LDOUBLE         5
5108585484eSchristos #define PRINT_C_SIZE            6
5118585484eSchristos #define PRINT_C_PTRDIFF         7
5128585484eSchristos #define PRINT_C_INTMAX          8
5138585484eSchristos 
5148585484eSchristos #ifndef MAX
5158585484eSchristos #define MAX(x, y) ((x >= y) ? x : y)
5168585484eSchristos #endif	/* !defined(MAX) */
5178585484eSchristos #ifndef CHARTOINT
5188585484eSchristos #define CHARTOINT(ch) (ch - '0')
5198585484eSchristos #endif	/* !defined(CHARTOINT) */
5208585484eSchristos #ifndef ISDIGIT
5218585484eSchristos #define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
5228585484eSchristos #endif	/* !defined(ISDIGIT) */
5238585484eSchristos #ifndef ISNAN
5248585484eSchristos #define ISNAN(x) (x != x)
5258585484eSchristos #endif	/* !defined(ISNAN) */
5268585484eSchristos #ifndef ISINF
5278585484eSchristos #define ISINF(x) (x != 0.0 && x + x == x)
5288585484eSchristos #endif	/* !defined(ISINF) */
5298585484eSchristos 
5308585484eSchristos #ifdef OUTCHAR
5318585484eSchristos #undef OUTCHAR
5328585484eSchristos #endif	/* defined(OUTCHAR) */
5338585484eSchristos #define OUTCHAR(str, len, size, ch)                                          \
5348585484eSchristos do {                                                                         \
5358585484eSchristos 	if (len + 1 < size)                                                  \
5368585484eSchristos 		str[len] = ch;                                               \
5378585484eSchristos 	(len)++;                                                             \
5388585484eSchristos } while (/* CONSTCOND */ 0)
5398585484eSchristos 
5408585484eSchristos static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
5418585484eSchristos static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
5428585484eSchristos static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
5438585484eSchristos static void printsep(char *, size_t *, size_t);
5448585484eSchristos static int getnumsep(int);
5458585484eSchristos static int getexponent(LDOUBLE);
5468585484eSchristos static int convert(UINTMAX_T, char *, size_t, int, int);
5478585484eSchristos static UINTMAX_T cast(LDOUBLE);
5488585484eSchristos static UINTMAX_T myround(LDOUBLE);
5498585484eSchristos static LDOUBLE mypow10(int);
550abb0f93cSkardel 
551abb0f93cSkardel int
5528585484eSchristos rpl_vsnprintf(char *str, size_t size, const char *format, va_list args);
5538585484eSchristos 
5548585484eSchristos int
5558585484eSchristos rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
556abb0f93cSkardel {
5578585484eSchristos 	LDOUBLE fvalue;
5588585484eSchristos 	INTMAX_T value;
5598585484eSchristos 	unsigned char cvalue;
5608585484eSchristos 	const char *strvalue;
5618585484eSchristos 	INTMAX_T *intmaxptr;
5628585484eSchristos 	PTRDIFF_T *ptrdiffptr;
5638585484eSchristos 	SSIZE_T *sizeptr;
5648585484eSchristos 	LLONG *llongptr;
5658585484eSchristos 	long int *longptr;
5668585484eSchristos 	int *intptr;
5678585484eSchristos 	short int *shortptr;
5688585484eSchristos 	signed char *charptr;
5698585484eSchristos 	size_t len = 0;
5708585484eSchristos 	int overflow = 0;
5718585484eSchristos 	int base = 0;
5728585484eSchristos 	int cflags = 0;
5738585484eSchristos 	int flags = 0;
5748585484eSchristos 	int width = 0;
5758585484eSchristos 	int precision = -1;
5768585484eSchristos 	int state = PRINT_S_DEFAULT;
5778585484eSchristos 	char ch = *format++;
5788585484eSchristos 
5798585484eSchristos 	/*
5808585484eSchristos 	 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
5818585484eSchristos 	 * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
5828585484eSchristos 	 * even if a size larger than zero was specified.  At least NetBSD's
5838585484eSchristos 	 * snprintf(3) does the same, as well as other versions of this file.
5848585484eSchristos 	 * (Though some of these versions will write to a non-NULL buffer even
5858585484eSchristos 	 * if a size of zero was specified, which violates the standard.)
5868585484eSchristos 	 */
5878585484eSchristos 	if (str == NULL && size != 0)
5888585484eSchristos 		size = 0;
5898585484eSchristos 
5908585484eSchristos 	while (ch != '\0')
5918585484eSchristos 		switch (state) {
5928585484eSchristos 		case PRINT_S_DEFAULT:
5938585484eSchristos 			if (ch == '%')
5948585484eSchristos 				state = PRINT_S_FLAGS;
5958585484eSchristos 			else
5968585484eSchristos 				OUTCHAR(str, len, size, ch);
5978585484eSchristos 			ch = *format++;
5988585484eSchristos 			break;
5998585484eSchristos 		case PRINT_S_FLAGS:
6008585484eSchristos 			switch (ch) {
6018585484eSchristos 			case '-':
6028585484eSchristos 				flags |= PRINT_F_MINUS;
6038585484eSchristos 				ch = *format++;
6048585484eSchristos 				break;
6058585484eSchristos 			case '+':
6068585484eSchristos 				flags |= PRINT_F_PLUS;
6078585484eSchristos 				ch = *format++;
6088585484eSchristos 				break;
6098585484eSchristos 			case ' ':
6108585484eSchristos 				flags |= PRINT_F_SPACE;
6118585484eSchristos 				ch = *format++;
6128585484eSchristos 				break;
6138585484eSchristos 			case '#':
6148585484eSchristos 				flags |= PRINT_F_NUM;
6158585484eSchristos 				ch = *format++;
6168585484eSchristos 				break;
6178585484eSchristos 			case '0':
6188585484eSchristos 				flags |= PRINT_F_ZERO;
6198585484eSchristos 				ch = *format++;
6208585484eSchristos 				break;
6218585484eSchristos 			case '\'':	/* SUSv2 flag (not in C99). */
6228585484eSchristos 				flags |= PRINT_F_QUOTE;
6238585484eSchristos 				ch = *format++;
6248585484eSchristos 				break;
6258585484eSchristos 			default:
6268585484eSchristos 				state = PRINT_S_WIDTH;
6278585484eSchristos 				break;
628abb0f93cSkardel 			}
6298585484eSchristos 			break;
6308585484eSchristos 		case PRINT_S_WIDTH:
6318585484eSchristos 			if (ISDIGIT(ch)) {
6328585484eSchristos 				ch = CHARTOINT(ch);
6338585484eSchristos 				if (width > (INT_MAX - ch) / 10) {
6348585484eSchristos 					overflow = 1;
6358585484eSchristos 					goto out;
6368585484eSchristos 				}
6378585484eSchristos 				width = 10 * width + ch;
6388585484eSchristos 				ch = *format++;
6398585484eSchristos 			} else if (ch == '*') {
6408585484eSchristos 				/*
6418585484eSchristos 				 * C99 says: "A negative field width argument is
6428585484eSchristos 				 * taken as a `-' flag followed by a positive
6438585484eSchristos 				 * field width." (7.19.6.1, 5)
6448585484eSchristos 				 */
6458585484eSchristos 				if ((width = va_arg(args, int)) < 0) {
6468585484eSchristos 					flags |= PRINT_F_MINUS;
6478585484eSchristos 					width = -width;
6488585484eSchristos 				}
6498585484eSchristos 				ch = *format++;
6508585484eSchristos 				state = PRINT_S_DOT;
6518585484eSchristos 			} else
6528585484eSchristos 				state = PRINT_S_DOT;
6538585484eSchristos 			break;
6548585484eSchristos 		case PRINT_S_DOT:
6558585484eSchristos 			if (ch == '.') {
6568585484eSchristos 				state = PRINT_S_PRECISION;
6578585484eSchristos 				ch = *format++;
6588585484eSchristos 			} else
6598585484eSchristos 				state = PRINT_S_MOD;
6608585484eSchristos 			break;
6618585484eSchristos 		case PRINT_S_PRECISION:
6628585484eSchristos 			if (precision == -1)
6638585484eSchristos 				precision = 0;
6648585484eSchristos 			if (ISDIGIT(ch)) {
6658585484eSchristos 				ch = CHARTOINT(ch);
6668585484eSchristos 				if (precision > (INT_MAX - ch) / 10) {
6678585484eSchristos 					overflow = 1;
6688585484eSchristos 					goto out;
6698585484eSchristos 				}
6708585484eSchristos 				precision = 10 * precision + ch;
6718585484eSchristos 				ch = *format++;
6728585484eSchristos 			} else if (ch == '*') {
6738585484eSchristos 				/*
6748585484eSchristos 				 * C99 says: "A negative precision argument is
6758585484eSchristos 				 * taken as if the precision were omitted."
6768585484eSchristos 				 * (7.19.6.1, 5)
6778585484eSchristos 				 */
6788585484eSchristos 				if ((precision = va_arg(args, int)) < 0)
6798585484eSchristos 					precision = -1;
6808585484eSchristos 				ch = *format++;
6818585484eSchristos 				state = PRINT_S_MOD;
6828585484eSchristos 			} else
6838585484eSchristos 				state = PRINT_S_MOD;
6848585484eSchristos 			break;
6858585484eSchristos 		case PRINT_S_MOD:
6868585484eSchristos 			switch (ch) {
6878585484eSchristos 			case 'h':
6888585484eSchristos 				ch = *format++;
6898585484eSchristos 				if (ch == 'h') {	/* It's a char. */
6908585484eSchristos 					ch = *format++;
6918585484eSchristos 					cflags = PRINT_C_CHAR;
6928585484eSchristos 				} else
6938585484eSchristos 					cflags = PRINT_C_SHORT;
6948585484eSchristos 				break;
6958585484eSchristos 			case 'l':
6968585484eSchristos 				ch = *format++;
6978585484eSchristos 				if (ch == 'l') {	/* It's a long long. */
6988585484eSchristos 					ch = *format++;
6998585484eSchristos 					cflags = PRINT_C_LLONG;
7008585484eSchristos 				} else
7018585484eSchristos 					cflags = PRINT_C_LONG;
7028585484eSchristos 				break;
7038585484eSchristos 			case 'L':
7048585484eSchristos 				cflags = PRINT_C_LDOUBLE;
7058585484eSchristos 				ch = *format++;
7068585484eSchristos 				break;
7078585484eSchristos 			case 'j':
7088585484eSchristos 				cflags = PRINT_C_INTMAX;
7098585484eSchristos 				ch = *format++;
7108585484eSchristos 				break;
7118585484eSchristos 			case 't':
7128585484eSchristos 				cflags = PRINT_C_PTRDIFF;
7138585484eSchristos 				ch = *format++;
7148585484eSchristos 				break;
7158585484eSchristos 			case 'z':
7168585484eSchristos 				cflags = PRINT_C_SIZE;
7178585484eSchristos 				ch = *format++;
7188585484eSchristos 				break;
7198585484eSchristos 			}
7208585484eSchristos 			state = PRINT_S_CONV;
7218585484eSchristos 			break;
7228585484eSchristos 		case PRINT_S_CONV:
7238585484eSchristos 			switch (ch) {
7248585484eSchristos 			case 'd':
7258585484eSchristos 				/* FALLTHROUGH */
7268585484eSchristos 			case 'i':
7278585484eSchristos 				switch (cflags) {
7288585484eSchristos 				case PRINT_C_CHAR:
7298585484eSchristos 					value = (signed char)va_arg(args, int);
7308585484eSchristos 					break;
7318585484eSchristos 				case PRINT_C_SHORT:
7328585484eSchristos 					value = (short int)va_arg(args, int);
7338585484eSchristos 					break;
7348585484eSchristos 				case PRINT_C_LONG:
7358585484eSchristos 					value = va_arg(args, long int);
7368585484eSchristos 					break;
7378585484eSchristos 				case PRINT_C_LLONG:
7388585484eSchristos 					value = va_arg(args, LLONG);
7398585484eSchristos 					break;
7408585484eSchristos 				case PRINT_C_SIZE:
7418585484eSchristos 					value = va_arg(args, SSIZE_T);
7428585484eSchristos 					break;
7438585484eSchristos 				case PRINT_C_INTMAX:
7448585484eSchristos 					value = va_arg(args, INTMAX_T);
7458585484eSchristos 					break;
7468585484eSchristos 				case PRINT_C_PTRDIFF:
7478585484eSchristos 					value = va_arg(args, PTRDIFF_T);
7488585484eSchristos 					break;
7498585484eSchristos 				default:
7508585484eSchristos 					value = va_arg(args, int);
7518585484eSchristos 					break;
7528585484eSchristos 				}
7538585484eSchristos 				fmtint(str, &len, size, value, 10, width,
7548585484eSchristos 				    precision, flags);
7558585484eSchristos 				break;
7568585484eSchristos 			case 'X':
7578585484eSchristos 				flags |= PRINT_F_UP;
7588585484eSchristos 				/* FALLTHROUGH */
7598585484eSchristos 			case 'x':
7608585484eSchristos 				base = 16;
7618585484eSchristos 				/* FALLTHROUGH */
7628585484eSchristos 			case 'o':
7638585484eSchristos 				if (base == 0)
7648585484eSchristos 					base = 8;
7658585484eSchristos 				/* FALLTHROUGH */
7668585484eSchristos 			case 'u':
7678585484eSchristos 				if (base == 0)
7688585484eSchristos 					base = 10;
7698585484eSchristos 				flags |= PRINT_F_UNSIGNED;
7708585484eSchristos 				switch (cflags) {
7718585484eSchristos 				case PRINT_C_CHAR:
7728585484eSchristos 					value = (unsigned char)va_arg(args,
7738585484eSchristos 					    unsigned int);
7748585484eSchristos 					break;
7758585484eSchristos 				case PRINT_C_SHORT:
7768585484eSchristos 					value = (unsigned short int)va_arg(args,
7778585484eSchristos 					    unsigned int);
7788585484eSchristos 					break;
7798585484eSchristos 				case PRINT_C_LONG:
7808585484eSchristos 					value = va_arg(args, unsigned long int);
7818585484eSchristos 					break;
7828585484eSchristos 				case PRINT_C_LLONG:
7838585484eSchristos 					value = va_arg(args, ULLONG);
7848585484eSchristos 					break;
7858585484eSchristos 				case PRINT_C_SIZE:
7868585484eSchristos 					value = va_arg(args, size_t);
7878585484eSchristos 					break;
7888585484eSchristos 				case PRINT_C_INTMAX:
7898585484eSchristos 					value = va_arg(args, UINTMAX_T);
7908585484eSchristos 					break;
7918585484eSchristos 				case PRINT_C_PTRDIFF:
7928585484eSchristos 					value = va_arg(args, UPTRDIFF_T);
7938585484eSchristos 					break;
7948585484eSchristos 				default:
7958585484eSchristos 					value = va_arg(args, unsigned int);
7968585484eSchristos 					break;
7978585484eSchristos 				}
7988585484eSchristos 				fmtint(str, &len, size, value, base, width,
7998585484eSchristos 				    precision, flags);
8008585484eSchristos 				break;
8018585484eSchristos 			case 'A':
8028585484eSchristos 				/* Not yet supported, we'll use "%F". */
8038585484eSchristos 				/* FALLTHROUGH */
8048585484eSchristos 			case 'F':
8058585484eSchristos 				flags |= PRINT_F_UP;
8068585484eSchristos 				/* FALLTHROUGH */
8078585484eSchristos 			case 'a':
8088585484eSchristos 				/* Not yet supported, we'll use "%f". */
8098585484eSchristos 				/* FALLTHROUGH */
8108585484eSchristos 			case 'f':
8118585484eSchristos 				if (cflags == PRINT_C_LDOUBLE)
8128585484eSchristos 					fvalue = va_arg(args, LDOUBLE);
8138585484eSchristos 				else
8148585484eSchristos 					fvalue = va_arg(args, double);
8158585484eSchristos 				fmtflt(str, &len, size, fvalue, width,
8168585484eSchristos 				    precision, flags, &overflow);
8178585484eSchristos 				if (overflow)
8188585484eSchristos 					goto out;
8198585484eSchristos 				break;
8208585484eSchristos 			case 'E':
8218585484eSchristos 				flags |= PRINT_F_UP;
8228585484eSchristos 				/* FALLTHROUGH */
8238585484eSchristos 			case 'e':
8248585484eSchristos 				flags |= PRINT_F_TYPE_E;
8258585484eSchristos 				if (cflags == PRINT_C_LDOUBLE)
8268585484eSchristos 					fvalue = va_arg(args, LDOUBLE);
8278585484eSchristos 				else
8288585484eSchristos 					fvalue = va_arg(args, double);
8298585484eSchristos 				fmtflt(str, &len, size, fvalue, width,
8308585484eSchristos 				    precision, flags, &overflow);
8318585484eSchristos 				if (overflow)
8328585484eSchristos 					goto out;
8338585484eSchristos 				break;
8348585484eSchristos 			case 'G':
8358585484eSchristos 				flags |= PRINT_F_UP;
8368585484eSchristos 				/* FALLTHROUGH */
8378585484eSchristos 			case 'g':
8388585484eSchristos 				flags |= PRINT_F_TYPE_G;
8398585484eSchristos 				if (cflags == PRINT_C_LDOUBLE)
8408585484eSchristos 					fvalue = va_arg(args, LDOUBLE);
8418585484eSchristos 				else
8428585484eSchristos 					fvalue = va_arg(args, double);
8438585484eSchristos 				/*
8448585484eSchristos 				 * If the precision is zero, it is treated as
8458585484eSchristos 				 * one (cf. C99: 7.19.6.1, 8).
8468585484eSchristos 				 */
8478585484eSchristos 				if (precision == 0)
8488585484eSchristos 					precision = 1;
8498585484eSchristos 				fmtflt(str, &len, size, fvalue, width,
8508585484eSchristos 				    precision, flags, &overflow);
8518585484eSchristos 				if (overflow)
8528585484eSchristos 					goto out;
8538585484eSchristos 				break;
8548585484eSchristos 			case 'c':
8558585484eSchristos 				cvalue = va_arg(args, int);
8568585484eSchristos 				OUTCHAR(str, len, size, cvalue);
8578585484eSchristos 				break;
8588585484eSchristos 			case 's':
8598585484eSchristos 				strvalue = va_arg(args, char *);
8608585484eSchristos 				fmtstr(str, &len, size, strvalue, width,
8618585484eSchristos 				    precision, flags);
8628585484eSchristos 				break;
8638585484eSchristos 			case 'p':
8648585484eSchristos 				/*
8658585484eSchristos 				 * C99 says: "The value of the pointer is
8668585484eSchristos 				 * converted to a sequence of printing
8678585484eSchristos 				 * characters, in an implementation-defined
8688585484eSchristos 				 * manner." (C99: 7.19.6.1, 8)
8698585484eSchristos 				 */
8708585484eSchristos 				if ((strvalue = va_arg(args, void *)) == NULL)
8718585484eSchristos 					/*
8728585484eSchristos 					 * We use the glibc format.  BSD prints
8738585484eSchristos 					 * "0x0", SysV "0".
8748585484eSchristos 					 */
8758585484eSchristos 					fmtstr(str, &len, size, "(nil)", width,
8768585484eSchristos 					    -1, flags);
8778585484eSchristos 				else {
8788585484eSchristos 					/*
8798585484eSchristos 					 * We use the BSD/glibc format.  SysV
8808585484eSchristos 					 * omits the "0x" prefix (which we emit
8818585484eSchristos 					 * using the PRINT_F_NUM flag).
8828585484eSchristos 					 */
8838585484eSchristos 					flags |= PRINT_F_NUM;
8848585484eSchristos 					flags |= PRINT_F_UNSIGNED;
8858585484eSchristos 					fmtint(str, &len, size,
8868585484eSchristos 					    (UINTPTR_T)strvalue, 16, width,
8878585484eSchristos 					    precision, flags);
8888585484eSchristos 				}
8898585484eSchristos 				break;
8908585484eSchristos 			case 'n':
8918585484eSchristos 				switch (cflags) {
8928585484eSchristos 				case PRINT_C_CHAR:
8938585484eSchristos 					charptr = va_arg(args, signed char *);
8948b8da087Schristos 					*charptr = (signed char)len;
8958585484eSchristos 					break;
8968585484eSchristos 				case PRINT_C_SHORT:
8978585484eSchristos 					shortptr = va_arg(args, short int *);
8988b8da087Schristos 					*shortptr = (short int)len;
8998585484eSchristos 					break;
9008585484eSchristos 				case PRINT_C_LONG:
9018585484eSchristos 					longptr = va_arg(args, long int *);
9028b8da087Schristos 					*longptr = (long int)len;
9038585484eSchristos 					break;
9048585484eSchristos 				case PRINT_C_LLONG:
9058585484eSchristos 					llongptr = va_arg(args, LLONG *);
9068b8da087Schristos 					*llongptr = (LLONG)len;
9078585484eSchristos 					break;
9088585484eSchristos 				case PRINT_C_SIZE:
9098585484eSchristos 					/*
9108585484eSchristos 					 * C99 says that with the "z" length
9118585484eSchristos 					 * modifier, "a following `n' conversion
9128585484eSchristos 					 * specifier applies to a pointer to a
9138585484eSchristos 					 * signed integer type corresponding to
9148585484eSchristos 					 * size_t argument." (7.19.6.1, 7)
9158585484eSchristos 					 */
9168585484eSchristos 					sizeptr = va_arg(args, SSIZE_T *);
9178b8da087Schristos 					*sizeptr = (SSIZE_T)len;
9188585484eSchristos 					break;
9198585484eSchristos 				case PRINT_C_INTMAX:
9208585484eSchristos 					intmaxptr = va_arg(args, INTMAX_T *);
9218b8da087Schristos 					*intmaxptr = (INTMAX_T)len;
9228585484eSchristos 					break;
9238585484eSchristos 				case PRINT_C_PTRDIFF:
9248585484eSchristos 					ptrdiffptr = va_arg(args, PTRDIFF_T *);
9258b8da087Schristos 					*ptrdiffptr = (PTRDIFF_T)len;
9268585484eSchristos 					break;
9278585484eSchristos 				default:
9288585484eSchristos 					intptr = va_arg(args, int *);
9298b8da087Schristos 					*intptr = (int)len;
9308585484eSchristos 					break;
9318585484eSchristos 				}
9328585484eSchristos 				break;
9338585484eSchristos 			case '%':	/* Print a "%" character verbatim. */
9348585484eSchristos 				OUTCHAR(str, len, size, ch);
9358585484eSchristos 				break;
9368585484eSchristos 			default:	/* Skip other characters. */
9378585484eSchristos 				break;
9388585484eSchristos 			}
9398585484eSchristos 			ch = *format++;
9408585484eSchristos 			state = PRINT_S_DEFAULT;
9418585484eSchristos 			base = cflags = flags = width = 0;
9428585484eSchristos 			precision = -1;
9438585484eSchristos 			break;
9448585484eSchristos 		}
9458585484eSchristos out:
9468585484eSchristos 	if (len < size)
9478585484eSchristos 		str[len] = '\0';
9488585484eSchristos 	else if (size > 0)
9498585484eSchristos 		str[size - 1] = '\0';
9508585484eSchristos 
9518585484eSchristos 	if (overflow || len >= INT_MAX) {
9528585484eSchristos 		errno = overflow ? EOVERFLOW : ERANGE;
9538585484eSchristos 		return -1;
9548585484eSchristos 	}
9558585484eSchristos 	return (int)len;
9568585484eSchristos }
9578585484eSchristos 
9588585484eSchristos static void
9598585484eSchristos fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
9608585484eSchristos        int precision, int flags)
9618585484eSchristos {
9628585484eSchristos 	int padlen, strln;	/* Amount to pad. */
9638585484eSchristos 	int noprecision = (precision == -1);
9648585484eSchristos 
9658585484eSchristos 	if (value == NULL)	/* We're forgiving. */
9668585484eSchristos 		value = "(null)";
9678585484eSchristos 
9688585484eSchristos 	/* If a precision was specified, don't read the string past it. */
9698585484eSchristos 	for (strln = 0; value[strln] != '\0' &&
9708585484eSchristos 	    (noprecision || strln < precision); strln++)
9718585484eSchristos 		continue;
9728585484eSchristos 
9738585484eSchristos 	if ((padlen = width - strln) < 0)
9748585484eSchristos 		padlen = 0;
9758585484eSchristos 	if (flags & PRINT_F_MINUS)	/* Left justify. */
9768585484eSchristos 		padlen = -padlen;
9778585484eSchristos 
9788585484eSchristos 	while (padlen > 0) {	/* Leading spaces. */
9798585484eSchristos 		OUTCHAR(str, *len, size, ' ');
9808585484eSchristos 		padlen--;
9818585484eSchristos 	}
9828585484eSchristos 	while (*value != '\0' && (noprecision || precision-- > 0)) {
9838585484eSchristos 		OUTCHAR(str, *len, size, *value);
9848585484eSchristos 		value++;
9858585484eSchristos 	}
9868585484eSchristos 	while (padlen < 0) {	/* Trailing spaces. */
9878585484eSchristos 		OUTCHAR(str, *len, size, ' ');
9888585484eSchristos 		padlen++;
9898585484eSchristos 	}
9908585484eSchristos }
9918585484eSchristos 
9928585484eSchristos static void
9938585484eSchristos fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
9948585484eSchristos        int precision, int flags)
9958585484eSchristos {
9968585484eSchristos 	UINTMAX_T uvalue;
9978585484eSchristos 	char iconvert[MAX_CONVERT_LENGTH];
9988585484eSchristos 	char sign = 0;
9998585484eSchristos 	char hexprefix = 0;
10008585484eSchristos 	int spadlen = 0;	/* Amount to space pad. */
10018585484eSchristos 	int zpadlen = 0;	/* Amount to zero pad. */
10028585484eSchristos 	int pos;
10038585484eSchristos 	int separators = (flags & PRINT_F_QUOTE);
10048585484eSchristos 	int noprecision = (precision == -1);
10058585484eSchristos 
10068585484eSchristos 	if (flags & PRINT_F_UNSIGNED)
10078585484eSchristos 		uvalue = value;
10088585484eSchristos 	else {
10098585484eSchristos 		uvalue = (value >= 0) ? value : -value;
10108585484eSchristos 		if (value < 0)
10118585484eSchristos 			sign = '-';
10128585484eSchristos 		else if (flags & PRINT_F_PLUS)	/* Do a sign. */
10138585484eSchristos 			sign = '+';
10148585484eSchristos 		else if (flags & PRINT_F_SPACE)
10158585484eSchristos 			sign = ' ';
10168585484eSchristos 	}
10178585484eSchristos 
10188585484eSchristos 	pos = convert(uvalue, iconvert, sizeof(iconvert), base,
10198585484eSchristos 	    flags & PRINT_F_UP);
10208585484eSchristos 
10218585484eSchristos 	if (flags & PRINT_F_NUM && uvalue != 0) {
10228585484eSchristos 		/*
10238585484eSchristos 		 * C99 says: "The result is converted to an `alternative form'.
10248585484eSchristos 		 * For `o' conversion, it increases the precision, if and only
10258585484eSchristos 		 * if necessary, to force the first digit of the result to be a
10268585484eSchristos 		 * zero (if the value and precision are both 0, a single 0 is
10278585484eSchristos 		 * printed).  For `x' (or `X') conversion, a nonzero result has
10288585484eSchristos 		 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
10298585484eSchristos 		 */
10308585484eSchristos 		switch (base) {
10318585484eSchristos 		case 8:
10328585484eSchristos 			if (precision <= pos)
10338585484eSchristos 				precision = pos + 1;
10348585484eSchristos 			break;
10358585484eSchristos 		case 16:
10368585484eSchristos 			hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
10378585484eSchristos 			break;
10388585484eSchristos 		}
10398585484eSchristos 	}
10408585484eSchristos 
10418585484eSchristos 	if (separators)	/* Get the number of group separators we'll print. */
10428585484eSchristos 		separators = getnumsep(pos);
10438585484eSchristos 
10448585484eSchristos 	zpadlen = precision - pos - separators;
10458585484eSchristos 	spadlen = width                         /* Minimum field width. */
10468585484eSchristos 	    - separators                        /* Number of separators. */
10478585484eSchristos 	    - MAX(precision, pos)               /* Number of integer digits. */
10488585484eSchristos 	    - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
10498585484eSchristos 	    - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
10508585484eSchristos 
10518585484eSchristos 	if (zpadlen < 0)
10528585484eSchristos 		zpadlen = 0;
10538585484eSchristos 	if (spadlen < 0)
10548585484eSchristos 		spadlen = 0;
10558585484eSchristos 
10568585484eSchristos 	/*
10578585484eSchristos 	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
10588585484eSchristos 	 * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
10598585484eSchristos 	 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
10608585484eSchristos 	 */
10618585484eSchristos 	if (flags & PRINT_F_MINUS)	/* Left justify. */
10628585484eSchristos 		spadlen = -spadlen;
10638585484eSchristos 	else if (flags & PRINT_F_ZERO && noprecision) {
10648585484eSchristos 		zpadlen += spadlen;
10658585484eSchristos 		spadlen = 0;
10668585484eSchristos 	}
10678585484eSchristos 	while (spadlen > 0) {	/* Leading spaces. */
10688585484eSchristos 		OUTCHAR(str, *len, size, ' ');
10698585484eSchristos 		spadlen--;
10708585484eSchristos 	}
10718585484eSchristos 	if (sign != 0)	/* Sign. */
10728585484eSchristos 		OUTCHAR(str, *len, size, sign);
10738585484eSchristos 	if (hexprefix != 0) {	/* A "0x" or "0X" prefix. */
10748585484eSchristos 		OUTCHAR(str, *len, size, '0');
10758585484eSchristos 		OUTCHAR(str, *len, size, hexprefix);
10768585484eSchristos 	}
10778585484eSchristos 	while (zpadlen > 0) {	/* Leading zeros. */
10788585484eSchristos 		OUTCHAR(str, *len, size, '0');
10798585484eSchristos 		zpadlen--;
10808585484eSchristos 	}
10818585484eSchristos 	while (pos > 0) {	/* The actual digits. */
10828585484eSchristos 		pos--;
10838585484eSchristos 		OUTCHAR(str, *len, size, iconvert[pos]);
10848585484eSchristos 		if (separators > 0 && pos > 0 && pos % 3 == 0)
10858585484eSchristos 			printsep(str, len, size);
10868585484eSchristos 	}
10878585484eSchristos 	while (spadlen < 0) {	/* Trailing spaces. */
10888585484eSchristos 		OUTCHAR(str, *len, size, ' ');
10898585484eSchristos 		spadlen++;
10908585484eSchristos 	}
10918585484eSchristos }
10928585484eSchristos 
10938585484eSchristos static void
10948585484eSchristos fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
10958585484eSchristos        int precision, int flags, int *overflow)
10968585484eSchristos {
10978585484eSchristos 	LDOUBLE ufvalue;
10988585484eSchristos 	UINTMAX_T intpart;
10998585484eSchristos 	UINTMAX_T fracpart;
11008585484eSchristos 	UINTMAX_T mask;
11018585484eSchristos 	const char *infnan = NULL;
11028585484eSchristos 	char iconvert[MAX_CONVERT_LENGTH];
11038585484eSchristos 	char fconvert[MAX_CONVERT_LENGTH];
11048585484eSchristos 	char econvert[4];	/* "e-12" (without nul-termination). */
11058585484eSchristos 	char esign = 0;
11068585484eSchristos 	char sign = 0;
11078585484eSchristos 	int leadfraczeros = 0;
11088585484eSchristos 	int exponent = 0;
11098585484eSchristos 	int emitpoint = 0;
11108585484eSchristos 	int omitzeros = 0;
11118585484eSchristos 	int omitcount = 0;
11128585484eSchristos 	int padlen = 0;
11138585484eSchristos 	int epos = 0;
11148585484eSchristos 	int fpos = 0;
11158585484eSchristos 	int ipos = 0;
11168585484eSchristos 	int separators = (flags & PRINT_F_QUOTE);
11178585484eSchristos 	int estyle = (flags & PRINT_F_TYPE_E);
11188585484eSchristos #if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
11198585484eSchristos 	struct lconv *lc = localeconv();
11208585484eSchristos #endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
11218585484eSchristos 
11228585484eSchristos 	/*
11238585484eSchristos 	 * AIX' man page says the default is 0, but C99 and at least Solaris'
11248585484eSchristos 	 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
11258585484eSchristos 	 * defaults to 6.
11268585484eSchristos 	 */
11278585484eSchristos 	if (precision == -1)
11288585484eSchristos 		precision = 6;
11298585484eSchristos 
11308585484eSchristos 	if (fvalue < 0.0)
11318585484eSchristos 		sign = '-';
11328585484eSchristos 	else if (flags & PRINT_F_PLUS)	/* Do a sign. */
11338585484eSchristos 		sign = '+';
11348585484eSchristos 	else if (flags & PRINT_F_SPACE)
11358585484eSchristos 		sign = ' ';
11368585484eSchristos 
11378585484eSchristos 	if (ISNAN(fvalue))
11388585484eSchristos 		infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
11398585484eSchristos 	else if (ISINF(fvalue))
11408585484eSchristos 		infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
11418585484eSchristos 
11428585484eSchristos 	if (infnan != NULL) {
11438585484eSchristos 		if (sign != 0)
11448585484eSchristos 			iconvert[ipos++] = sign;
11458585484eSchristos 		while (*infnan != '\0')
11468585484eSchristos 			iconvert[ipos++] = *infnan++;
11478585484eSchristos 		fmtstr(str, len, size, iconvert, width, ipos, flags);
11488585484eSchristos 		return;
11498585484eSchristos 	}
11508585484eSchristos 
11518585484eSchristos 	/* "%e" (or "%E") or "%g" (or "%G") conversion. */
11528585484eSchristos 	if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
11538585484eSchristos 		if (flags & PRINT_F_TYPE_G) {
11548585484eSchristos 			/*
11558585484eSchristos 			 * For "%g" (and "%G") conversions, the precision
11568585484eSchristos 			 * specifies the number of significant digits, which
11578585484eSchristos 			 * includes the digits in the integer part.  The
11588585484eSchristos 			 * conversion will or will not be using "e-style" (like
11598585484eSchristos 			 * "%e" or "%E" conversions) depending on the precision
11608585484eSchristos 			 * and on the exponent.  However, the exponent can be
11618585484eSchristos 			 * affected by rounding the converted value, so we'll
11628585484eSchristos 			 * leave this decision for later.  Until then, we'll
11638585484eSchristos 			 * assume that we're going to do an "e-style" conversion
11648585484eSchristos 			 * (in order to get the exponent calculated).  For
11658585484eSchristos 			 * "e-style", the precision must be decremented by one.
11668585484eSchristos 			 */
11678585484eSchristos 			precision--;
11688585484eSchristos 			/*
11698585484eSchristos 			 * For "%g" (and "%G") conversions, trailing zeros are
11708585484eSchristos 			 * removed from the fractional portion of the result
11718585484eSchristos 			 * unless the "#" flag was specified.
11728585484eSchristos 			 */
11738585484eSchristos 			if (!(flags & PRINT_F_NUM))
11748585484eSchristos 				omitzeros = 1;
11758585484eSchristos 		}
11768585484eSchristos 		exponent = getexponent(fvalue);
11778585484eSchristos 		estyle = 1;
11788585484eSchristos 	}
11798585484eSchristos 
11808585484eSchristos again:
11818585484eSchristos 	/*
11828585484eSchristos 	 * Sorry, we only support 9, 19, or 38 digits (that is, the number of
11838585484eSchristos 	 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
11848585484eSchristos 	 * minus one) past the decimal point due to our conversion method.
11858585484eSchristos 	 */
11868585484eSchristos 	switch (sizeof(UINTMAX_T)) {
11878585484eSchristos 	case 16:
11888585484eSchristos 		if (precision > 38)
11898585484eSchristos 			precision = 38;
11908585484eSchristos 		break;
11918585484eSchristos 	case 8:
11928585484eSchristos 		if (precision > 19)
11938585484eSchristos 			precision = 19;
11948585484eSchristos 		break;
11958585484eSchristos 	default:
11968585484eSchristos 		if (precision > 9)
11978585484eSchristos 			precision = 9;
11988585484eSchristos 		break;
11998585484eSchristos 	}
12008585484eSchristos 
12018585484eSchristos 	ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
12028585484eSchristos 	if (estyle)	/* We want exactly one integer digit. */
12038585484eSchristos 		ufvalue /= mypow10(exponent);
12048585484eSchristos 
12058585484eSchristos 	if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
12068585484eSchristos 		*overflow = 1;
12078585484eSchristos 		return;
12088585484eSchristos 	}
12098585484eSchristos 
12108585484eSchristos 	/*
12118585484eSchristos 	 * Factor of ten with the number of digits needed for the fractional
12128585484eSchristos 	 * part.  For example, if the precision is 3, the mask will be 1000.
12138585484eSchristos 	 */
12148b8da087Schristos 	mask = (UINTMAX_T)mypow10(precision);
12158585484eSchristos 	/*
12168585484eSchristos 	 * We "cheat" by converting the fractional part to integer by
12178585484eSchristos 	 * multiplying by a factor of ten.
12188585484eSchristos 	 */
12198585484eSchristos 	if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
12208585484eSchristos 		/*
12218585484eSchristos 		 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
12228585484eSchristos 		 * (because precision = 3).  Now, myround(1000 * 0.99962) will
12238585484eSchristos 		 * return 1000.  So, the integer part must be incremented by one
12248585484eSchristos 		 * and the fractional part must be set to zero.
12258585484eSchristos 		 */
12268585484eSchristos 		intpart++;
12278585484eSchristos 		fracpart = 0;
12288585484eSchristos 		if (estyle && intpart == 10) {
12298585484eSchristos 			/*
12308585484eSchristos 			 * The value was rounded up to ten, but we only want one
12318585484eSchristos 			 * integer digit if using "e-style".  So, the integer
12328585484eSchristos 			 * part must be set to one and the exponent must be
12338585484eSchristos 			 * incremented by one.
12348585484eSchristos 			 */
12358585484eSchristos 			intpart = 1;
12368585484eSchristos 			exponent++;
12378585484eSchristos 		}
12388585484eSchristos 	}
12398585484eSchristos 
12408585484eSchristos 	/*
12418585484eSchristos 	 * Now that we know the real exponent, we can check whether or not to
12428585484eSchristos 	 * use "e-style" for "%g" (and "%G") conversions.  If we don't need
12438585484eSchristos 	 * "e-style", the precision must be adjusted and the integer and
12448585484eSchristos 	 * fractional parts must be recalculated from the original value.
12458585484eSchristos 	 *
12468585484eSchristos 	 * C99 says: "Let P equal the precision if nonzero, 6 if the precision
12478585484eSchristos 	 * is omitted, or 1 if the precision is zero.  Then, if a conversion
12488585484eSchristos 	 * with style `E' would have an exponent of X:
12498585484eSchristos 	 *
12508585484eSchristos 	 * - if P > X >= -4, the conversion is with style `f' (or `F') and
12518585484eSchristos 	 *   precision P - (X + 1).
12528585484eSchristos 	 *
12538585484eSchristos 	 * - otherwise, the conversion is with style `e' (or `E') and precision
12548585484eSchristos 	 *   P - 1." (7.19.6.1, 8)
12558585484eSchristos 	 *
12568585484eSchristos 	 * Note that we had decremented the precision by one.
12578585484eSchristos 	 */
12588585484eSchristos 	if (flags & PRINT_F_TYPE_G && estyle &&
12598585484eSchristos 	    precision + 1 > exponent && exponent >= -4) {
12608585484eSchristos 		precision -= exponent;
12618585484eSchristos 		estyle = 0;
12628585484eSchristos 		goto again;
12638585484eSchristos 	}
12648585484eSchristos 
12658585484eSchristos 	if (estyle) {
12668585484eSchristos 		if (exponent < 0) {
12678585484eSchristos 			exponent = -exponent;
12688585484eSchristos 			esign = '-';
12698585484eSchristos 		} else
12708585484eSchristos 			esign = '+';
12718585484eSchristos 
12728585484eSchristos 		/*
12738585484eSchristos 		 * Convert the exponent.  The sizeof(econvert) is 4.  So, the
12748585484eSchristos 		 * econvert buffer can hold e.g. "e+99" and "e-99".  We don't
12758585484eSchristos 		 * support an exponent which contains more than two digits.
12768585484eSchristos 		 * Therefore, the following stores are safe.
12778585484eSchristos 		 */
12788585484eSchristos 		epos = convert(exponent, econvert, 2, 10, 0);
12798585484eSchristos 		/*
12808585484eSchristos 		 * C99 says: "The exponent always contains at least two digits,
12818585484eSchristos 		 * and only as many more digits as necessary to represent the
12828585484eSchristos 		 * exponent." (7.19.6.1, 8)
12838585484eSchristos 		 */
12848585484eSchristos 		if (epos == 1)
12858585484eSchristos 			econvert[epos++] = '0';
12868585484eSchristos 		econvert[epos++] = esign;
12878585484eSchristos 		econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
12888585484eSchristos 	}
12898585484eSchristos 
12908585484eSchristos 	/* Convert the integer part and the fractional part. */
12918585484eSchristos 	ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
12928585484eSchristos 	if (fracpart != 0)	/* convert() would return 1 if fracpart == 0. */
12938585484eSchristos 		fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
12948585484eSchristos 
12958585484eSchristos 	leadfraczeros = precision - fpos;
12968585484eSchristos 
12978585484eSchristos 	if (omitzeros) {
12988585484eSchristos 		if (fpos > 0)	/* Omit trailing fractional part zeros. */
12998585484eSchristos 			while (omitcount < fpos && fconvert[omitcount] == '0')
13008585484eSchristos 				omitcount++;
13018585484eSchristos 		else {	/* The fractional part is zero, omit it completely. */
13028585484eSchristos 			omitcount = precision;
13038585484eSchristos 			leadfraczeros = 0;
13048585484eSchristos 		}
13058585484eSchristos 		precision -= omitcount;
13068585484eSchristos 	}
13078585484eSchristos 
13088585484eSchristos 	/*
13098585484eSchristos 	 * Print a decimal point if either the fractional part is non-zero
13108585484eSchristos 	 * and/or the "#" flag was specified.
13118585484eSchristos 	 */
13128585484eSchristos 	if (precision > 0 || flags & PRINT_F_NUM)
13138585484eSchristos 		emitpoint = 1;
13148585484eSchristos 	if (separators)	/* Get the number of group separators we'll print. */
13158585484eSchristos 		separators = getnumsep(ipos);
13168585484eSchristos 
13178585484eSchristos 	padlen = width                  /* Minimum field width. */
13188585484eSchristos 	    - ipos                      /* Number of integer digits. */
13198585484eSchristos 	    - epos                      /* Number of exponent characters. */
13208585484eSchristos 	    - precision                 /* Number of fractional digits. */
13218585484eSchristos 	    - separators                /* Number of group separators. */
13228585484eSchristos 	    - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
13238585484eSchristos 	    - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
13248585484eSchristos 
13258585484eSchristos 	if (padlen < 0)
13268585484eSchristos 		padlen = 0;
13278585484eSchristos 
13288585484eSchristos 	/*
13298585484eSchristos 	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
13308585484eSchristos 	 * ignored." (7.19.6.1, 6)
13318585484eSchristos 	 */
13328585484eSchristos 	if (flags & PRINT_F_MINUS)	/* Left justifty. */
13338585484eSchristos 		padlen = -padlen;
13348585484eSchristos 	else if (flags & PRINT_F_ZERO && padlen > 0) {
13358585484eSchristos 		if (sign != 0) {	/* Sign. */
13368585484eSchristos 			OUTCHAR(str, *len, size, sign);
13378585484eSchristos 			sign = 0;
13388585484eSchristos 		}
13398585484eSchristos 		while (padlen > 0) {	/* Leading zeros. */
13408585484eSchristos 			OUTCHAR(str, *len, size, '0');
13418585484eSchristos 			padlen--;
13428585484eSchristos 		}
13438585484eSchristos 	}
13448585484eSchristos 	while (padlen > 0) {	/* Leading spaces. */
13458585484eSchristos 		OUTCHAR(str, *len, size, ' ');
13468585484eSchristos 		padlen--;
13478585484eSchristos 	}
13488585484eSchristos 	if (sign != 0)	/* Sign. */
13498585484eSchristos 		OUTCHAR(str, *len, size, sign);
13508585484eSchristos 	while (ipos > 0) {	/* Integer part. */
13518585484eSchristos 		ipos--;
13528585484eSchristos 		OUTCHAR(str, *len, size, iconvert[ipos]);
13538585484eSchristos 		if (separators > 0 && ipos > 0 && ipos % 3 == 0)
13548585484eSchristos 			printsep(str, len, size);
13558585484eSchristos 	}
13568585484eSchristos 	if (emitpoint) {	/* Decimal point. */
13578585484eSchristos #if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
13588585484eSchristos 		if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
13598585484eSchristos 			OUTCHAR(str, *len, size, *lc->decimal_point);
13608585484eSchristos 		else	/* We'll always print some decimal point character. */
13618585484eSchristos #endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
13628585484eSchristos 			OUTCHAR(str, *len, size, '.');
13638585484eSchristos 	}
13648585484eSchristos 	while (leadfraczeros > 0) {	/* Leading fractional part zeros. */
13658585484eSchristos 		OUTCHAR(str, *len, size, '0');
13668585484eSchristos 		leadfraczeros--;
13678585484eSchristos 	}
13688585484eSchristos 	while (fpos > omitcount) {	/* The remaining fractional part. */
13698585484eSchristos 		fpos--;
13708585484eSchristos 		OUTCHAR(str, *len, size, fconvert[fpos]);
13718585484eSchristos 	}
13728585484eSchristos 	while (epos > 0) {	/* Exponent. */
13738585484eSchristos 		epos--;
13748585484eSchristos 		OUTCHAR(str, *len, size, econvert[epos]);
13758585484eSchristos 	}
13768585484eSchristos 	while (padlen < 0) {	/* Trailing spaces. */
13778585484eSchristos 		OUTCHAR(str, *len, size, ' ');
13788585484eSchristos 		padlen++;
13798585484eSchristos 	}
13808585484eSchristos }
13818585484eSchristos 
13828585484eSchristos static void
13838585484eSchristos printsep(char *str, size_t *len, size_t size)
13848585484eSchristos {
13858585484eSchristos #if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
13868585484eSchristos 	struct lconv *lc = localeconv();
13878585484eSchristos 	int i;
13888585484eSchristos 
13898585484eSchristos 	if (lc->thousands_sep != NULL)
13908585484eSchristos 		for (i = 0; lc->thousands_sep[i] != '\0'; i++)
13918585484eSchristos 			OUTCHAR(str, *len, size, lc->thousands_sep[i]);
13928585484eSchristos 	else
13938585484eSchristos #endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
13948585484eSchristos 		OUTCHAR(str, *len, size, ',');
13958585484eSchristos }
13968585484eSchristos 
13978585484eSchristos static int
13988585484eSchristos getnumsep(int digits)
13998585484eSchristos {
14008585484eSchristos 	int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
14018585484eSchristos #if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
14028585484eSchristos 	int strln;
14038585484eSchristos 	struct lconv *lc = localeconv();
14048585484eSchristos 
14058585484eSchristos 	/* We support an arbitrary separator length (including zero). */
14068585484eSchristos 	if (lc->thousands_sep != NULL) {
14078585484eSchristos 		for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
14088585484eSchristos 			continue;
14098585484eSchristos 		separators *= strln;
14108585484eSchristos 	}
14118585484eSchristos #endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
14128585484eSchristos 	return separators;
14138585484eSchristos }
14148585484eSchristos 
14158585484eSchristos static int
14168585484eSchristos getexponent(LDOUBLE value)
14178585484eSchristos {
14188585484eSchristos 	LDOUBLE tmp = (value >= 0.0) ? value : -value;
14198585484eSchristos 	int exponent = 0;
14208585484eSchristos 
14218585484eSchristos 	/*
14228585484eSchristos 	 * We check for 99 > exponent > -99 in order to work around possible
14238585484eSchristos 	 * endless loops which could happen (at least) in the second loop (at
14248585484eSchristos 	 * least) if we're called with an infinite value.  However, we checked
14258585484eSchristos 	 * for infinity before calling this function using our ISINF() macro, so
14268585484eSchristos 	 * this might be somewhat paranoid.
14278585484eSchristos 	 */
14288585484eSchristos 	while (tmp < 1.0 && tmp > 0.0 && --exponent > -99)
14298585484eSchristos 		tmp *= 10;
14308585484eSchristos 	while (tmp >= 10.0 && ++exponent < 99)
14318585484eSchristos 		tmp /= 10;
14328585484eSchristos 
14338585484eSchristos 	return exponent;
14348585484eSchristos }
14358585484eSchristos 
14368585484eSchristos static int
14378585484eSchristos convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
14388585484eSchristos {
14398585484eSchristos 	const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
14408585484eSchristos 	size_t pos = 0;
14418585484eSchristos 
14428585484eSchristos 	/* We return an unterminated buffer with the digits in reverse order. */
14438585484eSchristos 	do {
14448585484eSchristos 		buf[pos++] = digits[value % base];
14458585484eSchristos 		value /= base;
14468585484eSchristos 	} while (value != 0 && pos < size);
14478585484eSchristos 
14488585484eSchristos 	return (int)pos;
14498585484eSchristos }
14508585484eSchristos 
14518585484eSchristos static UINTMAX_T
14528585484eSchristos cast(LDOUBLE value)
14538585484eSchristos {
14548585484eSchristos 	UINTMAX_T result;
14558585484eSchristos 
14568585484eSchristos 	/*
14578585484eSchristos 	 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
14588585484eSchristos 	 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
14598585484eSchristos 	 * it may be increased to the nearest higher representable value for the
14608585484eSchristos 	 * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
14618585484eSchristos 	 * value although converting the latter to UINTMAX_T would overflow.
14628585484eSchristos 	 */
14638585484eSchristos 	if (value >= UINTMAX_MAX)
14648585484eSchristos 		return UINTMAX_MAX;
14658585484eSchristos 
14668b8da087Schristos 	result = (UINTMAX_T)value;
14678585484eSchristos 	/*
14688585484eSchristos 	 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
14698585484eSchristos 	 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
14708585484eSchristos 	 * the standard).  Sigh.
14718585484eSchristos 	 */
14728585484eSchristos 	return (result <= value) ? result : result - 1;
14738585484eSchristos }
14748585484eSchristos 
14758585484eSchristos static UINTMAX_T
14768585484eSchristos myround(LDOUBLE value)
14778585484eSchristos {
14788585484eSchristos 	UINTMAX_T intpart = cast(value);
14798585484eSchristos 
14808585484eSchristos 	return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
14818585484eSchristos }
14828585484eSchristos 
14838585484eSchristos static LDOUBLE
14848585484eSchristos mypow10(int exponent)
14858585484eSchristos {
14868585484eSchristos 	LDOUBLE result = 1;
14878585484eSchristos 
14888585484eSchristos 	while (exponent > 0) {
14898585484eSchristos 		result *= 10;
14908585484eSchristos 		exponent--;
14918585484eSchristos 	}
14928585484eSchristos 	while (exponent < 0) {
14938585484eSchristos 		result /= 10;
14948585484eSchristos 		exponent++;
14958585484eSchristos 	}
14968585484eSchristos 	return result;
14978585484eSchristos }
14988585484eSchristos #endif	/* HW_WANT_RPL_VSNPRINTF */
14998585484eSchristos 
15008585484eSchristos #if HW_WANT_RPL_VASPRINTF
15018585484eSchristos #if NEED_MYMEMCPY
15028585484eSchristos void *
15038585484eSchristos mymemcpy(void *dst, void *src, size_t len)
15048585484eSchristos {
15058585484eSchristos 	const char *from = src;
15068585484eSchristos 	char *to = dst;
15078585484eSchristos 
15088585484eSchristos 	/* No need for optimization, we use this only to replace va_copy(3). */
15098585484eSchristos 	while (len-- > 0)
15108585484eSchristos 		*to++ = *from++;
15118585484eSchristos 	return dst;
15128585484eSchristos }
15138585484eSchristos #endif	/* NEED_MYMEMCPY */
15148585484eSchristos 
15158585484eSchristos int
15168585484eSchristos rpl_vasprintf(char **ret, const char *format, va_list ap);
15178585484eSchristos 
15188585484eSchristos int
15198585484eSchristos rpl_vasprintf(char **ret, const char *format, va_list ap)
15208585484eSchristos {
15218585484eSchristos 	size_t size;
15228585484eSchristos 	int len;
15238585484eSchristos 	va_list aq;
15248585484eSchristos 
15258585484eSchristos 	VA_COPY(aq, ap);
15268585484eSchristos 	len = vsnprintf(NULL, 0, format, aq);
15278585484eSchristos 	VA_END_COPY(aq);
15288585484eSchristos 	if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
15298585484eSchristos 		return -1;
15308585484eSchristos 	return vsnprintf(*ret, size, format, ap);
15318585484eSchristos }
15328585484eSchristos #endif	/* HW_WANT_RPL_VASPRINTF */
15338585484eSchristos 
15348585484eSchristos #if HW_WANT_RPL_SNPRINTF
15358585484eSchristos #if HAVE_STDARG_H
15368585484eSchristos int
15378585484eSchristos rpl_snprintf(char *str, size_t size, const char *format, ...);
15388585484eSchristos 
15398585484eSchristos int
15408585484eSchristos rpl_snprintf(char *str, size_t size, const char *format, ...)
1541abb0f93cSkardel #else
15428585484eSchristos int
15438585484eSchristos rpl_snprintf(va_alist) va_dcl
15448585484eSchristos #endif	/* HAVE_STDARG_H */
15458585484eSchristos {
15468585484eSchristos #if !HAVE_STDARG_H
15478585484eSchristos 	char *str;
15488585484eSchristos 	size_t size;
15498585484eSchristos 	char *format;
15508585484eSchristos #endif	/* HAVE_STDARG_H */
15518585484eSchristos 	va_list ap;
15528585484eSchristos 	int len;
15538585484eSchristos 
15548585484eSchristos 	VA_START(ap, format);
15558585484eSchristos 	VA_SHIFT(ap, str, char *);
15568585484eSchristos 	VA_SHIFT(ap, size, size_t);
15578585484eSchristos 	VA_SHIFT(ap, format, const char *);
15588585484eSchristos 	len = vsnprintf(str, size, format, ap);
15598585484eSchristos 	va_end(ap);
15608585484eSchristos 	return len;
15618585484eSchristos }
15628585484eSchristos #endif	/* HW_WANT_RPL_SNPRINTF */
15638585484eSchristos 
15648585484eSchristos #if HW_WANT_RPL_ASPRINTF
15658585484eSchristos #if HAVE_STDARG_H
15668585484eSchristos int
15678585484eSchristos rpl_asprintf(char **ret, const char *format, ...);
15688585484eSchristos 
15698585484eSchristos int
15708585484eSchristos rpl_asprintf(char **ret, const char *format, ...)
15718585484eSchristos #else
15728585484eSchristos int
15738585484eSchristos rpl_asprintf(va_alist) va_dcl
15748585484eSchristos #endif	/* HAVE_STDARG_H */
15758585484eSchristos {
15768585484eSchristos #if !HAVE_STDARG_H
15778585484eSchristos 	char **ret;
15788585484eSchristos 	char *format;
15798585484eSchristos #endif	/* HAVE_STDARG_H */
15808585484eSchristos 	va_list ap;
15818585484eSchristos 	int len;
15828585484eSchristos 
15838585484eSchristos 	VA_START(ap, format);
15848585484eSchristos 	VA_SHIFT(ap, ret, char **);
15858585484eSchristos 	VA_SHIFT(ap, format, const char *);
15868585484eSchristos 	len = vasprintf(ret, format, ap);
15878585484eSchristos 	va_end(ap);
15888585484eSchristos 	return len;
15898585484eSchristos }
15908585484eSchristos #endif	/* HW_WANT_RPL_ASPRINTF */
15918585484eSchristos #else	/* Dummy declaration to avoid empty translation unit warnings. */
1592*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT
15938585484eSchristos #endif	/* HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || [...] */
15948585484eSchristos 
15958585484eSchristos #if TEST_SNPRINTF
15968585484eSchristos int
15978585484eSchristos main(void)
15988585484eSchristos {
15998585484eSchristos 	const char *float_fmt[] = {
16008585484eSchristos 		/* "%E" and "%e" formats. */
16018585484eSchristos #if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
16028585484eSchristos 		"%.16e",
16038585484eSchristos 		"%22.16e",
16048585484eSchristos 		"%022.16e",
16058585484eSchristos 		"%-22.16e",
16068585484eSchristos 		"%#+'022.16e",
16078585484eSchristos #endif	/* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
16088585484eSchristos 		"foo|%#+0123.9E|bar",
16098585484eSchristos 		"%-123.9e",
16108585484eSchristos 		"%123.9e",
16118585484eSchristos 		"%+23.9e",
16128585484eSchristos 		"%+05.8e",
16138585484eSchristos 		"%-05.8e",
16148585484eSchristos 		"%05.8e",
16158585484eSchristos 		"%+5.8e",
16168585484eSchristos 		"%-5.8e",
16178585484eSchristos 		"% 5.8e",
16188585484eSchristos 		"%5.8e",
16198585484eSchristos 		"%+4.9e",
16208585484eSchristos #if !OS_LINUX	/* glibc sometimes gets these wrong. */
16218585484eSchristos 		"%+#010.0e",
16228585484eSchristos 		"%#10.1e",
16238585484eSchristos 		"%10.5e",
16248585484eSchristos 		"% 10.5e",
16258585484eSchristos 		"%5.0e",
16268585484eSchristos 		"%5.e",
16278585484eSchristos 		"%#5.0e",
16288585484eSchristos 		"%#5.e",
16298585484eSchristos 		"%3.2e",
16308585484eSchristos 		"%3.1e",
16318585484eSchristos 		"%-1.5e",
16328585484eSchristos 		"%1.5e",
16338585484eSchristos 		"%01.3e",
16348585484eSchristos 		"%1.e",
16358585484eSchristos 		"%.1e",
16368585484eSchristos 		"%#.0e",
16378585484eSchristos 		"%+.0e",
16388585484eSchristos 		"% .0e",
16398585484eSchristos 		"%.0e",
16408585484eSchristos 		"%#.e",
16418585484eSchristos 		"%+.e",
16428585484eSchristos 		"% .e",
16438585484eSchristos 		"%.e",
16448585484eSchristos 		"%4e",
16458585484eSchristos 		"%e",
16468585484eSchristos 		"%E",
16478585484eSchristos #endif	/* !OS_LINUX */
16488585484eSchristos 		/* "%F" and "%f" formats. */
16498585484eSchristos #if !OS_BSD && !OS_IRIX
16508585484eSchristos 		"% '022f",
16518585484eSchristos 		"%+'022f",
16528585484eSchristos 		"%-'22f",
16538585484eSchristos 		"%'22f",
16548585484eSchristos #if HAVE_LONG_LONG_INT
16558585484eSchristos 		"%.16f",
16568585484eSchristos 		"%22.16f",
16578585484eSchristos 		"%022.16f",
16588585484eSchristos 		"%-22.16f",
16598585484eSchristos 		"%#+'022.16f",
16608585484eSchristos #endif	/* HAVE_LONG_LONG_INT */
16618585484eSchristos #endif	/* !OS_BSD && !OS_IRIX */
16628585484eSchristos 		"foo|%#+0123.9F|bar",
16638585484eSchristos 		"%-123.9f",
16648585484eSchristos 		"%123.9f",
16658585484eSchristos 		"%+23.9f",
16668585484eSchristos 		"%+#010.0f",
16678585484eSchristos 		"%#10.1f",
16688585484eSchristos 		"%10.5f",
16698585484eSchristos 		"% 10.5f",
16708585484eSchristos 		"%+05.8f",
16718585484eSchristos 		"%-05.8f",
16728585484eSchristos 		"%05.8f",
16738585484eSchristos 		"%+5.8f",
16748585484eSchristos 		"%-5.8f",
16758585484eSchristos 		"% 5.8f",
16768585484eSchristos 		"%5.8f",
16778585484eSchristos 		"%5.0f",
16788585484eSchristos 		"%5.f",
16798585484eSchristos 		"%#5.0f",
16808585484eSchristos 		"%#5.f",
16818585484eSchristos 		"%+4.9f",
16828585484eSchristos 		"%3.2f",
16838585484eSchristos 		"%3.1f",
16848585484eSchristos 		"%-1.5f",
16858585484eSchristos 		"%1.5f",
16868585484eSchristos 		"%01.3f",
16878585484eSchristos 		"%1.f",
16888585484eSchristos 		"%.1f",
16898585484eSchristos 		"%#.0f",
16908585484eSchristos 		"%+.0f",
16918585484eSchristos 		"% .0f",
16928585484eSchristos 		"%.0f",
16938585484eSchristos 		"%#.f",
16948585484eSchristos 		"%+.f",
16958585484eSchristos 		"% .f",
16968585484eSchristos 		"%.f",
16978585484eSchristos 		"%4f",
16988585484eSchristos 		"%f",
16998585484eSchristos 		"%F",
17008585484eSchristos 		/* "%G" and "%g" formats. */
17018585484eSchristos #if !OS_BSD && !OS_IRIX && !OS_LINUX
17028585484eSchristos 		"% '022g",
17038585484eSchristos 		"%+'022g",
17048585484eSchristos 		"%-'22g",
17058585484eSchristos 		"%'22g",
17068585484eSchristos #if HAVE_LONG_LONG_INT
17078585484eSchristos 		"%.16g",
17088585484eSchristos 		"%22.16g",
17098585484eSchristos 		"%022.16g",
17108585484eSchristos 		"%-22.16g",
17118585484eSchristos 		"%#+'022.16g",
17128585484eSchristos #endif	/* HAVE_LONG_LONG_INT */
17138585484eSchristos #endif	/* !OS_BSD && !OS_IRIX && !OS_LINUX */
17148585484eSchristos 		"foo|%#+0123.9G|bar",
17158585484eSchristos 		"%-123.9g",
17168585484eSchristos 		"%123.9g",
17178585484eSchristos 		"%+23.9g",
17188585484eSchristos 		"%+05.8g",
17198585484eSchristos 		"%-05.8g",
17208585484eSchristos 		"%05.8g",
17218585484eSchristos 		"%+5.8g",
17228585484eSchristos 		"%-5.8g",
17238585484eSchristos 		"% 5.8g",
17248585484eSchristos 		"%5.8g",
17258585484eSchristos 		"%+4.9g",
17268585484eSchristos #if !OS_LINUX	/* glibc sometimes gets these wrong. */
17278585484eSchristos 		"%+#010.0g",
17288585484eSchristos 		"%#10.1g",
17298585484eSchristos 		"%10.5g",
17308585484eSchristos 		"% 10.5g",
17318585484eSchristos 		"%5.0g",
17328585484eSchristos 		"%5.g",
17338585484eSchristos 		"%#5.0g",
17348585484eSchristos 		"%#5.g",
17358585484eSchristos 		"%3.2g",
17368585484eSchristos 		"%3.1g",
17378585484eSchristos 		"%-1.5g",
17388585484eSchristos 		"%1.5g",
17398585484eSchristos 		"%01.3g",
17408585484eSchristos 		"%1.g",
17418585484eSchristos 		"%.1g",
17428585484eSchristos 		"%#.0g",
17438585484eSchristos 		"%+.0g",
17448585484eSchristos 		"% .0g",
17458585484eSchristos 		"%.0g",
17468585484eSchristos 		"%#.g",
17478585484eSchristos 		"%+.g",
17488585484eSchristos 		"% .g",
17498585484eSchristos 		"%.g",
17508585484eSchristos 		"%4g",
17518585484eSchristos 		"%g",
17528585484eSchristos 		"%G",
17538585484eSchristos #endif	/* !OS_LINUX */
17548585484eSchristos 		NULL
17558585484eSchristos 	};
17568585484eSchristos 	double float_val[] = {
17578585484eSchristos 		-4.136,
17588585484eSchristos 		-134.52,
17598585484eSchristos 		-5.04030201,
17608585484eSchristos 		-3410.01234,
17618585484eSchristos 		-999999.999999,
17628585484eSchristos 		-913450.29876,
17638585484eSchristos 		-913450.2,
17648585484eSchristos 		-91345.2,
17658585484eSchristos 		-9134.2,
17668585484eSchristos 		-913.2,
17678585484eSchristos 		-91.2,
17688585484eSchristos 		-9.2,
17698585484eSchristos 		-9.9,
17708585484eSchristos 		4.136,
17718585484eSchristos 		134.52,
17728585484eSchristos 		5.04030201,
17738585484eSchristos 		3410.01234,
17748585484eSchristos 		999999.999999,
17758585484eSchristos 		913450.29876,
17768585484eSchristos 		913450.2,
17778585484eSchristos 		91345.2,
17788585484eSchristos 		9134.2,
17798585484eSchristos 		913.2,
17808585484eSchristos 		91.2,
17818585484eSchristos 		9.2,
17828585484eSchristos 		9.9,
17838585484eSchristos 		9.96,
17848585484eSchristos 		9.996,
17858585484eSchristos 		9.9996,
17868585484eSchristos 		9.99996,
17878585484eSchristos 		9.999996,
17888585484eSchristos 		9.9999996,
17898585484eSchristos 		9.99999996,
17908585484eSchristos 		0.99999996,
17918585484eSchristos 		0.99999999,
17928585484eSchristos 		0.09999999,
17938585484eSchristos 		0.00999999,
17948585484eSchristos 		0.00099999,
17958585484eSchristos 		0.00009999,
17968585484eSchristos 		0.00000999,
17978585484eSchristos 		0.00000099,
17988585484eSchristos 		0.00000009,
17998585484eSchristos 		0.00000001,
18008585484eSchristos 		0.0000001,
18018585484eSchristos 		0.000001,
18028585484eSchristos 		0.00001,
18038585484eSchristos 		0.0001,
18048585484eSchristos 		0.001,
18058585484eSchristos 		0.01,
18068585484eSchristos 		0.1,
18078585484eSchristos 		1.0,
18088585484eSchristos 		1.5,
18098585484eSchristos 		-1.5,
18108585484eSchristos 		-1.0,
18118585484eSchristos 		-0.1,
18128585484eSchristos #if !OS_BSD	/* BSD sometimes gets these wrong. */
18138585484eSchristos #ifdef INFINITY
18148585484eSchristos 		INFINITY,
18158585484eSchristos 		-INFINITY,
18168585484eSchristos #endif	/* defined(INFINITY) */
18178585484eSchristos #ifdef NAN
18188585484eSchristos 		NAN,
18198585484eSchristos #endif	/* defined(NAN) */
18208585484eSchristos #endif	/* !OS_BSD */
18218585484eSchristos 		0
18228585484eSchristos 	};
18238585484eSchristos 	const char *long_fmt[] = {
18248585484eSchristos 		"foo|%0123ld|bar",
18258585484eSchristos #if !OS_IRIX
18268585484eSchristos 		"% '0123ld",
18278585484eSchristos 		"%+'0123ld",
18288585484eSchristos 		"%-'123ld",
18298585484eSchristos 		"%'123ld",
18308585484eSchristos #endif	/* !OS_IRiX */
18318585484eSchristos 		"%123.9ld",
18328585484eSchristos 		"% 123.9ld",
18338585484eSchristos 		"%+123.9ld",
18348585484eSchristos 		"%-123.9ld",
18358585484eSchristos 		"%0123ld",
18368585484eSchristos 		"% 0123ld",
18378585484eSchristos 		"%+0123ld",
18388585484eSchristos 		"%-0123ld",
18398585484eSchristos 		"%10.5ld",
18408585484eSchristos 		"% 10.5ld",
18418585484eSchristos 		"%+10.5ld",
18428585484eSchristos 		"%-10.5ld",
18438585484eSchristos 		"%010ld",
18448585484eSchristos 		"% 010ld",
18458585484eSchristos 		"%+010ld",
18468585484eSchristos 		"%-010ld",
18478585484eSchristos 		"%4.2ld",
18488585484eSchristos 		"% 4.2ld",
18498585484eSchristos 		"%+4.2ld",
18508585484eSchristos 		"%-4.2ld",
18518585484eSchristos 		"%04ld",
18528585484eSchristos 		"% 04ld",
18538585484eSchristos 		"%+04ld",
18548585484eSchristos 		"%-04ld",
18558585484eSchristos 		"%5.5ld",
18568585484eSchristos 		"%+22.33ld",
18578585484eSchristos 		"%01.3ld",
18588585484eSchristos 		"%1.5ld",
18598585484eSchristos 		"%-1.5ld",
18608585484eSchristos 		"%44ld",
18618585484eSchristos 		"%4ld",
18628585484eSchristos 		"%4.0ld",
18638585484eSchristos 		"%4.ld",
18648585484eSchristos 		"%.44ld",
18658585484eSchristos 		"%.4ld",
18668585484eSchristos 		"%.0ld",
18678585484eSchristos 		"%.ld",
18688585484eSchristos 		"%ld",
18698585484eSchristos 		NULL
18708585484eSchristos 	};
18718585484eSchristos 	long int long_val[] = {
18728585484eSchristos #ifdef LONG_MAX
18738585484eSchristos 		LONG_MAX,
18748585484eSchristos #endif	/* LONG_MAX */
18758585484eSchristos #ifdef LONG_MIN
18768585484eSchristos 		LONG_MIN,
18778585484eSchristos #endif	/* LONG_MIN */
18788585484eSchristos 		-91340,
18798585484eSchristos 		91340,
18808585484eSchristos 		341,
18818585484eSchristos 		134,
18828585484eSchristos 		0203,
18838585484eSchristos 		-1,
18848585484eSchristos 		1,
18858585484eSchristos 		0
18868585484eSchristos 	};
18878585484eSchristos 	const char *ulong_fmt[] = {
18888585484eSchristos 		/* "%u" formats. */
18898585484eSchristos 		"foo|%0123lu|bar",
18908585484eSchristos #if !OS_IRIX
18918585484eSchristos 		"% '0123lu",
18928585484eSchristos 		"%+'0123lu",
18938585484eSchristos 		"%-'123lu",
18948585484eSchristos 		"%'123lu",
18958585484eSchristos #endif	/* !OS_IRiX */
18968585484eSchristos 		"%123.9lu",
18978585484eSchristos 		"% 123.9lu",
18988585484eSchristos 		"%+123.9lu",
18998585484eSchristos 		"%-123.9lu",
19008585484eSchristos 		"%0123lu",
19018585484eSchristos 		"% 0123lu",
19028585484eSchristos 		"%+0123lu",
19038585484eSchristos 		"%-0123lu",
19048585484eSchristos 		"%5.5lu",
19058585484eSchristos 		"%+22.33lu",
19068585484eSchristos 		"%01.3lu",
19078585484eSchristos 		"%1.5lu",
19088585484eSchristos 		"%-1.5lu",
19098585484eSchristos 		"%44lu",
19108585484eSchristos 		"%lu",
19118585484eSchristos 		/* "%o" formats. */
19128585484eSchristos 		"foo|%#0123lo|bar",
19138585484eSchristos 		"%#123.9lo",
19148585484eSchristos 		"%# 123.9lo",
19158585484eSchristos 		"%#+123.9lo",
19168585484eSchristos 		"%#-123.9lo",
19178585484eSchristos 		"%#0123lo",
19188585484eSchristos 		"%# 0123lo",
19198585484eSchristos 		"%#+0123lo",
19208585484eSchristos 		"%#-0123lo",
19218585484eSchristos 		"%#5.5lo",
19228585484eSchristos 		"%#+22.33lo",
19238585484eSchristos 		"%#01.3lo",
19248585484eSchristos 		"%#1.5lo",
19258585484eSchristos 		"%#-1.5lo",
19268585484eSchristos 		"%#44lo",
19278585484eSchristos 		"%#lo",
19288585484eSchristos 		"%123.9lo",
19298585484eSchristos 		"% 123.9lo",
19308585484eSchristos 		"%+123.9lo",
19318585484eSchristos 		"%-123.9lo",
19328585484eSchristos 		"%0123lo",
19338585484eSchristos 		"% 0123lo",
19348585484eSchristos 		"%+0123lo",
19358585484eSchristos 		"%-0123lo",
19368585484eSchristos 		"%5.5lo",
19378585484eSchristos 		"%+22.33lo",
19388585484eSchristos 		"%01.3lo",
19398585484eSchristos 		"%1.5lo",
19408585484eSchristos 		"%-1.5lo",
19418585484eSchristos 		"%44lo",
19428585484eSchristos 		"%lo",
19438585484eSchristos 		/* "%X" and "%x" formats. */
19448585484eSchristos 		"foo|%#0123lX|bar",
19458585484eSchristos 		"%#123.9lx",
19468585484eSchristos 		"%# 123.9lx",
19478585484eSchristos 		"%#+123.9lx",
19488585484eSchristos 		"%#-123.9lx",
19498585484eSchristos 		"%#0123lx",
19508585484eSchristos 		"%# 0123lx",
19518585484eSchristos 		"%#+0123lx",
19528585484eSchristos 		"%#-0123lx",
19538585484eSchristos 		"%#5.5lx",
19548585484eSchristos 		"%#+22.33lx",
19558585484eSchristos 		"%#01.3lx",
19568585484eSchristos 		"%#1.5lx",
19578585484eSchristos 		"%#-1.5lx",
19588585484eSchristos 		"%#44lx",
19598585484eSchristos 		"%#lx",
19608585484eSchristos 		"%#lX",
19618585484eSchristos 		"%123.9lx",
19628585484eSchristos 		"% 123.9lx",
19638585484eSchristos 		"%+123.9lx",
19648585484eSchristos 		"%-123.9lx",
19658585484eSchristos 		"%0123lx",
19668585484eSchristos 		"% 0123lx",
19678585484eSchristos 		"%+0123lx",
19688585484eSchristos 		"%-0123lx",
19698585484eSchristos 		"%5.5lx",
19708585484eSchristos 		"%+22.33lx",
19718585484eSchristos 		"%01.3lx",
19728585484eSchristos 		"%1.5lx",
19738585484eSchristos 		"%-1.5lx",
19748585484eSchristos 		"%44lx",
19758585484eSchristos 		"%lx",
19768585484eSchristos 		"%lX",
19778585484eSchristos 		NULL
19788585484eSchristos 	};
19798585484eSchristos 	unsigned long int ulong_val[] = {
19808585484eSchristos #ifdef ULONG_MAX
19818585484eSchristos 		ULONG_MAX,
19828585484eSchristos #endif	/* ULONG_MAX */
19838585484eSchristos 		91340,
19848585484eSchristos 		341,
19858585484eSchristos 		134,
19868585484eSchristos 		0203,
19878585484eSchristos 		1,
19888585484eSchristos 		0
19898585484eSchristos 	};
19908585484eSchristos 	const char *llong_fmt[] = {
19918585484eSchristos 		"foo|%0123lld|bar",
19928585484eSchristos 		"%123.9lld",
19938585484eSchristos 		"% 123.9lld",
19948585484eSchristos 		"%+123.9lld",
19958585484eSchristos 		"%-123.9lld",
19968585484eSchristos 		"%0123lld",
19978585484eSchristos 		"% 0123lld",
19988585484eSchristos 		"%+0123lld",
19998585484eSchristos 		"%-0123lld",
20008585484eSchristos 		"%5.5lld",
20018585484eSchristos 		"%+22.33lld",
20028585484eSchristos 		"%01.3lld",
20038585484eSchristos 		"%1.5lld",
20048585484eSchristos 		"%-1.5lld",
20058585484eSchristos 		"%44lld",
20068585484eSchristos 		"%lld",
20078585484eSchristos 		NULL
20088585484eSchristos 	};
20098585484eSchristos 	LLONG llong_val[] = {
20108585484eSchristos #ifdef LLONG_MAX
20118585484eSchristos 		LLONG_MAX,
20128585484eSchristos #endif	/* LLONG_MAX */
20138585484eSchristos #ifdef LLONG_MIN
20148585484eSchristos 		LLONG_MIN,
20158585484eSchristos #endif	/* LLONG_MIN */
20168585484eSchristos 		-91340,
20178585484eSchristos 		91340,
20188585484eSchristos 		341,
20198585484eSchristos 		134,
20208585484eSchristos 		0203,
20218585484eSchristos 		-1,
20228585484eSchristos 		1,
20238585484eSchristos 		0
20248585484eSchristos 	};
20258585484eSchristos 	const char *string_fmt[] = {
20268585484eSchristos 		"foo|%10.10s|bar",
20278585484eSchristos 		"%-10.10s",
20288585484eSchristos 		"%10.10s",
20298585484eSchristos 		"%10.5s",
20308585484eSchristos 		"%5.10s",
20318585484eSchristos 		"%10.1s",
20328585484eSchristos 		"%1.10s",
20338585484eSchristos 		"%10.0s",
20348585484eSchristos 		"%0.10s",
20358585484eSchristos 		"%-42.5s",
20368585484eSchristos 		"%2.s",
20378585484eSchristos 		"%.10s",
20388585484eSchristos 		"%.1s",
20398585484eSchristos 		"%.0s",
20408585484eSchristos 		"%.s",
20418585484eSchristos 		"%4s",
20428585484eSchristos 		"%s",
20438585484eSchristos 		NULL
20448585484eSchristos 	};
20458585484eSchristos 	const char *string_val[] = {
20468585484eSchristos 		"Hello",
20478585484eSchristos 		"Hello, world!",
20488585484eSchristos 		"Sound check: One, two, three.",
20498585484eSchristos 		"This string is a little longer than the other strings.",
20508585484eSchristos 		"1",
20518585484eSchristos 		"",
20528585484eSchristos 		NULL
20538585484eSchristos 	};
20548585484eSchristos #if !OS_SYSV	/* SysV uses a different format than we do. */
20558585484eSchristos 	const char *pointer_fmt[] = {
20568585484eSchristos 		"foo|%p|bar",
20578585484eSchristos 		"%42p",
20588585484eSchristos 		"%p",
20598585484eSchristos 		NULL
20608585484eSchristos 	};
20618585484eSchristos 	const char *pointer_val[] = {
20628585484eSchristos 		*pointer_fmt,
20638585484eSchristos 		*string_fmt,
20648585484eSchristos 		*string_val,
20658585484eSchristos 		NULL
20668585484eSchristos 	};
20678585484eSchristos #endif	/* !OS_SYSV */
20688585484eSchristos 	char buf1[1024], buf2[1024];
20698585484eSchristos 	double value, digits = 9.123456789012345678901234567890123456789;
20708585484eSchristos 	int i, j, r1, r2, failed = 0, num = 0;
20718585484eSchristos 
20728585484eSchristos /*
20738585484eSchristos  * Use -DTEST_NILS in order to also test the conversion of nil values.  Might
20748585484eSchristos  * segfault on systems which don't support converting a NULL pointer with "%s"
20758585484eSchristos  * and lets some test cases fail against BSD and glibc due to bugs in their
20768585484eSchristos  * implementations.
20778585484eSchristos  */
20788585484eSchristos #ifndef TEST_NILS
20798585484eSchristos #define TEST_NILS 0
20808585484eSchristos #elif TEST_NILS
20818585484eSchristos #undef TEST_NILS
20828585484eSchristos #define TEST_NILS 1
20838585484eSchristos #endif	/* !defined(TEST_NILS) */
20848585484eSchristos #ifdef TEST
20858585484eSchristos #undef TEST
20868585484eSchristos #endif	/* defined(TEST) */
20878585484eSchristos #define TEST(fmt, val)                                                         \
20888585484eSchristos do {                                                                           \
20898585484eSchristos 	for (i = 0; fmt[i] != NULL; i++)                                       \
20908585484eSchristos 		for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) {          \
20918585484eSchristos 			r1 = sprintf(buf1, fmt[i], val[j]);                    \
20928585484eSchristos 			r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]);     \
20938585484eSchristos 			if (strcmp(buf1, buf2) != 0 || r1 != r2) {             \
20948585484eSchristos 				(void)printf("Results don't match, "           \
20958585484eSchristos 				    "format string: %s\n"                      \
20968585484eSchristos 				    "\t sprintf(3): [%s] (%d)\n"               \
20978585484eSchristos 				    "\tsnprintf(3): [%s] (%d)\n",              \
20988585484eSchristos 				    fmt[i], buf1, r1, buf2, r2);               \
20998585484eSchristos 				failed++;                                      \
21008585484eSchristos 			}                                                      \
21018585484eSchristos 			num++;                                                 \
21028585484eSchristos 		}                                                              \
21038585484eSchristos } while (/* CONSTCOND */ 0)
21048585484eSchristos 
21058585484eSchristos #if HAVE_LOCALE_H
21068585484eSchristos 	(void)setlocale(LC_ALL, "");
21078585484eSchristos #endif	/* HAVE_LOCALE_H */
21088585484eSchristos 
21098585484eSchristos 	(void)puts("Testing our snprintf(3) against your system's sprintf(3).");
21108585484eSchristos 	TEST(float_fmt, float_val);
21118585484eSchristos 	TEST(long_fmt, long_val);
21128585484eSchristos 	TEST(ulong_fmt, ulong_val);
21138585484eSchristos 	TEST(llong_fmt, llong_val);
21148585484eSchristos 	TEST(string_fmt, string_val);
21158585484eSchristos #if !OS_SYSV	/* SysV uses a different format than we do. */
21168585484eSchristos 	TEST(pointer_fmt, pointer_val);
21178585484eSchristos #endif	/* !OS_SYSV */
21188585484eSchristos 	(void)printf("Result: %d out of %d tests failed.\n", failed, num);
21198585484eSchristos 
21208585484eSchristos 	(void)fputs("Checking how many digits we support: ", stdout);
21218585484eSchristos 	for (i = 0; i < 100; i++) {
21228585484eSchristos 		value = pow(10, i) * digits;
21238585484eSchristos 		(void)sprintf(buf1, "%.1f", value);
21248585484eSchristos 		(void)snprintf(buf2, sizeof(buf2), "%.1f", value);
21258585484eSchristos 		if (strcmp(buf1, buf2) != 0) {
21268585484eSchristos 			(void)printf("apparently %d.\n", i);
21278585484eSchristos 			break;
21288585484eSchristos 		}
21298585484eSchristos 	}
21308585484eSchristos 	return (failed == 0) ? 0 : 1;
21318585484eSchristos }
21328585484eSchristos #endif	/* TEST_SNPRINTF */
21338585484eSchristos 
21348585484eSchristos /* vim: set joinspaces textwidth=80: */
2135