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

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:  */
1.1.1.2 ! root      280: 
1.1       root      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)
1.1.1.2 ! root      343: #  define breakeven_point   2   /* AXP (DEC Alpha)     - gcc or cc or egcs */
1.1       root      344: #endif
                    345: #if defined(__i386__)  || defined(__i386)
1.1.1.2 ! root      346: #  define breakeven_point  12   /* Intel Pentium/Linux - gcc 2.96 */
1.1       root      347: #endif
                    348: #if defined(__hppa)
1.1.1.2 ! root      349: #  define breakeven_point  10   /* HP-PA               - gcc */
1.1       root      350: #endif
                    351: #if defined(__sparc__) || defined(__sparc)
1.1.1.2 ! root      352: #  define breakeven_point  33   /* Sun Sparc 5         - gcc 2.8.1 */
1.1       root      353: #endif
                    354: 
                    355: /* some other values of possible interest: */
1.1.1.2 ! root      356: /* #define breakeven_point  8 *//* VAX 4000          - vaxc */
        !           357: /* #define breakeven_point 19 *//* VAX 4000          - gcc 2.7.0 */
1.1       root      358: 
                    359: #ifndef breakeven_point
1.1.1.2 ! root      360: #  define breakeven_point   6   /* some reasonable one-size-fits-all value */
1.1       root      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)
1.1.1.2 ! root      380: int asprintf(char **ptr, const char *fmt, /*args */ ...);
1.1       root      381: #endif
                    382: #if defined(NEED_VASPRINTF)
1.1.1.2 ! root      383: int vasprintf(char **ptr, const char *fmt, va_list ap);
1.1       root      384: #endif
                    385: #if defined(NEED_ASNPRINTF)
1.1.1.2 ! root      386: int asnprintf(char **ptr, size_t str_m, const char *fmt, /*args */ ...);
1.1       root      387: #endif
                    388: #if defined(NEED_VASNPRINTF)
1.1.1.2 ! root      389: int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
1.1       root      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)
1.1.1.2 ! root      404: int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args */
        !           405:                       ...);
1.1       root      406: #if !defined(NEED_SNPRINTF_ONLY)
                    407: int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
                    408: #endif
                    409: #endif
                    410: 
                    411: /* declarations */
                    412: 
                    413: static char credits[] = "\n\
                    414: @(#)snprintf.c, v2.2: Mark Martinec, <[email protected]>\n\
                    415: @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
                    416: @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
                    417: 
                    418: #if defined(NEED_ASPRINTF)
1.1.1.2 ! root      419: int asprintf(char **ptr, const char *fmt, /*args */ ...)
        !           420: {
1.1       root      421:   va_list ap;
                    422:   size_t str_m;
                    423:   int str_l;
                    424: 
                    425:   *ptr = NULL;
1.1.1.2 ! root      426:   va_start(ap, fmt);            /* measure the required size */
        !           427:   str_l = portable_vsnprintf(NULL, (size_t) 0, fmt, ap);
1.1       root      428:   va_end(ap);
1.1.1.2 ! root      429:   assert(str_l >= 0);           /* possible integer overflow if str_m > INT_MAX */
        !           430:   *ptr = (char *)malloc(str_m = (size_t) str_l + 1);
        !           431:   if (*ptr == NULL) {
        !           432:     errno = ENOMEM;
        !           433:     str_l = -1;
        !           434:   } else {
1.1       root      435:     int str_l2;
                    436:     va_start(ap, fmt);
                    437:     str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
                    438:     va_end(ap);
                    439:     assert(str_l2 == str_l);
                    440:   }
                    441:   return str_l;
                    442: }
                    443: #endif
                    444: 
                    445: #if defined(NEED_VASPRINTF)
1.1.1.2 ! root      446: int vasprintf(char **ptr, const char *fmt, va_list ap)
        !           447: {
1.1       root      448:   size_t str_m;
                    449:   int str_l;
                    450: 
                    451:   *ptr = NULL;
1.1.1.2 ! root      452:   {
        !           453:     va_list ap2;
        !           454:     va_copy(ap2, ap);           /* don't consume the original ap, we'll need it again */
        !           455:     str_l = portable_vsnprintf(NULL, (size_t) 0, fmt, ap2);     /*get required size */
1.1       root      456:     va_end(ap2);
                    457:   }
1.1.1.2 ! root      458:   assert(str_l >= 0);           /* possible integer overflow if str_m > INT_MAX */
        !           459:   *ptr = (char *)malloc(str_m = (size_t) str_l + 1);
        !           460:   if (*ptr == NULL) {
        !           461:     errno = ENOMEM;
        !           462:     str_l = -1;
        !           463:   } else {
1.1       root      464:     int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
                    465:     assert(str_l2 == str_l);
                    466:   }
                    467:   return str_l;
                    468: }
                    469: #endif
                    470: 
                    471: #if defined(NEED_ASNPRINTF)
