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