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