mirror of
https://git.savannah.gnu.org/git/gnulib.git
synced 2026-10-24 00:48:56 +03:00
232 lines
8.2 KiB
Plaintext
232 lines
8.2 KiB
Plaintext
@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
|