Blame lib/snprintf.c

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