1/*
2 * Modified by Dave Hart for integration into NTP 4.2.7 <hart@ntp.org>
3 *
4 * Changed in a backwards-incompatible way to separate HAVE_SNPRINTF
5 * from HW_WANT_RPL_SNPRINTF, etc. for each of the four replaced
6 * functions.
7 *
8 * Changed to honor hw_force_rpl_snprintf=yes, etc.  This is used by NTP
9 * to test rpl_snprintf() and rpl_vsnprintf() on platforms which provide
10 * C99-compliant implementations.
11 */
12
13/* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */
14
15/*
16 * Copyright (c) 1995 Patrick Powell.
17 *
18 * This code is based on code written by Patrick Powell <papowell@astart.com>.
19 * It may be used for any purpose as long as this notice remains intact on all
20 * source code distributions.
21 */
22
23/*
24 * Copyright (c) 2008 Holger Weiss.
25 *
26 * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
27 * My changes to the code may freely be used, modified and/or redistributed for
28 * any purpose.  It would be nice if additions and fixes to this file (including
29 * trivial code cleanups) would be sent back in order to let me include them in
30 * the version available at <http://www.jhweiss.de/software/snprintf.html>.
31 * However, this is not a requirement for using or redistributing (possibly
32 * modified) versions of this file, nor is leaving this notice intact mandatory.
33 */
34
35/*
36 * History
37 *
38 * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
39 *
40 * 	Fixed the detection of infinite floating point values on IRIX (and
41 * 	possibly other systems) and applied another few minor cleanups.
42 *
43 * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
44 *
45 * 	Added a lot of new features, fixed many bugs, and incorporated various
46 * 	improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
47 * 	<rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
48 * 	<djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
49 * 	projects.  The additions include: support the "e", "E", "g", "G", and
50 * 	"F" conversion specifiers (and use conversion style "f" or "F" for the
51 * 	still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
52 * 	"t", and "z" length modifiers; support the "#" flag and the (non-C99)
53 * 	"'" flag; use localeconv(3) (if available) to get both the current
54 * 	locale's decimal point character and the separator between groups of
55 * 	digits; fix the handling of various corner cases of field width and
56 * 	precision specifications; fix various floating point conversion bugs;
57 * 	handle infinite and NaN floating point values; don't attempt to write to
58 * 	the output buffer (which may be NULL) if a size of zero was specified;
59 * 	check for integer overflow of the field width, precision, and return
60 * 	values and during the floating point conversion; use the OUTCHAR() macro
61 * 	instead of a function for better performance; provide asprintf(3) and
62 * 	vasprintf(3) functions; add new test cases.  The replacement functions
63 * 	have been renamed to use an "rpl_" prefix, the function calls in the
64 * 	main project (and in this file) must be redefined accordingly for each
65 * 	replacement function which is needed (by using Autoconf or other means).
66 * 	Various other minor improvements have been applied and the coding style
67 * 	was cleaned up for consistency.
68 *
69 * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
70 *
71 * 	C99 compliant snprintf(3) and vsnprintf(3) functions return the number
72 * 	of characters that would have been written to a sufficiently sized
73 * 	buffer (excluding the '\0').  The original code simply returned the
74 * 	length of the resulting output string, so that's been fixed.
75 *
76 * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
77 *
78 * 	The original code assumed that both snprintf(3) and vsnprintf(3) were
79 * 	missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
80 * 	the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
81 *
82 * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
83 *
84 * 	The PGP code was using unsigned hexadecimal formats.  Unfortunately,
85 * 	unsigned formats simply didn't work.
86 *
87 * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
88 *
89 * 	Ok, added some minimal floating point support, which means this probably
90 * 	requires libm on most operating systems.  Don't yet support the exponent
91 * 	(e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
92 * 	wasn't being exercised in ways which showed it, so that's been fixed.
93 * 	Also, formatted the code to Mutt conventions, and removed dead code left
94 * 	over from the original.  Also, there is now a builtin-test, run with:
95 * 	gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
96 *
97 * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
98 *
99 * 	This was ugly.  It is still ugly.  I opted out of floating point
100 * 	numbers, but the formatter understands just about everything from the
101 * 	normal C string format, at least as far as I can tell from the Solaris
102 * 	2.5 printf(3S) man page.
103 */
104
105/*
106 * ToDo
107 *
108 * - Add wide character support.
109 * - Add support for "%a" and "%A" conversions.
110 * - Create test routines which predefine the expected results.  Our test cases
111 *   usually expose bugs in system implementations rather than in ours :-)
112 */
113
114/*
115 * Usage
116 *
117 * 1) The following preprocessor macros should be defined to 1 if the feature or
118 *    file in question is available on the target system (by using Autoconf or
119 *    other means), though basic functionality should be available as long as
120 *    HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
121 *
122 *	HW_WANT_RPL_VSNPRINTF
123 *	HW_WANT_RPL_SNPRINTF
124 *	HW_WANT_RPL_VASPRINTF
125 *	HW_WANT_RPL_ASPRINTF
126 *	HAVE_VSNPRINTF	// define to 1 #if HW_WANT_RPL_VSNPRINTF
127 *	HAVE_SNPRINTF	// define to 1 #if HW_WANT_RPL_SNPRINTF
128 *	HAVE_VASPRINTF	// define to 1 #if HW_WANT_RPL_VASPRINTF
129 *	HAVE_ASPRINTF	// define to 1 #if HW_WANT_RPL_ASPRINTF
130 *	HAVE_STDARG_H
131 *	HAVE_STDDEF_H
132 *	HAVE_STDINT_H
133 *	HAVE_STDLIB_H
134 *	HAVE_INTTYPES_H
135 *	HAVE_LOCALE_H
136 *	HAVE_LOCALECONV
137 *	HAVE_LCONV_DECIMAL_POINT
138 *	HAVE_LCONV_THOUSANDS_SEP
139 *	HAVE_LONG_DOUBLE
140 *	HAVE_LONG_LONG_INT
141 *	HAVE_UNSIGNED_LONG_LONG_INT
142 *	HAVE_INTMAX_T
143 *	HAVE_UINTMAX_T
144 *	HAVE_UINTPTR_T
145 *	HAVE_PTRDIFF_T
146 *	HAVE_VA_COPY
147 *	HAVE___VA_COPY
148 *
149 * 2) The calls to the functions which should be replaced must be redefined
150 *    throughout the project files (by using Autoconf or other means):
151 *
152 *	#if HW_WANT_RPL_VSNPRINTF
153 *	#define vsnprintf rpl_vsnprintf
154 *	#endif
155 *	#if HW_WANT_RPL_SNPRINTF
156 *	#define snprintf rpl_snprintf
157 *	#endif
158 *	#if HW_WANT_RPL_VASPRINTF
159 *	#define vasprintf rpl_vasprintf
160 *	#endif
161 *	#if HW_WANT_RPL_ASPRINTF
162 *	#define asprintf rpl_asprintf
163 *	#endif
164 *
165 * 3) The required replacement functions should be declared in some header file
166 *    included throughout the project files:
167 *
168 *	#if HAVE_CONFIG_H
169 *	#include <config.h>
170 *	#endif
171 *	#if HAVE_STDARG_H
172 *	#include <stdarg.h>
173 *	#if HW_WANT_RPL_VSNPRINTF
174 *	int rpl_vsnprintf(char *, size_t, const char *, va_list);
175 *	#endif
176 *	#if HW_WANT_RPL_SNPRINTF
177 *	int rpl_snprintf(char *, size_t, const char *, ...);
178 *	#endif
179 *	#if HW_WANT_RPL_VASPRINTF
180 *	int rpl_vasprintf(char **, const char *, va_list);
181 *	#endif
182 *	#if HW_WANT_RPL_ASPRINTF
183 *	int rpl_asprintf(char **, const char *, ...);
184 *	#endif
185 *	#endif
186 *
187 * Autoconf macros for handling step 1 and step 2 are available at
188 * <http://www.jhweiss.de/software/snprintf.html>.
189 */
190
191#if HAVE_CONFIG_H
192#include <config.h>
193#endif	/* HAVE_CONFIG_H */
194
195#if TEST_SNPRINTF
196#include <math.h>	/* For pow(3), NAN, and INFINITY. */
197#include <string.h>	/* For strcmp(3). */
198#if defined(__NetBSD__) || \
199    defined(__FreeBSD__) || \
200    defined(__OpenBSD__) || \
201    defined(__NeXT__) || \
202    defined(__bsd__)
203#define OS_BSD 1
204#elif defined(sgi) || defined(__sgi)
205#ifndef __c99
206#define __c99	/* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
207#endif	/* !defined(__c99) */
208#define OS_IRIX 1
209#define OS_SYSV 1
210#elif defined(__svr4__)
211#define OS_SYSV 1
212#elif defined(__linux__)
213#define OS_LINUX 1
214#endif	/* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
215#if HAVE_CONFIG_H	/* Undefine definitions possibly done in config.h. */
216#ifdef HAVE_SNPRINTF
217#undef HAVE_SNPRINTF
218#endif	/* defined(HAVE_SNPRINTF) */
219#ifdef HAVE_VSNPRINTF
220#undef HAVE_VSNPRINTF
221#endif	/* defined(HAVE_VSNPRINTF) */
222#ifdef HAVE_ASPRINTF
223#undef HAVE_ASPRINTF
224#endif	/* defined(HAVE_ASPRINTF) */
225#ifdef HAVE_VASPRINTF
226#undef HAVE_VASPRINTF
227#endif	/* defined(HAVE_VASPRINTF) */
228#ifdef snprintf
229#undef snprintf
230#endif	/* defined(snprintf) */
231#ifdef vsnprintf
232#undef vsnprintf
233#endif	/* defined(vsnprintf) */
234#ifdef asprintf
235#undef asprintf
236#endif	/* defined(asprintf) */
237#ifdef vasprintf
238#undef vasprintf
239#endif	/* defined(vasprintf) */
240#else	/* By default, we assume a modern system for testing. */
241#ifndef HAVE_STDARG_H
242#define HAVE_STDARG_H 1
243#endif	/* HAVE_STDARG_H */
244#ifndef HAVE_STDDEF_H
245#define HAVE_STDDEF_H 1
246#endif	/* HAVE_STDDEF_H */
247#ifndef HAVE_STDINT_H
248#define HAVE_STDINT_H 1
249#endif	/* HAVE_STDINT_H */
250#ifndef HAVE_STDLIB_H
251#define HAVE_STDLIB_H 1
252#endif	/* HAVE_STDLIB_H */
253#ifndef HAVE_INTTYPES_H
254#define HAVE_INTTYPES_H 1
255#endif	/* HAVE_INTTYPES_H */
256#ifndef HAVE_LOCALE_H
257#define HAVE_LOCALE_H 1
258#endif	/* HAVE_LOCALE_H */
259#ifndef HAVE_LOCALECONV
260#define HAVE_LOCALECONV 1
261#endif	/* !defined(HAVE_LOCALECONV) */
262#ifndef HAVE_LCONV_DECIMAL_POINT
263#define HAVE_LCONV_DECIMAL_POINT 1
264#endif	/* HAVE_LCONV_DECIMAL_POINT */
265#ifndef HAVE_LCONV_THOUSANDS_SEP
266#define HAVE_LCONV_THOUSANDS_SEP 1
267#endif	/* HAVE_LCONV_THOUSANDS_SEP */
268#ifndef HAVE_LONG_DOUBLE
269#define HAVE_LONG_DOUBLE 1
270#endif	/* !defined(HAVE_LONG_DOUBLE) */
271#ifndef HAVE_LONG_LONG_INT
272#define HAVE_LONG_LONG_INT 1
273#endif	/* !defined(HAVE_LONG_LONG_INT) */
274#ifndef HAVE_UNSIGNED_LONG_LONG_INT
275#define HAVE_UNSIGNED_LONG_LONG_INT 1
276#endif	/* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
277#ifndef HAVE_INTMAX_T
278#define HAVE_INTMAX_T 1
279#endif	/* !defined(HAVE_INTMAX_T) */
280#ifndef HAVE_UINTMAX_T
281#define HAVE_UINTMAX_T 1
282#endif	/* !defined(HAVE_UINTMAX_T) */
283#ifndef HAVE_UINTPTR_T
284#define HAVE_UINTPTR_T 1
285#endif	/* !defined(HAVE_UINTPTR_T) */
286#ifndef HAVE_PTRDIFF_T
287#define HAVE_PTRDIFF_T 1
288#endif	/* !defined(HAVE_PTRDIFF_T) */
289#ifndef HAVE_VA_COPY
290#define HAVE_VA_COPY 1
291#endif	/* !defined(HAVE_VA_COPY) */
292#ifndef HAVE___VA_COPY
293#define HAVE___VA_COPY 1
294#endif	/* !defined(HAVE___VA_COPY) */
295#endif	/* HAVE_CONFIG_H */
296#define snprintf rpl_snprintf
297#define vsnprintf rpl_vsnprintf
298#define asprintf rpl_asprintf
299#define vasprintf rpl_vasprintf
300#endif	/* TEST_SNPRINTF */
301
302#if HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || HW_WANT_RPL_VASPRINTF
303#include <stdio.h>	/* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
304#ifdef VA_START
305#undef VA_START
306#endif	/* defined(VA_START) */
307#ifdef VA_SHIFT
308#undef VA_SHIFT
309#endif	/* defined(VA_SHIFT) */
310#if HAVE_STDARG_H
311#include <stdarg.h>
312#define VA_START(ap, last) va_start(ap, last)
313#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
314#else	/* Assume <varargs.h> is available. */
315#include <varargs.h>
316#define VA_START(ap, last) va_start(ap)	/* "last" is ignored. */
317#define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
318#endif	/* HAVE_STDARG_H */
319
320#if HW_WANT_RPL_VASPRINTF
321#if HAVE_STDLIB_H
322#include <stdlib.h>	/* For malloc(3). */
323#endif	/* HAVE_STDLIB_H */
324#ifdef VA_COPY
325#undef VA_COPY
326#endif	/* defined(VA_COPY) */
327#ifdef VA_END_COPY
328#undef VA_END_COPY
329#endif	/* defined(VA_END_COPY) */
330#if HAVE_VA_COPY
331#define VA_COPY(dest, src) va_copy(dest, src)
332#define VA_END_COPY(ap) va_end(ap)
333#elif HAVE___VA_COPY
334#define VA_COPY(dest, src) __va_copy(dest, src)
335#define VA_END_COPY(ap) va_end(ap)
336#else
337#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
338#define VA_END_COPY(ap) /* No-op. */
339#define NEED_MYMEMCPY 1
340static void *mymemcpy(void *, void *, size_t);
341#endif	/* HAVE_VA_COPY */
342#endif	/* HW_WANT_RPL_VASPRINTF */
343
344#if HW_WANT_RPL_VSNPRINTF
345#include <errno.h>	/* For ERANGE and errno. */
346#include <limits.h>	/* For *_MAX. */
347#if HAVE_INTTYPES_H
348#include <inttypes.h>	/* For intmax_t (if not defined in <stdint.h>). */
349#endif	/* HAVE_INTTYPES_H */
350#if HAVE_LOCALE_H
351#include <locale.h>	/* For localeconv(3). */
352#endif	/* HAVE_LOCALE_H */
353#if HAVE_STDDEF_H
354#include <stddef.h>	/* For ptrdiff_t. */
355#endif	/* HAVE_STDDEF_H */
356#if HAVE_STDINT_H
357#include <stdint.h>	/* For intmax_t. */
358#endif	/* HAVE_STDINT_H */
359
360/* Support for unsigned long long int.  We may also need ULLONG_MAX. */
361#ifndef ULONG_MAX	/* We may need ULONG_MAX as a fallback. */
362#ifdef UINT_MAX
363#define ULONG_MAX UINT_MAX
364#else
365#define ULONG_MAX INT_MAX
366#endif	/* defined(UINT_MAX) */
367#endif	/* !defined(ULONG_MAX) */
368#ifdef ULLONG
369#undef ULLONG
370#endif	/* defined(ULLONG) */
371#if HAVE_UNSIGNED_LONG_LONG_INT
372#define ULLONG unsigned long long int
373#ifndef ULLONG_MAX
374#define ULLONG_MAX ULONG_MAX
375#endif	/* !defined(ULLONG_MAX) */
376#else
377#define ULLONG unsigned long int
378#ifdef ULLONG_MAX
379#undef ULLONG_MAX
380#endif	/* defined(ULLONG_MAX) */
381#define ULLONG_MAX ULONG_MAX
382#endif	/* HAVE_LONG_LONG_INT */
383
384/* Support for uintmax_t.  We also need UINTMAX_MAX. */
385#ifdef UINTMAX_T
386#undef UINTMAX_T
387#endif	/* defined(UINTMAX_T) */
388#if HAVE_UINTMAX_T || defined(uintmax_t)
389#define UINTMAX_T uintmax_t
390#ifndef UINTMAX_MAX
391#define UINTMAX_MAX ULLONG_MAX
392#endif	/* !defined(UINTMAX_MAX) */
393#else
394#define UINTMAX_T ULLONG
395#ifdef UINTMAX_MAX
396#undef UINTMAX_MAX
397#endif	/* defined(UINTMAX_MAX) */
398#define UINTMAX_MAX ULLONG_MAX
399#endif	/* HAVE_UINTMAX_T || defined(uintmax_t) */
400
401/* Support for long double. */
402#ifndef LDOUBLE
403#if HAVE_LONG_DOUBLE
404#define LDOUBLE long double
405#else
406#define LDOUBLE double
407#endif	/* HAVE_LONG_DOUBLE */
408#endif	/* !defined(LDOUBLE) */
409
410/* Support for long long int. */
411#ifndef LLONG
412#if HAVE_LONG_LONG_INT
413#define LLONG long long int
414#else
415#define LLONG long int
416#endif	/* HAVE_LONG_LONG_INT */
417#endif	/* !defined(LLONG) */
418
419/* Support for intmax_t. */
420#ifndef INTMAX_T
421#if HAVE_INTMAX_T || defined(intmax_t)
422#define INTMAX_T intmax_t
423#else
424#define INTMAX_T LLONG
425#endif	/* HAVE_INTMAX_T || defined(intmax_t) */
426#endif	/* !defined(INTMAX_T) */
427
428/* Support for uintptr_t. */
429#ifndef UINTPTR_T
430#if HAVE_UINTPTR_T || defined(uintptr_t)
431#define UINTPTR_T uintptr_t
432#else
433#define UINTPTR_T unsigned long int
434#endif	/* HAVE_UINTPTR_T || defined(uintptr_t) */
435#endif	/* !defined(UINTPTR_T) */
436
437/* Support for ptrdiff_t. */
438#ifndef PTRDIFF_T
439#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
440#define PTRDIFF_T ptrdiff_t
441#else
442#define PTRDIFF_T long int
443#endif	/* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
444#endif	/* !defined(PTRDIFF_T) */
445
446/*
447 * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
448 * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
449 * unsigned type if necessary.  This should work just fine in practice.
450 */
451#ifndef UPTRDIFF_T
452#define UPTRDIFF_T PTRDIFF_T
453#endif	/* !defined(UPTRDIFF_T) */
454
455/*
456 * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
457 * However, we'll simply use size_t and convert it to a signed type if
458 * necessary.  This should work just fine in practice.
459 */
460#ifndef SSIZE_T
461#define SSIZE_T size_t
462#endif	/* !defined(SSIZE_T) */
463
464/* Either ERANGE or E2BIG should be available everywhere. */
465#ifndef ERANGE
466#define ERANGE E2BIG
467#endif	/* !defined(ERANGE) */
468#ifndef EOVERFLOW
469#define EOVERFLOW ERANGE
470#endif	/* !defined(EOVERFLOW) */
471
472/*
473 * Buffer size to hold the octal string representation of UINT128_MAX without
474 * nul-termination ("3777777777777777777777777777777777777777777").
475 */
476#ifdef MAX_CONVERT_LENGTH
477#undef MAX_CONVERT_LENGTH
478#endif	/* defined(MAX_CONVERT_LENGTH) */
479#define MAX_CONVERT_LENGTH      43
480
481/* Format read states. */
482#define PRINT_S_DEFAULT         0
483#define PRINT_S_FLAGS           1
484#define PRINT_S_WIDTH           2
485#define PRINT_S_DOT             3
486#define PRINT_S_PRECISION       4
487#define PRINT_S_MOD             5
488#define PRINT_S_CONV            6
489
490/* Format flags. */
491#define PRINT_F_MINUS           (1 << 0)
492#define PRINT_F_PLUS            (1 << 1)
493#define PRINT_F_SPACE           (1 << 2)
494#define PRINT_F_NUM             (1 << 3)
495#define PRINT_F_ZERO            (1 << 4)
496#define PRINT_F_QUOTE           (1 << 5)
497#define PRINT_F_UP              (1 << 6)
498#define PRINT_F_UNSIGNED        (1 << 7)
499#define PRINT_F_TYPE_G          (1 << 8)
500#define PRINT_F_TYPE_E          (1 << 9)
501
502/* Conversion flags. */
503#define PRINT_C_CHAR            1
504#define PRINT_C_SHORT           2
505#define PRINT_C_LONG            3
506#define PRINT_C_LLONG           4
507#define PRINT_C_LDOUBLE         5
508#define PRINT_C_SIZE            6
509#define PRINT_C_PTRDIFF         7
510#define PRINT_C_INTMAX          8
511
512#ifndef MAX
513#define MAX(x, y) ((x >= y) ? x : y)
514#endif	/* !defined(MAX) */
515#ifndef CHARTOINT
516#define CHARTOINT(ch) (ch - '0')
517#endif	/* !defined(CHARTOINT) */
518#ifndef ISDIGIT
519#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
520#endif	/* !defined(ISDIGIT) */
521#ifndef ISNAN
522#define ISNAN(x) (x != x)
523#endif	/* !defined(ISNAN) */
524#ifndef ISINF
525#define ISINF(x) (x != 0.0 && x + x == x)
526#endif	/* !defined(ISINF) */
527
528#ifdef OUTCHAR
529#undef OUTCHAR
530#endif	/* defined(OUTCHAR) */
531#define OUTCHAR(str, len, size, ch)                                          \
532do {                                                                         \
533	if (len + 1 < size)                                                  \
534		str[len] = ch;                                               \
535	(len)++;                                                             \
536} while (/* CONSTCOND */ 0)
537
538static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
539static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
540static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
541static void printsep(char *, size_t *, size_t);
542static int getnumsep(int);
543static int getexponent(LDOUBLE);
544static int convert(UINTMAX_T, char *, size_t, int, int);
545static UINTMAX_T cast(LDOUBLE);
546static UINTMAX_T myround(LDOUBLE);
547static LDOUBLE mypow10(int);
548
549int
550rpl_vsnprintf(char *str, size_t size, const char *format, va_list args);
551
552int
553rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
554{
555	LDOUBLE fvalue;
556	INTMAX_T value;
557	unsigned char cvalue;
558	const char *strvalue;
559	INTMAX_T *intmaxptr;
560	PTRDIFF_T *ptrdiffptr;
561	SSIZE_T *sizeptr;
562	LLONG *llongptr;
563	long int *longptr;
564	int *intptr;
565	short int *shortptr;
566	signed char *charptr;
567	size_t len = 0;
568	int overflow = 0;
569	int base = 0;
570	int cflags = 0;
571	int flags = 0;
572	int width = 0;
573	int precision = -1;
574	int state = PRINT_S_DEFAULT;
575	char ch = *format++;
576
577	/*
578	 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
579	 * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
580	 * even if a size larger than zero was specified.  At least NetBSD's
581	 * snprintf(3) does the same, as well as other versions of this file.
582	 * (Though some of these versions will write to a non-NULL buffer even
583	 * if a size of zero was specified, which violates the standard.)
584	 */
585	if (str == NULL && size != 0)
586		size = 0;
587
588	while (ch != '\0')
589		switch (state) {
590		case PRINT_S_DEFAULT:
591			if (ch == '%')
592				state = PRINT_S_FLAGS;
593			else
594				OUTCHAR(str, len, size, ch);
595			ch = *format++;
596			break;
597		case PRINT_S_FLAGS:
598			switch (ch) {
599			case '-':
600				flags |= PRINT_F_MINUS;
601				ch = *format++;
602				break;
603			case '+':
604				flags |= PRINT_F_PLUS;
605				ch = *format++;
606				break;
607			case ' ':
608				flags |= PRINT_F_SPACE;
609				ch = *format++;
610				break;
611			case '#':
612				flags |= PRINT_F_NUM;
613				ch = *format++;
614				break;
615			case '0':
616				flags |= PRINT_F_ZERO;
617				ch = *format++;
618				break;
619			case '\'':	/* SUSv2 flag (not in C99). */
620				flags |= PRINT_F_QUOTE;
621				ch = *format++;
622				break;
623			default:
624				state = PRINT_S_WIDTH;
625				break;
626			}
627			break;
628		case PRINT_S_WIDTH:
629			if (ISDIGIT(ch)) {
630				ch = CHARTOINT(ch);
631				if (width > (INT_MAX - ch) / 10) {
632					overflow = 1;
633					goto out;
634				}
635				width = 10 * width + ch;
636				ch = *format++;
637			} else if (ch == '*') {
638				/*
639				 * C99 says: "A negative field width argument is
640				 * taken as a `-' flag followed by a positive
641				 * field width." (7.19.6.1, 5)
642				 */
643				if ((width = va_arg(args, int)) < 0) {
644					flags |= PRINT_F_MINUS;
645					width = -width;
646				}
647				ch = *format++;
648				state = PRINT_S_DOT;
649			} else
650				state = PRINT_S_DOT;
651			break;
652		case PRINT_S_DOT:
653			if (ch == '.') {
654				state = PRINT_S_PRECISION;
655				ch = *format++;
656			} else
657				state = PRINT_S_MOD;
658			break;
659		case PRINT_S_PRECISION:
660			if (precision == -1)
661				precision = 0;
662			if (ISDIGIT(ch)) {
663				ch = CHARTOINT(ch);
664				if (precision > (INT_MAX - ch) / 10) {
665					overflow = 1;
666					goto out;
667				}
668				precision = 10 * precision + ch;
669				ch = *format++;
670			} else if (ch == '*') {
671				/*
672				 * C99 says: "A negative precision argument is
673				 * taken as if the precision were omitted."
674				 * (7.19.6.1, 5)
675				 */
676				if ((precision = va_arg(args, int)) < 0)
677					precision = -1;
678				ch = *format++;
679				state = PRINT_S_MOD;
680			} else
681				state = PRINT_S_MOD;
682			break;
683		case PRINT_S_MOD:
684			switch (ch) {
685			case 'h':
686				ch = *format++;
687				if (ch == 'h') {	/* It's a char. */
688					ch = *format++;
689					cflags = PRINT_C_CHAR;
690				} else
691					cflags = PRINT_C_SHORT;
692				break;
693			case 'l':
694				ch = *format++;
695				if (ch == 'l') {	/* It's a long long. */
696					ch = *format++;
697					cflags = PRINT_C_LLONG;
698				} else
699					cflags = PRINT_C_LONG;
700				break;
701			case 'L':
702				cflags = PRINT_C_LDOUBLE;
703				ch = *format++;
704				break;
705			case 'j':
706				cflags = PRINT_C_INTMAX;
707				ch = *format++;
708				break;
709			case 't':
710				cflags = PRINT_C_PTRDIFF;
711				ch = *format++;
712				break;
713			case 'z':
714				cflags = PRINT_C_SIZE;
715				ch = *format++;
716				break;
717			}
718			state = PRINT_S_CONV;
719			break;
720		case PRINT_S_CONV:
721			switch (ch) {
722			case 'd':
723				/* FALLTHROUGH */
724			case 'i':
725				switch (cflags) {
726				case PRINT_C_CHAR:
727					value = (signed char)va_arg(args, int);
728					break;
729				case PRINT_C_SHORT:
730					value = (short int)va_arg(args, int);
731					break;
732				case PRINT_C_LONG:
733					value = va_arg(args, long int);
734					break;
735				case PRINT_C_LLONG:
736					value = va_arg(args, LLONG);
737					break;
738				case PRINT_C_SIZE:
739					value = va_arg(args, SSIZE_T);
740					break;
741				case PRINT_C_INTMAX:
742					value = va_arg(args, INTMAX_T);
743					break;
744				case PRINT_C_PTRDIFF:
745					value = va_arg(args, PTRDIFF_T);
746					break;
747				default:
748					value = va_arg(args, int);
749					break;
750				}
751				fmtint(str, &len, size, value, 10, width,
752				    precision, flags);
753				break;
754			case 'X':
755				flags |= PRINT_F_UP;
756				/* FALLTHROUGH */
757			case 'x':
758				base = 16;
759				/* FALLTHROUGH */
760			case 'o':
761				if (base == 0)
762					base = 8;
763				/* FALLTHROUGH */
764			case 'u':
765				if (base == 0)
766					base = 10;
767				flags |= PRINT_F_UNSIGNED;
768				switch (cflags) {
769				case PRINT_C_CHAR:
770					value = (unsigned char)va_arg(args,
771					    unsigned int);
772					break;
773				case PRINT_C_SHORT:
774					value = (unsigned short int)va_arg(args,
775					    unsigned int);
776					break;
777				case PRINT_C_LONG:
778					value = va_arg(args, unsigned long int);
779					break;
780				case PRINT_C_LLONG:
781					value = va_arg(args, ULLONG);
782					break;
783				case PRINT_C_SIZE:
784					value = va_arg(args, size_t);
785					break;
786				case PRINT_C_INTMAX:
787					value = va_arg(args, UINTMAX_T);
788					break;
789				case PRINT_C_PTRDIFF:
790					value = va_arg(args, UPTRDIFF_T);
791					break;
792				default:
793					value = va_arg(args, unsigned int);
794					break;
795				}
796				fmtint(str, &len, size, value, base, width,
797				    precision, flags);
798				break;
799			case 'A':
800				/* Not yet supported, we'll use "%F". */
801				/* FALLTHROUGH */
802			case 'F':
803				flags |= PRINT_F_UP;
804				/* FALLTHROUGH */
805			case 'a':
806				/* Not yet supported, we'll use "%f". */
807				/* FALLTHROUGH */
808			case 'f':
809				if (cflags == PRINT_C_LDOUBLE)
810					fvalue = va_arg(args, LDOUBLE);
811				else
812					fvalue = va_arg(args, double);
813				fmtflt(str, &len, size, fvalue, width,
814				    precision, flags, &overflow);
815				if (overflow)
816					goto out;
817				break;
818			case 'E':
819				flags |= PRINT_F_UP;
820				/* FALLTHROUGH */
821			case 'e':
822				flags |= PRINT_F_TYPE_E;
823				if (cflags == PRINT_C_LDOUBLE)
824					fvalue = va_arg(args, LDOUBLE);
825				else
826					fvalue = va_arg(args, double);
827				fmtflt(str, &len, size, fvalue, width,
828				    precision, flags, &overflow);
829				if (overflow)
830					goto out;
831				break;
832			case 'G':
833				flags |= PRINT_F_UP;
834				/* FALLTHROUGH */
835			case 'g':
836				flags |= PRINT_F_TYPE_G;
837				if (cflags == PRINT_C_LDOUBLE)
838					fvalue = va_arg(args, LDOUBLE);
839				else
840					fvalue = va_arg(args, double);
841				/*
842				 * If the precision is zero, it is treated as
843				 * one (cf. C99: 7.19.6.1, 8).
844				 */
845				if (precision == 0)
846					precision = 1;
847				fmtflt(str, &len, size, fvalue, width,
848				    precision, flags, &overflow);
849				if (overflow)
850					goto out;
851				break;
852			case 'c':
853				cvalue = va_arg(args, int);
854				OUTCHAR(str, len, size, cvalue);
855				break;
856			case 's':
857				strvalue = va_arg(args, char *);
858				fmtstr(str, &len, size, strvalue, width,
859				    precision, flags);
860				break;
861			case 'p':
862				/*
863				 * C99 says: "The value of the pointer is
864				 * converted to a sequence of printing
865				 * characters, in an implementation-defined
866				 * manner." (C99: 7.19.6.1, 8)
867				 */
868				if ((strvalue = va_arg(args, void *)) == NULL)
869					/*
870					 * We use the glibc format.  BSD prints
871					 * "0x0", SysV "0".
872					 */
873					fmtstr(str, &len, size, "(nil)", width,
874					    -1, flags);
875				else {
876					/*
877					 * We use the BSD/glibc format.  SysV
878					 * omits the "0x" prefix (which we emit
879					 * using the PRINT_F_NUM flag).
880					 */
881					flags |= PRINT_F_NUM;
882					flags |= PRINT_F_UNSIGNED;
883					fmtint(str, &len, size,
884					    (UINTPTR_T)strvalue, 16, width,
885					    precision, flags);
886				}
887				break;
888			case 'n':
889				switch (cflags) {
890				case PRINT_C_CHAR:
891					charptr = va_arg(args, signed char *);
892					*charptr = (signed char)len;
893					break;
894				case PRINT_C_SHORT:
895					shortptr = va_arg(args, short int *);
896					*shortptr = (short int)len;
897					break;
898				case PRINT_C_LONG:
899					longptr = va_arg(args, long int *);
900					*longptr = (long int)len;
901					break;
902				case PRINT_C_LLONG:
903					llongptr = va_arg(args, LLONG *);
904					*llongptr = (LLONG)len;
905					break;
906				case PRINT_C_SIZE:
907					/*
908					 * C99 says that with the "z" length
909					 * modifier, "a following `n' conversion
910					 * specifier applies to a pointer to a
911					 * signed integer type corresponding to
912					 * size_t argument." (7.19.6.1, 7)
913					 */
914					sizeptr = va_arg(args, SSIZE_T *);
915					*sizeptr = (SSIZE_T)len;
916					break;
917				case PRINT_C_INTMAX:
918					intmaxptr = va_arg(args, INTMAX_T *);
919					*intmaxptr = (INTMAX_T)len;
920					break;
921				case PRINT_C_PTRDIFF:
922					ptrdiffptr = va_arg(args, PTRDIFF_T *);
923					*ptrdiffptr = (PTRDIFF_T)len;
924					break;
925				default:
926					intptr = va_arg(args, int *);
927					*intptr = (int)len;
928					break;
929				}
930				break;
931			case '%':	/* Print a "%" character verbatim. */
932				OUTCHAR(str, len, size, ch);
933				break;
934			default:	/* Skip other characters. */
935				break;
936			}
937			ch = *format++;
938			state = PRINT_S_DEFAULT;
939			base = cflags = flags = width = 0;
940			precision = -1;
941			break;
942		}
943out:
944	if (len < size)
945		str[len] = '\0';
946	else if (size > 0)
947		str[size - 1] = '\0';
948
949	if (overflow || len >= INT_MAX) {
950		errno = overflow ? EOVERFLOW : ERANGE;
951		return -1;
952	}
953	return (int)len;
954}
955
956static void
957fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
958       int precision, int flags)
959{
960	int padlen, strln;	/* Amount to pad. */
961	int noprecision = (precision == -1);
962
963	if (value == NULL)	/* We're forgiving. */
964		value = "(null)";
965
966	/* If a precision was specified, don't read the string past it. */
967	for (strln = 0; value[strln] != '\0' &&
968	    (noprecision || strln < precision); strln++)
969		continue;
970
971	if ((padlen = width - strln) < 0)
972		padlen = 0;
973	if (flags & PRINT_F_MINUS)	/* Left justify. */
974		padlen = -padlen;
975
976	while (padlen > 0) {	/* Leading spaces. */
977		OUTCHAR(str, *len, size, ' ');
978		padlen--;
979	}
980	while (*value != '\0' && (noprecision || precision-- > 0)) {
981		OUTCHAR(str, *len, size, *value);
982		value++;
983	}
984	while (padlen < 0) {	/* Trailing spaces. */
985		OUTCHAR(str, *len, size, ' ');
986		padlen++;
987	}
988}
989
990static void
991fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
992       int precision, int flags)
993{
994	UINTMAX_T uvalue;
995	char iconvert[MAX_CONVERT_LENGTH];
996	char sign = 0;
997	char hexprefix = 0;
998	int spadlen = 0;	/* Amount to space pad. */
999	int zpadlen = 0;	/* Amount to zero pad. */
1000	int pos;
1001	int separators = (flags & PRINT_F_QUOTE);
1002	int noprecision = (precision == -1);
1003
1004	if (flags & PRINT_F_UNSIGNED)
1005		uvalue = value;
1006	else {
1007		uvalue = (value >= 0) ? value : -value;
1008		if (value < 0)
1009			sign = '-';
1010		else if (flags & PRINT_F_PLUS)	/* Do a sign. */
1011			sign = '+';
1012		else if (flags & PRINT_F_SPACE)
1013			sign = ' ';
1014	}
1015
1016	pos = convert(uvalue, iconvert, sizeof(iconvert), base,
1017	    flags & PRINT_F_UP);
1018
1019	if (flags & PRINT_F_NUM && uvalue != 0) {
1020		/*
1021		 * C99 says: "The result is converted to an `alternative form'.
1022		 * For `o' conversion, it increases the precision, if and only
1023		 * if necessary, to force the first digit of the result to be a
1024		 * zero (if the value and precision are both 0, a single 0 is
1025		 * printed).  For `x' (or `X') conversion, a nonzero result has
1026		 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
1027		 */
1028		switch (base) {
1029		case 8:
1030			if (precision <= pos)
1031				precision = pos + 1;
1032			break;
1033		case 16:
1034			hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
1035			break;
1036		}
1037	}
1038
1039	if (separators)	/* Get the number of group separators we'll print. */
1040		separators = getnumsep(pos);
1041
1042	zpadlen = precision - pos - separators;
1043	spadlen = width                         /* Minimum field width. */
1044	    - separators                        /* Number of separators. */
1045	    - MAX(precision, pos)               /* Number of integer digits. */
1046	    - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
1047	    - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
1048
1049	if (zpadlen < 0)
1050		zpadlen = 0;
1051	if (spadlen < 0)
1052		spadlen = 0;
1053
1054	/*
1055	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1056	 * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
1057	 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
1058	 */
1059	if (flags & PRINT_F_MINUS)	/* Left justify. */
1060		spadlen = -spadlen;
1061	else if (flags & PRINT_F_ZERO && noprecision) {
1062		zpadlen += spadlen;
1063		spadlen = 0;
1064	}
1065	while (spadlen > 0) {	/* Leading spaces. */
1066		OUTCHAR(str, *len, size, ' ');
1067		spadlen--;
1068	}
1069	if (sign != 0)	/* Sign. */
1070		OUTCHAR(str, *len, size, sign);
1071	if (hexprefix != 0) {	/* A "0x" or "0X" prefix. */
1072		OUTCHAR(str, *len, size, '0');
1073		OUTCHAR(str, *len, size, hexprefix);
1074	}
1075	while (zpadlen > 0) {	/* Leading zeros. */
1076		OUTCHAR(str, *len, size, '0');
1077		zpadlen--;
1078	}
1079	while (pos > 0) {	/* The actual digits. */
1080		pos--;
1081		OUTCHAR(str, *len, size, iconvert[pos]);
1082		if (separators > 0 && pos > 0 && pos % 3 == 0)
1083			printsep(str, len, size);
1084	}
1085	while (spadlen < 0) {	/* Trailing spaces. */
1086		OUTCHAR(str, *len, size, ' ');
1087		spadlen++;
1088	}
1089}
1090
1091static void
1092fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
1093       int precision, int flags, int *overflow)
1094{
1095	LDOUBLE ufvalue;
1096	UINTMAX_T intpart;
1097	UINTMAX_T fracpart;
1098	UINTMAX_T mask;
1099	const char *infnan = NULL;
1100	char iconvert[MAX_CONVERT_LENGTH];
1101	char fconvert[MAX_CONVERT_LENGTH];
1102	char econvert[4];	/* "e-12" (without nul-termination). */
1103	char esign = 0;
1104	char sign = 0;
1105	int leadfraczeros = 0;
1106	int exponent = 0;
1107	int emitpoint = 0;
1108	int omitzeros = 0;
1109	int omitcount = 0;
1110	int padlen = 0;
1111	int epos = 0;
1112	int fpos = 0;
1113	int ipos = 0;
1114	int separators = (flags & PRINT_F_QUOTE);
1115	int estyle = (flags & PRINT_F_TYPE_E);
1116#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
1117	struct lconv *lc = localeconv();
1118#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
1119
1120	/*
1121	 * AIX' man page says the default is 0, but C99 and at least Solaris'
1122	 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
1123	 * defaults to 6.
1124	 */
1125	if (precision == -1)
1126		precision = 6;
1127
1128	if (fvalue < 0.0)
1129		sign = '-';
1130	else if (flags & PRINT_F_PLUS)	/* Do a sign. */
1131		sign = '+';
1132	else if (flags & PRINT_F_SPACE)
1133		sign = ' ';
1134
1135	if (ISNAN(fvalue))
1136		infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
1137	else if (ISINF(fvalue))
1138		infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
1139
1140	if (infnan != NULL) {
1141		if (sign != 0)
1142			iconvert[ipos++] = sign;
1143		while (*infnan != '\0')
1144			iconvert[ipos++] = *infnan++;
1145		fmtstr(str, len, size, iconvert, width, ipos, flags);
1146		return;
1147	}
1148
1149	/* "%e" (or "%E") or "%g" (or "%G") conversion. */
1150	if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
1151		if (flags & PRINT_F_TYPE_G) {
1152			/*
1153			 * For "%g" (and "%G") conversions, the precision
1154			 * specifies the number of significant digits, which
1155			 * includes the digits in the integer part.  The
1156			 * conversion will or will not be using "e-style" (like
1157			 * "%e" or "%E" conversions) depending on the precision
1158			 * and on the exponent.  However, the exponent can be
1159			 * affected by rounding the converted value, so we'll
1160			 * leave this decision for later.  Until then, we'll
1161			 * assume that we're going to do an "e-style" conversion
1162			 * (in order to get the exponent calculated).  For
1163			 * "e-style", the precision must be decremented by one.
1164			 */
1165			precision--;
1166			/*
1167			 * For "%g" (and "%G") conversions, trailing zeros are
1168			 * removed from the fractional portion of the result
1169			 * unless the "#" flag was specified.
1170			 */
1171			if (!(flags & PRINT_F_NUM))
1172				omitzeros = 1;
1173		}
1174		exponent = getexponent(fvalue);
1175		estyle = 1;
1176	}
1177
1178again:
1179	/*
1180	 * Sorry, we only support 9, 19, or 38 digits (that is, the number of
1181	 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
1182	 * minus one) past the decimal point due to our conversion method.
1183	 */
1184	switch (sizeof(UINTMAX_T)) {
1185	case 16:
1186		if (precision > 38)
1187			precision = 38;
1188		break;
1189	case 8:
1190		if (precision > 19)
1191			precision = 19;
1192		break;
1193	default:
1194		if (precision > 9)
1195			precision = 9;
1196		break;
1197	}
1198
1199	ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
1200	if (estyle)	/* We want exactly one integer digit. */
1201		ufvalue /= mypow10(exponent);
1202
1203	if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
1204		*overflow = 1;
1205		return;
1206	}
1207
1208	/*
1209	 * Factor of ten with the number of digits needed for the fractional
1210	 * part.  For example, if the precision is 3, the mask will be 1000.
1211	 */
1212	mask = (UINTMAX_T)mypow10(precision);
1213	/*
1214	 * We "cheat" by converting the fractional part to integer by
1215	 * multiplying by a factor of ten.
1216	 */
1217	if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
1218		/*
1219		 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
1220		 * (because precision = 3).  Now, myround(1000 * 0.99962) will
1221		 * return 1000.  So, the integer part must be incremented by one
1222		 * and the fractional part must be set to zero.
1223		 */
1224		intpart++;
1225		fracpart = 0;
1226		if (estyle && intpart == 10) {
1227			/*
1228			 * The value was rounded up to ten, but we only want one
1229			 * integer digit if using "e-style".  So, the integer
1230			 * part must be set to one and the exponent must be
1231			 * incremented by one.
1232			 */
1233			intpart = 1;
1234			exponent++;
1235		}
1236	}
1237
1238	/*
1239	 * Now that we know the real exponent, we can check whether or not to
1240	 * use "e-style" for "%g" (and "%G") conversions.  If we don't need
1241	 * "e-style", the precision must be adjusted and the integer and
1242	 * fractional parts must be recalculated from the original value.
1243	 *
1244	 * C99 says: "Let P equal the precision if nonzero, 6 if the precision
1245	 * is omitted, or 1 if the precision is zero.  Then, if a conversion
1246	 * with style `E' would have an exponent of X:
1247	 *
1248	 * - if P > X >= -4, the conversion is with style `f' (or `F') and
1249	 *   precision P - (X + 1).
1250	 *
1251	 * - otherwise, the conversion is with style `e' (or `E') and precision
1252	 *   P - 1." (7.19.6.1, 8)
1253	 *
1254	 * Note that we had decremented the precision by one.
1255	 */
1256	if (flags & PRINT_F_TYPE_G && estyle &&
1257	    precision + 1 > exponent && exponent >= -4) {
1258		precision -= exponent;
1259		estyle = 0;
1260		goto again;
1261	}
1262
1263	if (estyle) {
1264		if (exponent < 0) {
1265			exponent = -exponent;
1266			esign = '-';
1267		} else
1268			esign = '+';
1269
1270		/*
1271		 * Convert the exponent.  The sizeof(econvert) is 4.  So, the
1272		 * econvert buffer can hold e.g. "e+99" and "e-99".  We don't
1273		 * support an exponent which contains more than two digits.
1274		 * Therefore, the following stores are safe.
1275		 */
1276		epos = convert(exponent, econvert, 2, 10, 0);
1277		/*
1278		 * C99 says: "The exponent always contains at least two digits,
1279		 * and only as many more digits as necessary to represent the
1280		 * exponent." (7.19.6.1, 8)
1281		 */
1282		if (epos == 1)
1283			econvert[epos++] = '0';
1284		econvert[epos++] = esign;
1285		econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
1286	}
1287
1288	/* Convert the integer part and the fractional part. */
1289	ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
1290	if (fracpart != 0)	/* convert() would return 1 if fracpart == 0. */
1291		fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
1292
1293	leadfraczeros = precision - fpos;
1294
1295	if (omitzeros) {
1296		if (fpos > 0)	/* Omit trailing fractional part zeros. */
1297			while (omitcount < fpos && fconvert[omitcount] == '0')
1298				omitcount++;
1299		else {	/* The fractional part is zero, omit it completely. */
1300			omitcount = precision;
1301			leadfraczeros = 0;
1302		}
1303		precision -= omitcount;
1304	}
1305
1306	/*
1307	 * Print a decimal point if either the fractional part is non-zero
1308	 * and/or the "#" flag was specified.
1309	 */
1310	if (precision > 0 || flags & PRINT_F_NUM)
1311		emitpoint = 1;
1312	if (separators)	/* Get the number of group separators we'll print. */
1313		separators = getnumsep(ipos);
1314
1315	padlen = width                  /* Minimum field width. */
1316	    - ipos                      /* Number of integer digits. */
1317	    - epos                      /* Number of exponent characters. */
1318	    - precision                 /* Number of fractional digits. */
1319	    - separators                /* Number of group separators. */
1320	    - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
1321	    - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
1322
1323	if (padlen < 0)
1324		padlen = 0;
1325
1326	/*
1327	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
1328	 * ignored." (7.19.6.1, 6)
1329	 */
1330	if (flags & PRINT_F_MINUS)	/* Left justifty. */
1331		padlen = -padlen;
1332	else if (flags & PRINT_F_ZERO && padlen > 0) {
1333		if (sign != 0) {	/* Sign. */
1334			OUTCHAR(str, *len, size, sign);
1335			sign = 0;
1336		}
1337		while (padlen > 0) {	/* Leading zeros. */
1338			OUTCHAR(str, *len, size, '0');
1339			padlen--;
1340		}
1341	}
1342	while (padlen > 0) {	/* Leading spaces. */
1343		OUTCHAR(str, *len, size, ' ');
1344		padlen--;
1345	}
1346	if (sign != 0)	/* Sign. */
1347		OUTCHAR(str, *len, size, sign);
1348	while (ipos > 0) {	/* Integer part. */
1349		ipos--;
1350		OUTCHAR(str, *len, size, iconvert[ipos]);
1351		if (separators > 0 && ipos > 0 && ipos % 3 == 0)
1352			printsep(str, len, size);
1353	}
1354	if (emitpoint) {	/* Decimal point. */
1355#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
1356		if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
1357			OUTCHAR(str, *len, size, *lc->decimal_point);
1358		else	/* We'll always print some decimal point character. */
1359#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
1360			OUTCHAR(str, *len, size, '.');
1361	}
1362	while (leadfraczeros > 0) {	/* Leading fractional part zeros. */
1363		OUTCHAR(str, *len, size, '0');
1364		leadfraczeros--;
1365	}
1366	while (fpos > omitcount) {	/* The remaining fractional part. */
1367		fpos--;
1368		OUTCHAR(str, *len, size, fconvert[fpos]);
1369	}
1370	while (epos > 0) {	/* Exponent. */
1371		epos--;
1372		OUTCHAR(str, *len, size, econvert[epos]);
1373	}
1374	while (padlen < 0) {	/* Trailing spaces. */
1375		OUTCHAR(str, *len, size, ' ');
1376		padlen++;
1377	}
1378}
1379
1380static void
1381printsep(char *str, size_t *len, size_t size)
1382{
1383#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
1384	struct lconv *lc = localeconv();
1385	int i;
1386
1387	if (lc->thousands_sep != NULL)
1388		for (i = 0; lc->thousands_sep[i] != '\0'; i++)
1389			OUTCHAR(str, *len, size, lc->thousands_sep[i]);
1390	else
1391#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
1392		OUTCHAR(str, *len, size, ',');
1393}
1394
1395static int
1396getnumsep(int digits)
1397{
1398	int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
1399#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
1400	int strln;
1401	struct lconv *lc = localeconv();
1402
1403	/* We support an arbitrary separator length (including zero). */
1404	if (lc->thousands_sep != NULL) {
1405		for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
1406			continue;
1407		separators *= strln;
1408	}
1409#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
1410	return separators;
1411}
1412
1413static int
1414getexponent(LDOUBLE value)
1415{
1416	LDOUBLE tmp = (value >= 0.0) ? value : -value;
1417	int exponent = 0;
1418
1419	/*
1420	 * We check for 99 > exponent > -99 in order to work around possible
1421	 * endless loops which could happen (at least) in the second loop (at
1422	 * least) if we're called with an infinite value.  However, we checked
1423	 * for infinity before calling this function using our ISINF() macro, so
1424	 * this might be somewhat paranoid.
1425	 */
1426	while (tmp < 1.0 && tmp > 0.0 && --exponent > -99)
1427		tmp *= 10;
1428	while (tmp >= 10.0 && ++exponent < 99)
1429		tmp /= 10;
1430
1431	return exponent;
1432}
1433
1434static int
1435convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
1436{
1437	const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
1438	size_t pos = 0;
1439
1440	/* We return an unterminated buffer with the digits in reverse order. */
1441	do {
1442		buf[pos++] = digits[value % base];
1443		value /= base;
1444	} while (value != 0 && pos < size);
1445
1446	return (int)pos;
1447}
1448
1449static UINTMAX_T
1450cast(LDOUBLE value)
1451{
1452	UINTMAX_T result;
1453
1454	/*
1455	 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
1456	 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
1457	 * it may be increased to the nearest higher representable value for the
1458	 * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
1459	 * value although converting the latter to UINTMAX_T would overflow.
1460	 */
1461	if (value >= UINTMAX_MAX)
1462		return UINTMAX_MAX;
1463
1464	result = (UINTMAX_T)value;
1465	/*
1466	 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
1467	 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
1468	 * the standard).  Sigh.
1469	 */
1470	return (result <= value) ? result : result - 1;
1471}
1472
1473static UINTMAX_T
1474myround(LDOUBLE value)
1475{
1476	UINTMAX_T intpart = cast(value);
1477
1478	return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
1479}
1480
1481static LDOUBLE
1482mypow10(int exponent)
1483{
1484	LDOUBLE result = 1;
1485
1486	while (exponent > 0) {
1487		result *= 10;
1488		exponent--;
1489	}
1490	while (exponent < 0) {
1491		result /= 10;
1492		exponent++;
1493	}
1494	return result;
1495}
1496#endif	/* HW_WANT_RPL_VSNPRINTF */
1497
1498#if HW_WANT_RPL_VASPRINTF
1499#if NEED_MYMEMCPY
1500void *
1501mymemcpy(void *dst, void *src, size_t len)
1502{
1503	const char *from = src;
1504	char *to = dst;
1505
1506	/* No need for optimization, we use this only to replace va_copy(3). */
1507	while (len-- > 0)
1508		*to++ = *from++;
1509	return dst;
1510}
1511#endif	/* NEED_MYMEMCPY */
1512
1513int
1514rpl_vasprintf(char **ret, const char *format, va_list ap);
1515
1516int
1517rpl_vasprintf(char **ret, const char *format, va_list ap)
1518{
1519	size_t size;
1520	int len;
1521	va_list aq;
1522
1523	VA_COPY(aq, ap);
1524	len = vsnprintf(NULL, 0, format, aq);
1525	VA_END_COPY(aq);
1526	if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
1527		return -1;
1528	return vsnprintf(*ret, size, format, ap);
1529}
1530#endif	/* HW_WANT_RPL_VASPRINTF */
1531
1532#if HW_WANT_RPL_SNPRINTF
1533#if HAVE_STDARG_H
1534int
1535rpl_snprintf(char *str, size_t size, const char *format, ...);
1536
1537int
1538rpl_snprintf(char *str, size_t size, const char *format, ...)
1539#else
1540int
1541rpl_snprintf(va_alist) va_dcl
1542#endif	/* HAVE_STDARG_H */
1543{
1544#if !HAVE_STDARG_H
1545	char *str;
1546	size_t size;
1547	char *format;
1548#endif	/* HAVE_STDARG_H */
1549	va_list ap;
1550	int len;
1551
1552	VA_START(ap, format);
1553	VA_SHIFT(ap, str, char *);
1554	VA_SHIFT(ap, size, size_t);
1555	VA_SHIFT(ap, format, const char *);
1556	len = vsnprintf(str, size, format, ap);
1557	va_end(ap);
1558	return len;
1559}
1560#endif	/* HW_WANT_RPL_SNPRINTF */
1561
1562#if HW_WANT_RPL_ASPRINTF
1563#if HAVE_STDARG_H
1564int
1565rpl_asprintf(char **ret, const char *format, ...);
1566
1567int
1568rpl_asprintf(char **ret, const char *format, ...)
1569#else
1570int
1571rpl_asprintf(va_alist) va_dcl
1572#endif	/* HAVE_STDARG_H */
1573{
1574#if !HAVE_STDARG_H
1575	char **ret;
1576	char *format;
1577#endif	/* HAVE_STDARG_H */
1578	va_list ap;
1579	int len;
1580
1581	VA_START(ap, format);
1582	VA_SHIFT(ap, ret, char **);
1583	VA_SHIFT(ap, format, const char *);
1584	len = vasprintf(ret, format, ap);
1585	va_end(ap);
1586	return len;
1587}
1588#endif	/* HW_WANT_RPL_ASPRINTF */
1589#else	/* Dummy declaration to avoid empty translation unit warnings. */
1590int main(void);
1591#endif	/* HW_WANT_RPL_SNPRINTF || HW_WANT_RPL_VSNPRINTF || HW_WANT_RPL_ASPRINTF || [...] */
1592
1593#if TEST_SNPRINTF
1594int
1595main(void)
1596{
1597	const char *float_fmt[] = {
1598		/* "%E" and "%e" formats. */
1599#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
1600		"%.16e",
1601		"%22.16e",
1602		"%022.16e",
1603		"%-22.16e",
1604		"%#+'022.16e",
1605#endif	/* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
1606		"foo|%#+0123.9E|bar",
1607		"%-123.9e",
1608		"%123.9e",
1609		"%+23.9e",
1610		"%+05.8e",
1611		"%-05.8e",
1612		"%05.8e",
1613		"%+5.8e",
1614		"%-5.8e",
1615		"% 5.8e",
1616		"%5.8e",
1617		"%+4.9e",
1618#if !OS_LINUX	/* glibc sometimes gets these wrong. */
1619		"%+#010.0e",
1620		"%#10.1e",
1621		"%10.5e",
1622		"% 10.5e",
1623		"%5.0e",
1624		"%5.e",
1625		"%#5.0e",
1626		"%#5.e",
1627		"%3.2e",
1628		"%3.1e",
1629		"%-1.5e",
1630		"%1.5e",
1631		"%01.3e",
1632		"%1.e",
1633		"%.1e",
1634		"%#.0e",
1635		"%+.0e",
1636		"% .0e",
1637		"%.0e",
1638		"%#.e",
1639		"%+.e",
1640		"% .e",
1641		"%.e",
1642		"%4e",
1643		"%e",
1644		"%E",
1645#endif	/* !OS_LINUX */
1646		/* "%F" and "%f" formats. */
1647#if !OS_BSD && !OS_IRIX
1648		"% '022f",
1649		"%+'022f",
1650		"%-'22f",
1651		"%'22f",
1652#if HAVE_LONG_LONG_INT
1653		"%.16f",
1654		"%22.16f",
1655		"%022.16f",
1656		"%-22.16f",
1657		"%#+'022.16f",
1658#endif	/* HAVE_LONG_LONG_INT */
1659#endif	/* !OS_BSD && !OS_IRIX */
1660		"foo|%#+0123.9F|bar",
1661		"%-123.9f",
1662		"%123.9f",
1663		"%+23.9f",
1664		"%+#010.0f",
1665		"%#10.1f",
1666		"%10.5f",
1667		"% 10.5f",
1668		"%+05.8f",
1669		"%-05.8f",
1670		"%05.8f",
1671		"%+5.8f",
1672		"%-5.8f",
1673		"% 5.8f",
1674		"%5.8f",
1675		"%5.0f",
1676		"%5.f",
1677		"%#5.0f",
1678		"%#5.f",
1679		"%+4.9f",
1680		"%3.2f",
1681		"%3.1f",
1682		"%-1.5f",
1683		"%1.5f",
1684		"%01.3f",
1685		"%1.f",
1686		"%.1f",
1687		"%#.0f",
1688		"%+.0f",
1689		"% .0f",
1690		"%.0f",
1691		"%#.f",
1692		"%+.f",
1693		"% .f",
1694		"%.f",
1695		"%4f",
1696		"%f",
1697		"%F",
1698		/* "%G" and "%g" formats. */
1699#if !OS_BSD && !OS_IRIX && !OS_LINUX
1700		"% '022g",
1701		"%+'022g",
1702		"%-'22g",
1703		"%'22g",
1704#if HAVE_LONG_LONG_INT
1705		"%.16g",
1706		"%22.16g",
1707		"%022.16g",
1708		"%-22.16g",
1709		"%#+'022.16g",
1710#endif	/* HAVE_LONG_LONG_INT */
1711#endif	/* !OS_BSD && !OS_IRIX && !OS_LINUX */
1712		"foo|%#+0123.9G|bar",
1713		"%-123.9g",
1714		"%123.9g",
1715		"%+23.9g",
1716		"%+05.8g",
1717		"%-05.8g",
1718		"%05.8g",
1719		"%+5.8g",
1720		"%-5.8g",
1721		"% 5.8g",
1722		"%5.8g",
1723		"%+4.9g",
1724#if !OS_LINUX	/* glibc sometimes gets these wrong. */
1725		"%+#010.0g",
1726		"%#10.1g",
1727		"%10.5g",
1728		"% 10.5g",
1729		"%5.0g",
1730		"%5.g",
1731		"%#5.0g",
1732		"%#5.g",
1733		"%3.2g",
1734		"%3.1g",
1735		"%-1.5g",
1736		"%1.5g",
1737		"%01.3g",
1738		"%1.g",
1739		"%.1g",
1740		"%#.0g",
1741		"%+.0g",
1742		"% .0g",
1743		"%.0g",
1744		"%#.g",
1745		"%+.g",
1746		"% .g",
1747		"%.g",
1748		"%4g",
1749		"%g",
1750		"%G",
1751#endif	/* !OS_LINUX */
1752		NULL
1753	};
1754	double float_val[] = {
1755		-4.136,
1756		-134.52,
1757		-5.04030201,
1758		-3410.01234,
1759		-999999.999999,
1760		-913450.29876,
1761		-913450.2,
1762		-91345.2,
1763		-9134.2,
1764		-913.2,
1765		-91.2,
1766		-9.2,
1767		-9.9,
1768		4.136,
1769		134.52,
1770		5.04030201,
1771		3410.01234,
1772		999999.999999,
1773		913450.29876,
1774		913450.2,
1775		91345.2,
1776		9134.2,
1777		913.2,
1778		91.2,
1779		9.2,
1780		9.9,
1781		9.96,
1782		9.996,
1783		9.9996,
1784		9.99996,
1785		9.999996,
1786		9.9999996,
1787		9.99999996,
1788		0.99999996,
1789		0.99999999,
1790		0.09999999,
1791		0.00999999,
1792		0.00099999,
1793		0.00009999,
1794		0.00000999,
1795		0.00000099,
1796		0.00000009,
1797		0.00000001,
1798		0.0000001,
1799		0.000001,
1800		0.00001,
1801		0.0001,
1802		0.001,
1803		0.01,
1804		0.1,
1805		1.0,
1806		1.5,
1807		-1.5,
1808		-1.0,
1809		-0.1,
1810#if !OS_BSD	/* BSD sometimes gets these wrong. */
1811#ifdef INFINITY
1812		INFINITY,
1813		-INFINITY,
1814#endif	/* defined(INFINITY) */
1815#ifdef NAN
1816		NAN,
1817#endif	/* defined(NAN) */
1818#endif	/* !OS_BSD */
1819		0
1820	};
1821	const char *long_fmt[] = {
1822		"foo|%0123ld|bar",
1823#if !OS_IRIX
1824		"% '0123ld",
1825		"%+'0123ld",
1826		"%-'123ld",
1827		"%'123ld",
1828#endif	/* !OS_IRiX */
1829		"%123.9ld",
1830		"% 123.9ld",
1831		"%+123.9ld",
1832		"%-123.9ld",
1833		"%0123ld",
1834		"% 0123ld",
1835		"%+0123ld",
1836		"%-0123ld",
1837		"%10.5ld",
1838		"% 10.5ld",
1839		"%+10.5ld",
1840		"%-10.5ld",
1841		"%010ld",
1842		"% 010ld",
1843		"%+010ld",
1844		"%-010ld",
1845		"%4.2ld",
1846		"% 4.2ld",
1847		"%+4.2ld",
1848		"%-4.2ld",
1849		"%04ld",
1850		"% 04ld",
1851		"%+04ld",
1852		"%-04ld",
1853		"%5.5ld",
1854		"%+22.33ld",
1855		"%01.3ld",
1856		"%1.5ld",
1857		"%-1.5ld",
1858		"%44ld",
1859		"%4ld",
1860		"%4.0ld",
1861		"%4.ld",
1862		"%.44ld",
1863		"%.4ld",
1864		"%.0ld",
1865		"%.ld",
1866		"%ld",
1867		NULL
1868	};
1869	long int long_val[] = {
1870#ifdef LONG_MAX
1871		LONG_MAX,
1872#endif	/* LONG_MAX */
1873#ifdef LONG_MIN
1874		LONG_MIN,
1875#endif	/* LONG_MIN */
1876		-91340,
1877		91340,
1878		341,
1879		134,
1880		0203,
1881		-1,
1882		1,
1883		0
1884	};
1885	const char *ulong_fmt[] = {
1886		/* "%u" formats. */
1887		"foo|%0123lu|bar",
1888#if !OS_IRIX
1889		"% '0123lu",
1890		"%+'0123lu",
1891		"%-'123lu",
1892		"%'123lu",
1893#endif	/* !OS_IRiX */
1894		"%123.9lu",
1895		"% 123.9lu",
1896		"%+123.9lu",
1897		"%-123.9lu",
1898		"%0123lu",
1899		"% 0123lu",
1900		"%+0123lu",
1901		"%-0123lu",
1902		"%5.5lu",
1903		"%+22.33lu",
1904		"%01.3lu",
1905		"%1.5lu",
1906		"%-1.5lu",
1907		"%44lu",
1908		"%lu",
1909		/* "%o" formats. */
1910		"foo|%#0123lo|bar",
1911		"%#123.9lo",
1912		"%# 123.9lo",
1913		"%#+123.9lo",
1914		"%#-123.9lo",
1915		"%#0123lo",
1916		"%# 0123lo",
1917		"%#+0123lo",
1918		"%#-0123lo",
1919		"%#5.5lo",
1920		"%#+22.33lo",
1921		"%#01.3lo",
1922		"%#1.5lo",
1923		"%#-1.5lo",
1924		"%#44lo",
1925		"%#lo",
1926		"%123.9lo",
1927		"% 123.9lo",
1928		"%+123.9lo",
1929		"%-123.9lo",
1930		"%0123lo",
1931		"% 0123lo",
1932		"%+0123lo",
1933		"%-0123lo",
1934		"%5.5lo",
1935		"%+22.33lo",
1936		"%01.3lo",
1937		"%1.5lo",
1938		"%-1.5lo",
1939		"%44lo",
1940		"%lo",
1941		/* "%X" and "%x" formats. */
1942		"foo|%#0123lX|bar",
1943		"%#123.9lx",
1944		"%# 123.9lx",
1945		"%#+123.9lx",
1946		"%#-123.9lx",
1947		"%#0123lx",
1948		"%# 0123lx",
1949		"%#+0123lx",
1950		"%#-0123lx",
1951		"%#5.5lx",
1952		"%#+22.33lx",
1953		"%#01.3lx",
1954		"%#1.5lx",
1955		"%#-1.5lx",
1956		"%#44lx",
1957		"%#lx",
1958		"%#lX",
1959		"%123.9lx",
1960		"% 123.9lx",
1961		"%+123.9lx",
1962		"%-123.9lx",
1963		"%0123lx",
1964		"% 0123lx",
1965		"%+0123lx",
1966		"%-0123lx",
1967		"%5.5lx",
1968		"%+22.33lx",
1969		"%01.3lx",
1970		"%1.5lx",
1971		"%-1.5lx",
1972		"%44lx",
1973		"%lx",
1974		"%lX",
1975		NULL
1976	};
1977	unsigned long int ulong_val[] = {
1978#ifdef ULONG_MAX
1979		ULONG_MAX,
1980#endif	/* ULONG_MAX */
1981		91340,
1982		341,
1983		134,
1984		0203,
1985		1,
1986		0
1987	};
1988	const char *llong_fmt[] = {
1989		"foo|%0123lld|bar",
1990		"%123.9lld",
1991		"% 123.9lld",
1992		"%+123.9lld",
1993		"%-123.9lld",
1994		"%0123lld",
1995		"% 0123lld",
1996		"%+0123lld",
1997		"%-0123lld",
1998		"%5.5lld",
1999		"%+22.33lld",
2000		"%01.3lld",
2001		"%1.5lld",
2002		"%-1.5lld",
2003		"%44lld",
2004		"%lld",
2005		NULL
2006	};
2007	LLONG llong_val[] = {
2008#ifdef LLONG_MAX
2009		LLONG_MAX,
2010#endif	/* LLONG_MAX */
2011#ifdef LLONG_MIN
2012		LLONG_MIN,
2013#endif	/* LLONG_MIN */
2014		-91340,
2015		91340,
2016		341,
2017		134,
2018		0203,
2019		-1,
2020		1,
2021		0
2022	};
2023	const char *string_fmt[] = {
2024		"foo|%10.10s|bar",
2025		"%-10.10s",
2026		"%10.10s",
2027		"%10.5s",
2028		"%5.10s",
2029		"%10.1s",
2030		"%1.10s",
2031		"%10.0s",
2032		"%0.10s",
2033		"%-42.5s",
2034		"%2.s",
2035		"%.10s",
2036		"%.1s",
2037		"%.0s",
2038		"%.s",
2039		"%4s",
2040		"%s",
2041		NULL
2042	};
2043	const char *string_val[] = {
2044		"Hello",
2045		"Hello, world!",
2046		"Sound check: One, two, three.",
2047		"This string is a little longer than the other strings.",
2048		"1",
2049		"",
2050		NULL
2051	};
2052#if !OS_SYSV	/* SysV uses a different format than we do. */
2053	const char *pointer_fmt[] = {
2054		"foo|%p|bar",
2055		"%42p",
2056		"%p",
2057		NULL
2058	};
2059	const char *pointer_val[] = {
2060		*pointer_fmt,
2061		*string_fmt,
2062		*string_val,
2063		NULL
2064	};
2065#endif	/* !OS_SYSV */
2066	char buf1[1024], buf2[1024];
2067	double value, digits = 9.123456789012345678901234567890123456789;
2068	int i, j, r1, r2, failed = 0, num = 0;
2069
2070/*
2071 * Use -DTEST_NILS in order to also test the conversion of nil values.  Might
2072 * segfault on systems which don't support converting a NULL pointer with "%s"
2073 * and lets some test cases fail against BSD and glibc due to bugs in their
2074 * implementations.
2075 */
2076#ifndef TEST_NILS
2077#define TEST_NILS 0
2078#elif TEST_NILS
2079#undef TEST_NILS
2080#define TEST_NILS 1
2081#endif	/* !defined(TEST_NILS) */
2082#ifdef TEST
2083#undef TEST
2084#endif	/* defined(TEST) */
2085#define TEST(fmt, val)                                                         \
2086do {                                                                           \
2087	for (i = 0; fmt[i] != NULL; i++)                                       \
2088		for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) {          \
2089			r1 = sprintf(buf1, fmt[i], val[j]);                    \
2090			r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]);     \
2091			if (strcmp(buf1, buf2) != 0 || r1 != r2) {             \
2092				(void)printf("Results don't match, "           \
2093				    "format string: %s\n"                      \
2094				    "\t sprintf(3): [%s] (%d)\n"               \
2095				    "\tsnprintf(3): [%s] (%d)\n",              \
2096				    fmt[i], buf1, r1, buf2, r2);               \
2097				failed++;                                      \
2098			}                                                      \
2099			num++;                                                 \
2100		}                                                              \
2101} while (/* CONSTCOND */ 0)
2102
2103#if HAVE_LOCALE_H
2104	(void)setlocale(LC_ALL, "");
2105#endif	/* HAVE_LOCALE_H */
2106
2107	(void)puts("Testing our snprintf(3) against your system's sprintf(3).");
2108	TEST(float_fmt, float_val);
2109	TEST(long_fmt, long_val);
2110	TEST(ulong_fmt, ulong_val);
2111	TEST(llong_fmt, llong_val);
2112	TEST(string_fmt, string_val);
2113#if !OS_SYSV	/* SysV uses a different format than we do. */
2114	TEST(pointer_fmt, pointer_val);
2115#endif	/* !OS_SYSV */
2116	(void)printf("Result: %d out of %d tests failed.\n", failed, num);
2117
2118	(void)fputs("Checking how many digits we support: ", stdout);
2119	for (i = 0; i < 100; i++) {
2120		value = pow(10, i) * digits;
2121		(void)sprintf(buf1, "%.1f", value);
2122		(void)snprintf(buf2, sizeof(buf2), "%.1f", value);
2123		if (strcmp(buf1, buf2) != 0) {
2124			(void)printf("apparently %d.\n", i);
2125			break;
2126		}
2127	}
2128	return (failed == 0) ? 0 : 1;
2129}
2130#endif	/* TEST_SNPRINTF */
2131
2132/* vim: set joinspaces textwidth=80: */
2133