Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.  * snprintf.c - a portable implementation of snprintf
  3.  *
  4.  * AUTHOR
  5.  *   Mark Martinec <mark.martinec@ijs.si>, 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 <mark.martinec@ijs.si>
  120.  *              - added main test program, fixed remaining inconsistencies,
  121.  *                added optional (long long int) support;
  122.  * 1999-04-12   V1.1  Mark Martinec <mark.martinec@ijs.si>
  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 <mark.martinec@ijs.si>
  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 <mark.martinec@ijs.si>
  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 <edwiny@autonomy.com> 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 <mark.martinec@ijs.si>
  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 <mark.martinec@ijs.si>
  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 <mark.martinec@ijs.si>
  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. /* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
  284. /* ============================================= */
  285.  
  286. #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
  287. #define PORTABLE_SNPRINTF_VERSION_MINOR 2
  288.  
  289. #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
  290. # if defined(NEED_SNPRINTF_ONLY)
  291. # undef NEED_SNPRINTF_ONLY
  292. # endif
  293. # if !defined(PREFER_PORTABLE_SNPRINTF)
  294. # define PREFER_PORTABLE_SNPRINTF
  295. # endif
  296. #endif
  297.  
  298. #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
  299. #define SOLARIS_COMPATIBLE
  300. #endif
  301.  
  302. #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
  303. #define HPUX_COMPATIBLE
  304. #endif
  305.  
  306. #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
  307. #define DIGITAL_UNIX_COMPATIBLE
  308. #endif
  309.  
  310. #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
  311. #define PERL_COMPATIBLE
  312. #endif
  313.  
  314. #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
  315. #define LINUX_COMPATIBLE
  316. #endif
  317.  
  318. #include <sys/types.h>
  319. #include <string.h>
  320. #include <stdlib.h>
  321. #include <stdio.h>
  322. #include <stdarg.h>
  323. #include <assert.h>
  324. #include <errno.h>
  325.  
  326. #ifdef isdigit
  327. #undef isdigit
  328. #endif
  329. #define isdigit(c) ((c) >= '0' && (c) <= '9')
  330.  
  331. /* For copying strings longer or equal to 'breakeven_point'
  332.  * it is more efficient to call memcpy() than to do it inline.
  333.  * The value depends mostly on the processor architecture,
  334.  * but also on the compiler and its optimization capabilities.
  335.  * The value is not critical, some small value greater than zero
  336.  * will be just fine if you don't care to squeeze every drop
  337.  * of performance out of the code.
  338.  *
  339.  * Small values favor memcpy, large values favor inline code.
  340.  */
  341. #if defined(__alpha__) || defined(__alpha)
  342. #  define breakeven_point   2   /* AXP (DEC Alpha)     - gcc or cc or egcs */
  343. #endif
  344. #if defined(__i386__)  || defined(__i386)
  345. #  define breakeven_point  12   /* Intel Pentium/Linux - gcc 2.96 */
  346. #endif
  347. #if defined(__hppa)
  348. #  define breakeven_point  10   /* HP-PA               - gcc */
  349. #endif
  350. #if defined(__sparc__) || defined(__sparc)
  351. #  define breakeven_point  33   /* Sun Sparc 5         - gcc 2.8.1 */
  352. #endif
  353.  
  354. /* some other values of possible interest: */
  355. /* #define breakeven_point  8 */  /* VAX 4000          - vaxc */
  356. /* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */
  357.  
  358. #ifndef breakeven_point
  359. #  define breakeven_point   6   /* some reasonable one-size-fits-all value */
  360. #endif
  361.  
  362. #define fast_memcpy(d,s,n) \
  363.   { register size_t nn = (size_t)(n); \
  364.     if (nn >= breakeven_point) memcpy((d), (s), nn); \
  365.     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
  366.       register char *dd; register const char *ss; \
  367.       for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
  368.  
  369. #define fast_memset(d,c,n) \
  370.   { register size_t nn = (size_t)(n); \
  371.     if (nn >= breakeven_point) memset((d), (int)(c), nn); \
  372.     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
  373.       register char *dd; register const int cc=(int)(c); \
  374.       for (dd=(d); nn>0; nn--) *dd++ = cc; } }
  375.  
  376. /* prototypes */
  377.  
  378. #if defined(NEED_ASPRINTF)
  379. int asprintf   (char **ptr, const char *fmt, /*args*/ ...);
  380. #endif
  381. #if defined(NEED_VASPRINTF)
  382. int vasprintf  (char **ptr, const char *fmt, va_list ap);
  383. #endif
  384. #if defined(NEED_ASNPRINTF)
  385. int asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
  386. #endif
  387. #if defined(NEED_VASNPRINTF)
  388. int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
  389. #endif
  390.  
  391. #if defined(HAVE_SNPRINTF)
  392. /* declare our portable snprintf  routine under name portable_snprintf  */
  393. /* declare our portable vsnprintf routine under name portable_vsnprintf */
  394. #else
  395. /* declare our portable routines under names snprintf and vsnprintf */
  396. #define portable_snprintf snprintf
  397. #if !defined(NEED_SNPRINTF_ONLY)
  398. #define portable_vsnprintf vsnprintf
  399. #endif
  400. #endif
  401.  
  402. #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
  403. int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
  404. #if !defined(NEED_SNPRINTF_ONLY)
  405. int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
  406. #endif
  407. #endif
  408.  
  409. /* declarations */
  410.  
  411. static char credits[] = "\n\
  412. @(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
  413. @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
  414. @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
  415.  
  416. #if defined(NEED_ASPRINTF)
  417. int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
  418.   va_list ap;
  419.   size_t str_m;
  420.   int str_l;
  421.  
  422.   *ptr = NULL;
  423.   va_start(ap, fmt);                            /* measure the required size */
  424.   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
  425.   va_end(ap);
  426.   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
  427.   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
  428.   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
  429.   else {
  430.     int str_l2;
  431.     va_start(ap, fmt);
  432.     str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
  433.     va_end(ap);
  434.     assert(str_l2 == str_l);
  435.   }
  436.   return str_l;
  437. }
  438. #endif
  439.  
  440. #if defined(NEED_VASPRINTF)
  441. int vasprintf(char **ptr, const char *fmt, va_list ap) {
  442.   size_t str_m;
  443.   int str_l;
  444.  
  445.   *ptr = NULL;
  446.   { va_list ap2;
  447.     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
  448.     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
  449.     va_end(ap2);
  450.   }
  451.   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
  452.   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
  453.   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
  454.   else {
  455.     int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
  456.     assert(str_l2 == str_l);
  457.   }
  458.   return str_l;
  459. }
  460. #endif
  461.  
  462. #if defined(NEED_ASNPRINTF)
  463. int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
  464.   va_list ap;
  465.   int str_l;
  466.  
  467.   *ptr = NULL;
  468.   va_start(ap, fmt);                            /* measure the required size */
  469.   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
  470.   va_end(ap);
  471.   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
  472.   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
  473.   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
  474.   if (str_m == 0) {  /* not interested in resulting string, just return size */
  475.   } else {
  476.     *ptr = (char *) malloc(str_m);
  477.     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
  478.     else {
  479.       int str_l2;
  480.       va_start(ap, fmt);
  481.       str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
  482.       va_end(ap);
  483.       assert(str_l2 == str_l);
  484.     }
  485.   }
  486.   return str_l;
  487. }
  488. #endif
  489.  
  490. #if defined(NEED_VASNPRINTF)
  491. int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
  492.   int str_l;
  493.  
  494.   *ptr = NULL;
  495.   { va_list ap2;
  496.     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
  497.     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
  498.     va_end(ap2);
  499.   }
  500.   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
  501.   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
  502.   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
  503.   if (str_m == 0) {  /* not interested in resulting string, just return size */
  504.   } else {
  505.     *ptr = (char *) malloc(str_m);
  506.     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
  507.     else {
  508.       int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
  509.       assert(str_l2 == str_l);
  510.     }
  511.   }
  512.   return str_l;
  513. }
  514. #endif
  515.  
  516. /*
  517.  * If the system does have snprintf and the portable routine is not
  518.  * specifically required, this module produces no code for snprintf/vsnprintf.
  519.  */
  520. #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
  521.  
  522. #if !defined(NEED_SNPRINTF_ONLY)
  523. int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
  524.   va_list ap;
  525.   int str_l;
  526.  
  527.   va_start(ap, fmt);
  528.   str_l = portable_vsnprintf(str, str_m, fmt, ap);
  529.   va_end(ap);
  530.   return str_l;
  531. }
  532. #endif
  533.  
  534. #if defined(NEED_SNPRINTF_ONLY)
  535. int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
  536. #else
  537. int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
  538. #endif
  539.  
  540. #if defined(NEED_SNPRINTF_ONLY)
  541.   va_list ap;
  542. #endif
  543.   size_t str_l = 0;
  544.   const char *p = fmt;
  545.  
  546. /* In contrast with POSIX, the ISO C99 now says
  547.  * that str can be NULL and str_m can be 0.
  548.  * This is more useful than the old:  if (str_m < 1) return -1; */
  549.  
  550. #if defined(NEED_SNPRINTF_ONLY)
  551.   va_start(ap, fmt);
  552. #endif
  553.   if (!p) p = "";
  554.   while (*p) {
  555.     if (*p != '%') {
  556.    /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
  557.    /* but the following code achieves better performance for cases
  558.     * where format string is long and contains few conversions */
  559.       const char *q = strchr(p+1,'%');
  560.       size_t n = !q ? strlen(p) : (q-p);
  561.       if (str_l < str_m) {
  562.         size_t avail = str_m-str_l;
  563.         fast_memcpy(str+str_l, p, (n>avail?avail:n));
  564.       }
  565.       p += n; str_l += n;
  566.     } else {
  567.       const char *starting_p;
  568.       size_t min_field_width = 0, precision = 0;
  569.       int zero_padding = 0, precision_specified = 0, justify_left = 0;
  570.       int alternate_form = 0, force_sign = 0;
  571.       int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
  572.                                      the ' ' flag should be ignored. */
  573.       char length_modifier = '\0';            /* allowed values: \0, h, l, L */
  574.       char tmp[32];/* temporary buffer for simple numeric->string conversion */
  575.  
  576.       const char *str_arg;      /* string address in case of string argument */
  577.       size_t str_arg_l;         /* natural field width of arg without padding
  578.                                    and sign */
  579.       unsigned char uchar_arg;
  580.         /* unsigned char argument value - only defined for c conversion.
  581.            N.B. standard explicitly states the char argument for
  582.            the c conversion is unsigned */
  583.  
  584.       size_t number_of_zeros_to_pad = 0;
  585.         /* number of zeros to be inserted for numeric conversions
  586.            as required by the precision or minimal field width */
  587.  
  588.       size_t zero_padding_insertion_ind = 0;
  589.         /* index into tmp where zero padding is to be inserted */
  590.  
  591.       char fmt_spec = '\0';
  592.         /* current conversion specifier character */
  593.  
  594.       str_arg = credits;/* just to make compiler happy (defined but not used)*/
  595.       str_arg = NULL;
  596.       starting_p = p; p++;  /* skip '%' */
  597.    /* parse flags */
  598.       while (*p == '0' || *p == '-' || *p == '+' ||
  599.              *p == ' ' || *p == '#' || *p == '\'') {
  600.         switch (*p) {
  601.         case '0': zero_padding = 1; break;
  602.         case '-': justify_left = 1; break;
  603.         case '+': force_sign = 1; space_for_positive = 0; break;
  604.         case ' ': force_sign = 1;
  605.      /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
  606. #ifdef PERL_COMPATIBLE
  607.      /* ... but in Perl the last of ' ' and '+' applies */
  608.                   space_for_positive = 1;
  609. #endif
  610.                   break;
  611.         case '#': alternate_form = 1; break;
  612.         case '\'': break;
  613.         }
  614.         p++;
  615.       }
  616.    /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
  617.  
  618.    /* parse field width */
  619.       if (*p == '*') {
  620.         int j;
  621.         p++; j = va_arg(ap, int);
  622.         if (j >= 0) min_field_width = j;
  623.         else { min_field_width = -j; justify_left = 1; }
  624.       } else if (isdigit((int)(*p))) {
  625.         /* size_t could be wider than unsigned int;
  626.            make sure we treat argument like common implementations do */
  627.         unsigned int uj = *p++ - '0';
  628.         while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
  629.         min_field_width = uj;
  630.       }
  631.    /* parse precision */
  632.       if (*p == '.') {
  633.         p++; precision_specified = 1;
  634.         if (*p == '*') {
  635.           int j = va_arg(ap, int);
  636.           p++;
  637.           if (j >= 0) precision = j;
  638.           else {
  639.             precision_specified = 0; precision = 0;
  640.          /* NOTE:
  641.           *   Solaris 2.6 man page claims that in this case the precision
  642.           *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
  643.           *   claim that this case should be treated as unspecified precision,
  644.           *   which is what we do here.
  645.           */
  646.           }
  647.         } else if (isdigit((int)(*p))) {
  648.           /* size_t could be wider than unsigned int;
  649.              make sure we treat argument like common implementations do */
  650.           unsigned int uj = *p++ - '0';
  651.           while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
  652.           precision = uj;
  653.         }
  654.       }
  655.    /* parse 'h', 'l' and 'll' length modifiers */
  656.       if (*p == 'h' || *p == 'l') {
  657.         length_modifier = *p; p++;
  658.         if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */
  659. #ifdef SNPRINTF_LONGLONG_SUPPORT
  660.           length_modifier = '2';                  /* double l encoded as '2' */
  661. #else
  662.           length_modifier = 'l';                 /* treat it as a single 'l' */
  663. #endif
  664.           p++;
  665.         }
  666.       }
  667.       fmt_spec = *p;
  668.    /* common synonyms: */
  669.       switch (fmt_spec) {
  670.       case 'i': fmt_spec = 'd'; break;
  671.       case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
  672.       case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
  673.       case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
  674.       default: break;
  675.       }
  676.    /* get parameter value, do initial processing */
  677.       switch (fmt_spec) {
  678.       case '%': /* % behaves similar to 's' regarding flags and field widths */
  679.       case 'c': /* c behaves similar to 's' regarding flags and field widths */
  680.       case 's':
  681.         length_modifier = '\0';          /* wint_t and wchar_t not supported */
  682.      /* the result of zero padding flag with non-numeric conversion specifier*/
  683.      /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
  684.      /* Digital Unix and Linux does not. */
  685. #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
  686.         zero_padding = 0;    /* turn zero padding off for string conversions */
  687. #endif
  688.         str_arg_l = 1;
  689.         switch (fmt_spec) {
  690.         case '%':
  691.           str_arg = p; break;
  692.         case 'c': {
  693.           int j = va_arg(ap, int);
  694.           uchar_arg = (unsigned char) j;   /* standard demands unsigned char */
  695.           str_arg = (const char *) &uchar_arg;
  696.           break;
  697.         }
  698.         case 's':
  699.           str_arg = va_arg(ap, const char *);
  700.           if (!str_arg) str_arg_l = 0;
  701.        /* make sure not to address string beyond the specified precision !!! */
  702.           else if (!precision_specified) str_arg_l = strlen(str_arg);
  703.        /* truncate string if necessary as requested by precision */
  704.           else if (precision == 0) str_arg_l = 0;
  705.           else {
  706.        /* memchr on HP does not like n > 2^31  !!! */
  707.             const char *q = memchr(str_arg, '\0',
  708.                              precision <= 0x7fffffff ? precision : 0x7fffffff);
  709.             str_arg_l = !q ? precision : (q-str_arg);
  710.           }
  711.           break;
  712.         default: break;
  713.         }
  714.         break;
  715.       case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
  716.         /* NOTE: the u, o, x, X and p conversion specifiers imply
  717.                  the value is unsigned;  d implies a signed value */
  718.  
  719.         int arg_sign = 0;
  720.           /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
  721.             +1 if greater than zero (or nonzero for unsigned arguments),
  722.             -1 if negative (unsigned argument is never negative) */
  723.  
  724.         int int_arg = 0;  unsigned int uint_arg = 0;
  725.           /* only defined for length modifier h, or for no length modifiers */
  726.  
  727.         long int long_arg = 0;  unsigned long int ulong_arg = 0;
  728.           /* only defined for length modifier l */
  729.  
  730.         void *ptr_arg = NULL;
  731.           /* pointer argument value -only defined for p conversion */
  732.  
  733. #ifdef SNPRINTF_LONGLONG_SUPPORT
  734.         long long int long_long_arg = 0;
  735.         unsigned long long int ulong_long_arg = 0;
  736.           /* only defined for length modifier ll */
  737. #endif
  738.         if (fmt_spec == 'p') {
  739.         /* HPUX 10: An l, h, ll or L before any other conversion character
  740.          *   (other than d, i, u, o, x, or X) is ignored.
  741.          * Digital Unix:
  742.          *   not specified, but seems to behave as HPUX does.
  743.          * Solaris: If an h, l, or L appears before any other conversion
  744.          *   specifier (other than d, i, u, o, x, or X), the behavior
  745.          *   is undefined. (Actually %hp converts only 16-bits of address
  746.          *   and %llp treats address as 64-bit data which is incompatible
  747.          *   with (void *) argument on a 32-bit system).
  748.          */
  749. #ifdef SOLARIS_COMPATIBLE
  750. #  ifdef SOLARIS_BUG_COMPATIBLE
  751.           /* keep length modifiers even if it represents 'll' */
  752. #  else
  753.           if (length_modifier == '2') length_modifier = '\0';
  754. #  endif
  755. #else
  756.           length_modifier = '\0';
  757. #endif
  758.           ptr_arg = va_arg(ap, void *);
  759.           if (ptr_arg != NULL) arg_sign = 1;
  760.         } else if (fmt_spec == 'd') {  /* signed */
  761.           switch (length_modifier) {
  762.           case '\0':
  763.           case 'h':
  764.          /* It is non-portable to specify a second argument of char or short
  765.           * to va_arg, because arguments seen by the called function
  766.           * are not char or short.  C converts char and short arguments
  767.           * to int before passing them to a function.
  768.           */
  769.             int_arg = va_arg(ap, int);
  770.             if      (int_arg > 0) arg_sign =  1;
  771.             else if (int_arg < 0) arg_sign = -1;
  772.             break;
  773.           case 'l':
  774.             long_arg = va_arg(ap, long int);
  775.             if      (long_arg > 0) arg_sign =  1;
  776.             else if (long_arg < 0) arg_sign = -1;
  777.             break;
  778. #ifdef SNPRINTF_LONGLONG_SUPPORT
  779.           case '2':
  780.             long_long_arg = va_arg(ap, long long int);
  781.             if      (long_long_arg > 0) arg_sign =  1;
  782.             else if (long_long_arg < 0) arg_sign = -1;
  783.             break;
  784. #endif
  785.           }
  786.         } else {  /* unsigned */
  787.           switch (length_modifier) {
  788.           case '\0':
  789.           case 'h':
  790.             uint_arg = va_arg(ap, unsigned int);
  791.             if (uint_arg) arg_sign = 1;
  792.             break;
  793.           case 'l':
  794.             ulong_arg = va_arg(ap, unsigned long int);
  795.             if (ulong_arg) arg_sign = 1;
  796.             break;
  797. #ifdef SNPRINTF_LONGLONG_SUPPORT
  798.           case '2':
  799.             ulong_long_arg = va_arg(ap, unsigned long long int);
  800.             if (ulong_long_arg) arg_sign = 1;
  801.             break;
  802. #endif
  803.           }
  804.         }
  805.         str_arg = tmp; str_arg_l = 0;
  806.      /* NOTE:
  807.       *   For d, i, u, o, x, and X conversions, if precision is specified,
  808.       *   the '0' flag should be ignored. This is so with Solaris 2.6,
  809.       *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
  810.       */
  811. #ifndef PERL_COMPATIBLE
  812.         if (precision_specified) zero_padding = 0;
  813. #endif
  814.         if (fmt_spec == 'd') {
  815.           if (force_sign && arg_sign >= 0)
  816.             tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
  817.          /* leave negative numbers for sprintf to handle,
  818.             to avoid handling tricky cases like (short int)(-32768) */
  819. #ifdef LINUX_COMPATIBLE
  820.         } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
  821.           tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
  822. #endif
  823.         } else if (alternate_form) {
  824.           if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
  825.             { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
  826.          /* alternate form should have no effect for p conversion, but ... */
  827. #ifdef HPUX_COMPATIBLE
  828.           else if (fmt_spec == 'p'
  829.          /* HPUX 10: for an alternate form of p conversion,
  830.           *          a nonzero result is prefixed by 0x. */
  831. #ifndef HPUX_BUG_COMPATIBLE
  832.          /* Actually it uses 0x prefix even for a zero value. */
  833.                    && arg_sign != 0
  834. #endif
  835.                   ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
  836. #endif
  837.         }
  838.         zero_padding_insertion_ind = str_arg_l;
  839.         if (!precision_specified) precision = 1;   /* default precision is 1 */
  840.         if (precision == 0 && arg_sign == 0
  841. #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
  842.             && fmt_spec != 'p'
  843.          /* HPUX 10 man page claims: With conversion character p the result of
  844.           * converting a zero value with a precision of zero is a null string.
  845.           * Actually HP returns all zeroes, and Linux returns "(nil)". */
  846. #endif
  847.         ) {
  848.          /* converted to null string */
  849.          /* When zero value is formatted with an explicit precision 0,
  850.             the resulting formatted string is empty (d, i, u, o, x, X, p).   */
  851.         } else {
  852.           char f[5]; int f_l = 0;
  853.           f[f_l++] = '%';    /* construct a simple format string for sprintf */
  854.           if (!length_modifier) { }
  855.           else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
  856.           else f[f_l++] = length_modifier;
  857.           f[f_l++] = fmt_spec; f[f_l++] = '\0';
  858.           if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
  859.           else if (fmt_spec == 'd') {  /* signed */
  860.             switch (length_modifier) {
  861.             case '\0':
  862.             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;
  863.             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
  864. #ifdef SNPRINTF_LONGLONG_SUPPORT
  865.             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
  866. #endif
  867.             }
  868.           } else {  /* unsigned */
  869.             switch (length_modifier) {
  870.             case '\0':
  871.             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;
  872.             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
  873. #ifdef SNPRINTF_LONGLONG_SUPPORT
  874.             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
  875. #endif
  876.             }
  877.           }
  878.          /* include the optional minus sign and possible "0x"
  879.             in the region before the zero padding insertion point */
  880.           if (zero_padding_insertion_ind < str_arg_l &&
  881.               tmp[zero_padding_insertion_ind] == '-') {
  882.             zero_padding_insertion_ind++;
  883.           }
  884.           if (zero_padding_insertion_ind+1 < str_arg_l &&
  885.               tmp[zero_padding_insertion_ind]   == '0' &&
  886.              (tmp[zero_padding_insertion_ind+1] == 'x' ||
  887.               tmp[zero_padding_insertion_ind+1] == 'X') ) {
  888.             zero_padding_insertion_ind += 2;
  889.           }
  890.         }
  891.         { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
  892.           if (alternate_form && fmt_spec == 'o'
  893. #ifdef HPUX_COMPATIBLE                                  /* ("%#.o",0) -> ""  */
  894.               && (str_arg_l > 0)
  895. #endif
  896. #ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* ("%#o",0) -> "00" */
  897. #else
  898.               /* unless zero is already the first character */
  899.               && !(zero_padding_insertion_ind < str_arg_l
  900.                    && tmp[zero_padding_insertion_ind] == '0')
  901. #endif
  902.           ) {        /* assure leading zero for alternate-form octal numbers */
  903.             if (!precision_specified || precision < num_of_digits+1) {
  904.              /* precision is increased to force the first character to be zero,
  905.                 except if a zero value is formatted with an explicit precision
  906.                 of zero */
  907.               precision = num_of_digits+1; precision_specified = 1;
  908.             }
  909.           }
  910.        /* zero padding to specified precision? */
  911.           if (num_of_digits < precision)
  912.             number_of_zeros_to_pad = precision - num_of_digits;
  913.         }
  914.      /* zero padding to specified minimal field width? */
  915.         if (!justify_left && zero_padding) {
  916.           int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
  917.           if (n > 0) number_of_zeros_to_pad += n;
  918.         }
  919.         break;
  920.       }
  921.       default: /* unrecognized conversion specifier, keep format string as-is*/
  922.         zero_padding = 0;  /* turn zero padding off for non-numeric convers. */
  923. #ifndef DIGITAL_UNIX_COMPATIBLE
  924.         justify_left = 1; min_field_width = 0;                /* reset flags */
  925. #endif
  926. #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
  927.      /* keep the entire format string unchanged */
  928.         str_arg = starting_p; str_arg_l = p - starting_p;
  929.      /* well, not exactly so for Linux, which does something inbetween,
  930.       * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
  931. #else
  932.      /* discard the unrecognized conversion, just keep *
  933.       * the unrecognized conversion character          */
  934.         str_arg = p; str_arg_l = 0;
  935. #endif
  936.         if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged
  937.                                  if not at end-of-string */
  938.         break;
  939.       }
  940.       if (*p) p++;      /* step over the just processed conversion specifier */
  941.    /* insert padding to the left as requested by min_field_width;
  942.       this does not include the zero padding in case of numerical conversions*/
  943.       if (!justify_left) {                /* left padding with blank or zero */
  944.         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
  945.         if (n > 0) {
  946.           if (str_l < str_m) {
  947.             size_t avail = str_m-str_l;
  948.             fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
  949.           }
  950.           str_l += n;
  951.         }
  952.       }
  953.    /* zero padding as requested by the precision or by the minimal field width
  954.     * for numeric conversions required? */
  955.       if (number_of_zeros_to_pad <= 0) {
  956.      /* will not copy first part of numeric right now, *
  957.       * force it to be copied later in its entirety    */
  958.         zero_padding_insertion_ind = 0;
  959.       } else {
  960.      /* insert first part of numerics (sign or '0x') before zero padding */
  961.         int n = zero_padding_insertion_ind;
  962.         if (n > 0) {
  963.           if (str_l < str_m) {
  964.             size_t avail = str_m-str_l;
  965.             fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
  966.           }
  967.           str_l += n;
  968.         }
  969.      /* insert zero padding as requested by the precision or min field width */
  970.         n = number_of_zeros_to_pad;
  971.         if (n > 0) {
  972.           if (str_l < str_m) {
  973.             size_t avail = str_m-str_l;
  974.             fast_memset(str+str_l, '0', (n>avail?avail:n));
  975.           }
  976.           str_l += n;
  977.         }
  978.       }
  979.    /* insert formatted string
  980.     * (or as-is conversion specifier for unknown conversions) */
  981.       { int n = str_arg_l - zero_padding_insertion_ind;
  982.         if (n > 0) {
  983.           if (str_l < str_m) {
  984.             size_t avail = str_m-str_l;
  985.             fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
  986.                         (n>avail?avail:n));
  987.           }
  988.           str_l += n;
  989.         }
  990.       }
  991.    /* insert right padding */
  992.       if (justify_left) {          /* right blank padding to the field width */
  993.         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
  994.         if (n > 0) {
  995.           if (str_l < str_m) {
  996.             size_t avail = str_m-str_l;
  997.             fast_memset(str+str_l, ' ', (n>avail?avail:n));
  998.           }
  999.           str_l += n;
  1000.         }
  1001.       }
  1002.     }
  1003.   }
  1004. #if defined(NEED_SNPRINTF_ONLY)
  1005.   va_end(ap);
  1006. #endif
  1007.   if (str_m > 0) { /* make sure the string is null-terminated
  1008.                       even at the expense of overwriting the last character
  1009.                       (shouldn't happen, but just in case) */
  1010.     str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
  1011.   }
  1012.   /* Return the number of characters formatted (excluding trailing null
  1013.    * character), that is, the number of characters that would have been
  1014.    * written to the buffer if it were large enough.
  1015.    *
  1016.    * The value of str_l should be returned, but str_l is of unsigned type
  1017.    * size_t, and snprintf is int, possibly leading to an undetected
  1018.    * integer overflow, resulting in a negative return value, which is illegal.
  1019.    * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
  1020.    * Should errno be set to EOVERFLOW and EOF returned in this case???
  1021.    */
  1022.   return (int) str_l;
  1023. }
  1024. #endif
  1025.