1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110 |
- /*
- * Copyright (c) 1995 Patrick Powell.
- *
- * This code is based on code written by Patrick Powell <papowell@astart.com>.
- * It may be used for any purpose as long as this notice remains intact on all
- * source code distributions.
- */
- /*
- * Copyright (c) 2008 Holger Weiss.
- *
- * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
- * My changes to the code may freely be used, modified and/or redistributed for
- * any purpose. It would be nice if additions and fixes to this file (including
- * trivial code cleanups) would be sent back in order to let me include them in
- * the version available at <http://www.jhweiss.de/software/snprintf.html>.
- * However, this is not a requirement for using or redistributing (possibly
- * modified) versions of this file, nor is leaving this notice intact mandatory.
- */
- /*
- * History
- *
- * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
- *
- * Fixed the detection of infinite floating point values on IRIX (and
- * possibly other systems) and applied another few minor cleanups.
- *
- * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
- *
- * Added a lot of new features, fixed many bugs, and incorporated various
- * improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
- * <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
- * <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
- * projects. The additions include: support the "e", "E", "g", "G", and
- * "F" conversion specifiers (and use conversion style "f" or "F" for the
- * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
- * "t", and "z" length modifiers; support the "#" flag and the (non-C99)
- * "'" flag; use localeconv(3) (if available) to get both the current
- * locale's decimal point character and the separator between groups of
- * digits; fix the handling of various corner cases of field width and
- * precision specifications; fix various floating point conversion bugs;
- * handle infinite and NaN floating point values; don't attempt to write to
- * the output buffer (which may be NULL) if a size of zero was specified;
- * check for integer overflow of the field width, precision, and return
- * values and during the floating point conversion; use the OUTCHAR() macro
- * instead of a function for better performance; provide asprintf(3) and
- * vasprintf(3) functions; add new test cases. The replacement functions
- * have been renamed to use an "rpl_" prefix, the function calls in the
- * main project (and in this file) must be redefined accordingly for each
- * replacement function which is needed (by using Autoconf or other means).
- * Various other minor improvements have been applied and the coding style
- * was cleaned up for consistency.
- *
- * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
- *
- * C99 compliant snprintf(3) and vsnprintf(3) functions return the number
- * of characters that would have been written to a sufficiently sized
- * buffer (excluding the '\0'). The original code simply returned the
- * length of the resulting output string, so that's been fixed.
- *
- * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
- *
- * The original code assumed that both snprintf(3) and vsnprintf(3) were
- * missing. Some systems only have snprintf(3) but not vsnprintf(3), so
- * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
- *
- * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
- *
- * The PGP code was using unsigned hexadecimal formats. Unfortunately,
- * unsigned formats simply didn't work.
- *
- * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
- *
- * Ok, added some minimal floating point support, which means this probably
- * requires libm on most operating systems. Don't yet support the exponent
- * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
- * wasn't being exercised in ways which showed it, so that's been fixed.
- * Also, formatted the code to Mutt conventions, and removed dead code left
- * over from the original. Also, there is now a builtin-test, run with:
- * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
- *
- * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
- *
- * This was ugly. It is still ugly. I opted out of floating point
- * numbers, but the formatter understands just about everything from the
- * normal C string format, at least as far as I can tell from the Solaris
- * 2.5 printf(3S) man page.
- */
- /*
- * ToDo
- *
- * - Add wide character support.
- * - Add support for "%a" and "%A" conversions.
- * - Create test routines which predefine the expected results. Our test cases
- * usually expose bugs in system implementations rather than in ours :-)
- */
- /*
- * Usage
- *
- * 1) The following preprocessor macros should be defined to 1 if the feature or
- * file in question is available on the target system (by using Autoconf or
- * other means), though basic functionality should be available as long as
- * HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
- *
- * HAVE_VSNPRINTF
- * HAVE_SNPRINTF
- * HAVE_VASPRINTF
- * HAVE_ASPRINTF
- * HAVE_STDARG_H
- * HAVE_STDDEF_H
- * HAVE_STDINT_H
- * HAVE_STDLIB_H
- * HAVE_FLOAT_H
- * HAVE_INTTYPES_H
- * HAVE_LOCALE_H
- * HAVE_LOCALECONV
- * HAVE_LCONV_DECIMAL_POINT
- * HAVE_LCONV_THOUSANDS_SEP
- * HAVE_LONG_DOUBLE
- * HAVE_LONG_LONG_INT
- * HAVE_UNSIGNED_LONG_LONG_INT
- * HAVE_INTMAX_T
- * HAVE_UINTMAX_T
- * HAVE_UINTPTR_T
- * HAVE_PTRDIFF_T
- * HAVE_VA_COPY
- * HAVE___VA_COPY
- *
- * 2) The calls to the functions which should be replaced must be redefined
- * throughout the project files (by using Autoconf or other means):
- *
- * #define vsnprintf rpl_vsnprintf
- * #define snprintf rpl_snprintf
- * #define vasprintf rpl_vasprintf
- * #define asprintf rpl_asprintf
- *
- * 3) The required replacement functions should be declared in some header file
- * included throughout the project files:
- *
- * #if HAVE_CONFIG_H
- * #include <config.h>
- * #endif
- * #if HAVE_STDARG_H
- * #include <stdarg.h>
- * #if !HAVE_VSNPRINTF
- * int rpl_vsnprintf(char *, size_t, const char *, va_list);
- * #endif
- * #if !HAVE_SNPRINTF
- * int rpl_snprintf(char *, size_t, const char *, ...);
- * #endif
- * #if !HAVE_VASPRINTF
- * int rpl_vasprintf(char **, const char *, va_list);
- * #endif
- * #if !HAVE_ASPRINTF
- * int rpl_asprintf(char **, const char *, ...);
- * #endif
- * #endif
- *
- * Autoconf macros for handling step 1 and step 2 are available at
- * <http://www.jhweiss.de/software/snprintf.html>.
- */
- #if HAVE_CONFIG_H
- #include "snp_config.h"
- #endif /* HAVE_CONFIG_H */
- #if TEST_SNPRINTF
- #include <math.h> /* For pow(3), NAN, and INFINITY. */
- #include <string.h> /* For strcmp(3). */
- #if defined(__NetBSD__) || \
- defined(__FreeBSD__) || \
- defined(__OpenBSD__) || \
- defined(__NeXT__) || \
- defined(__bsd__)
- #define OS_BSD 1
- #elif defined(sgi) || defined(__sgi)
- #ifndef __c99
- #define __c99 /* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
- #endif /* !defined(__c99) */
- #define OS_IRIX 1
- #define OS_SYSV 1
- #elif defined(__svr4__)
- #define OS_SYSV 1
- #elif defined(__linux__)
- #define OS_LINUX 1
- #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
- #if HAVE_CONFIG_H /* Undefine definitions possibly done in config.h. */
- #ifdef HAVE_SNPRINTF
- #undef HAVE_SNPRINTF
- #endif /* defined(HAVE_SNPRINTF) */
- #ifdef HAVE_VSNPRINTF
- #undef HAVE_VSNPRINTF
- #endif /* defined(HAVE_VSNPRINTF) */
- #ifdef HAVE_ASPRINTF
- #undef HAVE_ASPRINTF
- #endif /* defined(HAVE_ASPRINTF) */
- #ifdef HAVE_VASPRINTF
- #undef HAVE_VASPRINTF
- #endif /* defined(HAVE_VASPRINTF) */
- #ifdef snprintf
- #undef snprintf
- #endif /* defined(snprintf) */
- #ifdef vsnprintf
- #undef vsnprintf
- #endif /* defined(vsnprintf) */
- #ifdef asprintf
- #undef asprintf
- #endif /* defined(asprintf) */
- #ifdef vasprintf
- #undef vasprintf
- #endif /* defined(vasprintf) */
- #else /* By default, we assume a modern system for testing. */
- #ifndef HAVE_STDARG_H
- #define HAVE_STDARG_H 1
- #endif /* HAVE_STDARG_H */
- #ifndef HAVE_STDDEF_H
- #define HAVE_STDDEF_H 1
- #endif /* HAVE_STDDEF_H */
- #ifndef HAVE_STDINT_H
- #define HAVE_STDINT_H 1
- #endif /* HAVE_STDINT_H */
- #ifndef HAVE_STDLIB_H
- #define HAVE_STDLIB_H 1
- #endif /* HAVE_STDLIB_H */
- #ifndef HAVE_FLOAT_H
- #define HAVE_FLOAT_H 1
- #endif /* HAVE_FLOAT_H */
- #ifndef HAVE_INTTYPES_H
- #define HAVE_INTTYPES_H 1
- #endif /* HAVE_INTTYPES_H */
- #ifndef HAVE_LOCALE_H
- #define HAVE_LOCALE_H 1
- #endif /* HAVE_LOCALE_H */
- #ifndef HAVE_LOCALECONV
- #define HAVE_LOCALECONV 1
- #endif /* !defined(HAVE_LOCALECONV) */
- #ifndef HAVE_LCONV_DECIMAL_POINT
- #define HAVE_LCONV_DECIMAL_POINT 1
- #endif /* HAVE_LCONV_DECIMAL_POINT */
- #ifndef HAVE_LCONV_THOUSANDS_SEP
- #define HAVE_LCONV_THOUSANDS_SEP 1
- #endif /* HAVE_LCONV_THOUSANDS_SEP */
- #ifndef HAVE_LONG_DOUBLE
- #define HAVE_LONG_DOUBLE 1
- #endif /* !defined(HAVE_LONG_DOUBLE) */
- #ifndef HAVE_LONG_LONG_INT
- #define HAVE_LONG_LONG_INT 1
- #endif /* !defined(HAVE_LONG_LONG_INT) */
- #ifndef HAVE_UNSIGNED_LONG_LONG_INT
- #define HAVE_UNSIGNED_LONG_LONG_INT 1
- #endif /* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
- #ifndef HAVE_INTMAX_T
- #define HAVE_INTMAX_T 1
- #endif /* !defined(HAVE_INTMAX_T) */
- #ifndef HAVE_UINTMAX_T
- #define HAVE_UINTMAX_T 1
- #endif /* !defined(HAVE_UINTMAX_T) */
- #ifndef HAVE_UINTPTR_T
- #define HAVE_UINTPTR_T 1
- #endif /* !defined(HAVE_UINTPTR_T) */
- #ifndef HAVE_PTRDIFF_T
- #define HAVE_PTRDIFF_T 1
- #endif /* !defined(HAVE_PTRDIFF_T) */
- #ifndef HAVE_VA_COPY
- #define HAVE_VA_COPY 1
- #endif /* !defined(HAVE_VA_COPY) */
- #ifndef HAVE___VA_COPY
- #define HAVE___VA_COPY 1
- #endif /* !defined(HAVE___VA_COPY) */
- #endif /* HAVE_CONFIG_H */
- #define snprintf rpl_snprintf
- #define vsnprintf rpl_vsnprintf
- #define asprintf rpl_asprintf
- #define vasprintf rpl_vasprintf
- #endif /* TEST_SNPRINTF */
- #if !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || !HAVE_VASPRINTF
- #include <stdio.h> /* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
- #ifdef VA_START
- #undef VA_START
- #endif /* defined(VA_START) */
- #ifdef VA_SHIFT
- #undef VA_SHIFT
- #endif /* defined(VA_SHIFT) */
- #if HAVE_STDARG_H
- #include <stdarg.h>
- #define VA_START(ap, last) va_start(ap, last)
- #define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
- #else /* Assume <varargs.h> is available. */
- #include <varargs.h>
- #define VA_START(ap, last) va_start(ap) /* "last" is ignored. */
- #define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
- #endif /* HAVE_STDARG_H */
- #if !HAVE_VASPRINTF
- #if HAVE_STDLIB_H
- #include <stdlib.h> /* For malloc(3). */
- #endif /* HAVE_STDLIB_H */
- #ifdef VA_COPY
- #undef VA_COPY
- #endif /* defined(VA_COPY) */
- #ifdef VA_END_COPY
- #undef VA_END_COPY
- #endif /* defined(VA_END_COPY) */
- #if HAVE_VA_COPY
- #define VA_COPY(dest, src) va_copy(dest, src)
- #define VA_END_COPY(ap) va_end(ap)
- #elif HAVE___VA_COPY
- #define VA_COPY(dest, src) __va_copy(dest, src)
- #define VA_END_COPY(ap) va_end(ap)
- #else
- #define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
- #define VA_END_COPY(ap) /* No-op. */
- #define NEED_MYMEMCPY 1
- static void *mymemcpy(void *, void *, size_t);
- #endif /* HAVE_VA_COPY */
- #endif /* !HAVE_VASPRINTF */
- #if !HAVE_VSNPRINTF
- #include <errno.h> /* For ERANGE and errno. */
- #include <limits.h> /* For *_MAX. */
- #if HAVE_FLOAT_H
- #include <float.h> /* For *DBL_{MIN,MAX}_10_EXP. */
- #endif /* HAVE_FLOAT_H */
- #if HAVE_INTTYPES_H
- #include <inttypes.h> /* For intmax_t (if not defined in <stdint.h>). */
- #endif /* HAVE_INTTYPES_H */
- #if HAVE_LOCALE_H
- #include <locale.h> /* For localeconv(3). */
- #endif /* HAVE_LOCALE_H */
- #if HAVE_STDDEF_H
- #include <stddef.h> /* For ptrdiff_t. */
- #endif /* HAVE_STDDEF_H */
- #if HAVE_STDINT_H
- #include <stdint.h> /* For intmax_t. */
- #endif /* HAVE_STDINT_H */
- /* Support for unsigned long long int. We may also need ULLONG_MAX. */
- #ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */
- #ifdef UINT_MAX
- #define ULONG_MAX UINT_MAX
- #else
- #define ULONG_MAX INT_MAX
- #endif /* defined(UINT_MAX) */
- #endif /* !defined(ULONG_MAX) */
- #ifdef ULLONG
- #undef ULLONG
- #endif /* defined(ULLONG) */
- #if HAVE_UNSIGNED_LONG_LONG_INT
- #define ULLONG unsigned long long int
- #ifndef ULLONG_MAX
- #define ULLONG_MAX ULONG_MAX
- #endif /* !defined(ULLONG_MAX) */
- #else
- #define ULLONG unsigned long int
- #ifdef ULLONG_MAX
- #undef ULLONG_MAX
- #endif /* defined(ULLONG_MAX) */
- #define ULLONG_MAX ULONG_MAX
- #endif /* HAVE_LONG_LONG_INT */
- /* Support for uintmax_t. We also need UINTMAX_MAX. */
- #ifdef UINTMAX_T
- #undef UINTMAX_T
- #endif /* defined(UINTMAX_T) */
- #if HAVE_UINTMAX_T || defined(uintmax_t)
- #define UINTMAX_T uintmax_t
- #ifndef UINTMAX_MAX
- #define UINTMAX_MAX ULLONG_MAX
- #endif /* !defined(UINTMAX_MAX) */
- #else
- #define UINTMAX_T ULLONG
- #ifdef UINTMAX_MAX
- #undef UINTMAX_MAX
- #endif /* defined(UINTMAX_MAX) */
- #define UINTMAX_MAX ULLONG_MAX
- #endif /* HAVE_UINTMAX_T || defined(uintmax_t) */
- /* Support for long double. */
- #ifndef LDOUBLE
- #if HAVE_LONG_DOUBLE
- #define LDOUBLE long double
- #define LDOUBLE_MIN_10_EXP LDBL_MIN_10_EXP
- #define LDOUBLE_MAX_10_EXP LDBL_MAX_10_EXP
- #else
- #define LDOUBLE double
- #define LDOUBLE_MIN_10_EXP DBL_MIN_10_EXP
- #define LDOUBLE_MAX_10_EXP DBL_MAX_10_EXP
- #endif /* HAVE_LONG_DOUBLE */
- #endif /* !defined(LDOUBLE) */
- /* Support for long long int. */
- #ifndef LLONG
- #if HAVE_LONG_LONG_INT
- #define LLONG long long int
- #else
- #define LLONG long int
- #endif /* HAVE_LONG_LONG_INT */
- #endif /* !defined(LLONG) */
- /* Support for intmax_t. */
- #ifndef INTMAX_T
- #if HAVE_INTMAX_T || defined(intmax_t)
- #define INTMAX_T intmax_t
- #else
- #define INTMAX_T LLONG
- #endif /* HAVE_INTMAX_T || defined(intmax_t) */
- #endif /* !defined(INTMAX_T) */
- /* Support for uintptr_t. */
- #ifndef UINTPTR_T
- #if HAVE_UINTPTR_T || defined(uintptr_t)
- #define UINTPTR_T uintptr_t
- #else
- #define UINTPTR_T unsigned long int
- #endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
- #endif /* !defined(UINTPTR_T) */
- /* Support for ptrdiff_t. */
- #ifndef PTRDIFF_T
- #if HAVE_PTRDIFF_T || defined(ptrdiff_t)
- #define PTRDIFF_T ptrdiff_t
- #else
- #define PTRDIFF_T long int
- #endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
- #endif /* !defined(PTRDIFF_T) */
- /*
- * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
- * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
- * unsigned type if necessary. This should work just fine in practice.
- */
- #ifndef UPTRDIFF_T
- #define UPTRDIFF_T PTRDIFF_T
- #endif /* !defined(UPTRDIFF_T) */
- /*
- * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
- * However, we'll simply use size_t and convert it to a signed type if
- * necessary. This should work just fine in practice.
- */
- #ifndef SSIZE_T
- #define SSIZE_T size_t
- #endif /* !defined(SSIZE_T) */
- /* Either ERANGE or E2BIG should be available everywhere. */
- #ifndef ERANGE
- #define ERANGE E2BIG
- #endif /* !defined(ERANGE) */
- #ifndef EOVERFLOW
- #define EOVERFLOW ERANGE
- #endif /* !defined(EOVERFLOW) */
- /*
- * Buffer size to hold the octal string representation of UINT128_MAX without
- * nul-termination ("3777777777777777777777777777777777777777777").
- */
- #ifdef MAX_CONVERT_LENGTH
- #undef MAX_CONVERT_LENGTH
- #endif /* defined(MAX_CONVERT_LENGTH) */
- #define MAX_CONVERT_LENGTH 43
- /* Format read states. */
- #define PRINT_S_DEFAULT 0
- #define PRINT_S_FLAGS 1
- #define PRINT_S_WIDTH 2
- #define PRINT_S_DOT 3
- #define PRINT_S_PRECISION 4
- #define PRINT_S_MOD 5
- #define PRINT_S_CONV 6
- /* Format flags. */
- #define PRINT_F_MINUS (1 << 0)
- #define PRINT_F_PLUS (1 << 1)
- #define PRINT_F_SPACE (1 << 2)
- #define PRINT_F_NUM (1 << 3)
- #define PRINT_F_ZERO (1 << 4)
- #define PRINT_F_QUOTE (1 << 5)
- #define PRINT_F_UP (1 << 6)
- #define PRINT_F_UNSIGNED (1 << 7)
- #define PRINT_F_TYPE_G (1 << 8)
- #define PRINT_F_TYPE_E (1 << 9)
- /* Conversion flags. */
- #define PRINT_C_CHAR 1
- #define PRINT_C_SHORT 2
- #define PRINT_C_LONG 3
- #define PRINT_C_LLONG 4
- #define PRINT_C_LDOUBLE 5
- #define PRINT_C_SIZE 6
- #define PRINT_C_PTRDIFF 7
- #define PRINT_C_INTMAX 8
- #ifndef MAX
- #define MAX(x, y) ((x >= y) ? x : y)
- #endif /* !defined(MAX) */
- #ifndef CHARTOINT
- #define CHARTOINT(ch) (ch - '0')
- #endif /* !defined(CHARTOINT) */
- #ifndef ISDIGIT
- #define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
- #endif /* !defined(ISDIGIT) */
- #ifndef ISNAN
- #define ISNAN(x) (x != x)
- #endif /* !defined(ISNAN) */
- #ifndef ISINF
- #define ISINF(x) ((x < -1 || x > 1) && x + x == x)
- #endif /* !defined(ISINF) */
- #ifdef OUTCHAR
- #undef OUTCHAR
- #endif /* defined(OUTCHAR) */
- #define OUTCHAR(str, len, size, ch) \
- do { \
- if (len + 1 < size) \
- str[len] = ch; \
- (len)++; \
- } while (/* CONSTCOND */ 0)
- static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
- static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
- static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
- static void printsep(char *, size_t *, size_t);
- static int getnumsep(int);
- static int getexponent(LDOUBLE);
- static int convert(UINTMAX_T, char *, size_t, int, int);
- static UINTMAX_T cast(LDOUBLE);
- static UINTMAX_T myround(LDOUBLE);
- static LDOUBLE mypow10(int);
- extern int errno;
- int
- rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
- {
- LDOUBLE fvalue;
- INTMAX_T value;
- unsigned char cvalue;
- const char *strvalue;
- INTMAX_T *intmaxptr;
- PTRDIFF_T *ptrdiffptr;
- SSIZE_T *sizeptr;
- LLONG *llongptr;
- long int *longptr;
- int *intptr;
- short int *shortptr;
- signed char *charptr;
- size_t len = 0;
- int overflow = 0;
- int base = 0;
- int cflags = 0;
- int flags = 0;
- int width = 0;
- int precision = -1;
- int state = PRINT_S_DEFAULT;
- char ch = *format++;
- /*
- * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
- * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
- * even if a size larger than zero was specified. At least NetBSD's
- * snprintf(3) does the same, as well as other versions of this file.
- * (Though some of these versions will write to a non-NULL buffer even
- * if a size of zero was specified, which violates the standard.)
- */
- if (str == NULL && size != 0)
- size = 0;
- while (ch != '\0')
- switch (state) {
- case PRINT_S_DEFAULT:
- if (ch == '%')
- state = PRINT_S_FLAGS;
- else
- OUTCHAR(str, len, size, ch);
- ch = *format++;
- break;
- case PRINT_S_FLAGS:
- switch (ch) {
- case '-':
- flags |= PRINT_F_MINUS;
- ch = *format++;
- break;
- case '+':
- flags |= PRINT_F_PLUS;
- ch = *format++;
- break;
- case ' ':
- flags |= PRINT_F_SPACE;
- ch = *format++;
- break;
- case '#':
- flags |= PRINT_F_NUM;
- ch = *format++;
- break;
- case '0':
- flags |= PRINT_F_ZERO;
- ch = *format++;
- break;
- case '\'': /* SUSv2 flag (not in C99). */
- flags |= PRINT_F_QUOTE;
- ch = *format++;
- break;
- default:
- state = PRINT_S_WIDTH;
- break;
- }
- break;
- case PRINT_S_WIDTH:
- if (ISDIGIT(ch)) {
- ch = CHARTOINT(ch);
- if (width > (INT_MAX - ch) / 10) {
- overflow = 1;
- goto out;
- }
- width = 10 * width + ch;
- ch = *format++;
- } else if (ch == '*') {
- /*
- * C99 says: "A negative field width argument is
- * taken as a `-' flag followed by a positive
- * field width." (7.19.6.1, 5)
- */
- if ((width = va_arg(args, int)) < 0) {
- flags |= PRINT_F_MINUS;
- width = -width;
- }
- ch = *format++;
- state = PRINT_S_DOT;
- } else
- state = PRINT_S_DOT;
- break;
- case PRINT_S_DOT:
- if (ch == '.') {
- state = PRINT_S_PRECISION;
- ch = *format++;
- } else
- state = PRINT_S_MOD;
- break;
- case PRINT_S_PRECISION:
- if (precision == -1)
- precision = 0;
- if (ISDIGIT(ch)) {
- ch = CHARTOINT(ch);
- if (precision > (INT_MAX - ch) / 10) {
- overflow = 1;
- goto out;
- }
- precision = 10 * precision + ch;
- ch = *format++;
- } else if (ch == '*') {
- /*
- * C99 says: "A negative precision argument is
- * taken as if the precision were omitted."
- * (7.19.6.1, 5)
- */
- if ((precision = va_arg(args, int)) < 0)
- precision = -1;
- ch = *format++;
- state = PRINT_S_MOD;
- } else
- state = PRINT_S_MOD;
- break;
- case PRINT_S_MOD:
- switch (ch) {
- case 'h':
- ch = *format++;
- if (ch == 'h') { /* It's a char. */
- ch = *format++;
- cflags = PRINT_C_CHAR;
- } else
- cflags = PRINT_C_SHORT;
- break;
- case 'l':
- ch = *format++;
- if (ch == 'l') { /* It's a long long. */
- ch = *format++;
- cflags = PRINT_C_LLONG;
- } else
- cflags = PRINT_C_LONG;
- break;
- case 'L':
- cflags = PRINT_C_LDOUBLE;
- ch = *format++;
- break;
- case 'j':
- cflags = PRINT_C_INTMAX;
- ch = *format++;
- break;
- case 't':
- cflags = PRINT_C_PTRDIFF;
- ch = *format++;
- break;
- case 'z':
- cflags = PRINT_C_SIZE;
- ch = *format++;
- break;
- }
- state = PRINT_S_CONV;
- break;
- case PRINT_S_CONV:
- switch (ch) {
- case 'd':
- /* FALLTHROUGH */
- case 'i':
- switch (cflags) {
- case PRINT_C_CHAR:
- value = (signed char)va_arg(args, int);
- break;
- case PRINT_C_SHORT:
- value = (short int)va_arg(args, int);
- break;
- case PRINT_C_LONG:
- value = va_arg(args, long int);
- break;
- case PRINT_C_LLONG:
- value = va_arg(args, LLONG);
- break;
- case PRINT_C_SIZE:
- value = va_arg(args, SSIZE_T);
- break;
- case PRINT_C_INTMAX:
- value = va_arg(args, INTMAX_T);
- break;
- case PRINT_C_PTRDIFF:
- value = va_arg(args, PTRDIFF_T);
- break;
- default:
- value = va_arg(args, int);
- break;
- }
- fmtint(str, &len, size, value, 10, width,
- precision, flags);
- break;
- case 'X':
- flags |= PRINT_F_UP;
- /* FALLTHROUGH */
- case 'x':
- base = 16;
- /* FALLTHROUGH */
- case 'o':
- if (base == 0)
- base = 8;
- /* FALLTHROUGH */
- case 'u':
- if (base == 0)
- base = 10;
- flags |= PRINT_F_UNSIGNED;
- switch (cflags) {
- case PRINT_C_CHAR:
- value = (unsigned char)va_arg(args,
- unsigned int);
- break;
- case PRINT_C_SHORT:
- value = (unsigned short int)va_arg(args,
- unsigned int);
- break;
- case PRINT_C_LONG:
- value = va_arg(args, unsigned long int);
- break;
- case PRINT_C_LLONG:
- value = va_arg(args, ULLONG);
- break;
- case PRINT_C_SIZE:
- value = va_arg(args, size_t);
- break;
- case PRINT_C_INTMAX:
- value = va_arg(args, UINTMAX_T);
- break;
- case PRINT_C_PTRDIFF:
- value = va_arg(args, UPTRDIFF_T);
- break;
- default:
- value = va_arg(args, unsigned int);
- break;
- }
- fmtint(str, &len, size, value, base, width,
- precision, flags);
- break;
- case 'A':
- /* Not yet supported, we'll use "%F". */
- /* FALLTHROUGH */
- case 'E':
- if (ch == 'E')
- flags |= PRINT_F_TYPE_E;
- /* FALLTHROUGH */
- case 'G':
- if (ch == 'G')
- flags |= PRINT_F_TYPE_G;
- /* FALLTHROUGH */
- case 'F':
- flags |= PRINT_F_UP;
- /* FALLTHROUGH */
- case 'a':
- /* Not yet supported, we'll use "%f". */
- /* FALLTHROUGH */
- case 'e':
- if (ch == 'e')
- flags |= PRINT_F_TYPE_E;
- /* FALLTHROUGH */
- case 'g':
- if (ch == 'g')
- flags |= PRINT_F_TYPE_G;
- /* FALLTHROUGH */
- case 'f':
- if (cflags == PRINT_C_LDOUBLE)
- fvalue = va_arg(args, LDOUBLE);
- else
- fvalue = va_arg(args, double);
- fmtflt(str, &len, size, fvalue, width,
- precision, flags, &overflow);
- if (overflow)
- goto out;
- break;
- case 'c':
- cvalue = va_arg(args, int);
- OUTCHAR(str, len, size, cvalue);
- break;
- case 's':
- strvalue = va_arg(args, char *);
- fmtstr(str, &len, size, strvalue, width,
- precision, flags);
- break;
- case 'p':
- /*
- * C99 says: "The value of the pointer is
- * converted to a sequence of printing
- * characters, in an implementation-defined
- * manner." (C99: 7.19.6.1, 8)
- */
- if ((strvalue = va_arg(args, void *)) == NULL)
- /*
- * We use the glibc format. BSD prints
- * "0x0", SysV "0".
- */
- fmtstr(str, &len, size, "(nil)", width,
- -1, flags);
- else {
- /*
- * We use the BSD/glibc format. SysV
- * omits the "0x" prefix (which we emit
- * using the PRINT_F_NUM flag).
- */
- flags |= PRINT_F_NUM;
- flags |= PRINT_F_UNSIGNED;
- fmtint(str, &len, size,
- (UINTPTR_T)strvalue, 16, width,
- precision, flags);
- }
- break;
- case 'n':
- switch (cflags) {
- case PRINT_C_CHAR:
- charptr = va_arg(args, signed char *);
- *charptr = len;
- break;
- case PRINT_C_SHORT:
- shortptr = va_arg(args, short int *);
- *shortptr = len;
- break;
- case PRINT_C_LONG:
- longptr = va_arg(args, long int *);
- *longptr = len;
- break;
- case PRINT_C_LLONG:
- llongptr = va_arg(args, LLONG *);
- *llongptr = len;
- break;
- case PRINT_C_SIZE:
- /*
- * C99 says that with the "z" length
- * modifier, "a following `n' conversion
- * specifier applies to a pointer to a
- * signed integer type corresponding to
- * size_t argument." (7.19.6.1, 7)
- */
- sizeptr = va_arg(args, SSIZE_T *);
- *sizeptr = len;
- break;
- case PRINT_C_INTMAX:
- intmaxptr = va_arg(args, INTMAX_T *);
- *intmaxptr = len;
- break;
- case PRINT_C_PTRDIFF:
- ptrdiffptr = va_arg(args, PTRDIFF_T *);
- *ptrdiffptr = len;
- break;
- default:
- intptr = va_arg(args, int *);
- *intptr = len;
- break;
- }
- break;
- case '%': /* Print a "%" character verbatim. */
- OUTCHAR(str, len, size, ch);
- break;
- default: /* Skip other characters. */
- break;
- }
- ch = *format++;
- state = PRINT_S_DEFAULT;
- base = cflags = flags = width = 0;
- precision = -1;
- break;
- }
- out:
- if (len < size)
- str[len] = '\0';
- else if (size > 0)
- str[size - 1] = '\0';
- if (overflow || len > INT_MAX) {
- errno = EOVERFLOW;
- return -1;
- }
- return (int)len;
- }
- static void
- fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
- int precision, int flags)
- {
- int padlen, strln; /* Amount to pad. */
- int noprecision = (precision == -1);
- if (value == NULL) /* We're forgiving. */
- value = "(null)";
- /* If a precision was specified, don't read the string past it. */
- for (strln = 0; value[strln] != '\0' &&
- (noprecision || strln < precision); strln++)
- continue;
- if ((padlen = width - strln) < 0)
- padlen = 0;
- if (flags & PRINT_F_MINUS) /* Left justify. */
- padlen = -padlen;
- while (padlen > 0) { /* Leading spaces. */
- OUTCHAR(str, *len, size, ' ');
- padlen--;
- }
- while (*value != '\0' && (noprecision || precision-- > 0)) {
- OUTCHAR(str, *len, size, *value);
- value++;
- }
- while (padlen < 0) { /* Trailing spaces. */
- OUTCHAR(str, *len, size, ' ');
- padlen++;
- }
- }
- static void
- fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
- int precision, int flags)
- {
- UINTMAX_T uvalue;
- char iconvert[MAX_CONVERT_LENGTH];
- char sign = 0;
- char hexprefix = 0;
- int spadlen = 0; /* Amount to space pad. */
- int zpadlen = 0; /* Amount to zero pad. */
- int pos;
- int separators = (flags & PRINT_F_QUOTE);
- int noprecision = (precision == -1);
- if (flags & PRINT_F_UNSIGNED)
- uvalue = value;
- else {
- uvalue = (value >= 0) ? value : -value;
- if (value < 0)
- sign = '-';
- else if (flags & PRINT_F_PLUS) /* Do a sign. */
- sign = '+';
- else if (flags & PRINT_F_SPACE)
- sign = ' ';
- }
- pos = convert(uvalue, iconvert, sizeof(iconvert), base,
- flags & PRINT_F_UP);
- if (flags & PRINT_F_NUM && uvalue != 0) {
- /*
- * C99 says: "The result is converted to an `alternative form'.
- * For `o' conversion, it increases the precision, if and only
- * if necessary, to force the first digit of the result to be a
- * zero (if the value and precision are both 0, a single 0 is
- * printed). For `x' (or `X') conversion, a nonzero result has
- * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
- */
- switch (base) {
- case 8:
- if (precision <= pos)
- precision = pos + 1;
- break;
- case 16:
- hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
- break;
- }
- }
- if (separators) /* Get the number of group separators we'll print. */
- separators = getnumsep(pos);
- zpadlen = precision - pos - separators;
- spadlen = width /* Minimum field width. */
- - separators /* Number of separators. */
- - MAX(precision, pos) /* Number of integer digits. */
- - ((sign != 0) ? 1 : 0) /* Will we print a sign? */
- - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
- if (zpadlen < 0)
- zpadlen = 0;
- if (spadlen < 0)
- spadlen = 0;
- /*
- * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
- * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
- * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
- */
- if (flags & PRINT_F_MINUS) /* Left justify. */
- spadlen = -spadlen;
- else if (flags & PRINT_F_ZERO && noprecision) {
- zpadlen += spadlen;
- spadlen = 0;
- }
- while (spadlen > 0) { /* Leading spaces. */
- OUTCHAR(str, *len, size, ' ');
- spadlen--;
- }
- if (sign != 0) /* Sign. */
- OUTCHAR(str, *len, size, sign);
- if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
- OUTCHAR(str, *len, size, '0');
- OUTCHAR(str, *len, size, hexprefix);
- }
- while (zpadlen > 0) { /* Leading zeros. */
- OUTCHAR(str, *len, size, '0');
- zpadlen--;
- }
- while (pos > 0) { /* The actual digits. */
- pos--;
- OUTCHAR(str, *len, size, iconvert[pos]);
- if (separators > 0 && pos > 0 && pos % 3 == 0)
- printsep(str, len, size);
- }
- while (spadlen < 0) { /* Trailing spaces. */
- OUTCHAR(str, *len, size, ' ');
- spadlen++;
- }
- }
- static void
- fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
- int precision, int flags, int *overflow)
- {
- LDOUBLE ufvalue;
- UINTMAX_T intpart;
- UINTMAX_T fracpart;
- UINTMAX_T mask;
- const char *infnan = NULL;
- char iconvert[MAX_CONVERT_LENGTH];
- char fconvert[MAX_CONVERT_LENGTH];
- char econvert[5]; /* "e-300" (without nul-termination). */
- char esign = 0;
- char sign = 0;
- int leadfraczeros = 0;
- int exponent = 0;
- int emitpoint = 0;
- int omitzeros = 0;
- int omitcount = 0;
- int padlen = 0;
- int epos = 0;
- int fpos = 0;
- int ipos = 0;
- int separators = (flags & PRINT_F_QUOTE);
- int estyle = (flags & PRINT_F_TYPE_E);
- #if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
- struct lconv *lc = localeconv();
- #endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
- /*
- * AIX' man page says the default is 0, but C99 and at least Solaris'
- * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
- * defaults to 6.
- */
- if (precision == -1)
- precision = 6;
- if (fvalue < 0.0)
- sign = '-';
- else if (flags & PRINT_F_PLUS) /* Do a sign. */
- sign = '+';
- else if (flags & PRINT_F_SPACE)
- sign = ' ';
- if (ISNAN(fvalue))
- infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
- else if (ISINF(fvalue))
- infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
- if (infnan != NULL) {
- if (sign != 0)
- iconvert[ipos++] = sign;
- while (*infnan != '\0')
- iconvert[ipos++] = *infnan++;
- fmtstr(str, len, size, iconvert, width, ipos, flags);
- return;
- }
- /* "%e" (or "%E") or "%g" (or "%G") conversion. */
- if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
- if (flags & PRINT_F_TYPE_G) {
- /*
- * If the precision is zero, it is treated as one (cf.
- * C99: 7.19.6.1, 8).
- */
- if (precision == 0)
- precision = 1;
- /*
- * For "%g" (and "%G") conversions, the precision
- * specifies the number of significant digits, which
- * includes the digits in the integer part. The
- * conversion will or will not be using "e-style" (like
- * "%e" or "%E" conversions) depending on the precision
- * and on the exponent. However, the exponent can be
- * affected by rounding the converted value, so we'll
- * leave this decision for later. Until then, we'll
- * assume that we're going to do an "e-style" conversion
- * (in order to get the exponent calculated). For
- * "e-style", the precision must be decremented by one.
- */
- precision--;
- /*
- * For "%g" (and "%G") conversions, trailing zeros are
- * removed from the fractional portion of the result
- * unless the "#" flag was specified.
- */
- if (!(flags & PRINT_F_NUM))
- omitzeros = 1;
- }
- exponent = getexponent(fvalue);
- estyle = 1;
- }
- again:
- /*
- * Sorry, we only support 9, 19, or 38 digits (that is, the number of
- * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
- * minus one) past the decimal point due to our conversion method.
- */
- switch (sizeof(UINTMAX_T)) {
- case 16:
- if (precision > 38)
- precision = 38;
- break;
- case 8:
- if (precision > 19)
- precision = 19;
- break;
- default:
- if (precision > 9)
- precision = 9;
- break;
- }
- ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
- if (estyle) /* We want exactly one integer digit. */
- ufvalue /= mypow10(exponent);
- if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
- *overflow = 1;
- return;
- }
- /*
- * Factor of ten with the number of digits needed for the fractional
- * part. For example, if the precision is 3, the mask will be 1000.
- */
- mask = mypow10(precision);
- /*
- * We "cheat" by converting the fractional part to integer by
- * multiplying by a factor of ten.
- */
- if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
- /*
- * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
- * (because precision = 3). Now, myround(1000 * 0.99962) will
- * return 1000. So, the integer part must be incremented by one
- * and the fractional part must be set to zero.
- */
- intpart++;
- fracpart = 0;
- if (estyle && intpart == 10) {
- /*
- * The value was rounded up to ten, but we only want one
- * integer digit if using "e-style". So, the integer
- * part must be set to one and the exponent must be
- * incremented by one.
- */
- intpart = 1;
- exponent++;
- }
- }
- /*
- * Now that we know the real exponent, we can check whether or not to
- * use "e-style" for "%g" (and "%G") conversions. If we don't need
- * "e-style", the precision must be adjusted and the integer and
- * fractional parts must be recalculated from the original value.
- *
- * C99 says: "Let P equal the precision if nonzero, 6 if the precision
- * is omitted, or 1 if the precision is zero. Then, if a conversion
- * with style `E' would have an exponent of X:
- *
- * - if P > X >= -4, the conversion is with style `f' (or `F') and
- * precision P - (X + 1).
- *
- * - otherwise, the conversion is with style `e' (or `E') and precision
- * P - 1." (7.19.6.1, 8)
- *
- * Note that we had decremented the precision by one.
- */
- if (flags & PRINT_F_TYPE_G && estyle &&
- precision + 1 > exponent && exponent >= -4) {
- precision -= exponent;
- estyle = 0;
- goto again;
- }
- if (estyle) {
- if (exponent < 0) {
- exponent = -exponent;
- esign = '-';
- } else
- esign = '+';
- /*
- * Convert the exponent. The sizeof(econvert) is 5. So, the
- * econvert buffer can hold e.g. "e+999" and "e-999". We don't
- * support an exponent which contains more than three digits.
- * Therefore, the following stores are safe.
- */
- epos = convert(exponent, econvert, 3, 10, 0);
- /*
- * C99 says: "The exponent always contains at least two digits,
- * and only as many more digits as necessary to represent the
- * exponent." (7.19.6.1, 8)
- */
- if (epos == 1)
- econvert[epos++] = '0';
- econvert[epos++] = esign;
- econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
- }
- /* Convert the integer part and the fractional part. */
- ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
- if (fracpart != 0) /* convert() would return 1 if fracpart == 0. */
- fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
- leadfraczeros = precision - fpos;
- if (omitzeros) {
- if (fpos > 0) /* Omit trailing fractional part zeros. */
- while (omitcount < fpos && fconvert[omitcount] == '0')
- omitcount++;
- else { /* The fractional part is zero, omit it completely. */
- omitcount = precision;
- leadfraczeros = 0;
- }
- precision -= omitcount;
- }
- /*
- * Print a decimal point if either the fractional part is non-zero
- * and/or the "#" flag was specified.
- */
- if (precision > 0 || flags & PRINT_F_NUM)
- emitpoint = 1;
- if (separators) /* Get the number of group separators we'll print. */
- separators = getnumsep(ipos);
- padlen = width /* Minimum field width. */
- - ipos /* Number of integer digits. */
- - epos /* Number of exponent characters. */
- - precision /* Number of fractional digits. */
- - separators /* Number of group separators. */
- - (emitpoint ? 1 : 0) /* Will we print a decimal point? */
- - ((sign != 0) ? 1 : 0); /* Will we print a sign character? */
- if (padlen < 0)
- padlen = 0;
- /*
- * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
- * ignored." (7.19.6.1, 6)
- */
- if (flags & PRINT_F_MINUS) /* Left justifty. */
- padlen = -padlen;
- else if (flags & PRINT_F_ZERO && padlen > 0) {
- if (sign != 0) { /* Sign. */
- OUTCHAR(str, *len, size, sign);
- sign = 0;
- }
- while (padlen > 0) { /* Leading zeros. */
- OUTCHAR(str, *len, size, '0');
- padlen--;
- }
- }
- while (padlen > 0) { /* Leading spaces. */
- OUTCHAR(str, *len, size, ' ');
- padlen--;
- }
- if (sign != 0) /* Sign. */
- OUTCHAR(str, *len, size, sign);
- while (ipos > 0) { /* Integer part. */
- ipos--;
- OUTCHAR(str, *len, size, iconvert[ipos]);
- if (separators > 0 && ipos > 0 && ipos % 3 == 0)
- printsep(str, len, size);
- }
- if (emitpoint) { /* Decimal point. */
- #if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
- if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
- OUTCHAR(str, *len, size, *lc->decimal_point);
- else /* We'll always print some decimal point character. */
- #endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
- OUTCHAR(str, *len, size, '.');
- }
- while (leadfraczeros > 0) { /* Leading fractional part zeros. */
- OUTCHAR(str, *len, size, '0');
- leadfraczeros--;
- }
- while (fpos > omitcount) { /* The remaining fractional part. */
- fpos--;
- OUTCHAR(str, *len, size, fconvert[fpos]);
- }
- while (epos > 0) { /* Exponent. */
- epos--;
- OUTCHAR(str, *len, size, econvert[epos]);
- }
- while (padlen < 0) { /* Trailing spaces. */
- OUTCHAR(str, *len, size, ' ');
- padlen++;
- }
- }
- static void
- printsep(char *str, size_t *len, size_t size)
- {
- #if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
- struct lconv *lc = localeconv();
- int i;
- if (lc->thousands_sep != NULL)
- for (i = 0; lc->thousands_sep[i] != '\0'; i++)
- OUTCHAR(str, *len, size, lc->thousands_sep[i]);
- else
- #endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
- OUTCHAR(str, *len, size, ',');
- }
- static int
- getnumsep(int digits)
- {
- int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
- #if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
- int strln;
- struct lconv *lc = localeconv();
- /* We support an arbitrary separator length (including zero). */
- if (lc->thousands_sep != NULL) {
- for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
- continue;
- separators *= strln;
- }
- #endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
- return separators;
- }
- static int
- getexponent(LDOUBLE value)
- {
- LDOUBLE tmp = (value >= 0.0) ? value : -value;
- int exponent = 0;
- /*
- * We check for LDOUBLE_MAX_10_EXP >= exponent >= LDOUBLE_MIN_10_EXP in
- * order to work around possible endless loops which could happen (at
- * least) in the second loop (at least) if we're called with an infinite
- * value. However, we checked for infinity before calling this function
- * using our ISINF() macro, so this might be somewhat paranoid.
- */
- while (tmp < 1.0 && tmp > 0.0 && --exponent >= LDOUBLE_MIN_10_EXP)
- tmp *= 10;
- while (tmp >= 10.0 && ++exponent <= LDOUBLE_MAX_10_EXP)
- tmp /= 10;
- return exponent;
- }
- static int
- convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
- {
- const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
- size_t pos = 0;
- /* We return an unterminated buffer with the digits in reverse order. */
- do {
- buf[pos++] = digits[value % base];
- value /= base;
- } while (value != 0 && pos < size);
- return (int)pos;
- }
- static UINTMAX_T
- cast(LDOUBLE value)
- {
- UINTMAX_T result;
- /*
- * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
- * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
- * it may be increased to the nearest higher representable value for the
- * comparison (cf. C99: 6.3.1.4, 2). It might then equal the LDOUBLE
- * value although converting the latter to UINTMAX_T would overflow.
- */
- if (value >= UINTMAX_MAX)
- return UINTMAX_MAX;
- result = value;
- /*
- * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
- * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
- * the standard). Sigh.
- */
- return (result <= value) ? result : result - 1;
- }
- static UINTMAX_T
- myround(LDOUBLE value)
- {
- UINTMAX_T intpart = cast(value);
- return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
- }
- static LDOUBLE
- mypow10(int exponent)
- {
- LDOUBLE result = 1;
- while (exponent > 0) {
- result *= 10;
- exponent--;
- }
- while (exponent < 0) {
- result /= 10;
- exponent++;
- }
- return result;
- }
- #endif /* !HAVE_VSNPRINTF */
- #if !HAVE_VASPRINTF
- #if NEED_MYMEMCPY
- void *
- mymemcpy(void *dst, void *src, size_t len)
- {
- const char *from = src;
- char *to = dst;
- /* No need for optimization, we use this only to replace va_copy(3). */
- while (len-- > 0)
- *to++ = *from++;
- return dst;
- }
- #endif /* NEED_MYMEMCPY */
- int
- rpl_vasprintf(char **ret, const char *format, va_list ap)
- {
- size_t size;
- int len;
- va_list aq;
- VA_COPY(aq, ap);
- len = vsnprintf(NULL, 0, format, aq);
- VA_END_COPY(aq);
- if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
- return -1;
- return vsnprintf(*ret, size, format, ap);
- }
- #endif /* !HAVE_VASPRINTF */
- #if !HAVE_SNPRINTF
- #if HAVE_STDARG_H
- int
- rpl_snprintf(char *str, size_t size, const char *format, ...)
- #else
- int
- rpl_snprintf(va_alist) va_dcl
- #endif /* HAVE_STDARG_H */
- {
- #if !HAVE_STDARG_H
- char *str;
- size_t size;
- char *format;
- #endif /* HAVE_STDARG_H */
- va_list ap;
- int len;
- VA_START(ap, format);
- VA_SHIFT(ap, str, char *);
- VA_SHIFT(ap, size, size_t);
- VA_SHIFT(ap, format, const char *);
- len = vsnprintf(str, size, format, ap);
- va_end(ap);
- return len;
- }
- #endif /* !HAVE_SNPRINTF */
- #if !HAVE_ASPRINTF
- #if HAVE_STDARG_H
- int
- rpl_asprintf(char **ret, const char *format, ...)
- #else
- int
- rpl_asprintf(va_alist) va_dcl
- #endif /* HAVE_STDARG_H */
- {
- #if !HAVE_STDARG_H
- char **ret;
- char *format;
- #endif /* HAVE_STDARG_H */
- va_list ap;
- int len;
- VA_START(ap, format);
- VA_SHIFT(ap, ret, char **);
- VA_SHIFT(ap, format, const char *);
- len = vasprintf(ret, format, ap);
- va_end(ap);
- return len;
- }
- #endif /* !HAVE_ASPRINTF */
- #else /* Dummy declaration to avoid empty translation unit warnings. */
- int main(int argc, char **argv);
- #endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */
- int sprintf(char *s, const char *fmt, ...)
- {
- int n;
- va_list arg;
- va_start(arg, fmt);
- n = vsnprintf(s, 1024, fmt, arg);
- va_end(arg);
- return n;
- }
- int vsprintf (char *d, const char *s, va_list ap)
- {
- return vsnprintf(d, 1024, s, ap);
- }
- #if TEST_SNPRINTF
- int
- main(void)
- {
- const char *float_fmt[] = {
- /* "%E" and "%e" formats. */
- #if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
- "%.16e",
- "%22.16e",
- "%022.16e",
- "%-22.16e",
- "%#+'022.16e",
- #endif /* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
- "foo|%#+0123.9E|bar",
- "%-123.9e",
- "%123.9e",
- "%+23.9e",
- "%+05.8e",
- "%-05.8e",
- "%05.8e",
- "%+5.8e",
- "%-5.8e",
- "% 5.8e",
- "%5.8e",
- "%+4.9e",
- #if !OS_LINUX /* glibc sometimes gets these wrong. */
- "%+#010.0e",
- "%#10.1e",
- "%10.5e",
- "% 10.5e",
- "%5.0e",
- "%5.e",
- "%#5.0e",
- "%#5.e",
- "%3.2e",
- "%3.1e",
- "%-1.5e",
- "%1.5e",
- "%01.3e",
- "%1.e",
- "%.1e",
- "%#.0e",
- "%+.0e",
- "% .0e",
- "%.0e",
- "%#.e",
- "%+.e",
- "% .e",
- "%.e",
- "%4e",
- "%e",
- "%E",
- #endif /* !OS_LINUX */
- /* "%F" and "%f" formats. */
- #if !OS_BSD && !OS_IRIX
- "% '022f",
- "%+'022f",
- "%-'22f",
- "%'22f",
- #if HAVE_LONG_LONG_INT
- "%.16f",
- "%22.16f",
- "%022.16f",
- "%-22.16f",
- "%#+'022.16f",
- #endif /* HAVE_LONG_LONG_INT */
- #endif /* !OS_BSD && !OS_IRIX */
- "foo|%#+0123.9F|bar",
- "%-123.9f",
- "%123.9f",
- "%+23.9f",
- "%+#010.0f",
- "%#10.1f",
- "%10.5f",
- "% 10.5f",
- "%+05.8f",
- "%-05.8f",
- "%05.8f",
- "%+5.8f",
- "%-5.8f",
- "% 5.8f",
- "%5.8f",
- "%5.0f",
- "%5.f",
- "%#5.0f",
- "%#5.f",
- "%+4.9f",
- "%3.2f",
- "%3.1f",
- "%-1.5f",
- "%1.5f",
- "%01.3f",
- "%1.f",
- "%.1f",
- "%#.0f",
- "%+.0f",
- "% .0f",
- "%.0f",
- "%#.f",
- "%+.f",
- "% .f",
- "%.f",
- "%4f",
- "%f",
- "%F",
- /* "%G" and "%g" formats. */
- #if !OS_BSD && !OS_IRIX && !OS_LINUX
- "% '022g",
- "%+'022g",
- "%-'22g",
- "%'22g",
- #if HAVE_LONG_LONG_INT
- "%.16g",
- "%22.16g",
- "%022.16g",
- "%-22.16g",
- "%#+'022.16g",
- #endif /* HAVE_LONG_LONG_INT */
- #endif /* !OS_BSD && !OS_IRIX && !OS_LINUX */
- "foo|%#+0123.9G|bar",
- "%-123.9g",
- "%123.9g",
- "%+23.9g",
- "%+05.8g",
- "%-05.8g",
- "%05.8g",
- "%+5.8g",
- "%-5.8g",
- "% 5.8g",
- "%5.8g",
- "%+4.9g",
- #if !OS_LINUX /* glibc sometimes gets these wrong. */
- "%+#010.0g",
- "%#10.1g",
- "%10.5g",
- "% 10.5g",
- "%5.0g",
- "%5.g",
- "%#5.0g",
- "%#5.g",
- "%3.2g",
- "%3.1g",
- "%-1.5g",
- "%1.5g",
- "%01.3g",
- "%1.g",
- "%.1g",
- "%#.0g",
- "%+.0g",
- "% .0g",
- "%.0g",
- "%#.g",
- "%+.g",
- "% .g",
- "%.g",
- "%4g",
- "%g",
- "%G",
- #endif /* !OS_LINUX */
- NULL
- };
- double float_val[] = {
- -4.136,
- -134.52,
- -5.04030201,
- -3410.01234,
- -999999.999999,
- -913450.29876,
- -913450.2,
- -91345.2,
- -9134.2,
- -913.2,
- -91.2,
- -9.2,
- -9.9,
- 4.136,
- 134.52,
- 5.04030201,
- 3410.01234,
- 999999.999999,
- 913450.29876,
- 913450.2,
- 91345.2,
- 9134.2,
- 913.2,
- 91.2,
- 9.2,
- 9.9,
- 9.96,
- 9.996,
- 9.9996,
- 9.99996,
- 9.999996,
- 9.9999996,
- 9.99999996,
- 0.99999996,
- 0.99999999,
- 0.09999999,
- 0.00999999,
- 0.00099999,
- 0.00009999,
- 0.00000999,
- 0.00000099,
- 0.00000009,
- 0.00000001,
- 0.0000001,
- 0.000001,
- 0.00001,
- 0.0001,
- 0.001,
- 0.01,
- 0.1,
- 1.0,
- 1.5,
- -1.5,
- -1.0,
- -0.1,
- #if !OS_BSD /* BSD sometimes gets these wrong. */
- #ifdef INFINITY
- INFINITY,
- -INFINITY,
- #endif /* defined(INFINITY) */
- #ifdef NAN
- NAN,
- #endif /* defined(NAN) */
- #endif /* !OS_BSD */
- 0
- };
- const char *long_fmt[] = {
- "foo|%0123ld|bar",
- #if !OS_IRIX
- "% '0123ld",
- "%+'0123ld",
- "%-'123ld",
- "%'123ld",
- #endif /* !OS_IRiX */
- "%123.9ld",
- "% 123.9ld",
- "%+123.9ld",
- "%-123.9ld",
- "%0123ld",
- "% 0123ld",
- "%+0123ld",
- "%-0123ld",
- "%10.5ld",
- "% 10.5ld",
- "%+10.5ld",
- "%-10.5ld",
- "%010ld",
- "% 010ld",
- "%+010ld",
- "%-010ld",
- "%4.2ld",
- "% 4.2ld",
- "%+4.2ld",
- "%-4.2ld",
- "%04ld",
- "% 04ld",
- "%+04ld",
- "%-04ld",
- "%5.5ld",
- "%+22.33ld",
- "%01.3ld",
- "%1.5ld",
- "%-1.5ld",
- "%44ld",
- "%4ld",
- "%4.0ld",
- "%4.ld",
- "%.44ld",
- "%.4ld",
- "%.0ld",
- "%.ld",
- "%ld",
- NULL
- };
- long int long_val[] = {
- #ifdef LONG_MAX
- LONG_MAX,
- #endif /* LONG_MAX */
- #ifdef LONG_MIN
- LONG_MIN,
- #endif /* LONG_MIN */
- -91340,
- 91340,
- 341,
- 134,
- 0203,
- -1,
- 1,
- 0
- };
- const char *ulong_fmt[] = {
- /* "%u" formats. */
- "foo|%0123lu|bar",
- #if !OS_IRIX
- "% '0123lu",
- "%+'0123lu",
- "%-'123lu",
- "%'123lu",
- #endif /* !OS_IRiX */
- "%123.9lu",
- "% 123.9lu",
- "%+123.9lu",
- "%-123.9lu",
- "%0123lu",
- "% 0123lu",
- "%+0123lu",
- "%-0123lu",
- "%5.5lu",
- "%+22.33lu",
- "%01.3lu",
- "%1.5lu",
- "%-1.5lu",
- "%44lu",
- "%lu",
- /* "%o" formats. */
- "foo|%#0123lo|bar",
- "%#123.9lo",
- "%# 123.9lo",
- "%#+123.9lo",
- "%#-123.9lo",
- "%#0123lo",
- "%# 0123lo",
- "%#+0123lo",
- "%#-0123lo",
- "%#5.5lo",
- "%#+22.33lo",
- "%#01.3lo",
- "%#1.5lo",
- "%#-1.5lo",
- "%#44lo",
- "%#lo",
- "%123.9lo",
- "% 123.9lo",
- "%+123.9lo",
- "%-123.9lo",
- "%0123lo",
- "% 0123lo",
- "%+0123lo",
- "%-0123lo",
- "%5.5lo",
- "%+22.33lo",
- "%01.3lo",
- "%1.5lo",
- "%-1.5lo",
- "%44lo",
- "%lo",
- /* "%X" and "%x" formats. */
- "foo|%#0123lX|bar",
- "%#123.9lx",
- "%# 123.9lx",
- "%#+123.9lx",
- "%#-123.9lx",
- "%#0123lx",
- "%# 0123lx",
- "%#+0123lx",
- "%#-0123lx",
- "%#5.5lx",
- "%#+22.33lx",
- "%#01.3lx",
- "%#1.5lx",
- "%#-1.5lx",
- "%#44lx",
- "%#lx",
- "%#lX",
- "%123.9lx",
- "% 123.9lx",
- "%+123.9lx",
- "%-123.9lx",
- "%0123lx",
- "% 0123lx",
- "%+0123lx",
- "%-0123lx",
- "%5.5lx",
- "%+22.33lx",
- "%01.3lx",
- "%1.5lx",
- "%-1.5lx",
- "%44lx",
- "%lx",
- "%lX",
- NULL
- };
- unsigned long int ulong_val[] = {
- #ifdef ULONG_MAX
- ULONG_MAX,
- #endif /* ULONG_MAX */
- 91340,
- 341,
- 134,
- 0203,
- 1,
- 0
- };
- const char *llong_fmt[] = {
- "foo|%0123lld|bar",
- "%123.9lld",
- "% 123.9lld",
- "%+123.9lld",
- "%-123.9lld",
- "%0123lld",
- "% 0123lld",
- "%+0123lld",
- "%-0123lld",
- "%5.5lld",
- "%+22.33lld",
- "%01.3lld",
- "%1.5lld",
- "%-1.5lld",
- "%44lld",
- "%lld",
- NULL
- };
- LLONG llong_val[] = {
- #ifdef LLONG_MAX
- LLONG_MAX,
- #endif /* LLONG_MAX */
- #ifdef LLONG_MIN
- LLONG_MIN,
- #endif /* LLONG_MIN */
- -91340,
- 91340,
- 341,
- 134,
- 0203,
- -1,
- 1,
- 0
- };
- const char *string_fmt[] = {
- "foo|%10.10s|bar",
- "%-10.10s",
- "%10.10s",
- "%10.5s",
- "%5.10s",
- "%10.1s",
- "%1.10s",
- "%10.0s",
- "%0.10s",
- "%-42.5s",
- "%2.s",
- "%.10s",
- "%.1s",
- "%.0s",
- "%.s",
- "%4s",
- "%s",
- NULL
- };
- const char *string_val[] = {
- "Hello",
- "Hello, world!",
- "Sound check: One, two, three.",
- "This string is a little longer than the other strings.",
- "1",
- "",
- NULL
- };
- #if !OS_SYSV /* SysV uses a different format than we do. */
- const char *pointer_fmt[] = {
- "foo|%p|bar",
- "%42p",
- "%p",
- NULL
- };
- const char *pointer_val[] = {
- *pointer_fmt,
- *string_fmt,
- *string_val,
- NULL
- };
- #endif /* !OS_SYSV */
- char buf1[1024], buf2[1024];
- double value, digits = 9.123456789012345678901234567890123456789;
- int i, j, r1, r2, failed = 0, num = 0;
- /*
- * Use -DTEST_NILS in order to also test the conversion of nil values. Might
- * segfault on systems which don't support converting a NULL pointer with "%s"
- * and lets some test cases fail against BSD and glibc due to bugs in their
- * implementations.
- */
- #ifndef TEST_NILS
- #define TEST_NILS 0
- #elif TEST_NILS
- #undef TEST_NILS
- #define TEST_NILS 1
- #endif /* !defined(TEST_NILS) */
- #ifdef TEST
- #undef TEST
- #endif /* defined(TEST) */
- #define TEST(fmt, val) \
- do { \
- for (i = 0; fmt[i] != NULL; i++) \
- for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) { \
- r1 = sprintf(buf1, fmt[i], val[j]); \
- r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]); \
- if (strcmp(buf1, buf2) != 0 || r1 != r2) { \
- (void)printf("Results don't match, " \
- "format string: %s\n" \
- "\t sprintf(3): [%s] (%d)\n" \
- "\tsnprintf(3): [%s] (%d)\n", \
- fmt[i], buf1, r1, buf2, r2); \
- failed++; \
- } \
- num++; \
- } \
- } while (/* CONSTCOND */ 0)
- #if HAVE_LOCALE_H
- (void)setlocale(LC_ALL, "");
- #endif /* HAVE_LOCALE_H */
- (void)puts("Testing our snprintf(3) against your system's sprintf(3).");
- TEST(float_fmt, float_val);
- TEST(long_fmt, long_val);
- TEST(ulong_fmt, ulong_val);
- TEST(llong_fmt, llong_val);
- TEST(string_fmt, string_val);
- #if !OS_SYSV /* SysV uses a different format than we do. */
- TEST(pointer_fmt, pointer_val);
- #endif /* !OS_SYSV */
- (void)printf("Result: %d out of %d tests failed.\n", failed, num);
- (void)fputs("Checking how many digits we support: ", stdout);
- for (i = 0; i < 100; i++) {
- value = pow(10, i) * digits;
- (void)sprintf(buf1, "%.1f", value);
- (void)snprintf(buf2, sizeof(buf2), "%.1f", value);
- if (strcmp(buf1, buf2) != 0) {
- (void)printf("apparently %d.\n", i);
- break;
- }
- }
- return (failed == 0) ? 0 : 1;
- }
- #endif /* TEST_SNPRINTF */
- /* vim: set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */
|