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