snprintf.c
+ - a portable implementation of snprintf,
+ including
+vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
+
+
+
snprintf is a routine to convert numeric and string arguments
+to formatted strings. It is similar to sprintf(3) provided in a
+system's C library, yet it requires an additional argument - the buffer
+size - and it guarantees never to store anything beyond the given buffer,
+regardless of the format or arguments to be formatted. Some newer
+operating systems do provide snprintf in their C library,
+but many do not or do provide an inadequate (slow or idiosyncratic)
+version, which calls for a portable implementation of this routine.
+
+
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.
+
+
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.
+
+
+
Supported conversion specifiers and data types
+
+
This snprintf only supports the following conversion specifiers:
+s, c, d, o, u, 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.
+
+
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.
+
+
+
Conversion of numeric data (conversion specifiers d, o, u, 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.
+
+
Length modifiers h, l and ll are ignored for c and s conversions
+(data types wint_t and wchar_t are not supported).
+
+
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 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
+
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
+
+
+
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).
+
+
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.
+
+
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.
+
+
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).
+
+
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.
+
+
There is a very low-traffic mailing list snprintf-announce@ijs.si
+where announcements about new versions will be posted
+as well as warnings about threatening bugs if discovered.
+The posting is restricted to snprintf developer(s).
+
+
You can also subscribe to the list by mailing
+the command SUBSCRIBE either in the subject or in the message body
+to the address snprintf-announce-request@ijs.si . You will be asked for
+confirmation before subscription will be effective.
+
+
The list of members is only accessible to the list administrator,
+so there is no need for concern about automatic e-mail address gatherers.
+
+
Questions about the mailing list and concerns for the attention
+of a person should be sent to snprintf-announce-admin@ijs.si
+
+
There is no general discussion list about portable snprintf
+at the moment. Please send comments and suggestion to the author.
+
+
+
Revision history
+
+
Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade.
+
+
+
1999-06-30 V1.3 Mark Martinec <mark.martinec@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@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@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;
+
added vsnprintf (patch also independently proposed by
+ Caolán McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
+
+
+
2000-06-27 V2.1 Mark Martinec <mark.martinec@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 Caolán 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 Caolán McNamara
+
+
+
2000-10-06 V2.2 Mark Martinec <mark.martinec@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;
+
+
+
+
Other implementations of snprintf
+
+
I am aware of some other (more or less) portable implementations
+of snprintf. I do not claim they are free software - please refer
+to their respective copyright and licensing terms.
+If you know of other versions please let
+me know.
+
+
+
a very thorough implementation (src/util_snprintf.c)
+by the Apache Group distributed with the
+Apache web server
+- http://www.apache.org/ .
+Does its own floating point conversions using routines
+ecvt(3), fcvt(3) and gcvt(3) from the standard C library
+or from the GNU libc.
+
+ This is from the code:
+
+This software [...] was originally based
+on public domain software written at the
+National Center
+for Supercomputing Applications, University of Illinois,
+Urbana-Champaign.
+[...] This code is based on, and used with the permission of,
+the SIO stdio-replacement strx_* functions by Panos Tsirigotis
+<panos@alumni.cs.colorado.edu> for xinetd.
+
+
+
QCI
+Utilities use a modified version of snprintf from the Apache group.
+
+
implementations as distributed with
+OpenBSD,
+FreeBSD, and
+NetBSD
+are all wrappers to vfprintf.c, which is derived from software
+contributed to Berkeley by Chris Torek.
+
+
implementation from Prof. Patrick Powell
+<papowell@sdsu.edu>,
+Dept. Electrical and Computer Engineering, San Diego State University,
+San Diego, CA 92182-1309, published in
+Bugtraq
+archives for 3rd quarter (Jul-Aug) 1995.
+No floating point conversions.
+
+
Brandon Long's
+<blong@fiction.net>
+modified version
+of Prof. Patrick Powell's snprintf with contributions from others.
+With minimal floating point support.
+
+
implementation (src/snprintf.c) as distributed with
+sendmail - http://www.sendmail.org/
+is a cleaned up Prof. Patrick Powell's version
+to compile properly and to support .precision and %lx.
+
+
implementation used by
+newlog
+(a replacement for syslog(3)) made available by
+the SOS Corporation.
+Enabling floating point support is a compile-time option.
+
+
implementation by Michael Richardson
+<mcr@metis.milkyway.com>
+is available at
+http://sandelman.ottawa.on.ca/SSW/snp/snp.html.
+It is based on BSD44-lite's vfprintf() call, modified to function
+on SunOS. Needs internal routines from the 4.4 strtod (included),
+requires GCC to compile the long long (aka quad_t) portions.
+
+
implementation from Tomi Salo
+<ttsalo@ssh.fi>
+distributed with
+SSH 2.0
+Unix Server. Not in public domain.
+Floating point conversions done by system's sprintf.
+
+
+
+
diff -r 828f3afd5c76 -r 5a71d53d0228 snprintf_2.2/snprintf-orig.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/snprintf_2.2/snprintf-orig.c Sat Jan 19 19:14:36 2002 +0100
@@ -0,0 +1,1025 @@
+/*
+ * snprintf.c - a portable implementation of snprintf
+ *
+ * AUTHOR
+ * Mark Martinec , April 1999.
+ *
+ * Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Length modifiers h, l and ll are ignored for c and s conversions (data
+ * types wint_t and wchar_t are not supported).
+ *
+ * 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 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
+ *
+ * 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).
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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).
+ *
+ * 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.
+ *
+ * AVAILABILITY
+ * http://www.ijs.si/software/snprintf/
+ *
+ * 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
+ * - added main test program, fixed remaining inconsistencies,
+ * added optional (long long int) support;
+ * 1999-04-12 V1.1 Mark Martinec
+ * - 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
+ * - 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
+ * - 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 for
+ * spotting the problem);
+ * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
+ * to snprintf.h
+ * 2000-02-14 V2.0 (never released) Mark Martinec
+ * - 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
+ * - 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
+ * - 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;
+ */
+
+
+/* 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 */
+
+/* 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 */
+
+/* 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 */
+
+/* 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.
+ */
+/* #define NEED_SNPRINTF_ONLY */
+
+/* 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
+ */
+/* #define NEED_ASPRINTF */
+/* #define NEED_ASNPRINTF */
+/* #define NEED_VASPRINTF */
+/* #define NEED_VASNPRINTF */
+
+
+/* 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.
+ */
+
+
+
+/* ============================================= */
+/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
+/* ============================================= */
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#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
+
+#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
+#define SOLARIS_COMPATIBLE
+#endif
+
+#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
+#define HPUX_COMPATIBLE
+#endif
+
+#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
+#define DIGITAL_UNIX_COMPATIBLE
+#endif
+
+#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
+#define PERL_COMPATIBLE
+#endif
+
+#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
+#define LINUX_COMPATIBLE
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef isdigit
+#undef isdigit
+#endif
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+/* 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
+
+/* some other values of possible interest: */
+/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */
+/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */
+
+#ifndef breakeven_point
+# define breakeven_point 6 /* some reasonable one-size-fits-all value */
+#endif
+
+#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++; } }
+
+#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; } }
+
+/* prototypes */
+
+#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 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
+
+#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
+
+/* declarations */
+
+static char credits[] = "\n\
+@(#)snprintf.c, v2.2: Mark Martinec, \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";
+
+#if defined(NEED_ASPRINTF)
+int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
+ va_list ap;
+ size_t str_m;
+ int str_l;
+
+ *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;
+}
+#endif
+
+#if defined(NEED_VASPRINTF)
+int vasprintf(char **ptr, const char *fmt, va_list ap) {
+ size_t str_m;
+ int str_l;
+
+ *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;
+}
+#endif
+
+#if defined(NEED_ASNPRINTF)
+int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
+ va_list ap;
+ int str_l;
+
+ *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;
+}
+#endif
+
+#if defined(NEED_VASNPRINTF)
+int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
+ int str_l;
+
+ *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;
+}
+#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)
+
+#if !defined(NEED_SNPRINTF_ONLY)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
+ va_list ap;
+ int str_l;
+
+ va_start(ap, fmt);
+ str_l = portable_vsnprintf(str, str_m, fmt, ap);
+ va_end(ap);
+ return str_l;
+}
+#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
+
+#if defined(NEED_SNPRINTF_ONLY)
+ va_list ap;
+#endif
+ size_t str_l = 0;
+ const char *p = fmt;
+
+/* 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; */
+
+#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 */
+
+ 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 */
+
+ 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 */
+
+ size_t zero_padding_insertion_ind = 0;
+ /* index into tmp where zero padding is to be inserted */
+
+ char fmt_spec = '\0';
+ /* current conversion specifier character */
+
+ 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. */
+
+ /* 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 */
+
+ 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) */
+
+ int int_arg = 0; unsigned int uint_arg = 0;
+ /* only defined for length modifier h, or for no length modifiers */
+
+ long int long_arg = 0; unsigned long int ulong_arg = 0;
+ /* only defined for length modifier l */
+
+ void *ptr_arg = NULL;
+ /* pointer argument value -only defined for p conversion */
+
+#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
+#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" */
+#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;
+}
+#endif
diff -r 828f3afd5c76 -r 5a71d53d0228 snprintf_2.2/snprintf.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/snprintf_2.2/snprintf.c Sat Jan 19 19:14:36 2002 +0100
@@ -0,0 +1,1032 @@
+#include
+#define NEED_ASPRINTF
+#define NEED_VASPRINTF
+/*
+ * snprintf.c - a portable implementation of snprintf
+ *
+ * AUTHOR
+ * Mark Martinec , April 1999.
+ *
+ * Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Length modifiers h, l and ll are ignored for c and s conversions (data
+ * types wint_t and wchar_t are not supported).
+ *
+ * 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 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
+ *
+ * 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).
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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).
+ *
+ * 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.
+ *
+ * AVAILABILITY
+ * http://www.ijs.si/software/snprintf/
+ *
+ * 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
+ * - added main test program, fixed remaining inconsistencies,
+ * added optional (long long int) support;
+ * 1999-04-12 V1.1 Mark Martinec
+ * - 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
+ * - 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
+ * - 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 for
+ * spotting the problem);
+ * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
+ * to snprintf.h
+ * 2000-02-14 V2.0 (never released) Mark Martinec
+ * - 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
+ * - 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
+ * - 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;
+ */
+
+
+/* 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 */
+
+/* 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 */
+
+/* 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 */
+
+/* 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.
+ */
+/* #define NEED_SNPRINTF_ONLY */
+
+/* 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
+ */
+#ifndef va_copy
+#define va_copy(ap2,ap) ap2 = ap
+#endif
+
+/* #define NEED_ASPRINTF */
+/* #define NEED_ASNPRINTF */
+/* #define NEED_VASPRINTF */
+/* #define NEED_VASNPRINTF */
+
+
+/* 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.
+ */
+
+
+
+/* ============================================= */
+/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
+/* ============================================= */
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#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
+
+#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
+#define SOLARIS_COMPATIBLE
+#endif
+
+#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
+#define HPUX_COMPATIBLE
+#endif
+
+#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
+#define DIGITAL_UNIX_COMPATIBLE
+#endif
+
+#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
+#define PERL_COMPATIBLE
+#endif
+
+#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
+#define LINUX_COMPATIBLE
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef isdigit
+#undef isdigit
+#endif
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+/* 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
+
+/* some other values of possible interest: */
+/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */
+/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */
+
+#ifndef breakeven_point
+# define breakeven_point 6 /* some reasonable one-size-fits-all value */
+#endif
+
+#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++; } }
+
+#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; } }
+
+/* prototypes */
+
+#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 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
+
+#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
+
+/* declarations */
+
+static char credits[] = "\n\
+@(#)snprintf.c, v2.2: Mark Martinec, \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";
+
+#if defined(NEED_ASPRINTF)
+int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
+ va_list ap;
+ size_t str_m;
+ int str_l;
+
+ *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;
+}
+#endif
+
+#if defined(NEED_VASPRINTF)
+int vasprintf(char **ptr, const char *fmt, va_list ap) {
+ size_t str_m;
+ int str_l;
+
+ *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;
+}
+#endif
+
+#if defined(NEED_ASNPRINTF)
+int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
+ va_list ap;
+ int str_l;
+
+ *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;
+}
+#endif
+
+#if defined(NEED_VASNPRINTF)
+int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
+ int str_l;
+
+ *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;
+}
+#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)
+
+#if !defined(NEED_SNPRINTF_ONLY)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
+ va_list ap;
+ int str_l;
+
+ va_start(ap, fmt);
+ str_l = portable_vsnprintf(str, str_m, fmt, ap);
+ va_end(ap);
+ return str_l;
+}
+#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
+
+#if defined(NEED_SNPRINTF_ONLY)
+ va_list ap;
+#endif
+ size_t str_l = 0;
+ const char *p = fmt;
+
+/* 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; */
+
+#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 */
+
+ 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 */
+
+ 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 */
+
+ size_t zero_padding_insertion_ind = 0;
+ /* index into tmp where zero padding is to be inserted */
+
+ char fmt_spec = '\0';
+ /* current conversion specifier character */
+
+ 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. */
+
+ /* 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 */
+
+ 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) */
+
+ int int_arg = 0; unsigned int uint_arg = 0;
+ /* only defined for length modifier h, or for no length modifiers */
+
+ long int long_arg = 0; unsigned long int ulong_arg = 0;
+ /* only defined for length modifier l */
+
+ void *ptr_arg = NULL;
+ /* pointer argument value -only defined for p conversion */
+
+#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
+#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" */
+#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) {
+ ssize_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) {
+ ssize_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) {
+ ssize_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) {
+ ssize_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) {
+ ssize_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;
+}
+#endif
diff -r 828f3afd5c76 -r 5a71d53d0228 snprintf_2.2/snprintf.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/snprintf_2.2/snprintf.h Sat Jan 19 19:14:36 2002 +0100
@@ -0,0 +1,26 @@
+#ifndef _PORTABLE_SNPRINTF_H_
+#define _PORTABLE_SNPRINTF_H_
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#ifdef HAVE_SNPRINTF
+#include
+#else
+extern int snprintf(char *, size_t, const char *, /*args*/ ...);
+extern int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+#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
diff -r 828f3afd5c76 -r 5a71d53d0228 snprintf_2.2/test.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/snprintf_2.2/test.c Sat Jan 19 19:14:36 2002 +0100
@@ -0,0 +1,689 @@
+/*
+ * test.c - test a portable implementation of snprintf
+ *
+ * AUTHOR
+ * Mark Martinec , April 1999.
+ *
+ * Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * NOTE: This test program is a QUICK and DIRTY tool
+ * ===== used while testing and benchmarking my portable snprintf.
+ * Certain data types are not fully supported, certain test
+ * cases were fabricated during testing by modifying the code
+ * or running it by specifying test parameters in the command line.
+ *
+ * You are on your own if you want to use this test program!
+ */
+
+/* If no command arguments are specified do the exhaustive test.
+ * This takes a long time. You may want to reduce the fw and fp
+ * upper limits in the for loops.
+ * You may also reduce the number of test elements in the array iargs.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#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
+
+#ifdef HAVE_SNPRINTF
+extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+#else
+extern int snprintf(char *, size_t, const char *, /*args*/ ...);
+extern int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+#ifndef HAVE_SNPRINTF
+#define portable_snprintf snprintf
+#define portable_vsnprintf vsnprintf
+#endif
+
+#ifndef CLOCKS_PER_SEC
+#define CLOCKS_PER_SEC 100
+#endif
+
+#define min(a,b) ((a)<(b) ? (a) : (b))
+#define max(a,b) ((a)>(b) ? (a) : (b))
+
+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);
+
+#ifndef NEED_SNPRINTF_ONLY
+int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, ...) {
+ va_list ap;
+ int str_l;
+
+ va_start(ap, fmt);
+ str_l = vsnprintf(str, str_m, fmt, ap);
+ va_end(ap);
+ return str_l;
+}
+#endif
+
+#ifdef NEED_VASPRINTF
+int wrap_vasprintf(char **ptr, const char *fmt, /*args*/ ...);
+int wrap_vasprintf(char **ptr, const char *fmt, ...) {
+ va_list ap;
+ int str_l;
+
+ va_start(ap, fmt);
+ str_l = vasprintf(ptr, fmt, ap);
+ va_end(ap);
+ return str_l;
+}
+#endif
+
+#ifdef NEED_VASNPRINTF
+int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
+int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, ...) {
+ va_list ap;
+ int str_l;
+
+ va_start(ap, fmt);
+ str_l = vasnprintf(ptr, str_m, fmt, ap);
+ va_end(ap);
+ return str_l;
+}
+#endif
+
+int main(int argc, char *argv[]) {
+ char str1[256], str2[256];
+#ifdef HAVE_SNPRINTF
+ char str3[256];
+#endif
+ int len1, len2, len3;
+ int bad = 0;
+ size_t str_m = 20; /* declared str size */
+
+ if (0) {
+ /* benchmarking */
+ const int cnt = 100000;
+ size_t size;
+ char str[40000];
+ time_t t0,t;
+ int j,len,l1,l2;
+ char *p;
+ int breakpoint;
+
+ size = 18000;
+
+ printf("\n\nsize = %d\n", (int)size);
+ p = malloc(size); assert(p);
+ memset(p,'h',size); p[size-1] = '\0';
+ t0 = clock();
+
+ printf("\ndetermine breakeven point to see when it is worth\n");
+ printf("calling memcpy and when to do inline string copy\n");
+ printf("str_l, memcpy, inline\n");
+ for (breakpoint=0; breakpoint<=35; breakpoint++) {
+ register size_t nnn = (size_t)breakpoint;
+ printf("%5ld", nnn);
+ for (j=10*cnt; j>0; j--) memcpy(str, p, nnn);
+ t = clock(); printf(" %1.3f", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=10*cnt; j>0; j--) {
+ register size_t nn = (size_t)breakpoint;
+ if (nn > 0) {
+ register char *dd; register const char *ss;
+ for (ss=p, dd=str; nn>0; nn--) *dd++ = *ss++;
+ }
+ }
+ t = clock(); printf(" %1.3f\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ }
+
+ printf("\nmeasuring time to SKIP a long format with no conversions\n");
+ p[0] = '%'; p[1] = 's';
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p,"1234567890");
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,(size_t)8,p,"1234567890");
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ p[0] = p[1] = 'h';
+
+ printf("\nmeasuring time to copy a long format with no conversions\n");
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p);
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ for (j=cnt; j>0; j--) sprintf(str,p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ printf("\nmeasuring time to copy a long format with one conversion\n");
+ p[size-10] = '%';
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p);
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ for (j=cnt; j>0; j--) sprintf(str,p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ printf("\nmeasuring string argument copy speed\n");
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%.18000s",p);
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%.18000s",p);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ for (j=cnt; j>0; j--) sprintf(str,"%.18000s",p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ printf("\nmeasuring left padding speed\n");
+ p[0] = '\0';
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%-18000s",p);
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%-18000s",p);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ for (j=cnt; j>0; j--) sprintf(str,"%-18000s",p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ printf("\nmeasuring right padding speed\n");
+ p[0] = '\0';
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%18000s",p);
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%18000s",p);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ for (j=cnt; j>0; j--) sprintf(str,"%18000s",p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ printf("\nmeasuring zero padding speed\n");
+ for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%018000d",1);
+ t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%018000d",1);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+ assert(l1==l2);
+ for (j=cnt; j>0; j--) sprintf(str,"%018000d",1);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ printf("\nmeasuring system's sprintf to efficiently handle truncated strings\n");
+ memset(p,'h',size); p[size-1] = '\0';
+ t0 = clock();
+ for (j=cnt; j>0; j--) len = strlen(p);
+ printf("len = %d\n", len);
+ t = clock(); printf("t_strlen = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+/* test if the system sprintf scans the whole string (e.g. by strlen)
+ * before recognizing this was a bad idea since the format specified
+ * a truncated string precision, e.g. "%.8s" .
+ */
+ for (j=cnt; j>0; j--) sprintf(str,"%.2s",p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+#ifdef HAVE_SNPRINTF
+ for (j=cnt; j>0; j--) snprintf(str,sizeof(str),"%.2s",p);
+ t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+#endif
+ for (j=cnt; j>0; j--) portable_snprintf(str,sizeof(str),"%.2s",p);
+ t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+ free(p);
+ return 0;
+ }
+
+
+/* preliminary halfhearted test */
+{
+ const char fmt[] = "Bla%.4s%05iHE%%%-50sTail";
+ char *ptr4=0, *ptr5=0, *ptr6=0, *ptr7=0;
+ int len1f;
+ char str_full[256];
+
+ printf("\npreliminary test: snprintf\n");
+ len1 = snprintf(str1, str_m, fmt, "abcdef",-12,"str");
+ len1f = snprintf(str_full, sizeof(str_full), fmt, "abcdef",-12,"str");
+ assert(len1f==len1);
+ assert(memcmp(str1,str_full,min(len1,str_m-1)) == 0);
+ assert(str1[str_m-1] == '\0');
+ assert(str_full[sizeof(str_full)-1] == '\0');
+
+#ifndef NEED_SNPRINTF_ONLY
+ printf("preliminary test: vsnprintf\n");
+ len2 = wrap_vsnprintf(str2, str_m, fmt, "abcdef",-12,"str");
+ assert(len2==len1);
+ assert(memcmp(str1,str2,min(len1+1,str_m)) == 0);
+ assert(str2[str_m-1] == '\0');
+#endif
+
+#ifdef NEED_ASPRINTF
+ printf("preliminary test: asprintf\n");
+ len4 = asprintf(&ptr4, fmt, "abcdef",-12,"str");
+ assert(ptr4);
+ assert(len4==len1);
+ assert(memcmp(str_full,ptr4,min(len4+1,sizeof(str_full))) == 0);
+ assert(ptr4[len4] == '\0');
+#endif
+
+#ifdef NEED_ASNPRINTF
+ printf("preliminary test: asnprintf\n");
+ len5 = asnprintf(&ptr5, str_m, fmt, "abcdef",-12,"str");
+ assert(ptr5);
+ assert(len5==len1);
+ assert(memcmp(str1,ptr5,min(len5+1,str_m)) == 0);
+ assert(ptr5[len5] == '\0');
+#endif
+
+#ifdef NEED_VASPRINTF
+ printf("preliminary test: vasprintf\n");
+ len6 = wrap_vasprintf(&ptr6, fmt, "abcdef",-12,"str");
+ assert(ptr6);
+ assert(len6==len1);
+ assert(memcmp(str_full,ptr6,min(len6+1,sizeof(str_full))) == 0);
+ assert(ptr6[len6] == '\0');
+#endif
+
+#ifdef NEED_VASNPRINTF
+ printf("preliminary test: vasnprintf\n");
+ len7 = wrap_vasnprintf(&ptr7, str_m, fmt, "abcdef",-12,"str");
+ assert(ptr7);
+ assert(len7==len1);
+ assert(memcmp(str1,ptr7,min(len7+1,str_m)) == 0);
+ assert(ptr7[len7] == '\0');
+#endif
+
+ if (ptr4) free(ptr4);
+ if (ptr5) free(ptr5);
+ if (ptr6) free(ptr6);
+ if (ptr7) free(ptr7);
+}
+
+/* second preliminary halfhearted test */
+{
+ printf("\nsecond preliminary test:\n");
+
+ printf("test 0a\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "");
+ len2 = sprintf (str2, "");
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 0b\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "YK");
+ len2 = sprintf (str2, "YK");
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 1\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%+d",0);
+ len2 = sprintf (str2, "%+d",0);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 2\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.2147483647s", "13");
+ len2 = sprintf (str2, "%.2147483647s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 3a\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.2147483648s", "13");
+ len2 = sprintf (str2, "%.2147483648s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 3b\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.2147483649s", "13");
+ len2 = sprintf (str2, "%.2147483649s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 4\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%-.2147483647s", "13");
+ len2 = sprintf (str2, "%-.2147483647s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 5\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%-.2147483648s", "13");
+ len2 = sprintf (str2, "%-.2147483648s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 6\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.4294967295s", "13");
+ len2 = sprintf (str2, "%.4294967295s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 7\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.4294967296s", "13");
+ len2 = sprintf (str2, "%.4294967296s", "13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+
+ printf("test 12\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483647,"13");
+ len2 = sprintf (str2, "%.*s", 2147483647,"13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 13\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483648U,"13");
+ len2 = sprintf (str2, "%.*s", 2147483648U,"13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 14\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483647,"13");
+ len2 = sprintf (str2, "%-.*s", 2147483647,"13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 15\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483648U,"13");
+ len2 = sprintf (str2, "%-.*s", 2147483648U,"13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 16\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967295U,"13");
+ len2 = sprintf (str2, "%.*s", 4294967295U,"13");
+ printf("len1=%d, len2=%d\n", len1,len2);
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 17\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967296U,"13");
+/* len2 = sprintf (str2, "%.*s", 4294967296U,"13"); */ /* core dumps on HPUX */
+/* assert(len1==len2);
+ * assert(memcmp(str1,str2,(size_t)len1) == 0);
+ */
+
+
+ printf("test 95\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%c",'A');
+ len2 = sprintf (str2, "%c",'A');
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 96\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%10c",'A');
+ len2 = sprintf (str2, "%10c",'A');
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 97\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%-10c",'A');
+ len2 = sprintf (str2, "%-10c",'A');
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 98\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%.10c",'A');
+ len2 = sprintf (str2, "%.10c",'A');
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+ printf("test 99\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, sizeof(str1), "%-.10c",'A');
+ len2 = sprintf (str2, "%-.10c",'A');
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+
+ printf("test 100\n");
+ memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2));
+ len1 = snprintf(str1, (size_t)8, "blaBhb%shehe%cX","ABCD",'1');
+ len2 = sprintf (str2, "blaBhb%shehe%cX","ABCD",'1');
+ assert(len1==len2);
+ assert(memcmp(str1,str2,(size_t)7) == 0);
+ assert(str1[7] == '\0');
+ assert(memcmp(str1+14,str2+16,(size_t)(len1-16)) == 0);
+
+}
+
+ /* testing for correctness and compatibility */
+ if (argc >= 3) {
+ char *c; int alldigits = 1;
+ for (c=argv[2]; *c; c++)
+ if (! (*c == '-' || (*c >= '0' && *c <= '9'))) alldigits = 0;
+ if (alldigits) {
+ int j = atoi(argv[2]);
+ len1 = portable_snprintf(str1, str_m, argv[1], j, 3);
+ len2 = sprintf(str2, argv[1], j, 3);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, argv[1], j, 3);
+#endif
+ } else {
+ len1 = portable_snprintf(str1, str_m, argv[1], argv[2], 3);
+ len2 = sprintf(str2, argv[1], argv[2], 3);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, argv[1], argv[2], 3);
+#endif
+ }
+ printf("portable: |%s| len = %d\n", str1, len1);
+ printf("sys sprintf: |%s| len = %d\n", str2, len2);
+#ifdef HAVE_SNPRINTF
+ printf("sys snprintf: |%s| len = %d\n", str3, len3);
+#endif
+ } else { /* exhaustive testing */
+
+ const char flags[] = "+- 0#"; /* set of test flags (including '\0')*/
+ int flags_l = strlen(flags);
+
+ const char fspec[] = "scdpoxXuiy"; /* set of test formats (including '\0') */
+ int fspec_l = strlen(fspec);
+
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+ const char datatype[] = " hl2"; /* set of datatypes */
+#else
+ const char datatype[] = " hl"; /* set of datatypes */
+#endif
+ int datatype_l = strlen(datatype);
+
+ const long int iargs[] = /* set of numeric test arguments */
+ { 0,1,9,10,28,99,100,127,128,129,998,1000,32767,32768,32769,
+ -1,-9,-10,-28,-99,-100,-127,-128,-129,
+ -998,-1000,-32767,-32768,-32769 };
+ int iargs_l = sizeof(iargs)/sizeof(iargs[0]);
+ const char *sargs[] = /* set of string test arguments */
+ { "", "a", "0", "-ab", "abcde", "abcdefghijk mnopqrstuv" };
+ int sargs_l = sizeof(sargs)/sizeof(sargs[0]);
+
+ char fmt[256];
+ int fmt_l;
+ int a, fs, fl1, fl2, fl3, fl4, fl5, fw, fp, dt;
+
+ for (fs=0; fs<=fspec_l; fs++) { /* format specifier */
+ int strtype = (fspec[fs] == 's' || fspec[fs] == '%');
+ int args_l = (strtype ? sargs_l : iargs_l);
+
+ for (fw= -1; fw<=3; fw++) { /* minimal field width */
+
+ printf("Trying format %%");
+ if (fw >= 0) printf("%d", fw);
+ if (fspec[fs]) putchar(fspec[fs]);
+ putchar('\n');
+
+ for (fp= -2; fp<=3; fp++) { /* format field precision */
+
+ /* data type modifiers */
+ for (dt=0; dt < ((strtype||fspec[fs]=='c') ? 1 : datatype_l); dt++) {
+
+ int dataty = datatype[dt];
+
+ if (fspec[fs] == 'D' || fspec[fs] == 'U' || fspec[fs] == 'O')
+ dataty = 'l';
+
+ if (fspec[fs] == 'p' && dataty == '2') continue;
+
+ for (fl1=0; fl1<=flags_l; fl1++) { /* flags */
+ for (fl2=0; fl2<=flags_l; fl2++) {
+ for (fl3=0; fl3<=flags_l; fl3++) {
+ for (fl4=0; fl4<=flags_l; fl4++) {
+ for (fl5=0; fl5<=flags_l; fl5++) {
+
+ for (a=0; a= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fw);
+ if (fp >= -1) {
+ fmt[fmt_l++] = '.';
+ if (fp >= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fp);
+ }
+ if (dataty == '2')
+ { fmt[fmt_l++] = 'l'; fmt[fmt_l++] = 'l'; }
+ else if (dataty != ' ')
+ { fmt[fmt_l++] = dataty; }
+
+ if (fspec[fs]) fmt[fmt_l++] = fspec[fs];
+ fmt[fmt_l++] = '\0';
+
+ if (a==0 && fl1==flags_l && fl2==flags_l && fl3==flags_l
+ && fl4==flags_l && fl5==flags_l) printf("%s\n", fmt);
+
+#ifdef HAVE_SNPRINTF
+ memset(str1,'G',sizeof(str1));
+ memset(str2,'G',sizeof(str2));
+ memset(str3,'G',sizeof(str3));
+#endif
+ len1 = len2 = len3 = 0;
+ if (strtype) {
+ len1 = portable_snprintf(str1, str_m, fmt, sargs[a]);
+ len2 = sprintf(str2, fmt, sargs[a]);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, fmt, sargs[a]);
+#endif
+ } else if (fspec[fs] == 'p') {
+ len1 = portable_snprintf(str1, str_m, fmt, (void *)iargs[a]);
+ len2 = sprintf(str2, fmt, (void *)iargs[a]);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, fmt, (void *)iargs[a]);
+#endif
+ } else {
+ switch (dataty) {
+ case '\0':
+ len1 = portable_snprintf(str1, str_m, fmt, (int) iargs[a]);
+ len2 = sprintf(str2, fmt, (int) iargs[a]);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, fmt, (int) iargs[a]);
+#endif
+ break;
+ case 'h':
+ len1 = portable_snprintf(str1, str_m, fmt, (short int)iargs[a]);
+ len2 = sprintf(str2, fmt, (short int)iargs[a]);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, fmt, (short int)iargs[a]);
+#endif
+ break;
+ case 'l':
+ len1 = portable_snprintf(str1, str_m, fmt, (long int)iargs[a]);
+ len2 = sprintf(str2, fmt, (long int)iargs[a]);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, fmt, (long int)iargs[a]);
+#endif
+ break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+ case '2':
+ len1 = portable_snprintf(str1, str_m, fmt, (long long int)iargs[a]);
+ len2 = sprintf(str2, fmt, (long long int)iargs[a]);
+#ifdef HAVE_SNPRINTF
+ len3 = snprintf(str3, str_m, fmt, (long long int)iargs[a]);
+#endif
+ break;
+#endif
+ }
+ }
+
+ if (0) {
+#ifdef HAVE_SNPRINTF
+ } else if (len1 != len3 ||
+ memcmp(str1,str3,min(len1+20,sizeof(str1))) != 0) {
+ bad = 1;
+ if (strtype) printf("\n2: %s, <%s>\n", fmt, sargs[a]);
+ else printf("\n2: %s, %ld\n", fmt, iargs[a]);
+ printf("portable: |%s| len = %d\n", str1, len1);
+ printf("sys sprintf: |%s| len = %d\n", str2, len2);
+ printf("sys snprintf: |%s| len = %d\n", str3, len3);
+#else
+ } else if (len1 != len2 ||
+ (len1>0 && memcmp(str1,str2,min(len1,str_m)-1) != 0)) {
+ bad = 1;
+ if (strtype) printf("\n1: %s, <%s>\n", fmt, sargs[a]);
+ else printf("\n1: %s, %ld\n", fmt, iargs[a]);
+ printf("portable: |%s| len = %d\n", str1, len1);
+ printf("sys sprintf: |%s| len = %d\n", str2, len2);
+#endif
+ }
+ if (bad) return(1);
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ return (bad?1:0);
+}
diff -r 828f3afd5c76 -r 5a71d53d0228 tokenizer.c
--- a/tokenizer.c Sun Apr 01 01:43:46 2001 +0200
+++ b/tokenizer.c Sat Jan 19 19:14:36 2002 +0100
@@ -112,6 +112,12 @@
if(tokz->ungetc!=-1){
c=tokz->ungetc;
tokz->ungetc=-1;
+ }else if (tokz->flags&TOKZ_READ_FROM_BUFFER) {
+ assert(tokz->buffer.data!=NULL);
+ if (tokz->buffer.pos==tokz->buffer.len)
+ c=EOF;
+ else
+ c=tokz->buffer.data[tokz->buffer.pos++];
}else{
c=getc(tokz->file);
}
@@ -578,6 +584,7 @@
{
int c, c2, e;
+ if (!(tokz->flags&TOKZ_READ_FROM_BUFFER))
assert(tokz->file!=NULL);
tok_free(tok);
@@ -804,6 +811,9 @@
tokz->nest_lvl=0;
tokz->filestack_n=0;
tokz->filestack=NULL;
+ tokz->buffer.data=0;
+ tokz->buffer.len=0;
+ tokz->buffer.pos=0;
return tokz;
}
@@ -838,6 +848,27 @@
return tokz;
}
+Tokenizer *tokz_prepare_buffer(char *buffer, int len)
+{
+ Tokenizer *tokz;
+ char old=0;
+
+ tokz=tokz_create();
+ if (len>0) {
+ old=buffer[len-1];
+ buffer[len-1]='\0';
+ }
+
+ tokz->flags|=TOKZ_READ_FROM_BUFFER;
+ tokz->buffer.data=scopy(buffer);
+ tokz->buffer.len=len>0 ? len : strlen(tokz->buffer.data);
+ tokz->buffer.pos=0;
+
+ if (old>0)
+ buffer[len-1]=old;
+
+ return tokz;
+}
/*
* File close