@node Modernized printf
@section Modernized printf

@c Copyright (C) 2024--2026 Free Software Foundation, Inc.

@c Permission is granted to copy, distribute and/or modify this document
@c under the terms of the GNU Free Documentation License, Version 1.3 or
@c any later version published by the Free Software Foundation; with no
@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
@c copy of the license is at <https://www.gnu.org/licenses/fdl-1.3.en.html>.

@c Written by Bruno Haible.

The @code{*zprintf} family of functions is
a modernized form of the @code{*printf} family of functions.

@subheading The problem

The @code{*printf} functions have a return type @samp{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 @code{EOVERFLOW}
that indicates a result whose size would be 2 GiB or larger.

@c A symptom of this missing EOVERFLOW handling can be
@c warnings from static analyzers, see e.g.
@c https://lists.gnu.org/archive/html/bug-gnulib/2023-06/msg00014.html

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

For output to @code{FILE} streams (such as @code{fprintf}),
beginners are tempted to ignore the return value of each call
and instead check @code{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 @emph{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 @code{int} argument,
can only fail with two possible error codes:
@itemize
@item
@code{ENOMEM}, when
the result would be too large to allocate in the process' memory.
@item
@code{EOVERFLOW}, when
the result is 2 GiB or larger but still allocatable.
@end itemize

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

Should @code{EOVERFLOW} be handled like @code{ENOMEM}, by aborting the program?
No, as mentioned above, that would be an arbitrary limitation, which the
@ifinfo
GNU Coding Standards
urge us to avoid
(@pxref{Semantics,,, standards}).
@end ifinfo
@ifnotinfo
@url{https://www.gnu.org/prep/standards/html_node/Semantics.html,,GNU Coding Standards}
urge us to avoid.
@end ifnotinfo

@subheading The solution

The @code{*zprintf} functions are like the @code{*printf} functions,
except that the return type is
@itemize
@item
@code{ptrdiff_t} instead of @code{int},
for output to strings,
@item
@code{off64_t} (which is always equivalent to @code{int64_t})
instead of @code{int},
for output to file streams and file descriptors.
@end itemize
@noindent
Thus, for these functions, @code{EOVERFLOW} cannot occur
(except for format strings which take widths as argument,
which we have excluded above),
and the ``checking'' wrappers (functions @code{xvasprintf}, etc.)
are thus sufficient for ensuring an error-free result.

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

The following Gnulib functions and modules exist:

@mindex szprintf
@mindex szprintf-posix
@mindex szprintf-gnu
@mindex vszprintf
@mindex vszprintf-posix
@mindex vszprintf-gnu
@mindex snzprintf
@mindex snzprintf-posix
@mindex snzprintf-gnu
@mindex vsnzprintf
@mindex vsnzprintf-posix
@mindex vsnzprintf-gnu
@mindex vaszprintf
@mindex vaszprintf-posix
@mindex vaszprintf-gnu
@mindex c-snzprintf
@mindex c-snzprintf-gnu
@mindex c-vsnzprintf
@mindex c-vsnzprintf-gnu
@mindex c-vaszprintf
@mindex c-vaszprintf-gnu
@mindex fzprintf
@mindex fzprintf-posix
@mindex fzprintf-gnu
@mindex vfzprintf
@mindex vfzprintf-posix
@mindex vfzprintf-gnu
@mindex zprintf
@mindex zprintf-posix
@mindex zprintf-gnu
@mindex vzprintf
@mindex vzprintf-posix
@mindex vzprintf-gnu
@mindex dzprintf
@mindex dzprintf-posix
@mindex dzprintf-gnu
@mindex vdzprintf
@mindex vdzprintf-posix
@mindex vdzprintf-gnu
@mindex obstack-zprintf
@mindex obstack-zprintf-posix
@mindex obstack-zprintf-gnu
@multitable @columnfractions .25 .25 .5
@headitem Original function @tab Modernized function @tab Modules
@item @code{sprintf} @tab @code{szprintf}
 @tab @code{szprintf}, @code{szprintf-posix}, @code{szprintf-gnu}
@item @code{vsprintf} @tab @code{vszprintf}
 @tab @code{vszprintf}, @code{vszprintf-posix}, @code{vszprintf-gnu}
@item @code{snprintf} @tab @code{snzprintf}
 @tab @code{snzprintf}, @code{snzprintf-posix}, @code{snzprintf-gnu}
@item @code{vsnprintf} @tab @code{vsnzprintf}
 @tab @code{vsnzprintf}, @code{vsnzprintf-posix}, @code{vsnzprintf-gnu}
@item @code{asprintf} @tab @code{aszprintf}
 @tab @code{vaszprintf}, @code{vaszprintf-posix}, @code{vaszprintf-gnu}
@item @code{vasprintf} @tab @code{vaszprintf}
 @tab @code{vaszprintf}, @code{vaszprintf-posix}, @code{vaszprintf-gnu}
@item @code{c_snprintf} @tab @code{c_snzprintf}
 @tab @code{c-snzprintf}, @code{c-snzprintf-gnu}
@item @code{c_vsnprintf} @tab @code{c_vsnzprintf}
 @tab @code{c-vsnzprintf}, @code{c-vsnzprintf-gnu}
@item @code{c_asprintf} @tab @code{c_aszprintf}
 @tab @code{c-vaszprintf}, @code{c-vaszprintf-gnu}
@item @code{c_vasprintf} @tab @code{c_vaszprintf}
 @tab @code{c-vaszprintf}, @code{c-vaszprintf-gnu}
@item @code{fprintf} @tab @code{fzprintf}
 @tab @code{fzprintf}, @code{fzprintf-posix}, @code{fzprintf-gnu}
@item @code{vfprintf} @tab @code{vfzprintf}
 @tab @code{vfzprintf}, @code{vfzprintf-posix}, @code{vfzprintf-gnu}
@item @code{printf} @tab @code{zprintf}
 @tab @code{zprintf}, @code{zprintf-posix}, @code{zprintf-gnu}
@item @code{vprintf} @tab @code{vzprintf}
 @tab @code{vzprintf}, @code{vzprintf-posix}, @code{vzprintf-gnu}
@item @code{dprintf} @tab @code{dzprintf}
 @tab @code{dzprintf}, @code{dzprintf-posix}, @code{dzprintf-gnu}
@item @code{vdprintf} @tab @code{vdzprintf}
 @tab @code{vdzprintf}, @code{vdzprintf-posix}, @code{vdzprintf-gnu}
@item @code{obstack_printf} @tab @code{obstack_zprintf}
 @tab @code{obstack-zprintf},
      @code{obstack-zprintf-posix}, @code{obstack-zprintf-gnu}
@item @code{obstack_vprintf} @tab @code{obstack_vzprintf}
 @tab @code{obstack-zprintf},
      @code{obstack-zprintf-posix}, @code{obstack-zprintf-gnu}
@end multitable

The following functions use the @code{*zprintf} functions under the hood
and thus don't need a @code{*zprintf} variant:

@mindex xvasprintf
@mindex xvasprintf-posix
@mindex xvasprintf-gnu
@mindex c-xvasprintf
@mindex xprintf
@mindex xprintf-posix
@mindex xprintf-gnu
@multitable @columnfractions .5 .5
@headitem Function @tab Modules
@item @code{xasprintf}
 @tab @code{xvasprintf}, @code{xvasprintf-posix}, @code{xvasprintf-gnu}
@item @code{xvasprintf}
 @tab @code{xvasprintf}, @code{xvasprintf-posix}, @code{xvasprintf-gnu}
@item @code{c_xasprintf}
 @tab @code{c-xvasprintf}
@item @code{c_xvasprintf}
 @tab @code{c-xvasprintf}
@item @code{xprintf}
 @tab @code{xprintf}, @code{xprintf-posix}, @code{xprintf-gnu}
@item @code{xvprintf}
 @tab @code{xprintf}, @code{xprintf-posix}, @code{xprintf-gnu}
@item @code{xfprintf}
 @tab @code{xprintf}, @code{xprintf-posix}, @code{xprintf-gnu}
@item @code{xvfprintf}
 @tab @code{xprintf}, @code{xprintf-posix}, @code{xprintf-gnu}
@end multitable

Note: Even with the @code{*zprintf} functions,
you need to be prepared to handle specific error codes
when you use non-simple format strings:
@itemize
@item
@code{EILSEQ} when
the format string takes wide strings or wide characters as arguments,
@item
@code{EOVERFLOW} when
the format string takes a width as argument
and you cannot ensure that its value is in the range @code{0}...@code{INT_MAX}.
@end itemize
