1280849Scy/*
2280849Scy * Modified by Dave Hart for integration into NTP 4.2.7 <hart@ntp.org>
3280849Scy *
4280849Scy * Changed in a backwards-incompatible way to separate HAVE_SNPRINTF
5280849Scy * from HW_WANT_RPL_SNPRINTF, etc. for each of the four replaced
6280849Scy * functions.
7280849Scy *
8280849Scy * Changed to honor hw_force_rpl_snprintf=yes, etc.  This is used by NTP
9280849Scy * to test rpl_snprintf() and rpl_vsnprintf() on platforms which provide
10280849Scy * C99-compliant implementations.
11280849Scy */
12280849Scy
13280849Scy/* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */
14280849Scy
15280849Scy/*
16280849Scy * Copyright (c) 1995 Patrick Powell.
17280849Scy *
18280849Scy * This code is based on code written by Patrick Powell <papowell@astart.com>.
19280849Scy * It may be used for any purpose as long as this notice remains intact on all
20280849Scy * source code distributions.
21280849Scy */
22280849Scy
23280849Scy/*
24280849Scy * Copyright (c) 2008 Holger Weiss.
25280849Scy *
26280849Scy * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
27280849Scy * My changes to the code may freely be used, modified and/or redistributed for
28280849Scy * any purpose.  It would be nice if additions and fixes to this file (including
29280849Scy * trivial code cleanups) would be sent back in order to let me include them in
30280849Scy * the version available at <http://www.jhweiss.de/software/snprintf.html>.
31280849Scy * However, this is not a requirement for using or redistributing (possibly
32280849Scy * modified) versions of this file, nor is leaving this notice intact mandatory.
33280849Scy */
34280849Scy
35280849Scy/*
36280849Scy * History
37280849Scy *
38280849Scy * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
39280849Scy *
40280849Scy * 	Fixed the detection of infinite floating point values on IRIX (and
41280849Scy * 	possibly other systems) and applied another few minor cleanups.
42280849Scy *
43280849Scy * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
44280849Scy *
45280849Scy * 	Added a lot of new features, fixed many bugs, and incorporated various
46280849Scy * 	improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
47280849Scy * 	<rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
48280849Scy * 	<djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
49280849Scy * 	projects.  The additions include: support the "e", "E", "g", "G", and
50280849Scy * 	"F" conversion specifiers (and use conversion style "f" or "F" for the
51280849Scy * 	still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
52280849Scy * 	"t", and "z" length modifiers; support the "#" flag and the (non-C99)
53280849Scy * 	"'" flag; use localeconv(3) (if available) to get both the current
54280849Scy * 	locale's decimal point character and the separator between groups of
55280849Scy * 	digits; fix the handling of various corner cases of field width and
56280849Scy * 	precision specifications; fix various floating point conversion bugs;
57280849Scy * 	handle infinite and NaN floating point values; don't attempt to write to
58280849Scy * 	the output buffer (which may be NULL) if a size of zero was specified;
59280849Scy * 	check for integer overflow of the field width, precision, and return
60280849Scy * 	values and during the floating point conversion; use the OUTCHAR() macro
61280849Scy * 	instead of a function for better performance; provide asprintf(3) and
62280849Scy * 	vasprintf(3) functions; add new test cases.  The replacement functions
63280849Scy * 	have been renamed to use an "rpl_" prefix, the function calls in the
64280849Scy * 	main project (and in this file) must be redefined accordingly for each
65280849Scy * 	replacement function which is needed (by using Autoconf or other means).
66280849Scy * 	Various other minor improvements have been applied and the coding style
67280849Scy * 	was cleaned up for consistency.
68280849Scy *
69280849Scy * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
70280849Scy *
71280849Scy * 	C99 compliant snprintf(3) and vsnprintf(3) functions return the number
72280849Scy * 	of characters that would have been written to a sufficiently sized
73280849Scy * 	buffer (excluding the '\0').  The original code simply returned the
74280849Scy * 	length of the resulting output string, so that's been fixed.
75280849Scy *
76280849Scy * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
77280849Scy *
78280849Scy * 	The original code assumed that both snprintf(3) and vsnprintf(3) were
79280849Scy * 	missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
80280849Scy * 	the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
81280849Scy *
82280849Scy * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
83280849Scy *
84280849Scy * 	The PGP code was using unsigned hexadecimal formats.  Unfortunately,
85280849Scy * 	unsigned formats simply didn't work.
86280849Scy *
87280849Scy * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
88280849Scy *
89280849Scy * 	Ok, added some minimal floating point support, which means this probably
90280849Scy * 	requires libm on most operating systems.  Don't yet support the exponent
91280849Scy * 	(e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
92280849Scy * 	wasn't being exercised in ways which showed it, so that's been fixed.
93280849Scy * 	Also, formatted the code to Mutt conventions, and removed dead code left
94280849Scy * 	over from the original.  Also, there is now a builtin-test, run with:
95280849Scy * 	gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
96280849Scy *
97280849Scy * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
98280849Scy *
99280849Scy * 	This was ugly.  It is still ugly.  I opted out of floating point
100280849Scy * 	numbers, but the formatter understands just about everything from the
101280849Scy * 	normal C string format, at least as far as I can tell from the Solaris
102280849Scy * 	2.5 printf(3S) man page.
103280849Scy */
104280849Scy
105280849Scy/*
106280849Scy * ToDo
107280849Scy *
108280849Scy * - Add wide character support.
109280849Scy * - Add support for "%a" and "%A" conversions.
110280849Scy * - Create test routines which predefine the expected results.  Our test cases
111280849Scy *   usually expose bugs in system implementations rather than in ours :-)
112280849Scy */
113280849Scy
114280849Scy/*
115280849Scy * Usage
116280849Scy *
117280849Scy * 1) The following preprocessor macros should be defined to 1 if the feature or
118280849Scy *    file in question is available on the target system (by using Autoconf or
119280849Scy *    other means), though basic functionality should be available as long as
120280849Scy *    HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
121280849Scy *
122280849Scy *	HW_WANT_RPL_VSNPRINTF
123280849Scy *	HW_WANT_RPL_SNPRINTF
124280849Scy *	HW_WANT_RPL_VASPRINTF
125280849Scy *	HW_WANT_RPL_ASPRINTF
126280849Scy *	HAVE_VSNPRINTF	// define to 1 #if HW_WANT_RPL_VSNPRINTF
127280849Scy *	HAVE_SNPRINTF	// define to 1 #if HW_WANT_RPL_SNPRINTF
128280849Scy *	HAVE_VASPRINTF	// define to 1 #if HW_WANT_RPL_VASPRINTF
129280849Scy *	HAVE_ASPRINTF	// define to 1 #if HW_WANT_RPL_ASPRINTF
130280849Scy *	HAVE_STDARG_H
131280849Scy *	HAVE_STDDEF_H
132280849Scy *	HAVE_STDINT_H
133280849Scy *	HAVE_STDLIB_H
134280849Scy *	HAVE_INTTYPES_H
135280849Scy *	HAVE_LOCALE_H
136280849Scy *	HAVE_LOCALECONV
137280849Scy *	HAVE_LCONV_DECIMAL_POINT
138280849Scy *	HAVE_LCONV_THOUSANDS_SEP
139280849Scy *	HAVE_LONG_DOUBLE
140280849Scy *	HAVE_LONG_LONG_INT
141280849Scy *	HAVE_UNSIGNED_LONG_LONG_INT
142280849Scy *	HAVE_INTMAX_T
143280849Scy *	HAVE_UINTMAX_T
144280849Scy *	HAVE_UINTPTR_T
145280849Scy *	HAVE_PTRDIFF_T
146280849Scy *	HAVE_VA_COPY
147280849Scy *	HAVE___VA_COPY
148280849Scy *
149280849Scy * 2) The calls to the functions which should be replaced must be redefined
150280849Scy *    throughout the project files (by using Autoconf or other means):
151280849Scy *
152280849Scy *	#if HW_WANT_RPL_VSNPRINTF
153280849Scy *	#define vsnprintf rpl_vsnprintf
154280849Scy *	#endif
155280849Scy *	#if HW_WANT_RPL_SNPRINTF
156280849Scy *	#define snprintf rpl_snprintf
157280849Scy *	#endif
158280849Scy *	#if HW_WANT_RPL_VASPRINTF
159280849Scy *	#define vasprintf rpl_vasprintf
160280849Scy *	#endif
161280849Scy *	#if HW_WANT_RPL_ASPRINTF
162280849Scy *	#define asprintf rpl_asprintf
163280849Scy *	#endif
164280849Scy *
165280849Scy * 3) The required replacement functions should be declared in some header file
166280849Scy *    included throughout the project files:
167280849Scy *
168280849Scy *	#if HAVE_CONFIG_H
169280849Scy *	#include <config.h>
170280849Scy *	#endif
171280849Scy *	#if HAVE_STDARG_H
172280849Scy *	#include <stdarg.h>
173280849Scy *	#if HW_WANT_RPL_VSNPRINTF
174280849Scy *	int rpl_vsnprintf(char *, size_t, const char *, va_list);
175280849Scy *	#endif
176280849Scy *	#if HW_WANT_RPL_SNPRINTF
177280849Scy *	int rpl_snprintf(char *, size_t, const char *, ...);
178280849Scy *	#endif
179280849Scy *	#if HW_WANT_RPL_VASPRINTF
180280849Scy *	int rpl_vasprintf(char **, const char *, va_list);
181280849Scy *	#endif
182280849Scy *	#if HW_WANT_RPL_ASPRINTF
183280849Scy *	int rpl_asprintf(char **, const char *, ...);
184280849Scy *	#endif
185280849Scy *	#endif
186280849Scy *
187280849Scy * Autoconf macros for handling step 1 and step 2 are available at
188280849Scy * <http://www.jhweiss.de/software/snprintf.html>.
189280849Scy */
190280849Scy
191280849Scy#if HAVE_CONFIG_H
19282498Sroberto#include <config.h>
193280849Scy#endif	/* HAVE_CONFIG_H */
19482498Sroberto
195280849Scy#if TEST_SNPRINTF
196280849Scy#include <math.h>	/* For pow(3), NAN, and INFINITY. */
197280849Scy#include <string.h>	/* For strcmp(3). */
198280849Scy#if defined(__NetBSD__) || \
199280849Scy    defined(__FreeBSD__) || \
200280849Scy    defined(__OpenBSD__) || \
201280849Scy    defined(__NeXT__) || \
202280849Scy    defined(__bsd__)
203280849Scy#define OS_BSD 1
204280849Scy#elif defined(sgi) || defined(__sgi)
205280849Scy#ifndef __c99
206280849Scy#define __c99	/* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
207280849Scy#endif	/* !defined(__c99) */
208280849Scy#define OS_IRIX 1
209280849Scy#define OS_SYSV 1
210280849Scy#elif defined(__svr4__)
211280849Scy#define OS_SYSV 1
212280849Scy#elif defined(__linux__)
213280849Scy#define OS_LINUX 1
214280849Scy#endif	/* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
215280849Scy#if HAVE_CONFIG_H	/* Undefine definitions possibly done in config.h. */
216280849Scy#ifdef HAVE_SNPRINTF
217280849Scy#undef HAVE_SNPRINTF
218280849Scy#endif	/* defined(HAVE_SNPRINTF) */
219280849Scy#ifdef HAVE_VSNPRINTF
220280849Scy#undef HAVE_VSNPRINTF
221280849Scy#endif	/* defined(HAVE_VSNPRINTF) */
222280849Scy#ifdef HAVE_ASPRINTF
223280849Scy#undef HAVE_ASPRINTF
224280849Scy#endif	/* defined(HAVE_ASPRINTF) */
225280849Scy#ifdef HAVE_VASPRINTF
226280849Scy#undef HAVE_VASPRINTF
227280849Scy#endif	/* defined(HAVE_VASPRINTF) */
228280849Scy#ifdef snprintf
229280849Scy#undef snprintf
230280849Scy#endif	/* defined(snprintf) */
231280849Scy#ifdef vsnprintf
232280849Scy#undef vsnprintf
233280849Scy#endif	/* defined(vsnprintf) */
234280849Scy#ifdef asprintf
235280849Scy#undef asprintf
236280849Scy#endif	/* defined(asprintf) */
237280849Scy#ifdef vasprintf
238280849Scy#undef vasprintf
239280849Scy#endif	/* defined(vasprintf) */
240280849Scy#else	/* By default, we assume a modern system for testing. */
241280849Scy#ifndef HAVE_STDARG_H
242280849Scy#define HAVE_STDARG_H 1
243280849Scy#endif	/* HAVE_STDARG_H */
244280849Scy#ifndef HAVE_STDDEF_H
245280849Scy#define HAVE_STDDEF_H 1
246280849Scy#endif	/* HAVE_STDDEF_H */
247280849Scy#ifndef HAVE_STDINT_H
248280849Scy#define HAVE_STDINT_H 1
249280849Scy#endif	/* HAVE_STDINT_H */
250280849Scy#ifndef HAVE_STDLIB_H
251280849Scy#define HAVE_STDLIB_H 1
252280849Scy#endif	/* HAVE_STDLIB_H */
253280849Scy#ifndef HAVE_INTTYPES_H
254280849Scy#define HAVE_INTTYPES_H 1
255280849Scy#endif	/* HAVE_INTTYPES_H */
256280849Scy#ifndef HAVE_LOCALE_H
257280849Scy#define HAVE_LOCALE_H 1
258280849Scy#endif	/* HAVE_LOCALE_H */
259280849Scy#ifndef HAVE_LOCALECONV
260280849Scy#define HAVE_LOCALECONV 1
261280849Scy#endif	/* !defined(HAVE_LOCALECONV) */
262280849Scy#ifndef HAVE_LCONV_DECIMAL_POINT
263280849Scy#define HAVE_LCONV_DECIMAL_POINT 1
264280849Scy#endif	/* HAVE_LCONV_DECIMAL_POINT */
265280849Scy#ifndef HAVE_LCONV_THOUSANDS_SEP
266280849Scy#define HAVE_LCONV_THOUSANDS_SEP 1
267280849Scy#endif	/* HAVE_LCONV_THOUSANDS_SEP */
268280849Scy#ifndef HAVE_LONG_DOUBLE
269280849Scy#define HAVE_LONG_DOUBLE 1
270280849Scy#endif	/* !defined(HAVE_LONG_DOUBLE) */
271280849Scy#ifndef HAVE_LONG_LONG_INT
272280849Scy#define HAVE_LONG_LONG_INT 1
273280849Scy#endif	/* !defined(HAVE_LONG_LONG_INT) */
274280849Scy#ifndef HAVE_UNSIGNED_LONG_LONG_INT
275280849Scy#define HAVE_UNSIGNED_LONG_LONG_INT 1
276280849Scy#endif	/* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
277280849Scy#ifndef HAVE_INTMAX_T
278280849Scy#define HAVE_INTMAX_T 1
279280849Scy#endif	/* !defined(HAVE_INTMAX_T) */
280280849Scy#ifndef HAVE_UINTMAX_T
281280849Scy#define HAVE_UINTMAX_T 1
282280849Scy#endif	/* !defined(HAVE_UINTMAX_T) */
283280849Scy#ifndef HAVE_UINTPTR_T
284280849Scy#define HAVE_UINTPTR_T 1
285280849Scy#endif	/* !defined(HAVE_UINTPTR_T) */
286280849Scy#ifndef HAVE_PTRDIFF_T
287280849Scy#define HAVE_PTRDIFF_T 1
288280849Scy#endif	/* !defined(HAVE_PTRDIFF_T) */
289280849Scy#ifndef HAVE_VA_COPY
290280849Scy#define HAVE_VA_COPY 1
291280849Scy#endif	/* !defined(HAVE_VA_COPY) */
292280849Scy#ifndef HAVE___VA_COPY
293280849Scy#define HAVE___VA_COPY 1
294280849Scy#endif	/* !defined(HAVE___VA_COPY) */
295280849Scy#endif	/* HAVE_CONFIG_H */
296280849Scy#define snprintf rpl_snprintf
297280849Scy#define vsnprintf rpl_vsnprintf
298280849Scy#define asprintf rpl_asprintf
299280849Scy#define vasprintf rpl_vasprintf
300280849Scy#endif	/* TEST_SNPRINTF */
30182498Sroberto
302280849Scy#if HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || HW_WANT_RPL_VASPRINTF
303280849Scy#include <stdio.h>	/* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
304280849Scy#ifdef VA_START
305280849Scy#undef VA_START
306280849Scy#endif	/* defined(VA_START) */
307280849Scy#ifdef VA_SHIFT
308280849Scy#undef VA_SHIFT
309280849Scy#endif	/* defined(VA_SHIFT) */
310280849Scy#if HAVE_STDARG_H
31182498Sroberto#include <stdarg.h>
312280849Scy#define VA_START(ap, last) va_start(ap, last)
313280849Scy#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
314280849Scy#else	/* Assume <varargs.h> is available. */
31582498Sroberto#include <varargs.h>
316280849Scy#define VA_START(ap, last) va_start(ap)	/* "last" is ignored. */
317280849Scy#define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
318280849Scy#endif	/* HAVE_STDARG_H */
31982498Sroberto
320280849Scy#if HW_WANT_RPL_VASPRINTF
321280849Scy#if HAVE_STDLIB_H
322280849Scy#include <stdlib.h>	/* For malloc(3). */
323280849Scy#endif	/* HAVE_STDLIB_H */
324280849Scy#ifdef VA_COPY
325280849Scy#undef VA_COPY
326280849Scy#endif	/* defined(VA_COPY) */
327280849Scy#ifdef VA_END_COPY
328280849Scy#undef VA_END_COPY
329280849Scy#endif	/* defined(VA_END_COPY) */
330280849Scy#if HAVE_VA_COPY
331280849Scy#define VA_COPY(dest, src) va_copy(dest, src)
332280849Scy#define VA_END_COPY(ap) va_end(ap)
333280849Scy#elif HAVE___VA_COPY
334280849Scy#define VA_COPY(dest, src) __va_copy(dest, src)
335280849Scy#define VA_END_COPY(ap) va_end(ap)
336280849Scy#else
337280849Scy#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
338280849Scy#define VA_END_COPY(ap) /* No-op. */
339280849Scy#define NEED_MYMEMCPY 1
340280849Scystatic void *mymemcpy(void *, void *, size_t);
341280849Scy#endif	/* HAVE_VA_COPY */
342280849Scy#endif	/* HW_WANT_RPL_VASPRINTF */
343132451Sroberto
344280849Scy#if HW_WANT_RPL_VSNPRINTF
345280849Scy#include <errno.h>	/* For ERANGE and errno. */
346280849Scy#include <limits.h>	/* For *_MAX. */
347280849Scy#if HAVE_INTTYPES_H
348280849Scy#include <inttypes.h>	/* For intmax_t (if not defined in <stdint.h>). */
349280849Scy#endif	/* HAVE_INTTYPES_H */
350280849Scy#if HAVE_LOCALE_H
351280849Scy#include <locale.h>	/* For localeconv(3). */
352280849Scy#endif	/* HAVE_LOCALE_H */
353280849Scy#if HAVE_STDDEF_H
354280849Scy#include <stddef.h>	/* For ptrdiff_t. */
355280849Scy#endif	/* HAVE_STDDEF_H */
356280849Scy#if HAVE_STDINT_H
357280849Scy#include <stdint.h>	/* For intmax_t. */
358280849Scy#endif	/* HAVE_STDINT_H */
359280849Scy
360280849Scy/* Support for unsigned long long int.  We may also need ULLONG_MAX. */
361280849Scy#ifndef ULONG_MAX	/* We may need ULONG_MAX as a fallback. */
362280849Scy#ifdef UINT_MAX
363280849Scy#define ULONG_MAX UINT_MAX
36482498Sroberto#else
365280849Scy#define ULONG_MAX INT_MAX
366280849Scy#endif	/* defined(UINT_MAX) */
367280849Scy#endif	/* !defined(ULONG_MAX) */
368280849Scy#ifdef ULLONG
369280849Scy#undef ULLONG
370280849Scy#endif	/* defined(ULLONG) */
371280849Scy#if HAVE_UNSIGNED_LONG_LONG_INT
372280849Scy#define ULLONG unsigned long long int
373280849Scy#ifndef ULLONG_MAX
374280849Scy#define ULLONG_MAX ULONG_MAX
375280849Scy#endif	/* !defined(ULLONG_MAX) */
376280849Scy#else
377280849Scy#define ULLONG unsigned long int
378280849Scy#ifdef ULLONG_MAX
379280849Scy#undef ULLONG_MAX
380280849Scy#endif	/* defined(ULLONG_MAX) */
381280849Scy#define ULLONG_MAX ULONG_MAX
382280849Scy#endif	/* HAVE_LONG_LONG_INT */
383280849Scy
384280849Scy/* Support for uintmax_t.  We also need UINTMAX_MAX. */
385280849Scy#ifdef UINTMAX_T
386280849Scy#undef UINTMAX_T
387280849Scy#endif	/* defined(UINTMAX_T) */
388280849Scy#if HAVE_UINTMAX_T || defined(uintmax_t)
389280849Scy#define UINTMAX_T uintmax_t
390280849Scy#ifndef UINTMAX_MAX
391280849Scy#define UINTMAX_MAX ULLONG_MAX
392280849Scy#endif	/* !defined(UINTMAX_MAX) */
393280849Scy#else
394280849Scy#define UINTMAX_T ULLONG
395280849Scy#ifdef UINTMAX_MAX
396280849Scy#undef UINTMAX_MAX
397280849Scy#endif	/* defined(UINTMAX_MAX) */
398280849Scy#define UINTMAX_MAX ULLONG_MAX
399280849Scy#endif	/* HAVE_UINTMAX_T || defined(uintmax_t) */
400280849Scy
401280849Scy/* Support for long double. */
402280849Scy#ifndef LDOUBLE
403280849Scy#if HAVE_LONG_DOUBLE
404280849Scy#define LDOUBLE long double
405280849Scy#else
406280849Scy#define LDOUBLE double
407280849Scy#endif	/* HAVE_LONG_DOUBLE */
408280849Scy#endif	/* !defined(LDOUBLE) */
409280849Scy
410280849Scy/* Support for long long int. */
411280849Scy#ifndef LLONG
412280849Scy#if HAVE_LONG_LONG_INT
413280849Scy#define LLONG long long int
414280849Scy#else
415280849Scy#define LLONG long int
416280849Scy#endif	/* HAVE_LONG_LONG_INT */
417280849Scy#endif	/* !defined(LLONG) */
418280849Scy
419280849Scy/* Support for intmax_t. */
420280849Scy#ifndef INTMAX_T
421280849Scy#if HAVE_INTMAX_T || defined(intmax_t)
422280849Scy#define INTMAX_T intmax_t
423280849Scy#else
424280849Scy#define INTMAX_T LLONG
425280849Scy#endif	/* HAVE_INTMAX_T || defined(intmax_t) */
426280849Scy#endif	/* !defined(INTMAX_T) */
427280849Scy
428280849Scy/* Support for uintptr_t. */
429280849Scy#ifndef UINTPTR_T
430280849Scy#if HAVE_UINTPTR_T || defined(uintptr_t)
431280849Scy#define UINTPTR_T uintptr_t
432280849Scy#else
433280849Scy#define UINTPTR_T unsigned long int
434280849Scy#endif	/* HAVE_UINTPTR_T || defined(uintptr_t) */
435280849Scy#endif	/* !defined(UINTPTR_T) */
436280849Scy
437280849Scy/* Support for ptrdiff_t. */
438280849Scy#ifndef PTRDIFF_T
439280849Scy#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
440280849Scy#define PTRDIFF_T ptrdiff_t
441280849Scy#else
442280849Scy#define PTRDIFF_T long int
443280849Scy#endif	/* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
444280849Scy#endif	/* !defined(PTRDIFF_T) */
445280849Scy
446280849Scy/*
447280849Scy * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
448280849Scy * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
449280849Scy * unsigned type if necessary.  This should work just fine in practice.
450280849Scy */
451280849Scy#ifndef UPTRDIFF_T
452280849Scy#define UPTRDIFF_T PTRDIFF_T
453280849Scy#endif	/* !defined(UPTRDIFF_T) */
454280849Scy
455280849Scy/*
456280849Scy * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
457280849Scy * However, we'll simply use size_t and convert it to a signed type if
458280849Scy * necessary.  This should work just fine in practice.
459280849Scy */
460280849Scy#ifndef SSIZE_T
461280849Scy#define SSIZE_T size_t
462280849Scy#endif	/* !defined(SSIZE_T) */
463280849Scy
464280849Scy/* Either ERANGE or E2BIG should be available everywhere. */
465280849Scy#ifndef ERANGE
466280849Scy#define ERANGE E2BIG
467280849Scy#endif	/* !defined(ERANGE) */
468280849Scy#ifndef EOVERFLOW
469280849Scy#define EOVERFLOW ERANGE
470280849Scy#endif	/* !defined(EOVERFLOW) */
471280849Scy
472280849Scy/*
473280849Scy * Buffer size to hold the octal string representation of UINT128_MAX without
474280849Scy * nul-termination ("3777777777777777777777777777777777777777777").
475280849Scy */
476280849Scy#ifdef MAX_CONVERT_LENGTH
477280849Scy#undef MAX_CONVERT_LENGTH
478280849Scy#endif	/* defined(MAX_CONVERT_LENGTH) */
479280849Scy#define MAX_CONVERT_LENGTH      43
480280849Scy
481280849Scy/* Format read states. */
482280849Scy#define PRINT_S_DEFAULT         0
483280849Scy#define PRINT_S_FLAGS           1
484280849Scy#define PRINT_S_WIDTH           2
485280849Scy#define PRINT_S_DOT             3
486280849Scy#define PRINT_S_PRECISION       4
487280849Scy#define PRINT_S_MOD             5
488280849Scy#define PRINT_S_CONV            6
489280849Scy
490280849Scy/* Format flags. */
491280849Scy#define PRINT_F_MINUS           (1 << 0)
492280849Scy#define PRINT_F_PLUS            (1 << 1)
493280849Scy#define PRINT_F_SPACE           (1 << 2)
494280849Scy#define PRINT_F_NUM             (1 << 3)
495280849Scy#define PRINT_F_ZERO            (1 << 4)
496280849Scy#define PRINT_F_QUOTE           (1 << 5)
497280849Scy#define PRINT_F_UP              (1 << 6)
498280849Scy#define PRINT_F_UNSIGNED        (1 << 7)
499280849Scy#define PRINT_F_TYPE_G          (1 << 8)
500280849Scy#define PRINT_F_TYPE_E          (1 << 9)
501280849Scy
502280849Scy/* Conversion flags. */
503280849Scy#define PRINT_C_CHAR            1
504280849Scy#define PRINT_C_SHORT           2
505280849Scy#define PRINT_C_LONG            3
506280849Scy#define PRINT_C_LLONG           4
507280849Scy#define PRINT_C_LDOUBLE         5
508280849Scy#define PRINT_C_SIZE            6
509280849Scy#define PRINT_C_PTRDIFF         7
510280849Scy#define PRINT_C_INTMAX          8
511280849Scy
512280849Scy#ifndef MAX
513280849Scy#define MAX(x, y) ((x >= y) ? x : y)
514280849Scy#endif	/* !defined(MAX) */
515280849Scy#ifndef CHARTOINT
516280849Scy#define CHARTOINT(ch) (ch - '0')
517280849Scy#endif	/* !defined(CHARTOINT) */
518280849Scy#ifndef ISDIGIT
519280849Scy#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
520280849Scy#endif	/* !defined(ISDIGIT) */
521280849Scy#ifndef ISNAN
522280849Scy#define ISNAN(x) (x != x)
523280849Scy#endif	/* !defined(ISNAN) */
524280849Scy#ifndef ISINF
525280849Scy#define ISINF(x) (x != 0.0 && x + x == x)
526280849Scy#endif	/* !defined(ISINF) */
527280849Scy
528280849Scy#ifdef OUTCHAR
529280849Scy#undef OUTCHAR
530280849Scy#endif	/* defined(OUTCHAR) */
531280849Scy#define OUTCHAR(str, len, size, ch)                                          \
532280849Scydo {                                                                         \
533280849Scy	if (len + 1 < size)                                                  \
534280849Scy		str[len] = ch;                                               \
535280849Scy	(len)++;                                                             \
536280849Scy} while (/* CONSTCOND */ 0)
537280849Scy
538280849Scystatic void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
539280849Scystatic void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
540280849Scystatic void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
541280849Scystatic void printsep(char *, size_t *, size_t);
542280849Scystatic int getnumsep(int);
543280849Scystatic int getexponent(LDOUBLE);
544280849Scystatic int convert(UINTMAX_T, char *, size_t, int, int);
545280849Scystatic UINTMAX_T cast(LDOUBLE);
546280849Scystatic UINTMAX_T myround(LDOUBLE);
547280849Scystatic LDOUBLE mypow10(int);
548280849Scy
549280849Scyint
550280849Scyrpl_vsnprintf(char *str, size_t size, const char *format, va_list args);
551280849Scy
552280849Scyint
553280849Scyrpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
554280849Scy{
555280849Scy	LDOUBLE fvalue;
556280849Scy	INTMAX_T value;
557280849Scy	unsigned char cvalue;
558280849Scy	const char *strvalue;
559280849Scy	INTMAX_T *intmaxptr;
560280849Scy	PTRDIFF_T *ptrdiffptr;
561280849Scy	SSIZE_T *sizeptr;
562280849Scy	LLONG *llongptr;
563280849Scy	long int *longptr;
564280849Scy	int *intptr;
565280849Scy	short int *shortptr;
566280849Scy	signed char *charptr;
567280849Scy	size_t len = 0;
568280849Scy	int overflow = 0;
569280849Scy	int base = 0;
570280849Scy	int cflags = 0;
571280849Scy	int flags = 0;
572280849Scy	int width = 0;
573280849Scy	int precision = -1;
574280849Scy	int state = PRINT_S_DEFAULT;
575280849Scy	char ch = *format++;
576280849Scy
577280849Scy	/*
578280849Scy	 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
579280849Scy	 * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
580280849Scy	 * even if a size larger than zero was specified.  At least NetBSD's
581280849Scy	 * snprintf(3) does the same, as well as other versions of this file.
582280849Scy	 * (Though some of these versions will write to a non-NULL buffer even
583280849Scy	 * if a size of zero was specified, which violates the standard.)
584280849Scy	 */
585280849Scy	if (str == NULL && size != 0)
586280849Scy		size = 0;
587280849Scy
588280849Scy	while (ch != '\0')
589280849Scy		switch (state) {
590280849Scy		case PRINT_S_DEFAULT:
591280849Scy			if (ch == '%')
592280849Scy				state = PRINT_S_FLAGS;
593280849Scy			else
594280849Scy				OUTCHAR(str, len, size, ch);
595280849Scy			ch = *format++;
596280849Scy			break;
597280849Scy		case PRINT_S_FLAGS:
598280849Scy			switch (ch) {
599280849Scy			case '-':
600280849Scy				flags |= PRINT_F_MINUS;
601280849Scy				ch = *format++;
602280849Scy				break;
603280849Scy			case '+':
604280849Scy				flags |= PRINT_F_PLUS;
605280849Scy				ch = *format++;
606280849Scy				break;
607280849Scy			case ' ':
608280849Scy				flags |= PRINT_F_SPACE;
609280849Scy				ch = *format++;
610280849Scy				break;
611280849Scy			case '#':
612280849Scy				flags |= PRINT_F_NUM;
613280849Scy				ch = *format++;
614280849Scy				break;
615280849Scy			case '0':
616280849Scy				flags |= PRINT_F_ZERO;
617280849Scy				ch = *format++;
618280849Scy				break;
619280849Scy			case '\'':	/* SUSv2 flag (not in C99). */
620280849Scy				flags |= PRINT_F_QUOTE;
621280849Scy				ch = *format++;
622280849Scy				break;
623280849Scy			default:
624280849Scy				state = PRINT_S_WIDTH;
625280849Scy				break;
626280849Scy			}
627280849Scy			break;
628280849Scy		case PRINT_S_WIDTH:
629280849Scy			if (ISDIGIT(ch)) {
630280849Scy				ch = CHARTOINT(ch);
631280849Scy				if (width > (INT_MAX - ch) / 10) {
632280849Scy					overflow = 1;
633280849Scy					goto out;
634280849Scy				}
635280849Scy				width = 10 * width + ch;
636280849Scy				ch = *format++;
637280849Scy			} else if (ch == '*') {
638280849Scy				/*
639280849Scy				 * C99 says: "A negative field width argument is
640280849Scy				 * taken as a `-' flag followed by a positive
641280849Scy				 * field width." (7.19.6.1, 5)
642280849Scy				 */
643280849Scy				if ((width = va_arg(args, int)) < 0) {
644280849Scy					flags |= PRINT_F_MINUS;
645280849Scy					width = -width;
646280849Scy				}
647280849Scy				ch = *format++;
648280849Scy				state = PRINT_S_DOT;
649280849Scy			} else
650280849Scy				state = PRINT_S_DOT;
651280849Scy			break;
652280849Scy		case PRINT_S_DOT:
653280849Scy			if (ch == '.') {
654280849Scy				state = PRINT_S_PRECISION;
655280849Scy				ch = *format++;
656280849Scy			} else
657280849Scy				state = PRINT_S_MOD;
658280849Scy			break;
659280849Scy		case PRINT_S_PRECISION:
660280849Scy			if (precision == -1)
661280849Scy				precision = 0;
662280849Scy			if (ISDIGIT(ch)) {
663280849Scy				ch = CHARTOINT(ch);
664280849Scy				if (precision > (INT_MAX - ch) / 10) {
665280849Scy					overflow = 1;
666280849Scy					goto out;
667280849Scy				}
668280849Scy				precision = 10 * precision + ch;
669280849Scy				ch = *format++;
670280849Scy			} else if (ch == '*') {
671280849Scy				/*
672280849Scy				 * C99 says: "A negative precision argument is
673280849Scy				 * taken as if the precision were omitted."
674280849Scy				 * (7.19.6.1, 5)
675280849Scy				 */
676280849Scy				if ((precision = va_arg(args, int)) < 0)
677280849Scy					precision = -1;
678280849Scy				ch = *format++;
679280849Scy				state = PRINT_S_MOD;
680280849Scy			} else
681280849Scy				state = PRINT_S_MOD;
682280849Scy			break;
683280849Scy		case PRINT_S_MOD:
684280849Scy			switch (ch) {
685280849Scy			case 'h':
686280849Scy				ch = *format++;
687280849Scy				if (ch == 'h') {	/* It's a char. */
688280849Scy					ch = *format++;
689280849Scy					cflags = PRINT_C_CHAR;
690280849Scy				} else
691280849Scy					cflags = PRINT_C_SHORT;
692280849Scy				break;
693280849Scy			case 'l':
694280849Scy				ch = *format++;
695280849Scy				if (ch == 'l') {	/* It's a long long. */
696280849Scy					ch = *format++;
697280849Scy					cflags = PRINT_C_LLONG;
698280849Scy				} else
699280849Scy					cflags = PRINT_C_LONG;
700280849Scy				break;
701280849Scy			case 'L':
702280849Scy				cflags = PRINT_C_LDOUBLE;
703280849Scy				ch = *format++;
704280849Scy				break;
705280849Scy			case 'j':
706280849Scy				cflags = PRINT_C_INTMAX;
707280849Scy				ch = *format++;
708280849Scy				break;
709280849Scy			case 't':
710280849Scy				cflags = PRINT_C_PTRDIFF;
711280849Scy				ch = *format++;
712280849Scy				break;
713280849Scy			case 'z':
714280849Scy				cflags = PRINT_C_SIZE;
715280849Scy				ch = *format++;
716280849Scy				break;
717280849Scy			}
718280849Scy			state = PRINT_S_CONV;
719280849Scy			break;
720280849Scy		case PRINT_S_CONV:
721280849Scy			switch (ch) {
722280849Scy			case 'd':
723280849Scy				/* FALLTHROUGH */
724280849Scy			case 'i':
725280849Scy				switch (cflags) {
726280849Scy				case PRINT_C_CHAR:
727280849Scy					value = (signed char)va_arg(args, int);
728280849Scy					break;
729280849Scy				case PRINT_C_SHORT:
730280849Scy					value = (short int)va_arg(args, int);
731280849Scy					break;
732280849Scy				case PRINT_C_LONG:
733280849Scy					value = va_arg(args, long int);
734280849Scy					break;
735280849Scy				case PRINT_C_LLONG:
736280849Scy					value = va_arg(args, LLONG);
737280849Scy					break;
738280849Scy				case PRINT_C_SIZE:
739280849Scy					value = va_arg(args, SSIZE_T);
740280849Scy					break;
741280849Scy				case PRINT_C_INTMAX:
742280849Scy					value = va_arg(args, INTMAX_T);
743280849Scy					break;
744280849Scy				case PRINT_C_PTRDIFF:
745280849Scy					value = va_arg(args, PTRDIFF_T);
746280849Scy					break;
747280849Scy				default:
748280849Scy					value = va_arg(args, int);
749280849Scy					break;
750280849Scy				}
751280849Scy				fmtint(str, &len, size, value, 10, width,
752280849Scy				    precision, flags);
753280849Scy				break;
754280849Scy			case 'X':
755280849Scy				flags |= PRINT_F_UP;
756280849Scy				/* FALLTHROUGH */
757280849Scy			case 'x':
758280849Scy				base = 16;
759280849Scy				/* FALLTHROUGH */
760280849Scy			case 'o':
761280849Scy				if (base == 0)
762280849Scy					base = 8;
763280849Scy				/* FALLTHROUGH */
764280849Scy			case 'u':
765280849Scy				if (base == 0)
766280849Scy					base = 10;
767280849Scy				flags |= PRINT_F_UNSIGNED;
768280849Scy				switch (cflags) {
769280849Scy				case PRINT_C_CHAR:
770280849Scy					value = (unsigned char)va_arg(args,
771280849Scy					    unsigned int);
772280849Scy					break;
773280849Scy				case PRINT_C_SHORT:
774280849Scy					value = (unsigned short int)va_arg(args,
775280849Scy					    unsigned int);
776280849Scy					break;
777280849Scy				case PRINT_C_LONG:
778280849Scy					value = va_arg(args, unsigned long int);
779280849Scy					break;
780280849Scy				case PRINT_C_LLONG:
781280849Scy					value = va_arg(args, ULLONG);
782280849Scy					break;
783280849Scy				case PRINT_C_SIZE:
784280849Scy					value = va_arg(args, size_t);
785280849Scy					break;
786280849Scy				case PRINT_C_INTMAX:
787280849Scy					value = va_arg(args, UINTMAX_T);
788280849Scy					break;
789280849Scy				case PRINT_C_PTRDIFF:
790280849Scy					value = va_arg(args, UPTRDIFF_T);
791280849Scy					break;
792280849Scy				default:
793280849Scy					value = va_arg(args, unsigned int);
794280849Scy					break;
795280849Scy				}
796280849Scy				fmtint(str, &len, size, value, base, width,
797280849Scy				    precision, flags);
798280849Scy				break;
799280849Scy			case 'A':
800280849Scy				/* Not yet supported, we'll use "%F". */
801280849Scy				/* FALLTHROUGH */
802280849Scy			case 'F':
803280849Scy				flags |= PRINT_F_UP;
804280849Scy				/* FALLTHROUGH */
805280849Scy			case 'a':
806280849Scy				/* Not yet supported, we'll use "%f". */
807280849Scy				/* FALLTHROUGH */
808280849Scy			case 'f':
809280849Scy				if (cflags == PRINT_C_LDOUBLE)
810280849Scy					fvalue = va_arg(args, LDOUBLE);
811280849Scy				else
812280849Scy					fvalue = va_arg(args, double);
813280849Scy				fmtflt(str, &len, size, fvalue, width,
814280849Scy				    precision, flags, &overflow);
815280849Scy				if (overflow)
816280849Scy					goto out;
817280849Scy				break;
818280849Scy			case 'E':
819280849Scy				flags |= PRINT_F_UP;
820280849Scy				/* FALLTHROUGH */
821280849Scy			case 'e':
822280849Scy				flags |= PRINT_F_TYPE_E;
823280849Scy				if (cflags == PRINT_C_LDOUBLE)
824280849Scy					fvalue = va_arg(args, LDOUBLE);
825280849Scy				else
826280849Scy					fvalue = va_arg(args, double);
827280849Scy				fmtflt(str, &len, size, fvalue, width,
828280849Scy				    precision, flags, &overflow);
829280849Scy				if (overflow)
830280849Scy					goto out;
831280849Scy				break;
832280849Scy			case 'G':
833280849Scy				flags |= PRINT_F_UP;
834280849Scy				/* FALLTHROUGH */
835280849Scy			case 'g':
836280849Scy				flags |= PRINT_F_TYPE_G;
837280849Scy				if (cflags == PRINT_C_LDOUBLE)
838280849Scy					fvalue = va_arg(args, LDOUBLE);
839280849Scy				else
840280849Scy					fvalue = va_arg(args, double);
841280849Scy				/*
842280849Scy				 * If the precision is zero, it is treated as
843280849Scy				 * one (cf. C99: 7.19.6.1, 8).
844280849Scy				 */
845280849Scy				if (precision == 0)
846280849Scy					precision = 1;
847280849Scy				fmtflt(str, &len, size, fvalue, width,
848280849Scy				    precision, flags, &overflow);
849280849Scy				if (overflow)
850280849Scy					goto out;
851280849Scy				break;
852280849Scy			case 'c':
853280849Scy				cvalue = va_arg(args, int);
854280849Scy				OUTCHAR(str, len, size, cvalue);
855280849Scy				break;
856280849Scy			case 's':
857280849Scy				strvalue = va_arg(args, char *);
858280849Scy				fmtstr(str, &len, size, strvalue, width,
859280849Scy				    precision, flags);
860280849Scy				break;
861280849Scy			case 'p':
862280849Scy				/*
863280849Scy				 * C99 says: "The value of the pointer is
864280849Scy				 * converted to a sequence of printing
865280849Scy				 * characters, in an implementation-defined
866280849Scy				 * manner." (C99: 7.19.6.1, 8)
867280849Scy				 */
868280849Scy				if ((strvalue = va_arg(args, void *)) == NULL)
869280849Scy					/*
870280849Scy					 * We use the glibc format.  BSD prints
871280849Scy					 * "0x0", SysV "0".
872280849Scy					 */
873280849Scy					fmtstr(str, &len, size, "(nil)", width,
874280849Scy					    -1, flags);
875280849Scy				else {
876280849Scy					/*
877280849Scy					 * We use the BSD/glibc format.  SysV
878280849Scy					 * omits the "0x" prefix (which we emit
879280849Scy					 * using the PRINT_F_NUM flag).
880280849Scy					 */
881280849Scy					flags |= PRINT_F_NUM;
882280849Scy					flags |= PRINT_F_UNSIGNED;
883280849Scy					fmtint(str, &len, size,
884280849Scy					    (UINTPTR_T)strvalue, 16, width,
885280849Scy					    precision, flags);
886280849Scy				}
887280849Scy				break;
888280849Scy			case 'n':
889280849Scy				switch (cflags) {
890280849Scy				case PRINT_C_CHAR:
891280849Scy					charptr = va_arg(args, signed char *);
892293423Sdelphij					*charptr = (signed char)len;
893280849Scy					break;
894280849Scy				case PRINT_C_SHORT:
895280849Scy					shortptr = va_arg(args, short int *);
896293423Sdelphij					*shortptr = (short int)len;
897280849Scy					break;
898280849Scy				case PRINT_C_LONG:
899280849Scy					longptr = va_arg(args, long int *);
900293423Sdelphij					*longptr = (long int)len;
901280849Scy					break;
902280849Scy				case PRINT_C_LLONG:
903280849Scy					llongptr = va_arg(args, LLONG *);
904293423Sdelphij					*llongptr = (LLONG)len;
905280849Scy					break;
906280849Scy				case PRINT_C_SIZE:
907280849Scy					/*
908280849Scy					 * C99 says that with the "z" length
909280849Scy					 * modifier, "a following `n' conversion
910280849Scy					 * specifier applies to a pointer to a
911280849Scy					 * signed integer type corresponding to
912280849Scy					 * size_t argument." (7.19.6.1, 7)
913280849Scy					 */
914280849Scy					sizeptr = va_arg(args, SSIZE_T *);
915293423Sdelphij					*sizeptr = (SSIZE_T)len;
916280849Scy					break;
917280849Scy				case PRINT_C_INTMAX:
918280849Scy					intmaxptr = va_arg(args, INTMAX_T *);
919293423Sdelphij					*intmaxptr = (INTMAX_T)len;
920280849Scy					break;
921280849Scy				case PRINT_C_PTRDIFF:
922280849Scy					ptrdiffptr = va_arg(args, PTRDIFF_T *);
923293423Sdelphij					*ptrdiffptr = (PTRDIFF_T)len;
924280849Scy					break;
925280849Scy				default:
926280849Scy					intptr = va_arg(args, int *);
927293423Sdelphij					*intptr = (int)len;
928280849Scy					break;
929280849Scy				}
930280849Scy				break;
931280849Scy			case '%':	/* Print a "%" character verbatim. */
932280849Scy				OUTCHAR(str, len, size, ch);
933280849Scy				break;
934280849Scy			default:	/* Skip other characters. */
935280849Scy				break;
936280849Scy			}
937280849Scy			ch = *format++;
938280849Scy			state = PRINT_S_DEFAULT;
939280849Scy			base = cflags = flags = width = 0;
940280849Scy			precision = -1;
941280849Scy			break;
942280849Scy		}
943280849Scyout:
944280849Scy	if (len < size)
945280849Scy		str[len] = '\0';
946280849Scy	else if (size > 0)
947280849Scy		str[size - 1] = '\0';
948280849Scy
949280849Scy	if (overflow || len >= INT_MAX) {
950280849Scy		errno = overflow ? EOVERFLOW : ERANGE;
951280849Scy		return -1;
952280849Scy	}
953280849Scy	return (int)len;
954280849Scy}
955280849Scy
956280849Scystatic void
957280849Scyfmtstr(char *str, size_t *len, size_t size, const char *value, int width,
958280849Scy       int precision, int flags)
959280849Scy{
960280849Scy	int padlen, strln;	/* Amount to pad. */
961280849Scy	int noprecision = (precision == -1);
962280849Scy
963280849Scy	if (value == NULL)	/* We're forgiving. */
964280849Scy		value = "(null)";
965280849Scy
966280849Scy	/* If a precision was specified, don't read the string past it. */
967280849Scy	for (strln = 0; value[strln] != '\0' &&
968280849Scy	    (noprecision || strln < precision); strln++)
969280849Scy		continue;
970280849Scy
971280849Scy	if ((padlen = width - strln) < 0)
972280849Scy		padlen = 0;
973280849Scy	if (flags & PRINT_F_MINUS)	/* Left justify. */
974280849Scy		padlen = -padlen;
975280849Scy
976280849Scy	while (padlen > 0) {	/* Leading spaces. */
977280849Scy		OUTCHAR(str, *len, size, ' ');
978280849Scy		padlen--;
979280849Scy	}
980280849Scy	while (*value != '\0' && (noprecision || precision-- > 0)) {
981280849Scy		OUTCHAR(str, *len, size, *value);
982280849Scy		value++;
983280849Scy	}
984280849Scy	while (padlen < 0) {	/* Trailing spaces. */
985280849Scy		OUTCHAR(str, *len, size, ' ');
986280849Scy		padlen++;
987280849Scy	}
988280849Scy}
989280849Scy
990280849Scystatic void
991280849Scyfmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
992280849Scy       int precision, int flags)
993280849Scy{
994280849Scy	UINTMAX_T uvalue;
995280849Scy	char iconvert[MAX_CONVERT_LENGTH];
996280849Scy	char sign = 0;
997280849Scy	char hexprefix = 0;
998280849Scy	int spadlen = 0;	/* Amount to space pad. */
999280849Scy	int zpadlen = 0;	/* Amount to zero pad. */
1000280849Scy	int pos;
1001280849Scy	int separators = (flags & PRINT_F_QUOTE);
1002280849Scy	int noprecision = (precision == -1);
1003280849Scy
1004280849Scy	if (flags & PRINT_F_UNSIGNED)
1005280849Scy		uvalue = value;
1006280849Scy	else {
1007280849Scy		uvalue = (value >= 0) ? value : -value;
1008280849Scy		if (value < 0)
1009280849Scy			sign = '-';
1010280849Scy		else if (flags & PRINT_F_PLUS)	/* Do a sign. */
1011280849Scy			sign = '+';
1012280849Scy		else if (flags & PRINT_F_SPACE)
1013280849Scy			sign = ' ';
1014280849Scy	}
1015280849Scy
1016280849Scy	pos = convert(uvalue, iconvert, sizeof(iconvert), base,
1017280849Scy	    flags & PRINT_F_UP);
1018280849Scy
1019280849Scy	if (flags & PRINT_F_NUM && uvalue != 0) {
1020280849Scy		/*
1021280849Scy		 * C99 says: "The result is converted to an `alternative form'.
1022280849Scy		 * For `o' conversion, it increases the precision, if and only
1023280849Scy		 * if necessary, to force the first digit of the result to be a
1024280849Scy		 * zero (if the value and precision are both 0, a single 0 is
1025280849Scy		 * printed).  For `x' (or `X') conversion, a nonzero result has
1026280849Scy		 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
1027280849Scy		 */
1028280849Scy		switch (base) {
1029280849Scy		case 8:
1030280849Scy			if (precision <= pos)
1031280849Scy				precision = pos + 1;
1032280849Scy			break;
1033280849Scy		case 16:
1034280849Scy			hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
1035280849Scy			break;
1036280849Scy		}
1037280849Scy	}
1038280849Scy
1039280849Scy	if (separators)	/* Get the number of group separators we'll print. */
1040280849Scy		separators = getnumsep(pos);
1041280849Scy
1042280849Scy	zpadlen = precision - pos - separators;
1043280849Scy	spadlen = width                         /* Minimum field width. */
1044280849Scy	    - separators                        /* Number of separators. */
1045280849Scy	    - MAX(precision, pos)               /* Number of integer digits. */
1046280849Scy	    - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
1047280849Scy	    - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
1048280849Scy
1049280849Scy	if (zpadlen < 0)
1050280849Scy		zpadlen = 0;
1051280849Scy	if (spadlen < 0)
1052280849Scy		spadlen = 0;
1053280849Scy
1054280849Scy	/*
1055280849Scy	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1056280849Scy	 * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
1057280849Scy	 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
1058280849Scy	 */
1059280849Scy	if (flags & PRINT_F_MINUS)	/* Left justify. */
1060280849Scy		spadlen = -spadlen;
1061280849Scy	else if (flags & PRINT_F_ZERO && noprecision) {
1062280849Scy		zpadlen += spadlen;
1063280849Scy		spadlen = 0;
1064280849Scy	}
1065280849Scy	while (spadlen > 0) {	/* Leading spaces. */
1066280849Scy		OUTCHAR(str, *len, size, ' ');
1067280849Scy		spadlen--;
1068280849Scy	}
1069280849Scy	if (sign != 0)	/* Sign. */
1070280849Scy		OUTCHAR(str, *len, size, sign);
1071280849Scy	if (hexprefix != 0) {	/* A "0x" or "0X" prefix. */
1072280849Scy		OUTCHAR(str, *len, size, '0');
1073280849Scy		OUTCHAR(str, *len, size, hexprefix);
1074280849Scy	}
1075280849Scy	while (zpadlen > 0) {	/* Leading zeros. */
1076280849Scy		OUTCHAR(str, *len, size, '0');
1077280849Scy		zpadlen--;
1078280849Scy	}
1079280849Scy	while (pos > 0) {	/* The actual digits. */
1080280849Scy		pos--;
1081280849Scy		OUTCHAR(str, *len, size, iconvert[pos]);
1082280849Scy		if (separators > 0 && pos > 0 && pos % 3 == 0)
1083280849Scy			printsep(str, len, size);
1084280849Scy	}
1085280849Scy	while (spadlen < 0) {	/* Trailing spaces. */
1086280849Scy		OUTCHAR(str, *len, size, ' ');
1087280849Scy		spadlen++;
1088280849Scy	}
1089280849Scy}
1090280849Scy
1091280849Scystatic void
1092280849Scyfmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
1093280849Scy       int precision, int flags, int *overflow)
1094280849Scy{
1095280849Scy	LDOUBLE ufvalue;
1096280849Scy	UINTMAX_T intpart;
1097280849Scy	UINTMAX_T fracpart;
1098280849Scy	UINTMAX_T mask;
1099280849Scy	const char *infnan = NULL;
1100280849Scy	char iconvert[MAX_CONVERT_LENGTH];
1101280849Scy	char fconvert[MAX_CONVERT_LENGTH];
1102280849Scy	char econvert[4];	/* "e-12" (without nul-termination). */
1103280849Scy	char esign = 0;
1104280849Scy	char sign = 0;
1105280849Scy	int leadfraczeros = 0;
1106280849Scy	int exponent = 0;
1107280849Scy	int emitpoint = 0;
1108280849Scy	int omitzeros = 0;
1109280849Scy	int omitcount = 0;
1110280849Scy	int padlen = 0;
1111280849Scy	int epos = 0;
1112280849Scy	int fpos = 0;
1113280849Scy	int ipos = 0;
1114280849Scy	int separators = (flags & PRINT_F_QUOTE);
1115280849Scy	int estyle = (flags & PRINT_F_TYPE_E);
1116280849Scy#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
1117280849Scy	struct lconv *lc = localeconv();
1118280849Scy#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
1119280849Scy
1120280849Scy	/*
1121280849Scy	 * AIX' man page says the default is 0, but C99 and at least Solaris'
1122280849Scy	 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
1123280849Scy	 * defaults to 6.
1124280849Scy	 */
1125280849Scy	if (precision == -1)
1126280849Scy		precision = 6;
1127280849Scy
1128280849Scy	if (fvalue < 0.0)
1129280849Scy		sign = '-';
1130280849Scy	else if (flags & PRINT_F_PLUS)	/* Do a sign. */
1131280849Scy		sign = '+';
1132280849Scy	else if (flags & PRINT_F_SPACE)
1133280849Scy		sign = ' ';
1134280849Scy
1135280849Scy	if (ISNAN(fvalue))
1136280849Scy		infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
1137280849Scy	else if (ISINF(fvalue))
1138280849Scy		infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
1139280849Scy
1140280849Scy	if (infnan != NULL) {
1141280849Scy		if (sign != 0)
1142280849Scy			iconvert[ipos++] = sign;
1143280849Scy		while (*infnan != '\0')
1144280849Scy			iconvert[ipos++] = *infnan++;
1145280849Scy		fmtstr(str, len, size, iconvert, width, ipos, flags);
1146280849Scy		return;
1147280849Scy	}
1148280849Scy
1149280849Scy	/* "%e" (or "%E") or "%g" (or "%G") conversion. */
1150280849Scy	if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
1151280849Scy		if (flags & PRINT_F_TYPE_G) {
1152280849Scy			/*
1153280849Scy			 * For "%g" (and "%G") conversions, the precision
1154280849Scy			 * specifies the number of significant digits, which
1155280849Scy			 * includes the digits in the integer part.  The
1156280849Scy			 * conversion will or will not be using "e-style" (like
1157280849Scy			 * "%e" or "%E" conversions) depending on the precision
1158280849Scy			 * and on the exponent.  However, the exponent can be
1159280849Scy			 * affected by rounding the converted value, so we'll
1160280849Scy			 * leave this decision for later.  Until then, we'll
1161280849Scy			 * assume that we're going to do an "e-style" conversion
1162280849Scy			 * (in order to get the exponent calculated).  For
1163280849Scy			 * "e-style", the precision must be decremented by one.
1164280849Scy			 */
1165280849Scy			precision--;
1166280849Scy			/*
1167280849Scy			 * For "%g" (and "%G") conversions, trailing zeros are
1168280849Scy			 * removed from the fractional portion of the result
1169280849Scy			 * unless the "#" flag was specified.
1170280849Scy			 */
1171280849Scy			if (!(flags & PRINT_F_NUM))
1172280849Scy				omitzeros = 1;
1173280849Scy		}
1174280849Scy		exponent = getexponent(fvalue);
1175280849Scy		estyle = 1;
1176280849Scy	}
1177280849Scy
1178280849Scyagain:
1179280849Scy	/*
1180280849Scy	 * Sorry, we only support 9, 19, or 38 digits (that is, the number of
1181280849Scy	 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
1182280849Scy	 * minus one) past the decimal point due to our conversion method.
1183280849Scy	 */
1184280849Scy	switch (sizeof(UINTMAX_T)) {
1185280849Scy	case 16:
1186280849Scy		if (precision > 38)
1187280849Scy			precision = 38;
1188280849Scy		break;
1189280849Scy	case 8:
1190280849Scy		if (precision > 19)
1191280849Scy			precision = 19;
1192280849Scy		break;
1193280849Scy	default:
1194280849Scy		if (precision > 9)
1195280849Scy			precision = 9;
1196280849Scy		break;
1197280849Scy	}
1198280849Scy
1199280849Scy	ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
1200280849Scy	if (estyle)	/* We want exactly one integer digit. */
1201280849Scy		ufvalue /= mypow10(exponent);
1202280849Scy
1203280849Scy	if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
1204280849Scy		*overflow = 1;
1205280849Scy		return;
1206280849Scy	}
1207280849Scy
1208280849Scy	/*
1209280849Scy	 * Factor of ten with the number of digits needed for the fractional
1210280849Scy	 * part.  For example, if the precision is 3, the mask will be 1000.
1211280849Scy	 */
1212293423Sdelphij	mask = (UINTMAX_T)mypow10(precision);
1213280849Scy	/*
1214280849Scy	 * We "cheat" by converting the fractional part to integer by
1215280849Scy	 * multiplying by a factor of ten.
1216280849Scy	 */
1217280849Scy	if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
1218280849Scy		/*
1219280849Scy		 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
1220280849Scy		 * (because precision = 3).  Now, myround(1000 * 0.99962) will
1221280849Scy		 * return 1000.  So, the integer part must be incremented by one
1222280849Scy		 * and the fractional part must be set to zero.
1223280849Scy		 */
1224280849Scy		intpart++;
1225280849Scy		fracpart = 0;
1226280849Scy		if (estyle && intpart == 10) {
1227280849Scy			/*
1228280849Scy			 * The value was rounded up to ten, but we only want one
1229280849Scy			 * integer digit if using "e-style".  So, the integer
1230280849Scy			 * part must be set to one and the exponent must be
1231280849Scy			 * incremented by one.
1232280849Scy			 */
1233280849Scy			intpart = 1;
1234280849Scy			exponent++;
1235280849Scy		}
1236280849Scy	}
1237280849Scy
1238280849Scy	/*
1239280849Scy	 * Now that we know the real exponent, we can check whether or not to
1240280849Scy	 * use "e-style" for "%g" (and "%G") conversions.  If we don't need
1241280849Scy	 * "e-style", the precision must be adjusted and the integer and
1242280849Scy	 * fractional parts must be recalculated from the original value.
1243280849Scy	 *
1244280849Scy	 * C99 says: "Let P equal the precision if nonzero, 6 if the precision
1245280849Scy	 * is omitted, or 1 if the precision is zero.  Then, if a conversion
1246280849Scy	 * with style `E' would have an exponent of X:
1247280849Scy	 *
1248280849Scy	 * - if P > X >= -4, the conversion is with style `f' (or `F') and
1249280849Scy	 *   precision P - (X + 1).
1250280849Scy	 *
1251280849Scy	 * - otherwise, the conversion is with style `e' (or `E') and precision
1252280849Scy	 *   P - 1." (7.19.6.1, 8)
1253280849Scy	 *
1254280849Scy	 * Note that we had decremented the precision by one.
1255280849Scy	 */
1256280849Scy	if (flags & PRINT_F_TYPE_G && estyle &&
1257280849Scy	    precision + 1 > exponent && exponent >= -4) {
1258280849Scy		precision -= exponent;
1259280849Scy		estyle = 0;
1260280849Scy		goto again;
1261280849Scy	}
1262280849Scy
1263280849Scy	if (estyle) {
1264280849Scy		if (exponent < 0) {
1265280849Scy			exponent = -exponent;
1266280849Scy			esign = '-';
1267280849Scy		} else
1268280849Scy			esign = '+';
1269280849Scy
1270280849Scy		/*
1271280849Scy		 * Convert the exponent.  The sizeof(econvert) is 4.  So, the
1272280849Scy		 * econvert buffer can hold e.g. "e+99" and "e-99".  We don't
1273280849Scy		 * support an exponent which contains more than two digits.
1274280849Scy		 * Therefore, the following stores are safe.
1275280849Scy		 */
1276280849Scy		epos = convert(exponent, econvert, 2, 10, 0);
1277280849Scy		/*
1278280849Scy		 * C99 says: "The exponent always contains at least two digits,
1279280849Scy		 * and only as many more digits as necessary to represent the
1280280849Scy		 * exponent." (7.19.6.1, 8)
1281280849Scy		 */
1282280849Scy		if (epos == 1)
1283280849Scy			econvert[epos++] = '0';
1284280849Scy		econvert[epos++] = esign;
1285280849Scy		econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
1286280849Scy	}
1287280849Scy
1288280849Scy	/* Convert the integer part and the fractional part. */
1289280849Scy	ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
1290280849Scy	if (fracpart != 0)	/* convert() would return 1 if fracpart == 0. */
1291280849Scy		fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
1292280849Scy
1293280849Scy	leadfraczeros = precision - fpos;
1294280849Scy
1295280849Scy	if (omitzeros) {
1296280849Scy		if (fpos > 0)	/* Omit trailing fractional part zeros. */
1297280849Scy			while (omitcount < fpos && fconvert[omitcount] == '0')
1298280849Scy				omitcount++;
1299280849Scy		else {	/* The fractional part is zero, omit it completely. */
1300280849Scy			omitcount = precision;
1301280849Scy			leadfraczeros = 0;
1302280849Scy		}
1303280849Scy		precision -= omitcount;
1304280849Scy	}
1305280849Scy
1306280849Scy	/*
1307280849Scy	 * Print a decimal point if either the fractional part is non-zero
1308280849Scy	 * and/or the "#" flag was specified.
1309280849Scy	 */
1310280849Scy	if (precision > 0 || flags & PRINT_F_NUM)
1311280849Scy		emitpoint = 1;
1312280849Scy	if (separators)	/* Get the number of group separators we'll print. */
1313280849Scy		separators = getnumsep(ipos);
1314280849Scy
1315280849Scy	padlen = width                  /* Minimum field width. */
1316280849Scy	    - ipos                      /* Number of integer digits. */
1317280849Scy	    - epos                      /* Number of exponent characters. */
1318280849Scy	    - precision                 /* Number of fractional digits. */
1319280849Scy	    - separators                /* Number of group separators. */
1320280849Scy	    - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
1321280849Scy	    - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
1322280849Scy
1323280849Scy	if (padlen < 0)
1324280849Scy		padlen = 0;
1325280849Scy
1326280849Scy	/*
1327280849Scy	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1328280849Scy	 * ignored." (7.19.6.1, 6)
1329280849Scy	 */
1330280849Scy	if (flags & PRINT_F_MINUS)	/* Left justifty. */
1331280849Scy		padlen = -padlen;
1332280849Scy	else if (flags & PRINT_F_ZERO && padlen > 0) {
1333280849Scy		if (sign != 0) {	/* Sign. */
1334280849Scy			OUTCHAR(str, *len, size, sign);
1335280849Scy			sign = 0;
1336280849Scy		}
1337280849Scy		while (padlen > 0) {	/* Leading zeros. */
1338280849Scy			OUTCHAR(str, *len, size, '0');
1339280849Scy			padlen--;
1340280849Scy		}
1341280849Scy	}
1342280849Scy	while (padlen > 0) {	/* Leading spaces. */
1343280849Scy		OUTCHAR(str, *len, size, ' ');
1344280849Scy		padlen--;
1345280849Scy	}
1346280849Scy	if (sign != 0)	/* Sign. */
1347280849Scy		OUTCHAR(str, *len, size, sign);
1348280849Scy	while (ipos > 0) {	/* Integer part. */
1349280849Scy		ipos--;
1350280849Scy		OUTCHAR(str, *len, size, iconvert[ipos]);
1351280849Scy		if (separators > 0 && ipos > 0 && ipos % 3 == 0)
1352280849Scy			printsep(str, len, size);
1353280849Scy	}
1354280849Scy	if (emitpoint) {	/* Decimal point. */
1355280849Scy#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
1356280849Scy		if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
1357280849Scy			OUTCHAR(str, *len, size, *lc->decimal_point);
1358280849Scy		else	/* We'll always print some decimal point character. */
1359280849Scy#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
1360280849Scy			OUTCHAR(str, *len, size, '.');
1361280849Scy	}
1362280849Scy	while (leadfraczeros > 0) {	/* Leading fractional part zeros. */
1363280849Scy		OUTCHAR(str, *len, size, '0');
1364280849Scy		leadfraczeros--;
1365280849Scy	}
1366280849Scy	while (fpos > omitcount) {	/* The remaining fractional part. */
1367280849Scy		fpos--;
1368280849Scy		OUTCHAR(str, *len, size, fconvert[fpos]);
1369280849Scy	}
1370280849Scy	while (epos > 0) {	/* Exponent. */
1371280849Scy		epos--;
1372280849Scy		OUTCHAR(str, *len, size, econvert[epos]);
1373280849Scy	}
1374280849Scy	while (padlen < 0) {	/* Trailing spaces. */
1375280849Scy		OUTCHAR(str, *len, size, ' ');
1376280849Scy		padlen++;
1377280849Scy	}
1378280849Scy}
1379280849Scy
1380280849Scystatic void
1381280849Scyprintsep(char *str, size_t *len, size_t size)
1382280849Scy{
1383280849Scy#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
1384280849Scy	struct lconv *lc = localeconv();
1385280849Scy	int i;
1386280849Scy
1387280849Scy	if (lc->thousands_sep != NULL)
1388280849Scy		for (i = 0; lc->thousands_sep[i] != '\0'; i++)
1389280849Scy			OUTCHAR(str, *len, size, lc->thousands_sep[i]);
1390280849Scy	else
1391280849Scy#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
1392280849Scy		OUTCHAR(str, *len, size, ',');
1393280849Scy}
1394280849Scy
1395280849Scystatic int
1396280849Scygetnumsep(int digits)
1397280849Scy{
1398280849Scy	int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
1399280849Scy#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
1400280849Scy	int strln;
1401280849Scy	struct lconv *lc = localeconv();
1402280849Scy
1403280849Scy	/* We support an arbitrary separator length (including zero). */
1404280849Scy	if (lc->thousands_sep != NULL) {
1405280849Scy		for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
1406280849Scy			continue;
1407280849Scy		separators *= strln;
1408280849Scy	}
1409280849Scy#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
1410280849Scy	return separators;
1411280849Scy}
1412280849Scy
1413280849Scystatic int
1414280849Scygetexponent(LDOUBLE value)
1415280849Scy{
1416280849Scy	LDOUBLE tmp = (value >= 0.0) ? value : -value;
1417280849Scy	int exponent = 0;
1418280849Scy
1419280849Scy	/*
1420280849Scy	 * We check for 99 > exponent > -99 in order to work around possible
1421280849Scy	 * endless loops which could happen (at least) in the second loop (at
1422280849Scy	 * least) if we're called with an infinite value.  However, we checked
1423280849Scy	 * for infinity before calling this function using our ISINF() macro, so
1424280849Scy	 * this might be somewhat paranoid.
1425280849Scy	 */
1426280849Scy	while (tmp < 1.0 && tmp > 0.0 && --exponent > -99)
1427280849Scy		tmp *= 10;
1428280849Scy	while (tmp >= 10.0 && ++exponent < 99)
1429280849Scy		tmp /= 10;
1430280849Scy
1431280849Scy	return exponent;
1432280849Scy}
1433280849Scy
1434280849Scystatic int
1435280849Scyconvert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
1436280849Scy{
1437280849Scy	const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
1438280849Scy	size_t pos = 0;
1439280849Scy
1440280849Scy	/* We return an unterminated buffer with the digits in reverse order. */
1441280849Scy	do {
1442280849Scy		buf[pos++] = digits[value % base];
1443280849Scy		value /= base;
1444280849Scy	} while (value != 0 && pos < size);
1445280849Scy
1446280849Scy	return (int)pos;
1447280849Scy}
1448280849Scy
1449280849Scystatic UINTMAX_T
1450280849Scycast(LDOUBLE value)
1451280849Scy{
1452280849Scy	UINTMAX_T result;
1453280849Scy
1454280849Scy	/*
1455280849Scy	 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
1456280849Scy	 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
1457280849Scy	 * it may be increased to the nearest higher representable value for the
1458280849Scy	 * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
1459280849Scy	 * value although converting the latter to UINTMAX_T would overflow.
1460280849Scy	 */
1461280849Scy	if (value >= UINTMAX_MAX)
1462280849Scy		return UINTMAX_MAX;
1463280849Scy
1464293423Sdelphij	result = (UINTMAX_T)value;
1465280849Scy	/*
1466280849Scy	 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
1467280849Scy	 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
1468280849Scy	 * the standard).  Sigh.
1469280849Scy	 */
1470280849Scy	return (result <= value) ? result : result - 1;
1471280849Scy}
1472280849Scy
1473280849Scystatic UINTMAX_T
1474280849Scymyround(LDOUBLE value)
1475280849Scy{
1476280849Scy	UINTMAX_T intpart = cast(value);
1477280849Scy
1478280849Scy	return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
1479280849Scy}
1480280849Scy
1481280849Scystatic LDOUBLE
1482280849Scymypow10(int exponent)
1483280849Scy{
1484280849Scy	LDOUBLE result = 1;
1485280849Scy
1486280849Scy	while (exponent > 0) {
1487280849Scy		result *= 10;
1488280849Scy		exponent--;
1489280849Scy	}
1490280849Scy	while (exponent < 0) {
1491280849Scy		result /= 10;
1492280849Scy		exponent++;
1493280849Scy	}
1494280849Scy	return result;
1495280849Scy}
1496280849Scy#endif	/* HW_WANT_RPL_VSNPRINTF */
1497280849Scy
1498280849Scy#if HW_WANT_RPL_VASPRINTF
1499280849Scy#if NEED_MYMEMCPY
1500280849Scyvoid *
1501280849Scymymemcpy(void *dst, void *src, size_t len)
1502280849Scy{
1503280849Scy	const char *from = src;
1504280849Scy	char *to = dst;
1505280849Scy
1506280849Scy	/* No need for optimization, we use this only to replace va_copy(3). */
1507280849Scy	while (len-- > 0)
1508280849Scy		*to++ = *from++;
1509280849Scy	return dst;
1510280849Scy}
1511280849Scy#endif	/* NEED_MYMEMCPY */
1512280849Scy
1513280849Scyint
1514280849Scyrpl_vasprintf(char **ret, const char *format, va_list ap);
1515280849Scy
1516280849Scyint
1517280849Scyrpl_vasprintf(char **ret, const char *format, va_list ap)
1518280849Scy{
1519280849Scy	size_t size;
1520280849Scy	int len;
1521280849Scy	va_list aq;
1522280849Scy
1523280849Scy	VA_COPY(aq, ap);
1524280849Scy	len = vsnprintf(NULL, 0, format, aq);
1525280849Scy	VA_END_COPY(aq);
1526280849Scy	if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
1527280849Scy		return -1;
1528280849Scy	return vsnprintf(*ret, size, format, ap);
1529280849Scy}
1530280849Scy#endif	/* HW_WANT_RPL_VASPRINTF */
1531280849Scy
1532280849Scy#if HW_WANT_RPL_SNPRINTF
1533280849Scy#if HAVE_STDARG_H
1534280849Scyint
1535280849Scyrpl_snprintf(char *str, size_t size, const char *format, ...);
1536280849Scy
1537280849Scyint
1538280849Scyrpl_snprintf(char *str, size_t size, const char *format, ...)
1539280849Scy#else
1540280849Scyint
1541280849Scyrpl_snprintf(va_alist) va_dcl
1542280849Scy#endif	/* HAVE_STDARG_H */
1543280849Scy{
1544280849Scy#if !HAVE_STDARG_H
154582498Sroberto	char *str;
1546280849Scy	size_t size;
1547280849Scy	char *format;
1548280849Scy#endif	/* HAVE_STDARG_H */
154982498Sroberto	va_list ap;
1550280849Scy	int len;
1551280849Scy
1552280849Scy	VA_START(ap, format);
1553280849Scy	VA_SHIFT(ap, str, char *);
1554280849Scy	VA_SHIFT(ap, size, size_t);
1555280849Scy	VA_SHIFT(ap, format, const char *);
1556280849Scy	len = vsnprintf(str, size, format, ap);
155782498Sroberto	va_end(ap);
1558280849Scy	return len;
1559280849Scy}
1560280849Scy#endif	/* HW_WANT_RPL_SNPRINTF */
1561280849Scy
1562280849Scy#if HW_WANT_RPL_ASPRINTF
1563280849Scy#if HAVE_STDARG_H
1564280849Scyint
1565280849Scyrpl_asprintf(char **ret, const char *format, ...);
1566280849Scy
1567280849Scyint
1568280849Scyrpl_asprintf(char **ret, const char *format, ...)
156982498Sroberto#else
1570280849Scyint
1571280849Scyrpl_asprintf(va_alist) va_dcl
1572280849Scy#endif	/* HAVE_STDARG_H */
1573280849Scy{
1574280849Scy#if !HAVE_STDARG_H
1575280849Scy	char **ret;
1576280849Scy	char *format;
1577280849Scy#endif	/* HAVE_STDARG_H */
1578280849Scy	va_list ap;
1579280849Scy	int len;
1580280849Scy
1581280849Scy	VA_START(ap, format);
1582280849Scy	VA_SHIFT(ap, ret, char **);
1583280849Scy	VA_SHIFT(ap, format, const char *);
1584280849Scy	len = vasprintf(ret, format, ap);
158582498Sroberto	va_end(ap);
1586280849Scy	return len;
158782498Sroberto}
1588280849Scy#endif	/* HW_WANT_RPL_ASPRINTF */
1589280849Scy#else	/* Dummy declaration to avoid empty translation unit warnings. */
1590280849Scyint main(void);
1591280849Scy#endif	/* HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || [...] */
159282498Sroberto
1593280849Scy#if TEST_SNPRINTF
159482498Srobertoint
1595280849Scymain(void)
159682498Sroberto{
1597280849Scy	const char *float_fmt[] = {
1598280849Scy		/* "%E" and "%e" formats. */
1599280849Scy#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
1600280849Scy		"%.16e",
1601280849Scy		"%22.16e",
1602280849Scy		"%022.16e",
1603280849Scy		"%-22.16e",
1604280849Scy		"%#+'022.16e",
1605280849Scy#endif	/* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
1606280849Scy		"foo|%#+0123.9E|bar",
1607280849Scy		"%-123.9e",
1608280849Scy		"%123.9e",
1609280849Scy		"%+23.9e",
1610280849Scy		"%+05.8e",
1611280849Scy		"%-05.8e",
1612280849Scy		"%05.8e",
1613280849Scy		"%+5.8e",
1614280849Scy		"%-5.8e",
1615280849Scy		"% 5.8e",
1616280849Scy		"%5.8e",
1617280849Scy		"%+4.9e",
1618280849Scy#if !OS_LINUX	/* glibc sometimes gets these wrong. */
1619280849Scy		"%+#010.0e",
1620280849Scy		"%#10.1e",
1621280849Scy		"%10.5e",
1622280849Scy		"% 10.5e",
1623280849Scy		"%5.0e",
1624280849Scy		"%5.e",
1625280849Scy		"%#5.0e",
1626280849Scy		"%#5.e",
1627280849Scy		"%3.2e",
1628280849Scy		"%3.1e",
1629280849Scy		"%-1.5e",
1630280849Scy		"%1.5e",
1631280849Scy		"%01.3e",
1632280849Scy		"%1.e",
1633280849Scy		"%.1e",
1634280849Scy		"%#.0e",
1635280849Scy		"%+.0e",
1636280849Scy		"% .0e",
1637280849Scy		"%.0e",
1638280849Scy		"%#.e",
1639280849Scy		"%+.e",
1640280849Scy		"% .e",
1641280849Scy		"%.e",
1642280849Scy		"%4e",
1643280849Scy		"%e",
1644280849Scy		"%E",
1645280849Scy#endif	/* !OS_LINUX */
1646280849Scy		/* "%F" and "%f" formats. */
1647280849Scy#if !OS_BSD && !OS_IRIX
1648280849Scy		"% '022f",
1649280849Scy		"%+'022f",
1650280849Scy		"%-'22f",
1651280849Scy		"%'22f",
1652280849Scy#if HAVE_LONG_LONG_INT
1653280849Scy		"%.16f",
1654280849Scy		"%22.16f",
1655280849Scy		"%022.16f",
1656280849Scy		"%-22.16f",
1657280849Scy		"%#+'022.16f",
1658280849Scy#endif	/* HAVE_LONG_LONG_INT */
1659280849Scy#endif	/* !OS_BSD && !OS_IRIX */
1660280849Scy		"foo|%#+0123.9F|bar",
1661280849Scy		"%-123.9f",
1662280849Scy		"%123.9f",
1663280849Scy		"%+23.9f",
1664280849Scy		"%+#010.0f",
1665280849Scy		"%#10.1f",
1666280849Scy		"%10.5f",
1667280849Scy		"% 10.5f",
1668280849Scy		"%+05.8f",
1669280849Scy		"%-05.8f",
1670280849Scy		"%05.8f",
1671280849Scy		"%+5.8f",
1672280849Scy		"%-5.8f",
1673280849Scy		"% 5.8f",
1674280849Scy		"%5.8f",
1675280849Scy		"%5.0f",
1676280849Scy		"%5.f",
1677280849Scy		"%#5.0f",
1678280849Scy		"%#5.f",
1679280849Scy		"%+4.9f",
1680280849Scy		"%3.2f",
1681280849Scy		"%3.1f",
1682280849Scy		"%-1.5f",
1683280849Scy		"%1.5f",
1684280849Scy		"%01.3f",
1685280849Scy		"%1.f",
1686280849Scy		"%.1f",
1687280849Scy		"%#.0f",
1688280849Scy		"%+.0f",
1689280849Scy		"% .0f",
1690280849Scy		"%.0f",
1691280849Scy		"%#.f",
1692280849Scy		"%+.f",
1693280849Scy		"% .f",
1694280849Scy		"%.f",
1695280849Scy		"%4f",
1696280849Scy		"%f",
1697280849Scy		"%F",
1698280849Scy		/* "%G" and "%g" formats. */
1699280849Scy#if !OS_BSD && !OS_IRIX && !OS_LINUX
1700280849Scy		"% '022g",
1701280849Scy		"%+'022g",
1702280849Scy		"%-'22g",
1703280849Scy		"%'22g",
1704280849Scy#if HAVE_LONG_LONG_INT
1705280849Scy		"%.16g",
1706280849Scy		"%22.16g",
1707280849Scy		"%022.16g",
1708280849Scy		"%-22.16g",
1709280849Scy		"%#+'022.16g",
1710280849Scy#endif	/* HAVE_LONG_LONG_INT */
1711280849Scy#endif	/* !OS_BSD && !OS_IRIX && !OS_LINUX */
1712280849Scy		"foo|%#+0123.9G|bar",
1713280849Scy		"%-123.9g",
1714280849Scy		"%123.9g",
1715280849Scy		"%+23.9g",
1716280849Scy		"%+05.8g",
1717280849Scy		"%-05.8g",
1718280849Scy		"%05.8g",
1719280849Scy		"%+5.8g",
1720280849Scy		"%-5.8g",
1721280849Scy		"% 5.8g",
1722280849Scy		"%5.8g",
1723280849Scy		"%+4.9g",
1724280849Scy#if !OS_LINUX	/* glibc sometimes gets these wrong. */
1725280849Scy		"%+#010.0g",
1726280849Scy		"%#10.1g",
1727280849Scy		"%10.5g",
1728280849Scy		"% 10.5g",
1729280849Scy		"%5.0g",
1730280849Scy		"%5.g",
1731280849Scy		"%#5.0g",
1732280849Scy		"%#5.g",
1733280849Scy		"%3.2g",
1734280849Scy		"%3.1g",
1735280849Scy		"%-1.5g",
1736280849Scy		"%1.5g",
1737280849Scy		"%01.3g",
1738280849Scy		"%1.g",
1739280849Scy		"%.1g",
1740280849Scy		"%#.0g",
1741280849Scy		"%+.0g",
1742280849Scy		"% .0g",
1743280849Scy		"%.0g",
1744280849Scy		"%#.g",
1745280849Scy		"%+.g",
1746280849Scy		"% .g",
1747280849Scy		"%.g",
1748280849Scy		"%4g",
1749280849Scy		"%g",
1750280849Scy		"%G",
1751280849Scy#endif	/* !OS_LINUX */
1752280849Scy		NULL
1753280849Scy	};
1754280849Scy	double float_val[] = {
1755280849Scy		-4.136,
1756280849Scy		-134.52,
1757280849Scy		-5.04030201,
1758280849Scy		-3410.01234,
1759280849Scy		-999999.999999,
1760280849Scy		-913450.29876,
1761280849Scy		-913450.2,
1762280849Scy		-91345.2,
1763280849Scy		-9134.2,
1764280849Scy		-913.2,
1765280849Scy		-91.2,
1766280849Scy		-9.2,
1767280849Scy		-9.9,
1768280849Scy		4.136,
1769280849Scy		134.52,
1770280849Scy		5.04030201,
1771280849Scy		3410.01234,
1772280849Scy		999999.999999,
1773280849Scy		913450.29876,
1774280849Scy		913450.2,
1775280849Scy		91345.2,
1776280849Scy		9134.2,
1777280849Scy		913.2,
1778280849Scy		91.2,
1779280849Scy		9.2,
1780280849Scy		9.9,
1781280849Scy		9.96,
1782280849Scy		9.996,
1783280849Scy		9.9996,
1784280849Scy		9.99996,
1785280849Scy		9.999996,
1786280849Scy		9.9999996,
1787280849Scy		9.99999996,
1788280849Scy		0.99999996,
1789280849Scy		0.99999999,
1790280849Scy		0.09999999,
1791280849Scy		0.00999999,
1792280849Scy		0.00099999,
1793280849Scy		0.00009999,
1794280849Scy		0.00000999,
1795280849Scy		0.00000099,
1796280849Scy		0.00000009,
1797280849Scy		0.00000001,
1798280849Scy		0.0000001,
1799280849Scy		0.000001,
1800280849Scy		0.00001,
1801280849Scy		0.0001,
1802280849Scy		0.001,
1803280849Scy		0.01,
1804280849Scy		0.1,
1805280849Scy		1.0,
1806280849Scy		1.5,
1807280849Scy		-1.5,
1808280849Scy		-1.0,
1809280849Scy		-0.1,
1810280849Scy#if !OS_BSD	/* BSD sometimes gets these wrong. */
1811280849Scy#ifdef INFINITY
1812280849Scy		INFINITY,
1813280849Scy		-INFINITY,
1814280849Scy#endif	/* defined(INFINITY) */
1815280849Scy#ifdef NAN
1816280849Scy		NAN,
1817280849Scy#endif	/* defined(NAN) */
1818280849Scy#endif	/* !OS_BSD */
1819280849Scy		0
1820280849Scy	};
1821280849Scy	const char *long_fmt[] = {
1822280849Scy		"foo|%0123ld|bar",
1823280849Scy#if !OS_IRIX
1824280849Scy		"% '0123ld",
1825280849Scy		"%+'0123ld",
1826280849Scy		"%-'123ld",
1827280849Scy		"%'123ld",
1828280849Scy#endif	/* !OS_IRiX */
1829280849Scy		"%123.9ld",
1830280849Scy		"% 123.9ld",
1831280849Scy		"%+123.9ld",
1832280849Scy		"%-123.9ld",
1833280849Scy		"%0123ld",
1834280849Scy		"% 0123ld",
1835280849Scy		"%+0123ld",
1836280849Scy		"%-0123ld",
1837280849Scy		"%10.5ld",
1838280849Scy		"% 10.5ld",
1839280849Scy		"%+10.5ld",
1840280849Scy		"%-10.5ld",
1841280849Scy		"%010ld",
1842280849Scy		"% 010ld",
1843280849Scy		"%+010ld",
1844280849Scy		"%-010ld",
1845280849Scy		"%4.2ld",
1846280849Scy		"% 4.2ld",
1847280849Scy		"%+4.2ld",
1848280849Scy		"%-4.2ld",
1849280849Scy		"%04ld",
1850280849Scy		"% 04ld",
1851280849Scy		"%+04ld",
1852280849Scy		"%-04ld",
1853280849Scy		"%5.5ld",
1854280849Scy		"%+22.33ld",
1855280849Scy		"%01.3ld",
1856280849Scy		"%1.5ld",
1857280849Scy		"%-1.5ld",
1858280849Scy		"%44ld",
1859280849Scy		"%4ld",
1860280849Scy		"%4.0ld",
1861280849Scy		"%4.ld",
1862280849Scy		"%.44ld",
1863280849Scy		"%.4ld",
1864280849Scy		"%.0ld",
1865280849Scy		"%.ld",
1866280849Scy		"%ld",
1867280849Scy		NULL
1868280849Scy	};
1869280849Scy	long int long_val[] = {
1870280849Scy#ifdef LONG_MAX
1871280849Scy		LONG_MAX,
1872280849Scy#endif	/* LONG_MAX */
1873280849Scy#ifdef LONG_MIN
1874280849Scy		LONG_MIN,
1875280849Scy#endif	/* LONG_MIN */
1876280849Scy		-91340,
1877280849Scy		91340,
1878280849Scy		341,
1879280849Scy		134,
1880280849Scy		0203,
1881280849Scy		-1,
1882280849Scy		1,
1883280849Scy		0
1884280849Scy	};
1885280849Scy	const char *ulong_fmt[] = {
1886280849Scy		/* "%u" formats. */
1887280849Scy		"foo|%0123lu|bar",
1888280849Scy#if !OS_IRIX
1889280849Scy		"% '0123lu",
1890280849Scy		"%+'0123lu",
1891280849Scy		"%-'123lu",
1892280849Scy		"%'123lu",
1893280849Scy#endif	/* !OS_IRiX */
1894280849Scy		"%123.9lu",
1895280849Scy		"% 123.9lu",
1896280849Scy		"%+123.9lu",
1897280849Scy		"%-123.9lu",
1898280849Scy		"%0123lu",
1899280849Scy		"% 0123lu",
1900280849Scy		"%+0123lu",
1901280849Scy		"%-0123lu",
1902280849Scy		"%5.5lu",
1903280849Scy		"%+22.33lu",
1904280849Scy		"%01.3lu",
1905280849Scy		"%1.5lu",
1906280849Scy		"%-1.5lu",
1907280849Scy		"%44lu",
1908280849Scy		"%lu",
1909280849Scy		/* "%o" formats. */
1910280849Scy		"foo|%#0123lo|bar",
1911280849Scy		"%#123.9lo",
1912280849Scy		"%# 123.9lo",
1913280849Scy		"%#+123.9lo",
1914280849Scy		"%#-123.9lo",
1915280849Scy		"%#0123lo",
1916280849Scy		"%# 0123lo",
1917280849Scy		"%#+0123lo",
1918280849Scy		"%#-0123lo",
1919280849Scy		"%#5.5lo",
1920280849Scy		"%#+22.33lo",
1921280849Scy		"%#01.3lo",
1922280849Scy		"%#1.5lo",
1923280849Scy		"%#-1.5lo",
1924280849Scy		"%#44lo",
1925280849Scy		"%#lo",
1926280849Scy		"%123.9lo",
1927280849Scy		"% 123.9lo",
1928280849Scy		"%+123.9lo",
1929280849Scy		"%-123.9lo",
1930280849Scy		"%0123lo",
1931280849Scy		"% 0123lo",
1932280849Scy		"%+0123lo",
1933280849Scy		"%-0123lo",
1934280849Scy		"%5.5lo",
1935280849Scy		"%+22.33lo",
1936280849Scy		"%01.3lo",
1937280849Scy		"%1.5lo",
1938280849Scy		"%-1.5lo",
1939280849Scy		"%44lo",
1940280849Scy		"%lo",
1941280849Scy		/* "%X" and "%x" formats. */
1942280849Scy		"foo|%#0123lX|bar",
1943280849Scy		"%#123.9lx",
1944280849Scy		"%# 123.9lx",
1945280849Scy		"%#+123.9lx",
1946280849Scy		"%#-123.9lx",
1947280849Scy		"%#0123lx",
1948280849Scy		"%# 0123lx",
1949280849Scy		"%#+0123lx",
1950280849Scy		"%#-0123lx",
1951280849Scy		"%#5.5lx",
1952280849Scy		"%#+22.33lx",
1953280849Scy		"%#01.3lx",
1954280849Scy		"%#1.5lx",
1955280849Scy		"%#-1.5lx",
1956280849Scy		"%#44lx",
1957280849Scy		"%#lx",
1958280849Scy		"%#lX",
1959280849Scy		"%123.9lx",
1960280849Scy		"% 123.9lx",
1961280849Scy		"%+123.9lx",
1962280849Scy		"%-123.9lx",
1963280849Scy		"%0123lx",
1964280849Scy		"% 0123lx",
1965280849Scy		"%+0123lx",
1966280849Scy		"%-0123lx",
1967280849Scy		"%5.5lx",
1968280849Scy		"%+22.33lx",
1969280849Scy		"%01.3lx",
1970280849Scy		"%1.5lx",
1971280849Scy		"%-1.5lx",
1972280849Scy		"%44lx",
1973280849Scy		"%lx",
1974280849Scy		"%lX",
1975280849Scy		NULL
1976280849Scy	};
1977280849Scy	unsigned long int ulong_val[] = {
1978280849Scy#ifdef ULONG_MAX
1979280849Scy		ULONG_MAX,
1980280849Scy#endif	/* ULONG_MAX */
1981280849Scy		91340,
1982280849Scy		341,
1983280849Scy		134,
1984280849Scy		0203,
1985280849Scy		1,
1986280849Scy		0
1987280849Scy	};
1988280849Scy	const char *llong_fmt[] = {
1989280849Scy		"foo|%0123lld|bar",
1990280849Scy		"%123.9lld",
1991280849Scy		"% 123.9lld",
1992280849Scy		"%+123.9lld",
1993280849Scy		"%-123.9lld",
1994280849Scy		"%0123lld",
1995280849Scy		"% 0123lld",
1996280849Scy		"%+0123lld",
1997280849Scy		"%-0123lld",
1998280849Scy		"%5.5lld",
1999280849Scy		"%+22.33lld",
2000280849Scy		"%01.3lld",
2001280849Scy		"%1.5lld",
2002280849Scy		"%-1.5lld",
2003280849Scy		"%44lld",
2004280849Scy		"%lld",
2005280849Scy		NULL
2006280849Scy	};
2007280849Scy	LLONG llong_val[] = {
2008280849Scy#ifdef LLONG_MAX
2009280849Scy		LLONG_MAX,
2010280849Scy#endif	/* LLONG_MAX */
2011280849Scy#ifdef LLONG_MIN
2012280849Scy		LLONG_MIN,
2013280849Scy#endif	/* LLONG_MIN */
2014280849Scy		-91340,
2015280849Scy		91340,
2016280849Scy		341,
2017280849Scy		134,
2018280849Scy		0203,
2019280849Scy		-1,
2020280849Scy		1,
2021280849Scy		0
2022280849Scy	};
2023280849Scy	const char *string_fmt[] = {
2024280849Scy		"foo|%10.10s|bar",
2025280849Scy		"%-10.10s",
2026280849Scy		"%10.10s",
2027280849Scy		"%10.5s",
2028280849Scy		"%5.10s",
2029280849Scy		"%10.1s",
2030280849Scy		"%1.10s",
2031280849Scy		"%10.0s",
2032280849Scy		"%0.10s",
2033280849Scy		"%-42.5s",
2034280849Scy		"%2.s",
2035280849Scy		"%.10s",
2036280849Scy		"%.1s",
2037280849Scy		"%.0s",
2038280849Scy		"%.s",
2039280849Scy		"%4s",
2040280849Scy		"%s",
2041280849Scy		NULL
2042280849Scy	};
2043280849Scy	const char *string_val[] = {
2044280849Scy		"Hello",
2045280849Scy		"Hello, world!",
2046280849Scy		"Sound check: One, two, three.",
2047280849Scy		"This string is a little longer than the other strings.",
2048280849Scy		"1",
2049280849Scy		"",
2050280849Scy		NULL
2051280849Scy	};
2052280849Scy#if !OS_SYSV	/* SysV uses a different format than we do. */
2053280849Scy	const char *pointer_fmt[] = {
2054280849Scy		"foo|%p|bar",
2055280849Scy		"%42p",
2056280849Scy		"%p",
2057280849Scy		NULL
2058280849Scy	};
2059280849Scy	const char *pointer_val[] = {
2060280849Scy		*pointer_fmt,
2061280849Scy		*string_fmt,
2062280849Scy		*string_val,
2063280849Scy		NULL
2064280849Scy	};
2065280849Scy#endif	/* !OS_SYSV */
2066280849Scy	char buf1[1024], buf2[1024];
2067280849Scy	double value, digits = 9.123456789012345678901234567890123456789;
2068280849Scy	int i, j, r1, r2, failed = 0, num = 0;
2069280849Scy
2070280849Scy/*
2071280849Scy * Use -DTEST_NILS in order to also test the conversion of nil values.  Might
2072280849Scy * segfault on systems which don't support converting a NULL pointer with "%s"
2073280849Scy * and lets some test cases fail against BSD and glibc due to bugs in their
2074280849Scy * implementations.
2075280849Scy */
2076280849Scy#ifndef TEST_NILS
2077280849Scy#define TEST_NILS 0
2078280849Scy#elif TEST_NILS
2079280849Scy#undef TEST_NILS
2080280849Scy#define TEST_NILS 1
2081280849Scy#endif	/* !defined(TEST_NILS) */
2082280849Scy#ifdef TEST
2083280849Scy#undef TEST
2084280849Scy#endif	/* defined(TEST) */
2085280849Scy#define TEST(fmt, val)                                                         \
2086280849Scydo {                                                                           \
2087280849Scy	for (i = 0; fmt[i] != NULL; i++)                                       \
2088280849Scy		for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) {          \
2089280849Scy			r1 = sprintf(buf1, fmt[i], val[j]);                    \
2090280849Scy			r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]);     \
2091280849Scy			if (strcmp(buf1, buf2) != 0 || r1 != r2) {             \
2092280849Scy				(void)printf("Results don't match, "           \
2093280849Scy				    "format string: %s\n"                      \
2094280849Scy				    "\t sprintf(3): [%s] (%d)\n"               \
2095280849Scy				    "\tsnprintf(3): [%s] (%d)\n",              \
2096280849Scy				    fmt[i], buf1, r1, buf2, r2);               \
2097280849Scy				failed++;                                      \
2098280849Scy			}                                                      \
2099280849Scy			num++;                                                 \
2100280849Scy		}                                                              \
2101280849Scy} while (/* CONSTCOND */ 0)
2102280849Scy
2103280849Scy#if HAVE_LOCALE_H
2104280849Scy	(void)setlocale(LC_ALL, "");
2105280849Scy#endif	/* HAVE_LOCALE_H */
2106280849Scy
2107280849Scy	(void)puts("Testing our snprintf(3) against your system's sprintf(3).");
2108280849Scy	TEST(float_fmt, float_val);
2109280849Scy	TEST(long_fmt, long_val);
2110280849Scy	TEST(ulong_fmt, ulong_val);
2111280849Scy	TEST(llong_fmt, llong_val);
2112280849Scy	TEST(string_fmt, string_val);
2113280849Scy#if !OS_SYSV	/* SysV uses a different format than we do. */
2114280849Scy	TEST(pointer_fmt, pointer_val);
2115280849Scy#endif	/* !OS_SYSV */
2116280849Scy	(void)printf("Result: %d out of %d tests failed.\n", failed, num);
2117280849Scy
2118280849Scy	(void)fputs("Checking how many digits we support: ", stdout);
2119280849Scy	for (i = 0; i < 100; i++) {
2120280849Scy		value = pow(10, i) * digits;
2121280849Scy		(void)sprintf(buf1, "%.1f", value);
2122280849Scy		(void)snprintf(buf2, sizeof(buf2), "%.1f", value);
2123280849Scy		if (strcmp(buf1, buf2) != 0) {
2124280849Scy			(void)printf("apparently %d.\n", i);
2125280849Scy			break;
2126280849Scy		}
2127280849Scy	}
2128280849Scy	return (failed == 0) ? 0 : 1;
212982498Sroberto}
2130280849Scy#endif	/* TEST_SNPRINTF */
2131280849Scy
2132280849Scy/* vim: set joinspaces textwidth=80: */
2133