[csw-devel] SF.net SVN: gar:[3587] csw/mgar/pkg/watch/trunk
skayser at users.sourceforge.net
skayser at users.sourceforge.net
Wed Mar 4 23:52:36 CET 2009
Revision: 3587
http://gar.svn.sourceforge.net/gar/?rev=3587&view=rev
Author: skayser
Date: 2009-03-04 22:52:35 +0000 (Wed, 04 Mar 2009)
Log Message:
-----------
watch: fixed asprintf()
Modified Paths:
--------------
csw/mgar/pkg/watch/trunk/Makefile
csw/mgar/pkg/watch/trunk/checksums
csw/mgar/pkg/watch/trunk/files/patch-extralibs.diff
csw/mgar/pkg/watch/trunk/files/snprintf.c
csw/mgar/pkg/watch/trunk/files/snprintf.h
Modified: csw/mgar/pkg/watch/trunk/Makefile
===================================================================
--- csw/mgar/pkg/watch/trunk/Makefile 2009-03-04 21:54:05 UTC (rev 3586)
+++ csw/mgar/pkg/watch/trunk/Makefile 2009-03-04 22:52:35 UTC (rev 3587)
@@ -1,13 +1,14 @@
-# Todo
-# * asprintf() from portable snprintf.c isn't working yet
-# Header line on watch output always reads "Every fs: "
-#
+# Todo:
+# * watch called for programs with longer than terminal output
+# (for example "find ." or "ls -lR" or "last" on build8x) causes
+# broken pipe on second program invocation
GARNAME = watch
GARVERSION = 3.2.7
CATEGORIES = utils
DESCRIPTION = Watch a program output change over time
-define BLURB
+define Blurb
+ "watch" from the procps package.
endef
# Usually we would use $(SF_MIRROR), but the procps folks didn't upload
@@ -35,14 +36,17 @@
#
# 1) Remove unnecessary build instructions that would cause errors otherwise
# 2) Bring in GNU getopt for getopt_long()
-# 3) Bring in asprintf()
+# 3) Bring in snprintf.c for a portable version of asprintf()
#
-# GNU getopt from http://cherokee-project.com
-# asprintf from http://www.ijs.si/software/snprintf/
+# GNU getopt taken from
+# http://svn.cherokee-project.com/browser/cherokee/trunk/cherokee/getopt
+# snprintf.c taken from http://www.jhweiss.de/software/snprintf.html
#
-# Tell snprintf.c that we have snprintf and need asprintf()
-CFLAGS := $(CFLAGS) -DHAVE_SNPRINTF -DNEED_ASPRINTF
+# Don't tell snprintf.c that you have v?snprintf, output will be garbled
+CFLAGS := $(CFLAGS) -DHAVE_STDARG_H -DHAVE_STDLIB_H -DHAVE_STDDEF
+CFLAGS := $(CFLAGS) -DHAVE_INTTYPES_H -DHAVE_LOCALE_H -DHAVE_LOCALECONV
+CFLAGS := $(CFLAGS) -DHAVE_LOCALECONV -DHAVE_UINTMAX_T -DHAVE_VA_COPY
pre-build-modulated:
@rm $(WORKSRC)/ps/module.mk $(WORKSRC)/proc/module.mk
@@ -51,13 +55,14 @@
@cp $(FILEDIR)/gettext.h* $(WORKSRC)
$(MAKECOOKIE)
+build-custom: BUILD_ARGS:=ALL_CFLAGS="$(CFLAGS)" ALL_LDFLAGS="$(LDFLAGS)"
build-custom:
- @(cd $(WORKSRC); \
- gmake ALL_CFLAGS="$(CFLAGS)" ALL_LDFLAGS="$(LDFLAGS)" snprintf.o; \
- gmake ALL_CFLAGS="$(CFLAGS)" ALL_LDFLAGS="$(LDFLAGS)" getopt.o; \
- gmake ALL_CFLAGS="$(CFLAGS)" ALL_LDFLAGS="$(LDFLAGS)" getopt1.o; \
- gmake ALL_CFLAGS="$(CFLAGS) snprintf.o getopt.o getopt1.o" \
- ALL_LDFLAGS="$(LDFLAGS)" watch)
+ @gmake -C $(WORKSRC) $(BUILD_ARGS) snprintf.o
+ @gmake -C $(WORKSRC) $(BUILD_ARGS) getopt.o
+ @gmake -C $(WORKSRC) $(BUILD_ARGS) getopt1.o
+ @gmake -C $(WORKSRC) \
+ ALL_CFLAGS="$(CFLAGS) snprintf.o getopt.o getopt1.o" \
+ ALL_LDFLAGS="$(LDFLAGS)" watch
$(MAKECOOKIE)
install-custom:
Modified: csw/mgar/pkg/watch/trunk/checksums
===================================================================
--- csw/mgar/pkg/watch/trunk/checksums 2009-03-04 21:54:05 UTC (rev 3586)
+++ csw/mgar/pkg/watch/trunk/checksums 2009-03-04 22:52:35 UTC (rev 3587)
@@ -1,2 +1,2 @@
f490bca772b16472962c7b9f23b1e97d download/procps-3.2.7.tar.gz
-223ba15056890d0d15431276d027d480 download/patch-extralibs.diff
+05f5e9e304c4300b056ffd61eafebffa download/patch-extralibs.diff
Modified: csw/mgar/pkg/watch/trunk/files/patch-extralibs.diff
===================================================================
--- csw/mgar/pkg/watch/trunk/files/patch-extralibs.diff 2009-03-04 21:54:05 UTC (rev 3586)
+++ csw/mgar/pkg/watch/trunk/files/patch-extralibs.diff 2009-03-04 22:52:35 UTC (rev 3587)
@@ -14,7 +14,7 @@
#include <unistd.h>
#include <termios.h>
#include <locale.h>
-+#include <stdarg.h>
++#include "snprintf.h"
#include "proc/procps.h"
#ifdef FORCE_8BIT
Modified: csw/mgar/pkg/watch/trunk/files/snprintf.c
===================================================================
--- csw/mgar/pkg/watch/trunk/files/snprintf.c 2009-03-04 21:54:05 UTC (rev 3586)
+++ csw/mgar/pkg/watch/trunk/files/snprintf.c 2009-03-04 22:52:35 UTC (rev 3587)
@@ -1,1025 +1,2099 @@
+/* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */
+
/*
- * snprintf.c - a portable implementation of snprintf
+ * Copyright (c) 1995 Patrick Powell.
*
- * AUTHOR
- * Mark Martinec <mark.martinec at ijs.si>, April 1999.
+ * This code is based on code written by Patrick Powell <papowell at 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.
*
- * Copyright 1999, Mark Martinec. All rights reserved.
+ * This version of the code is maintained by Holger Weiss <holger at 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
*
- * TERMS AND CONDITIONS
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the "Frontier Artistic License" which comes
- * with this Kit.
+ * 2008-01-20 Holger Weiss <holger at jhweiss.de> for C99-snprintf 1.1:
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the Frontier Artistic License for more details.
+ * Fixed the detection of infinite floating point values on IRIX (and
+ * possibly other systems) and applied another few minor cleanups.
*
- * You should have received a copy of the Frontier Artistic License
- * with this Kit in the file named LICENSE.txt .
- * If not, I'll be glad to provide one.
+ * 2008-01-06 Holger Weiss <holger at jhweiss.de> for C99-snprintf 1.0:
*
- * FEATURES
- * - careful adherence to specs regarding flags, field width and precision;
- * - good performance for large string handling (large format, large
- * argument or large paddings). Performance is similar to system's sprintf
- * and in several cases significantly better (make sure you compile with
- * optimizations turned on, tell the compiler the code is strict ANSI
- * if necessary to give it more freedom for optimizations);
- * - return value semantics per ISO/IEC 9899:1999 ("ISO C99");
- * - written in standard ISO/ANSI C - requires an ANSI C compiler.
+ * Added a lot of new features, fixed many bugs, and incorporated various
+ * improvements done by Andrew Tridgell <tridge at samba.org>, Russ Allbery
+ * <rra at stanford.edu>, Hrvoje Niksic <hniksic at xemacs.org>, Damien Miller
+ * <djm at 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.
*
- * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
+ * 2007-07-23 Holger Weiss <holger at jhweiss.de> for Mutt 1.5.13:
*
- * This snprintf only supports the following conversion specifiers:
- * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
- * with flags: '-', '+', ' ', '0' and '#'.
- * An asterisk is supported for field width as well as precision.
+ * 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.
*
- * Length modifiers 'h' (short int), 'l' (long int),
- * and 'll' (long long int) are supported.
- * NOTE:
- * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
- * length modifier 'll' is recognized but treated the same as 'l',
- * which may cause argument value truncation! Defining
- * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
- * handles length modifier 'll'. long long int is a language extension
- * which may not be portable.
+ * 1998-03-05 Michael Elkins <me at mutt.org> for Mutt 0.90.8:
*
- * Conversion of numeric data (conversion specifiers d, u, o, x, X, p)
- * with length modifiers (none or h, l, ll) is left to the system routine
- * sprintf, but all handling of flags, field width and precision as well as
- * c and s conversions is done very carefully by this portable routine.
- * If a string precision (truncation) is specified (e.g. %.8s) it is
- * guaranteed the string beyond the specified precision will not be referenced.
+ * 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.
*
- * Length modifiers h, l and ll are ignored for c and s conversions (data
- * types wint_t and wchar_t are not supported).
+ * 1998-01-27 Thomas Roessler <roessler at does-not-exist.org> for Mutt 0.89i:
*
- * The following common synonyms for conversion characters are supported:
- * - i is a synonym for d
- * - D is a synonym for ld, explicit length modifiers are ignored
- * - U is a synonym for lu, explicit length modifiers are ignored
- * - O is a synonym for lo, explicit length modifiers are ignored
- * The D, O and U conversion characters are nonstandard, they are supported
- * for backward compatibility only, and should not be used for new code.
+ * The PGP code was using unsigned hexadecimal formats. Unfortunately,
+ * unsigned formats simply didn't work.
*
- * The following is specifically NOT supported:
- * - flag ' (thousands' grouping character) is recognized but ignored
- * - numeric conversion specifiers: f, e, E, g, G and synonym F,
- * as well as the new a and A conversion specifiers
- * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
- * - wide character/string conversions: lc, ls, and nonstandard
- * synonyms C and S
- * - writeback of converted string length: conversion character n
- * - the n$ specification for direct reference to n-th argument
- * - locales
+ * 1997-10-22 Brandon Long <blong at fiction.net> for Mutt 0.87.1:
*
- * It is permitted for str_m to be zero, and it is permitted to specify NULL
- * pointer for resulting string argument if str_m is zero (as per ISO C99).
+ * 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
*
- * The return value is the number of characters which would be generated
- * for the given input, excluding the trailing null. If this value
- * is greater or equal to str_m, not all characters from the result
- * have been stored in str, output bytes beyond the (str_m-1) -th character
- * are discarded. If str_m is greater than zero it is guaranteed
- * the resulting string will be null-terminated.
+ * 2996-09-15 Brandon Long <blong at fiction.net> for Mutt 0.43:
*
- * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
- * but is different from some older and vendor implementations,
- * and is also different from XPG, XSH5, SUSv2 specifications.
- * For historical discussion on changes in the semantics and standards
- * of snprintf see printf(3) man page in the Linux programmers manual.
+ * 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
*
- * Routines asprintf and vasprintf return a pointer (in the ptr argument)
- * to a buffer sufficiently large to hold the resulting string. This pointer
- * should be passed to free(3) to release the allocated storage when it is
- * no longer needed. If sufficient space cannot be allocated, these functions
- * will return -1 and set ptr to be a NULL pointer. These two routines are a
- * GNU C library extensions (glibc).
+ * - 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
*
- * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
- * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
- * characters into the allocated output string, the last character in the
- * allocated buffer then gets the terminating null. If the formatted string
- * length (the return value) is greater than or equal to the str_m argument,
- * the resulting string was truncated and some of the formatted characters
- * were discarded. These routines present a handy way to limit the amount
- * of allocated memory to some sane value.
+ * 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:
*
- * AVAILABILITY
- * http://www.ijs.si/software/snprintf/
+ * HAVE_VSNPRINTF
+ * HAVE_SNPRINTF
+ * HAVE_VASPRINTF
+ * HAVE_ASPRINTF
+ * HAVE_STDARG_H
+ * HAVE_STDDEF_H
+ * HAVE_STDINT_H
+ * HAVE_STDLIB_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
*
- * REVISION HISTORY
- * 1999-04 V0.9 Mark Martinec
- * - initial version, some modifications after comparing printf
- * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10,
- * and checking how Perl handles sprintf (differently!);
- * 1999-04-09 V1.0 Mark Martinec <mark.martinec at ijs.si>
- * - added main test program, fixed remaining inconsistencies,
- * added optional (long long int) support;
- * 1999-04-12 V1.1 Mark Martinec <mark.martinec at ijs.si>
- * - support the 'p' conversion (pointer to void);
- * - if a string precision is specified
- * make sure the string beyond the specified precision
- * will not be referenced (e.g. by strlen);
- * 1999-04-13 V1.2 Mark Martinec <mark.martinec at ijs.si>
- * - support synonyms %D=%ld, %U=%lu, %O=%lo;
- * - speed up the case of long format string with few conversions;
- * 1999-06-30 V1.3 Mark Martinec <mark.martinec at ijs.si>
- * - fixed runaway loop (eventually crashing when str_l wraps
- * beyond 2^31) while copying format string without
- * conversion specifiers to a buffer that is too short
- * (thanks to Edwin Young <edwiny at autonomy.com> for
- * spotting the problem);
- * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
- * to snprintf.h
- * 2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec at ijs.si>
- * - relaxed license terms: The Artistic License now applies.
- * You may still apply the GNU GENERAL PUBLIC LICENSE
- * as was distributed with previous versions, if you prefer;
- * - changed REVISION HISTORY dates to use ISO 8601 date format;
- * - added vsnprintf (patch also independently proposed by
- * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
- * 2000-06-27 V2.1 Mark Martinec <mark.martinec at ijs.si>
- * - removed POSIX check for str_m<1; value 0 for str_m is
- * allowed by ISO C99 (and GNU C library 2.1) - (pointed out
- * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie).
- * Besides relaxed license this change in standards adherence
- * is the main reason to bump up the major version number;
- * - added nonstandard routines asnprintf, vasnprintf, asprintf,
- * vasprintf that dynamically allocate storage for the
- * resulting string; these routines are not compiled by default,
- * see comments where NEED_V?ASN?PRINTF macros are defined;
- * - autoconf contributed by Caolan McNamara
- * 2000-10-06 V2.2 Mark Martinec <mark.martinec at ijs.si>
- * - BUG FIX: the %c conversion used a temporary variable
- * that was no longer in scope when referenced,
- * possibly causing incorrect resulting character;
- * - BUG FIX: make precision and minimal field width unsigned
- * to handle huge values (2^31 <= n < 2^32) correctly;
- * also be more careful in the use of signed/unsigned/size_t
- * internal variables - probably more careful than many
- * vendor implementations, but there may still be a case
- * where huge values of str_m, precision or minimal field
- * could cause incorrect behaviour;
- * - use separate variables for signed/unsigned arguments,
- * and for short/int, long, and long long argument lengths
- * to avoid possible incompatibilities on certain
- * computer architectures. Also use separate variable
- * arg_sign to hold sign of a numeric argument,
- * to make code more transparent;
- * - some fiddling with zero padding and "0x" to make it
- * Linux compatible;
- * - systematically use macros fast_memcpy and fast_memset
- * instead of case-by-case hand optimization; determine some
- * breakeven string lengths for different architectures;
- * - terminology change: 'format' -> 'conversion specifier',
- * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")',
- * 'alternative form' -> 'alternate form',
- * 'data type modifier' -> 'length modifier';
- * - several comments rephrased and new ones added;
- * - make compiler not complain about 'credits' defined but
- * not used;
+ * 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 <config.h>
+#endif /* HAVE_CONFIG_H */
-/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf.
- *
- * If HAVE_SNPRINTF is defined this module will not produce code for
- * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well,
- * causing this portable version of snprintf to be called portable_snprintf
- * (and portable_vsnprintf).
- */
-/* #define HAVE_SNPRINTF */
+#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_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 */
-/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and
- * vsnprintf but you would prefer to use the portable routine(s) instead.
- * In this case the portable routine is declared as portable_snprintf
- * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf')
- * is defined to expand to 'portable_v?snprintf' - see file snprintf.h .
- * Defining this macro is only useful if HAVE_SNPRINTF is also defined,
- * but does does no harm if defined nevertheless.
- */
-/* #define PREFER_PORTABLE_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 */
-/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support
- * data type (long long int) and length modifier 'll' (e.g. %lld).
- * If undefined, 'll' is recognized but treated as a single 'l'.
- *
- * If the system's sprintf does not handle 'll'
- * the SNPRINTF_LONGLONG_SUPPORT must not be defined!
- *
- * This is off by default as (long long int) is a language extension.
- */
-/* #define SNPRINTF_LONGLONG_SUPPORT */
+#if !HAVE_VASPRINTF
+#define vasprintf rpl_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 */
-/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf.
- * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly,
- * otherwise both snprintf and vsnprintf routines will be defined
- * and snprintf will be a simple wrapper around vsnprintf, at the expense
- * of an extra procedure call.
+#if !HAVE_VSNPRINTF
+#define vsnprintf rpl_vsnprintf
+#include <errno.h> /* For ERANGE and errno. */
+#include <limits.h> /* For *_MAX. */
+#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
+#else
+#define LDOUBLE double
+#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.
*/
-/* #define NEED_SNPRINTF_ONLY */
+#ifndef UPTRDIFF_T
+#define UPTRDIFF_T PTRDIFF_T
+#endif /* !defined(UPTRDIFF_T) */
-/* Define NEED_V?ASN?PRINTF macros if you need library extension
- * routines asprintf, vasprintf, asnprintf, vasnprintf respectively,
- * and your system library does not provide them. They are all small
- * wrapper routines around portable_vsnprintf. Defining any of the four
- * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY
- * and turns on PREFER_PORTABLE_SNPRINTF.
- *
- * Watch for name conflicts with the system library if these routines
- * are already present there.
- *
- * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as
- * specified by C99, to be able to traverse the same list of arguments twice.
- * I don't know of any other standard and portable way of achieving the same.
- * With some versions of gcc you may use __va_copy(). You might even get away
- * with "ap2 = ap", in this case you must not call va_end(ap2) !
- * #define va_copy(ap2,ap) ap2 = ap
+/*
+ * 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.
*/
-/* #define NEED_ASPRINTF */
-/* #define NEED_ASNPRINTF */
-/* #define NEED_VASPRINTF */
-/* #define NEED_VASNPRINTF */
+#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) */
-/* Define the following macros if desired:
- * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE,
- * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE,
- * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE,
- * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE,
- *
- * - For portable applications it is best not to rely on peculiarities
- * of a given implementation so it may be best not to define any
- * of the macros that select compatibility and to avoid features
- * that vary among the systems.
- *
- * - Selecting compatibility with more than one operating system
- * is not strictly forbidden but is not recommended.
- *
- * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE .
- *
- * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is
- * documented in a sprintf man page on a given operating system
- * and actually adhered to by the system's sprintf (but not on
- * most other operating systems). It may also refer to and enable
- * a behaviour that is declared 'undefined' or 'implementation specific'
- * in the man page but a given implementation behaves predictably
- * in a certain way.
- *
- * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf
- * that contradicts the sprintf man page on the same operating system.
- *
- * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE
- * conditionals take into account all idiosyncrasies of a particular
- * implementation, there may be other incompatibilities.
+/*
+ * 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
-
-/* ============================================= */
-/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
-/* ============================================= */
+/* 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)
-#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
-#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+/* 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
-#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
-# if defined(NEED_SNPRINTF_ONLY)
-# undef NEED_SNPRINTF_ONLY
-# endif
-# if !defined(PREFER_PORTABLE_SNPRINTF)
-# define PREFER_PORTABLE_SNPRINTF
-# endif
-#endif
+#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 != 0.0 && x + x == x)
+#endif /* !defined(ISINF) */
-#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
-#define SOLARIS_COMPATIBLE
-#endif
+#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)
-#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
-#define HPUX_COMPATIBLE
-#endif
+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);
-#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
-#define DIGITAL_UNIX_COMPATIBLE
-#endif
+extern int errno;
-#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
-#define PERL_COMPATIBLE
-#endif
+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++;
-#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
-#define LINUX_COMPATIBLE
-#endif
+ /*
+ * 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;
-#include <sys/types.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <assert.h>
-#include <errno.h>
+ 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 'F':
+ flags |= PRINT_F_UP;
+ case 'a':
+ /* Not yet supported, we'll use "%f". */
+ /* 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 'E':
+ flags |= PRINT_F_UP;
+ /* FALLTHROUGH */
+ case 'e':
+ flags |= PRINT_F_TYPE_E;
+ 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 'G':
+ flags |= PRINT_F_UP;
+ /* FALLTHROUGH */
+ case 'g':
+ flags |= PRINT_F_TYPE_G;
+ if (cflags == PRINT_C_LDOUBLE)
+ fvalue = va_arg(args, LDOUBLE);
+ else
+ fvalue = va_arg(args, double);
+ /*
+ * If the precision is zero, it is treated as
+ * one (cf. C99: 7.19.6.1, 8).
+ */
+ if (precision == 0)
+ precision = 1;
+ 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';
-#ifdef isdigit
-#undef isdigit
-#endif
-#define isdigit(c) ((c) >= '0' && (c) <= '9')
+ if (overflow || len >= INT_MAX) {
+ errno = overflow ? EOVERFLOW : ERANGE;
+ return -1;
+ }
+ return (int)len;
+}
-/* For copying strings longer or equal to 'breakeven_point'
- * it is more efficient to call memcpy() than to do it inline.
- * The value depends mostly on the processor architecture,
- * but also on the compiler and its optimization capabilities.
- * The value is not critical, some small value greater than zero
- * will be just fine if you don't care to squeeze every drop
- * of performance out of the code.
- *
- * Small values favor memcpy, large values favor inline code.
- */
-#if defined(__alpha__) || defined(__alpha)
-# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */
-#endif
-#if defined(__i386__) || defined(__i386)
-# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */
-#endif
-#if defined(__hppa)
-# define breakeven_point 10 /* HP-PA - gcc */
-#endif
-#if defined(__sparc__) || defined(__sparc)
-# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */
-#endif
+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);
-/* some other values of possible interest: */
-/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */
-/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */
+ if (value == NULL) /* We're forgiving. */
+ value = "(null)";
-#ifndef breakeven_point
-# define breakeven_point 6 /* some reasonable one-size-fits-all value */
-#endif
+ /* If a precision was specified, don't read the string past it. */
+ for (strln = 0; value[strln] != '\0' &&
+ (noprecision || strln < precision); strln++)
+ continue;
-#define fast_memcpy(d,s,n) \
- { register size_t nn = (size_t)(n); \
- if (nn >= breakeven_point) memcpy((d), (s), nn); \
- else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
- register char *dd; register const char *ss; \
- for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
+ if ((padlen = width - strln) < 0)
+ padlen = 0;
+ if (flags & PRINT_F_MINUS) /* Left justify. */
+ padlen = -padlen;
-#define fast_memset(d,c,n) \
- { register size_t nn = (size_t)(n); \
- if (nn >= breakeven_point) memset((d), (int)(c), nn); \
- else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
- register char *dd; register const int cc=(int)(c); \
- for (dd=(d); nn>0; nn--) *dd++ = cc; } }
+ 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++;
+ }
+}
-/* prototypes */
+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 defined(NEED_ASPRINTF)
-int asprintf (char **ptr, const char *fmt, /*args*/ ...);
-#endif
-#if defined(NEED_VASPRINTF)
-int vasprintf (char **ptr, const char *fmt, va_list ap);
-#endif
-#if defined(NEED_ASNPRINTF)
-int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
-#endif
-#if defined(NEED_VASNPRINTF)
-int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
-#endif
+ 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 = ' ';
+ }
-#if defined(HAVE_SNPRINTF)
-/* declare our portable snprintf routine under name portable_snprintf */
-/* declare our portable vsnprintf routine under name portable_vsnprintf */
-#else
-/* declare our portable routines under names snprintf and vsnprintf */
-#define portable_snprintf snprintf
-#if !defined(NEED_SNPRINTF_ONLY)
-#define portable_vsnprintf vsnprintf
-#endif
-#endif
+ pos = convert(uvalue, iconvert, sizeof(iconvert), base,
+ flags & PRINT_F_UP);
-#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
-int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
-#if !defined(NEED_SNPRINTF_ONLY)
-int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
-#endif
-#endif
+ 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;
+ }
+ }
-/* declarations */
+ if (separators) /* Get the number of group separators we'll print. */
+ separators = getnumsep(pos);
-static char credits[] = "\n\
-@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec at ijs.si>\n\
-@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
-@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
+ 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 defined(NEED_ASPRINTF)
-int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
- va_list ap;
- size_t str_m;
- int str_l;
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (spadlen < 0)
+ spadlen = 0;
- *ptr = NULL;
- va_start(ap, fmt); /* measure the required size */
- str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
- va_end(ap);
- assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
- *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
- if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
- else {
- int str_l2;
- va_start(ap, fmt);
- str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
- va_end(ap);
- assert(str_l2 == str_l);
- }
- return str_l;
+ /*
+ * 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++;
+ }
}
-#endif
-#if defined(NEED_VASPRINTF)
-int vasprintf(char **ptr, const char *fmt, va_list ap) {
- size_t str_m;
- int str_l;
+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[4]; /* "e-12" (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 */
- *ptr = NULL;
- { va_list ap2;
- va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
- str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
- va_end(ap2);
- }
- assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
- *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
- if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
- else {
- int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
- assert(str_l2 == str_l);
- }
- return str_l;
+ /*
+ * 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) {
+ /*
+ * 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 4. So, the
+ * econvert buffer can hold e.g. "e+99" and "e-99". We don't
+ * support an exponent which contains more than two digits.
+ * Therefore, the following stores are safe.
+ */
+ epos = convert(exponent, econvert, 2, 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++;
+ }
}
-#endif
-#if defined(NEED_ASNPRINTF)
-int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
- va_list ap;
- int str_l;
+static void
+printsep(char *str, size_t *len, size_t size)
+{
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+ struct lconv *lc = localeconv();
+ int i;
- *ptr = NULL;
- va_start(ap, fmt); /* measure the required size */
- str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
- va_end(ap);
- assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
- if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
- /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
- if (str_m == 0) { /* not interested in resulting string, just return size */
- } else {
- *ptr = (char *) malloc(str_m);
- if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
- else {
- int str_l2;
- va_start(ap, fmt);
- str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
- va_end(ap);
- assert(str_l2 == str_l);
- }
- }
- return str_l;
+ 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, ',');
}
-#endif
-#if defined(NEED_VASNPRINTF)
-int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
- int str_l;
+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();
- *ptr = NULL;
- { va_list ap2;
- va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
- str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
- va_end(ap2);
- }
- assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
- if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
- /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
- if (str_m == 0) { /* not interested in resulting string, just return size */
- } else {
- *ptr = (char *) malloc(str_m);
- if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
- else {
- int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
- assert(str_l2 == str_l);
- }
- }
- return str_l;
+ /* 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;
}
-#endif
-/*
- * If the system does have snprintf and the portable routine is not
- * specifically required, this module produces no code for snprintf/vsnprintf.
- */
-#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+static int
+getexponent(LDOUBLE value)
+{
+ LDOUBLE tmp = (value >= 0.0) ? value : -value;
+ int exponent = 0;
-#if !defined(NEED_SNPRINTF_ONLY)
-int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
- va_list ap;
- int str_l;
+ /*
+ * We check for 99 > exponent > -99 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 > -99)
+ tmp *= 10;
+ while (tmp >= 10.0 && ++exponent < 99)
+ tmp /= 10;
- va_start(ap, fmt);
- str_l = portable_vsnprintf(str, str_m, fmt, ap);
- va_end(ap);
- return str_l;
+ return exponent;
}
-#endif
-#if defined(NEED_SNPRINTF_ONLY)
-int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
-#else
-int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
-#endif
+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;
-#if defined(NEED_SNPRINTF_ONLY)
- va_list ap;
-#endif
- size_t str_l = 0;
- const char *p = fmt;
+ /* We return an unterminated buffer with the digits in reverse order. */
+ do {
+ buf[pos++] = digits[value % base];
+ value /= base;
+ } while (value != 0 && pos < size);
-/* In contrast with POSIX, the ISO C99 now says
- * that str can be NULL and str_m can be 0.
- * This is more useful than the old: if (str_m < 1) return -1; */
+ return (int)pos;
+}
-#if defined(NEED_SNPRINTF_ONLY)
- va_start(ap, fmt);
-#endif
- if (!p) p = "";
- while (*p) {
- if (*p != '%') {
- /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */
- /* but the following code achieves better performance for cases
- * where format string is long and contains few conversions */
- const char *q = strchr(p+1,'%');
- size_t n = !q ? strlen(p) : (q-p);
- if (str_l < str_m) {
- size_t avail = str_m-str_l;
- fast_memcpy(str+str_l, p, (n>avail?avail:n));
- }
- p += n; str_l += n;
- } else {
- const char *starting_p;
- size_t min_field_width = 0, precision = 0;
- int zero_padding = 0, precision_specified = 0, justify_left = 0;
- int alternate_form = 0, force_sign = 0;
- int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
- the ' ' flag should be ignored. */
- char length_modifier = '\0'; /* allowed values: \0, h, l, L */
- char tmp[32];/* temporary buffer for simple numeric->string conversion */
+static UINTMAX_T
+cast(LDOUBLE value)
+{
+ UINTMAX_T result;
- const char *str_arg; /* string address in case of string argument */
- size_t str_arg_l; /* natural field width of arg without padding
- and sign */
- unsigned char uchar_arg;
- /* unsigned char argument value - only defined for c conversion.
- N.B. standard explicitly states the char argument for
- the c conversion is unsigned */
+ /*
+ * 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;
- size_t number_of_zeros_to_pad = 0;
- /* number of zeros to be inserted for numeric conversions
- as required by the precision or minimal field width */
+ 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;
+}
- size_t zero_padding_insertion_ind = 0;
- /* index into tmp where zero padding is to be inserted */
+static UINTMAX_T
+myround(LDOUBLE value)
+{
+ UINTMAX_T intpart = cast(value);
- char fmt_spec = '\0';
- /* current conversion specifier character */
+ return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
+}
- str_arg = credits;/* just to make compiler happy (defined but not used)*/
- str_arg = NULL;
- starting_p = p; p++; /* skip '%' */
- /* parse flags */
- while (*p == '0' || *p == '-' || *p == '+' ||
- *p == ' ' || *p == '#' || *p == '\'') {
- switch (*p) {
- case '0': zero_padding = 1; break;
- case '-': justify_left = 1; break;
- case '+': force_sign = 1; space_for_positive = 0; break;
- case ' ': force_sign = 1;
- /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
-#ifdef PERL_COMPATIBLE
- /* ... but in Perl the last of ' ' and '+' applies */
- space_for_positive = 1;
-#endif
- break;
- case '#': alternate_form = 1; break;
- case '\'': break;
- }
- p++;
- }
- /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
+static LDOUBLE
+mypow10(int exponent)
+{
+ LDOUBLE result = 1;
- /* parse field width */
- if (*p == '*') {
- int j;
- p++; j = va_arg(ap, int);
- if (j >= 0) min_field_width = j;
- else { min_field_width = -j; justify_left = 1; }
- } else if (isdigit((int)(*p))) {
- /* size_t could be wider than unsigned int;
- make sure we treat argument like common implementations do */
- unsigned int uj = *p++ - '0';
- while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
- min_field_width = uj;
- }
- /* parse precision */
- if (*p == '.') {
- p++; precision_specified = 1;
- if (*p == '*') {
- int j = va_arg(ap, int);
- p++;
- if (j >= 0) precision = j;
- else {
- precision_specified = 0; precision = 0;
- /* NOTE:
- * Solaris 2.6 man page claims that in this case the precision
- * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page
- * claim that this case should be treated as unspecified precision,
- * which is what we do here.
- */
- }
- } else if (isdigit((int)(*p))) {
- /* size_t could be wider than unsigned int;
- make sure we treat argument like common implementations do */
- unsigned int uj = *p++ - '0';
- while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
- precision = uj;
- }
- }
- /* parse 'h', 'l' and 'll' length modifiers */
- if (*p == 'h' || *p == 'l') {
- length_modifier = *p; p++;
- if (length_modifier == 'l' && *p == 'l') { /* double l = long long */
-#ifdef SNPRINTF_LONGLONG_SUPPORT
- length_modifier = '2'; /* double l encoded as '2' */
-#else
- length_modifier = 'l'; /* treat it as a single 'l' */
-#endif
- p++;
- }
- }
- fmt_spec = *p;
- /* common synonyms: */
- switch (fmt_spec) {
- case 'i': fmt_spec = 'd'; break;
- case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
- case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
- case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
- default: break;
- }
- /* get parameter value, do initial processing */
- switch (fmt_spec) {
- case '%': /* % behaves similar to 's' regarding flags and field widths */
- case 'c': /* c behaves similar to 's' regarding flags and field widths */
- case 's':
- length_modifier = '\0'; /* wint_t and wchar_t not supported */
- /* the result of zero padding flag with non-numeric conversion specifier*/
- /* is undefined. Solaris and HPUX 10 does zero padding in this case, */
- /* Digital Unix and Linux does not. */
-#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
- zero_padding = 0; /* turn zero padding off for string conversions */
-#endif
- str_arg_l = 1;
- switch (fmt_spec) {
- case '%':
- str_arg = p; break;
- case 'c': {
- int j = va_arg(ap, int);
- uchar_arg = (unsigned char) j; /* standard demands unsigned char */
- str_arg = (const char *) &uchar_arg;
- break;
- }
- case 's':
- str_arg = va_arg(ap, const char *);
- if (!str_arg) str_arg_l = 0;
- /* make sure not to address string beyond the specified precision !!! */
- else if (!precision_specified) str_arg_l = strlen(str_arg);
- /* truncate string if necessary as requested by precision */
- else if (precision == 0) str_arg_l = 0;
- else {
- /* memchr on HP does not like n > 2^31 !!! */
- const char *q = memchr(str_arg, '\0',
- precision <= 0x7fffffff ? precision : 0x7fffffff);
- str_arg_l = !q ? precision : (q-str_arg);
- }
- break;
- default: break;
- }
- break;
- case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
- /* NOTE: the u, o, x, X and p conversion specifiers imply
- the value is unsigned; d implies a signed value */
+ while (exponent > 0) {
+ result *= 10;
+ exponent--;
+ }
+ while (exponent < 0) {
+ result /= 10;
+ exponent++;
+ }
+ return result;
+}
+#endif /* !HAVE_VSNPRINTF */
- int arg_sign = 0;
- /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
- +1 if greater than zero (or nonzero for unsigned arguments),
- -1 if negative (unsigned argument is never negative) */
+#if !HAVE_VASPRINTF
+#if NEED_MYMEMCPY
+void *
+mymemcpy(void *dst, void *src, size_t len)
+{
+ const char *from = src;
+ char *to = dst;
- int int_arg = 0; unsigned int uint_arg = 0;
- /* only defined for length modifier h, or for no length modifiers */
+ /* No need for optimization, we use this only to replace va_copy(3). */
+ while (len-- > 0)
+ *to++ = *from++;
+ return dst;
+}
+#endif /* NEED_MYMEMCPY */
- long int long_arg = 0; unsigned long int ulong_arg = 0;
- /* only defined for length modifier l */
+int
+rpl_vasprintf(char **ret, const char *format, va_list ap)
+{
+ size_t size;
+ int len;
+ va_list aq;
- void *ptr_arg = NULL;
- /* pointer argument value -only defined for p conversion */
+ 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 */
-#ifdef SNPRINTF_LONGLONG_SUPPORT
- long long int long_long_arg = 0;
- unsigned long long int ulong_long_arg = 0;
- /* only defined for length modifier ll */
-#endif
- if (fmt_spec == 'p') {
- /* HPUX 10: An l, h, ll or L before any other conversion character
- * (other than d, i, u, o, x, or X) is ignored.
- * Digital Unix:
- * not specified, but seems to behave as HPUX does.
- * Solaris: If an h, l, or L appears before any other conversion
- * specifier (other than d, i, u, o, x, or X), the behavior
- * is undefined. (Actually %hp converts only 16-bits of address
- * and %llp treats address as 64-bit data which is incompatible
- * with (void *) argument on a 32-bit system).
- */
-#ifdef SOLARIS_COMPATIBLE
-# ifdef SOLARIS_BUG_COMPATIBLE
- /* keep length modifiers even if it represents 'll' */
-# else
- if (length_modifier == '2') length_modifier = '\0';
-# endif
+#if !HAVE_SNPRINTF
+#if HAVE_STDARG_H
+int
+rpl_snprintf(char *str, size_t size, const char *format, ...)
#else
- length_modifier = '\0';
-#endif
- ptr_arg = va_arg(ap, void *);
- if (ptr_arg != NULL) arg_sign = 1;
- } else if (fmt_spec == 'd') { /* signed */
- switch (length_modifier) {
- case '\0':
- case 'h':
- /* It is non-portable to specify a second argument of char or short
- * to va_arg, because arguments seen by the called function
- * are not char or short. C converts char and short arguments
- * to int before passing them to a function.
- */
- int_arg = va_arg(ap, int);
- if (int_arg > 0) arg_sign = 1;
- else if (int_arg < 0) arg_sign = -1;
- break;
- case 'l':
- long_arg = va_arg(ap, long int);
- if (long_arg > 0) arg_sign = 1;
- else if (long_arg < 0) arg_sign = -1;
- break;
-#ifdef SNPRINTF_LONGLONG_SUPPORT
- case '2':
- long_long_arg = va_arg(ap, long long int);
- if (long_long_arg > 0) arg_sign = 1;
- else if (long_long_arg < 0) arg_sign = -1;
- break;
-#endif
- }
- } else { /* unsigned */
- switch (length_modifier) {
- case '\0':
- case 'h':
- uint_arg = va_arg(ap, unsigned int);
- if (uint_arg) arg_sign = 1;
- break;
- case 'l':
- ulong_arg = va_arg(ap, unsigned long int);
- if (ulong_arg) arg_sign = 1;
- break;
-#ifdef SNPRINTF_LONGLONG_SUPPORT
- case '2':
- ulong_long_arg = va_arg(ap, unsigned long long int);
- if (ulong_long_arg) arg_sign = 1;
- break;
-#endif
- }
- }
- str_arg = tmp; str_arg_l = 0;
- /* NOTE:
- * For d, i, u, o, x, and X conversions, if precision is specified,
- * the '0' flag should be ignored. This is so with Solaris 2.6,
- * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
- */
-#ifndef PERL_COMPATIBLE
- if (precision_specified) zero_padding = 0;
-#endif
- if (fmt_spec == 'd') {
- if (force_sign && arg_sign >= 0)
- tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
- /* leave negative numbers for sprintf to handle,
- to avoid handling tricky cases like (short int)(-32768) */
-#ifdef LINUX_COMPATIBLE
- } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
- tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
-#endif
- } else if (alternate_form) {
- if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
- { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
- /* alternate form should have no effect for p conversion, but ... */
-#ifdef HPUX_COMPATIBLE
- else if (fmt_spec == 'p'
- /* HPUX 10: for an alternate form of p conversion,
- * a nonzero result is prefixed by 0x. */
-#ifndef HPUX_BUG_COMPATIBLE
- /* Actually it uses 0x prefix even for a zero value. */
- && arg_sign != 0
-#endif
- ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
-#endif
- }
- zero_padding_insertion_ind = str_arg_l;
- if (!precision_specified) precision = 1; /* default precision is 1 */
- if (precision == 0 && arg_sign == 0
-#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
- && fmt_spec != 'p'
- /* HPUX 10 man page claims: With conversion character p the result of
- * converting a zero value with a precision of zero is a null string.
- * Actually HP returns all zeroes, and Linux returns "(nil)". */
-#endif
- ) {
- /* converted to null string */
- /* When zero value is formatted with an explicit precision 0,
- the resulting formatted string is empty (d, i, u, o, x, X, p). */
- } else {
- char f[5]; int f_l = 0;
- f[f_l++] = '%'; /* construct a simple format string for sprintf */
- if (!length_modifier) { }
- else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
- else f[f_l++] = length_modifier;
- f[f_l++] = fmt_spec; f[f_l++] = '\0';
- if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
- else if (fmt_spec == 'd') { /* signed */
- switch (length_modifier) {
- case '\0':
- case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break;
- case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
-#ifdef SNPRINTF_LONGLONG_SUPPORT
- case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
-#endif
- }
- } else { /* unsigned */
- switch (length_modifier) {
- case '\0':
- case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break;
- case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
-#ifdef SNPRINTF_LONGLONG_SUPPORT
- case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
-#endif
- }
- }
- /* include the optional minus sign and possible "0x"
- in the region before the zero padding insertion point */
- if (zero_padding_insertion_ind < str_arg_l &&
- tmp[zero_padding_insertion_ind] == '-') {
- zero_padding_insertion_ind++;
- }
- if (zero_padding_insertion_ind+1 < str_arg_l &&
- tmp[zero_padding_insertion_ind] == '0' &&
- (tmp[zero_padding_insertion_ind+1] == 'x' ||
- tmp[zero_padding_insertion_ind+1] == 'X') ) {
- zero_padding_insertion_ind += 2;
- }
- }
- { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
- if (alternate_form && fmt_spec == 'o'
-#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */
- && (str_arg_l > 0)
-#endif
-#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */
+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
- /* unless zero is already the first character */
- && !(zero_padding_insertion_ind < str_arg_l
- && tmp[zero_padding_insertion_ind] == '0')
-#endif
- ) { /* assure leading zero for alternate-form octal numbers */
- if (!precision_specified || precision < num_of_digits+1) {
- /* precision is increased to force the first character to be zero,
- except if a zero value is formatted with an explicit precision
- of zero */
- precision = num_of_digits+1; precision_specified = 1;
- }
- }
- /* zero padding to specified precision? */
- if (num_of_digits < precision)
- number_of_zeros_to_pad = precision - num_of_digits;
- }
- /* zero padding to specified minimal field width? */
- if (!justify_left && zero_padding) {
- int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
- if (n > 0) number_of_zeros_to_pad += n;
- }
- break;
- }
- default: /* unrecognized conversion specifier, keep format string as-is*/
- zero_padding = 0; /* turn zero padding off for non-numeric convers. */
-#ifndef DIGITAL_UNIX_COMPATIBLE
- justify_left = 1; min_field_width = 0; /* reset flags */
-#endif
-#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
- /* keep the entire format string unchanged */
- str_arg = starting_p; str_arg_l = p - starting_p;
- /* well, not exactly so for Linux, which does something inbetween,
- * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */
-#else
- /* discard the unrecognized conversion, just keep *
- * the unrecognized conversion character */
- str_arg = p; str_arg_l = 0;
-#endif
- if (*p) str_arg_l++; /* include invalid conversion specifier unchanged
- if not at end-of-string */
- break;
- }
- if (*p) p++; /* step over the just processed conversion specifier */
- /* insert padding to the left as requested by min_field_width;
- this does not include the zero padding in case of numerical conversions*/
- if (!justify_left) { /* left padding with blank or zero */
- int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
- if (n > 0) {
- if (str_l < str_m) {
- size_t avail = str_m-str_l;
- fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
- }
- str_l += n;
- }
- }
- /* zero padding as requested by the precision or by the minimal field width
- * for numeric conversions required? */
- if (number_of_zeros_to_pad <= 0) {
- /* will not copy first part of numeric right now, *
- * force it to be copied later in its entirety */
- zero_padding_insertion_ind = 0;
- } else {
- /* insert first part of numerics (sign or '0x') before zero padding */
- int n = zero_padding_insertion_ind;
- if (n > 0) {
- if (str_l < str_m) {
- size_t avail = str_m-str_l;
- fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
- }
- str_l += n;
- }
- /* insert zero padding as requested by the precision or min field width */
- n = number_of_zeros_to_pad;
- if (n > 0) {
- if (str_l < str_m) {
- size_t avail = str_m-str_l;
- fast_memset(str+str_l, '0', (n>avail?avail:n));
- }
- str_l += n;
- }
- }
- /* insert formatted string
- * (or as-is conversion specifier for unknown conversions) */
- { int n = str_arg_l - zero_padding_insertion_ind;
- if (n > 0) {
- if (str_l < str_m) {
- size_t avail = str_m-str_l;
- fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
- (n>avail?avail:n));
- }
- str_l += n;
- }
- }
- /* insert right padding */
- if (justify_left) { /* right blank padding to the field width */
- int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
- if (n > 0) {
- if (str_l < str_m) {
- size_t avail = str_m-str_l;
- fast_memset(str+str_l, ' ', (n>avail?avail:n));
- }
- str_l += n;
- }
- }
- }
- }
-#if defined(NEED_SNPRINTF_ONLY)
- va_end(ap);
-#endif
- if (str_m > 0) { /* make sure the string is null-terminated
- even at the expense of overwriting the last character
- (shouldn't happen, but just in case) */
- str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
- }
- /* Return the number of characters formatted (excluding trailing null
- * character), that is, the number of characters that would have been
- * written to the buffer if it were large enough.
- *
- * The value of str_l should be returned, but str_l is of unsigned type
- * size_t, and snprintf is int, possibly leading to an undetected
- * integer overflow, resulting in a negative return value, which is illegal.
- * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
- * Should errno be set to EOVERFLOW and EOF returned in this case???
- */
- return (int) str_l;
+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
+#endif /* !HAVE_ASPRINTF */
+#else /* Dummy declaration to avoid empty translation unit warnings. */
+int main(void);
+#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */
+
+#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 textwidth=80: */
Modified: csw/mgar/pkg/watch/trunk/files/snprintf.h
===================================================================
--- csw/mgar/pkg/watch/trunk/files/snprintf.h 2009-03-04 21:54:05 UTC (rev 3586)
+++ csw/mgar/pkg/watch/trunk/files/snprintf.h 2009-03-04 22:52:35 UTC (rev 3587)
@@ -1,26 +1,29 @@
-#ifndef _PORTABLE_SNPRINTF_H_
-#define _PORTABLE_SNPRINTF_H_
+/* $Id: system.h,v 1.1.1.1 2008/01/06 03:24:00 holger Exp $ */
-#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
-#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+#ifndef SYSTEM_H
+#define SYSTEM_H
-#ifdef HAVE_SNPRINTF
-#include <stdio.h>
-#else
-extern int snprintf(char *, size_t, const char *, /*args*/ ...);
-extern int vsnprintf(char *, size_t, const char *, va_list);
-#endif
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
-#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)
-extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
-extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
-#define snprintf portable_snprintf
-#define vsnprintf portable_vsnprintf
-#endif
-
-extern int asprintf (char **ptr, const char *fmt, /*args*/ ...);
-extern int vasprintf (char **ptr, const char *fmt, va_list ap);
-extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
-extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
-
-#endif
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#if !HAVE_VSNPRINTF
+int rpl_vsnprintf(char *, size_t, const char *, va_list);
+#define vsnprintf rpl_vsnprintf
+#endif /* !HAVE_VSNPRINTF */
+#if !HAVE_SNPRINTF
+int rpl_snprintf(char *, size_t, const char *, ...);
@@ Diff output truncated at 100000 characters. @@
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the devel
mailing list