1.1.1.2 ! root      472: int asnprintf(char **ptr, size_t str_m, const char *fmt, /*args */ ...)
        !           473: {
1.1       root      474:   va_list ap;
                    475:   int str_l;
                    476: 
                    477:   *ptr = NULL;
1.1.1.2 ! root      478:   va_start(ap, fmt);            /* measure the required size */
        !           479:   str_l = portable_vsnprintf(NULL, (size_t) 0, fmt, ap);
1.1       root      480:   va_end(ap);
1.1.1.2 ! root      481:   assert(str_l >= 0);           /* possible integer overflow if str_m > INT_MAX */
        !           482:   if ((size_t) str_l + 1 < str_m)
        !           483:     str_m = (size_t) str_l + 1; /* truncate */
1.1       root      484:   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1.1.1.2 ! root      485:   if (str_m == 0) {             /* not interested in resulting string, just return size */
1.1       root      486:   } else {
1.1.1.2 ! root      487:     *ptr = (char *)malloc(str_m);
        !           488:     if (*ptr == NULL) {
        !           489:       errno = ENOMEM;
        !           490:       str_l = -1;
        !           491:     } else {
1.1       root      492:       int str_l2;
                    493:       va_start(ap, fmt);
                    494:       str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
                    495:       va_end(ap);
                    496:       assert(str_l2 == str_l);
                    497:     }
                    498:   }
                    499:   return str_l;
                    500: }
                    501: #endif
                    502: 
                    503: #if defined(NEED_VASNPRINTF)
1.1.1.2 ! root      504: int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap)
        !           505: {
1.1       root      506:   int str_l;
                    507: 
                    508:   *ptr = NULL;
1.1.1.2 ! root      509:   {
        !           510:     va_list ap2;
        !           511:     va_copy(ap2, ap);           /* don't consume the original ap, we'll need it again */
        !           512:     str_l = portable_vsnprintf(NULL, (size_t) 0, fmt, ap2);     /*get required size */
1.1       root      513:     va_end(ap2);
                    514:   }
1.1.1.2 ! root      515:   assert(str_l >= 0);           /* possible integer overflow if str_m > INT_MAX */
        !           516:   if ((size_t) str_l + 1 < str_m)
        !           517:     str_m = (size_t) str_l + 1; /* truncate */
1.1       root      518:   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1.1.1.2 ! root      519:   if (str_m == 0) {             /* not interested in resulting string, just return size */
1.1       root      520:   } else {
1.1.1.2 ! root      521:     *ptr = (char *)malloc(str_m);
        !           522:     if (*ptr == NULL) {
        !           523:       errno = ENOMEM;
        !           524:       str_l = -1;
        !           525:     } else {
1.1       root      526:       int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
                    527:       assert(str_l2 == str_l);
                    528:     }
                    529:   }
                    530:   return str_l;
                    531: }
                    532: #endif
                    533: 
                    534: /*
                    535:  * If the system does have snprintf and the portable routine is not
                    536:  * specifically required, this module produces no code for snprintf/vsnprintf.
                    537:  */
                    538: #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
                    539: 
                    540: #if !defined(NEED_SNPRINTF_ONLY)
1.1.1.2 ! root      541: int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args */ ...)
        !           542: {
1.1       root      543:   va_list ap;
                    544:   int str_l;
                    545: 
                    546:   va_start(ap, fmt);
                    547:   str_l = portable_vsnprintf(str, str_m, fmt, ap);
                    548:   va_end(ap);
                    549:   return str_l;
                    550: }
                    551: #endif
                    552: 
                    553: #if defined(NEED_SNPRINTF_ONLY)
