17.13 Modernized printf

The *zprintf family of functions is a modernized form of the *printf family of functions.

The problem

The *printf functions have a return type ‘int’ and therefore can only produce results that are up to (2 GiB - 1 byte) long.

The problem with this is not so much that it is an arbitrary limitation (that persists even in processes that have, say, 50 GiB of RAM available). The bigger problem is that in reliable programs, it requires handling of an error code EOVERFLOW that indicates a result whose size would be 2 GiB or larger.

How does a reliable program do error handling of *printf function calls? For output to strings and file descriptors (such as sprintf and dprintf), there is no other way than to check each such call.

For output to FILE streams (such as fprintf), beginners are tempted to ignore the return value of each call and instead check ferror (stream) at the end. The problem with this approach is that at the moment the error is detected, incorrect output has already been sent onto the stream. So, in this case as well, the reliable approach is to check each such call.

The simple format strings that most programs use in 99% of the places, namely with no wide string or wide character arguments, nor with widths passed as int argument, can only fail with two possible error codes:

Many GNU programs use “checking” wrappers (functions xvasprintf, etc.) that check for ENOMEM and call xalloc_die, thus aborting the program in that case. The problem is that EOVERFLOW is not handled, even with such wrappers.

Should EOVERFLOW be handled like ENOMEM, by aborting the program? No, as mentioned above, that would be an arbitrary limitation, which the GNU Coding Standards urge us to avoid.

The solution

The *zprintf functions are like the *printf functions, except that the return type is

Thus, for these functions, EOVERFLOW cannot occur (except for format strings which take widths as argument, which we have excluded above), and the “checking” wrappers (functions xvasprintf, etc.) are thus sufficient for ensuring an error-free result.

Note: In 64-bit processes, ptrdiff_t is 64 bits wide, i.e. equivalent to int64_t. In 32-bit processes, ptrdiff_t is only 32 bits wide, but since in these environments, memory regions of 2 GiB or larger cannot be allocated anyway (malloc would fail with ENOMEM), this type is sufficient.

The following Gnulib functions and modules exist:

Original functionModernized functionModules
sprintfszprintfszprintf, szprintf-posix, szprintf-gnu
vsprintfvszprintfvszprintf, vszprintf-posix, vszprintf-gnu
snprintfsnzprintfsnzprintf, snzprintf-posix, snzprintf-gnu
vsnprintfvsnzprintfvsnzprintf, vsnzprintf-posix, vsnzprintf-gnu
asprintfaszprintfvaszprintf, vaszprintf-posix, vaszprintf-gnu
vasprintfvaszprintfvaszprintf, vaszprintf-posix, vaszprintf-gnu
c_snprintfc_snzprintfc-snzprintf, c-snzprintf-gnu
c_vsnprintfc_vsnzprintfc-vsnzprintf, c-vsnzprintf-gnu
c_asprintfc_aszprintfc-vaszprintf, c-vaszprintf-gnu
c_vasprintfc_vaszprintfc-vaszprintf, c-vaszprintf-gnu
fprintffzprintffzprintf, fzprintf-posix, fzprintf-gnu
vfprintfvfzprintfvfzprintf, vfzprintf-posix, vfzprintf-gnu
printfzprintfzprintf, zprintf-posix, zprintf-gnu
vprintfvzprintfvzprintf, vzprintf-posix, vzprintf-gnu
dprintfdzprintfdzprintf, dzprintf-posix, dzprintf-gnu
vdprintfvdzprintfvdzprintf, vdzprintf-posix, vdzprintf-gnu
obstack_printfobstack_zprintfobstack-zprintf, obstack-zprintf-posix, obstack-zprintf-gnu
obstack_vprintfobstack_vzprintfobstack-zprintf, obstack-zprintf-posix, obstack-zprintf-gnu

The following functions use the *zprintf functions under the hood and thus don’t need a *zprintf variant:

FunctionModules
xasprintfxvasprintf, xvasprintf-posix, xvasprintf-gnu
xvasprintfxvasprintf, xvasprintf-posix, xvasprintf-gnu
c_xasprintfc-xvasprintf
c_xvasprintfc-xvasprintf
xprintfxprintf, xprintf-posix, xprintf-gnu
xvprintfxprintf, xprintf-posix, xprintf-gnu
xfprintfxprintf, xprintf-posix, xprintf-gnu
xvfprintfxprintf, xprintf-posix, xprintf-gnu

Note: Even with the *zprintf functions, you need to be prepared to handle specific error codes when you use non-simple format strings: