diff --git a/share/man/man9/printf.9 b/share/man/man9/printf.9 --- a/share/man/man9/printf.9 +++ b/share/man/man9/printf.9 @@ -24,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 18, 2025 +.Dd December 19, 2025 .Dt PRINTF 9 .Os .Sh NAME @@ -95,14 +95,15 @@ The base value is the output base (radix) expressed as an octal value; for example, \e10 gives octal and \e20 gives hexadecimal. The arguments are made up of a sequence of bit identifiers. -Each bit identifier begins with an -.Em octal -value which is the number of the bit (starting from 1) this identifier -describes. +Each bit identifier begins with a character specifying the number of the bit +(starting from 1) this identifier describes. +The characters from \e01 to \e40 can be used to specify bit numbers in the +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 bit. -The string is terminated by either the bit number at the start of the next -bit identifier or +The identifier is terminated by either the bit number at the start of the next +bit identifier or by .Dv NUL for the last bit identifier. .Pp @@ -173,6 +174,16 @@ out: 41:41:5a:5a .Ed .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 .Bd -literal -offset indent log(LOG_DEBUG, "%s%d: been there.\en", sc->sc_name, sc->sc_unit); diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -640,9 +640,12 @@ * * where is the output base expressed as a control character, e.g. * \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 next characters (up to a control character, i.e. a character <= 32), - * give the name of the register. Thus: + * the first of which gives the bit number to be inspected and the next + * characters (up to the bit number of the next argument or a final NUL + * 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"); * @@ -650,10 +653,27 @@ * * reg=3 * + * The same output would be generated by using: + * + * kvprintf("reg=%b\n", 3, "\10\201BITTWO\200BITONE"); + * * XXX: %D -- Hexdump, takes pointer and separator string: * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX * ("%*D", len, ptr, " " -> XX XX XX XX ... */ + +static inline bool +isbitpos(char c) +{ + return (c != '\0' && (c <= ' ' || (c & 0x80) != 0)); +} + +static inline bool +isprintnospace(char c) +{ + return (isprint(c) && c != ' '); +} + int kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap) { @@ -950,15 +970,18 @@ if (bconv && num != 0) { /* %b conversion flag format. */ tmp = retval; - while (*q) { - n = *q++; - if (num & (1 << (n - 1))) { + while (isbitpos(*q)) { + if ((*q & 0x80) != 0) + n = *q++ & 0x7f; + else + n = *q++ - 1; + if (num & (1ULL << n)) { PCHAR(retval != tmp ? ',' : '<'); - for (; (n = *q) > ' '; ++q) - PCHAR(n); + for (; isprintnospace(*q); ++q) + PCHAR(*q); } else - for (; *q > ' '; ++q) + for (; isprintnospace(*q); ++q) continue; } if (retval != tmp) {