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