1.1.1.2 ! root      554: int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args */ ...)
        !           555: {
1.1       root      556: #else
1.1.1.2 ! root      557: int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
        !           558: {
1.1       root      559: #endif
                    560: 
                    561: #if defined(NEED_SNPRINTF_ONLY)
                    562:   va_list ap;
                    563: #endif
                    564:   size_t str_l = 0;
                    565:   const char *p = fmt;
                    566: 
                    567: /* In contrast with POSIX, the ISO C99 now says
                    568:  * that str can be NULL and str_m can be 0.
                    569:  * This is more useful than the old:  if (str_m < 1) return -1; */
                    570: 
                    571: #if defined(NEED_SNPRINTF_ONLY)
                    572:   va_start(ap, fmt);
                    573: #endif
1.1.1.2 ! root      574:   if (!p)
        !           575:     p = "";
1.1       root      576:   while (*p) {
                    577:     if (*p != '%') {
1.1.1.2 ! root      578:       /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
        !           579:       /* but the following code achieves better performance for cases
        !           580:        * where format string is long and contains few conversions */
        !           581:       const char *q = strchr(p + 1, '%');
        !           582:       size_t n = !q ? strlen(p) : (q - p);
1.1       root      583:       if (str_l < str_m) {
1.1.1.2 ! root      584:         size_t avail = str_m - str_l;
        !           585:         fast_memcpy(str + str_l, p, (n > avail ? avail : n));
1.1       root      586:       }
1.1.1.2 ! root      587:       p += n;
        !           588:       str_l += n;
1.1       root      589:     } else {
                    590:       const char *starting_p;
                    591:       size_t min_field_width = 0, precision = 0;
                    592:       int zero_padding = 0, precision_specified = 0, justify_left = 0;
                    593:       int alternate_form = 0, force_sign = 0;
1.1.1.2 ! root      594:       int space_for_positive = 1;       /* If both the ' ' and '+' flags appear,
        !           595:                                            the ' ' flag should be ignored. */
        !           596:       char length_modifier = '\0';      /* allowed values: \0, h, l, L */
        !           597:       char tmp[32];             /* temporary buffer for simple numeric->string conversion */
1.1       root      598: 
                    599:       const char *str_arg;      /* string address in case of string argument */
                    600:       size_t str_arg_l;         /* natural field width of arg without padding
                    601:                                    and sign */
                    602:       unsigned char uchar_arg;
1.1.1.2 ! root      603:       /* unsigned char argument value - only defined for c conversion.
        !           604:          N.B. standard explicitly states the char argument for
        !           605:          the c conversion is unsigned */
1.1       root      606: 
                    607:       size_t number_of_zeros_to_pad = 0;
1.1.1.2 ! root      608:       /* number of zeros to be inserted for numeric conversions
        !           609:          as required by the precision or minimal field width */
1.1       root      610: 
                    611:       size_t zero_padding_insertion_ind = 0;
1.1.1.2 ! root      612:       /* index into tmp where zero padding is to be inserted */
1.1       root      613: 
                    614:       char fmt_spec = '\0';
1.1.1.2 ! root      615:       /* current conversion specifier character */
1.1       root      616: 
1.1.1.2 ! root      617:       str_arg = credits;        /* just to make compiler happy (defined but not used) */
1.1       root      618:       str_arg = NULL;
1.1.1.2 ! root      619:       starting_p = p;
        !           620:       p++;                      /* skip '%' */
        !           621:       /* parse flags */
1.1       root      622:       while (*p == '0' || *p == '-' || *p == '+' ||
                    623:              *p == ' ' || *p == '#' || *p == '\'') {
                    624:         switch (*p) {
1.1.1.2 ! root      625:         case '0':
        !           626:           zero_padding = 1;
        !           627:           break;
        !           628:         case '-':
        !           629:           justify_left = 1;
        !           630:           break;
        !           631:         case '+':
        !           632:           force_sign = 1;
        !           633:           space_for_positive = 0;
        !           634:           break;
        !           635:         case ' ':
        !           636:           force_sign = 1;
        !           637:           /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
1.1       root      638: #ifdef PERL_COMPATIBLE
1.1.1.2 ! root      639:           /* ... but in Perl the last of ' ' and '+' applies */
        !           640:           space_for_positive = 1;
1.1       root      641: #endif
1.1.1.2 ! root      642:           break;
        !           643:         case '#':
        !           644:           alternate_form = 1;
        !           645:           break;
        !           646:         case '\'':
        !           647:           break;
1.1       root      648:         }
                    649:         p++;
                    650:       }
1.1.1.2 ! root      651:       /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
1.1       root      652: 
1.1.1.2 ! root      653:       /* parse field width */
1.1       root      654:       if (*p == '*') {
                    655:         int j;
1.1.1.2 ! root      656:         p++;
        !           657:         j = va_arg(ap, int);
        !           658:         if (j >= 0)
        !           659:           min_field_width = j;
        !           660:         else {
        !           661:           min_field_width = -j;
        !           662:           justify_left = 1;
        !           663:         }
1.1       root      664:       } else if (isdigit((int)(*p))) {
                    665:         /* size_t could be wider than unsigned int;
                    666:            make sure we treat argument like common implementations do */
                    667:         unsigned int uj = *p++ - '0';
1.1.1.2 ! root      668:         while (isdigit((int)(*p)))
        !           669:           uj = 10 * uj + (unsigned int)(*p++ - '0');
1.1       root      670:         min_field_width = uj;
                    671:       }
1.1.1.2 ! root      672:       /* parse precision */
1.1       root      673:       if (*p == '.') {
1.1.1.2 ! root      674:         p++;
        !           675:         precision_specified = 1;
1.1       root      676:         if (*p == '*') {
                    677:           int j = va_arg(ap, int);
                    678:           p++;
1.1.1.2 ! root      679:           if (j >= 0)
        !           680:             precision = j;
1.1       root      681:           else {
1.1.1.2 ! root      682:             precision_specified = 0;
        !           683:             precision = 0;
        !           684:             /* NOTE:
        !           685:              *   Solaris 2.6 man page claims that in this case the precision
        !           686:              *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
        !           687:              *   claim that this case should be treated as unspecified precision,
        !           688:              *   which is what we do here.
        !           689:              */
1.1       root      690:           }
                    691:         } else if (isdigit((int)(*p))) {
                    692:           /* size_t could be wider than unsigned int;
                    693:              make sure we treat argument like common implementations do */
                    694:           unsigned int uj = *p++ - '0';
1.1.1.2 ! root      695:           while (isdigit((int)(*p)))
        !           696:             uj = 10 * uj + (unsigned int)(*p++ - '0');
1.1       root      697:           precision = uj;
                    698:         }
                    699:       }
1.1.1.2 ! root      700:       /* parse 'h', 'l' and 'll' length modifiers */
1.1       root      701:       if (*p == 'h' || *p == 'l') {
1.1.1.2 ! root      702:         length_modifier = *p;
        !           703:         p++;
        !           704:         if (length_modifier == 'l' && *p == 'l') {      /* double l = long long */
1.1       root      705: #ifdef SNPRINTF_LONGLONG_SUPPORT
1.1.1.2 ! root      706:           length_modifier = '2';        /* double l encoded as '2' */
1.1       root      707: #else
1.1.1.2 ! root      708:           length_modifier = 'l';        /* treat it as a single 'l' */
1.1       root      709: #endif
                    710:           p++;
                    711:         }
                    712:       }
                    713:       fmt_spec = *p;
1.1.1.2 ! root      714:       /* common synonyms: */
1.1       root      715:       switch (fmt_spec) {
1.1.1.2 ! root      716:       case 'i':
        !           717:         fmt_spec = 'd';
        !           718:         break;
        !           719:       case 'D':
        !           720:         fmt_spec = 'd';
        !           721:         length_modifier = 'l';
        !           722:         break;
        !           723:       case 'U':
        !           724:         fmt_spec = 'u';
        !           725:         length_modifier = 'l';
        !           726:         break;
        !           727:       case 'O':
        !           728:         fmt_spec = 'o';
        !           729:         length_modifier = 'l';
        !           730:         break;
        !           731:       default:
        !           732:         break;
1.1       root      733:       }
1.1.1.2 ! root      734:       /* get parameter value, do initial processing */
1.1       root      735:       switch (fmt_spec) {
1.1.1.2 ! root      736:       case '%':                /* % behaves similar to 's' regarding flags and field widths */
        !           737:       case 'c':                /* c behaves similar to 's' regarding flags and field widths */
1.1       root      738:       case 's':
1.1.1.2 ! root      739:         length_modifier = '\0'; /* wint_t and wchar_t not supported */
        !           740:         /* the result of zero padding flag with non-numeric conversion specifier */
        !           741:         /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
        !           742:         /* Digital Unix and Linux does not. */
1.1       root      743: #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
1.1.1.2 ! root      744:         zero_padding = 0;       /* turn zero padding off for string conversions */
1.1       root      745: #endif
                    746:         str_arg_l = 1;
                    747:         switch (fmt_spec) {
                    748:         case '%':
1.1.1.2 ! root      749:           str_arg = p;
1.1       root      750:           break;
1.1.1.2 ! root      751:         case 'c':
        !           752:           {
        !           753:             int j = va_arg(ap, int);
        !           754:             uchar_arg = (unsigned char)j;       /* standard demands unsigned char */
        !           755:             str_arg = (const char *)&uchar_arg;
        !           756:             break;
        !           757:           }
1.1       root      758:         case 's':
                    759:           str_arg = va_arg(ap, const char *);
1.1.1.2 ! root      760:           if (!str_arg)
        !           761:             str_arg_l = 0;
        !           762:           /* make sure not to address string beyond the specified precision !!! */
        !           763:           else if (!precision_specified)
        !           764:             str_arg_l = strlen(str_arg);
        !           765:           /* truncate string if necessary as requested by precision */
        !           766:           else if (precision == 0)
        !           767:             str_arg_l = 0;
1.1       root      768:           else {
1.1.1.2 ! root      769:             /* memchr on HP does not like n > 2^31  !!! */
1.1       root      770:             const char *q = memchr(str_arg, '\0',
1.1.1.2 ! root      771:                                    precision <=
        !           772:                                    0x7fffffff ? precision : 0x7fffffff);
        !           773:             str_arg_l = !q ? precision : (q - str_arg);
1.1       root      774:           }
                    775:           break;
1.1.1.2 ! root      776:         default:
        !           777:           break;
1.1       root      778:         }
                    779:         break;
1.1.1.2 ! root      780:       case 'd':
        !           781:       case 'u':
        !           782:       case 'o':
        !           783:       case 'x':
        !           784:       case 'X':
        !           785:       case 'p':
        !           786:         {
        !           787:           /* NOTE: the u, o, x, X and p conversion specifiers imply
        !           788:              the value is unsigned;  d implies a signed value */
1.1       root      789: 
1.1.1.2 ! root      790:           int arg_sign = 0;
1.1       root      791:           /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
1.1.1.2 ! root      792:              +1 if greater than zero (or nonzero for unsigned arguments),
        !           793:              -1 if negative (unsigned argument is never negative) */
1.1       root      794: 
1.1.1.2 ! root      795:           int int_arg = 0;
        !           796:           unsigned int uint_arg = 0;
1.1       root      797:           /* only defined for length modifier h, or for no length modifiers */
                    798: 
1.1.1.2 ! root      799:           long int long_arg = 0;
        !           800:           unsigned long int ulong_arg = 0;
1.1       root      801:           /* only defined for length modifier l */
                    802: 
1.1.1.2 ! root      803:           void *ptr_arg = NULL;
1.1       root      804:           /* pointer argument value -only defined for p conversion */
                    805: 
                    806: #ifdef SNPRINTF_LONGLONG_SUPPORT
1.1.1.2 ! root      807:           long long int long_long_arg = 0;
        !           808:           unsigned long long int ulong_long_arg = 0;
1.1       root      809:           /* only defined for length modifier ll */
                    810: #endif
1.1.1.2 ! root      811:           if (fmt_spec == 'p') {
        !           812:             /* HPUX 10: An l, h, ll or L before any other conversion character
        !           813:              *   (other than d, i, u, o, x, or X) is ignored.
        !           814:              * Digital Unix:
        !           815:              *   not specified, but seems to behave as HPUX does.
        !           816:              * Solaris: If an h, l, or L appears before any other conversion
        !           817:              *   specifier (other than d, i, u, o, x, or X), the behavior
        !           818:              *   is undefined. (Actually %hp converts only 16-bits of address
        !           819:              *   and %llp treats address as 64-bit data which is incompatible
        !           820:              *   with (void *) argument on a 32-bit system).
        !           821:              */
1.1       root      822: #ifdef SOLARIS_COMPATIBLE
                    823: #  ifdef SOLARIS_BUG_COMPATIBLE
1.1.1.2 ! root      824:             /* keep length modifiers even if it represents 'll' */
1.1       root      825: #  else
1.1.1.2 ! root      826:             if (length_modifier == '2')
        !           827:               length_modifier = '\0';
1.1       root      828: #  endif
                    829: #else
1.1.1.2 ! root      830:             length_modifier = '\0';
1.1       root      831: #endif
1.1.1.2 ! root      832:             ptr_arg = va_arg(ap, void *);
        !           833:             if (ptr_arg != NULL)
        !           834:               arg_sign = 1;
        !           835:           } else if (fmt_spec == 'd') { /* signed */
        !           836:             switch (length_modifier) {
        !           837:             case '\0':
        !           838:             case 'h':
        !           839:               /* It is non-portable to specify a second argument of char or short
        !           840:                * to va_arg, because arguments seen by the called function
        !           841:                * are not char or short.  C converts char and short arguments
        !           842:                * to int before passing them to a function.
        !           843:                */
        !           844:               int_arg = va_arg(ap, int);
        !           845:               if (int_arg > 0)
        !           846:                 arg_sign = 1;
        !           847:               else if (int_arg < 0)
        !           848:                 arg_sign = -1;
        !           849:               break;
        !           850:             case 'l':
        !           851:               long_arg = va_arg(ap, long int);
        !           852:               if (long_arg > 0)
        !           853:                 arg_sign = 1;
        !           854:               else if (long_arg < 0)
        !           855:                 arg_sign = -1;
        !           856:               break;
1.1       root      857: #ifdef SNPRINTF_LONGLONG_SUPPORT
1.1.1.2 ! root      858:             case '2':
        !           859:               long_long_arg = va_arg(ap, long long int);
        !           860:               if (long_long_arg > 0)
        !           861:                 arg_sign = 1;
        !           862:               else if (long_long_arg < 0)
        !           863:                 arg_sign = -1;
        !           864:               break;
1.1       root      865: #endif
1.1.1.2 ! root      866:             }
        !           867:           } else {              /* unsigned */
        !           868:             switch (length_modifier) {
        !           869:             case '\0':
        !           870:             case 'h':
        !           871:               uint_arg = va_arg(ap, unsigned int);
        !           872:               if (uint_arg)
        !           873:                 arg_sign = 1;
        !           874:               break;
        !           875:             case 'l':
        !           876:               ulong_arg = va_arg(ap, unsigned long int);
        !           877:               if (ulong_arg)
        !           878:                 arg_sign = 1;
        !           879:               break;
1.1       root      880: #ifdef SNPRINTF_LONGLONG_SUPPORT
1.1.1.2 ! root      881:             case '2':
        !           882:               ulong_long_arg = va_arg(ap, unsigned long long int);
        !           883:               if (ulong_long_arg)
        !           884:                 arg_sign = 1;
        !           885:               break;
1.1       root      886: #endif
1.1.1.2 ! root      887:             }
1.1       root      888:           }
1.1.1.2 ! root      889:           str_arg = tmp;
        !           890:           str_arg_l = 0;
        !           891:           /* NOTE:
        !           892:            *   For d, i, u, o, x, and X conversions, if precision is specified,
        !           893:            *   the '0' flag should be ignored. This is so with Solaris 2.6,
        !           894:            *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
        !           895:            */
1.1       root      896: #ifndef PERL_COMPATIBLE
1.1.1.2 ! root      897:           if (precision_specified)
        !           898:             zero_padding = 0;
1.1       root      899: #endif
1.1.1.2 ! root      900:           if (fmt_spec == 'd') {
        !           901:             if (force_sign && arg_sign >= 0)
        !           902:               tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
        !           903:             /* leave negative numbers for sprintf to handle,
        !           904:                to avoid handling tricky cases like (short int)(-32768) */
1.1       root      905: #ifdef LINUX_COMPATIBLE
1.1.1.2 ! root      906:           } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
        !           907:             tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1.1       root      908: #endif
1.1.1.2 ! root      909:           } else if (alternate_form) {
        !           910:             if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X')) {
        !           911:               tmp[str_arg_l++] = '0';
        !           912:               tmp[str_arg_l++] = fmt_spec;
        !           913:             }
        !           914:             /* alternate form should have no effect for p conversion, but ... */
1.1       root      915: #ifdef HPUX_COMPATIBLE
1.1.1.2 ! root      916:             else if (fmt_spec == 'p'
        !           917:                      /* HPUX 10: for an alternate form of p conversion,
        !           918:                       *          a nonzero result is prefixed by 0x. */
1.1       root      919: #ifndef HPUX_BUG_COMPATIBLE
1.1.1.2 ! root      920:                      /* Actually it uses 0x prefix even for a zero value. */
        !           921:                      && arg_sign != 0
1.1       root      922: #endif
1.1.1.2 ! root      923:               ) {
        !           924:               tmp[str_arg_l++] = '0';
        !           925:               tmp[str_arg_l++] = 'x';
        !           926:             }
1.1       root      927: #endif
1.1.1.2 ! root      928:           }
        !           929:           zero_padding_insertion_ind = str_arg_l;
        !           930:           if (!precision_specified)
        !           931:             precision = 1;      /* default precision is 1 */
        !           932:           if (precision == 0 && arg_sign == 0
1.1       root      933: #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1.1.1.2 ! root      934:               && fmt_spec != 'p'
        !           935:               /* HPUX 10 man page claims: With conversion character p the result of
        !           936:                * converting a zero value with a precision of zero is a null string.
        !           937:                * Actually HP returns all zeroes, and Linux returns "(nil)". */
        !           938: #endif
        !           939:             ) {
        !           940:             /* converted to null string */
        !           941:             /* When zero value is formatted with an explicit precision 0,
        !           942:                the resulting formatted string is empty (d, i, u, o, x, X, p).   */
        !           943:           } else {
        !           944:             char f[5];
        !           945:             int f_l = 0;
        !           946:             f[f_l++] = '%';     /* construct a simple format string for sprintf */
        !           947:             if (!length_modifier) {
        !           948:             } else if (length_modifier == '2') {
        !           949:               f[f_l++] = 'l';
        !           950:               f[f_l++] = 'l';
        !           951:             } else
        !           952:               f[f_l++] = length_modifier;
        !           953:             f[f_l++] = fmt_spec;
        !           954:             f[f_l++] = '\0';
        !           955:             if (fmt_spec == 'p')
        !           956:               str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
        !           957:             else if (fmt_spec == 'd') { /* signed */
        !           958:               switch (length_modifier) {
        !           959:               case '\0':
        !           960:               case 'h':
        !           961:                 str_arg_l += sprintf(tmp + str_arg_l, f, int_arg);
        !           962:                 break;
        !           963:               case 'l':
        !           964:                 str_arg_l += sprintf(tmp + str_arg_l, f, long_arg);
        !           965:                 break;
1.1       root      966: #ifdef SNPRINTF_LONGLONG_SUPPORT
1.1.1.2 ! root      967:               case '2':
        !           968:                 str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg);
        !           969:                 break;
        !           970: #endif
        !           971:               }
        !           972:             } else {            /* unsigned */
        !           973:               switch (length_modifier) {
        !           974:               case '\0':
        !           975:               case 'h':
        !           976:                 str_arg_l += sprintf(tmp + str_arg_l, f, uint_arg);
        !           977:                 break;
        !           978:               case 'l':
        !           979:                 str_arg_l += sprintf(tmp + str_arg_l, f, ulong_arg);
        !           980:                 break;
1.1       root      981: #ifdef SNPRINTF_LONGLONG_SUPPORT
1.1.1.2 ! root      982:               case '2':
        !           983:                 str_arg_l += sprintf(tmp + str_arg_l, f, ulong_long_arg);
        !           984:                 break;
1.1       root      985: #endif
1.1.1.2 ! root      986:               }
        !           987:             }
        !           988:             /* include the optional minus sign and possible "0x"
        !           989:                in the region before the zero padding insertion point */
        !           990:             if (zero_padding_insertion_ind < str_arg_l &&
        !           991:                 tmp[zero_padding_insertion_ind] == '-') {
        !           992:               zero_padding_insertion_ind++;
        !           993:             }
        !           994:             if (zero_padding_insertion_ind + 1 < str_arg_l &&
        !           995:                 tmp[zero_padding_insertion_ind] == '0' &&
        !           996:                 (tmp[zero_padding_insertion_ind + 1] == 'x' ||
        !           997:                  tmp[zero_padding_insertion_ind + 1] == 'X')) {
        !           998:               zero_padding_insertion_ind += 2;
1.1       root      999:             }
                   1000:           }
1.1.1.2 ! root     1001:           {
        !          1002:             size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
        !          1003:             if (alternate_form && fmt_spec == 'o'
        !          1004: #ifdef HPUX_COMPATIBLE          /* ("%#.o",0) -> ""  */
        !          1005:                 && (str_arg_l > 0)
1.1       root     1006: #endif
1.1.1.2 ! root     1007: #ifdef DIGITAL_UNIX_BUG_COMPATIBLE      /* ("%#o",0) -> "00" */
1.1       root     1008: #else
1.1.1.2 ! root     1009:                 /* unless zero is already the first character */
        !          1010:                 && !(zero_padding_insertion_ind < str_arg_l
        !          1011:                      && tmp[zero_padding_insertion_ind] == '0')
        !          1012: #endif
        !          1013:               ) {               /* assure leading zero for alternate-form octal numbers */
        !          1014:               if (!precision_specified || precision < num_of_digits + 1) {
        !          1015:                 /* precision is increased to force the first character to be zero,
        !          1016:                    except if a zero value is formatted with an explicit precision
        !          1017:                    of zero */
        !          1018:                 precision = num_of_digits + 1;
        !          1019:                 precision_specified = 1;
        !          1020:               }
1.1       root     1021:             }
1.1.1.2 ! root     1022:             /* zero padding to specified precision? */
        !          1023:             if (num_of_digits < precision)
        !          1024:               number_of_zeros_to_pad = precision - num_of_digits;
        !          1025:           }
        !          1026:           /* zero padding to specified minimal field width? */
        !          1027:           if (!justify_left && zero_padding) {
        !          1028:             int n = min_field_width - (str_arg_l + number_of_zeros_to_pad);
        !          1029:             if (n > 0)
        !          1030:               number_of_zeros_to_pad += n;
1.1       root     1031:           }
1.1.1.2 ! root     1032:           break;
1.1       root     1033:         }
1.1.1.2 ! root     1034:       default:                 /* unrecognized conversion specifier, keep format string as-is */
        !          1035:         zero_padding = 0;       /* turn zero padding off for non-numeric convers. */
1.1       root     1036: #ifndef DIGITAL_UNIX_COMPATIBLE
1.1.1.2 ! root     1037:         justify_left = 1;
        !          1038:         min_field_width = 0;    /* reset flags */
1.1       root     1039: #endif
                   1040: #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1.1.1.2 ! root     1041:         /* keep the entire format string unchanged */
        !          1042:         str_arg = starting_p;
        !          1043:         str_arg_l = p - starting_p;
        !          1044:         /* well, not exactly so for Linux, which does something inbetween,
        !          1045:          * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
1.1       root     1046: #else
1.1.1.2 ! root     1047:         /* discard the unrecognized conversion, just keep *
        !          1048:          * the unrecognized conversion character          */
        !          1049:         str_arg = p;
        !          1050:         str_arg_l = 0;
        !          1051: #endif
        !          1052:         if (*p)
        !          1053:           str_arg_l++;          /* include invalid conversion specifier unchanged
        !          1054:                                    if not at end-of-string */
1.1       root     1055:         break;
                   1056:       }
1.1.1.2 ! root     1057:       if (*p)
        !          1058:         p++;                    /* step over the just processed conversion specifier */
        !          1059:       /* insert padding to the left as requested by min_field_width;
        !          1060:          this does not include the zero padding in case of numerical conversions */
        !          1061:       if (!justify_left) {      /* left padding with blank or zero */
        !          1062:         int n = min_field_width - (str_arg_l + number_of_zeros_to_pad);
1.1       root     1063:         if (n > 0) {
                   1064:           if (str_l < str_m) {
1.1.1.2 ! root     1065:             size_t avail = str_m - str_l;
        !          1066:             fast_memset(str + str_l, (zero_padding ? '0' : ' '),
        !          1067:                         (n > avail ? avail : n));
1.1       root     1068:           }
                   1069:           str_l += n;
                   1070:         }
                   1071:       }
1.1.1.2 ! root     1072:       /* zero padding as requested by the precision or by the minimal field width
        !          1073:        * for numeric conversions required? */
1.1       root     1074:       if (number_of_zeros_to_pad <= 0) {
1.1.1.2 ! root     1075:         /* will not copy first part of numeric right now, *
        !          1076:          * force it to be copied later in its entirety    */
1.1       root     1077:         zero_padding_insertion_ind = 0;
                   1078:       } else {
1.1.1.2 ! root     1079:         /* insert first part of numerics (sign or '0x') before zero padding */
1.1       root     1080:         int n = zero_padding_insertion_ind;
                   1081:         if (n > 0) {
                   1082:           if (str_l < str_m) {
1.1.1.2 ! root     1083:             size_t avail = str_m - str_l;
        !          1084:             fast_memcpy(str + str_l, str_arg, (n > avail ? avail : n));
1.1       root     1085:           }
                   1086:           str_l += n;
                   1087:         }
1.1.1.2 ! root     1088:         /* insert zero padding as requested by the precision or min field width */
1.1       root     1089:         n = number_of_zeros_to_pad;
                   1090:         if (n > 0) {
                   1091:           if (str_l < str_m) {
1.1.1.2 ! root     1092:             size_t avail = str_m - str_l;
        !          1093:             fast_memset(str + str_l, '0', (n > avail ? avail : n));
1.1       root     1094:           }
                   1095:           str_l += n;
                   1096:         }
                   1097:       }
1.1.1.2 ! root     1098:       /* insert formatted string
        !          1099:        * (or as-is conversion specifier for unknown conversions) */
        !          1100:       {
        !          1101:         int n = str_arg_l - zero_padding_insertion_ind;
1.1       root     1102:         if (n > 0) {
                   1103:           if (str_l < str_m) {
1.1.1.2 ! root     1104:             size_t avail = str_m - str_l;
        !          1105:             fast_memcpy(str + str_l,
        !          1106:                         str_arg + zero_padding_insertion_ind,
        !          1107:                         (n > avail ? avail : n));
1.1       root     1108:           }
                   1109:           str_l += n;
                   1110:         }
                   1111:       }
1.1.1.2 ! root     1112:       /* insert right padding */
        !          1113:       if (justify_left) {       /* right blank padding to the field width */
        !          1114:         int n = min_field_width - (str_arg_l + number_of_zeros_to_pad);
1.1       root     1115:         if (n > 0) {
                   1116:           if (str_l < str_m) {
1.1.1.2 ! root     1117:             size_t avail = str_m - str_l;
        !          1118:             fast_memset(str + str_l, ' ', (n > avail ? avail : n));
1.1       root     1119:           }
                   1120:           str_l += n;
                   1121:         }
                   1122:       }
                   1123:     }
                   1124:   }
                   1125: #if defined(NEED_SNPRINTF_ONLY)
                   1126:   va_end(ap);
                   1127: #endif
1.1.1.2 ! root     1128:   if (str_m > 0) {              /* make sure the string is null-terminated
        !          1129:                                    even at the expense of overwriting the last character
        !          1130:                                    (shouldn't happen, but just in case) */
        !          1131:     str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
1.1       root     1132:   }
                   1133:   /* Return the number of characters formatted (excluding trailing null
                   1134:    * character), that is, the number of characters that would have been
                   1135:    * written to the buffer if it were large enough.
                   1136:    *
                   1137:    * The value of str_l should be returned, but str_l is of unsigned type
                   1138:    * size_t, and snprintf is int, possibly leading to an undetected
                   1139:    * integer overflow, resulting in a negative return value, which is illegal.
                   1140:    * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
                   1141:    * Should errno be set to EOVERFLOW and EOF returned in this case???
                   1142:    */
1.1.1.2 ! root     1143:   return (int)str_l;
1.1       root     1144: }
                   1145: #endif

unix.superglobalmegacorp.com

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