This is more actively maintained, straightforward to integrate now that we have Gnulib integration, and simplifies groff's overall licensing.
* bootstrap.conf (gnulib_modules): Add `snprintf' and `vsnprintf'. * configure.ac: Remove local checks for `snprintf' and `vsnprintf'. * Makefile.am: Remove various comments related to local checks for `snprintf' and `vsnprintf'. * src/include/lib.h: Remove declarations of `snprintf' and `vsnprintf'. * src/libs/libgroff/libgroff.am (EXTRA_DIST): Remove `src/libs/snprintf/snprintf.c'. * src/libs/snprintf/snprintf.c: Remove. * LICENSES: Remove section about `src/libs/snprintf/snprintf.c'. * MANIFEST: Remove entry for `src/libs/libxutil'. --- LICENSES | 10 - MANIFEST | 1 - Makefile.am | 9 +- bootstrap.conf | 2 + configure.ac | 6 +- src/include/lib.h | 10 - src/libs/libgroff/libgroff.am | 6 +- src/libs/snprintf/snprintf.c | 1131 ----------------------------------------- 8 files changed, 6 insertions(+), 1169 deletions(-) delete mode 100644 src/libs/snprintf/snprintf.c diff --git a/LICENSES b/LICENSES index 2471a001..7cd93c4a 100644 --- a/LICENSES +++ b/LICENSES @@ -36,16 +36,6 @@ hasn't been assigned to the FSF). Source Files ------------ - . The file `src/libs/snprintf/snprintf.c', written by Mark Martinec - <mark.marti...@ijs.si>. - - Please look into snprintf.c for the copyright message. - - The complete snprintf package together with documentation is available - from - - http://www.ijs.si/software/snprintf/ . - . The `grn' preprocessor, written by Barry Roitblat <ba...@rentonww.com> and David Slattengren <slatt...@xinet.com>. These files have been part of the original Berkeley ditroff distribution, without AT&T code, and diff --git a/MANIFEST b/MANIFEST index 2bec20fe..4fe3b383 100644 --- a/MANIFEST +++ b/MANIFEST @@ -101,7 +101,6 @@ the groff source distribution. libdriver Parser for intermediate output and postprocessor code. libgroff Library for general support functions used everywhere. libxutil Utility functions for xditview and xtotroff. - snprintf An implementation of snprintf() and friends. ./src/preproc Preprocessors. eqn Mathematical formulae. diff --git a/Makefile.am b/Makefile.am index 7de03f58..2d235f81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -397,14 +397,12 @@ man7dir=$(manroot)/man$(man7ext) # HAVE_PUTENV if you have putenv() # HAVE_RENAME if you have rename() # HAVE_SETLOCALE if you have setlocale() -# HAVE_SNPRINTF if you have snprintf() # HAVE_STRCASECMP if you have strcasecmp() # HAVE_STRNCASECMP if you have strncasecmp() # HAVE_STRERROR if you have strerror() # HAVE_STRSEP if you have strsep() # HAVE_STRTOL if you have strtol() # HAVE_SYMLINK if you have symlink() -# HAVE_VSNPRINTF if you have vsnprintf() # # NEED_DECLARATION_GETTIMEOFTODAY # if your C++ <sys/time.h> doesn't declare @@ -415,8 +413,6 @@ man7dir=$(manroot)/man$(man7ext) # NEED_DECLARATION_PUTENV if your C++ <stdlib.h> doesn't declare # putenv() # NEED_DECLARATION_RAND if your C++ <stdlib.h> doesn't declare rand() -# NEED_DECLARATION_SNPRINTF if your C++ <stdio.h> doesn't declare -# snprintf() # NEED_DECLARATION_SRAND if your C++ <stdlib.h> doesn't declare srand() # NEED_DECLARATION_STRCASECMP if your C++ <string.h> doesn't declare # strcasecmp() @@ -425,8 +421,6 @@ man7dir=$(manroot)/man$(man7ext) # strncasecmp() # NEED_DECLARATION_VFPRINTF if your C++ <stdio.h> doesn't declare # vfprintf() -# NEED_DECLARATION_VSNPRINTF if your C++ <stdio.h> doesn't declare -# vsnprintf() # # HAVE_DECL_GETC_UNLOCKED if you have getc_unlocked() # HAVE_DECL_SYS_SIGLIST if you have sys_siglist[] @@ -461,12 +455,11 @@ man7dir=$(manroot)/man$(man7ext) # Include # -# {fmod,getcwd,mkstemp,putenv,snprintf,strcasecmp, +# {fmod,getcwd,mkstemp,putenv,strcasecmp, # strerror,strncasecmp,strtol}.$(OBJEXT) # # LIBOBJS # in LIBOBJS if your C library is missing the corresponding function. -# vsnprintf is defined in the snprintf.$(OBJEXT) module. # `CCC' is the compiler for C++ (.cpp) files. CCC=@CXX@ diff --git a/bootstrap.conf b/bootstrap.conf index deda4f60..6fd28de3 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -41,6 +41,8 @@ gnulib_modules=" non-recursive-gnulib-prefix-hack wcwidth fprintf-posix + snprintf + vsnprintf " # Name of the Makefile.am diff --git a/configure.ac b/configure.ac index bf46a4e4..42bfe87e 100644 --- a/configure.ac +++ b/configure.ac @@ -91,12 +91,10 @@ GROFF_NEED_DECLARATION([popen]) GROFF_NEED_DECLARATION([pclose]) GROFF_NEED_DECLARATION([putenv]) GROFF_NEED_DECLARATION([rand]) -GROFF_NEED_DECLARATION([snprintf]) GROFF_NEED_DECLARATION([srand]) GROFF_NEED_DECLARATION([strcasecmp]) GROFF_NEED_DECLARATION([strncasecmp]) GROFF_NEED_DECLARATION([vfprintf]) -GROFF_NEED_DECLARATION([vsnprintf]) GROFF_SYS_NERR GROFF_SYS_ERRLIST GROFF_OSFCN_H @@ -128,10 +126,8 @@ AC_SUBST([LIBEXT],[$acl_libext]) AC_FUNC_MMAP saved_libs="$LIBS" LIBS="$LIBS $LIBC $LIBM" -AC_REPLACE_FUNCS([fmod getcwd putenv snprintf strcasecmp \ +AC_REPLACE_FUNCS([fmod getcwd putenv strcasecmp \ strerror strncasecmp strtol]) -# vsnprintf is in the same source file as snprintf -AC_CHECK_FUNCS([vsnprintf], [], [AC_LIBOBJ([snprintf])]) LIBS="$saved_libs" AC_CHECK_FUNCS([gettimeofday isatty kill rename setlocale strsep]) GROFF_MKSTEMP diff --git a/src/include/lib.h b/src/include/lib.h index 382f0597..1e4c1974 100644 --- a/src/include/lib.h +++ b/src/include/lib.h @@ -59,16 +59,6 @@ double groff_hypot(double, double); #include <stdarg.h> -/* HP-UX 10.20 and LynxOS 4.0.0 don't declare snprintf() */ -#if !defined(HAVE_SNPRINTF) || defined(NEED_DECLARATION_SNPRINTF) -extern "C" { int snprintf(char *, size_t, const char *, /*args*/ ...); } -#endif - -/* LynxOS 4.0.0 has snprintf() but no vsnprintf() */ -#if !defined(HAVE_VSNPRINTF) || defined(NEED_DECLARATION_VSNPRINTF) -extern "C" { int vsnprintf(char *, size_t, const char *, va_list); } -#endif - /* LynxOS 4.0.0 doesn't declare vfprintf() */ #ifdef NEED_DECLARATION_VFPRINTF extern "C" { int vfprintf(FILE *, const char *, va_list); } diff --git a/src/libs/libgroff/libgroff.am b/src/libs/libgroff/libgroff.am index b25d64cb..85ede5f1 100644 --- a/src/libs/libgroff/libgroff.am +++ b/src/libs/libgroff/libgroff.am @@ -79,8 +79,7 @@ libgroff_a_SOURCES = \ src/libs/libgroff/relocatable.h nodist_libgroff_a_SOURCES = src/libs/libgroff/version.cpp -# TODO: these .c files could be removed (use gnulib instead), same -# thing for snprintf. +# TODO: these .c files could be removed (use gnulib instead). EXTRA_DIST += \ src/libs/libgroff/mkstemp.cpp \ src/libs/libgroff/fmod.c \ @@ -93,8 +92,7 @@ EXTRA_DIST += \ src/libs/libgroff/config.charset \ src/libs/libgroff/ref-add.sin \ src/libs/libgroff/ref-del.sin \ - src/libs/libgroff/make-uniuni \ - src/libs/snprintf/snprintf.c + src/libs/libgroff/make-uniuni CLEANFILES += \ src/libs/libgroff/version.cpp \ diff --git a/src/libs/snprintf/snprintf.c b/src/libs/snprintf/snprintf.c deleted file mode 100644 index fc245f14..00000000 --- a/src/libs/snprintf/snprintf.c +++ /dev/null @@ -1,1131 +0,0 @@ -/* - * snprintf.c - a portable implementation of snprintf - * - * AUTHOR - * Mark Martinec <mark.marti...@ijs.si>, April 1999. - * - * Copyright 1999-2002 Mark Martinec. All rights reserved. - * - * TERMS AND CONDITIONS - * This program is free software; it is dual licensed, the terms of the - * "Frontier Artistic License" or the "GNU General Public License" - * can be chosen at your discretion. The chosen license then applies - * solely and in its entirety. Both licenses come 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 license for more details. - * - * You should have received a copy of the "Frontier Artistic License" - * with this Kit in the file named LICENSE.txt, and the copy of - * the "GNU General Public License" in the file named LICENSE-GPL.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; - * - works also with non-ASCII 8-bit character sets (e.g. EBCDIC) - * provided strings are '\0'-terminated. - * - * 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 and for the 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 <mark.marti...@ijs.si> - * - added main test program, fixed remaining inconsistencies, - * added optional (long long int) support; - * 1999-04-12 V1.1 Mark Martinec <mark.marti...@ijs.si> - * - support the 'p' conversion (pointer to void); - * - if a string precision is specified - * make sure the string beyond the specified precision - * will not be referenced (e.g. by strlen); - * 1999-04-13 V1.2 Mark Martinec <mark.marti...@ijs.si> - * - support synonyms %D=%ld, %U=%lu, %O=%lo; - * - speed up the case of long format string with few conversions; - * 1999-06-30 V1.3 Mark Martinec <mark.marti...@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 <edw...@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.marti...@ijs.si> - * - relaxed license terms: The Artistic License now applies. - * You may still apply the GNU GENERAL PUBLIC LICENSE - * as was distributed with previous versions, if you prefer; - * - changed REVISION HISTORY dates to use ISO 8601 date format; - * - added vsnprintf (patch also independently proposed by - * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) - * 2000-06-27 V2.1 Mark Martinec <mark.marti...@ijs.si> - * - removed POSIX check for str_m<1; value 0 for str_m is - * allowed by ISO C99 (and GNU C library 2.1) - (pointed out - * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). - * Besides relaxed license this change in standards adherence - * is the main reason to bump up the major version number; - * - added nonstandard routines asnprintf, vasnprintf, asprintf, - * vasprintf that dynamically allocate storage for the - * resulting string; these routines are not compiled by default, - * see comments where NEED_V?ASN?PRINTF macros are defined; - * - autoconf contributed by Caolan McNamara - * 2000-10-06 V2.2 Mark Martinec <mark.marti...@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; - * 2001-08 V2.3 Mark Martinec <mark.marti...@ijs.si> - * .. 2002-02 - writeback conversion specifier 'n' is now supported; - * - bump the size of a temporary buffer for simple - * numeric->string conversion from 32 to 48 characters - * in anticipation of 128-bit machines; - * - added #include <stddef.h> and <stdarg.h> to snprintf.h; - * - fixed one assert in test.c - * (thanks to Tuomo A Turunen for reporting this problem); - * - portability fix: use isdigit() provided with <ctype.h> - * and do not assume character set is ASCII - call strtoul() - * if needed to convert field width and precision; - * - check for broken or non-ANSI native sprintf (e.g. SunOS) - * which does not return string lenth, and work around it; - * - shouldn't happen, but just in case (applies to numeric - * conversions only): added assertion after a call to - * system's sprintf to make sure we detect a problem - * as it happens (or very shortly - but still - after a - * buffer overflow occured for some strange reason - * in system's sprintf); - * - cleanup: avoid comparing signed and unsigned values - * (ANSI c++ complaint); added a couple of 'const' qualifiers; - * - changed few comments, new references to some other - * implementations added to the README file; - * - it appears the Artistic License and its variant the Frontier - * Artistic License are incompatible with GPL and precludes - * this work to be included with GPL-licensed work. This was - * not my intention. The fact that this package is dual licensed - * comes to the rescue. Changed the credits[] string, and - * TERMS AND CONDITIONS to explicitly say so, stressing - * the fact that this work is dual licensed. - */ - - -/* 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 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) __va_copy((ap2),(ap)) - * #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 3 - -#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 <sys/types.h> -#include <ctype.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <assert.h> -#include <errno.h> - -/* 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 favour memcpy & memset (extra procedure call, less code), - * large values favour inline code (saves procedure call, more code). - */ -#if defined(__alpha__) || defined(__alpha) -# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc */ -#endif -#if defined(__i386__) || defined(__i386) -# define breakeven_point 15 /* Intel Pentium/Linux - gcc 2.96 (12..30) */ -#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) { /* 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) { /* 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; } } - -/* The following isdigit() is not portable (e.g. may not work - * with non-ASCII character sets). Use the system-provided isdigit() - * if available, otherwise uncomment: - * #define isdigit(c) ((c) >= '0' && (c) <= '9') - */ - -/* atosizet converts a span of decimal digits to a number of type size_t. - * It is a macro, similar to: (but not quite, p will be modified!) - * void atosizet(const char *p, const char **endp, size_t *result); - * endp will point to just beyond the digits substring. - * This is _not_ a general-purpose macro: - * - the first argument will be modified; - * - the first character must already be checked to be a digit! - * NOTE: size_t could be wider than unsigned int; - * but we treat numeric string like common implementations do! - * If character set is ASCII (checking with a quick and simple-minded test) - * we convert string to a number inline for speed, otherwise we call strtoul. - */ -#define atosizet(p, endp, result) \ - if ((int)'0' == 48) { /* a compile-time constant expression, */ \ - /* hoping the code from one branch */ \ - /* will be optimized away */ \ - /* looks like ASCII character set, let's hope it really is */ \ - register unsigned int uj = (unsigned int)(*(p)++ - '0'); \ - while (isdigit((int)(*(p)))) \ - uj = 10*uj + (unsigned int)(*(p)++ - '0'); \ - if ((endp) != NULL) *(endp) = (p); \ - *(result) = (size_t) uj; \ - } else { \ - /* non-ASCII character set, play by the rules */ \ - char *ep; /* NOTE: no 'const' to make strtoul happy! */ \ - /* NOTE: clip (unsigned long) to (unsigned int) as is common !!! */ \ - const unsigned int uj = (unsigned int) strtoul((p), &ep, 10); \ - /* The following assignment is legal: the address of a non-const */ \ - /* object can be assigned to a pointer to a const object, but */ \ - /* that pointer cannot be used to alter the value of the object. */ \ - if ((endp) != NULL) *(endp) = ep; \ - /* if num too large the result will be ULONG_MAX and errno=ERANGE */ \ - *(result) = (size_t) uj; \ - } \ - -/* 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 const char credits[] = "\n\ -@(#)snprintf.c, v2.3: Mark Martinec, <mark.marti...@ijs.si>\n\ -@(#)snprintf.c, v2.3: Copyright 1999-2002 Mark Martinec. Dual licensed: Frontier Artistic License or GNU General Public License applies.\n\ -@(#)snprintf.c, v2.3: 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 { - const 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 { - const 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 to 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 (0) { /* compile time decision between two equivalent alternatives */ - /* this is simple but slow */ - if (str_l < str_m) str[str_l] = *p; - p++; str_l++; - } else { - /* this usually achieves much better performance for cases - * where format string is long and contains few conversions */ - const char *const q = strchr(p+1,'%'); - const size_t n = !q ? strlen(p) : (q-p); - if (str_l < str_m) { - const 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[48];/* 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 flags '0' and '-' both appear, the '0' flag should be ignored. */ - - /* parse field width */ - if (*p == '*') { - const int j = va_arg(ap, int); - p++; - if (j >= 0) min_field_width = j; - else { min_field_width = -j; justify_left = 1; } - } else if (isdigit((int)(*p))) { - atosizet(p, &p, &min_field_width); - } - /* parse precision */ - if (*p == '.') { - p++; precision_specified = 1; - if (*p == '*') { - const 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))) { - atosizet(p, &p, &precision); - } - } - /* parse 'h', 'l' and 'll' length modifiers */ - if (*p == 'h' || *p == 'l') { - length_modifier = *p; p++; - if (length_modifier == 'l' && *p == 'l') { /* double el = long long */ -#ifdef SNPRINTF_LONGLONG_SUPPORT - length_modifier = '2'; /* double letter el encoded as '2' */ -#else - length_modifier = 'l'; /* treat it as a single 'l' (letter el) */ -#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': { - const 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 *const q = (const char *) 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 (letter el) */ - - 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 (double letter el) */ -#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 char or short as the second argument - * 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': /* letter el */ - 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': /* letter el */ - 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 { - static int sprintf_return_value_is_ansi_compliant = -1; /* unknown */ - char f[5]; int f_l = 0, sprintf_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 (sprintf_return_value_is_ansi_compliant < 0) { /* not yet known */ - /* let's do a little run-time experiment (only once) to see if the - * native sprintf returns a string length as required by ANSI, or has - * some other ideas like the old SunOS which returns buffer address */ - sprintf_return_value_is_ansi_compliant = - (sprintf(tmp+str_arg_l, "%d", 19) == 2); - } - if (fmt_spec == 'p') sprintf_l=sprintf(tmp+str_arg_l, f, ptr_arg); - else if (fmt_spec == 'd') { /* signed */ - switch (length_modifier) { - case '\0': - case 'h': sprintf_l=sprintf(tmp+str_arg_l, f, int_arg); break; - case 'l': sprintf_l=sprintf(tmp+str_arg_l, f, long_arg); break; -#ifdef SNPRINTF_LONGLONG_SUPPORT - case '2': sprintf_l=sprintf(tmp+str_arg_l,f,long_long_arg); break; -#endif - } - } else { /* unsigned */ - switch (length_modifier) { - case '\0': - case 'h': sprintf_l=sprintf(tmp+str_arg_l, f, uint_arg); break; - case 'l': sprintf_l=sprintf(tmp+str_arg_l, f, ulong_arg); break; -#ifdef SNPRINTF_LONGLONG_SUPPORT - case '2': sprintf_l=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; -#endif - } - } - if (!sprintf_return_value_is_ansi_compliant) { /* broken sprintf? */ - tmp[sizeof(tmp)-1] = '\0'; sprintf_l = strlen(tmp+str_arg_l); - } - assert(sprintf_l >= 0); /* should not happen; problem in sprintf? */ - assert(sprintf_l+str_arg_l < sizeof(tmp)); /*better late then never*/ - str_arg_l += sprintf_l; - /* 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; - } - } - { const 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) { - const int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); - if (n > 0) number_of_zeros_to_pad += n; - } - break; - } - case 'n': { - void *const ptr = va_arg(ap, void *); - if (ptr != NULL) { - /* same problem of size_t -> int type conversion as with the - * snprintf return value - see comment at the end of this procedure */ - switch (length_modifier) { - case '\0': *( int *const)ptr = str_l; break; - case 'h': *(short int *const)ptr = str_l; break; - case 'l': *(long int *const)ptr = str_l; break; -#ifdef SNPRINTF_LONGLONG_SUPPORT - case '2': *(long long int *const)ptr = str_l; break; -#endif - } - } - /* no argument converted */ - min_field_width = number_of_zeros_to_pad = str_arg_l = 0; - 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 */ - const int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); - if (n > 0) { - if (str_l < str_m) { - const size_t avail = str_m-str_l; - fast_memset(str+str_l, (zero_padding?'0':' '), - ((unsigned int)n > avail ? avail : (unsigned int)n)); - } - str_l += n; - } - } - /* is 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 the 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 */ - { const int n = zero_padding_insertion_ind; - if (n > 0) { - if (str_l < str_m) { - const size_t avail = str_m-str_l; - fast_memcpy(str+str_l, str_arg, - ((unsigned int)n > avail ? avail : (unsigned int)n)); - } - str_l += n; - } - } - /* insert zero padding as requested by the precision or min field width */ - { const int n = number_of_zeros_to_pad; - if (n > 0) { - if (str_l < str_m) { - const size_t avail = str_m-str_l; - fast_memset(str+str_l, '0', - ((unsigned int)n > avail ? avail : (unsigned int)n)); - } - str_l += n; - } - } - } - /* insert formatted string - * (or as-is conversion specifier for unknown conversions) */ - { const int n = str_arg_l - zero_padding_insertion_ind; - if (n > 0) { - if (str_l < str_m) { - const size_t avail = str_m-str_l; - fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, - ((unsigned int)n > avail ? avail : (unsigned int)n)); - } - str_l += n; - } - } - /* insert right padding */ - if (justify_left) { /* right blank padding to the field width */ - const int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); - if (n > 0) { - if (str_l < str_m) { - const size_t avail = str_m-str_l; - fast_memset(str+str_l, ' ', - ((unsigned int)n > avail ? avail : (unsigned int)n)); - } - str_l += n; - } - } - } - } -#if defined(NEED_SNPRINTF_ONLY) - va_end(ap); -#endif - if (str_m > 0) { /* make sure the string is null-terminated, possibly - at the expense of overwriting the last character */ - 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 invalid. - * 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 -- 2.16.1