printf.9: Support more than 32 bits in %b

This will be usable after clang has been extended to accept length
modifiers for %b when compiling kernel code.
But we need FreeBSD to support it first...

Reviewed by:		markj, Timo Völker
MFC after:		1 week
Differential Revision:	https://reviews.freebsd.org/D54286
This commit is contained in:
Michael Tuexen
2025-12-19 17:26:37 +01:00
parent a79e2278c5
commit d2cb9cab84
2 changed files with 49 additions and 16 deletions
+18 -7
View File
@@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd December 18, 2025 .Dd December 19, 2025
.Dt PRINTF 9 .Dt PRINTF 9
.Os .Os
.Sh NAME .Sh NAME
@@ -95,14 +95,15 @@ arguments.
The base value is the output base (radix) expressed as an octal value; The base value is the output base (radix) expressed as an octal value;
for example, \e10 gives octal and \e20 gives hexadecimal. for example, \e10 gives octal and \e20 gives hexadecimal.
The arguments are made up of a sequence of bit identifiers. The arguments are made up of a sequence of bit identifiers.
Each bit identifier begins with an Each bit identifier begins with a character specifying the number of the bit
.Em octal (starting from 1) this identifier describes.
value which is the number of the bit (starting from 1) this identifier The characters from \e01 to \e40 can be used to specify bit numbers in the
describes. range from 1 to 32 and characters from \e200 to \e377 to specify bit numbers
in the range from 1 to 128.
The rest of the identifier is a string of characters containing the name of The rest of the identifier is a string of characters containing the name of
the bit. the bit.
The string is terminated by either the bit number at the start of the next The identifier is terminated by either the bit number at the start of the next
bit identifier or bit identifier or by
.Dv NUL .Dv NUL
for the last bit identifier. for the last bit identifier.
.Pp .Pp
@@ -173,6 +174,16 @@ reg=3<BITTWO,BITONE>
out: 41:41:5a:5a out: 41:41:5a:5a
.Ed .Ed
.Pp .Pp
The same output will be generated by the following function:
.Bd -literal -offset indent
void
printf_test(void)
{
printf("reg=%b\en", 3, "\e10\e201BITTWO\e200BITONE");
printf("out: %4D\en", "AAZZ", ":");
}
.Ed
.Pp
The call The call
.Bd -literal -offset indent .Bd -literal -offset indent
log(LOG_DEBUG, "%s%d: been there.\en", sc->sc_name, sc->sc_unit); log(LOG_DEBUG, "%s%d: been there.\en", sc->sc_name, sc->sc_unit);
+31 -9
View File
@@ -628,6 +628,18 @@ ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
return (p); return (p);
} }
static inline bool
isbitpos(char c)
{
return (c != '\0' && (c <= ' ' || (c & 0x80) != 0));
}
static inline bool
isprintnospace(char c)
{
return (isprint(c) && c != ' ');
}
/* /*
* Scaled down version of printf(3). * Scaled down version of printf(3).
* *
@@ -640,9 +652,12 @@ ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
* *
* where <base> is the output base expressed as a control character, e.g. * where <base> is the output base expressed as a control character, e.g.
* \10 gives octal; \20 gives hex. Each arg is a sequence of characters, * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
* the first of which gives the bit number to be inspected (origin 1), and * the first of which gives the bit number to be inspected and the next
* the next characters (up to a control character, i.e. a character <= 32), * characters (up to the bit number of the next argument or a final NUL
* give the name of the register. Thus: * character), give the name of the register.
* The bit number can be encoded as a character between 1 and 32 or as a
* character between 128 and 255.
* Thus:
* *
* kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE"); * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
* *
@@ -650,6 +665,10 @@ ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
* *
* reg=3<BITTWO,BITONE> * reg=3<BITTWO,BITONE>
* *
* The same output would be generated by using:
*
* kvprintf("reg=%b\n", 3, "\10\201BITTWO\200BITONE");
*
* XXX: %D -- Hexdump, takes pointer and separator string: * XXX: %D -- Hexdump, takes pointer and separator string:
* ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
* ("%*D", len, ptr, " " -> XX XX XX XX ... * ("%*D", len, ptr, " " -> XX XX XX XX ...
@@ -950,15 +969,18 @@ reswitch: switch (ch = (u_char)*fmt++) {
if (bconv && num != 0) { if (bconv && num != 0) {
/* %b conversion flag format. */ /* %b conversion flag format. */
tmp = retval; tmp = retval;
while (*q) { while (isbitpos(*q)) {
n = *q++; if ((*q & 0x80) != 0)
if (num & (1 << (n - 1))) { n = *q++ & 0x7f;
else
n = *q++ - 1;
if (num & (1ULL << n)) {
PCHAR(retval != tmp ? PCHAR(retval != tmp ?
',' : '<'); ',' : '<');
for (; (n = *q) > ' '; ++q) for (; isprintnospace(*q); ++q)
PCHAR(n); PCHAR(*q);
} else } else
for (; *q > ' '; ++q) for (; isprintnospace(*q); ++q)
continue; continue;
} }
if (retval != tmp) { if (retval != tmp) {