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