xref: /linux/fs/bcachefs/printbuf.c (revision 2dcb605e86891f17bea3d77ba05f5d12cdf38573)
1401ec4dbSKent Overstreet // SPDX-License-Identifier: LGPL-2.1+
2401ec4dbSKent Overstreet /* Copyright (C) 2022 Kent Overstreet */
3401ec4dbSKent Overstreet 
4560661d4SKent Overstreet #include <linux/bitmap.h>
5401ec4dbSKent Overstreet #include <linux/err.h>
6401ec4dbSKent Overstreet #include <linux/export.h>
7401ec4dbSKent Overstreet #include <linux/kernel.h>
8401ec4dbSKent Overstreet #include <linux/slab.h>
9401ec4dbSKent Overstreet #include <linux/string_helpers.h>
10401ec4dbSKent Overstreet 
11401ec4dbSKent Overstreet #include "printbuf.h"
12401ec4dbSKent Overstreet 
13*2dcb605eSKent Overstreet static inline unsigned __printbuf_linelen(struct printbuf *buf, unsigned pos)
14*2dcb605eSKent Overstreet {
15*2dcb605eSKent Overstreet 	return pos - buf->last_newline;
16*2dcb605eSKent Overstreet }
17*2dcb605eSKent Overstreet 
18401ec4dbSKent Overstreet static inline unsigned printbuf_linelen(struct printbuf *buf)
19401ec4dbSKent Overstreet {
20*2dcb605eSKent Overstreet 	return __printbuf_linelen(buf, buf->pos);
21*2dcb605eSKent Overstreet }
22*2dcb605eSKent Overstreet 
23*2dcb605eSKent Overstreet /*
24*2dcb605eSKent Overstreet  * Returns spaces from start of line, if set, or 0 if unset:
25*2dcb605eSKent Overstreet  */
26*2dcb605eSKent Overstreet static inline unsigned cur_tabstop(struct printbuf *buf)
27*2dcb605eSKent Overstreet {
28*2dcb605eSKent Overstreet 	return buf->cur_tabstop < buf->nr_tabstops
29*2dcb605eSKent Overstreet 		? buf->_tabstops[buf->cur_tabstop]
30*2dcb605eSKent Overstreet 		: 0;
31401ec4dbSKent Overstreet }
32401ec4dbSKent Overstreet 
33401ec4dbSKent Overstreet int bch2_printbuf_make_room(struct printbuf *out, unsigned extra)
34401ec4dbSKent Overstreet {
35401ec4dbSKent Overstreet 	/* Reserved space for terminating nul: */
36401ec4dbSKent Overstreet 	extra += 1;
37401ec4dbSKent Overstreet 
38acce32a5SKent Overstreet 	if (out->pos + extra <= out->size)
39401ec4dbSKent Overstreet 		return 0;
40401ec4dbSKent Overstreet 
41acce32a5SKent Overstreet 	if (!out->heap_allocated) {
42acce32a5SKent Overstreet 		out->overflow = true;
43acce32a5SKent Overstreet 		return 0;
44acce32a5SKent Overstreet 	}
45acce32a5SKent Overstreet 
46acce32a5SKent Overstreet 	unsigned new_size = roundup_pow_of_two(out->size + extra);
47401ec4dbSKent Overstreet 
48401ec4dbSKent Overstreet 	/*
49401ec4dbSKent Overstreet 	 * Note: output buffer must be freeable with kfree(), it's not required
50401ec4dbSKent Overstreet 	 * that the user use printbuf_exit().
51401ec4dbSKent Overstreet 	 */
52acce32a5SKent Overstreet 	char *buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT);
53401ec4dbSKent Overstreet 
54401ec4dbSKent Overstreet 	if (!buf) {
55401ec4dbSKent Overstreet 		out->allocation_failure = true;
56acce32a5SKent Overstreet 		out->overflow = true;
57401ec4dbSKent Overstreet 		return -ENOMEM;
58401ec4dbSKent Overstreet 	}
59401ec4dbSKent Overstreet 
60401ec4dbSKent Overstreet 	out->buf	= buf;
61401ec4dbSKent Overstreet 	out->size	= new_size;
62401ec4dbSKent Overstreet 	return 0;
63401ec4dbSKent Overstreet }
64401ec4dbSKent Overstreet 
65acce32a5SKent Overstreet static void printbuf_advance_pos(struct printbuf *out, unsigned len)
66acce32a5SKent Overstreet {
67acce32a5SKent Overstreet 	out->pos += min(len, printbuf_remaining(out));
68acce32a5SKent Overstreet }
69acce32a5SKent Overstreet 
70*2dcb605eSKent Overstreet static void printbuf_insert_spaces(struct printbuf *out, unsigned pos, unsigned nr)
71*2dcb605eSKent Overstreet {
72*2dcb605eSKent Overstreet 	unsigned move = out->pos - pos;
73*2dcb605eSKent Overstreet 
74*2dcb605eSKent Overstreet 	bch2_printbuf_make_room(out, nr);
75*2dcb605eSKent Overstreet 
76*2dcb605eSKent Overstreet 	if (pos + nr < out->size)
77*2dcb605eSKent Overstreet 		memmove(out->buf + pos + nr,
78*2dcb605eSKent Overstreet 			out->buf + pos,
79*2dcb605eSKent Overstreet 			min(move, out->size - 1 - pos - nr));
80*2dcb605eSKent Overstreet 
81*2dcb605eSKent Overstreet 	if (pos < out->size)
82*2dcb605eSKent Overstreet 		memset(out->buf + pos, ' ', min(nr, out->size - pos));
83*2dcb605eSKent Overstreet 
84*2dcb605eSKent Overstreet 	printbuf_advance_pos(out, nr);
85*2dcb605eSKent Overstreet 	printbuf_nul_terminate_reserved(out);
86*2dcb605eSKent Overstreet }
87*2dcb605eSKent Overstreet 
88*2dcb605eSKent Overstreet static void __printbuf_do_indent(struct printbuf *out, unsigned pos)
89*2dcb605eSKent Overstreet {
90*2dcb605eSKent Overstreet 	while (true) {
91*2dcb605eSKent Overstreet 		int pad;
92*2dcb605eSKent Overstreet 		unsigned len = out->pos - pos;
93*2dcb605eSKent Overstreet 		char *p = out->buf + pos;
94*2dcb605eSKent Overstreet 		char *n = memscan(p, '\n', len);
95*2dcb605eSKent Overstreet 		if (cur_tabstop(out)) {
96*2dcb605eSKent Overstreet 			n = min(n, (char *) memscan(p, '\r', len));
97*2dcb605eSKent Overstreet 			n = min(n, (char *) memscan(p, '\t', len));
98*2dcb605eSKent Overstreet 		}
99*2dcb605eSKent Overstreet 
100*2dcb605eSKent Overstreet 		pos = n - out->buf;
101*2dcb605eSKent Overstreet 		if (pos == out->pos)
102*2dcb605eSKent Overstreet 			break;
103*2dcb605eSKent Overstreet 
104*2dcb605eSKent Overstreet 		switch (*n) {
105*2dcb605eSKent Overstreet 		case '\n':
106*2dcb605eSKent Overstreet 			pos++;
107*2dcb605eSKent Overstreet 			out->last_newline = pos;
108*2dcb605eSKent Overstreet 
109*2dcb605eSKent Overstreet 			printbuf_insert_spaces(out, pos, out->indent);
110*2dcb605eSKent Overstreet 
111*2dcb605eSKent Overstreet 			pos = min(pos + out->indent, out->pos);
112*2dcb605eSKent Overstreet 			out->last_field = pos;
113*2dcb605eSKent Overstreet 			out->cur_tabstop = 0;
114*2dcb605eSKent Overstreet 			break;
115*2dcb605eSKent Overstreet 		case '\r':
116*2dcb605eSKent Overstreet 			memmove(n, n + 1, out->pos - pos);
117*2dcb605eSKent Overstreet 			--out->pos;
118*2dcb605eSKent Overstreet 			pad = (int) cur_tabstop(out) - (int) __printbuf_linelen(out, pos);
119*2dcb605eSKent Overstreet 			if (pad > 0) {
120*2dcb605eSKent Overstreet 				printbuf_insert_spaces(out, out->last_field, pad);
121*2dcb605eSKent Overstreet 				pos += pad;
122*2dcb605eSKent Overstreet 			}
123*2dcb605eSKent Overstreet 
124*2dcb605eSKent Overstreet 			out->last_field = pos;
125*2dcb605eSKent Overstreet 			out->cur_tabstop++;
126*2dcb605eSKent Overstreet 			break;
127*2dcb605eSKent Overstreet 		case '\t':
128*2dcb605eSKent Overstreet 			pad = (int) cur_tabstop(out) - (int) __printbuf_linelen(out, pos) - 1;
129*2dcb605eSKent Overstreet 			if (pad > 0) {
130*2dcb605eSKent Overstreet 				*n = ' ';
131*2dcb605eSKent Overstreet 				printbuf_insert_spaces(out, pos, pad - 1);
132*2dcb605eSKent Overstreet 				pos += pad;
133*2dcb605eSKent Overstreet 			} else {
134*2dcb605eSKent Overstreet 				memmove(n, n + 1, out->pos - pos);
135*2dcb605eSKent Overstreet 				--out->pos;
136*2dcb605eSKent Overstreet 			}
137*2dcb605eSKent Overstreet 
138*2dcb605eSKent Overstreet 			out->last_field = pos;
139*2dcb605eSKent Overstreet 			out->cur_tabstop++;
140*2dcb605eSKent Overstreet 			break;
141*2dcb605eSKent Overstreet 		}
142*2dcb605eSKent Overstreet 	}
143*2dcb605eSKent Overstreet }
144*2dcb605eSKent Overstreet 
145*2dcb605eSKent Overstreet static inline void printbuf_do_indent(struct printbuf *out, unsigned pos)
146*2dcb605eSKent Overstreet {
147*2dcb605eSKent Overstreet 	if (out->has_indent_or_tabstops && !out->suppress_indent_tabstop_handling)
148*2dcb605eSKent Overstreet 		__printbuf_do_indent(out, pos);
149*2dcb605eSKent Overstreet }
150*2dcb605eSKent Overstreet 
151401ec4dbSKent Overstreet void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list args)
152401ec4dbSKent Overstreet {
153401ec4dbSKent Overstreet 	int len;
154401ec4dbSKent Overstreet 
155401ec4dbSKent Overstreet 	do {
156401ec4dbSKent Overstreet 		va_list args2;
157401ec4dbSKent Overstreet 
158401ec4dbSKent Overstreet 		va_copy(args2, args);
159acce32a5SKent Overstreet 		len = vsnprintf(out->buf + out->pos, printbuf_remaining_size(out), fmt, args2);
160816054f4SKent Overstreet 		va_end(args2);
161acce32a5SKent Overstreet 	} while (len > printbuf_remaining(out) &&
162acce32a5SKent Overstreet 		 !bch2_printbuf_make_room(out, len));
163401ec4dbSKent Overstreet 
164*2dcb605eSKent Overstreet 	unsigned indent_pos = out->pos;
165acce32a5SKent Overstreet 	printbuf_advance_pos(out, len);
166*2dcb605eSKent Overstreet 	printbuf_do_indent(out, indent_pos);
167401ec4dbSKent Overstreet }
168401ec4dbSKent Overstreet 
169401ec4dbSKent Overstreet void bch2_prt_printf(struct printbuf *out, const char *fmt, ...)
170401ec4dbSKent Overstreet {
171401ec4dbSKent Overstreet 	va_list args;
172401ec4dbSKent Overstreet 	int len;
173401ec4dbSKent Overstreet 
174401ec4dbSKent Overstreet 	do {
175401ec4dbSKent Overstreet 		va_start(args, fmt);
176acce32a5SKent Overstreet 		len = vsnprintf(out->buf + out->pos, printbuf_remaining_size(out), fmt, args);
177401ec4dbSKent Overstreet 		va_end(args);
178acce32a5SKent Overstreet 	} while (len > printbuf_remaining(out) &&
179acce32a5SKent Overstreet 		 !bch2_printbuf_make_room(out, len));
180401ec4dbSKent Overstreet 
181*2dcb605eSKent Overstreet 	unsigned indent_pos = out->pos;
182acce32a5SKent Overstreet 	printbuf_advance_pos(out, len);
183*2dcb605eSKent Overstreet 	printbuf_do_indent(out, indent_pos);
184401ec4dbSKent Overstreet }
185401ec4dbSKent Overstreet 
186401ec4dbSKent Overstreet /**
18796dea3d5SKent Overstreet  * bch2_printbuf_str() - returns printbuf's buf as a C string, guaranteed to be
18896dea3d5SKent Overstreet  * null terminated
18996dea3d5SKent Overstreet  * @buf:	printbuf to terminate
19096dea3d5SKent Overstreet  * Returns:	Printbuf contents, as a nul terminated C string
191401ec4dbSKent Overstreet  */
192401ec4dbSKent Overstreet const char *bch2_printbuf_str(const struct printbuf *buf)
193401ec4dbSKent Overstreet {
194401ec4dbSKent Overstreet 	/*
195401ec4dbSKent Overstreet 	 * If we've written to a printbuf then it's guaranteed to be a null
196401ec4dbSKent Overstreet 	 * terminated string - but if we haven't, then we might not have
197401ec4dbSKent Overstreet 	 * allocated a buffer at all:
198401ec4dbSKent Overstreet 	 */
199401ec4dbSKent Overstreet 	return buf->pos
200401ec4dbSKent Overstreet 		? buf->buf
201401ec4dbSKent Overstreet 		: "";
202401ec4dbSKent Overstreet }
203401ec4dbSKent Overstreet 
204401ec4dbSKent Overstreet /**
20596dea3d5SKent Overstreet  * bch2_printbuf_exit() - exit a printbuf, freeing memory it owns and poisoning it
206401ec4dbSKent Overstreet  * against accidental use.
20796dea3d5SKent Overstreet  * @buf:	printbuf to exit
208401ec4dbSKent Overstreet  */
209401ec4dbSKent Overstreet void bch2_printbuf_exit(struct printbuf *buf)
210401ec4dbSKent Overstreet {
211401ec4dbSKent Overstreet 	if (buf->heap_allocated) {
212401ec4dbSKent Overstreet 		kfree(buf->buf);
213401ec4dbSKent Overstreet 		buf->buf = ERR_PTR(-EINTR); /* poison value */
214401ec4dbSKent Overstreet 	}
215401ec4dbSKent Overstreet }
216401ec4dbSKent Overstreet 
217401ec4dbSKent Overstreet void bch2_printbuf_tabstops_reset(struct printbuf *buf)
218401ec4dbSKent Overstreet {
219401ec4dbSKent Overstreet 	buf->nr_tabstops = 0;
220401ec4dbSKent Overstreet }
221401ec4dbSKent Overstreet 
222401ec4dbSKent Overstreet void bch2_printbuf_tabstop_pop(struct printbuf *buf)
223401ec4dbSKent Overstreet {
224401ec4dbSKent Overstreet 	if (buf->nr_tabstops)
225401ec4dbSKent Overstreet 		--buf->nr_tabstops;
226401ec4dbSKent Overstreet }
227401ec4dbSKent Overstreet 
228401ec4dbSKent Overstreet /*
22996dea3d5SKent Overstreet  * bch2_printbuf_tabstop_set() - add a tabstop, n spaces from the previous tabstop
230401ec4dbSKent Overstreet  *
231401ec4dbSKent Overstreet  * @buf: printbuf to control
232401ec4dbSKent Overstreet  * @spaces: number of spaces from previous tabpstop
233401ec4dbSKent Overstreet  *
234401ec4dbSKent Overstreet  * In the future this function may allocate memory if setting more than
235401ec4dbSKent Overstreet  * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start
236401ec4dbSKent Overstreet  * of line.
237401ec4dbSKent Overstreet  */
238401ec4dbSKent Overstreet int bch2_printbuf_tabstop_push(struct printbuf *buf, unsigned spaces)
239401ec4dbSKent Overstreet {
240401ec4dbSKent Overstreet 	unsigned prev_tabstop = buf->nr_tabstops
241401ec4dbSKent Overstreet 		? buf->_tabstops[buf->nr_tabstops - 1]
242401ec4dbSKent Overstreet 		: 0;
243401ec4dbSKent Overstreet 
244401ec4dbSKent Overstreet 	if (WARN_ON(buf->nr_tabstops >= ARRAY_SIZE(buf->_tabstops)))
245401ec4dbSKent Overstreet 		return -EINVAL;
246401ec4dbSKent Overstreet 
247401ec4dbSKent Overstreet 	buf->_tabstops[buf->nr_tabstops++] = prev_tabstop + spaces;
248401ec4dbSKent Overstreet 	buf->has_indent_or_tabstops = true;
249401ec4dbSKent Overstreet 	return 0;
250401ec4dbSKent Overstreet }
251401ec4dbSKent Overstreet 
252401ec4dbSKent Overstreet /**
25396dea3d5SKent Overstreet  * bch2_printbuf_indent_add() - add to the current indent level
254401ec4dbSKent Overstreet  *
255401ec4dbSKent Overstreet  * @buf: printbuf to control
256401ec4dbSKent Overstreet  * @spaces: number of spaces to add to the current indent level
257401ec4dbSKent Overstreet  *
258401ec4dbSKent Overstreet  * Subsequent lines, and the current line if the output position is at the start
259401ec4dbSKent Overstreet  * of the current line, will be indented by @spaces more spaces.
260401ec4dbSKent Overstreet  */
261401ec4dbSKent Overstreet void bch2_printbuf_indent_add(struct printbuf *buf, unsigned spaces)
262401ec4dbSKent Overstreet {
263401ec4dbSKent Overstreet 	if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
264401ec4dbSKent Overstreet 		spaces = 0;
265401ec4dbSKent Overstreet 
266401ec4dbSKent Overstreet 	buf->indent += spaces;
267401ec4dbSKent Overstreet 	prt_chars(buf, ' ', spaces);
268401ec4dbSKent Overstreet 
269401ec4dbSKent Overstreet 	buf->has_indent_or_tabstops = true;
270401ec4dbSKent Overstreet }
271401ec4dbSKent Overstreet 
272401ec4dbSKent Overstreet /**
27396dea3d5SKent Overstreet  * bch2_printbuf_indent_sub() - subtract from the current indent level
274401ec4dbSKent Overstreet  *
275401ec4dbSKent Overstreet  * @buf: printbuf to control
276401ec4dbSKent Overstreet  * @spaces: number of spaces to subtract from the current indent level
277401ec4dbSKent Overstreet  *
278401ec4dbSKent Overstreet  * Subsequent lines, and the current line if the output position is at the start
279401ec4dbSKent Overstreet  * of the current line, will be indented by @spaces less spaces.
280401ec4dbSKent Overstreet  */
281401ec4dbSKent Overstreet void bch2_printbuf_indent_sub(struct printbuf *buf, unsigned spaces)
282401ec4dbSKent Overstreet {
283401ec4dbSKent Overstreet 	if (WARN_ON_ONCE(spaces > buf->indent))
284401ec4dbSKent Overstreet 		spaces = buf->indent;
285401ec4dbSKent Overstreet 
286401ec4dbSKent Overstreet 	if (buf->last_newline + buf->indent == buf->pos) {
287401ec4dbSKent Overstreet 		buf->pos -= spaces;
288401ec4dbSKent Overstreet 		printbuf_nul_terminate(buf);
289401ec4dbSKent Overstreet 	}
290401ec4dbSKent Overstreet 	buf->indent -= spaces;
291401ec4dbSKent Overstreet 
292401ec4dbSKent Overstreet 	if (!buf->indent && !buf->nr_tabstops)
293401ec4dbSKent Overstreet 		buf->has_indent_or_tabstops = false;
294401ec4dbSKent Overstreet }
295401ec4dbSKent Overstreet 
296401ec4dbSKent Overstreet void bch2_prt_newline(struct printbuf *buf)
297401ec4dbSKent Overstreet {
298401ec4dbSKent Overstreet 	bch2_printbuf_make_room(buf, 1 + buf->indent);
299401ec4dbSKent Overstreet 
300acce32a5SKent Overstreet 	__prt_char_reserved(buf, '\n');
301401ec4dbSKent Overstreet 
302401ec4dbSKent Overstreet 	buf->last_newline	= buf->pos;
303401ec4dbSKent Overstreet 
304acce32a5SKent Overstreet 	__prt_chars_reserved(buf, ' ', buf->indent);
305401ec4dbSKent Overstreet 
306acce32a5SKent Overstreet 	printbuf_nul_terminate_reserved(buf);
307401ec4dbSKent Overstreet 
308401ec4dbSKent Overstreet 	buf->last_field		= buf->pos;
309401ec4dbSKent Overstreet 	buf->cur_tabstop	= 0;
310401ec4dbSKent Overstreet }
311401ec4dbSKent Overstreet 
312401ec4dbSKent Overstreet static void __prt_tab(struct printbuf *out)
313401ec4dbSKent Overstreet {
314401ec4dbSKent Overstreet 	int spaces = max_t(int, 0, cur_tabstop(out) - printbuf_linelen(out));
315401ec4dbSKent Overstreet 
316401ec4dbSKent Overstreet 	prt_chars(out, ' ', spaces);
317401ec4dbSKent Overstreet 
318401ec4dbSKent Overstreet 	out->last_field = out->pos;
319401ec4dbSKent Overstreet 	out->cur_tabstop++;
320401ec4dbSKent Overstreet }
321401ec4dbSKent Overstreet 
322401ec4dbSKent Overstreet /**
32396dea3d5SKent Overstreet  * bch2_prt_tab() - Advance printbuf to the next tabstop
32496dea3d5SKent Overstreet  * @out:	printbuf to control
325401ec4dbSKent Overstreet  *
326401ec4dbSKent Overstreet  * Advance output to the next tabstop by printing spaces.
327401ec4dbSKent Overstreet  */
328401ec4dbSKent Overstreet void bch2_prt_tab(struct printbuf *out)
329401ec4dbSKent Overstreet {
330401ec4dbSKent Overstreet 	if (WARN_ON(!cur_tabstop(out)))
331401ec4dbSKent Overstreet 		return;
332401ec4dbSKent Overstreet 
333401ec4dbSKent Overstreet 	__prt_tab(out);
334401ec4dbSKent Overstreet }
335401ec4dbSKent Overstreet 
336401ec4dbSKent Overstreet static void __prt_tab_rjust(struct printbuf *buf)
337401ec4dbSKent Overstreet {
338401ec4dbSKent Overstreet 	int pad = (int) cur_tabstop(buf) - (int) printbuf_linelen(buf);
339*2dcb605eSKent Overstreet 	if (pad > 0)
340*2dcb605eSKent Overstreet 		printbuf_insert_spaces(buf, buf->last_field, pad);
341401ec4dbSKent Overstreet 
342401ec4dbSKent Overstreet 	buf->last_field = buf->pos;
343401ec4dbSKent Overstreet 	buf->cur_tabstop++;
344401ec4dbSKent Overstreet }
345401ec4dbSKent Overstreet 
346401ec4dbSKent Overstreet /**
34796dea3d5SKent Overstreet  * bch2_prt_tab_rjust - Advance printbuf to the next tabstop, right justifying
348401ec4dbSKent Overstreet  * previous output
349401ec4dbSKent Overstreet  *
350401ec4dbSKent Overstreet  * @buf: printbuf to control
351401ec4dbSKent Overstreet  *
352401ec4dbSKent Overstreet  * Advance output to the next tabstop by inserting spaces immediately after the
353401ec4dbSKent Overstreet  * previous tabstop, right justifying previously outputted text.
354401ec4dbSKent Overstreet  */
355401ec4dbSKent Overstreet void bch2_prt_tab_rjust(struct printbuf *buf)
356401ec4dbSKent Overstreet {
357401ec4dbSKent Overstreet 	if (WARN_ON(!cur_tabstop(buf)))
358401ec4dbSKent Overstreet 		return;
359401ec4dbSKent Overstreet 
360401ec4dbSKent Overstreet 	__prt_tab_rjust(buf);
361401ec4dbSKent Overstreet }
362401ec4dbSKent Overstreet 
363401ec4dbSKent Overstreet /**
36496dea3d5SKent Overstreet  * bch2_prt_bytes_indented() - Print an array of chars, handling embedded control characters
365401ec4dbSKent Overstreet  *
36696dea3d5SKent Overstreet  * @out:	output printbuf
367401ec4dbSKent Overstreet  * @str:	string to print
368401ec4dbSKent Overstreet  * @count:	number of bytes to print
369401ec4dbSKent Overstreet  *
370401ec4dbSKent Overstreet  * The following contol characters are handled as so:
371401ec4dbSKent Overstreet  *   \n: prt_newline	newline that obeys current indent level
372401ec4dbSKent Overstreet  *   \t: prt_tab	advance to next tabstop
373401ec4dbSKent Overstreet  *   \r: prt_tab_rjust	advance to next tabstop, with right justification
374401ec4dbSKent Overstreet  */
375401ec4dbSKent Overstreet void bch2_prt_bytes_indented(struct printbuf *out, const char *str, unsigned count)
376401ec4dbSKent Overstreet {
377*2dcb605eSKent Overstreet 	unsigned indent_pos = out->pos;
378401ec4dbSKent Overstreet 	prt_bytes(out, str, count);
379*2dcb605eSKent Overstreet 	printbuf_do_indent(out, indent_pos);
380401ec4dbSKent Overstreet }
381401ec4dbSKent Overstreet 
382401ec4dbSKent Overstreet /**
38396dea3d5SKent Overstreet  * bch2_prt_human_readable_u64() - Print out a u64 in human readable units
38496dea3d5SKent Overstreet  * @out:	output printbuf
38596dea3d5SKent Overstreet  * @v:		integer to print
386401ec4dbSKent Overstreet  *
38796dea3d5SKent Overstreet  * Units of 2^10 (default) or 10^3 are controlled via @out->si_units
388401ec4dbSKent Overstreet  */
38996dea3d5SKent Overstreet void bch2_prt_human_readable_u64(struct printbuf *out, u64 v)
390401ec4dbSKent Overstreet {
39196dea3d5SKent Overstreet 	bch2_printbuf_make_room(out, 10);
392acce32a5SKent Overstreet 	unsigned len = string_get_size(v, 1, !out->si_units,
39396dea3d5SKent Overstreet 				       out->buf + out->pos,
39496dea3d5SKent Overstreet 				       printbuf_remaining_size(out));
395acce32a5SKent Overstreet 	printbuf_advance_pos(out, len);
396401ec4dbSKent Overstreet }
397401ec4dbSKent Overstreet 
398401ec4dbSKent Overstreet /**
39996dea3d5SKent Overstreet  * bch2_prt_human_readable_s64() - Print out a s64 in human readable units
40096dea3d5SKent Overstreet  * @out:	output printbuf
40196dea3d5SKent Overstreet  * @v:		integer to print
402401ec4dbSKent Overstreet  *
40396dea3d5SKent Overstreet  * Units of 2^10 (default) or 10^3 are controlled via @out->si_units
404401ec4dbSKent Overstreet  */
40596dea3d5SKent Overstreet void bch2_prt_human_readable_s64(struct printbuf *out, s64 v)
406401ec4dbSKent Overstreet {
407401ec4dbSKent Overstreet 	if (v < 0)
40896dea3d5SKent Overstreet 		prt_char(out, '-');
40996dea3d5SKent Overstreet 	bch2_prt_human_readable_u64(out, abs(v));
410401ec4dbSKent Overstreet }
411401ec4dbSKent Overstreet 
412401ec4dbSKent Overstreet /**
41396dea3d5SKent Overstreet  * bch2_prt_units_u64() - Print out a u64 according to printbuf unit options
41496dea3d5SKent Overstreet  * @out:	output printbuf
41596dea3d5SKent Overstreet  * @v:		integer to print
416401ec4dbSKent Overstreet  *
417401ec4dbSKent Overstreet  * Units are either raw (default), or human reabable units (controlled via
418401ec4dbSKent Overstreet  * @buf->human_readable_units)
419401ec4dbSKent Overstreet  */
420401ec4dbSKent Overstreet void bch2_prt_units_u64(struct printbuf *out, u64 v)
421401ec4dbSKent Overstreet {
422401ec4dbSKent Overstreet 	if (out->human_readable_units)
423401ec4dbSKent Overstreet 		bch2_prt_human_readable_u64(out, v);
424401ec4dbSKent Overstreet 	else
425401ec4dbSKent Overstreet 		bch2_prt_printf(out, "%llu", v);
426401ec4dbSKent Overstreet }
427401ec4dbSKent Overstreet 
428401ec4dbSKent Overstreet /**
42996dea3d5SKent Overstreet  * bch2_prt_units_s64() - Print out a s64 according to printbuf unit options
43096dea3d5SKent Overstreet  * @out:	output printbuf
43196dea3d5SKent Overstreet  * @v:		integer to print
432401ec4dbSKent Overstreet  *
433401ec4dbSKent Overstreet  * Units are either raw (default), or human reabable units (controlled via
434401ec4dbSKent Overstreet  * @buf->human_readable_units)
435401ec4dbSKent Overstreet  */
436401ec4dbSKent Overstreet void bch2_prt_units_s64(struct printbuf *out, s64 v)
437401ec4dbSKent Overstreet {
438401ec4dbSKent Overstreet 	if (v < 0)
439401ec4dbSKent Overstreet 		prt_char(out, '-');
440401ec4dbSKent Overstreet 	bch2_prt_units_u64(out, abs(v));
441401ec4dbSKent Overstreet }
442401ec4dbSKent Overstreet 
443401ec4dbSKent Overstreet void bch2_prt_string_option(struct printbuf *out,
444401ec4dbSKent Overstreet 			    const char * const list[],
445401ec4dbSKent Overstreet 			    size_t selected)
446401ec4dbSKent Overstreet {
447acce32a5SKent Overstreet 	for (size_t i = 0; list[i]; i++)
448401ec4dbSKent Overstreet 		bch2_prt_printf(out, i == selected ? "[%s] " : "%s ", list[i]);
449401ec4dbSKent Overstreet }
450401ec4dbSKent Overstreet 
451401ec4dbSKent Overstreet void bch2_prt_bitflags(struct printbuf *out,
452401ec4dbSKent Overstreet 		       const char * const list[], u64 flags)
453401ec4dbSKent Overstreet {
454401ec4dbSKent Overstreet 	unsigned bit, nr = 0;
455401ec4dbSKent Overstreet 	bool first = true;
456401ec4dbSKent Overstreet 
457401ec4dbSKent Overstreet 	while (list[nr])
458401ec4dbSKent Overstreet 		nr++;
459401ec4dbSKent Overstreet 
46048f866e9SKent Overstreet 	while (flags && (bit = __ffs64(flags)) < nr) {
461401ec4dbSKent Overstreet 		if (!first)
462401ec4dbSKent Overstreet 			bch2_prt_printf(out, ",");
463401ec4dbSKent Overstreet 		first = false;
464401ec4dbSKent Overstreet 		bch2_prt_printf(out, "%s", list[bit]);
46548f866e9SKent Overstreet 		flags ^= BIT_ULL(bit);
466401ec4dbSKent Overstreet 	}
467401ec4dbSKent Overstreet }
468560661d4SKent Overstreet 
469560661d4SKent Overstreet void bch2_prt_bitflags_vector(struct printbuf *out,
470560661d4SKent Overstreet 			      const char * const list[],
471560661d4SKent Overstreet 			      unsigned long *v, unsigned nr)
472560661d4SKent Overstreet {
473560661d4SKent Overstreet 	bool first = true;
474560661d4SKent Overstreet 	unsigned i;
475560661d4SKent Overstreet 
476560661d4SKent Overstreet 	for (i = 0; i < nr; i++)
477560661d4SKent Overstreet 		if (!list[i]) {
478560661d4SKent Overstreet 			nr = i - 1;
479560661d4SKent Overstreet 			break;
480560661d4SKent Overstreet 		}
481560661d4SKent Overstreet 
482560661d4SKent Overstreet 	for_each_set_bit(i, v, nr) {
483560661d4SKent Overstreet 		if (!first)
484560661d4SKent Overstreet 			bch2_prt_printf(out, ",");
485560661d4SKent Overstreet 		first = false;
486560661d4SKent Overstreet 		bch2_prt_printf(out, "%s", list[i]);
487560661d4SKent Overstreet 	}
488560661d4SKent Overstreet }
489