Index | Thread | Search

From:
Showta Ishizaki <zakinko@snowrabbit.org>
Subject:
printf(3): "%#.0o" of 0 prints nothing instead of "0"
To:
tech@openbsd.org
Cc:
zakinko@snowrabbit.org
Date:
Fri, 11 Sep 2026 03:29:15 +0900

Download raw body.

Thread
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