xref: /netbsd-src/lib/libutil/snprintb.3 (revision 4ed1e5f14431e22e51c46dbc5390209db113389a)
1.\"     $NetBSD: snprintb.3,v 1.39 2024/04/08 21:28:35 rillig Exp $
2.\"
3.\" Copyright (c) 1998, 2024 The NetBSD Foundation, Inc.
4.\" All rights reserved.
5.\"
6.\" This code is derived from software contributed to The NetBSD Foundation
7.\" by Jeremy Cooper.
8.\"
9.\" Redistribution and use in source and binary forms, with or without
10.\" modification, are permitted provided that the following conditions
11.\" are met:
12.\" 1. Redistributions of source code must retain the above copyright
13.\"    notice, this list of conditions and the following disclaimer.
14.\" 2. Redistributions in binary form must reproduce the above copyright
15.\"    notice, this list of conditions and the following disclaimer in the
16.\"    documentation and/or other materials provided with the distribution.
17.\"
18.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28.\" POSSIBILITY OF SUCH DAMAGE.
29.\"
30.Dd April 8, 2024
31.Dt SNPRINTB 3
32.Os
33.Sh NAME
34.Nm snprintb ,
35.Nm snprintb_m
36.Nd bitmask output conversion
37.Sh LIBRARY
38.Lb libutil
39.Sh SYNOPSIS
40.In util.h
41.Ft int
42.Fn "snprintb" "char *buf" "size_t bufsize" "const char *fmt" "uint64_t val"
43.Ft int
44.Fn "snprintb_m" "char *buf" "size_t bufsize" "const char *fmt" "uint64_t val" \
45"size_t max"
46.Sh DESCRIPTION
47The
48.Fn snprintb
49function formats a bitmask into a mnemonic form suitable for printing.
50.Pp
51It formats the integer
52.Fa val
53into the buffer
54.Fa buf ,
55of size
56.Fa bufsize ,
57interpreting the bits within that integer as flags or groups of bits.
58The buffer is always
59.Tn NUL Ns -terminated.
60If the buffer
61.Fa buf
62is too small to hold the formatted output,
63.Fn snprintb
64will fill as much as it can, and return the number of bytes
65that it would have written if the buffer were long enough excluding the
66terminating
67.Tn NUL .
68If
69.Fa bufsize
70is zero, nothing is written and
71.Fa buf
72may be a null pointer.
73.Pp
74The
75.Fn snprintb_m
76function accepts an additional
77.Fa max
78argument.
79If this argument is zero, the
80.Fn snprintb_m
81function behaves exactly like the
82.Fn snprintb
83function.
84If the
85.Fa max
86argument has a non-zero value, it represents the maximum length of a
87formatted string.
88If the formatted string would require more than
89.Fa max
90characters, the
91.Fn snprintb_m
92function returns multiple formatted strings in the output buffer
93.Fa buf .
94Each string is
95.Tn NUL Ns -terminated ,
96and the last string is followed by an
97additional
98.Tn NUL
99character
100.Pq or, if you prefer, a zero-length string .
101.Pp
102The decoding directive in
103.Fa fmt
104describes how the bitfield is to be interpreted and displayed.
105It follows two possible formats, referred to as
106.Dq old
107and
108.Dq new .
109The
110.Dq old
111format is limited to describing single bits in a 32-bit value,
112the bit positions are 1-based.
113The
114.Dq new
115format supports multi-bit fields and 64-bit values,
116the bit positions are 0-based.
117.Pp
118If the first character of
119.Fa fmt
120is
121.Pq in C escape-character format
122.Ql \e177
123or
124.Ql \ex7f ,
125the remainder of the
126.Fa fmt
127argument follows the
128.Dq new
129format.
130.Pp
131The next character
132.Po the first for the
133.Dq old
134format
135.Pc
136specifies the numeral base in which to print the numbers in the output.
137The possible values are
138.Ql \e010
139or
140.Ql \ex08
141for octal,
142.Ql \e012
143or
144.Ql \ex0a
145for decimal, and
146.Ql \e020
147or
148.Ql \ex10
149for hexadecimal.
150.Pp
151The remaining characters in the
152.Fa fmt
153argument represent the formatting conversions,
154according to the
155.Dq old
156or
157.Dq new
158format.
159.
160.Ss Old Format
161.Pp
162In the
163.Dq old
164format, each conversion specifies a bit position
165and a description that is printed if the corresponding bit is set.
166.Pp
167The bit position is a 1-based single-byte binary value,
168ranging from
169.Ql \e001
170or
171.Ql \ex01
172(1) for the least significant bit up to
173.Ql \e040
174or
175.Ql \ex20
176(32) for the most significant bit.
177.Pp
178The description is delimited by the next character whose value is 32 or less
179.Po see
180.Xr ascii 7
181.Pc ,
182or by the end of the format string itself.
183.
184.Ss New Format
185.Pp
186In the
187.Dq new
188format,
189each conversion begins with a conversion type,
190followed by type-specific parameters, each encoded as a single byte,
191followed by a
192.Tn NUL Ns -terminated description.
193The bit positions are 0-based,
194ranging from
195.Ql \e000
196or
197.Ql \ex00
198(0) for the least significant bit to
199.Ql \e077
200or
201.Ql \ex3f
202(63) for the most significant bit.
203.
204.Bl -tag -width Cm
205.
206.It Cm b Ar bit Ar descr
207Prints the description from
208.Ar descr
209if the bit at the position
210.Ar bit
211is set.
212.
213.It Cm f Ar lsb Ar width Ar descr
214Prints the description from
215.Ar descr ,
216a delimiting
217.Sq \&=
218and the numerical value of the multi-bit field
219whose least significant bit is at
220.Ar lsb
221and that spans
222.Ar width
223bits.
224To print individual values of the field, see the
225.Sq Cm \&=
226and
227.Sq Cm \&*
228conversions below.
229.
230.It Cm \&= Ar cmp Ar descr
231Compares the field value from the previous
232.Sq Cm f
233conversion to the single-byte value
234.Ar cmp ,
235ranging from
236.Ql \e000
237or
238.Ql \ex00
239(0) to
240.Ql \e377
241or
242.Ql \exff
243(255).
244If they are equal, prints
245.Ql \&=
246followed by the description from
247.Ar descr .
248This conversion may be repeated.
249.
250.It Cm F Ar lsb Ar width Op Ar descr
251Describes a multi-bit field like
252.Sq Cm f ,
253but just extracts the value for use with the
254.Sq Cm \&:
255and
256.Sq Cm \&*
257conversions below.
258The description from
259.Ar descr
260is ignored,
261it is only present for uniformity with the other conversions.
262.
263.It Cm \&: Ar cmp Ar descr
264Compares the field value from the previous
265.Sq Cm F
266conversion to the single-byte value
267.Ar cmp ,
268ranging from
269.Ql \e000
270or
271.Ql \ex00
272(0) to
273.Ql \e377
274or
275.Ql \exff
276(255).
277If they are equal, prints the description from
278.Ar descr .
279This conversion may be repeated.
280.
281.It Cm * Ar fmt
282If none of the previous
283.Sq Cm \&=
284or
285.Sq Cm \&:
286conversions matched, prints the format string
287.Ar fmt
288via
289.Xr snprintf 3 .
290The format string
291.Ar fmt
292may contain a single
293.Vt uintmax_t
294conversion specification to print the field value that did not match.
295.El
296.Pp
297The new format is terminated by an additional
298.Tn NUL
299character at the end, following that delimiting the last conversion.
300This
301.Tn NUL
302is supplied by the compiler to terminate the string literal and
303doesn't need to be written explicitly.
304.Sh RETURN VALUES
305The
306.Fn snprintb
307and
308.Fn snprintb_m
309functions return the number of bytes that they would have written to the buffer
310if there was adequate space, excluding the final terminating NUL, or \-1 in
311case an error occurred.
312For
313.Fn snprintb_m ,
314the NUL characters terminating each individual string are included in the
315total number of bytes.
316.Sh EXAMPLES
317Two examples of the old formatting style:
318.Bd -literal -offset indent
319snprintb(buf, bufsize, "\e010\e002BITTWO\e001BITONE", 3)
320\(rA "03<BITTWO,BITONE>"
321
322snprintb(buf, bufsize,
323    "\ex10"
324    "\ex10" "NOTBOOT"
325    "\ex0f" "FPP"
326    "\ex0e" "SDVMA"
327    "\ex0c" "VIDEO"
328    "\ex0b" "LORES"
329    "\ex0a" "FPA"
330    "\ex09" "DIAG"
331    "\ex07" "CACHE"
332    "\ex06" "IOCACHE"
333    "\ex05" "LOOPBACK"
334    "\ex04" "DBGCACHE",
335    0xe860)
336\(rA "0xe860<NOTBOOT,FPP,SDVMA,VIDEO,CACHE,IOCACHE>"
337.Ed
338.Pp
339An example of the new formatting style:
340.Bd -literal -offset indent
341snprintb(buf, bufsize,
342    "\e177\e020"
343    "b\e000" "LSB\e0"
344    "b\e001" "BITONE\e0"
345    "f\e004\e004" "NIBBLE2\e0"
346    "f\e020\e004" "BURST\e0"
347        "=\ex04" "FOUR\e0"
348        "=\ex0f" "FIFTEEN\e0"
349    "b\e037" "MSB\e0",
350    0x800f0701)
351\(rA "0x800f0701<LSB,NIBBLE2=0,BURST=0xf=FIFTEEN,MSB>"
352.Ed
353.Pp
354The same example using snprintb_m:
355.Bd -literal -offset indent
356snprintb_m(buf, bufsize,
357    "\e177\e020"
358    "b\e000" "LSB\e0"
359    "b\e001" "BITONE\e0"
360    "f\e004\e004" "NIBBLE2\e0"
361    "f\e020\e004" "BURST\e0"
362        "=\ex04" "FOUR\e0"
363        "=\ex0f" "FIFTEEN\e0"
364    "b\e037" "MSB\e0",
365    0x800f0701, 34)
366\(rA "0x800f0701<LSB,NIBBLE2=0>\e0"
367   "0x800f0701<BURST=0xf=FIFTEEN,MSB>\e0"
368   ""
369.Ed
370.Pp
371A more complex example from
372.In sys/mman.h
373that uses both the single-bit
374.Sq Cm b
375formatting as well as the multi-bit field
376.Sq Cm F
377formatting with a default
378.Sq Cm \&* :
379.Bd -literal -offset indent
380#define MAP_FMT "\e177\e020"                      \e
381        "b\e0"  "SHARED\e0"                       \e
382        "b\e1"  "PRIVATE\e0"                      \e
383        "b\e2"  "COPY\e0"                         \e
384        "b\e4"  "FIXED\e0"                        \e
385        "b\e5"  "RENAME\e0"                       \e
386        "b\e6"  "NORESERVE\e0"                    \e
387        "b\e7"  "INHERIT\e0"                      \e
388        "b\e11" "HASSEMAPHORE\e0"                 \e
389        "b\e12" "TRYFIXED\e0"                     \e
390        "b\e13" "WIRED\e0"                        \e
391        "F\e14\e1\e0"                              \e
392                ":\e0" "FILE\e0"                  \e
393                ":\e1" "ANONYMOUS\e0"             \e
394        "b\e15" "STACK\e0"                        \e
395        "F\e30\e010\e0"                            \e
396                ":\e000" "ALIGN=NONE\e0"          \e
397                ":\e012" "ALIGN=1KB\e0"           \e
398                ":\e013" "ALIGN=2KB\e0"           \e
399                ":\e014" "ALIGN=4KB\e0"           \e
400                ":\e015" "ALIGN=8KB\e0"           \e
401                ":\e016" "ALIGN=16KB\e0"          \e
402                ":\e017" "ALIGN=32KB\e0"          \e
403                ":\e020" "ALIGN=64KB\e0"          \e
404                ":\e021" "ALIGN=128KB\e0"         \e
405                ":\e022" "ALIGN=256KB\e0"         \e
406                ":\e023" "ALIGN=512KB\e0"         \e
407                ":\e024" "ALIGN=1MB\e0"           \e
408                ":\e025" "ALIGN=2MB\e0"           \e
409                ":\e026" "ALIGN=4MB\e0"           \e
410                ":\e027" "ALIGN=8MB\e0"           \e
411                ":\e030" "ALIGN=16MB\e0"          \e
412                ":\e034" "ALIGN=256MB\e0"         \e
413                ":\e040" "ALIGN=4GB\e0"           \e
414                ":\e044" "ALIGN=64GB\e0"          \e
415                ":\e050" "ALIGN=1TB\e0"           \e
416                ":\e054" "ALIGN=16TB\e0"          \e
417                ":\e060" "ALIGN=256TB\e0"         \e
418                ":\e064" "ALIGN=4PB\e0"           \e
419                ":\e070" "ALIGN=64PB\e0"          \e
420                ":\e074" "ALIGN=1EB\e0"           \e
421                "*"     "ALIGN=2^%ju\e0"
422
423snprintb(buf, bufsize, MAP_FMT, 0x0d001234)
424\(rA "0xd001234<COPY,FIXED,RENAME,HASSEMAPHORE,ANONYMOUS,ALIGN=8KB>"
425
426snprintb(buf, bufsize, MAP_FMT, 0x2e000000)
427\(rA "0x2e000000<FILE,ALIGN=2^46>"
428.Ed
429.Sh ERRORS
430.Fn snprintb
431will fail if:
432.Bl -tag -width Er
433.It Bq Er EINVAL
434The leading character
435.Po for the
436.Dq old
437format
438.Pc
439or the second character
440.Po for the
441.Dq new
442format
443.Pc
444does not describe a supported numeral base,
445or a bit number in the
446.Ar fmt
447argument is out of bounds,
448or the sequence of conversions in the
449.Ar fmt
450argument is invalid,
451or
452.Fn snprintf
453failed.
454.El
455.Sh SEE ALSO
456.Xr snprintf 3
457.Sh HISTORY
458The
459.Fn snprintb
460function was originally implemented as a non-standard
461.Li %b
462format string for the kernel
463.Fn printf
464function in
465.Nx 1.5
466and earlier releases.
467It was called
468.Fn bitmask_snprintf
469in
470.Nx 5.0
471and earlier releases.
472.Sh AUTHORS
473The
474.Dq new
475format was the invention of
476.An Chris Torek .
477.Sh CAVEATS
478When using hexadecimal character escapes for bit positions or field widths,
479if a following description starts with one of the letters A to F,
480that letter is considered part of the character escape.
481In such a situation, the character escape and the description must be
482put into separate string literals, as in
483.Li \[dq]\ex0f\[dq] \[dq]FIFTEEN\[dq] .
484