C99-snprintf

Overview

C99‑snprintf provides a portable implementation of snprintf(3), vsnprintf(3), asprintf(3), and vasprintf(3). It should be fully C99 compliant, with the exceptions that it doesn't provide wide character support and that %a and %A conversions aren't supported. C99‑snprintf should be buildable with any ANSI C compiler, it doesn't require libc functionality other than malloc(3) (for vasprintf(3)) and the stdarg(3) or varargs(3) macros, and it has no other prerequisites.

The snprintf(3) and vsnprintf(3) functions are part of the C99 standard library. However, they weren't included in the C89/C90 standards and some systems don't provide C99 compliant implementations of these functions. For example, if the ouput buffer isn't big enough to hold the full conversion result, IRIX up to the current release 6.5.30 and glibc up to 2.0.x don't return the same value as with a sufficiently sized buffer (which makes it impossible to precompute the required buffer size), and some older systems (such as 64-bit Solaris 7) ignore the specified buffer size and overrun the buffer if it's too small. The asprintf(3) and vasprintf(3) functions aren't standardized at all. They're included with recent releases of glibc and BSD's libc, but they aren't available on other systems, such as System V (e.g., Solaris).

So, if any of these functions are used, portable software should include replacement code which is used in case the functions aren't available or don't work correctly on the target system. C99‑snprintf can be included with software packages in order to provide such replacement functions.

Download

Usage

With GNU Autoconf 2.60 or newer

  1. Add snprintf.c to your project files. If you're using GNU Automake, you could use a line such as foo_LDADD = $(LIBOBJS) (where foo is the name of your program) in your Makefile.am.
  2. Add snprintf.m4 to your m4 include directory or to your acinclude.m4 file. If you're using neither, you can simply include the contents of snprintf.m4 in your configure.ac file.
  3. For each replacement function which might be needed in your project, call the according Autoconf macro in your configure.ac file. That is, in order to have all four replacement functions available if needed:
    HW_FUNC_VSNPRINTF
    HW_FUNC_SNPRINTF
    HW_FUNC_VASPRINTF
    HW_FUNC_ASPRINTF
  4. The required replacement functions should be declared in some header file included throughout your project files:
    #if HAVE_CONFIG_H
    #include <config.h>
    #endif
    #if HAVE_STDARG_H
    #include <stdarg.h>
    #if !HAVE_VSNPRINTF
    int rpl_vsnprintf(char *, size_t, const char *, va_list);
    #endif
    #if !HAVE_SNPRINTF
    int rpl_snprintf(char *, size_t, const char *, ...);
    #endif
    #if !HAVE_VASPRINTF
    int rpl_vasprintf(char **, const char *, va_list);
    #endif
    #if !HAVE_ASPRINTF
    int rpl_asprintf(char **, const char *, ...);
    #endif
    #endif	/* HAVE_STDARG_H */

Without GNU Autoconf

If you're not using GNU Autoconf, omit the steps 2 and 3 from the above instructions. Instead:

  1. The following preprocessor macros should be defined to 1 if the feature or file in question is available on the target system (though basic functionality should be available as long as HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly):
    HAVE_VSNPRINTF
    HAVE_SNPRINTF
    HAVE_VASPRINTF
    HAVE_ASPRINTF
    HAVE_STDARG_H
    HAVE_STDDEF_H
    HAVE_STDINT_H
    HAVE_STDLIB_H
    HAVE_INTTYPES_H
    HAVE_LOCALE_H
    HAVE_LOCALECONV
    HAVE_LCONV_DECIMAL_POINT
    HAVE_LCONV_THOUSANDS_SEP
    HAVE_LONG_DOUBLE
    HAVE_LONG_LONG_INT
    HAVE_UNSIGNED_LONG_LONG_INT
    HAVE_INTMAX_T
    HAVE_UINTMAX_T
    HAVE_UINTPTR_T
    HAVE_PTRDIFF_T
    HAVE_VA_COPY
    HAVE___VA_COPY
  2. The calls to the functions which should be replaced must be redefined throughout the project files (including snprintf.c itself):
    #define vsnprintf rpl_vsnprintf
    #define snprintf rpl_snprintf
    #define vasprintf rpl_vasprintf
    #define asprintf rpl_asprintf

