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