1 // SPDX-License-Identifier: LGPL-2.1+ 2 /* Copyright (C) 2022 Kent Overstreet */ 3 4 #include <linux/err.h> 5 #include <linux/export.h> 6 #include <linux/kernel.h> 7 #include <linux/slab.h> 8 #include <linux/string_helpers.h> 9 10 #include "printbuf.h" 11 12 static inline unsigned printbuf_linelen(struct printbuf *buf) 13 { 14 return buf->pos - buf->last_newline; 15 } 16 17 int bch2_printbuf_make_room(struct printbuf *out, unsigned extra) 18 { 19 unsigned new_size; 20 char *buf; 21 22 if (!out->heap_allocated) 23 return 0; 24 25 /* Reserved space for terminating nul: */ 26 extra += 1; 27 28 if (out->pos + extra < out->size) 29 return 0; 30 31 new_size = roundup_pow_of_two(out->size + extra); 32 33 /* 34 * Note: output buffer must be freeable with kfree(), it's not required 35 * that the user use printbuf_exit(). 36 */ 37 buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT); 38 39 if (!buf) { 40 out->allocation_failure = true; 41 return -ENOMEM; 42 } 43 44 out->buf = buf; 45 out->size = new_size; 46 return 0; 47 } 48 49 void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list args) 50 { 51 int len; 52 53 do { 54 va_list args2; 55 56 va_copy(args2, args); 57 len = vsnprintf(out->buf + out->pos, printbuf_remaining(out), fmt, args2); 58 } while (len + 1 >= printbuf_remaining(out) && 59 !bch2_printbuf_make_room(out, len + 1)); 60 61 len = min_t(size_t, len, 62 printbuf_remaining(out) ? printbuf_remaining(out) - 1 : 0); 63 out->pos += len; 64 } 65 66 void bch2_prt_printf(struct printbuf *out, const char *fmt, ...) 67 { 68 va_list args; 69 int len; 70 71 do { 72 va_start(args, fmt); 73 len = vsnprintf(out->buf + out->pos, printbuf_remaining(out), fmt, args); 74 va_end(args); 75 } while (len + 1 >= printbuf_remaining(out) && 76 !bch2_printbuf_make_room(out, len + 1)); 77 78 len = min_t(size_t, len, 79 printbuf_remaining(out) ? printbuf_remaining(out) - 1 : 0); 80 out->pos += len; 81 } 82 83 /** 84 * bch2_printbuf_str() - returns printbuf's buf as a C string, guaranteed to be 85 * null terminated 86 * @buf: printbuf to terminate 87 * Returns: Printbuf contents, as a nul terminated C string 88 */ 89 const char *bch2_printbuf_str(const struct printbuf *buf) 90 { 91 /* 92 * If we've written to a printbuf then it's guaranteed to be a null 93 * terminated string - but if we haven't, then we might not have 94 * allocated a buffer at all: 95 */ 96 return buf->pos 97 ? buf->buf 98 : ""; 99 } 100 101 /** 102 * bch2_printbuf_exit() - exit a printbuf, freeing memory it owns and poisoning it 103 * against accidental use. 104 * @buf: printbuf to exit 105 */ 106 void bch2_printbuf_exit(struct printbuf *buf) 107 { 108 if (buf->heap_allocated) { 109 kfree(buf->buf); 110 buf->buf = ERR_PTR(-EINTR); /* poison value */ 111 } 112 } 113 114 void bch2_printbuf_tabstops_reset(struct printbuf *buf) 115 { 116 buf->nr_tabstops = 0; 117 } 118 119 void bch2_printbuf_tabstop_pop(struct printbuf *buf) 120 { 121 if (buf->nr_tabstops) 122 --buf->nr_tabstops; 123 } 124 125 /* 126 * bch2_printbuf_tabstop_set() - add a tabstop, n spaces from the previous tabstop 127 * 128 * @buf: printbuf to control 129 * @spaces: number of spaces from previous tabpstop 130 * 131 * In the future this function may allocate memory if setting more than 132 * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start 133 * of line. 134 */ 135 int bch2_printbuf_tabstop_push(struct printbuf *buf, unsigned spaces) 136 { 137 unsigned prev_tabstop = buf->nr_tabstops 138 ? buf->_tabstops[buf->nr_tabstops - 1] 139 : 0; 140 141 if (WARN_ON(buf->nr_tabstops >= ARRAY_SIZE(buf->_tabstops))) 142 return -EINVAL; 143 144 buf->_tabstops[buf->nr_tabstops++] = prev_tabstop + spaces; 145 buf->has_indent_or_tabstops = true; 146 return 0; 147 } 148 149 /** 150 * bch2_printbuf_indent_add() - add to the current indent level 151 * 152 * @buf: printbuf to control 153 * @spaces: number of spaces to add to the current indent level 154 * 155 * Subsequent lines, and the current line if the output position is at the start 156 * of the current line, will be indented by @spaces more spaces. 157 */ 158 void bch2_printbuf_indent_add(struct printbuf *buf, unsigned spaces) 159 { 160 if (WARN_ON_ONCE(buf->indent + spaces < buf->indent)) 161 spaces = 0; 162 163 buf->indent += spaces; 164 prt_chars(buf, ' ', spaces); 165 166 buf->has_indent_or_tabstops = true; 167 } 168 169 /** 170 * bch2_printbuf_indent_sub() - subtract from the current indent level 171 * 172 * @buf: printbuf to control 173 * @spaces: number of spaces to subtract from the current indent level 174 * 175 * Subsequent lines, and the current line if the output position is at the start 176 * of the current line, will be indented by @spaces less spaces. 177 */ 178 void bch2_printbuf_indent_sub(struct printbuf *buf, unsigned spaces) 179 { 180 if (WARN_ON_ONCE(spaces > buf->indent)) 181 spaces = buf->indent; 182 183 if (buf->last_newline + buf->indent == buf->pos) { 184 buf->pos -= spaces; 185 printbuf_nul_terminate(buf); 186 } 187 buf->indent -= spaces; 188 189 if (!buf->indent && !buf->nr_tabstops) 190 buf->has_indent_or_tabstops = false; 191 } 192 193 void bch2_prt_newline(struct printbuf *buf) 194 { 195 unsigned i; 196 197 bch2_printbuf_make_room(buf, 1 + buf->indent); 198 199 __prt_char(buf, '\n'); 200 201 buf->last_newline = buf->pos; 202 203 for (i = 0; i < buf->indent; i++) 204 __prt_char(buf, ' '); 205 206 printbuf_nul_terminate(buf); 207 208 buf->last_field = buf->pos; 209 buf->cur_tabstop = 0; 210 } 211 212 /* 213 * Returns spaces from start of line, if set, or 0 if unset: 214 */ 215 static inline unsigned cur_tabstop(struct printbuf *buf) 216 { 217 return buf->cur_tabstop < buf->nr_tabstops 218 ? buf->_tabstops[buf->cur_tabstop] 219 : 0; 220 } 221 222 static void __prt_tab(struct printbuf *out) 223 { 224 int spaces = max_t(int, 0, cur_tabstop(out) - printbuf_linelen(out)); 225 226 prt_chars(out, ' ', spaces); 227 228 out->last_field = out->pos; 229 out->cur_tabstop++; 230 } 231 232 /** 233 * bch2_prt_tab() - Advance printbuf to the next tabstop 234 * @out: printbuf to control 235 * 236 * Advance output to the next tabstop by printing spaces. 237 */ 238 void bch2_prt_tab(struct printbuf *out) 239 { 240 if (WARN_ON(!cur_tabstop(out))) 241 return; 242 243 __prt_tab(out); 244 } 245 246 static void __prt_tab_rjust(struct printbuf *buf) 247 { 248 unsigned move = buf->pos - buf->last_field; 249 int pad = (int) cur_tabstop(buf) - (int) printbuf_linelen(buf); 250 251 if (pad > 0) { 252 bch2_printbuf_make_room(buf, pad); 253 254 if (buf->last_field + pad < buf->size) 255 memmove(buf->buf + buf->last_field + pad, 256 buf->buf + buf->last_field, 257 min(move, buf->size - 1 - buf->last_field - pad)); 258 259 if (buf->last_field < buf->size) 260 memset(buf->buf + buf->last_field, ' ', 261 min((unsigned) pad, buf->size - buf->last_field)); 262 263 buf->pos += pad; 264 printbuf_nul_terminate(buf); 265 } 266 267 buf->last_field = buf->pos; 268 buf->cur_tabstop++; 269 } 270 271 /** 272 * bch2_prt_tab_rjust - Advance printbuf to the next tabstop, right justifying 273 * previous output 274 * 275 * @buf: printbuf to control 276 * 277 * Advance output to the next tabstop by inserting spaces immediately after the 278 * previous tabstop, right justifying previously outputted text. 279 */ 280 void bch2_prt_tab_rjust(struct printbuf *buf) 281 { 282 if (WARN_ON(!cur_tabstop(buf))) 283 return; 284 285 __prt_tab_rjust(buf); 286 } 287 288 /** 289 * bch2_prt_bytes_indented() - Print an array of chars, handling embedded control characters 290 * 291 * @out: output printbuf 292 * @str: string to print 293 * @count: number of bytes to print 294 * 295 * The following contol characters are handled as so: 296 * \n: prt_newline newline that obeys current indent level 297 * \t: prt_tab advance to next tabstop 298 * \r: prt_tab_rjust advance to next tabstop, with right justification 299 */ 300 void bch2_prt_bytes_indented(struct printbuf *out, const char *str, unsigned count) 301 { 302 const char *unprinted_start = str; 303 const char *end = str + count; 304 305 if (!out->has_indent_or_tabstops || out->suppress_indent_tabstop_handling) { 306 prt_bytes(out, str, count); 307 return; 308 } 309 310 while (str != end) { 311 switch (*str) { 312 case '\n': 313 prt_bytes(out, unprinted_start, str - unprinted_start); 314 unprinted_start = str + 1; 315 bch2_prt_newline(out); 316 break; 317 case '\t': 318 if (likely(cur_tabstop(out))) { 319 prt_bytes(out, unprinted_start, str - unprinted_start); 320 unprinted_start = str + 1; 321 __prt_tab(out); 322 } 323 break; 324 case '\r': 325 if (likely(cur_tabstop(out))) { 326 prt_bytes(out, unprinted_start, str - unprinted_start); 327 unprinted_start = str + 1; 328 __prt_tab_rjust(out); 329 } 330 break; 331 } 332 333 str++; 334 } 335 336 prt_bytes(out, unprinted_start, str - unprinted_start); 337 } 338 339 /** 340 * bch2_prt_human_readable_u64() - Print out a u64 in human readable units 341 * @out: output printbuf 342 * @v: integer to print 343 * 344 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units 345 */ 346 void bch2_prt_human_readable_u64(struct printbuf *out, u64 v) 347 { 348 bch2_printbuf_make_room(out, 10); 349 out->pos += string_get_size(v, 1, !out->si_units, 350 out->buf + out->pos, 351 printbuf_remaining_size(out)); 352 } 353 354 /** 355 * bch2_prt_human_readable_s64() - Print out a s64 in human readable units 356 * @out: output printbuf 357 * @v: integer to print 358 * 359 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units 360 */ 361 void bch2_prt_human_readable_s64(struct printbuf *out, s64 v) 362 { 363 if (v < 0) 364 prt_char(out, '-'); 365 bch2_prt_human_readable_u64(out, abs(v)); 366 } 367 368 /** 369 * bch2_prt_units_u64() - Print out a u64 according to printbuf unit options 370 * @out: output printbuf 371 * @v: integer to print 372 * 373 * Units are either raw (default), or human reabable units (controlled via 374 * @buf->human_readable_units) 375 */ 376 void bch2_prt_units_u64(struct printbuf *out, u64 v) 377 { 378 if (out->human_readable_units) 379 bch2_prt_human_readable_u64(out, v); 380 else 381 bch2_prt_printf(out, "%llu", v); 382 } 383 384 /** 385 * bch2_prt_units_s64() - Print out a s64 according to printbuf unit options 386 * @out: output printbuf 387 * @v: integer to print 388 * 389 * Units are either raw (default), or human reabable units (controlled via 390 * @buf->human_readable_units) 391 */ 392 void bch2_prt_units_s64(struct printbuf *out, s64 v) 393 { 394 if (v < 0) 395 prt_char(out, '-'); 396 bch2_prt_units_u64(out, abs(v)); 397 } 398 399 void bch2_prt_string_option(struct printbuf *out, 400 const char * const list[], 401 size_t selected) 402 { 403 size_t i; 404 405 for (i = 0; list[i]; i++) 406 bch2_prt_printf(out, i == selected ? "[%s] " : "%s ", list[i]); 407 } 408 409 void bch2_prt_bitflags(struct printbuf *out, 410 const char * const list[], u64 flags) 411 { 412 unsigned bit, nr = 0; 413 bool first = true; 414 415 while (list[nr]) 416 nr++; 417 418 while (flags && (bit = __ffs64(flags)) < nr) { 419 if (!first) 420 bch2_prt_printf(out, ","); 421 first = false; 422 bch2_prt_printf(out, "%s", list[bit]); 423 flags ^= BIT_ULL(bit); 424 } 425 } 426