Download raw body.
printf(3): "%#.0o" of 0 prints nothing instead of "0"
For the o conversion the # flag asks for a leading zero, and C11
7.21.6.1p6 says it "increases the precision, if and only if necessary,
to force the first digit of the result to be a zero (if the value and
precision are both 0, a single 0 is printed)". vfprintf.c gets to the
digit loop with
if (_umax != 0 || prec != 0) {
so a zero with an explicit precision of zero produces no digits at all,
and the "handle octal leading 0" step below never runs:
printf("[%#.0o]\n", 0); [] should be [0]
printf("[%#o]\n", 0); [0]
printf("[%.0o]\n", 0); [] right, no # flag
printf("[%#.0o]\n", 8); [010]
glibc, FreeBSD and NetBSD print [0] for the first one; FreeBSD has
"|| (flags & ALT && base == 8)" at the same place. abseil's str_format
test compares against the C library and caught it on OpenBSD.
The diff adds the same condition to vfprintf.c and vfwprintf.c. I
rebuilt lib/libc from the 7.9 source with it and ran the lines above
against the new libc.so.103.0 through LD_LIBRARY_PATH: the first prints
[0], the other three are unchanged, and wprintf(3) behaves the same.
Not built on -current, though the diff applies to it unchanged.
Index: lib/libc/stdio/vfprintf.c
--- lib/libc/stdio/vfprintf.c.orig
+++ lib/libc/stdio/vfprintf.c
@@ -923,9 +923,12 @@
* ``The result of converting a zero value with an
* explicit precision of zero is no characters.''
* -- ANSI X3J11
+ *
+ * The # flag overrides that for octal: it asks for a
+ * leading zero, so "%#.0o" of 0 is "0", not "".
*/
cp = buf + BUF;
- if (_umax != 0 || prec != 0) {
+ if (_umax != 0 || prec != 0 || (flags & ALT && base == OCT)) {
/*
* Unsigned mod is hard, and unsigned mod
* by a constant is easier than that by
Index: lib/libc/stdio/vfwprintf.c
--- lib/libc/stdio/vfwprintf.c.orig
+++ lib/libc/stdio/vfwprintf.c
@@ -901,9 +901,12 @@
* ``The result of converting a zero value with an
* explicit precision of zero is no characters.''
* -- ANSI X3J11
+ *
+ * The # flag overrides that for octal: it asks for a
+ * leading zero, so "%#.0o" of 0 is "0", not "".
*/
cp = buf + BUF;
- if (_umax != 0 || prec != 0) {
+ if (_umax != 0 || prec != 0 || (flags & ALT && base == OCT)) {
/*
* Unsigned mod is hard, and unsigned mod
* by a constant is easier than that by
printf(3): "%#.0o" of 0 prints nothing instead of "0"