Annotation of generator/main/snprintf.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * snprintf.c - a portable implementation of snprintf
        !             3:  *
        !             4:  * AUTHOR
        !             5:  *   Mark Martinec <[email protected]>, April 1999.
        !             6:  *
        !             7:  *   Copyright 1999, Mark Martinec. All rights reserved.
        !             8:  *
        !             9:  * TERMS AND CONDITIONS
        !            10:  *   This program is free software; you can redistribute it and/or modify
        !            11:  *   it under the terms of the "Frontier Artistic License" which comes
        !            12:  *   with this Kit.
        !            13:  *
        !            14:  *   This program is distributed in the hope that it will be useful,
        !            15:  *   but WITHOUT ANY WARRANTY; without even the implied warranty
        !            16:  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
        !            17:  *   See the Frontier Artistic License for more details.
        !            18:  *
        !            19:  *   You should have received a copy of the Frontier Artistic License
        !            20:  *   with this Kit in the file named LICENSE.txt .
        !            21:  *   If not, I'll be glad to provide one.
        !            22:  *
        !            23:  * FEATURES
        !            24:  * - careful adherence to specs regarding flags, field width and precision;
        !            25:  * - good performance for large string handling (large format, large
        !            26:  *   argument or large paddings). Performance is similar to system's sprintf
        !            27:  *   and in several cases significantly better (make sure you compile with
        !            28:  *   optimizations turned on, tell the compiler the code is strict ANSI
        !            29:  *   if necessary to give it more freedom for optimizations);
        !            30:  * - return value semantics per ISO/IEC 9899:1999 ("ISO C99");
        !            31:  * - written in standard ISO/ANSI C - requires an ANSI C compiler.
        !            32:  *
        !            33:  * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
        !            34:  *
        !            35:  * This snprintf only supports the following conversion specifiers:
        !            36:  * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
        !            37:  * with flags: '-', '+', ' ', '0' and '#'.
        !            38:  * An asterisk is supported for field width as well as precision.
        !            39:  *
        !            40:  * Length modifiers 'h' (short int), 'l' (long int),
        !            41:  * and 'll' (long long int) are supported.
        !            42:  * NOTE:
        !            43:  *   If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
        !            44:  *   length modifier 'll' is recognized but treated the same as 'l',
        !            45:  *   which may cause argument value truncation! Defining
        !            46:  *   SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
        !            47:  *   handles length modifier 'll'.  long long int is a language extension
        !            48:  *   which may not be portable.
        !            49:  *
        !            50:  * Conversion of numeric data (conversion specifiers d, u, o, x, X, p)
        !            51:  * with length modifiers (none or h, l, ll) is left to the system routine
        !            52:  * sprintf, but all handling of flags, field width and precision as well as
        !            53:  * c and s conversions is done very carefully by this portable routine.
        !            54:  * If a string precision (truncation) is specified (e.g. %.8s) it is
        !            55:  * guaranteed the string beyond the specified precision will not be referenced.
        !            56:  *
        !            57:  * Length modifiers h, l and ll are ignored for c and s conversions (data
        !            58:  * types wint_t and wchar_t are not supported).
        !            59:  *
        !            60:  * The following common synonyms for conversion characters are supported:
        !            61:  *   - i is a synonym for d
        !            62:  *   - D is a synonym for ld, explicit length modifiers are ignored
        !            63:  *   - U is a synonym for lu, explicit length modifiers are ignored
        !            64:  *   - O is a synonym for lo, explicit length modifiers are ignored
        !            65:  * The D, O and U conversion characters are nonstandard, they are supported
        !            66:  * for backward compatibility only, and should not be used for new code.
        !            67:  *
        !            68:  * The following is specifically NOT supported:
        !            69:  *   - flag ' (thousands' grouping character) is recognized but ignored
        !            70:  *   - numeric conversion specifiers: f, e, E, g, G and synonym F,
        !            71:  *     as well as the new a and A conversion specifiers
        !            72:  *   - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
        !            73:  *   - wide character/string conversions: lc, ls, and nonstandard
        !            74:  *     synonyms C and S
        !            75:  *   - writeback of converted string length: conversion character n
        !            76:  *   - the n$ specification for direct reference to n-th argument
        !            77:  *   - locales
        !            78:  *
        !            79:  * It is permitted for str_m to be zero, and it is permitted to specify NULL
        !            80:  * pointer for resulting string argument if str_m is zero (as per ISO C99).
        !            81:  *
        !            82:  * The return value is the number of characters which would be generated
        !            83:  * for the given input, excluding the trailing null. If this value
        !            84:  * is greater or equal to str_m, not all characters from the result
        !            85:  * have been stored in str, output bytes beyond the (str_m-1) -th character
        !            86:  * are discarded. If str_m is greater than zero it is guaranteed
        !            87:  * the resulting string will be null-terminated.
        !            88:  *
        !            89:  * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
        !            90:  * but is different from some older and vendor implementations,
        !            91:  * and is also different from XPG, XSH5, SUSv2 specifications.
        !            92:  * For historical discussion on changes in the semantics and standards
        !            93:  * of snprintf see printf(3) man page in the Linux programmers manual.
        !            94:  *
        !            95:  * Routines asprintf and vasprintf return a pointer (in the ptr argument)
        !            96:  * to a buffer sufficiently large to hold the resulting string. This pointer
        !            97:  * should be passed to free(3) to release the allocated storage when it is
        !            98:  * no longer needed. If sufficient space cannot be allocated, these functions
        !            99:  * will return -1 and set ptr to be a NULL pointer. These two routines are a
        !           100:  * GNU C library extensions (glibc).
        !           101:  *
        !           102:  * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
        !           103:  * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
        !           104:  * characters into the allocated output string, the last character in the
        !           105:  * allocated buffer then gets the terminating null. If the formatted string
        !           106:  * length (the return value) is greater than or equal to the str_m argument,
        !           107:  * the resulting string was truncated and some of the formatted characters
        !           108:  * were discarded. These routines present a handy way to limit the amount
        !           109:  * of allocated memory to some sane value.
        !           110:  *
        !           111:  * AVAILABILITY
        !           112:  *   http://www.ijs.si/software/snprintf/
        !           113:  *
        !           114:  * REVISION HISTORY
        !           115:  * 1999-04     V0.9  Mark Martinec
        !           116:  *             - initial version, some modifications after comparing printf
        !           117:  *               man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10,
        !           118:  *               and checking how Perl handles sprintf (differently!);
        !           119:  * 1999-04-09  V1.0  Mark Martinec <[email protected]>
        !           120:  *             - added main test program, fixed remaining inconsistencies,
        !           121:  *               added optional (long long int) support;
        !           122:  * 1999-04-12  V1.1  Mark Martinec <[email protected]>
        !           123:  *             - support the 'p' conversion (pointer to void);
        !           124:  *             - if a string precision is specified
        !           125:  *               make sure the string beyond the specified precision
        !           126:  *               will not be referenced (e.g. by strlen);
        !           127:  * 1999-04-13  V1.2  Mark Martinec <[email protected]>
        !           128:  *             - support synonyms %D=%ld, %U=%lu, %O=%lo;
        !           129:  *             - speed up the case of long format string with few conversions;
        !           130:  * 1999-06-30  V1.3  Mark Martinec <[email protected]>
        !           131:  *             - fixed runaway loop (eventually crashing when str_l wraps
        !           132:  *               beyond 2^31) while copying format string without
        !           133:  *               conversion specifiers to a buffer that is too short
        !           134:  *               (thanks to Edwin Young <[email protected]> for
        !           135:  *               spotting the problem);
        !           136:  *             - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
        !           137:  *               to snprintf.h
        !           138:  * 2000-02-14  V2.0 (never released) Mark Martinec <[email protected]>
        !           139:  *             - relaxed license terms: The Artistic License now applies.
        !           140:  *               You may still apply the GNU GENERAL PUBLIC LICENSE
        !           141:  *               as was distributed with previous versions, if you prefer;
        !           142:  *             - changed REVISION HISTORY dates to use ISO 8601 date format;
        !           143:  *             - added vsnprintf (patch also independently proposed by
        !           144:  *               Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
        !           145:  * 2000-06-27  V2.1  Mark Martinec <[email protected]>
        !           146:  *             - removed POSIX check for str_m<1; value 0 for str_m is
        !           147:  *               allowed by ISO C99 (and GNU C library 2.1) - (pointed out
        !           148:  *               on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie).
        !           149:  *               Besides relaxed license this change in standards adherence
        !           150:  *               is the main reason to bump up the major version number;
        !           151:  *             - added nonstandard routines asnprintf, vasnprintf, asprintf,
        !           152:  *               vasprintf that dynamically allocate storage for the
        !           153:  *               resulting string; these routines are not compiled by default,
        !           154:  *               see comments where NEED_V?ASN?PRINTF macros are defined;
        !           155:  *             - autoconf contributed by Caolan McNamara
        !           156:  * 2000-10-06  V2.2  Mark Martinec <[email protected]>
        !           157:  *             - BUG FIX: the %c conversion used a temporary variable
        !           158:  *               that was no longer in scope when referenced,
        !           159:  *               possibly causing incorrect resulting character;
        !           160:  *             - BUG FIX: make precision and minimal field width unsigned
        !           161:  *               to handle huge values (2^31 <= n < 2^32) correctly;
        !           162:  *               also be more careful in the use of signed/unsigned/size_t
        !           163:  *               internal variables - probably more careful than many
        !           164:  *               vendor implementations, but there may still be a case
        !           165:  *               where huge values of str_m, precision or minimal field
        !           166:  *               could cause incorrect behaviour;
        !           167:  *             - use separate variables for signed/unsigned arguments,
        !           168:  *               and for short/int, long, and long long argument lengths
        !           169:  *               to avoid possible incompatibilities on certain
        !           170:  *               computer architectures. Also use separate variable
        !           171:  *               arg_sign to hold sign of a numeric argument,
        !           172:  *               to make code more transparent;
        !           173:  *             - some fiddling with zero padding and "0x" to make it
        !           174:  *               Linux compatible;
        !           175:  *             - systematically use macros fast_memcpy and fast_memset
        !           176:  *               instead of case-by-case hand optimization; determine some
        !           177:  *               breakeven string lengths for different architectures;
        !           178:  *             - terminology change: 'format' -> 'conversion specifier',
        !           179:  *               'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")',
        !           180:  *               'alternative form' -> 'alternate form',
        !           181:  *               'data type modifier' -> 'length modifier';
        !           182:  *             - several comments rephrased and new ones added;
        !           183:  *             - make compiler not complain about 'credits' defined but
        !           184:  *               not used;
        !           185:  */
        !           186: 
        !           187: 
        !           188: /* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf.
        !           189:  *
        !           190:  * If HAVE_SNPRINTF is defined this module will not produce code for
        !           191:  * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well,
        !           192:  * causing this portable version of snprintf to be called portable_snprintf
        !           193:  * (and portable_vsnprintf).
        !           194:  */
        !           195: /* #define HAVE_SNPRINTF */
        !           196: 
        !           197: /* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and
        !           198:  * vsnprintf but you would prefer to use the portable routine(s) instead.
        !           199:  * In this case the portable routine is declared as portable_snprintf
        !           200:  * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf')
        !           201:  * is defined to expand to 'portable_v?snprintf' - see file snprintf.h .
        !           202:  * Defining this macro is only useful if HAVE_SNPRINTF is also defined,
        !           203:  * but does does no harm if defined nevertheless.
        !           204:  */
        !           205: /* #define PREFER_PORTABLE_SNPRINTF */
        !           206: 
        !           207: /* Define SNPRINTF_LONGLONG_SUPPORT if you want to support
        !           208:  * data type (long long int) and length modifier 'll' (e.g. %lld).
        !           209:  * If undefined, 'll' is recognized but treated as a single 'l'.
        !           210:  *
        !           211:  * If the system's sprintf does not handle 'll'
        !           212:  * the SNPRINTF_LONGLONG_SUPPORT must not be defined!
        !           213:  *
        !           214:  * This is off by default as (long long int) is a language extension.
        !           215:  */
        !           216: /* #define SNPRINTF_LONGLONG_SUPPORT */
        !           217: 
        !           218: /* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf.
        !           219:  * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly,
        !           220:  * otherwise both snprintf and vsnprintf routines will be defined
        !           221:  * and snprintf will be a simple wrapper around vsnprintf, at the expense
        !           222:  * of an extra procedure call.
        !           223:  */
        !           224: /* #define NEED_SNPRINTF_ONLY */
        !           225: 
        !           226: /* Define NEED_V?ASN?PRINTF macros if you need library extension
        !           227:  * routines asprintf, vasprintf, asnprintf, vasnprintf respectively,
        !           228:  * and your system library does not provide them. They are all small
        !           229:  * wrapper routines around portable_vsnprintf. Defining any of the four
        !           230:  * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY
        !           231:  * and turns on PREFER_PORTABLE_SNPRINTF.
        !           232:  *
        !           233:  * Watch for name conflicts with the system library if these routines
        !           234:  * are already present there.
        !           235:  *
        !           236:  * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as
        !           237:  * specified by C99, to be able to traverse the same list of arguments twice.
        !           238:  * I don't know of any other standard and portable way of achieving the same.
        !           239:  * With some versions of gcc you may use __va_copy(). You might even get away
        !           240:  * with "ap2 = ap", in this case you must not call va_end(ap2) !
        !           241:  *   #define va_copy(ap2,ap) ap2 = ap
        !           242:  */
        !           243: /* #define NEED_ASPRINTF   */
        !           244: /* #define NEED_ASNPRINTF  */
        !           245: /* #define NEED_VASPRINTF  */
        !           246: /* #define NEED_VASNPRINTF */
        !           247: 
        !           248: 
        !           249: /* Define the following macros if desired:
        !           250:  *   SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE,
        !           251:  *   HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE,
        !           252:  *   DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE,
        !           253:  *   PERL_COMPATIBLE, PERL_BUG_COMPATIBLE,
        !           254:  *
        !           255:  * - For portable applications it is best not to rely on peculiarities
        !           256:  *   of a given implementation so it may be best not to define any
        !           257:  *   of the macros that select compatibility and to avoid features
        !           258:  *   that vary among the systems.
        !           259:  *
        !           260:  * - Selecting compatibility with more than one operating system
        !           261:  *   is not strictly forbidden but is not recommended.
        !           262:  *
        !           263:  * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE .
        !           264:  *
        !           265:  * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is
        !           266:  *   documented in a sprintf man page on a given operating system
        !           267:  *   and actually adhered to by the system's sprintf (but not on
        !           268:  *   most other operating systems). It may also refer to and enable
        !           269:  *   a behaviour that is declared 'undefined' or 'implementation specific'
        !           270:  *   in the man page but a given implementation behaves predictably
        !           271:  *   in a certain way.
        !           272:  *
        !           273:  * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf
        !           274:  *   that contradicts the sprintf man page on the same operating system.
        !           275:  *
        !           276:  * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE
        !           277:  *   conditionals take into account all idiosyncrasies of a particular
        !           278:  *   implementation, there may be other incompatibilities.
        !           279:  */
        !           280: 
        !           281: 
        !           282: 
        !           283: /* ============================================= */
        !           284: /* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
        !           285: /* ============================================= */
        !           286: 
        !           287: #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
        !           288: #define PORTABLE_SNPRINTF_VERSION_MINOR 2
        !           289: 
        !           290: #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
        !           291: # if defined(NEED_SNPRINTF_ONLY)
        !           292: # undef NEED_SNPRINTF_ONLY
        !           293: # endif
        !           294: # if !defined(PREFER_PORTABLE_SNPRINTF)
        !           295: # define PREFER_PORTABLE_SNPRINTF
        !           296: # endif
        !           297: #endif
        !           298: 
        !           299: #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
        !           300: #define SOLARIS_COMPATIBLE
        !           301: #endif
        !           302: 
        !           303: #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
        !           304: #define HPUX_COMPATIBLE
        !           305: #endif
        !           306: 
        !           307: #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
        !           308: #define DIGITAL_UNIX_COMPATIBLE
        !           309: #endif
        !           310: 
        !           311: #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
        !           312: #define PERL_COMPATIBLE
        !           313: #endif
        !           314: 
        !           315: #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
        !           316: #define LINUX_COMPATIBLE
        !           317: #endif
        !           318: 
        !           319: #include <sys/types.h>
        !           320: #include <string.h>
        !           321: #include <stdlib.h>
        !           322: #include <stdio.h>
        !           323: #include <stdarg.h>
        !           324: #include <assert.h>
        !           325: #include <errno.h>
        !           326: 
        !           327: #ifdef isdigit
        !           328: #undef isdigit
        !           329: #endif
        !           330: #define isdigit(c) ((c) >= '0' && (c) <= '9')
        !           331: 
        !           332: /* For copying strings longer or equal to 'breakeven_point'
        !           333:  * it is more efficient to call memcpy() than to do it inline.
        !           334:  * The value depends mostly on the processor architecture,
        !           335:  * but also on the compiler and its optimization capabilities.
        !           336:  * The value is not critical, some small value greater than zero
        !           337:  * will be just fine if you don't care to squeeze every drop
        !           338:  * of performance out of the code.
        !           339:  *
        !           340:  * Small values favor memcpy, large values favor inline code.
        !           341:  */
        !           342: #if defined(__alpha__) || defined(__alpha)
        !           343: #  define breakeven_point   2  /* AXP (DEC Alpha)     - gcc or cc or egcs */
        !           344: #endif
        !           345: #if defined(__i386__)  || defined(__i386)
        !           346: #  define breakeven_point  12  /* Intel Pentium/Linux - gcc 2.96 */
        !           347: #endif
        !           348: #if defined(__hppa)
        !           349: #  define breakeven_point  10  /* HP-PA               - gcc */
        !           350: #endif
        !           351: #if defined(__sparc__) || defined(__sparc)
        !           352: #  define breakeven_point  33  /* Sun Sparc 5         - gcc 2.8.1 */
        !           353: #endif
        !           354: 
        !           355: /* some other values of possible interest: */
        !           356: /* #define breakeven_point  8 */  /* VAX 4000          - vaxc */
        !           357: /* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */
        !           358: 
        !           359: #ifndef breakeven_point
        !           360: #  define breakeven_point   6  /* some reasonable one-size-fits-all value */
        !           361: #endif
        !           362: 
        !           363: #define fast_memcpy(d,s,n) \
        !           364:   { register size_t nn = (size_t)(n); \
        !           365:     if (nn >= breakeven_point) memcpy((d), (s), nn); \
        !           366:     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
        !           367:       register char *dd; register const char *ss; \
        !           368:       for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
        !           369: 
        !           370: #define fast_memset(d,c,n) \
        !           371:   { register size_t nn = (size_t)(n); \
        !           372:     if (nn >= breakeven_point) memset((d), (int)(c), nn); \
        !           373:     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
        !           374:       register char *dd; register const int cc=(int)(c); \
        !           375:       for (dd=(d); nn>0; nn--) *dd++ = cc; } }
        !           376: 
        !           377: /* prototypes */
        !           378: 
        !           379: #if defined(NEED_ASPRINTF)
        !           380: int asprintf   (char **ptr, const char *fmt, /*args*/ ...);
        !           381: #endif
        !           382: #if defined(NEED_VASPRINTF)
        !           383: int vasprintf  (char **ptr, const char *fmt, va_list ap);
        !           384: #endif
        !           385: #if defined(NEED_ASNPRINTF)
        !           386: int asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
        !           387: #endif
        !           388: #if defined(NEED_VASNPRINTF)
        !           389: int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
        !           390: #endif
        !           391: 
        !           392: #if defined(HAVE_SNPRINTF)
        !           393: /* declare our portable snprintf  routine under name portable_snprintf  */
        !           394: /* declare our portable vsnprintf routine under name portable_vsnprintf */
        !           395: #else
        !           396: /* declare our portable routines under names snprintf and vsnprintf */
        !           397: #define portable_snprintf snprintf
        !           398: #if !defined(NEED_SNPRINTF_ONLY)
        !           399: #define portable_vsnprintf vsnprintf
        !           400: #endif
        !           401: #endif
        !           402: 
        !           403: #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
        !           404: int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
        !           405: #if !defined(NEED_SNPRINTF_ONLY)
        !           406: int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
        !           407: #endif
        !           408: #endif
        !           409: 
        !           410: /* declarations */
        !           411: 
        !           412: static char credits[] = "\n\
        !           413: @(#)snprintf.c, v2.2: Mark Martinec, <[email protected]>\n\
        !           414: @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
        !           415: @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
        !           416: 
        !           417: #if defined(NEED_ASPRINTF)
        !           418: int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
        !           419:   va_list ap;
        !           420:   size_t str_m;
        !           421:   int str_l;
        !           422: 
        !           423:   *ptr = NULL;
        !           424:   va_start(ap, fmt);                            /* measure the required size */
        !           425:   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
        !           426:   va_end(ap);
        !           427:   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
        !           428:   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
        !           429:   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
        !           430:   else {
        !           431:     int str_l2;
        !           432:     va_start(ap, fmt);
        !           433:     str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
        !           434:     va_end(ap);
        !           435:     assert(str_l2 == str_l);
        !           436:   }
        !           437:   return str_l;
        !           438: }
        !           439: #endif
        !           440: 
        !           441: #if defined(NEED_VASPRINTF)
        !           442: int vasprintf(char **ptr, const char *fmt, va_list ap) {
        !           443:   size_t str_m;
        !           444:   int str_l;
        !           445: 
        !           446:   *ptr = NULL;
        !           447:   { va_list ap2;
        !           448:     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
        !           449:     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
        !           450:     va_end(ap2);
        !           451:   }
        !           452:   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
        !           453:   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
        !           454:   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
        !           455:   else {
        !           456:     int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
        !           457:     assert(str_l2 == str_l);
        !           458:   }
        !           459:   return str_l;
        !           460: }
        !           461: #endif
        !           462: 
        !           463: #if defined(NEED_ASNPRINTF)
        !           464: int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
        !           465:   va_list ap;
        !           466:   int str_l;
        !           467: 
        !           468:   *ptr = NULL;
        !           469:   va_start(ap, fmt);                            /* measure the required size */
        !           470:   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
        !           471:   va_end(ap);
        !           472:   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
        !           473:   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
        !           474:   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
        !           475:   if (str_m == 0) {  /* not interested in resulting string, just return size */
        !           476:   } else {
        !           477:     *ptr = (char *) malloc(str_m);
        !           478:     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
        !           479:     else {
        !           480:       int str_l2;
        !           481:       va_start(ap, fmt);
        !           482:       str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
        !           483:       va_end(ap);
        !           484:       assert(str_l2 == str_l);
        !           485:     }
        !           486:   }
        !           487:   return str_l;
        !           488: }
        !           489: #endif
        !           490: 
        !           491: #if defined(NEED_VASNPRINTF)
        !           492: int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
        !           493:   int str_l;
        !           494: 
        !           495:   *ptr = NULL;
        !           496:   { va_list ap2;
        !           497:     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
        !           498:     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
        !           499:     va_end(ap2);
        !           500:   }
        !           501:   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
        !           502:   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
        !           503:   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
        !           504:   if (str_m == 0) {  /* not interested in resulting string, just return size */
        !           505:   } else {
        !           506:     *ptr = (char *) malloc(str_m);
        !           507:     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
        !           508:     else {
        !           509:       int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
        !           510:       assert(str_l2 == str_l);
        !           511:     }
        !           512:   }
        !           513:   return str_l;
        !           514: }
        !           515: #endif
        !           516: 
        !           517: /*
        !           518:  * If the system does have snprintf and the portable routine is not
        !           519:  * specifically required, this module produces no code for snprintf/vsnprintf.
        !           520:  */
        !           521: #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
        !           522: 
        !           523: #if !defined(NEED_SNPRINTF_ONLY)
        !           524: int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
        !           525:   va_list ap;
        !           526:   int str_l;
        !           527: 
        !           528:   va_start(ap, fmt);
        !           529:   str_l = portable_vsnprintf(str, str_m, fmt, ap);
        !           530:   va_end(ap);
        !           531:   return str_l;
        !           532: }
        !           533: #endif
        !           534: 
        !           535: #if defined(NEED_SNPRINTF_ONLY)
        !           536: int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
        !           537: #else
        !           538: int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
        !           539: #endif
        !           540: 
        !           541: #if defined(NEED_SNPRINTF_ONLY)
        !           542:   va_list ap;
        !           543: #endif
        !           544:   size_t str_l = 0;
        !           545:   const char *p = fmt;
        !           546: 
        !           547: /* In contrast with POSIX, the ISO C99 now says
        !           548:  * that str can be NULL and str_m can be 0.
        !           549:  * This is more useful than the old:  if (str_m < 1) return -1; */
        !           550: 
        !           551: #if defined(NEED_SNPRINTF_ONLY)
        !           552:   va_start(ap, fmt);
        !           553: #endif
        !           554:   if (!p) p = "";
        !           555:   while (*p) {
        !           556:     if (*p != '%') {
        !           557:    /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
        !           558:    /* but the following code achieves better performance for cases
        !           559:     * where format string is long and contains few conversions */
        !           560:       const char *q = strchr(p+1,'%');
        !           561:       size_t n = !q ? strlen(p) : (q-p);
        !           562:       if (str_l < str_m) {
        !           563:         size_t avail = str_m-str_l;
        !           564:         fast_memcpy(str+str_l, p, (n>avail?avail:n));
        !           565:       }
        !           566:       p += n; str_l += n;
        !           567:     } else {
        !           568:       const char *starting_p;
        !           569:       size_t min_field_width = 0, precision = 0;
        !           570:       int zero_padding = 0, precision_specified = 0, justify_left = 0;
        !           571:       int alternate_form = 0, force_sign = 0;
        !           572:       int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
        !           573:                                      the ' ' flag should be ignored. */
        !           574:       char length_modifier = '\0';            /* allowed values: \0, h, l, L */
        !           575:       char tmp[32];/* temporary buffer for simple numeric->string conversion */
        !           576: 
        !           577:       const char *str_arg;      /* string address in case of string argument */
        !           578:       size_t str_arg_l;         /* natural field width of arg without padding
        !           579:                                    and sign */
        !           580:       unsigned char uchar_arg;
        !           581:         /* unsigned char argument value - only defined for c conversion.
        !           582:            N.B. standard explicitly states the char argument for
        !           583:            the c conversion is unsigned */
        !           584: 
        !           585:       size_t number_of_zeros_to_pad = 0;
        !           586:         /* number of zeros to be inserted for numeric conversions
        !           587:            as required by the precision or minimal field width */
        !           588: 
        !           589:       size_t zero_padding_insertion_ind = 0;
        !           590:         /* index into tmp where zero padding is to be inserted */
        !           591: 
        !           592:       char fmt_spec = '\0';
        !           593:         /* current conversion specifier character */
        !           594: 
        !           595:       str_arg = credits;/* just to make compiler happy (defined but not used)*/
        !           596:       str_arg = NULL;
        !           597:       starting_p = p; p++;  /* skip '%' */
        !           598:    /* parse flags */
        !           599:       while (*p == '0' || *p == '-' || *p == '+' ||
        !           600:              *p == ' ' || *p == '#' || *p == '\'') {
        !           601:         switch (*p) {
        !           602:         case '0': zero_padding = 1; break;
        !           603:         case '-': justify_left = 1; break;
        !           604:         case '+': force_sign = 1; space_for_positive = 0; break;
        !           605:         case ' ': force_sign = 1;
        !           606:      /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
        !           607: #ifdef PERL_COMPATIBLE
        !           608:      /* ... but in Perl the last of ' ' and '+' applies */
        !           609:                   space_for_positive = 1;
        !           610: #endif
        !           611:                   break;
        !           612:         case '#': alternate_form = 1; break;
        !           613:         case '\'': break;
        !           614:         }
        !           615:         p++;
        !           616:       }
        !           617:    /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
        !           618: 
        !           619:    /* parse field width */
        !           620:       if (*p == '*') {
        !           621:         int j;
        !           622:         p++; j = va_arg(ap, int);
        !           623:         if (j >= 0) min_field_width = j;
        !           624:         else { min_field_width = -j; justify_left = 1; }
        !           625:       } else if (isdigit((int)(*p))) {
        !           626:         /* size_t could be wider than unsigned int;
        !           627:            make sure we treat argument like common implementations do */
        !           628:         unsigned int uj = *p++ - '0';
        !           629:         while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
        !           630:         min_field_width = uj;
        !           631:       }
        !           632:    /* parse precision */
        !           633:       if (*p == '.') {
        !           634:         p++; precision_specified = 1;
        !           635:         if (*p == '*') {
        !           636:           int j = va_arg(ap, int);
        !           637:           p++;
        !           638:           if (j >= 0) precision = j;
        !           639:           else {
        !           640:             precision_specified = 0; precision = 0;
        !           641:          /* NOTE:
        !           642:           *   Solaris 2.6 man page claims that in this case the precision
        !           643:           *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
        !           644:           *   claim that this case should be treated as unspecified precision,
        !           645:           *   which is what we do here.
        !           646:           */
        !           647:           }
        !           648:         } else if (isdigit((int)(*p))) {
        !           649:           /* size_t could be wider than unsigned int;
        !           650:              make sure we treat argument like common implementations do */
        !           651:           unsigned int uj = *p++ - '0';
        !           652:           while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
        !           653:           precision = uj;
        !           654:         }
        !           655:       }
        !           656:    /* parse 'h', 'l' and 'll' length modifiers */
        !           657:       if (*p == 'h' || *p == 'l') {
        !           658:         length_modifier = *p; p++;
        !           659:         if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */
        !           660: #ifdef SNPRINTF_LONGLONG_SUPPORT
        !           661:           length_modifier = '2';                  /* double l encoded as '2' */
        !           662: #else
        !           663:           length_modifier = 'l';                 /* treat it as a single 'l' */
        !           664: #endif
        !           665:           p++;
        !           666:         }
        !           667:       }
        !           668:       fmt_spec = *p;
        !           669:    /* common synonyms: */
        !           670:       switch (fmt_spec) {
        !           671:       case 'i': fmt_spec = 'd'; break;
        !           672:       case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
        !           673:       case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
        !           674:       case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
        !           675:       default: break;
        !           676:       }
        !           677:    /* get parameter value, do initial processing */
        !           678:       switch (fmt_spec) {
        !           679:       case '%': /* % behaves similar to 's' regarding flags and field widths */
        !           680:       case 'c': /* c behaves similar to 's' regarding flags and field widths */
        !           681:       case 's':
        !           682:         length_modifier = '\0';          /* wint_t and wchar_t not supported */
        !           683:      /* the result of zero padding flag with non-numeric conversion specifier*/
        !           684:      /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
        !           685:      /* Digital Unix and Linux does not. */
        !           686: #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
        !           687:         zero_padding = 0;    /* turn zero padding off for string conversions */
        !           688: #endif
        !           689:         str_arg_l = 1;
        !           690:         switch (fmt_spec) {
        !           691:         case '%':
        !           692:           str_arg = p; break;
        !           693:         case 'c': {
        !           694:           int j = va_arg(ap, int);
        !           695:           uchar_arg = (unsigned char) j;   /* standard demands unsigned char */
        !           696:           str_arg = (const char *) &uchar_arg;
        !           697:           break;
        !           698:         }
        !           699:         case 's':
        !           700:           str_arg = va_arg(ap, const char *);
        !           701:           if (!str_arg) str_arg_l = 0;
        !           702:        /* make sure not to address string beyond the specified precision !!! */
        !           703:           else if (!precision_specified) str_arg_l = strlen(str_arg);
        !           704:        /* truncate string if necessary as requested by precision */
        !           705:           else if (precision == 0) str_arg_l = 0;
        !           706:           else {
        !           707:        /* memchr on HP does not like n > 2^31  !!! */
        !           708:             const char *q = memchr(str_arg, '\0',
        !           709:                              precision <= 0x7fffffff ? precision : 0x7fffffff);
        !           710:             str_arg_l = !q ? precision : (q-str_arg);
        !           711:           }
        !           712:           break;
        !           713:         default: break;
        !           714:         }
        !           715:         break;
        !           716:       case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
        !           717:         /* NOTE: the u, o, x, X and p conversion specifiers imply
        !           718:                  the value is unsigned;  d implies a signed value */
        !           719: 
        !           720:         int arg_sign = 0;
        !           721:           /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
        !           722:             +1 if greater than zero (or nonzero for unsigned arguments),
        !           723:             -1 if negative (unsigned argument is never negative) */
        !           724: 
        !           725:         int int_arg = 0;  unsigned int uint_arg = 0;
        !           726:           /* only defined for length modifier h, or for no length modifiers */
        !           727: 
        !           728:         long int long_arg = 0;  unsigned long int ulong_arg = 0;
        !           729:           /* only defined for length modifier l */
        !           730: 
        !           731:         void *ptr_arg = NULL;
        !           732:           /* pointer argument value -only defined for p conversion */
        !           733: 
        !           734: #ifdef SNPRINTF_LONGLONG_SUPPORT
        !           735:         long long int long_long_arg = 0;
        !           736:         unsigned long long int ulong_long_arg = 0;
        !           737:           /* only defined for length modifier ll */
        !           738: #endif
        !           739:         if (fmt_spec == 'p') {
        !           740:         /* HPUX 10: An l, h, ll or L before any other conversion character
        !           741:          *   (other than d, i, u, o, x, or X) is ignored.
        !           742:          * Digital Unix:
        !           743:          *   not specified, but seems to behave as HPUX does.
        !           744:          * Solaris: If an h, l, or L appears before any other conversion
        !           745:          *   specifier (other than d, i, u, o, x, or X), the behavior
        !           746:          *   is undefined. (Actually %hp converts only 16-bits of address
        !           747:          *   and %llp treats address as 64-bit data which is incompatible
        !           748:          *   with (void *) argument on a 32-bit system).
        !           749:          */
        !           750: #ifdef SOLARIS_COMPATIBLE
        !           751: #  ifdef SOLARIS_BUG_COMPATIBLE
        !           752:           /* keep length modifiers even if it represents 'll' */
        !           753: #  else
        !           754:           if (length_modifier == '2') length_modifier = '\0';
        !           755: #  endif
        !           756: #else
        !           757:           length_modifier = '\0';
        !           758: #endif
        !           759:           ptr_arg = va_arg(ap, void *);
        !           760:           if (ptr_arg != NULL) arg_sign = 1;
        !           761:         } else if (fmt_spec == 'd') {  /* signed */
        !           762:           switch (length_modifier) {
        !           763:           case '\0':
        !           764:           case 'h':
        !           765:          /* It is non-portable to specify a second argument of char or short
        !           766:           * to va_arg, because arguments seen by the called function
        !           767:           * are not char or short.  C converts char and short arguments
        !           768:           * to int before passing them to a function.
        !           769:           */
        !           770:             int_arg = va_arg(ap, int);
        !           771:             if      (int_arg > 0) arg_sign =  1;
        !           772:             else if (int_arg < 0) arg_sign = -1;
        !           773:             break;
        !           774:           case 'l':
        !           775:             long_arg = va_arg(ap, long int);
        !           776:             if      (long_arg > 0) arg_sign =  1;
        !           777:             else if (long_arg < 0) arg_sign = -1;
        !           778:             break;
        !           779: #ifdef SNPRINTF_LONGLONG_SUPPORT
        !           780:           case '2':
        !           781:             long_long_arg = va_arg(ap, long long int);
        !           782:             if      (long_long_arg > 0) arg_sign =  1;
        !           783:             else if (long_long_arg < 0) arg_sign = -1;
        !           784:             break;
        !           785: #endif
        !           786:           }
        !           787:         } else {  /* unsigned */
        !           788:           switch (length_modifier) {
        !           789:           case '\0':
        !           790:           case 'h':
        !           791:             uint_arg = va_arg(ap, unsigned int);
        !           792:             if (uint_arg) arg_sign = 1;
        !           793:             break;
        !           794:           case 'l':
        !           795:             ulong_arg = va_arg(ap, unsigned long int);
        !           796:             if (ulong_arg) arg_sign = 1;
        !           797:             break;
        !           798: #ifdef SNPRINTF_LONGLONG_SUPPORT
        !           799:           case '2':
        !           800:             ulong_long_arg = va_arg(ap, unsigned long long int);
        !           801:             if (ulong_long_arg) arg_sign = 1;
        !           802:             break;
        !           803: #endif
        !           804:           }
        !           805:         }
        !           806:         str_arg = tmp; str_arg_l = 0;
        !           807:      /* NOTE:
        !           808:       *   For d, i, u, o, x, and X conversions, if precision is specified,
        !           809:       *   the '0' flag should be ignored. This is so with Solaris 2.6,
        !           810:       *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
        !           811:       */
        !           812: #ifndef PERL_COMPATIBLE
        !           813:         if (precision_specified) zero_padding = 0;
        !           814: #endif
        !           815:         if (fmt_spec == 'd') {
        !           816:           if (force_sign && arg_sign >= 0)
        !           817:             tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
        !           818:          /* leave negative numbers for sprintf to handle,
        !           819:             to avoid handling tricky cases like (short int)(-32768) */
        !           820: #ifdef LINUX_COMPATIBLE
        !           821:         } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
        !           822:           tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
        !           823: #endif
        !           824:         } else if (alternate_form) {
        !           825:           if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
        !           826:             { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
        !           827:          /* alternate form should have no effect for p conversion, but ... */
        !           828: #ifdef HPUX_COMPATIBLE
        !           829:           else if (fmt_spec == 'p'
        !           830:          /* HPUX 10: for an alternate form of p conversion,
        !           831:           *          a nonzero result is prefixed by 0x. */
        !           832: #ifndef HPUX_BUG_COMPATIBLE
        !           833:          /* Actually it uses 0x prefix even for a zero value. */
        !           834:                    && arg_sign != 0
        !           835: #endif
        !           836:                   ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
        !           837: #endif
        !           838:         }
        !           839:         zero_padding_insertion_ind = str_arg_l;
        !           840:         if (!precision_specified) precision = 1;   /* default precision is 1 */
        !           841:         if (precision == 0 && arg_sign == 0
        !           842: #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
        !           843:             && fmt_spec != 'p'
        !           844:          /* HPUX 10 man page claims: With conversion character p the result of
        !           845:           * converting a zero value with a precision of zero is a null string.
        !           846:           * Actually HP returns all zeroes, and Linux returns "(nil)". */
        !           847: #endif
        !           848:         ) {
        !           849:          /* converted to null string */
        !           850:          /* When zero value is formatted with an explicit precision 0,
        !           851:             the resulting formatted string is empty (d, i, u, o, x, X, p).   */
        !           852:         } else {
        !           853:           char f[5]; int f_l = 0;
        !           854:           f[f_l++] = '%';    /* construct a simple format string for sprintf */
        !           855:           if (!length_modifier) { }
        !           856:           else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
        !           857:           else f[f_l++] = length_modifier;
        !           858:           f[f_l++] = fmt_spec; f[f_l++] = '\0';
        !           859:           if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
        !           860:           else if (fmt_spec == 'd') {  /* signed */
        !           861:             switch (length_modifier) {
        !           862:             case '\0':
        !           863:             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;
        !           864:             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
        !           865: #ifdef SNPRINTF_LONGLONG_SUPPORT
        !           866:             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
        !           867: #endif
        !           868:             }
        !           869:           } else {  /* unsigned */
        !           870:             switch (length_modifier) {
        !           871:             case '\0':
        !           872:             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;
        !           873:             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
        !           874: #ifdef SNPRINTF_LONGLONG_SUPPORT
        !           875:             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
        !           876: #endif
        !           877:             }
        !           878:           }
        !           879:          /* include the optional minus sign and possible "0x"
        !           880:             in the region before the zero padding insertion point */
        !           881:           if (zero_padding_insertion_ind < str_arg_l &&
        !           882:               tmp[zero_padding_insertion_ind] == '-') {
        !           883:             zero_padding_insertion_ind++;
        !           884:           }
        !           885:           if (zero_padding_insertion_ind+1 < str_arg_l &&
        !           886:               tmp[zero_padding_insertion_ind]   == '0' &&
        !           887:              (tmp[zero_padding_insertion_ind+1] == 'x' ||
        !           888:               tmp[zero_padding_insertion_ind+1] == 'X') ) {
        !           889:             zero_padding_insertion_ind += 2;
        !           890:           }
        !           891:         }
        !           892:         { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
        !           893:           if (alternate_form && fmt_spec == 'o'
        !           894: #ifdef HPUX_COMPATIBLE                                  /* ("%#.o",0) -> ""  */
        !           895:               && (str_arg_l > 0)
        !           896: #endif
        !           897: #ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* ("%#o",0) -> "00" */
        !           898: #else
        !           899:               /* unless zero is already the first character */
        !           900:               && !(zero_padding_insertion_ind < str_arg_l
        !           901:                    && tmp[zero_padding_insertion_ind] == '0')
        !           902: #endif
        !           903:           ) {        /* assure leading zero for alternate-form octal numbers */
        !           904:             if (!precision_specified || precision < num_of_digits+1) {
        !           905:              /* precision is increased to force the first character to be zero,
        !           906:                 except if a zero value is formatted with an explicit precision
        !           907:                 of zero */
        !           908:               precision = num_of_digits+1; precision_specified = 1;
        !           909:             }
        !           910:           }
        !           911:        /* zero padding to specified precision? */
        !           912:           if (num_of_digits < precision) 
        !           913:             number_of_zeros_to_pad = precision - num_of_digits;
        !           914:         }
        !           915:      /* zero padding to specified minimal field width? */
        !           916:         if (!justify_left && zero_padding) {
        !           917:           int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
        !           918:           if (n > 0) number_of_zeros_to_pad += n;
        !           919:         }
        !           920:         break;
        !           921:       }
        !           922:       default: /* unrecognized conversion specifier, keep format string as-is*/
        !           923:         zero_padding = 0;  /* turn zero padding off for non-numeric convers. */
        !           924: #ifndef DIGITAL_UNIX_COMPATIBLE
        !           925:         justify_left = 1; min_field_width = 0;                /* reset flags */
        !           926: #endif
        !           927: #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
        !           928:      /* keep the entire format string unchanged */
        !           929:         str_arg = starting_p; str_arg_l = p - starting_p;
        !           930:      /* well, not exactly so for Linux, which does something inbetween,
        !           931:       * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
        !           932: #else
        !           933:      /* discard the unrecognized conversion, just keep *
        !           934:       * the unrecognized conversion character          */
        !           935:         str_arg = p; str_arg_l = 0;
        !           936: #endif
        !           937:         if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged
        !           938:                                  if not at end-of-string */
        !           939:         break;
        !           940:       }
        !           941:       if (*p) p++;      /* step over the just processed conversion specifier */
        !           942:    /* insert padding to the left as requested by min_field_width;
        !           943:       this does not include the zero padding in case of numerical conversions*/
        !           944:       if (!justify_left) {                /* left padding with blank or zero */
        !           945:         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
        !           946:         if (n > 0) {
        !           947:           if (str_l < str_m) {
        !           948:             size_t avail = str_m-str_l;
        !           949:             fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
        !           950:           }
        !           951:           str_l += n;
        !           952:         }
        !           953:       }
        !           954:    /* zero padding as requested by the precision or by the minimal field width
        !           955:     * for numeric conversions required? */
        !           956:       if (number_of_zeros_to_pad <= 0) {
        !           957:      /* will not copy first part of numeric right now, *
        !           958:       * force it to be copied later in its entirety    */
        !           959:         zero_padding_insertion_ind = 0;
        !           960:       } else {
        !           961:      /* insert first part of numerics (sign or '0x') before zero padding */
        !           962:         int n = zero_padding_insertion_ind;
        !           963:         if (n > 0) {
        !           964:           if (str_l < str_m) {
        !           965:             size_t avail = str_m-str_l;
        !           966:             fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
        !           967:           }
        !           968:           str_l += n;
        !           969:         }
        !           970:      /* insert zero padding as requested by the precision or min field width */
        !           971:         n = number_of_zeros_to_pad;
        !           972:         if (n > 0) {
        !           973:           if (str_l < str_m) {
        !           974:             size_t avail = str_m-str_l;
        !           975:             fast_memset(str+str_l, '0', (n>avail?avail:n));
        !           976:           }
        !           977:           str_l += n;
        !           978:         }
        !           979:       }
        !           980:    /* insert formatted string
        !           981:     * (or as-is conversion specifier for unknown conversions) */
        !           982:       { int n = str_arg_l - zero_padding_insertion_ind;
        !           983:         if (n > 0) {
        !           984:           if (str_l < str_m) {
        !           985:             size_t avail = str_m-str_l;
        !           986:             fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
        !           987:                         (n>avail?avail:n));
        !           988:           }
        !           989:           str_l += n;
        !           990:         }
        !           991:       }
        !           992:    /* insert right padding */
        !           993:       if (justify_left) {          /* right blank padding to the field width */
        !           994:         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
        !           995:         if (n > 0) {
        !           996:           if (str_l < str_m) {
        !           997:             size_t avail = str_m-str_l;
        !           998:             fast_memset(str+str_l, ' ', (n>avail?avail:n));
        !           999:           }
        !          1000:           str_l += n;
        !          1001:         }
        !          1002:       }
        !          1003:     }
        !          1004:   }
        !          1005: #if defined(NEED_SNPRINTF_ONLY)
        !          1006:   va_end(ap);
        !          1007: #endif
        !          1008:   if (str_m > 0) { /* make sure the string is null-terminated
        !          1009:                       even at the expense of overwriting the last character
        !          1010:                       (shouldn't happen, but just in case) */
        !          1011:     str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
        !          1012:   }
        !          1013:   /* Return the number of characters formatted (excluding trailing null
        !          1014:    * character), that is, the number of characters that would have been
        !          1015:    * written to the buffer if it were large enough.
        !          1016:    *
        !          1017:    * The value of str_l should be returned, but str_l is of unsigned type
        !          1018:    * size_t, and snprintf is int, possibly leading to an undetected
        !          1019:    * integer overflow, resulting in a negative return value, which is illegal.
        !          1020:    * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
        !          1021:    * Should errno be set to EOVERFLOW and EOF returned in this case???
        !          1022:    */
        !          1023:   return (int) str_l;
        !          1024: }
        !          1025: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.