510 lines · c
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 13static inline unsigned __printbuf_linelen(struct printbuf *buf, unsigned pos)14{15 return pos - buf->last_newline;16}17 18static 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 */26static inline unsigned cur_tabstop(struct printbuf *buf)27{28 return buf->cur_tabstop < buf->nr_tabstops29 ? buf->_tabstops[buf->cur_tabstop]30 : 0;31}32 33int 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 /* Sanity check... */49 if (new_size > PAGE_SIZE << MAX_PAGE_ORDER) {50 out->allocation_failure = true;51 out->overflow = true;52 return -ENOMEM;53 }54 55 /*56 * Note: output buffer must be freeable with kfree(), it's not required57 * that the user use printbuf_exit().58 */59 char *buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT);60 61 if (!buf) {62 out->allocation_failure = true;63 out->overflow = true;64 return -ENOMEM;65 }66 67 out->buf = buf;68 out->size = new_size;69 return 0;70}71 72static void printbuf_advance_pos(struct printbuf *out, unsigned len)73{74 out->pos += min(len, printbuf_remaining(out));75}76 77static void printbuf_insert_spaces(struct printbuf *out, unsigned pos, unsigned nr)78{79 unsigned move = out->pos - pos;80 81 bch2_printbuf_make_room(out, nr);82 83 if (pos + nr < out->size)84 memmove(out->buf + pos + nr,85 out->buf + pos,86 min(move, out->size - 1 - pos - nr));87 88 if (pos < out->size)89 memset(out->buf + pos, ' ', min(nr, out->size - pos));90 91 printbuf_advance_pos(out, nr);92 printbuf_nul_terminate_reserved(out);93}94 95static void __printbuf_do_indent(struct printbuf *out, unsigned pos)96{97 while (true) {98 int pad;99 unsigned len = out->pos - pos;100 char *p = out->buf + pos;101 char *n = memscan(p, '\n', len);102 if (cur_tabstop(out)) {103 n = min(n, (char *) memscan(p, '\r', len));104 n = min(n, (char *) memscan(p, '\t', len));105 }106 107 pos = n - out->buf;108 if (pos == out->pos)109 break;110 111 switch (*n) {112 case '\n':113 pos++;114 out->last_newline = pos;115 116 printbuf_insert_spaces(out, pos, out->indent);117 118 pos = min(pos + out->indent, out->pos);119 out->last_field = pos;120 out->cur_tabstop = 0;121 break;122 case '\r':123 memmove(n, n + 1, out->pos - pos);124 --out->pos;125 pad = (int) cur_tabstop(out) - (int) __printbuf_linelen(out, pos);126 if (pad > 0) {127 printbuf_insert_spaces(out, out->last_field, pad);128 pos += pad;129 }130 131 out->last_field = pos;132 out->cur_tabstop++;133 break;134 case '\t':135 pad = (int) cur_tabstop(out) - (int) __printbuf_linelen(out, pos) - 1;136 if (pad > 0) {137 *n = ' ';138 printbuf_insert_spaces(out, pos, pad - 1);139 pos += pad;140 } else {141 memmove(n, n + 1, out->pos - pos);142 --out->pos;143 }144 145 out->last_field = pos;146 out->cur_tabstop++;147 break;148 }149 }150}151 152static inline void printbuf_do_indent(struct printbuf *out, unsigned pos)153{154 if (out->has_indent_or_tabstops && !out->suppress_indent_tabstop_handling)155 __printbuf_do_indent(out, pos);156}157 158void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list args)159{160 int len;161 162 do {163 va_list args2;164 165 va_copy(args2, args);166 len = vsnprintf(out->buf + out->pos, printbuf_remaining_size(out), fmt, args2);167 va_end(args2);168 } while (len > printbuf_remaining(out) &&169 !bch2_printbuf_make_room(out, len));170 171 unsigned indent_pos = out->pos;172 printbuf_advance_pos(out, len);173 printbuf_do_indent(out, indent_pos);174}175 176void bch2_prt_printf(struct printbuf *out, const char *fmt, ...)177{178 va_list args;179 int len;180 181 do {182 va_start(args, fmt);183 len = vsnprintf(out->buf + out->pos, printbuf_remaining_size(out), fmt, args);184 va_end(args);185 } while (len > printbuf_remaining(out) &&186 !bch2_printbuf_make_room(out, len));187 188 unsigned indent_pos = out->pos;189 printbuf_advance_pos(out, len);190 printbuf_do_indent(out, indent_pos);191}192 193/**194 * bch2_printbuf_str() - returns printbuf's buf as a C string, guaranteed to be195 * null terminated196 * @buf: printbuf to terminate197 * Returns: Printbuf contents, as a nul terminated C string198 */199const char *bch2_printbuf_str(const struct printbuf *buf)200{201 /*202 * If we've written to a printbuf then it's guaranteed to be a null203 * terminated string - but if we haven't, then we might not have204 * allocated a buffer at all:205 */206 return buf->pos207 ? buf->buf208 : "";209}210 211/**212 * bch2_printbuf_exit() - exit a printbuf, freeing memory it owns and poisoning it213 * against accidental use.214 * @buf: printbuf to exit215 */216void bch2_printbuf_exit(struct printbuf *buf)217{218 if (buf->heap_allocated) {219 kfree(buf->buf);220 buf->buf = ERR_PTR(-EINTR); /* poison value */221 }222}223 224void bch2_printbuf_tabstops_reset(struct printbuf *buf)225{226 buf->nr_tabstops = 0;227}228 229void bch2_printbuf_tabstop_pop(struct printbuf *buf)230{231 if (buf->nr_tabstops)232 --buf->nr_tabstops;233}234 235/*236 * bch2_printbuf_tabstop_set() - add a tabstop, n spaces from the previous tabstop237 *238 * @buf: printbuf to control239 * @spaces: number of spaces from previous tabpstop240 *241 * In the future this function may allocate memory if setting more than242 * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start243 * of line.244 */245int bch2_printbuf_tabstop_push(struct printbuf *buf, unsigned spaces)246{247 unsigned prev_tabstop = buf->nr_tabstops248 ? buf->_tabstops[buf->nr_tabstops - 1]249 : 0;250 251 if (WARN_ON(buf->nr_tabstops >= ARRAY_SIZE(buf->_tabstops)))252 return -EINVAL;253 254 buf->_tabstops[buf->nr_tabstops++] = prev_tabstop + spaces;255 buf->has_indent_or_tabstops = true;256 return 0;257}258 259/**260 * bch2_printbuf_indent_add() - add to the current indent level261 *262 * @buf: printbuf to control263 * @spaces: number of spaces to add to the current indent level264 *265 * Subsequent lines, and the current line if the output position is at the start266 * of the current line, will be indented by @spaces more spaces.267 */268void bch2_printbuf_indent_add(struct printbuf *buf, unsigned spaces)269{270 if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))271 spaces = 0;272 273 buf->indent += spaces;274 prt_chars(buf, ' ', spaces);275 276 buf->has_indent_or_tabstops = true;277}278 279/**280 * bch2_printbuf_indent_sub() - subtract from the current indent level281 *282 * @buf: printbuf to control283 * @spaces: number of spaces to subtract from the current indent level284 *285 * Subsequent lines, and the current line if the output position is at the start286 * of the current line, will be indented by @spaces less spaces.287 */288void bch2_printbuf_indent_sub(struct printbuf *buf, unsigned spaces)289{290 if (WARN_ON_ONCE(spaces > buf->indent))291 spaces = buf->indent;292 293 if (buf->last_newline + buf->indent == buf->pos) {294 buf->pos -= spaces;295 printbuf_nul_terminate(buf);296 }297 buf->indent -= spaces;298 299 if (!buf->indent && !buf->nr_tabstops)300 buf->has_indent_or_tabstops = false;301}302 303void bch2_prt_newline(struct printbuf *buf)304{305 bch2_printbuf_make_room(buf, 1 + buf->indent);306 307 __prt_char_reserved(buf, '\n');308 309 buf->last_newline = buf->pos;310 311 __prt_chars_reserved(buf, ' ', buf->indent);312 313 printbuf_nul_terminate_reserved(buf);314 315 buf->last_field = buf->pos;316 buf->cur_tabstop = 0;317}318 319void bch2_printbuf_strip_trailing_newline(struct printbuf *out)320{321 for (int p = out->pos - 1; p >= 0; --p) {322 if (out->buf[p] == '\n') {323 out->pos = p;324 break;325 }326 if (out->buf[p] != ' ')327 break;328 }329 330 printbuf_nul_terminate_reserved(out);331}332 333static void __prt_tab(struct printbuf *out)334{335 int spaces = max_t(int, 0, cur_tabstop(out) - printbuf_linelen(out));336 337 prt_chars(out, ' ', spaces);338 339 out->last_field = out->pos;340 out->cur_tabstop++;341}342 343/**344 * bch2_prt_tab() - Advance printbuf to the next tabstop345 * @out: printbuf to control346 *347 * Advance output to the next tabstop by printing spaces.348 */349void bch2_prt_tab(struct printbuf *out)350{351 if (WARN_ON(!cur_tabstop(out)))352 return;353 354 __prt_tab(out);355}356 357static void __prt_tab_rjust(struct printbuf *buf)358{359 int pad = (int) cur_tabstop(buf) - (int) printbuf_linelen(buf);360 if (pad > 0)361 printbuf_insert_spaces(buf, buf->last_field, pad);362 363 buf->last_field = buf->pos;364 buf->cur_tabstop++;365}366 367/**368 * bch2_prt_tab_rjust - Advance printbuf to the next tabstop, right justifying369 * previous output370 *371 * @buf: printbuf to control372 *373 * Advance output to the next tabstop by inserting spaces immediately after the374 * previous tabstop, right justifying previously outputted text.375 */376void bch2_prt_tab_rjust(struct printbuf *buf)377{378 if (WARN_ON(!cur_tabstop(buf)))379 return;380 381 __prt_tab_rjust(buf);382}383 384/**385 * bch2_prt_bytes_indented() - Print an array of chars, handling embedded control characters386 *387 * @out: output printbuf388 * @str: string to print389 * @count: number of bytes to print390 *391 * The following contol characters are handled as so:392 * \n: prt_newline newline that obeys current indent level393 * \t: prt_tab advance to next tabstop394 * \r: prt_tab_rjust advance to next tabstop, with right justification395 */396void bch2_prt_bytes_indented(struct printbuf *out, const char *str, unsigned count)397{398 unsigned indent_pos = out->pos;399 prt_bytes(out, str, count);400 printbuf_do_indent(out, indent_pos);401}402 403/**404 * bch2_prt_human_readable_u64() - Print out a u64 in human readable units405 * @out: output printbuf406 * @v: integer to print407 *408 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units409 */410void bch2_prt_human_readable_u64(struct printbuf *out, u64 v)411{412 bch2_printbuf_make_room(out, 10);413 unsigned len = string_get_size(v, 1, !out->si_units,414 out->buf + out->pos,415 printbuf_remaining_size(out));416 printbuf_advance_pos(out, len);417}418 419/**420 * bch2_prt_human_readable_s64() - Print out a s64 in human readable units421 * @out: output printbuf422 * @v: integer to print423 *424 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units425 */426void bch2_prt_human_readable_s64(struct printbuf *out, s64 v)427{428 if (v < 0)429 prt_char(out, '-');430 bch2_prt_human_readable_u64(out, abs(v));431}432 433/**434 * bch2_prt_units_u64() - Print out a u64 according to printbuf unit options435 * @out: output printbuf436 * @v: integer to print437 *438 * Units are either raw (default), or human reabable units (controlled via439 * @buf->human_readable_units)440 */441void bch2_prt_units_u64(struct printbuf *out, u64 v)442{443 if (out->human_readable_units)444 bch2_prt_human_readable_u64(out, v);445 else446 bch2_prt_printf(out, "%llu", v);447}448 449/**450 * bch2_prt_units_s64() - Print out a s64 according to printbuf unit options451 * @out: output printbuf452 * @v: integer to print453 *454 * Units are either raw (default), or human reabable units (controlled via455 * @buf->human_readable_units)456 */457void bch2_prt_units_s64(struct printbuf *out, s64 v)458{459 if (v < 0)460 prt_char(out, '-');461 bch2_prt_units_u64(out, abs(v));462}463 464void bch2_prt_string_option(struct printbuf *out,465 const char * const list[],466 size_t selected)467{468 for (size_t i = 0; list[i]; i++)469 bch2_prt_printf(out, i == selected ? "[%s] " : "%s ", list[i]);470}471 472void bch2_prt_bitflags(struct printbuf *out,473 const char * const list[], u64 flags)474{475 unsigned bit, nr = 0;476 bool first = true;477 478 while (list[nr])479 nr++;480 481 while (flags && (bit = __ffs64(flags)) < nr) {482 if (!first)483 bch2_prt_printf(out, ",");484 first = false;485 bch2_prt_printf(out, "%s", list[bit]);486 flags ^= BIT_ULL(bit);487 }488}489 490void bch2_prt_bitflags_vector(struct printbuf *out,491 const char * const list[],492 unsigned long *v, unsigned nr)493{494 bool first = true;495 unsigned i;496 497 for (i = 0; i < nr; i++)498 if (!list[i]) {499 nr = i - 1;500 break;501 }502 503 for_each_set_bit(i, v, nr) {504 if (!first)505 bch2_prt_printf(out, ",");506 first = false;507 bch2_prt_printf(out, "%s", list[i]);508 }509}510