1290000Sglebius/*
2290000Sglebius * Modified by Dave Hart for integration into NTP 4.2.7 <hart@ntp.org>
3290000Sglebius *
4290000Sglebius * Changed in a backwards-incompatible way to separate HAVE_SNPRINTF
5290000Sglebius * from HW_WANT_RPL_SNPRINTF, etc. for each of the four replaced
6290000Sglebius * functions.
7290000Sglebius *
8290000Sglebius * Changed to honor hw_force_rpl_snprintf=yes, etc.  This is used by NTP
9290000Sglebius * to test rpl_snprintf() and rpl_vsnprintf() on platforms which provide
10290000Sglebius * C99-compliant implementations.
11290000Sglebius */
12290000Sglebius
13290000Sglebius/* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */
14290000Sglebius
15290000Sglebius/*
16290000Sglebius * Copyright (c) 1995 Patrick Powell.
17290000Sglebius *
18290000Sglebius * This code is based on code written by Patrick Powell <papowell@astart.com>.
19290000Sglebius * It may be used for any purpose as long as this notice remains intact on all
20290000Sglebius * source code distributions.
21290000Sglebius */
22290000Sglebius
23290000Sglebius/*
24290000Sglebius * Copyright (c) 2008 Holger Weiss.
25290000Sglebius *
26290000Sglebius * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
27290000Sglebius * My changes to the code may freely be used, modified and/or redistributed for
28290000Sglebius * any purpose.  It would be nice if additions and fixes to this file (including
29290000Sglebius * trivial code cleanups) would be sent back in order to let me include them in
30290000Sglebius * the version available at <http://www.jhweiss.de/software/snprintf.html>.
31290000Sglebius * However, this is not a requirement for using or redistributing (possibly
32290000Sglebius * modified) versions of this file, nor is leaving this notice intact mandatory.
33290000Sglebius */
34290000Sglebius
35290000Sglebius/*
36290000Sglebius * History
37290000Sglebius *
38290000Sglebius * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
39290000Sglebius *
40290000Sglebius * 	Fixed the detection of infinite floating point values on IRIX (and
41290000Sglebius * 	possibly other systems) and applied another few minor cleanups.
42290000Sglebius *
43290000Sglebius * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
44290000Sglebius *
45290000Sglebius * 	Added a lot of new features, fixed many bugs, and incorporated various
46290000Sglebius * 	improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
47290000Sglebius * 	<rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
48290000Sglebius * 	<djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
49290000Sglebius * 	projects.  The additions include: support the "e", "E", "g", "G", and
50290000Sglebius * 	"F" conversion specifiers (and use conversion style "f" or "F" for the
51290000Sglebius * 	still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
52290000Sglebius * 	"t", and "z" length modifiers; support the "#" flag and the (non-C99)
53290000Sglebius * 	"'" flag; use localeconv(3) (if available) to get both the current
54290000Sglebius * 	locale's decimal point character and the separator between groups of
55290000Sglebius * 	digits; fix the handling of various corner cases of field width and
56290000Sglebius * 	precision specifications; fix various floating point conversion bugs;
57290000Sglebius * 	handle infinite and NaN floating point values; don't attempt to write to
58290000Sglebius * 	the output buffer (which may be NULL) if a size of zero was specified;
59290000Sglebius * 	check for integer overflow of the field width, precision, and return
60290000Sglebius * 	values and during the floating point conversion; use the OUTCHAR() macro
61290000Sglebius * 	instead of a function for better performance; provide asprintf(3) and
62290000Sglebius * 	vasprintf(3) functions; add new test cases.  The replacement functions
63290000Sglebius * 	have been renamed to use an "rpl_" prefix, the function calls in the
64290000Sglebius * 	main project (and in this file) must be redefined accordingly for each
65290000Sglebius * 	replacement function which is needed (by using Autoconf or other means).
66290000Sglebius * 	Various other minor improvements have been applied and the coding style
67290000Sglebius * 	was cleaned up for consistency.
68290000Sglebius *
69290000Sglebius * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
70290000Sglebius *
71290000Sglebius * 	C99 compliant snprintf(3) and vsnprintf(3) functions return the number
72290000Sglebius * 	of characters that would have been written to a sufficiently sized
73290000Sglebius * 	buffer (excluding the '\0').  The original code simply returned the
74290000Sglebius * 	length of the resulting output string, so that's been fixed.
75290000Sglebius *
76290000Sglebius * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
77290000Sglebius *
78290000Sglebius * 	The original code assumed that both snprintf(3) and vsnprintf(3) were
79290000Sglebius * 	missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
80290000Sglebius * 	the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
81290000Sglebius *
82290000Sglebius * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
83290000Sglebius *
84290000Sglebius * 	The PGP code was using unsigned hexadecimal formats.  Unfortunately,
85290000Sglebius * 	unsigned formats simply didn't work.
86290000Sglebius *
87290000Sglebius * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
88290000Sglebius *
89290000Sglebius * 	Ok, added some minimal floating point support, which means this probably
90290000Sglebius * 	requires libm on most operating systems.  Don't yet support the exponent
91290000Sglebius * 	(e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
92290000Sglebius * 	wasn't being exercised in ways which showed it, so that's been fixed.
93290000Sglebius * 	Also, formatted the code to Mutt conventions, and removed dead code left
94290000Sglebius * 	over from the original.  Also, there is now a builtin-test, run with:
95290000Sglebius * 	gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
96290000Sglebius *
97290000Sglebius * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
98290000Sglebius *
99290000Sglebius * 	This was ugly.  It is still ugly.  I opted out of floating point
100290000Sglebius * 	numbers, but the formatter understands just about everything from the
101290000Sglebius * 	normal C string format, at least as far as I can tell from the Solaris
102290000Sglebius * 	2.5 printf(3S) man page.
103290000Sglebius */
104290000Sglebius
105290000Sglebius/*
106290000Sglebius * ToDo
107290000Sglebius *
108290000Sglebius * - Add wide character support.
109290000Sglebius * - Add support for "%a" and "%A" conversions.
110290000Sglebius * - Create test routines which predefine the expected results.  Our test cases
111290000Sglebius *   usually expose bugs in system implementations rather than in ours :-)
112290000Sglebius */
113290000Sglebius
114290000Sglebius/*
115290000Sglebius * Usage
116290000Sglebius *
117290000Sglebius * 1) The following preprocessor macros should be defined to 1 if the feature or
118290000Sglebius *    file in question is available on the target system (by using Autoconf or
119290000Sglebius *    other means), though basic functionality should be available as long as
120290000Sglebius *    HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
121290000Sglebius *
122290000Sglebius *	HW_WANT_RPL_VSNPRINTF
123290000Sglebius *	HW_WANT_RPL_SNPRINTF
124290000Sglebius *	HW_WANT_RPL_VASPRINTF
125290000Sglebius *	HW_WANT_RPL_ASPRINTF
126290000Sglebius *	HAVE_VSNPRINTF	// define to 1 #if HW_WANT_RPL_VSNPRINTF
127290000Sglebius *	HAVE_SNPRINTF	// define to 1 #if HW_WANT_RPL_SNPRINTF
128290000Sglebius *	HAVE_VASPRINTF	// define to 1 #if HW_WANT_RPL_VASPRINTF
129290000Sglebius *	HAVE_ASPRINTF	// define to 1 #if HW_WANT_RPL_ASPRINTF
130290000Sglebius *	HAVE_STDARG_H
131290000Sglebius *	HAVE_STDDEF_H
132290000Sglebius *	HAVE_STDINT_H
133290000Sglebius *	HAVE_STDLIB_H
134290000Sglebius *	HAVE_INTTYPES_H
135290000Sglebius *	HAVE_LOCALE_H
136290000Sglebius *	HAVE_LOCALECONV
137290000Sglebius *	HAVE_LCONV_DECIMAL_POINT
138290000Sglebius *	HAVE_LCONV_THOUSANDS_SEP
139290000Sglebius *	HAVE_LONG_DOUBLE
140290000Sglebius *	HAVE_LONG_LONG_INT
141290000Sglebius *	HAVE_UNSIGNED_LONG_LONG_INT
142290000Sglebius *	HAVE_INTMAX_T
143290000Sglebius *	HAVE_UINTMAX_T
144290000Sglebius *	HAVE_UINTPTR_T
145290000Sglebius *	HAVE_PTRDIFF_T
146290000Sglebius *	HAVE_VA_COPY
147290000Sglebius *	HAVE___VA_COPY
148290000Sglebius *
149290000Sglebius * 2) The calls to the functions which should be replaced must be redefined
150290000Sglebius *    throughout the project files (by using Autoconf or other means):
151290000Sglebius *
152290000Sglebius *	#if HW_WANT_RPL_VSNPRINTF
153290000Sglebius *	#define vsnprintf rpl_vsnprintf
154290000Sglebius *	#endif
155290000Sglebius *	#if HW_WANT_RPL_SNPRINTF
156290000Sglebius *	#define snprintf rpl_snprintf
157290000Sglebius *	#endif
158290000Sglebius *	#if HW_WANT_RPL_VASPRINTF
159290000Sglebius *	#define vasprintf rpl_vasprintf
160290000Sglebius *	#endif
161290000Sglebius *	#if HW_WANT_RPL_ASPRINTF
162290000Sglebius *	#define asprintf rpl_asprintf
163290000Sglebius *	#endif
164290000Sglebius *
165290000Sglebius * 3) The required replacement functions should be declared in some header file
166290000Sglebius *    included throughout the project files:
167290000Sglebius *
168290000Sglebius *	#if HAVE_CONFIG_H
169290000Sglebius *	#include <config.h>
170290000Sglebius *	#endif
171290000Sglebius *	#if HAVE_STDARG_H
172290000Sglebius *	#include <stdarg.h>
173290000Sglebius *	#if HW_WANT_RPL_VSNPRINTF
174290000Sglebius *	int rpl_vsnprintf(char *, size_t, const char *, va_list);
175290000Sglebius *	#endif
176290000Sglebius *	#if HW_WANT_RPL_SNPRINTF
177290000Sglebius *	int rpl_snprintf(char *, size_t, const char *, ...);
178290000Sglebius *	#endif
179290000Sglebius *	#if HW_WANT_RPL_VASPRINTF
180290000Sglebius *	int rpl_vasprintf(char **, const char *, va_list);
181290000Sglebius *	#endif
182290000Sglebius *	#if HW_WANT_RPL_ASPRINTF
183290000Sglebius *	int rpl_asprintf(char **, const char *, ...);
184290000Sglebius *	#endif
185290000Sglebius *	#endif
186290000Sglebius *
187290000Sglebius * Autoconf macros for handling step 1 and step 2 are available at
188290000Sglebius * <http://www.jhweiss.de/software/snprintf.html>.
189290000Sglebius */
190290000Sglebius
191290000Sglebius#if HAVE_CONFIG_H
19282498Sroberto#include <config.h>
193290000Sglebius#endif	/* HAVE_CONFIG_H */
19482498Sroberto
195290000Sglebius#if TEST_SNPRINTF
196290000Sglebius#include <math.h>	/* For pow(3), NAN, and INFINITY. */
197290000Sglebius#include <string.h>	/* For strcmp(3). */
198290000Sglebius#if defined(__NetBSD__) || \
199290000Sglebius    defined(__FreeBSD__) || \
200290000Sglebius    defined(__OpenBSD__) || \
201290000Sglebius    defined(__NeXT__) || \
202290000Sglebius    defined(__bsd__)
203290000Sglebius#define OS_BSD 1
204290000Sglebius#elif defined(sgi) || defined(__sgi)
205290000Sglebius#ifndef __c99
206290000Sglebius#define __c99	/* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
207290000Sglebius#endif	/* !defined(__c99) */
208290000Sglebius#define OS_IRIX 1
209290000Sglebius#define OS_SYSV 1
210290000Sglebius#elif defined(__svr4__)
211290000Sglebius#define OS_SYSV 1
212290000Sglebius#elif defined(__linux__)
213290000Sglebius#define OS_LINUX 1
214290000Sglebius#endif	/* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
215290000Sglebius#if HAVE_CONFIG_H	/* Undefine definitions possibly done in config.h. */
216290000Sglebius#ifdef HAVE_SNPRINTF
217290000Sglebius#undef HAVE_SNPRINTF
218290000Sglebius#endif	/* defined(HAVE_SNPRINTF) */
219290000Sglebius#ifdef HAVE_VSNPRINTF
220290000Sglebius#undef HAVE_VSNPRINTF
221290000Sglebius#endif	/* defined(HAVE_VSNPRINTF) */
222290000Sglebius#ifdef HAVE_ASPRINTF
223290000Sglebius#undef HAVE_ASPRINTF
224290000Sglebius#endif	/* defined(HAVE_ASPRINTF) */
225290000Sglebius#ifdef HAVE_VASPRINTF
226290000Sglebius#undef HAVE_VASPRINTF
227290000Sglebius#endif	/* defined(HAVE_VASPRINTF) */
228290000Sglebius#ifdef snprintf
229290000Sglebius#undef snprintf
230290000Sglebius#endif	/* defined(snprintf) */
231290000Sglebius#ifdef vsnprintf
232290000Sglebius#undef vsnprintf
233290000Sglebius#endif	/* defined(vsnprintf) */
234290000Sglebius#ifdef asprintf
235290000Sglebius#undef asprintf
236290000Sglebius#endif	/* defined(asprintf) */
237290000Sglebius#ifdef vasprintf
238290000Sglebius#undef vasprintf
239290000Sglebius#endif	/* defined(vasprintf) */
240290000Sglebius#else	/* By default, we assume a modern system for testing. */
241290000Sglebius#ifndef HAVE_STDARG_H
242290000Sglebius#define HAVE_STDARG_H 1
243290000Sglebius#endif	/* HAVE_STDARG_H */
244290000Sglebius#ifndef HAVE_STDDEF_H
245290000Sglebius#define HAVE_STDDEF_H 1
246290000Sglebius#endif	/* HAVE_STDDEF_H */
247290000Sglebius#ifndef HAVE_STDINT_H
248290000Sglebius#define HAVE_STDINT_H 1
249290000Sglebius#endif	/* HAVE_STDINT_H */
250290000Sglebius#ifndef HAVE_STDLIB_H
251290000Sglebius#define HAVE_STDLIB_H 1
252290000Sglebius#endif	/* HAVE_STDLIB_H */
253290000Sglebius#ifndef HAVE_INTTYPES_H
254290000Sglebius#define HAVE_INTTYPES_H 1
255290000Sglebius#endif	/* HAVE_INTTYPES_H */
256290000Sglebius#ifndef HAVE_LOCALE_H
257290000Sglebius#define HAVE_LOCALE_H 1
258290000Sglebius#endif	/* HAVE_LOCALE_H */
259290000Sglebius#ifndef HAVE_LOCALECONV
260290000Sglebius#define HAVE_LOCALECONV 1
261290000Sglebius#endif	/* !defined(HAVE_LOCALECONV) */
262290000Sglebius#ifndef HAVE_LCONV_DECIMAL_POINT
263290000Sglebius#define HAVE_LCONV_DECIMAL_POINT 1
264290000Sglebius#endif	/* HAVE_LCONV_DECIMAL_POINT */
265290000Sglebius#ifndef HAVE_LCONV_THOUSANDS_SEP
266290000Sglebius#define HAVE_LCONV_THOUSANDS_SEP 1
267290000Sglebius#endif	/* HAVE_LCONV_THOUSANDS_SEP */
268290000Sglebius#ifndef HAVE_LONG_DOUBLE
269290000Sglebius#define HAVE_LONG_DOUBLE 1
270290000Sglebius#endif	/* !defined(HAVE_LONG_DOUBLE) */
271290000Sglebius#ifndef HAVE_LONG_LONG_INT
272290000Sglebius#define HAVE_LONG_LONG_INT 1
273290000Sglebius#endif	/* !defined(HAVE_LONG_LONG_INT) */
274290000Sglebius#ifndef HAVE_UNSIGNED_LONG_LONG_INT
275290000Sglebius#define HAVE_UNSIGNED_LONG_LONG_INT 1
276290000Sglebius#endif	/* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
277290000Sglebius#ifndef HAVE_INTMAX_T
278290000Sglebius#define HAVE_INTMAX_T 1
279290000Sglebius#endif	/* !defined(HAVE_INTMAX_T) */
280290000Sglebius#ifndef HAVE_UINTMAX_T
281290000Sglebius#define HAVE_UINTMAX_T 1
282290000Sglebius#endif	/* !defined(HAVE_UINTMAX_T) */
283290000Sglebius#ifndef HAVE_UINTPTR_T
284290000Sglebius#define HAVE_UINTPTR_T 1
285290000Sglebius#endif	/* !defined(HAVE_UINTPTR_T) */
286290000Sglebius#ifndef HAVE_PTRDIFF_T
287290000Sglebius#define HAVE_PTRDIFF_T 1
288290000Sglebius#endif	/* !defined(HAVE_PTRDIFF_T) */
289290000Sglebius#ifndef HAVE_VA_COPY
290290000Sglebius#define HAVE_VA_COPY 1
291290000Sglebius#endif	/* !defined(HAVE_VA_COPY) */
292290000Sglebius#ifndef HAVE___VA_COPY
293290000Sglebius#define HAVE___VA_COPY 1
294290000Sglebius#endif	/* !defined(HAVE___VA_COPY) */
295290000Sglebius#endif	/* HAVE_CONFIG_H */
296290000Sglebius#define snprintf rpl_snprintf
297290000Sglebius#define vsnprintf rpl_vsnprintf
298290000Sglebius#define asprintf rpl_asprintf
299290000Sglebius#define vasprintf rpl_vasprintf
300290000Sglebius#endif	/* TEST_SNPRINTF */
30182498Sroberto
302290000Sglebius#if HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || HW_WANT_RPL_VASPRINTF
303290000Sglebius#include <stdio.h>	/* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
304290000Sglebius#ifdef VA_START
305290000Sglebius#undef VA_START
306290000Sglebius#endif	/* defined(VA_START) */
307290000Sglebius#ifdef VA_SHIFT
308290000Sglebius#undef VA_SHIFT
309290000Sglebius#endif	/* defined(VA_SHIFT) */
310290000Sglebius#if HAVE_STDARG_H
31182498Sroberto#include <stdarg.h>
312290000Sglebius#define VA_START(ap, last) va_start(ap, last)
313290000Sglebius#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
314290000Sglebius#else	/* Assume <varargs.h> is available. */
31582498Sroberto#include <varargs.h>
316290000Sglebius#define VA_START(ap, last) va_start(ap)	/* "last" is ignored. */
317290000Sglebius#define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
318290000Sglebius#endif	/* HAVE_STDARG_H */
31982498Sroberto
320290000Sglebius#if HW_WANT_RPL_VASPRINTF
321290000Sglebius#if HAVE_STDLIB_H
322290000Sglebius#include <stdlib.h>	/* For malloc(3). */
323290000Sglebius#endif	/* HAVE_STDLIB_H */
324290000Sglebius#ifdef VA_COPY
325290000Sglebius#undef VA_COPY
326290000Sglebius#endif	/* defined(VA_COPY) */
327290000Sglebius#ifdef VA_END_COPY
328290000Sglebius#undef VA_END_COPY
329290000Sglebius#endif	/* defined(VA_END_COPY) */
330290000Sglebius#if HAVE_VA_COPY
331290000Sglebius#define VA_COPY(dest, src) va_copy(dest, src)
332290000Sglebius#define VA_END_COPY(ap) va_end(ap)
333290000Sglebius#elif HAVE___VA_COPY
334290000Sglebius#define VA_COPY(dest, src) __va_copy(dest, src)
335290000Sglebius#define VA_END_COPY(ap) va_end(ap)
336290000Sglebius#else
337290000Sglebius#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
338290000Sglebius#define VA_END_COPY(ap) /* No-op. */
339290000Sglebius#define NEED_MYMEMCPY 1
340290000Sglebiusstatic void *mymemcpy(void *, void *, size_t);
341290000Sglebius#endif	/* HAVE_VA_COPY */
342290000Sglebius#endif	/* HW_WANT_RPL_VASPRINTF */
343132451Sroberto
344290000Sglebius#if HW_WANT_RPL_VSNPRINTF
345290000Sglebius#include <errno.h>	/* For ERANGE and errno. */
346290000Sglebius#include <limits.h>	/* For *_MAX. */
347290000Sglebius#if HAVE_INTTYPES_H
348290000Sglebius#include <inttypes.h>	/* For intmax_t (if not defined in <stdint.h>). */
349290000Sglebius#endif	/* HAVE_INTTYPES_H */
350290000Sglebius#if HAVE_LOCALE_H
351290000Sglebius#include <locale.h>	/* For localeconv(3). */
352290000Sglebius#endif	/* HAVE_LOCALE_H */
353290000Sglebius#if HAVE_STDDEF_H
354290000Sglebius#include <stddef.h>	/* For ptrdiff_t. */
355290000Sglebius#endif	/* HAVE_STDDEF_H */
356290000Sglebius#if HAVE_STDINT_H
357290000Sglebius#include <stdint.h>	/* For intmax_t. */
358290000Sglebius#endif	/* HAVE_STDINT_H */
359290000Sglebius
360290000Sglebius/* Support for unsigned long long int.  We may also need ULLONG_MAX. */
361290000Sglebius#ifndef ULONG_MAX	/* We may need ULONG_MAX as a fallback. */
362290000Sglebius#ifdef UINT_MAX
363290000Sglebius#define ULONG_MAX UINT_MAX
36482498Sroberto#else
365290000Sglebius#define ULONG_MAX INT_MAX
366290000Sglebius#endif	/* defined(UINT_MAX) */
367290000Sglebius#endif	/* !defined(ULONG_MAX) */
368290000Sglebius#ifdef ULLONG
369290000Sglebius#undef ULLONG
370290000Sglebius#endif	/* defined(ULLONG) */
371290000Sglebius#if HAVE_UNSIGNED_LONG_LONG_INT
372290000Sglebius#define ULLONG unsigned long long int
373290000Sglebius#ifndef ULLONG_MAX
374290000Sglebius#define ULLONG_MAX ULONG_MAX
375290000Sglebius#endif	/* !defined(ULLONG_MAX) */
376290000Sglebius#else
377290000Sglebius#define ULLONG unsigned long int
378290000Sglebius#ifdef ULLONG_MAX
379290000Sglebius#undef ULLONG_MAX
380290000Sglebius#endif	/* defined(ULLONG_MAX) */
381290000Sglebius#define ULLONG_MAX ULONG_MAX
382290000Sglebius#endif	/* HAVE_LONG_LONG_INT */
383290000Sglebius
384290000Sglebius/* Support for uintmax_t.  We also need UINTMAX_MAX. */
385290000Sglebius#ifdef UINTMAX_T
386290000Sglebius#undef UINTMAX_T
387290000Sglebius#endif	/* defined(UINTMAX_T) */
388290000Sglebius#if HAVE_UINTMAX_T || defined(uintmax_t)
389290000Sglebius#define UINTMAX_T uintmax_t
390290000Sglebius#ifndef UINTMAX_MAX
391290000Sglebius#define UINTMAX_MAX ULLONG_MAX
392290000Sglebius#endif	/* !defined(UINTMAX_MAX) */
393290000Sglebius#else
394290000Sglebius#define UINTMAX_T ULLONG
395290000Sglebius#ifdef UINTMAX_MAX
396290000Sglebius#undef UINTMAX_MAX
397290000Sglebius#endif	/* defined(UINTMAX_MAX) */
398290000Sglebius#define UINTMAX_MAX ULLONG_MAX
399290000Sglebius#endif	/* HAVE_UINTMAX_T || defined(uintmax_t) */
400290000Sglebius
401290000Sglebius/* Support for long double. */
402290000Sglebius#ifndef LDOUBLE
403290000Sglebius#if HAVE_LONG_DOUBLE
404290000Sglebius#define LDOUBLE long double
405290000Sglebius#else
406290000Sglebius#define LDOUBLE double
407290000Sglebius#endif	/* HAVE_LONG_DOUBLE */
408290000Sglebius#endif	/* !defined(LDOUBLE) */
409290000Sglebius
410290000Sglebius/* Support for long long int. */
411290000Sglebius#ifndef LLONG
412290000Sglebius#if HAVE_LONG_LONG_INT
413290000Sglebius#define LLONG long long int
414290000Sglebius#else
415290000Sglebius#define LLONG long int
416290000Sglebius#endif	/* HAVE_LONG_LONG_INT */
417290000Sglebius#endif	/* !defined(LLONG) */
418290000Sglebius
419290000Sglebius/* Support for intmax_t. */
420290000Sglebius#ifndef INTMAX_T
421290000Sglebius#if HAVE_INTMAX_T || defined(intmax_t)
422290000Sglebius#define INTMAX_T intmax_t
423290000Sglebius#else
424290000Sglebius#define INTMAX_T LLONG
425290000Sglebius#endif	/* HAVE_INTMAX_T || defined(intmax_t) */
426290000Sglebius#endif	/* !defined(INTMAX_T) */
427290000Sglebius
428290000Sglebius/* Support for uintptr_t. */
429290000Sglebius#ifndef UINTPTR_T
430290000Sglebius#if HAVE_UINTPTR_T || defined(uintptr_t)
431290000Sglebius#define UINTPTR_T uintptr_t
432290000Sglebius#else
433290000Sglebius#define UINTPTR_T unsigned long int
434290000Sglebius#endif	/* HAVE_UINTPTR_T || defined(uintptr_t) */
435290000Sglebius#endif	/* !defined(UINTPTR_T) */
436290000Sglebius
437290000Sglebius/* Support for ptrdiff_t. */
438290000Sglebius#ifndef PTRDIFF_T
439290000Sglebius#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
440290000Sglebius#define PTRDIFF_T ptrdiff_t
441290000Sglebius#else
442290000Sglebius#define PTRDIFF_T long int
443290000Sglebius#endif	/* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
444290000Sglebius#endif	/* !defined(PTRDIFF_T) */
445290000Sglebius
446290000Sglebius/*
447290000Sglebius * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
448290000Sglebius * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
449290000Sglebius * unsigned type if necessary.  This should work just fine in practice.
450290000Sglebius */
451290000Sglebius#ifndef UPTRDIFF_T
452290000Sglebius#define UPTRDIFF_T PTRDIFF_T
453290000Sglebius#endif	/* !defined(UPTRDIFF_T) */
454290000Sglebius
455290000Sglebius/*
456290000Sglebius * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
457290000Sglebius * However, we'll simply use size_t and convert it to a signed type if
458290000Sglebius * necessary.  This should work just fine in practice.
459290000Sglebius */
460290000Sglebius#ifndef SSIZE_T
461290000Sglebius#define SSIZE_T size_t
462290000Sglebius#endif	/* !defined(SSIZE_T) */
463290000Sglebius
464290000Sglebius/* Either ERANGE or E2BIG should be available everywhere. */
465290000Sglebius#ifndef ERANGE
466290000Sglebius#define ERANGE E2BIG
467290000Sglebius#endif	/* !defined(ERANGE) */
468290000Sglebius#ifndef EOVERFLOW
469290000Sglebius#define EOVERFLOW ERANGE
470290000Sglebius#endif	/* !defined(EOVERFLOW) */
471290000Sglebius
472290000Sglebius/*
473290000Sglebius * Buffer size to hold the octal string representation of UINT128_MAX without
474290000Sglebius * nul-termination ("3777777777777777777777777777777777777777777").
475290000Sglebius */
476290000Sglebius#ifdef MAX_CONVERT_LENGTH
477290000Sglebius#undef MAX_CONVERT_LENGTH
478290000Sglebius#endif	/* defined(MAX_CONVERT_LENGTH) */
479290000Sglebius#define MAX_CONVERT_LENGTH      43
480290000Sglebius
481290000Sglebius/* Format read states. */
482290000Sglebius#define PRINT_S_DEFAULT         0
483290000Sglebius#define PRINT_S_FLAGS           1
484290000Sglebius#define PRINT_S_WIDTH           2
485290000Sglebius#define PRINT_S_DOT             3
486290000Sglebius#define PRINT_S_PRECISION       4
487290000Sglebius#define PRINT_S_MOD             5
488290000Sglebius#define PRINT_S_CONV            6
489290000Sglebius
490290000Sglebius/* Format flags. */
491290000Sglebius#define PRINT_F_MINUS           (1 << 0)
492290000Sglebius#define PRINT_F_PLUS            (1 << 1)
493290000Sglebius#define PRINT_F_SPACE           (1 << 2)
494290000Sglebius#define PRINT_F_NUM             (1 << 3)
495290000Sglebius#define PRINT_F_ZERO            (1 << 4)
496290000Sglebius#define PRINT_F_QUOTE           (1 << 5)
497290000Sglebius#define PRINT_F_UP              (1 << 6)
498290000Sglebius#define PRINT_F_UNSIGNED        (1 << 7)
499290000Sglebius#define PRINT_F_TYPE_G          (1 << 8)
500290000Sglebius#define PRINT_F_TYPE_E          (1 << 9)
501290000Sglebius
502290000Sglebius/* Conversion flags. */
503290000Sglebius#define PRINT_C_CHAR            1
504290000Sglebius#define PRINT_C_SHORT           2
505290000Sglebius#define PRINT_C_LONG            3
506290000Sglebius#define PRINT_C_LLONG           4
507290000Sglebius#define PRINT_C_LDOUBLE         5
508290000Sglebius#define PRINT_C_SIZE            6
509290000Sglebius#define PRINT_C_PTRDIFF         7
510290000Sglebius#define PRINT_C_INTMAX          8
511290000Sglebius
512290000Sglebius#ifndef MAX
513290000Sglebius#define MAX(x, y) ((x >= y) ? x : y)
514290000Sglebius#endif	/* !defined(MAX) */
515290000Sglebius#ifndef CHARTOINT
516290000Sglebius#define CHARTOINT(ch) (ch - '0')
517290000Sglebius#endif	/* !defined(CHARTOINT) */
518290000Sglebius#ifndef ISDIGIT
519290000Sglebius#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
520290000Sglebius#endif	/* !defined(ISDIGIT) */
521290000Sglebius#ifndef ISNAN
522290000Sglebius#define ISNAN(x) (x != x)
523290000Sglebius#endif	/* !defined(ISNAN) */
524290000Sglebius#ifndef ISINF
525290000Sglebius#define ISINF(x) (x != 0.0 && x + x == x)
526290000Sglebius#endif	/* !defined(ISINF) */
527290000Sglebius
528290000Sglebius#ifdef OUTCHAR
529290000Sglebius#undef OUTCHAR
530290000Sglebius#endif	/* defined(OUTCHAR) */
531290000Sglebius#define OUTCHAR(str, len, size, ch)                                          \
532290000Sglebiusdo {                                                                         \
533290000Sglebius	if (len + 1 < size)                                                  \
534290000Sglebius		str[len] = ch;                                               \
535290000Sglebius	(len)++;                                                             \
536290000Sglebius} while (/* CONSTCOND */ 0)
537290000Sglebius
538290000Sglebiusstatic void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
539290000Sglebiusstatic void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
540290000Sglebiusstatic void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
541290000Sglebiusstatic void printsep(char *, size_t *, size_t);
542290000Sglebiusstatic int getnumsep(int);
543290000Sglebiusstatic int getexponent(LDOUBLE);
544290000Sglebiusstatic int convert(UINTMAX_T, char *, size_t, int, int);
545290000Sglebiusstatic UINTMAX_T cast(LDOUBLE);
546290000Sglebiusstatic UINTMAX_T myround(LDOUBLE);
547290000Sglebiusstatic LDOUBLE mypow10(int);
548290000Sglebius
549290000Sglebiusint
550290000Sglebiusrpl_vsnprintf(char *str, size_t size, const char *format, va_list args);
551290000Sglebius
552290000Sglebiusint
553290000Sglebiusrpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
554290000Sglebius{
555290000Sglebius	LDOUBLE fvalue;
556290000Sglebius	INTMAX_T value;
557290000Sglebius	unsigned char cvalue;
558290000Sglebius	const char *strvalue;
559290000Sglebius	INTMAX_T *intmaxptr;
560290000Sglebius	PTRDIFF_T *ptrdiffptr;
561290000Sglebius	SSIZE_T *sizeptr;
562290000Sglebius	LLONG *llongptr;
563290000Sglebius	long int *longptr;
564290000Sglebius	int *intptr;
565290000Sglebius	short int *shortptr;
566290000Sglebius	signed char *charptr;
567290000Sglebius	size_t len = 0;
568290000Sglebius	int overflow = 0;
569290000Sglebius	int base = 0;
570290000Sglebius	int cflags = 0;
571290000Sglebius	int flags = 0;
572290000Sglebius	int width = 0;
573290000Sglebius	int precision = -1;
574290000Sglebius	int state = PRINT_S_DEFAULT;
575290000Sglebius	char ch = *format++;
576290000Sglebius
577290000Sglebius	/*
578290000Sglebius	 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
579290000Sglebius	 * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
580290000Sglebius	 * even if a size larger than zero was specified.  At least NetBSD's
581290000Sglebius	 * snprintf(3) does the same, as well as other versions of this file.
582290000Sglebius	 * (Though some of these versions will write to a non-NULL buffer even
583290000Sglebius	 * if a size of zero was specified, which violates the standard.)
584290000Sglebius	 */
585290000Sglebius	if (str == NULL && size != 0)
586290000Sglebius		size = 0;
587290000Sglebius
588290000Sglebius	while (ch != '\0')
589290000Sglebius		switch (state) {
590290000Sglebius		case PRINT_S_DEFAULT:
591290000Sglebius			if (ch == '%')
592290000Sglebius				state = PRINT_S_FLAGS;
593290000Sglebius			else
594290000Sglebius				OUTCHAR(str, len, size, ch);
595290000Sglebius			ch = *format++;
596290000Sglebius			break;
597290000Sglebius		case PRINT_S_FLAGS:
598290000Sglebius			switch (ch) {
599290000Sglebius			case '-':
600290000Sglebius				flags |= PRINT_F_MINUS;
601290000Sglebius				ch = *format++;
602290000Sglebius				break;
603290000Sglebius			case '+':
604290000Sglebius				flags |= PRINT_F_PLUS;
605290000Sglebius				ch = *format++;
606290000Sglebius				break;
607290000Sglebius			case ' ':
608290000Sglebius				flags |= PRINT_F_SPACE;
609290000Sglebius				ch = *format++;
610290000Sglebius				break;
611290000Sglebius			case '#':
612290000Sglebius				flags |= PRINT_F_NUM;
613290000Sglebius				ch = *format++;
614290000Sglebius				break;
615290000Sglebius			case '0':
616290000Sglebius				flags |= PRINT_F_ZERO;
617290000Sglebius				ch = *format++;
618290000Sglebius				break;
619290000Sglebius			case '\'':	/* SUSv2 flag (not in C99). */
620290000Sglebius				flags |= PRINT_F_QUOTE;
621290000Sglebius				ch = *format++;
622290000Sglebius				break;
623290000Sglebius			default:
624290000Sglebius				state = PRINT_S_WIDTH;
625290000Sglebius				break;
626290000Sglebius			}
627290000Sglebius			break;
628290000Sglebius		case PRINT_S_WIDTH:
629290000Sglebius			if (ISDIGIT(ch)) {
630290000Sglebius				ch = CHARTOINT(ch);
631290000Sglebius				if (width > (INT_MAX - ch) / 10) {
632290000Sglebius					overflow = 1;
633290000Sglebius					goto out;
634290000Sglebius				}
635290000Sglebius				width = 10 * width + ch;
636290000Sglebius				ch = *format++;
637290000Sglebius			} else if (ch == '*') {
638290000Sglebius				/*
639290000Sglebius				 * C99 says: "A negative field width argument is
640290000Sglebius				 * taken as a `-' flag followed by a positive
641290000Sglebius				 * field width." (7.19.6.1, 5)
642290000Sglebius				 */
643290000Sglebius				if ((width = va_arg(args, int)) < 0) {
644290000Sglebius					flags |= PRINT_F_MINUS;
645290000Sglebius					width = -width;
646290000Sglebius				}
647290000Sglebius				ch = *format++;
648290000Sglebius				state = PRINT_S_DOT;
649290000Sglebius			} else
650290000Sglebius				state = PRINT_S_DOT;
651290000Sglebius			break;
652290000Sglebius		case PRINT_S_DOT:
653290000Sglebius			if (ch == '.') {
654290000Sglebius				state = PRINT_S_PRECISION;
655290000Sglebius				ch = *format++;
656290000Sglebius			} else
657290000Sglebius				state = PRINT_S_MOD;
658290000Sglebius			break;
659290000Sglebius		case PRINT_S_PRECISION:
660290000Sglebius			if (precision == -1)
661290000Sglebius				precision = 0;
662290000Sglebius			if (ISDIGIT(ch)) {
663290000Sglebius				ch = CHARTOINT(ch);
664290000Sglebius				if (precision > (INT_MAX - ch) / 10) {
665290000Sglebius					overflow = 1;
666290000Sglebius					goto out;
667290000Sglebius				}
668290000Sglebius				precision = 10 * precision + ch;
669290000Sglebius				ch = *format++;
670290000Sglebius			} else if (ch == '*') {
671290000Sglebius				/*
672290000Sglebius				 * C99 says: "A negative precision argument is
673290000Sglebius				 * taken as if the precision were omitted."
674290000Sglebius				 * (7.19.6.1, 5)
675290000Sglebius				 */
676290000Sglebius				if ((precision = va_arg(args, int)) < 0)
677290000Sglebius					precision = -1;
678290000Sglebius				ch = *format++;
679290000Sglebius				state = PRINT_S_MOD;
680290000Sglebius			} else
681290000Sglebius				state = PRINT_S_MOD;
682290000Sglebius			break;
683290000Sglebius		case PRINT_S_MOD:
684290000Sglebius			switch (ch) {
685290000Sglebius			case 'h':
686290000Sglebius				ch = *format++;
687290000Sglebius				if (ch == 'h') {	/* It's a char. */
688290000Sglebius					ch = *format++;
689290000Sglebius					cflags = PRINT_C_CHAR;
690290000Sglebius				} else
691290000Sglebius					cflags = PRINT_C_SHORT;
692290000Sglebius				break;
693290000Sglebius			case 'l':
694290000Sglebius				ch = *format++;
695290000Sglebius				if (ch == 'l') {	/* It's a long long. */
696290000Sglebius					ch = *format++;
697290000Sglebius					cflags = PRINT_C_LLONG;
698290000Sglebius				} else
699290000Sglebius					cflags = PRINT_C_LONG;
700290000Sglebius				break;
701290000Sglebius			case 'L':
702290000Sglebius				cflags = PRINT_C_LDOUBLE;
703290000Sglebius				ch = *format++;
704290000Sglebius				break;
705290000Sglebius			case 'j':
706290000Sglebius				cflags = PRINT_C_INTMAX;
707290000Sglebius				ch = *format++;
708290000Sglebius				break;
709290000Sglebius			case 't':
710290000Sglebius				cflags = PRINT_C_PTRDIFF;
711290000Sglebius				ch = *format++;
712290000Sglebius				break;
713290000Sglebius			case 'z':
714290000Sglebius				cflags = PRINT_C_SIZE;
715290000Sglebius				ch = *format++;
716290000Sglebius				break;
717290000Sglebius			}
718290000Sglebius			state = PRINT_S_CONV;
719290000Sglebius			break;
720290000Sglebius		case PRINT_S_CONV:
721290000Sglebius			switch (ch) {
722290000Sglebius			case 'd':
723290000Sglebius				/* FALLTHROUGH */
724290000Sglebius			case 'i':
725290000Sglebius				switch (cflags) {
726290000Sglebius				case PRINT_C_CHAR:
727290000Sglebius					value = (signed char)va_arg(args, int);
728290000Sglebius					break;
729290000Sglebius				case PRINT_C_SHORT:
730290000Sglebius					value = (short int)va_arg(args, int);
731290000Sglebius					break;
732290000Sglebius				case PRINT_C_LONG:
733290000Sglebius					value = va_arg(args, long int);
734290000Sglebius					break;
735290000Sglebius				case PRINT_C_LLONG:
736290000Sglebius					value = va_arg(args, LLONG);
737290000Sglebius					break;
738290000Sglebius				case PRINT_C_SIZE:
739290000Sglebius					value = va_arg(args, SSIZE_T);
740290000Sglebius					break;
741290000Sglebius				case PRINT_C_INTMAX:
742290000Sglebius					value = va_arg(args, INTMAX_T);
743290000Sglebius					break;
744290000Sglebius				case PRINT_C_PTRDIFF:
745290000Sglebius					value = va_arg(args, PTRDIFF_T);
746290000Sglebius					break;
747290000Sglebius				default:
748290000Sglebius					value = va_arg(args, int);
749290000Sglebius					break;
750290000Sglebius				}
751290000Sglebius				fmtint(str, &len, size, value, 10, width,
752290000Sglebius				    precision, flags);
753290000Sglebius				break;
754290000Sglebius			case 'X':
755290000Sglebius				flags |= PRINT_F_UP;
756290000Sglebius				/* FALLTHROUGH */
757290000Sglebius			case 'x':
758290000Sglebius				base = 16;
759290000Sglebius				/* FALLTHROUGH */
760290000Sglebius			case 'o':
761290000Sglebius				if (base == 0)
762290000Sglebius					base = 8;
763290000Sglebius				/* FALLTHROUGH */
764290000Sglebius			case 'u':
765290000Sglebius				if (base == 0)
766290000Sglebius					base = 10;
767290000Sglebius				flags |= PRINT_F_UNSIGNED;
768290000Sglebius				switch (cflags) {
769290000Sglebius				case PRINT_C_CHAR:
770290000Sglebius					value = (unsigned char)va_arg(args,
771290000Sglebius					    unsigned int);
772290000Sglebius					break;
773290000Sglebius				case PRINT_C_SHORT:
774290000Sglebius					value = (unsigned short int)va_arg(args,
775290000Sglebius					    unsigned int);
776290000Sglebius					break;
777290000Sglebius				case PRINT_C_LONG:
778290000Sglebius					value = va_arg(args, unsigned long int);
779290000Sglebius					break;
780290000Sglebius				case PRINT_C_LLONG:
781290000Sglebius					value = va_arg(args, ULLONG);
782290000Sglebius					break;
783290000Sglebius				case PRINT_C_SIZE:
784290000Sglebius					value = va_arg(args, size_t);
785290000Sglebius					break;
786290000Sglebius				case PRINT_C_INTMAX:
787290000Sglebius					value = va_arg(args, UINTMAX_T);
788290000Sglebius					break;
789290000Sglebius				case PRINT_C_PTRDIFF:
790290000Sglebius					value = va_arg(args, UPTRDIFF_T);
791290000Sglebius					break;
792290000Sglebius				default:
793290000Sglebius					value = va_arg(args, unsigned int);
794290000Sglebius					break;
795290000Sglebius				}
796290000Sglebius				fmtint(str, &len, size, value, base, width,
797290000Sglebius				    precision, flags);
798290000Sglebius				break;
799290000Sglebius			case 'A':
800290000Sglebius				/* Not yet supported, we'll use "%F". */
801290000Sglebius				/* FALLTHROUGH */
802290000Sglebius			case 'F':
803290000Sglebius				flags |= PRINT_F_UP;
804290000Sglebius				/* FALLTHROUGH */
805290000Sglebius			case 'a':
806290000Sglebius				/* Not yet supported, we'll use "%f". */
807290000Sglebius				/* FALLTHROUGH */
808290000Sglebius			case 'f':
809290000Sglebius				if (cflags == PRINT_C_LDOUBLE)
810290000Sglebius					fvalue = va_arg(args, LDOUBLE);
811290000Sglebius				else
812290000Sglebius					fvalue = va_arg(args, double);
813290000Sglebius				fmtflt(str, &len, size, fvalue, width,
814290000Sglebius				    precision, flags, &overflow);
815290000Sglebius				if (overflow)
816290000Sglebius					goto out;
817290000Sglebius				break;
818290000Sglebius			case 'E':
819290000Sglebius				flags |= PRINT_F_UP;
820290000Sglebius				/* FALLTHROUGH */
821290000Sglebius			case 'e':
822290000Sglebius				flags |= PRINT_F_TYPE_E;
823290000Sglebius				if (cflags == PRINT_C_LDOUBLE)
824290000Sglebius					fvalue = va_arg(args, LDOUBLE);
825290000Sglebius				else
826290000Sglebius					fvalue = va_arg(args, double);
827290000Sglebius				fmtflt(str, &len, size, fvalue, width,
828290000Sglebius				    precision, flags, &overflow);
829290000Sglebius				if (overflow)
830290000Sglebius					goto out;
831290000Sglebius				break;
832290000Sglebius			case 'G':
833290000Sglebius				flags |= PRINT_F_UP;
834290000Sglebius				/* FALLTHROUGH */
835290000Sglebius			case 'g':
836290000Sglebius				flags |= PRINT_F_TYPE_G;
837290000Sglebius				if (cflags == PRINT_C_LDOUBLE)
838290000Sglebius					fvalue = va_arg(args, LDOUBLE);
839290000Sglebius				else
840290000Sglebius					fvalue = va_arg(args, double);
841290000Sglebius				/*
842290000Sglebius				 * If the precision is zero, it is treated as
843290000Sglebius				 * one (cf. C99: 7.19.6.1, 8).
844290000Sglebius				 */
845290000Sglebius				if (precision == 0)
846290000Sglebius					precision = 1;
847290000Sglebius				fmtflt(str, &len, size, fvalue, width,
848290000Sglebius				    precision, flags, &overflow);
849290000Sglebius				if (overflow)
850290000Sglebius					goto out;
851290000Sglebius				break;
852290000Sglebius			case 'c':
853290000Sglebius				cvalue = va_arg(args, int);
854290000Sglebius				OUTCHAR(str, len, size, cvalue);
855290000Sglebius				break;
856290000Sglebius			case 's':
857290000Sglebius				strvalue = va_arg(args, char *);
858290000Sglebius				fmtstr(str, &len, size, strvalue, width,
859290000Sglebius				    precision, flags);
860290000Sglebius				break;
861290000Sglebius			case 'p':
862290000Sglebius				/*
863290000Sglebius				 * C99 says: "The value of the pointer is
864290000Sglebius				 * converted to a sequence of printing
865290000Sglebius				 * characters, in an implementation-defined
866290000Sglebius				 * manner." (C99: 7.19.6.1, 8)
867290000Sglebius				 */
868290000Sglebius				if ((strvalue = va_arg(args, void *)) == NULL)
869290000Sglebius					/*
870290000Sglebius					 * We use the glibc format.  BSD prints
871290000Sglebius					 * "0x0", SysV "0".
872290000Sglebius					 */
873290000Sglebius					fmtstr(str, &len, size, "(nil)", width,
874290000Sglebius					    -1, flags);
875290000Sglebius				else {
876290000Sglebius					/*
877290000Sglebius					 * We use the BSD/glibc format.  SysV
878290000Sglebius					 * omits the "0x" prefix (which we emit
879290000Sglebius					 * using the PRINT_F_NUM flag).
880290000Sglebius					 */
881290000Sglebius					flags |= PRINT_F_NUM;
882290000Sglebius					flags |= PRINT_F_UNSIGNED;
883290000Sglebius					fmtint(str, &len, size,
884290000Sglebius					    (UINTPTR_T)strvalue, 16, width,
885290000Sglebius					    precision, flags);
886290000Sglebius				}
887290000Sglebius				break;
888290000Sglebius			case 'n':
889290000Sglebius				switch (cflags) {
890290000Sglebius				case PRINT_C_CHAR:
891290000Sglebius					charptr = va_arg(args, signed char *);
892293894Sglebius					*charptr = (signed char)len;
893290000Sglebius					break;
894290000Sglebius				case PRINT_C_SHORT:
895290000Sglebius					shortptr = va_arg(args, short int *);
896293894Sglebius					*shortptr = (short int)len;
897290000Sglebius					break;
898290000Sglebius				case PRINT_C_LONG:
899290000Sglebius					longptr = va_arg(args, long int *);
900293894Sglebius					*longptr = (long int)len;
901290000Sglebius					break;
902290000Sglebius				case PRINT_C_LLONG:
903290000Sglebius					llongptr = va_arg(args, LLONG *);
904293894Sglebius					*llongptr = (LLONG)len;
905290000Sglebius					break;
906290000Sglebius				case PRINT_C_SIZE:
907290000Sglebius					/*
908290000Sglebius					 * C99 says that with the "z" length
909290000Sglebius					 * modifier, "a following `n' conversion
910290000Sglebius					 * specifier applies to a pointer to a
911290000Sglebius					 * signed integer type corresponding to
912290000Sglebius					 * size_t argument." (7.19.6.1, 7)
913290000Sglebius					 */
914290000Sglebius					sizeptr = va_arg(args, SSIZE_T *);
915293894Sglebius					*sizeptr = (SSIZE_T)len;
916290000Sglebius					break;
917290000Sglebius				case PRINT_C_INTMAX:
918290000Sglebius					intmaxptr = va_arg(args, INTMAX_T *);
919293894Sglebius					*intmaxptr = (INTMAX_T)len;
920290000Sglebius					break;
921290000Sglebius				case PRINT_C_PTRDIFF:
922290000Sglebius					ptrdiffptr = va_arg(args, PTRDIFF_T *);
923293894Sglebius					*ptrdiffptr = (PTRDIFF_T)len;
924290000Sglebius					break;
925290000Sglebius				default:
926290000Sglebius					intptr = va_arg(args, int *);
927293894Sglebius					*intptr = (int)len;
928290000Sglebius					break;
929290000Sglebius				}
930290000Sglebius				break;
931290000Sglebius			case '%':	/* Print a "%" character verbatim. */
932290000Sglebius				OUTCHAR(str, len, size, ch);
933290000Sglebius				break;
934290000Sglebius			default:	/* Skip other characters. */
935290000Sglebius				break;
936290000Sglebius			}
937290000Sglebius			ch = *format++;
938290000Sglebius			state = PRINT_S_DEFAULT;
939290000Sglebius			base = cflags = flags = width = 0;
940290000Sglebius			precision = -1;
941290000Sglebius			break;
942290000Sglebius		}
943290000Sglebiusout:
944290000Sglebius	if (len < size)
945290000Sglebius		str[len] = '\0';
946290000Sglebius	else if (size > 0)
947290000Sglebius		str[size - 1] = '\0';
948290000Sglebius
949290000Sglebius	if (overflow || len >= INT_MAX) {
950290000Sglebius		errno = overflow ? EOVERFLOW : ERANGE;
951290000Sglebius		return -1;
952290000Sglebius	}
953290000Sglebius	return (int)len;
954290000Sglebius}
955290000Sglebius
956290000Sglebiusstatic void
957290000Sglebiusfmtstr(char *str, size_t *len, size_t size, const char *value, int width,
958290000Sglebius       int precision, int flags)
959290000Sglebius{
960290000Sglebius	int padlen, strln;	/* Amount to pad. */
961290000Sglebius	int noprecision = (precision == -1);
962290000Sglebius
963290000Sglebius	if (value == NULL)	/* We're forgiving. */
964290000Sglebius		value = "(null)";
965290000Sglebius
966290000Sglebius	/* If a precision was specified, don't read the string past it. */
967290000Sglebius	for (strln = 0; value[strln] != '\0' &&
968290000Sglebius	    (noprecision || strln < precision); strln++)
969290000Sglebius		continue;
970290000Sglebius
971290000Sglebius	if ((padlen = width - strln) < 0)
972290000Sglebius		padlen = 0;
973290000Sglebius	if (flags & PRINT_F_MINUS)	/* Left justify. */
974290000Sglebius		padlen = -padlen;
975290000Sglebius
976290000Sglebius	while (padlen > 0) {	/* Leading spaces. */
977290000Sglebius		OUTCHAR(str, *len, size, ' ');
978290000Sglebius		padlen--;
979290000Sglebius	}
980290000Sglebius	while (*value != '\0' && (noprecision || precision-- > 0)) {
981290000Sglebius		OUTCHAR(str, *len, size, *value);
982290000Sglebius		value++;
983290000Sglebius	}
984290000Sglebius	while (padlen < 0) {	/* Trailing spaces. */
985290000Sglebius		OUTCHAR(str, *len, size, ' ');
986290000Sglebius		padlen++;
987290000Sglebius	}
988290000Sglebius}
989290000Sglebius
990290000Sglebiusstatic void
991290000Sglebiusfmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
992290000Sglebius       int precision, int flags)
993290000Sglebius{
994290000Sglebius	UINTMAX_T uvalue;
995290000Sglebius	char iconvert[MAX_CONVERT_LENGTH];
996290000Sglebius	char sign = 0;
997290000Sglebius	char hexprefix = 0;
998290000Sglebius	int spadlen = 0;	/* Amount to space pad. */
999290000Sglebius	int zpadlen = 0;	/* Amount to zero pad. */
1000290000Sglebius	int pos;
1001290000Sglebius	int separators = (flags & PRINT_F_QUOTE);
1002290000Sglebius	int noprecision = (precision == -1);
1003290000Sglebius
1004290000Sglebius	if (flags & PRINT_F_UNSIGNED)
1005290000Sglebius		uvalue = value;
1006290000Sglebius	else {
1007290000Sglebius		uvalue = (value >= 0) ? value : -value;
1008290000Sglebius		if (value < 0)
1009290000Sglebius			sign = '-';
1010290000Sglebius		else if (flags & PRINT_F_PLUS)	/* Do a sign. */
1011290000Sglebius			sign = '+';
1012290000Sglebius		else if (flags & PRINT_F_SPACE)
1013290000Sglebius			sign = ' ';
1014290000Sglebius	}
1015290000Sglebius
1016290000Sglebius	pos = convert(uvalue, iconvert, sizeof(iconvert), base,
1017290000Sglebius	    flags & PRINT_F_UP);
1018290000Sglebius
1019290000Sglebius	if (flags & PRINT_F_NUM && uvalue != 0) {
1020290000Sglebius		/*
1021290000Sglebius		 * C99 says: "The result is converted to an `alternative form'.
1022290000Sglebius		 * For `o' conversion, it increases the precision, if and only
1023290000Sglebius		 * if necessary, to force the first digit of the result to be a
1024290000Sglebius		 * zero (if the value and precision are both 0, a single 0 is
1025290000Sglebius		 * printed).  For `x' (or `X') conversion, a nonzero result has
1026290000Sglebius		 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
1027290000Sglebius		 */
1028290000Sglebius		switch (base) {
1029290000Sglebius		case 8:
1030290000Sglebius			if (precision <= pos)
1031290000Sglebius				precision = pos + 1;
1032290000Sglebius			break;
1033290000Sglebius		case 16:
1034290000Sglebius			hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
1035290000Sglebius			break;
1036290000Sglebius		}
1037290000Sglebius	}
1038290000Sglebius
1039290000Sglebius	if (separators)	/* Get the number of group separators we'll print. */
1040290000Sglebius		separators = getnumsep(pos);
1041290000Sglebius
1042290000Sglebius	zpadlen = precision - pos - separators;
1043290000Sglebius	spadlen = width                         /* Minimum field width. */
1044290000Sglebius	    - separators                        /* Number of separators. */
1045290000Sglebius	    - MAX(precision, pos)               /* Number of integer digits. */
1046290000Sglebius	    - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
1047290000Sglebius	    - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
1048290000Sglebius
1049290000Sglebius	if (zpadlen < 0)
1050290000Sglebius		zpadlen = 0;
1051290000Sglebius	if (spadlen < 0)
1052290000Sglebius		spadlen = 0;
1053290000Sglebius
1054290000Sglebius	/*
1055290000Sglebius	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1056290000Sglebius	 * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
1057290000Sglebius	 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
1058290000Sglebius	 */
1059290000Sglebius	if (flags & PRINT_F_MINUS)	/* Left justify. */
1060290000Sglebius		spadlen = -spadlen;
1061290000Sglebius	else if (flags & PRINT_F_ZERO && noprecision) {
1062290000Sglebius		zpadlen += spadlen;
1063290000Sglebius		spadlen = 0;
1064290000Sglebius	}
1065290000Sglebius	while (spadlen > 0) {	/* Leading spaces. */
1066290000Sglebius		OUTCHAR(str, *len, size, ' ');
1067290000Sglebius		spadlen--;
1068290000Sglebius	}
1069290000Sglebius	if (sign != 0)	/* Sign. */
1070290000Sglebius		OUTCHAR(str, *len, size, sign);
1071290000Sglebius	if (hexprefix != 0) {	/* A "0x" or "0X" prefix. */
1072290000Sglebius		OUTCHAR(str, *len, size, '0');
1073290000Sglebius		OUTCHAR(str, *len, size, hexprefix);
1074290000Sglebius	}
1075290000Sglebius	while (zpadlen > 0) {	/* Leading zeros. */
1076290000Sglebius		OUTCHAR(str, *len, size, '0');
1077290000Sglebius		zpadlen--;
1078290000Sglebius	}
1079290000Sglebius	while (pos > 0) {	/* The actual digits. */
1080290000Sglebius		pos--;
1081290000Sglebius		OUTCHAR(str, *len, size, iconvert[pos]);
1082290000Sglebius		if (separators > 0 && pos > 0 && pos % 3 == 0)
1083290000Sglebius			printsep(str, len, size);
1084290000Sglebius	}
1085290000Sglebius	while (spadlen < 0) {	/* Trailing spaces. */
1086290000Sglebius		OUTCHAR(str, *len, size, ' ');
1087290000Sglebius		spadlen++;
1088290000Sglebius	}
1089290000Sglebius}
1090290000Sglebius
1091290000Sglebiusstatic void
1092290000Sglebiusfmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
1093290000Sglebius       int precision, int flags, int *overflow)
1094290000Sglebius{
1095290000Sglebius	LDOUBLE ufvalue;
1096290000Sglebius	UINTMAX_T intpart;
1097290000Sglebius	UINTMAX_T fracpart;
1098290000Sglebius	UINTMAX_T mask;
1099290000Sglebius	const char *infnan = NULL;
1100290000Sglebius	char iconvert[MAX_CONVERT_LENGTH];
1101290000Sglebius	char fconvert[MAX_CONVERT_LENGTH];
1102290000Sglebius	char econvert[4];	/* "e-12" (without nul-termination). */
1103290000Sglebius	char esign = 0;
1104290000Sglebius	char sign = 0;
1105290000Sglebius	int leadfraczeros = 0;
1106290000Sglebius	int exponent = 0;
1107290000Sglebius	int emitpoint = 0;
1108290000Sglebius	int omitzeros = 0;
1109290000Sglebius	int omitcount = 0;
1110290000Sglebius	int padlen = 0;
1111290000Sglebius	int epos = 0;
1112290000Sglebius	int fpos = 0;
1113290000Sglebius	int ipos = 0;
1114290000Sglebius	int separators = (flags & PRINT_F_QUOTE);
1115290000Sglebius	int estyle = (flags & PRINT_F_TYPE_E);
1116290000Sglebius#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
1117290000Sglebius	struct lconv *lc = localeconv();
1118290000Sglebius#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
1119290000Sglebius
1120290000Sglebius	/*
1121290000Sglebius	 * AIX' man page says the default is 0, but C99 and at least Solaris'
1122290000Sglebius	 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
1123290000Sglebius	 * defaults to 6.
1124290000Sglebius	 */
1125290000Sglebius	if (precision == -1)
1126290000Sglebius		precision = 6;
1127290000Sglebius
1128290000Sglebius	if (fvalue < 0.0)
1129290000Sglebius		sign = '-';
1130290000Sglebius	else if (flags & PRINT_F_PLUS)	/* Do a sign. */
1131290000Sglebius		sign = '+';
1132290000Sglebius	else if (flags & PRINT_F_SPACE)
1133290000Sglebius		sign = ' ';
1134290000Sglebius
1135290000Sglebius	if (ISNAN(fvalue))
1136290000Sglebius		infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
1137290000Sglebius	else if (ISINF(fvalue))
1138290000Sglebius		infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
1139290000Sglebius
1140290000Sglebius	if (infnan != NULL) {
1141290000Sglebius		if (sign != 0)
1142290000Sglebius			iconvert[ipos++] = sign;
1143290000Sglebius		while (*infnan != '\0')
1144290000Sglebius			iconvert[ipos++] = *infnan++;
1145290000Sglebius		fmtstr(str, len, size, iconvert, width, ipos, flags);
1146290000Sglebius		return;
1147290000Sglebius	}
1148290000Sglebius
1149290000Sglebius	/* "%e" (or "%E") or "%g" (or "%G") conversion. */
1150290000Sglebius	if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
1151290000Sglebius		if (flags & PRINT_F_TYPE_G) {
1152290000Sglebius			/*
1153290000Sglebius			 * For "%g" (and "%G") conversions, the precision
1154290000Sglebius			 * specifies the number of significant digits, which
1155290000Sglebius			 * includes the digits in the integer part.  The
1156290000Sglebius			 * conversion will or will not be using "e-style" (like
1157290000Sglebius			 * "%e" or "%E" conversions) depending on the precision
1158290000Sglebius			 * and on the exponent.  However, the exponent can be
1159290000Sglebius			 * affected by rounding the converted value, so we'll
1160290000Sglebius			 * leave this decision for later.  Until then, we'll
1161290000Sglebius			 * assume that we're going to do an "e-style" conversion
1162290000Sglebius			 * (in order to get the exponent calculated).  For
1163290000Sglebius			 * "e-style", the precision must be decremented by one.
1164290000Sglebius			 */
1165290000Sglebius			precision--;
1166290000Sglebius			/*
1167290000Sglebius			 * For "%g" (and "%G") conversions, trailing zeros are
1168290000Sglebius			 * removed from the fractional portion of the result
1169290000Sglebius			 * unless the "#" flag was specified.
1170290000Sglebius			 */
1171290000Sglebius			if (!(flags & PRINT_F_NUM))
1172290000Sglebius				omitzeros = 1;
1173290000Sglebius		}
1174290000Sglebius		exponent = getexponent(fvalue);
1175290000Sglebius		estyle = 1;
1176290000Sglebius	}
1177290000Sglebius
1178290000Sglebiusagain:
1179290000Sglebius	/*
1180290000Sglebius	 * Sorry, we only support 9, 19, or 38 digits (that is, the number of
1181290000Sglebius	 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
1182290000Sglebius	 * minus one) past the decimal point due to our conversion method.
1183290000Sglebius	 */
1184290000Sglebius	switch (sizeof(UINTMAX_T)) {
1185290000Sglebius	case 16:
1186290000Sglebius		if (precision > 38)
1187290000Sglebius			precision = 38;
1188290000Sglebius		break;
1189290000Sglebius	case 8:
1190290000Sglebius		if (precision > 19)
1191290000Sglebius			precision = 19;
1192290000Sglebius		break;
1193290000Sglebius	default:
1194290000Sglebius		if (precision > 9)
1195290000Sglebius			precision = 9;
1196290000Sglebius		break;
1197290000Sglebius	}
1198290000Sglebius
1199290000Sglebius	ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
1200290000Sglebius	if (estyle)	/* We want exactly one integer digit. */
1201290000Sglebius		ufvalue /= mypow10(exponent);
1202290000Sglebius
1203290000Sglebius	if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
1204290000Sglebius		*overflow = 1;
1205290000Sglebius		return;
1206290000Sglebius	}
1207290000Sglebius
1208290000Sglebius	/*
1209290000Sglebius	 * Factor of ten with the number of digits needed for the fractional
1210290000Sglebius	 * part.  For example, if the precision is 3, the mask will be 1000.
1211290000Sglebius	 */
1212293894Sglebius	mask = (UINTMAX_T)mypow10(precision);
1213290000Sglebius	/*
1214290000Sglebius	 * We "cheat" by converting the fractional part to integer by
1215290000Sglebius	 * multiplying by a factor of ten.
1216290000Sglebius	 */
1217290000Sglebius	if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
1218290000Sglebius		/*
1219290000Sglebius		 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
1220290000Sglebius		 * (because precision = 3).  Now, myround(1000 * 0.99962) will
1221290000Sglebius		 * return 1000.  So, the integer part must be incremented by one
1222290000Sglebius		 * and the fractional part must be set to zero.
1223290000Sglebius		 */
1224290000Sglebius		intpart++;
1225290000Sglebius		fracpart = 0;
1226290000Sglebius		if (estyle && intpart == 10) {
1227290000Sglebius			/*
1228290000Sglebius			 * The value was rounded up to ten, but we only want one
1229290000Sglebius			 * integer digit if using "e-style".  So, the integer
1230290000Sglebius			 * part must be set to one and the exponent must be
1231290000Sglebius			 * incremented by one.
1232290000Sglebius			 */
1233290000Sglebius			intpart = 1;
1234290000Sglebius			exponent++;
1235290000Sglebius		}
1236290000Sglebius	}
1237290000Sglebius
1238290000Sglebius	/*
1239290000Sglebius	 * Now that we know the real exponent, we can check whether or not to
1240290000Sglebius	 * use "e-style" for "%g" (and "%G") conversions.  If we don't need
1241290000Sglebius	 * "e-style", the precision must be adjusted and the integer and
1242290000Sglebius	 * fractional parts must be recalculated from the original value.
1243290000Sglebius	 *
1244290000Sglebius	 * C99 says: "Let P equal the precision if nonzero, 6 if the precision
1245290000Sglebius	 * is omitted, or 1 if the precision is zero.  Then, if a conversion
1246290000Sglebius	 * with style `E' would have an exponent of X:
1247290000Sglebius	 *
1248290000Sglebius	 * - if P > X >= -4, the conversion is with style `f' (or `F') and
1249290000Sglebius	 *   precision P - (X + 1).
1250290000Sglebius	 *
1251290000Sglebius	 * - otherwise, the conversion is with style `e' (or `E') and precision
1252290000Sglebius	 *   P - 1." (7.19.6.1, 8)
1253290000Sglebius	 *
1254290000Sglebius	 * Note that we had decremented the precision by one.
1255290000Sglebius	 */
1256290000Sglebius	if (flags & PRINT_F_TYPE_G && estyle &&
1257290000Sglebius	    precision + 1 > exponent && exponent >= -4) {
1258290000Sglebius		precision -= exponent;
1259290000Sglebius		estyle = 0;
1260290000Sglebius		goto again;
1261290000Sglebius	}
1262290000Sglebius
1263290000Sglebius	if (estyle) {
1264290000Sglebius		if (exponent < 0) {
1265290000Sglebius			exponent = -exponent;
1266290000Sglebius			esign = '-';
1267290000Sglebius		} else
1268290000Sglebius			esign = '+';
1269290000Sglebius
1270290000Sglebius		/*
1271290000Sglebius		 * Convert the exponent.  The sizeof(econvert) is 4.  So, the
1272290000Sglebius		 * econvert buffer can hold e.g. "e+99" and "e-99".  We don't
1273290000Sglebius		 * support an exponent which contains more than two digits.
1274290000Sglebius		 * Therefore, the following stores are safe.
1275290000Sglebius		 */
1276290000Sglebius		epos = convert(exponent, econvert, 2, 10, 0);
1277290000Sglebius		/*
1278290000Sglebius		 * C99 says: "The exponent always contains at least two digits,
1279290000Sglebius		 * and only as many more digits as necessary to represent the
1280290000Sglebius		 * exponent." (7.19.6.1, 8)
1281290000Sglebius		 */
1282290000Sglebius		if (epos == 1)
1283290000Sglebius			econvert[epos++] = '0';
1284290000Sglebius		econvert[epos++] = esign;
1285290000Sglebius		econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
1286290000Sglebius	}
1287290000Sglebius
1288290000Sglebius	/* Convert the integer part and the fractional part. */
1289290000Sglebius	ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
1290290000Sglebius	if (fracpart != 0)	/* convert() would return 1 if fracpart == 0. */
1291290000Sglebius		fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
1292290000Sglebius
1293290000Sglebius	leadfraczeros = precision - fpos;
1294290000Sglebius
1295290000Sglebius	if (omitzeros) {
1296290000Sglebius		if (fpos > 0)	/* Omit trailing fractional part zeros. */
1297290000Sglebius			while (omitcount < fpos && fconvert[omitcount] == '0')
1298290000Sglebius				omitcount++;
1299290000Sglebius		else {	/* The fractional part is zero, omit it completely. */
1300290000Sglebius			omitcount = precision;
1301290000Sglebius			leadfraczeros = 0;
1302290000Sglebius		}
1303290000Sglebius		precision -= omitcount;
1304290000Sglebius	}
1305290000Sglebius
1306290000Sglebius	/*
1307290000Sglebius	 * Print a decimal point if either the fractional part is non-zero
1308290000Sglebius	 * and/or the "#" flag was specified.
1309290000Sglebius	 */
1310290000Sglebius	if (precision > 0 || flags & PRINT_F_NUM)
1311290000Sglebius		emitpoint = 1;
1312290000Sglebius	if (separators)	/* Get the number of group separators we'll print. */
1313290000Sglebius		separators = getnumsep(ipos);
1314290000Sglebius
1315290000Sglebius	padlen = width                  /* Minimum field width. */
1316290000Sglebius	    - ipos                      /* Number of integer digits. */
1317290000Sglebius	    - epos                      /* Number of exponent characters. */
1318290000Sglebius	    - precision                 /* Number of fractional digits. */
1319290000Sglebius	    - separators                /* Number of group separators. */
1320290000Sglebius	    - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
1321290000Sglebius	    - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
1322290000Sglebius
1323290000Sglebius	if (padlen < 0)
1324290000Sglebius		padlen = 0;
1325290000Sglebius
1326290000Sglebius	/*
1327290000Sglebius	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1328290000Sglebius	 * ignored." (7.19.6.1, 6)
1329290000Sglebius	 */
1330290000Sglebius	if (flags & PRINT_F_MINUS)	/* Left justifty. */
1331290000Sglebius		padlen = -padlen;
1332290000Sglebius	else if (flags & PRINT_F_ZERO && padlen > 0) {
1333290000Sglebius		if (sign != 0) {	/* Sign. */
1334290000Sglebius			OUTCHAR(str, *len, size, sign);
1335290000Sglebius			sign = 0;
1336290000Sglebius		}
1337290000Sglebius		while (padlen > 0) {	/* Leading zeros. */
1338290000Sglebius			OUTCHAR(str, *len, size, '0');
1339290000Sglebius			padlen--;
1340290000Sglebius		}
1341290000Sglebius	}
1342290000Sglebius	while (padlen > 0) {	/* Leading spaces. */
1343290000Sglebius		OUTCHAR(str, *len, size, ' ');
1344290000Sglebius		padlen--;
1345290000Sglebius	}
1346290000Sglebius	if (sign != 0)	/* Sign. */
1347290000Sglebius		OUTCHAR(str, *len, size, sign);
1348290000Sglebius	while (ipos > 0) {	/* Integer part. */
1349290000Sglebius		ipos--;
1350290000Sglebius		OUTCHAR(str, *len, size, iconvert[ipos]);
1351290000Sglebius		if (separators > 0 && ipos > 0 && ipos % 3 == 0)
1352290000Sglebius			printsep(str, len, size);
1353290000Sglebius	}
1354290000Sglebius	if (emitpoint) {	/* Decimal point. */
1355290000Sglebius#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
1356290000Sglebius		if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
1357290000Sglebius			OUTCHAR(str, *len, size, *lc->decimal_point);
1358290000Sglebius		else	/* We'll always print some decimal point character. */
1359290000Sglebius#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
1360290000Sglebius			OUTCHAR(str, *len, size, '.');
1361290000Sglebius	}
1362290000Sglebius	while (leadfraczeros > 0) {	/* Leading fractional part zeros. */
1363290000Sglebius		OUTCHAR(str, *len, size, '0');
1364290000Sglebius		leadfraczeros--;
1365290000Sglebius	}
1366290000Sglebius	while (fpos > omitcount) {	/* The remaining fractional part. */
1367290000Sglebius		fpos--;
1368290000Sglebius		OUTCHAR(str, *len, size, fconvert[fpos]);
1369290000Sglebius	}
1370290000Sglebius	while (epos > 0) {	/* Exponent. */
1371290000Sglebius		epos--;
1372290000Sglebius		OUTCHAR(str, *len, size, econvert[epos]);
1373290000Sglebius	}
1374290000Sglebius	while (padlen < 0) {	/* Trailing spaces. */
1375290000Sglebius		OUTCHAR(str, *len, size, ' ');
1376290000Sglebius		padlen++;
1377290000Sglebius	}
1378290000Sglebius}
1379290000Sglebius
1380290000Sglebiusstatic void
1381290000Sglebiusprintsep(char *str, size_t *len, size_t size)
1382290000Sglebius{
1383290000Sglebius#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
1384290000Sglebius	struct lconv *lc = localeconv();
1385290000Sglebius	int i;
1386290000Sglebius
1387290000Sglebius	if (lc->thousands_sep != NULL)
1388290000Sglebius		for (i = 0; lc->thousands_sep[i] != '\0'; i++)
1389290000Sglebius			OUTCHAR(str, *len, size, lc->thousands_sep[i]);
1390290000Sglebius	else
1391290000Sglebius#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
1392290000Sglebius		OUTCHAR(str, *len, size, ',');
1393290000Sglebius}
1394290000Sglebius
1395290000Sglebiusstatic int
1396290000Sglebiusgetnumsep(int digits)
1397290000Sglebius{
1398290000Sglebius	int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
1399290000Sglebius#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
1400290000Sglebius	int strln;
1401290000Sglebius	struct lconv *lc = localeconv();
1402290000Sglebius
1403290000Sglebius	/* We support an arbitrary separator length (including zero). */
1404290000Sglebius	if (lc->thousands_sep != NULL) {
1405290000Sglebius		for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
1406290000Sglebius			continue;
1407290000Sglebius		separators *= strln;
1408290000Sglebius	}
1409290000Sglebius#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
1410290000Sglebius	return separators;
1411290000Sglebius}
1412290000Sglebius
1413290000Sglebiusstatic int
1414290000Sglebiusgetexponent(LDOUBLE value)
1415290000Sglebius{
1416290000Sglebius	LDOUBLE tmp = (value >= 0.0) ? value : -value;
1417290000Sglebius	int exponent = 0;
1418290000Sglebius
1419290000Sglebius	/*
1420290000Sglebius	 * We check for 99 > exponent > -99 in order to work around possible
1421290000Sglebius	 * endless loops which could happen (at least) in the second loop (at
1422290000Sglebius	 * least) if we're called with an infinite value.  However, we checked
1423290000Sglebius	 * for infinity before calling this function using our ISINF() macro, so
1424290000Sglebius	 * this might be somewhat paranoid.
1425290000Sglebius	 */
1426290000Sglebius	while (tmp < 1.0 && tmp > 0.0 && --exponent > -99)
1427290000Sglebius		tmp *= 10;
1428290000Sglebius	while (tmp >= 10.0 && ++exponent < 99)
1429290000Sglebius		tmp /= 10;
1430290000Sglebius
1431290000Sglebius	return exponent;
1432290000Sglebius}
1433290000Sglebius
1434290000Sglebiusstatic int
1435290000Sglebiusconvert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
1436290000Sglebius{
1437290000Sglebius	const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
1438290000Sglebius	size_t pos = 0;
1439290000Sglebius
1440290000Sglebius	/* We return an unterminated buffer with the digits in reverse order. */
1441290000Sglebius	do {
1442290000Sglebius		buf[pos++] = digits[value % base];
1443290000Sglebius		value /= base;
1444290000Sglebius	} while (value != 0 && pos < size);
1445290000Sglebius
1446290000Sglebius	return (int)pos;
1447290000Sglebius}
1448290000Sglebius
1449290000Sglebiusstatic UINTMAX_T
1450290000Sglebiuscast(LDOUBLE value)
1451290000Sglebius{
1452290000Sglebius	UINTMAX_T result;
1453290000Sglebius
1454290000Sglebius	/*
1455290000Sglebius	 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
1456290000Sglebius	 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
1457290000Sglebius	 * it may be increased to the nearest higher representable value for the
1458290000Sglebius	 * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
1459290000Sglebius	 * value although converting the latter to UINTMAX_T would overflow.
1460290000Sglebius	 */
1461290000Sglebius	if (value >= UINTMAX_MAX)
1462290000Sglebius		return UINTMAX_MAX;
1463290000Sglebius
1464293894Sglebius	result = (UINTMAX_T)value;
1465290000Sglebius	/*
1466290000Sglebius	 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
1467290000Sglebius	 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
1468290000Sglebius	 * the standard).  Sigh.
1469290000Sglebius	 */
1470290000Sglebius	return (result <= value) ? result : result - 1;
1471290000Sglebius}
1472290000Sglebius
1473290000Sglebiusstatic UINTMAX_T
1474290000Sglebiusmyround(LDOUBLE value)
1475290000Sglebius{
1476290000Sglebius	UINTMAX_T intpart = cast(value);
1477290000Sglebius
1478290000Sglebius	return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
1479290000Sglebius}
1480290000Sglebius
1481290000Sglebiusstatic LDOUBLE
1482290000Sglebiusmypow10(int exponent)
1483290000Sglebius{
1484290000Sglebius	LDOUBLE result = 1;
1485290000Sglebius
1486290000Sglebius	while (exponent > 0) {
1487290000Sglebius		result *= 10;
1488290000Sglebius		exponent--;
1489290000Sglebius	}
1490290000Sglebius	while (exponent < 0) {
1491290000Sglebius		result /= 10;
1492290000Sglebius		exponent++;
1493290000Sglebius	}
1494290000Sglebius	return result;
1495290000Sglebius}
1496290000Sglebius#endif	/* HW_WANT_RPL_VSNPRINTF */
1497290000Sglebius
1498290000Sglebius#if HW_WANT_RPL_VASPRINTF
1499290000Sglebius#if NEED_MYMEMCPY
1500290000Sglebiusvoid *
1501290000Sglebiusmymemcpy(void *dst, void *src, size_t len)
1502290000Sglebius{
1503290000Sglebius	const char *from = src;
1504290000Sglebius	char *to = dst;
1505290000Sglebius
1506290000Sglebius	/* No need for optimization, we use this only to replace va_copy(3). */
1507290000Sglebius	while (len-- > 0)
1508290000Sglebius		*to++ = *from++;
1509290000Sglebius	return dst;
1510290000Sglebius}
1511290000Sglebius#endif	/* NEED_MYMEMCPY */
1512290000Sglebius
1513290000Sglebiusint
1514290000Sglebiusrpl_vasprintf(char **ret, const char *format, va_list ap);
1515290000Sglebius
1516290000Sglebiusint
1517290000Sglebiusrpl_vasprintf(char **ret, const char *format, va_list ap)
1518290000Sglebius{
1519290000Sglebius	size_t size;
1520290000Sglebius	int len;
1521290000Sglebius	va_list aq;
1522290000Sglebius
1523290000Sglebius	VA_COPY(aq, ap);
1524290000Sglebius	len = vsnprintf(NULL, 0, format, aq);
1525290000Sglebius	VA_END_COPY(aq);
1526290000Sglebius	if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
1527290000Sglebius		return -1;
1528290000Sglebius	return vsnprintf(*ret, size, format, ap);
1529290000Sglebius}
1530290000Sglebius#endif	/* HW_WANT_RPL_VASPRINTF */
1531290000Sglebius
1532290000Sglebius#if HW_WANT_RPL_SNPRINTF
1533290000Sglebius#if HAVE_STDARG_H
1534290000Sglebiusint
1535290000Sglebiusrpl_snprintf(char *str, size_t size, const char *format, ...);
1536290000Sglebius
1537290000Sglebiusint
1538290000Sglebiusrpl_snprintf(char *str, size_t size, const char *format, ...)
1539290000Sglebius#else
1540290000Sglebiusint
1541290000Sglebiusrpl_snprintf(va_alist) va_dcl
1542290000Sglebius#endif	/* HAVE_STDARG_H */
1543290000Sglebius{
1544290000Sglebius#if !HAVE_STDARG_H
154582498Sroberto	char *str;
1546290000Sglebius	size_t size;
1547290000Sglebius	char *format;
1548290000Sglebius#endif	/* HAVE_STDARG_H */
154982498Sroberto	va_list ap;
1550290000Sglebius	int len;
1551290000Sglebius
1552290000Sglebius	VA_START(ap, format);
1553290000Sglebius	VA_SHIFT(ap, str, char *);
1554290000Sglebius	VA_SHIFT(ap, size, size_t);
1555290000Sglebius	VA_SHIFT(ap, format, const char *);
1556290000Sglebius	len = vsnprintf(str, size, format, ap);
155782498Sroberto	va_end(ap);
1558290000Sglebius	return len;
1559290000Sglebius}
1560290000Sglebius#endif	/* HW_WANT_RPL_SNPRINTF */
1561290000Sglebius
1562290000Sglebius#if HW_WANT_RPL_ASPRINTF
1563290000Sglebius#if HAVE_STDARG_H
1564290000Sglebiusint
1565290000Sglebiusrpl_asprintf(char **ret, const char *format, ...);
1566290000Sglebius
1567290000Sglebiusint
1568290000Sglebiusrpl_asprintf(char **ret, const char *format, ...)
156982498Sroberto#else
1570290000Sglebiusint
1571290000Sglebiusrpl_asprintf(va_alist) va_dcl
1572290000Sglebius#endif	/* HAVE_STDARG_H */
1573290000Sglebius{
1574290000Sglebius#if !HAVE_STDARG_H
1575290000Sglebius	char **ret;
1576290000Sglebius	char *format;
1577290000Sglebius#endif	/* HAVE_STDARG_H */
1578290000Sglebius	va_list ap;
1579290000Sglebius	int len;
1580290000Sglebius
1581290000Sglebius	VA_START(ap, format);
1582290000Sglebius	VA_SHIFT(ap, ret, char **);
1583290000Sglebius	VA_SHIFT(ap, format, const char *);
1584290000Sglebius	len = vasprintf(ret, format, ap);
158582498Sroberto	va_end(ap);
1586290000Sglebius	return len;
158782498Sroberto}
1588290000Sglebius#endif	/* HW_WANT_RPL_ASPRINTF */
1589290000Sglebius#else	/* Dummy declaration to avoid empty translation unit warnings. */
1590290000Sglebiusint main(void);
1591290000Sglebius#endif	/* HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || [...] */
159282498Sroberto
1593290000Sglebius#if TEST_SNPRINTF
159482498Srobertoint
1595290000Sglebiusmain(void)
159682498Sroberto{
1597290000Sglebius	const char *float_fmt[] = {
1598290000Sglebius		/* "%E" and "%e" formats. */
1599290000Sglebius#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
1600290000Sglebius		"%.16e",
1601290000Sglebius		"%22.16e",
1602290000Sglebius		"%022.16e",
1603290000Sglebius		"%-22.16e",
1604290000Sglebius		"%#+'022.16e",
1605290000Sglebius#endif	/* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
1606290000Sglebius		"foo|%#+0123.9E|bar",
1607290000Sglebius		"%-123.9e",
1608290000Sglebius		"%123.9e",
1609290000Sglebius		"%+23.9e",
1610290000Sglebius		"%+05.8e",
1611290000Sglebius		"%-05.8e",
1612290000Sglebius		"%05.8e",
1613290000Sglebius		"%+5.8e",
1614290000Sglebius		"%-5.8e",
1615290000Sglebius		"% 5.8e",
1616290000Sglebius		"%5.8e",
1617290000Sglebius		"%+4.9e",
1618290000Sglebius#if !OS_LINUX	/* glibc sometimes gets these wrong. */
1619290000Sglebius		"%+#010.0e",
1620290000Sglebius		"%#10.1e",
1621290000Sglebius		"%10.5e",
1622290000Sglebius		"% 10.5e",
1623290000Sglebius		"%5.0e",
1624290000Sglebius		"%5.e",
1625290000Sglebius		"%#5.0e",
1626290000Sglebius		"%#5.e",
1627290000Sglebius		"%3.2e",
1628290000Sglebius		"%3.1e",
1629290000Sglebius		"%-1.5e",
1630290000Sglebius		"%1.5e",
1631290000Sglebius		"%01.3e",
1632290000Sglebius		"%1.e",
1633290000Sglebius		"%.1e",
1634290000Sglebius		"%#.0e",
1635290000Sglebius		"%+.0e",
1636290000Sglebius		"% .0e",
1637290000Sglebius		"%.0e",
1638290000Sglebius		"%#.e",
1639290000Sglebius		"%+.e",
1640290000Sglebius		"% .e",
1641290000Sglebius		"%.e",
1642290000Sglebius		"%4e",
1643290000Sglebius		"%e",
1644290000Sglebius		"%E",
1645290000Sglebius#endif	/* !OS_LINUX */
1646290000Sglebius		/* "%F" and "%f" formats. */
1647290000Sglebius#if !OS_BSD && !OS_IRIX
1648290000Sglebius		"% '022f",
1649290000Sglebius		"%+'022f",
1650290000Sglebius		"%-'22f",
1651290000Sglebius		"%'22f",
1652290000Sglebius#if HAVE_LONG_LONG_INT
1653290000Sglebius		"%.16f",
1654290000Sglebius		"%22.16f",
1655290000Sglebius		"%022.16f",
1656290000Sglebius		"%-22.16f",
1657290000Sglebius		"%#+'022.16f",
1658290000Sglebius#endif	/* HAVE_LONG_LONG_INT */
1659290000Sglebius#endif	/* !OS_BSD && !OS_IRIX */
1660290000Sglebius		"foo|%#+0123.9F|bar",
1661290000Sglebius		"%-123.9f",
1662290000Sglebius		"%123.9f",
1663290000Sglebius		"%+23.9f",
1664290000Sglebius		"%+#010.0f",
1665290000Sglebius		"%#10.1f",
1666290000Sglebius		"%10.5f",
1667290000Sglebius		"% 10.5f",
1668290000Sglebius		"%+05.8f",
1669290000Sglebius		"%-05.8f",
1670290000Sglebius		"%05.8f",
1671290000Sglebius		"%+5.8f",
1672290000Sglebius		"%-5.8f",
1673290000Sglebius		"% 5.8f",
1674290000Sglebius		"%5.8f",
1675290000Sglebius		"%5.0f",
1676290000Sglebius		"%5.f",
1677290000Sglebius		"%#5.0f",
1678290000Sglebius		"%#5.f",
1679290000Sglebius		"%+4.9f",
1680290000Sglebius		"%3.2f",
1681290000Sglebius		"%3.1f",
1682290000Sglebius		"%-1.5f",
1683290000Sglebius		"%1.5f",
1684290000Sglebius		"%01.3f",
1685290000Sglebius		"%1.f",
1686290000Sglebius		"%.1f",
1687290000Sglebius		"%#.0f",
1688290000Sglebius		"%+.0f",
1689290000Sglebius		"% .0f",
1690290000Sglebius		"%.0f",
1691290000Sglebius		"%#.f",
1692290000Sglebius		"%+.f",
1693290000Sglebius		"% .f",
1694290000Sglebius		"%.f",
1695290000Sglebius		"%4f",
1696290000Sglebius		"%f",
1697290000Sglebius		"%F",
1698290000Sglebius		/* "%G" and "%g" formats. */
1699290000Sglebius#if !OS_BSD && !OS_IRIX && !OS_LINUX
1700290000Sglebius		"% '022g",
1701290000Sglebius		"%+'022g",
1702290000Sglebius		"%-'22g",
1703290000Sglebius		"%'22g",
1704290000Sglebius#if HAVE_LONG_LONG_INT
1705290000Sglebius		"%.16g",
1706290000Sglebius		"%22.16g",
1707290000Sglebius		"%022.16g",
1708290000Sglebius		"%-22.16g",
1709290000Sglebius		"%#+'022.16g",
1710290000Sglebius#endif	/* HAVE_LONG_LONG_INT */
1711290000Sglebius#endif	/* !OS_BSD && !OS_IRIX && !OS_LINUX */
1712290000Sglebius		"foo|%#+0123.9G|bar",
1713290000Sglebius		"%-123.9g",
1714290000Sglebius		"%123.9g",
1715290000Sglebius		"%+23.9g",
1716290000Sglebius		"%+05.8g",
1717290000Sglebius		"%-05.8g",
1718290000Sglebius		"%05.8g",
1719290000Sglebius		"%+5.8g",
1720290000Sglebius		"%-5.8g",
1721290000Sglebius		"% 5.8g",
1722290000Sglebius		"%5.8g",
1723290000Sglebius		"%+4.9g",
1724290000Sglebius#if !OS_LINUX	/* glibc sometimes gets these wrong. */
1725290000Sglebius		"%+#010.0g",
1726290000Sglebius		"%#10.1g",
1727290000Sglebius		"%10.5g",
1728290000Sglebius		"% 10.5g",
1729290000Sglebius		"%5.0g",
1730290000Sglebius		"%5.g",
1731290000Sglebius		"%#5.0g",
1732290000Sglebius		"%#5.g",
1733290000Sglebius		"%3.2g",
1734290000Sglebius		"%3.1g",
1735290000Sglebius		"%-1.5g",
1736290000Sglebius		"%1.5g",
1737290000Sglebius		"%01.3g",
1738290000Sglebius		"%1.g",
1739290000Sglebius		"%.1g",
1740290000Sglebius		"%#.0g",
1741290000Sglebius		"%+.0g",
1742290000Sglebius		"% .0g",
1743290000Sglebius		"%.0g",
1744290000Sglebius		"%#.g",
1745290000Sglebius		"%+.g",
1746290000Sglebius		"% .g",
1747290000Sglebius		"%.g",
1748290000Sglebius		"%4g",
1749290000Sglebius		"%g",
1750290000Sglebius		"%G",
1751290000Sglebius#endif	/* !OS_LINUX */
1752290000Sglebius		NULL
1753290000Sglebius	};
1754290000Sglebius	double float_val[] = {
1755290000Sglebius		-4.136,
1756290000Sglebius		-134.52,
1757290000Sglebius		-5.04030201,
1758290000Sglebius		-3410.01234,
1759290000Sglebius		-999999.999999,
1760290000Sglebius		-913450.29876,
1761290000Sglebius		-913450.2,
1762290000Sglebius		-91345.2,
1763290000Sglebius		-9134.2,
1764290000Sglebius		-913.2,
1765290000Sglebius		-91.2,
1766290000Sglebius		-9.2,
1767290000Sglebius		-9.9,
1768290000Sglebius		4.136,
1769290000Sglebius		134.52,
1770290000Sglebius		5.04030201,
1771290000Sglebius		3410.01234,
1772290000Sglebius		999999.999999,
1773290000Sglebius		913450.29876,
1774290000Sglebius		913450.2,
1775290000Sglebius		91345.2,
1776290000Sglebius		9134.2,
1777290000Sglebius		913.2,
1778290000Sglebius		91.2,
1779290000Sglebius		9.2,
1780290000Sglebius		9.9,
1781290000Sglebius		9.96,
1782290000Sglebius		9.996,
1783290000Sglebius		9.9996,
1784290000Sglebius		9.99996,
1785290000Sglebius		9.999996,
1786290000Sglebius		9.9999996,
1787290000Sglebius		9.99999996,
1788290000Sglebius		0.99999996,
1789290000Sglebius		0.99999999,
1790290000Sglebius		0.09999999,
1791290000Sglebius		0.00999999,
1792290000Sglebius		0.00099999,
1793290000Sglebius		0.00009999,
1794290000Sglebius		0.00000999,
1795290000Sglebius		0.00000099,
1796290000Sglebius		0.00000009,
1797290000Sglebius		0.00000001,
1798290000Sglebius		0.0000001,
1799290000Sglebius		0.000001,
1800290000Sglebius		0.00001,
1801290000Sglebius		0.0001,
1802290000Sglebius		0.001,
1803290000Sglebius		0.01,
1804290000Sglebius		0.1,
1805290000Sglebius		1.0,
1806290000Sglebius		1.5,
1807290000Sglebius		-1.5,
1808290000Sglebius		-1.0,
1809290000Sglebius		-0.1,
1810290000Sglebius#if !OS_BSD	/* BSD sometimes gets these wrong. */
1811290000Sglebius#ifdef INFINITY
1812290000Sglebius		INFINITY,
1813290000Sglebius		-INFINITY,
1814290000Sglebius#endif	/* defined(INFINITY) */
1815290000Sglebius#ifdef NAN
1816290000Sglebius		NAN,
1817290000Sglebius#endif	/* defined(NAN) */
1818290000Sglebius#endif	/* !OS_BSD */
1819290000Sglebius		0
1820290000Sglebius	};
1821290000Sglebius	const char *long_fmt[] = {
1822290000Sglebius		"foo|%0123ld|bar",
1823290000Sglebius#if !OS_IRIX
1824290000Sglebius		"% '0123ld",
1825290000Sglebius		"%+'0123ld",
1826290000Sglebius		"%-'123ld",
1827290000Sglebius		"%'123ld",
1828290000Sglebius#endif	/* !OS_IRiX */
1829290000Sglebius		"%123.9ld",
1830290000Sglebius		"% 123.9ld",
1831290000Sglebius		"%+123.9ld",
1832290000Sglebius		"%-123.9ld",
1833290000Sglebius		"%0123ld",
1834290000Sglebius		"% 0123ld",
1835290000Sglebius		"%+0123ld",
1836290000Sglebius		"%-0123ld",
1837290000Sglebius		"%10.5ld",
1838290000Sglebius		"% 10.5ld",
1839290000Sglebius		"%+10.5ld",
1840290000Sglebius		"%-10.5ld",
1841290000Sglebius		"%010ld",
1842290000Sglebius		"% 010ld",
1843290000Sglebius		"%+010ld",
1844290000Sglebius		"%-010ld",
1845290000Sglebius		"%4.2ld",
1846290000Sglebius		"% 4.2ld",
1847290000Sglebius		"%+4.2ld",
1848290000Sglebius		"%-4.2ld",
1849290000Sglebius		"%04ld",
1850290000Sglebius		"% 04ld",
1851290000Sglebius		"%+04ld",
1852290000Sglebius		"%-04ld",
1853290000Sglebius		"%5.5ld",
1854290000Sglebius		"%+22.33ld",
1855290000Sglebius		"%01.3ld",
1856290000Sglebius		"%1.5ld",
1857290000Sglebius		"%-1.5ld",
1858290000Sglebius		"%44ld",
1859290000Sglebius		"%4ld",
1860290000Sglebius		"%4.0ld",
1861290000Sglebius		"%4.ld",
1862290000Sglebius		"%.44ld",
1863290000Sglebius		"%.4ld",
1864290000Sglebius		"%.0ld",
1865290000Sglebius		"%.ld",
1866290000Sglebius		"%ld",
1867290000Sglebius		NULL
1868290000Sglebius	};
1869290000Sglebius	long int long_val[] = {
1870290000Sglebius#ifdef LONG_MAX
1871290000Sglebius		LONG_MAX,
1872290000Sglebius#endif	/* LONG_MAX */
1873290000Sglebius#ifdef LONG_MIN
1874290000Sglebius		LONG_MIN,
1875290000Sglebius#endif	/* LONG_MIN */
1876290000Sglebius		-91340,
1877290000Sglebius		91340,
1878290000Sglebius		341,
1879290000Sglebius		134,
1880290000Sglebius		0203,
1881290000Sglebius		-1,
1882290000Sglebius		1,
1883290000Sglebius		0
1884290000Sglebius	};
1885290000Sglebius	const char *ulong_fmt[] = {
1886290000Sglebius		/* "%u" formats. */
1887290000Sglebius		"foo|%0123lu|bar",
1888290000Sglebius#if !OS_IRIX
1889290000Sglebius		"% '0123lu",
1890290000Sglebius		"%+'0123lu",
1891290000Sglebius		"%-'123lu",
1892290000Sglebius		"%'123lu",
1893290000Sglebius#endif	/* !OS_IRiX */
1894290000Sglebius		"%123.9lu",
1895290000Sglebius		"% 123.9lu",
1896290000Sglebius		"%+123.9lu",
1897290000Sglebius		"%-123.9lu",
1898290000Sglebius		"%0123lu",
1899290000Sglebius		"% 0123lu",
1900290000Sglebius		"%+0123lu",
1901290000Sglebius		"%-0123lu",
1902290000Sglebius		"%5.5lu",
1903290000Sglebius		"%+22.33lu",
1904290000Sglebius		"%01.3lu",
1905290000Sglebius		"%1.5lu",
1906290000Sglebius		"%-1.5lu",
1907290000Sglebius		"%44lu",
1908290000Sglebius		"%lu",
1909290000Sglebius		/* "%o" formats. */
1910290000Sglebius		"foo|%#0123lo|bar",
1911290000Sglebius		"%#123.9lo",
1912290000Sglebius		"%# 123.9lo",
1913290000Sglebius		"%#+123.9lo",
1914290000Sglebius		"%#-123.9lo",
1915290000Sglebius		"%#0123lo",
1916290000Sglebius		"%# 0123lo",
1917290000Sglebius		"%#+0123lo",
1918290000Sglebius		"%#-0123lo",
1919290000Sglebius		"%#5.5lo",
1920290000Sglebius		"%#+22.33lo",
1921290000Sglebius		"%#01.3lo",
1922290000Sglebius		"%#1.5lo",
1923290000Sglebius		"%#-1.5lo",
1924290000Sglebius		"%#44lo",
1925290000Sglebius		"%#lo",
1926290000Sglebius		"%123.9lo",
1927290000Sglebius		"% 123.9lo",
1928290000Sglebius		"%+123.9lo",
1929290000Sglebius		"%-123.9lo",
1930290000Sglebius		"%0123lo",
1931290000Sglebius		"% 0123lo",
1932290000Sglebius		"%+0123lo",
1933290000Sglebius		"%-0123lo",
1934290000Sglebius		"%5.5lo",
1935290000Sglebius		"%+22.33lo",
1936290000Sglebius		"%01.3lo",
1937290000Sglebius		"%1.5lo",
1938290000Sglebius		"%-1.5lo",
1939290000Sglebius		"%44lo",
1940290000Sglebius		"%lo",
1941290000Sglebius		/* "%X" and "%x" formats. */
1942290000Sglebius		"foo|%#0123lX|bar",
1943290000Sglebius		"%#123.9lx",
1944290000Sglebius		"%# 123.9lx",
1945290000Sglebius		"%#+123.9lx",
1946290000Sglebius		"%#-123.9lx",
1947290000Sglebius		"%#0123lx",
1948290000Sglebius		"%# 0123lx",
1949290000Sglebius		"%#+0123lx",
1950290000Sglebius		"%#-0123lx",
1951290000Sglebius		"%#5.5lx",
1952290000Sglebius		"%#+22.33lx",
1953290000Sglebius		"%#01.3lx",
1954290000Sglebius		"%#1.5lx",
1955290000Sglebius		"%#-1.5lx",
1956290000Sglebius		"%#44lx",
1957290000Sglebius		"%#lx",
1958290000Sglebius		"%#lX",
1959290000Sglebius		"%123.9lx",
1960290000Sglebius		"% 123.9lx",
1961290000Sglebius		"%+123.9lx",
1962290000Sglebius		"%-123.9lx",
1963290000Sglebius		"%0123lx",
1964290000Sglebius		"% 0123lx",
1965290000Sglebius		"%+0123lx",
1966290000Sglebius		"%-0123lx",
1967290000Sglebius		"%5.5lx",
1968290000Sglebius		"%+22.33lx",
1969290000Sglebius		"%01.3lx",
1970290000Sglebius		"%1.5lx",
1971290000Sglebius		"%-1.5lx",
1972290000Sglebius		"%44lx",
1973290000Sglebius		"%lx",
1974290000Sglebius		"%lX",
1975290000Sglebius		NULL
1976290000Sglebius	};
1977290000Sglebius	unsigned long int ulong_val[] = {
1978290000Sglebius#ifdef ULONG_MAX
1979290000Sglebius		ULONG_MAX,
1980290000Sglebius#endif	/* ULONG_MAX */
1981290000Sglebius		91340,
1982290000Sglebius		341,
1983290000Sglebius		134,
1984290000Sglebius		0203,
1985290000Sglebius		1,
1986290000Sglebius		0
1987290000Sglebius	};
1988290000Sglebius	const char *llong_fmt[] = {
1989290000Sglebius		"foo|%0123lld|bar",
1990290000Sglebius		"%123.9lld",
1991290000Sglebius		"% 123.9lld",
1992290000Sglebius		"%+123.9lld",
1993290000Sglebius		"%-123.9lld",
1994290000Sglebius		"%0123lld",
1995290000Sglebius		"% 0123lld",
1996290000Sglebius		"%+0123lld",
1997290000Sglebius		"%-0123lld",
1998290000Sglebius		"%5.5lld",
1999290000Sglebius		"%+22.33lld",
2000290000Sglebius		"%01.3lld",
2001290000Sglebius		"%1.5lld",
2002290000Sglebius		"%-1.5lld",
2003290000Sglebius		"%44lld",
2004290000Sglebius		"%lld",
2005290000Sglebius		NULL
2006290000Sglebius	};
2007290000Sglebius	LLONG llong_val[] = {
2008290000Sglebius#ifdef LLONG_MAX
2009290000Sglebius		LLONG_MAX,
2010290000Sglebius#endif	/* LLONG_MAX */
2011290000Sglebius#ifdef LLONG_MIN
2012290000Sglebius		LLONG_MIN,
2013290000Sglebius#endif	/* LLONG_MIN */
2014290000Sglebius		-91340,
2015290000Sglebius		91340,
2016290000Sglebius		341,
2017290000Sglebius		134,
2018290000Sglebius		0203,
2019290000Sglebius		-1,
2020290000Sglebius		1,
2021290000Sglebius		0
2022290000Sglebius	};
2023290000Sglebius	const char *string_fmt[] = {
2024290000Sglebius		"foo|%10.10s|bar",
2025290000Sglebius		"%-10.10s",
2026290000Sglebius		"%10.10s",
2027290000Sglebius		"%10.5s",
2028290000Sglebius		"%5.10s",
2029290000Sglebius		"%10.1s",
2030290000Sglebius		"%1.10s",
2031290000Sglebius		"%10.0s",
2032290000Sglebius		"%0.10s",
2033290000Sglebius		"%-42.5s",
2034290000Sglebius		"%2.s",
2035290000Sglebius		"%.10s",
2036290000Sglebius		"%.1s",
2037290000Sglebius		"%.0s",
2038290000Sglebius		"%.s",
2039290000Sglebius		"%4s",
2040290000Sglebius		"%s",
2041290000Sglebius		NULL
2042290000Sglebius	};
2043290000Sglebius	const char *string_val[] = {
2044290000Sglebius		"Hello",
2045290000Sglebius		"Hello, world!",
2046290000Sglebius		"Sound check: One, two, three.",
2047290000Sglebius		"This string is a little longer than the other strings.",
2048290000Sglebius		"1",
2049290000Sglebius		"",
2050290000Sglebius		NULL
2051290000Sglebius	};
2052290000Sglebius#if !OS_SYSV	/* SysV uses a different format than we do. */
2053290000Sglebius	const char *pointer_fmt[] = {
2054290000Sglebius		"foo|%p|bar",
2055290000Sglebius		"%42p",
2056290000Sglebius		"%p",
2057290000Sglebius		NULL
2058290000Sglebius	};
2059290000Sglebius	const char *pointer_val[] = {
2060290000Sglebius		*pointer_fmt,
2061290000Sglebius		*string_fmt,
2062290000Sglebius		*string_val,
2063290000Sglebius		NULL
2064290000Sglebius	};
2065290000Sglebius#endif	/* !OS_SYSV */
2066290000Sglebius	char buf1[1024], buf2[1024];
2067290000Sglebius	double value, digits = 9.123456789012345678901234567890123456789;
2068290000Sglebius	int i, j, r1, r2, failed = 0, num = 0;
2069290000Sglebius
2070290000Sglebius/*
2071290000Sglebius * Use -DTEST_NILS in order to also test the conversion of nil values.  Might
2072290000Sglebius * segfault on systems which don't support converting a NULL pointer with "%s"
2073290000Sglebius * and lets some test cases fail against BSD and glibc due to bugs in their
2074290000Sglebius * implementations.
2075290000Sglebius */
2076290000Sglebius#ifndef TEST_NILS
2077290000Sglebius#define TEST_NILS 0
2078290000Sglebius#elif TEST_NILS
2079290000Sglebius#undef TEST_NILS
2080290000Sglebius#define TEST_NILS 1
2081290000Sglebius#endif	/* !defined(TEST_NILS) */
2082290000Sglebius#ifdef TEST
2083290000Sglebius#undef TEST
2084290000Sglebius#endif	/* defined(TEST) */
2085290000Sglebius#define TEST(fmt, val)                                                         \
2086290000Sglebiusdo {                                                                           \
2087290000Sglebius	for (i = 0; fmt[i] != NULL; i++)                                       \
2088290000Sglebius		for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) {          \
2089290000Sglebius			r1 = sprintf(buf1, fmt[i], val[j]);                    \
2090290000Sglebius			r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]);     \
2091290000Sglebius			if (strcmp(buf1, buf2) != 0 || r1 != r2) {             \
2092290000Sglebius				(void)printf("Results don't match, "           \
2093290000Sglebius				    "format string: %s\n"                      \
2094290000Sglebius				    "\t sprintf(3): [%s] (%d)\n"               \
2095290000Sglebius				    "\tsnprintf(3): [%s] (%d)\n",              \
2096290000Sglebius				    fmt[i], buf1, r1, buf2, r2);               \
2097290000Sglebius				failed++;                                      \
2098290000Sglebius			}                                                      \
2099290000Sglebius			num++;                                                 \
2100290000Sglebius		}                                                              \
2101290000Sglebius} while (/* CONSTCOND */ 0)
2102290000Sglebius
2103290000Sglebius#if HAVE_LOCALE_H
2104290000Sglebius	(void)setlocale(LC_ALL, "");
2105290000Sglebius#endif	/* HAVE_LOCALE_H */
2106290000Sglebius
2107290000Sglebius	(void)puts("Testing our snprintf(3) against your system's sprintf(3).");
2108290000Sglebius	TEST(float_fmt, float_val);
2109290000Sglebius	TEST(long_fmt, long_val);
2110290000Sglebius	TEST(ulong_fmt, ulong_val);
2111290000Sglebius	TEST(llong_fmt, llong_val);
2112290000Sglebius	TEST(string_fmt, string_val);
2113290000Sglebius#if !OS_SYSV	/* SysV uses a different format than we do. */
2114290000Sglebius	TEST(pointer_fmt, pointer_val);
2115290000Sglebius#endif	/* !OS_SYSV */
2116290000Sglebius	(void)printf("Result: %d out of %d tests failed.\n", failed, num);
2117290000Sglebius
2118290000Sglebius	(void)fputs("Checking how many digits we support: ", stdout);
2119290000Sglebius	for (i = 0; i < 100; i++) {
2120290000Sglebius		value = pow(10, i) * digits;
2121290000Sglebius		(void)sprintf(buf1, "%.1f", value);
2122290000Sglebius		(void)snprintf(buf2, sizeof(buf2), "%.1f", value);
2123290000Sglebius		if (strcmp(buf1, buf2) != 0) {
2124290000Sglebius			(void)printf("apparently %d.\n", i);
2125290000Sglebius			break;
2126290000Sglebius		}
2127290000Sglebius	}
2128290000Sglebius	return (failed == 0) ? 0 : 1;
212982498Sroberto}
2130290000Sglebius#endif	/* TEST_SNPRINTF */
2131290000Sglebius
2132290000Sglebius/* vim: set joinspaces textwidth=80: */
2133