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