History

2008-01-20 Holger Weiß for C99-snprintf 1.1

Fixed the detection of infinite floating point values on IRIX (and possibly other systems) and applied another few minor cleanups.

2008-01-04 Holger Weiß for C99-snprintf 1.0

Added a lot of new features, fixed many bugs, and incorporated various improvements done by Andrew Tridgell, Russ Allbery, Hrvoje Niksic, Damien Miller, and others for the Samba, INN, Wget, and OpenSSH projects. The additions include: support the e, E, g, G, and F conversion specifiers (and use conversion style f or F for the still unsupported a and A specifiers); support the hh, ll, j, t, and z length modifiers; support the # flag and the (non-C99) ' flag; use localeconv(3) (if available) to get both the current locale's decimal point character and the separator between groups of digits; fix the handling of various corner cases of field width and precision specifications; fix various floating point conversion bugs; handle infinite and NaN floating point values; don't attempt to write to the output buffer (which may be NULL) if a size of zero was specified; check for integer overflow of the field width, precision, and return values and during the floating point conversion; use the OUTCHAR() macro instead of a function for better performance; provide asprintf(3) and vasprintf(3) functions; add new test cases. The replacement functions have been renamed to use an "rpl_" prefix, the function calls in the main project must be redefined accordingly for each replacement function which is needed (by using Autoconf or other means). Various other minor improvements have been applied and the coding style was cleaned up for consistency.

2007-07-23 Holger Weiß for Mutt 1.5.13

C99 compliant snprintf(3) and vsnprintf(3) functions return the number of characters that would have been written to a sufficiently sized buffer (excluding the '\0'). The original code simply returned the length of the resulting output string, so that's been fixed.

1998-03-05 Michael Elkins for Mutt 0.90.8

The original code assumed that both snprintf(3) and vsnprintf(3) were missing. Some systems only have snprintf(3) but not vsnprintf(3), so the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.

1998-01-27 Thomas Roessler for Mutt 0.89i

The PGP code was using unsigned hexadecimal formats. Unfortunately, unsigned formats simply didn't work.

1997-10-22 Brandon Long for Mutt 0.87.1

Ok, added some minimal floating point support, which means this probably requires libm on most operating systems. Don't yet support the exponent (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just wasn't being exercised in ways which showed it, so that's been fixed. Also, formatted the code to Mutt conventions, and removed dead code left over from the original. Also, there is now a builtin-test, run with: gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf

2996-09-15 Brandon Long for Mutt 0.43

This was ugly. It is still ugly. I opted out of floating point numbers, but the formatter understands just about everything from the normal C string format, at least as far as I can tell from the Solaris 2.5 printf(3S) man page.

Bugs and Caveats

Depending on the size of the largest integer type available on the target platform, floating point precisions larger than 9, 19, or 38 are not supported. If a larger precision is specified, it will silently be reduced to the largest possible precision on the target system.

If the integral part of a floating point value doesn't fit into the largest integer type available on the target platform, the conversion will fail. In this case, C99‑snprintf will return -1 and set the global variable errno to indicate the error. The same is done if the specified field width or precision are (or if the return value would be) larger than INT_MAX.

C99‑snprintf makes a few assumptions regarding integer (and pointer value) conversions which aren't backed by the C standard, but which should be safe in practice.

Copyright and License

Copyright © 2008 Holger Weiß.
Copyright © 1995 Patrick Powell.

This code is based on code written by Patrick Powell <papowell@astart.com>. It may be used for any purpose as long as this notice remains intact on all source code distributions.

Newsletter

If you want to be informed about new C99-snprintf releases, you can subscribe to this project on freshmeat. You will then receive an e-mail as soon as a new C99-snprintf release is available, including a short summary of the changes.

Feedback

Feel free to drop me an e-mail if you encounter any problems with or have any comments on C99‑snprintf.

Other Implementations

See Mark Martinec's portable snprintf(3) page for an extensive link collection and especially James Antill's very good printf() implementation comparison.


Valid XHTML 1.1!  Valid CSS!  Powered by Debian GNU/Linux

Last modified: $Date: 2013/01/28 21:32:16 $ by Holger Weiß