summaryrefslogtreecommitdiff
path: root/lib/vsprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vsprintf.c')
-rw-r--r--lib/vsprintf.c1367
1 files changed, 634 insertions, 733 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3c1853a9d1c0..52dac8519a0a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -44,6 +44,7 @@
#ifdef CONFIG_BLOCK
#include <linux/blkdev.h>
#endif
+#include <linux/printbuf.h>
#include "../mm/internal.h" /* For the trace_print_flags arrays */
@@ -451,8 +452,8 @@ static_assert(sizeof(struct printf_spec) == 8);
#define PRECISION_MAX ((1 << 15) - 1)
static noinline_for_stack
-char *number(char *buf, char *end, unsigned long long num,
- struct printf_spec spec)
+void number(struct printbuf *out, unsigned long long num,
+ struct printf_spec spec)
{
/* put_dec requires 2-byte alignment of the buffer. */
char tmp[3 * sizeof(num)] __aligned(2);
@@ -512,67 +513,43 @@ char *number(char *buf, char *end, unsigned long long num,
if (i > precision)
precision = i;
/* leading space padding */
- field_width -= precision;
- if (!(spec.flags & (ZEROPAD | LEFT))) {
- while (--field_width >= 0) {
- if (buf < end)
- *buf = ' ';
- ++buf;
- }
+ field_width = max(0, field_width - precision);
+ if (!(spec.flags & (ZEROPAD | LEFT)) && field_width) {
+ __prt_chars(out, ' ', field_width);
+ field_width = 0;
}
/* sign */
- if (sign) {
- if (buf < end)
- *buf = sign;
- ++buf;
- }
+ if (sign)
+ __prt_char(out, sign);
/* "0x" / "0" prefix */
if (need_pfx) {
- if (spec.base == 16 || !is_zero) {
- if (buf < end)
- *buf = '0';
- ++buf;
- }
- if (spec.base == 16) {
- if (buf < end)
- *buf = ('X' | locase);
- ++buf;
- }
+ if (spec.base == 16 || !is_zero)
+ __prt_char(out, '0');
+ if (spec.base == 16)
+ __prt_char(out, 'X' | locase);
}
/* zero or space padding */
- if (!(spec.flags & LEFT)) {
+ if (!(spec.flags & LEFT) && field_width) {
char c = ' ' + (spec.flags & ZEROPAD);
- while (--field_width >= 0) {
- if (buf < end)
- *buf = c;
- ++buf;
- }
+ __prt_chars(out, c, field_width);
+ field_width = 0;
}
/* hmm even more zero padding? */
- while (i <= --precision) {
- if (buf < end)
- *buf = '0';
- ++buf;
- }
+ if (precision > i)
+ __prt_chars(out, '0', precision - i);
/* actual digits of result */
- while (--i >= 0) {
- if (buf < end)
- *buf = tmp[i];
- ++buf;
- }
+ while (--i >= 0)
+ __prt_char(out, tmp[i]);
/* trailing space padding */
- while (--field_width >= 0) {
- if (buf < end)
- *buf = ' ';
- ++buf;
- }
+ if (field_width)
+ __prt_chars(out, ' ', field_width);
- return buf;
+ printbuf_nul_terminate(out);
}
static noinline_for_stack
-char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
+void special_hex_number(struct printbuf *out, unsigned long long num, int size)
{
struct printf_spec spec;
@@ -582,25 +559,28 @@ char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
spec.base = 16;
spec.precision = -1;
- return number(buf, end, num, spec);
+ number(out, num, spec);
}
-static void move_right(char *buf, char *end, unsigned len, unsigned spaces)
+/*
+ * inserts @spaces spaces @len from the end of @out
+ */
+static void move_right(struct printbuf *out,
+ unsigned len, unsigned spaces)
{
- size_t size;
- if (buf >= end) /* nowhere to put anything */
- return;
- size = end - buf;
- if (size <= spaces) {
- memset(buf, ' ', size);
- return;
- }
- if (len) {
- if (len > size - spaces)
- len = size - spaces;
- memmove(buf + spaces, buf, len);
- }
- memset(buf, ' ', spaces);
+ unsigned move_src = out->pos - len;
+ unsigned move_dst = move_src + spaces;
+ unsigned remaining_from_dst = move_dst < out->size ? out->size - move_dst : 0;
+ unsigned remaining_from_src = move_src < out->size ? out->size - move_src : 0;
+
+ BUG_ON(len > out->pos);
+
+ memmove(out->buf + move_dst,
+ out->buf + move_src,
+ min(remaining_from_dst, len));
+ memset(out->buf + move_src, ' ',
+ min(remaining_from_src, spaces));
+ out->pos += spaces;
}
/*
@@ -612,67 +592,55 @@ static void move_right(char *buf, char *end, unsigned len, unsigned spaces)
* Returns: new buffer position after padding.
*/
static noinline_for_stack
-char *widen_string(char *buf, int n, char *end, struct printf_spec spec)
+void widen_string(struct printbuf *out, int n,
+ struct printf_spec spec)
{
unsigned spaces;
if (likely(n >= spec.field_width))
- return buf;
+ return;
/* we want to pad the sucker */
spaces = spec.field_width - n;
- if (!(spec.flags & LEFT)) {
- move_right(buf - n, end, n, spaces);
- return buf + spaces;
- }
- while (spaces--) {
- if (buf < end)
- *buf = ' ';
- ++buf;
- }
- return buf;
+ if (!(spec.flags & LEFT))
+ move_right(out, n, spaces);
+ else
+ prt_chars(out, ' ', spaces);
}
/* Handle string from a well known address. */
-static char *string_nocheck(char *buf, char *end, const char *s,
- struct printf_spec spec)
+static void string_nocheck(struct printbuf *out,
+ const char *s,
+ struct printf_spec spec)
{
- int len = 0;
- int lim = spec.precision;
+ int len = strnlen(s, spec.precision);
- while (lim--) {
- char c = *s++;
- if (!c)
- break;
- if (buf < end)
- *buf = c;
- ++buf;
- ++len;
- }
- return widen_string(buf, len, end, spec);
+ prt_bytes(out, s, len);
+ widen_string(out, len, spec);
}
-static char *err_ptr(char *buf, char *end, void *ptr,
- struct printf_spec spec)
+static void err_ptr(struct printbuf *out, void *ptr,
+ struct printf_spec spec)
{
int err = PTR_ERR(ptr);
const char *sym = errname(err);
- if (sym)
- return string_nocheck(buf, end, sym, spec);
-
- /*
- * Somebody passed ERR_PTR(-1234) or some other non-existing
- * Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to
- * printing it as its decimal representation.
- */
- spec.flags |= SIGN;
- spec.base = 10;
- return number(buf, end, err, spec);
+ if (sym) {
+ string_nocheck(out, sym, spec);
+ } else {
+ /*
+ * Somebody passed ERR_PTR(-1234) or some other non-existing
+ * Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to
+ * printing it as its decimal representation.
+ */
+ spec.flags |= SIGN;
+ spec.base = 10;
+ number(out, err, spec);
+ }
}
/* Be careful: error messages must fit into the given buffer. */
-static char *error_string(char *buf, char *end, const char *s,
- struct printf_spec spec)
+static void error_string(struct printbuf *out, const char *s,
+ struct printf_spec spec)
{
/*
* Hard limit to avoid a completely insane messages. It actually
@@ -682,7 +650,7 @@ static char *error_string(char *buf, char *end, const char *s,
if (spec.precision == -1)
spec.precision = 2 * sizeof(void *);
- return string_nocheck(buf, end, s, spec);
+ string_nocheck(out, s, spec);
}
/*
@@ -701,14 +669,15 @@ static const char *check_pointer_msg(const void *ptr)
return NULL;
}
-static int check_pointer(char **buf, char *end, const void *ptr,
+static int check_pointer(struct printbuf *out,
+ const void *ptr,
struct printf_spec spec)
{
const char *err_msg;
err_msg = check_pointer_msg(ptr);
if (err_msg) {
- *buf = error_string(*buf, end, err_msg, spec);
+ error_string(out, err_msg, spec);
return -EFAULT;
}
@@ -716,18 +685,19 @@ static int check_pointer(char **buf, char *end, const void *ptr,
}
static noinline_for_stack
-char *string(char *buf, char *end, const char *s,
- struct printf_spec spec)
+void string(struct printbuf *out,
+ const char *s,
+ struct printf_spec spec)
{
- if (check_pointer(&buf, end, s, spec))
- return buf;
+ if (check_pointer(out, s, spec))
+ return;
- return string_nocheck(buf, end, s, spec);
+ string_nocheck(out, s, spec);
}
-static char *pointer_string(char *buf, char *end,
- const void *ptr,
- struct printf_spec spec)
+static void pointer_string(struct printbuf *out,
+ const void *ptr,
+ struct printf_spec spec)
{
spec.base = 16;
spec.flags |= SMALL;
@@ -736,7 +706,7 @@ static char *pointer_string(char *buf, char *end,
spec.flags |= ZEROPAD;
}
- return number(buf, end, (unsigned long int)ptr, spec);
+ number(out, (unsigned long int)ptr, spec);
}
/* Make pointers available for printing early in the boot sequence. */
@@ -801,8 +771,9 @@ int ptr_to_hashval(const void *ptr, unsigned long *hashval_out)
return __ptr_to_hashval(ptr, hashval_out);
}
-static char *ptr_to_id(char *buf, char *end, const void *ptr,
- struct printf_spec spec)
+static void ptr_to_id(struct printbuf *out,
+ const void *ptr,
+ struct printf_spec spec)
{
const char *str = sizeof(ptr) == 8 ? "(____ptrval____)" : "(ptrval)";
unsigned long hashval;
@@ -813,47 +784,49 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr,
* as they are not actual addresses.
*/
if (IS_ERR_OR_NULL(ptr))
- return pointer_string(buf, end, ptr, spec);
+ return pointer_string(out, ptr, spec);
/* When debugging early boot use non-cryptographically secure hash. */
if (unlikely(debug_boot_weak_hash)) {
hashval = hash_long((unsigned long)ptr, 32);
- return pointer_string(buf, end, (const void *)hashval, spec);
+ return pointer_string(out, (const void *)hashval, spec);
}
ret = __ptr_to_hashval(ptr, &hashval);
if (ret) {
spec.field_width = 2 * sizeof(ptr);
/* string length must be less than default_width */
- return error_string(buf, end, str, spec);
+ return error_string(out, str, spec);
}
- return pointer_string(buf, end, (const void *)hashval, spec);
+ pointer_string(out, (const void *)hashval, spec);
}
-static char *default_pointer(char *buf, char *end, const void *ptr,
- struct printf_spec spec)
+static void default_pointer(struct printbuf *out,
+ const void *ptr,
+ struct printf_spec spec)
{
/*
* default is to _not_ leak addresses, so hash before printing,
* unless no_hash_pointers is specified on the command line.
*/
if (unlikely(no_hash_pointers))
- return pointer_string(buf, end, ptr, spec);
+ return pointer_string(out, ptr, spec);
- return ptr_to_id(buf, end, ptr, spec);
+ return ptr_to_id(out, ptr, spec);
}
int kptr_restrict __read_mostly;
static noinline_for_stack
-char *restricted_pointer(char *buf, char *end, const void *ptr,
- struct printf_spec spec)
+void restricted_pointer(struct printbuf *out,
+ const void *ptr,
+ struct printf_spec spec)
{
switch (kptr_restrict) {
case 0:
/* Handle as %p, hash and do _not_ leak addresses. */
- return default_pointer(buf, end, ptr, spec);
+ return default_pointer(out, ptr, spec);
case 1: {
const struct cred *cred;
@@ -864,7 +837,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
if (in_irq() || in_serving_softirq() || in_nmi()) {
if (spec.field_width == -1)
spec.field_width = 2 * sizeof(ptr);
- return error_string(buf, end, "pK-error", spec);
+ return error_string(out, "pK-error", spec);
}
/*
@@ -890,12 +863,13 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
break;
}
- return pointer_string(buf, end, ptr, spec);
+ return pointer_string(out, ptr, spec);
}
static noinline_for_stack
-char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_spec spec,
- const char *fmt)
+void dentry_name(struct printbuf *out,
+ const struct dentry *d, struct printf_spec spec,
+ const char *fmt)
{
const char *array[4], *s;
const struct dentry *p;
@@ -912,9 +886,9 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
rcu_read_lock();
for (i = 0; i < depth; i++, d = p) {
- if (check_pointer(&buf, end, d, spec)) {
+ if (check_pointer(out, d, spec)) {
rcu_read_unlock();
- return buf;
+ return;
}
p = READ_ONCE(d->d_parent);
@@ -927,7 +901,7 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
}
}
s = array[--i];
- for (n = 0; n != spec.precision; n++, buf++) {
+ for (n = 0; n != spec.precision; n++) {
char c = *s++;
if (!c) {
if (!i)
@@ -935,49 +909,47 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
c = '/';
s = array[--i];
}
- if (buf < end)
- *buf = c;
+ prt_char(out, c);
}
rcu_read_unlock();
- return widen_string(buf, n, end, spec);
+
+ widen_string(out, n, spec);
}
static noinline_for_stack
-char *file_dentry_name(char *buf, char *end, const struct file *f,
- struct printf_spec spec, const char *fmt)
+void file_dentry_name(struct printbuf *out,
+ const struct file *f,
+ struct printf_spec spec, const char *fmt)
{
- if (check_pointer(&buf, end, f, spec))
- return buf;
+ if (check_pointer(out, f, spec))
+ return;
- return dentry_name(buf, end, f->f_path.dentry, spec, fmt);
+ return dentry_name(out, f->f_path.dentry, spec, fmt);
}
#ifdef CONFIG_BLOCK
static noinline_for_stack
-char *bdev_name(char *buf, char *end, struct block_device *bdev,
- struct printf_spec spec, const char *fmt)
+void bdev_name(struct printbuf *out,
+ struct block_device *bdev,
+ struct printf_spec spec, const char *fmt)
{
struct gendisk *hd;
- if (check_pointer(&buf, end, bdev, spec))
- return buf;
+ if (check_pointer(out, bdev, spec))
+ return;
hd = bdev->bd_disk;
- buf = string(buf, end, hd->disk_name, spec);
+ string(out, hd->disk_name, spec);
if (bdev->bd_partno) {
- if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) {
- if (buf < end)
- *buf = 'p';
- buf++;
- }
- buf = number(buf, end, bdev->bd_partno, spec);
+ if (isdigit(hd->disk_name[strlen(hd->disk_name)-1]))
+ prt_char(out, 'p');
+ number(out, bdev->bd_partno, spec);
}
- return buf;
}
#endif
static noinline_for_stack
-char *symbol_string(char *buf, char *end, void *ptr,
- struct printf_spec spec, const char *fmt)
+void symbol_string(struct printbuf *out, void *ptr,
+ struct printf_spec spec, const char *fmt)
{
unsigned long value;
#ifdef CONFIG_KALLSYMS
@@ -1000,9 +972,9 @@ char *symbol_string(char *buf, char *end, void *ptr,
else
sprint_symbol_no_offset(sym, value);
- return string_nocheck(buf, end, sym, spec);
+ string_nocheck(out, sym, spec);
#else
- return special_hex_number(buf, end, value, sizeof(void *));
+ special_hex_number(out, value, sizeof(void *));
#endif
}
@@ -1037,8 +1009,8 @@ static const struct printf_spec default_dec04_spec = {
};
static noinline_for_stack
-char *resource_string(char *buf, char *end, struct resource *res,
- struct printf_spec spec, const char *fmt)
+void resource_string(struct printbuf *out, struct resource *res,
+ struct printf_spec spec, const char *fmt)
{
#ifndef IO_RSRC_PRINTK_SIZE
#define IO_RSRC_PRINTK_SIZE 6
@@ -1077,69 +1049,67 @@ char *resource_string(char *buf, char *end, struct resource *res,
#define FLAG_BUF_SIZE (2 * sizeof(res->flags))
#define DECODED_BUF_SIZE sizeof("[mem - 64bit pref window disabled]")
#define RAW_BUF_SIZE sizeof("[mem - flags 0x]")
- char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
+ char sym_buf[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
-
- char *p = sym, *pend = sym + sizeof(sym);
+ struct printbuf sym = PRINTBUF_EXTERN(sym_buf, sizeof(sym_buf));
int decode = (fmt[0] == 'R') ? 1 : 0;
const struct printf_spec *specp;
- if (check_pointer(&buf, end, res, spec))
- return buf;
+ if (check_pointer(out, res, spec))
+ return;
- *p++ = '[';
+ prt_char(&sym, '[');
if (res->flags & IORESOURCE_IO) {
- p = string_nocheck(p, pend, "io ", str_spec);
+ string_nocheck(&sym, "io ", str_spec);
specp = &io_spec;
} else if (res->flags & IORESOURCE_MEM) {
- p = string_nocheck(p, pend, "mem ", str_spec);
+ string_nocheck(&sym, "mem ", str_spec);
specp = &mem_spec;
} else if (res->flags & IORESOURCE_IRQ) {
- p = string_nocheck(p, pend, "irq ", str_spec);
+ string_nocheck(&sym, "irq ", str_spec);
specp = &default_dec_spec;
} else if (res->flags & IORESOURCE_DMA) {
- p = string_nocheck(p, pend, "dma ", str_spec);
+ string_nocheck(&sym, "dma ", str_spec);
specp = &default_dec_spec;
} else if (res->flags & IORESOURCE_BUS) {
- p = string_nocheck(p, pend, "bus ", str_spec);
+ string_nocheck(&sym, "bus ", str_spec);
specp = &bus_spec;
} else {
- p = string_nocheck(p, pend, "??? ", str_spec);
+ string_nocheck(&sym, "??? ", str_spec);
specp = &mem_spec;
decode = 0;
}
if (decode && res->flags & IORESOURCE_UNSET) {
- p = string_nocheck(p, pend, "size ", str_spec);
- p = number(p, pend, resource_size(res), *specp);
+ string_nocheck(&sym, "size ", str_spec);
+ number(&sym, resource_size(res), *specp);
} else {
- p = number(p, pend, res->start, *specp);
+ number(&sym, res->start, *specp);
if (res->start != res->end) {
- *p++ = '-';
- p = number(p, pend, res->end, *specp);
+ prt_char(&sym, '-');
+ number(&sym, res->end, *specp);
}
}
if (decode) {
if (res->flags & IORESOURCE_MEM_64)
- p = string_nocheck(p, pend, " 64bit", str_spec);
+ string_nocheck(&sym, " 64bit", str_spec);
if (res->flags & IORESOURCE_PREFETCH)
- p = string_nocheck(p, pend, " pref", str_spec);
+ string_nocheck(&sym, " pref", str_spec);
if (res->flags & IORESOURCE_WINDOW)
- p = string_nocheck(p, pend, " window", str_spec);
+ string_nocheck(&sym, " window", str_spec);
if (res->flags & IORESOURCE_DISABLED)
- p = string_nocheck(p, pend, " disabled", str_spec);
+ string_nocheck(&sym, " disabled", str_spec);
} else {
- p = string_nocheck(p, pend, " flags ", str_spec);
- p = number(p, pend, res->flags, default_flag_spec);
+ string_nocheck(&sym, " flags ", str_spec);
+ number(&sym, res->flags, default_flag_spec);
}
- *p++ = ']';
- *p = '\0';
+ prt_char(&sym, ']');
- return string_nocheck(buf, end, sym, spec);
+ string_nocheck(out, sym_buf, spec);
}
static noinline_for_stack
-char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
- const char *fmt)
+void hex_string(struct printbuf *out, u8 *addr,
+ struct printf_spec spec, const char *fmt)
{
int i, len = 1; /* if we pass '%ph[CDN]', field width remains
negative value, fallback to the default */
@@ -1147,10 +1117,10 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
if (spec.field_width == 0)
/* nothing to print */
- return buf;
+ return;
- if (check_pointer(&buf, end, addr, spec))
- return buf;
+ if (check_pointer(out, addr, spec))
+ return;
switch (fmt[1]) {
case 'C':
@@ -1171,34 +1141,27 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
len = min_t(int, spec.field_width, 64);
for (i = 0; i < len; ++i) {
- if (buf < end)
- *buf = hex_asc_hi(addr[i]);
- ++buf;
- if (buf < end)
- *buf = hex_asc_lo(addr[i]);
- ++buf;
-
- if (separator && i != len - 1) {
- if (buf < end)
- *buf = separator;
- ++buf;
- }
+ __prt_char(out, hex_asc_hi(addr[i]));
+ __prt_char(out, hex_asc_lo(addr[i]));
+
+ if (separator && i != len - 1)
+ __prt_char(out, separator);
}
- return buf;
+ printbuf_nul_terminate(out);
}
static noinline_for_stack
-char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
- struct printf_spec spec, const char *fmt)
+void bitmap_string(struct printbuf *out, unsigned long *bitmap,
+ struct printf_spec spec, const char *fmt)
{
const int CHUNKSZ = 32;
int nr_bits = max_t(int, spec.field_width, 0);
int i, chunksz;
bool first = true;
- if (check_pointer(&buf, end, bitmap, spec))
- return buf;
+ if (check_pointer(out, bitmap, spec))
+ return;
/* reused to print numbers */
spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 };
@@ -1217,54 +1180,45 @@ char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
bit = i % BITS_PER_LONG;
val = (bitmap[word] >> bit) & chunkmask;
- if (!first) {
- if (buf < end)
- *buf = ',';
- buf++;
- }
+ if (!first)
+ prt_char(out, ',');
first = false;
spec.field_width = DIV_ROUND_UP(chunksz, 4);
- buf = number(buf, end, val, spec);
+ number(out, val, spec);
chunksz = CHUNKSZ;
}
- return buf;
}
static noinline_for_stack
-char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap,
- struct printf_spec spec, const char *fmt)
+void bitmap_list_string(struct printbuf *out, unsigned long *bitmap,
+ struct printf_spec spec, const char *fmt)
{
int nr_bits = max_t(int, spec.field_width, 0);
bool first = true;
int rbot, rtop;
- if (check_pointer(&buf, end, bitmap, spec))
- return buf;
+ if (check_pointer(out, bitmap, spec))
+ return ;
for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) {
- if (!first) {
- if (buf < end)
- *buf = ',';
- buf++;
- }
+ if (!first)
+ prt_char(out, ',');
first = false;
- buf = number(buf, end, rbot, default_dec_spec);
+ number(out, rbot, default_dec_spec);
if (rtop == rbot + 1)
continue;
- if (buf < end)
- *buf = '-';
- buf = number(++buf, end, rtop - 1, default_dec_spec);
+ prt_char(out, '-');
+ number(out, rtop - 1, default_dec_spec);
}
- return buf;
}
static noinline_for_stack
-char *mac_address_string(char *buf, char *end, u8 *addr,
- struct printf_spec spec, const char *fmt)
+void mac_address_string(struct printbuf *out, u8 *addr,
+ struct printf_spec spec, const char *fmt)
{
char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")];
char *p = mac_addr;
@@ -1272,8 +1226,8 @@ char *mac_address_string(char *buf, char *end, u8 *addr,
char separator;
bool reversed = false;
- if (check_pointer(&buf, end, addr, spec))
- return buf;
+ if (check_pointer(out, addr, spec))
+ return;
switch (fmt[1]) {
case 'F':
@@ -1300,11 +1254,12 @@ char *mac_address_string(char *buf, char *end, u8 *addr,
}
*p = '\0';
- return string_nocheck(buf, end, mac_addr, spec);
+ string_nocheck(out, mac_addr, spec);
}
static noinline_for_stack
-char *ip4_string(char *p, const u8 *addr, const char *fmt)
+void ip4_string(struct printbuf *out,
+ const u8 *addr, const char *fmt)
{
int i;
bool leading_zeros = (fmt[0] == 'i');
@@ -1337,24 +1292,21 @@ char *ip4_string(char *p, const u8 *addr, const char *fmt)
int digits = put_dec_trunc8(temp, addr[index]) - temp;
if (leading_zeros) {
if (digits < 3)
- *p++ = '0';
+ prt_char(out, '0');
if (digits < 2)
- *p++ = '0';
+ prt_char(out, '0');
}
/* reverse the digits in the quad */
while (digits--)
- *p++ = temp[digits];
+ prt_char(out, temp[digits]);
if (i < 3)
- *p++ = '.';
+ prt_char(out, '.');
index += step;
}
- *p = '\0';
-
- return p;
}
static noinline_for_stack
-char *ip6_compressed_string(char *p, const char *addr)
+void ip6_compressed_string(struct printbuf *out, const char *addr)
{
int i, j, range;
unsigned char zerolength[8];
@@ -1398,14 +1350,14 @@ char *ip6_compressed_string(char *p, const char *addr)
for (i = 0; i < range; i++) {
if (i == colonpos) {
if (needcolon || i == 0)
- *p++ = ':';
- *p++ = ':';
+ __prt_char(out, ':');
+ __prt_char(out, ':');
needcolon = false;
i += longest - 1;
continue;
}
if (needcolon) {
- *p++ = ':';
+ __prt_char(out, ':');
needcolon = false;
}
/* hex u16 without leading 0s */
@@ -1414,81 +1366,79 @@ char *ip6_compressed_string(char *p, const char *addr)
lo = word & 0xff;
if (hi) {
if (hi > 0x0f)
- p = hex_byte_pack(p, hi);
+ prt_hex_byte(out, hi);
else
- *p++ = hex_asc_lo(hi);
- p = hex_byte_pack(p, lo);
+ __prt_char(out, hex_asc_lo(hi));
+ prt_hex_byte(out, lo);
}
else if (lo > 0x0f)
- p = hex_byte_pack(p, lo);
+ prt_hex_byte(out, lo);
else
- *p++ = hex_asc_lo(lo);
+ __prt_char(out, hex_asc_lo(lo));
needcolon = true;
}
if (useIPv4) {
if (needcolon)
- *p++ = ':';
- p = ip4_string(p, &in6.s6_addr[12], "I4");
+ __prt_char(out, ':');
+ ip4_string(out, &in6.s6_addr[12], "I4");
}
- *p = '\0';
- return p;
+ printbuf_nul_terminate(out);
}
static noinline_for_stack
-char *ip6_string(char *p, const char *addr, const char *fmt)
+void ip6_string(struct printbuf *out, const char *addr, const char *fmt)
{
int i;
for (i = 0; i < 8; i++) {
- p = hex_byte_pack(p, *addr++);
- p = hex_byte_pack(p, *addr++);
+ prt_hex_byte(out, *addr++);
+ prt_hex_byte(out, *addr++);
if (fmt[0] == 'I' && i != 7)
- *p++ = ':';
+ prt_char(out, ':');
}
- *p = '\0';
-
- return p;
}
static noinline_for_stack
-char *ip6_addr_string(char *buf, char *end, const u8 *addr,
- struct printf_spec spec, const char *fmt)
+void ip6_addr_string(struct printbuf *out, const u8 *addr,
+ struct printf_spec spec, const char *fmt)
{
- char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+ char ip6_addr_buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+ struct printbuf ip6_addr = PRINTBUF_EXTERN(ip6_addr_buf, sizeof(ip6_addr_buf));
if (fmt[0] == 'I' && fmt[2] == 'c')
- ip6_compressed_string(ip6_addr, addr);
+ ip6_compressed_string(&ip6_addr, addr);
else
- ip6_string(ip6_addr, addr, fmt);
+ ip6_string(&ip6_addr, addr, fmt);
- return string_nocheck(buf, end, ip6_addr, spec);
+ string_nocheck(out, ip6_addr_buf, spec);
}
static noinline_for_stack
-char *ip4_addr_string(char *buf, char *end, const u8 *addr,
- struct printf_spec spec, const char *fmt)
+void ip4_addr_string(struct printbuf *out, const u8 *addr,
+ struct printf_spec spec, const char *fmt)
{
- char ip4_addr[sizeof("255.255.255.255")];
+ char ip4_addr_buf[sizeof("255.255.255.255")];
+ struct printbuf ip4_addr = PRINTBUF_EXTERN(ip4_addr_buf, sizeof(ip4_addr_buf));
- ip4_string(ip4_addr, addr, fmt);
+ ip4_string(&ip4_addr, addr, fmt);
- return string_nocheck(buf, end, ip4_addr, spec);
+ string_nocheck(out, ip4_addr_buf, spec);
}
static noinline_for_stack
-char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
- struct printf_spec spec, const char *fmt)
+void ip6_addr_string_sa(struct printbuf *out,
+ const struct sockaddr_in6 *sa,
+ struct printf_spec spec, const char *fmt)
{
bool have_p = false, have_s = false, have_f = false, have_c = false;
- char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
- sizeof(":12345") + sizeof("/123456789") +
- sizeof("%1234567890")];
- char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr);
+ char ip6_addr_buf[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
+ sizeof(":12345") + sizeof("/123456789") +
+ sizeof("%1234567890")];
+ struct printbuf ip6_addr = PRINTBUF_EXTERN(ip6_addr_buf, sizeof(ip6_addr_buf));
const u8 *addr = (const u8 *) &sa->sin6_addr;
char fmt6[2] = { fmt[0], '6' };
- u8 off = 0;
fmt++;
while (isalpha(*++fmt)) {
@@ -1508,44 +1458,42 @@ char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
}
}
- if (have_p || have_s || have_f) {
- *p = '[';
- off = 1;
- }
+ if (have_p || have_s || have_f)
+ prt_char(&ip6_addr, '[');
if (fmt6[0] == 'I' && have_c)
- p = ip6_compressed_string(ip6_addr + off, addr);
+ ip6_compressed_string(&ip6_addr, addr);
else
- p = ip6_string(ip6_addr + off, addr, fmt6);
+ ip6_string(&ip6_addr, addr, fmt6);
if (have_p || have_s || have_f)
- *p++ = ']';
+ prt_char(&ip6_addr, ']');
if (have_p) {
- *p++ = ':';
- p = number(p, pend, ntohs(sa->sin6_port), spec);
+ prt_char(&ip6_addr, ':');
+ number(&ip6_addr, ntohs(sa->sin6_port), spec);
}
if (have_f) {
- *p++ = '/';
- p = number(p, pend, ntohl(sa->sin6_flowinfo &
- IPV6_FLOWINFO_MASK), spec);
+ prt_char(&ip6_addr, '/');
+ number(&ip6_addr, ntohl(sa->sin6_flowinfo &
+ IPV6_FLOWINFO_MASK), spec);
}
if (have_s) {
- *p++ = '%';
- p = number(p, pend, sa->sin6_scope_id, spec);
+ prt_char(&ip6_addr, '%');
+ number(&ip6_addr, sa->sin6_scope_id, spec);
}
- *p = '\0';
- return string_nocheck(buf, end, ip6_addr, spec);
+ string_nocheck(out, ip6_addr_buf, spec);
}
static noinline_for_stack
-char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
- struct printf_spec spec, const char *fmt)
+void ip4_addr_string_sa(struct printbuf *out,
+ const struct sockaddr_in *sa,
+ struct printf_spec spec, const char *fmt)
{
bool have_p = false;
- char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")];
- char *pend = ip4_addr + sizeof(ip4_addr);
+ char ip4_addr_buf[sizeof("255.255.255.255") + sizeof(":12345")];
+ struct printbuf ip4_addr = PRINTBUF_EXTERN(ip4_addr_buf, sizeof(ip4_addr_buf));
const u8 *addr = (const u8 *) &sa->sin_addr.s_addr;
char fmt4[3] = { fmt[0], '4', 0 };
@@ -1564,30 +1512,29 @@ char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
}
}
- p = ip4_string(ip4_addr, addr, fmt4);
+ ip4_string(&ip4_addr, addr, fmt4);
if (have_p) {
- *p++ = ':';
- p = number(p, pend, ntohs(sa->sin_port), spec);
+ prt_char(&ip4_addr, ':');
+ number(&ip4_addr, ntohs(sa->sin_port), spec);
}
- *p = '\0';
- return string_nocheck(buf, end, ip4_addr, spec);
+ string_nocheck(out, ip4_addr_buf, spec);
}
static noinline_for_stack
-char *ip_addr_string(char *buf, char *end, const void *ptr,
- struct printf_spec spec, const char *fmt)
+void ip_addr_string(struct printbuf *out, const void *ptr,
+ struct printf_spec spec, const char *fmt)
{
char *err_fmt_msg;
- if (check_pointer(&buf, end, ptr, spec))
- return buf;
+ if (check_pointer(out, ptr, spec))
+ return;
switch (fmt[1]) {
case '6':
- return ip6_addr_string(buf, end, ptr, spec, fmt);
+ return ip6_addr_string(out, ptr, spec, fmt);
case '4':
- return ip4_addr_string(buf, end, ptr, spec, fmt);
+ return ip4_addr_string(out, ptr, spec, fmt);
case 'S': {
const union {
struct sockaddr raw;
@@ -1597,21 +1544,21 @@ char *ip_addr_string(char *buf, char *end, const void *ptr,
switch (sa->raw.sa_family) {
case AF_INET:
- return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt);
+ return ip4_addr_string_sa(out, &sa->v4, spec, fmt);
case AF_INET6:
- return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt);
+ return ip6_addr_string_sa(out, &sa->v6, spec, fmt);
default:
- return error_string(buf, end, "(einval)", spec);
+ return error_string(out, "(einval)", spec);
}}
}
err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)";
- return error_string(buf, end, err_fmt_msg, spec);
+ return error_string(out, err_fmt_msg, spec);
}
static noinline_for_stack
-char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
- const char *fmt)
+void escaped_string(struct printbuf *out, u8 *addr,
+ struct printf_spec spec, const char *fmt)
{
bool found = true;
int count = 1;
@@ -1619,10 +1566,10 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
int len;
if (spec.field_width == 0)
- return buf; /* nothing to print */
+ return; /* nothing to print */
- if (check_pointer(&buf, end, addr, spec))
- return buf;
+ if (check_pointer(out, addr, spec))
+ return;
do {
switch (fmt[count++]) {
@@ -1657,44 +1604,35 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
flags = ESCAPE_ANY_NP;
len = spec.field_width < 0 ? 1 : spec.field_width;
-
- /*
- * string_escape_mem() writes as many characters as it can to
- * the given buffer, and returns the total size of the output
- * had the buffer been big enough.
- */
- buf += string_escape_mem(addr, len, buf, buf < end ? end - buf : 0, flags, NULL);
-
- return buf;
+ prt_escaped_string(out, addr, len, flags, NULL);
}
-static char *va_format(char *buf, char *end, struct va_format *va_fmt,
- struct printf_spec spec, const char *fmt)
+static void va_format(struct printbuf *out,
+ struct va_format *va_fmt,
+ struct printf_spec spec, const char *fmt)
{
va_list va;
- if (check_pointer(&buf, end, va_fmt, spec))
- return buf;
+ if (check_pointer(out, va_fmt, spec))
+ return;
va_copy(va, *va_fmt->va);
- buf += vsnprintf(buf, end > buf ? end - buf : 0, va_fmt->fmt, va);
+ prt_vprintf(out, va_fmt->fmt, va);
va_end(va);
-
- return buf;
}
static noinline_for_stack
-char *uuid_string(char *buf, char *end, const u8 *addr,
- struct printf_spec spec, const char *fmt)
+void uuid_string(struct printbuf *out, const u8 *addr,
+ struct printf_spec spec, const char *fmt)
{
- char uuid[UUID_STRING_LEN + 1];
- char *p = uuid;
+ char uuid_buf[UUID_STRING_LEN + 1];
+ struct printbuf uuid = PRINTBUF_EXTERN(uuid_buf, sizeof(uuid_buf));
int i;
const u8 *index = uuid_index;
bool uc = false;
- if (check_pointer(&buf, end, addr, spec))
- return buf;
+ if (check_pointer(out, addr, spec))
+ return;
switch (*(++fmt)) {
case 'L':
@@ -1710,60 +1648,58 @@ char *uuid_string(char *buf, char *end, const u8 *addr,
for (i = 0; i < 16; i++) {
if (uc)
- p = hex_byte_pack_upper(p, addr[index[i]]);
+ prt_hex_byte_upper(&uuid, addr[index[i]]);
else
- p = hex_byte_pack(p, addr[index[i]]);
+ prt_hex_byte(&uuid, addr[index[i]]);
switch (i) {
case 3:
case 5:
case 7:
case 9:
- *p++ = '-';
+ prt_char(&uuid, '-');
break;
}
}
- *p = 0;
-
- return string_nocheck(buf, end, uuid, spec);
+ string_nocheck(out, uuid_buf, spec);
}
static noinline_for_stack
-char *netdev_bits(char *buf, char *end, const void *addr,
- struct printf_spec spec, const char *fmt)
+void netdev_bits(struct printbuf *out, const void *addr,
+ struct printf_spec spec, const char *fmt)
{
unsigned long long num;
int size;
- if (check_pointer(&buf, end, addr, spec))
- return buf;
+ if (check_pointer(out, addr, spec))
+ return;
switch (fmt[1]) {
case 'F':
num = *(const netdev_features_t *)addr;
size = sizeof(netdev_features_t);
+ special_hex_number(out, num, size);
break;
default:
- return error_string(buf, end, "(%pN?)", spec);
+ error_string(out, "(%pN?)", spec);
+ break;
}
-
- return special_hex_number(buf, end, num, size);
}
static noinline_for_stack
-char *fourcc_string(char *buf, char *end, const u32 *fourcc,
- struct printf_spec spec, const char *fmt)
+void fourcc_string(struct printbuf *out, const u32 *fourcc,
+ struct printf_spec spec, const char *fmt)
{
- char output[sizeof("0123 little-endian (0x01234567)")];
- char *p = output;
+ char output_buf[sizeof("0123 little-endian (0x01234567)")];
+ struct printbuf output = PRINTBUF_EXTERN(output_buf, sizeof(output_buf));
unsigned int i;
u32 orig, val;
if (fmt[1] != 'c' || fmt[2] != 'c')
- return error_string(buf, end, "(%p4?)", spec);
+ return error_string(out, "(%p4?)", spec);
- if (check_pointer(&buf, end, fourcc, spec))
- return buf;
+ if (check_pointer(out, fourcc, spec))
+ return;
orig = get_unaligned(fourcc);
val = orig & ~BIT(31);
@@ -1772,31 +1708,29 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc,
unsigned char c = val >> (i * 8);
/* Print non-control ASCII characters as-is, dot otherwise */
- *p++ = isascii(c) && isprint(c) ? c : '.';
+ prt_char(&output, isascii(c) && isprint(c) ? c : '.');
}
- *p++ = ' ';
- strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian");
- p += strlen(p);
+ prt_char(&output, ' ');
+ prt_str(&output, orig & BIT(31) ? "big-endian" : "little-endian");
- *p++ = ' ';
- *p++ = '(';
- p = special_hex_number(p, output + sizeof(output) - 2, orig, sizeof(u32));
- *p++ = ')';
- *p = '\0';
+ prt_char(&output, ' ');
+ prt_char(&output, '(');
+ special_hex_number(&output, orig, sizeof(u32));
+ prt_char(&output, ')');
- return string(buf, end, output, spec);
+ string(out, output_buf, spec);
}
static noinline_for_stack
-char *address_val(char *buf, char *end, const void *addr,
- struct printf_spec spec, const char *fmt)
+void address_val(struct printbuf *out, const void *addr,
+ struct printf_spec spec, const char *fmt)
{
unsigned long long num;
int size;
- if (check_pointer(&buf, end, addr, spec))
- return buf;
+ if (check_pointer(out, addr, spec))
+ return;
switch (fmt[1]) {
case 'd':
@@ -1810,55 +1744,44 @@ char *address_val(char *buf, char *end, const void *addr,
break;
}
- return special_hex_number(buf, end, num, size);
+ special_hex_number(out, num, size);
}
static noinline_for_stack
-char *date_str(char *buf, char *end, const struct rtc_time *tm, bool r)
+void date_str(struct printbuf *out,
+ const struct rtc_time *tm, bool r)
{
int year = tm->tm_year + (r ? 0 : 1900);
int mon = tm->tm_mon + (r ? 0 : 1);
- buf = number(buf, end, year, default_dec04_spec);
- if (buf < end)
- *buf = '-';
- buf++;
-
- buf = number(buf, end, mon, default_dec02_spec);
- if (buf < end)
- *buf = '-';
- buf++;
-
- return number(buf, end, tm->tm_mday, default_dec02_spec);
+ number(out, year, default_dec04_spec);
+ prt_char(out, '-');
+ number(out, mon, default_dec02_spec);
+ prt_char(out, '-');
+ number(out, tm->tm_mday, default_dec02_spec);
}
static noinline_for_stack
-char *time_str(char *buf, char *end, const struct rtc_time *tm, bool r)
+void time_str(struct printbuf *out, const struct rtc_time *tm, bool r)
{
- buf = number(buf, end, tm->tm_hour, default_dec02_spec);
- if (buf < end)
- *buf = ':';
- buf++;
-
- buf = number(buf, end, tm->tm_min, default_dec02_spec);
- if (buf < end)
- *buf = ':';
- buf++;
-
- return number(buf, end, tm->tm_sec, default_dec02_spec);
+ number(out, tm->tm_hour, default_dec02_spec);
+ prt_char(out, ':');
+ number(out, tm->tm_min, default_dec02_spec);
+ prt_char(out, ':');
+ number(out, tm->tm_sec, default_dec02_spec);
}
static noinline_for_stack
-char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
- struct printf_spec spec, const char *fmt)
+void rtc_str(struct printbuf *out, const struct rtc_time *tm,
+ struct printf_spec spec, const char *fmt)
{
bool have_t = true, have_d = true;
bool raw = false, iso8601_separator = true;
bool found = true;
int count = 2;
- if (check_pointer(&buf, end, tm, spec))
- return buf;
+ if (check_pointer(out, tm, spec))
+ return;
switch (fmt[count]) {
case 'd':
@@ -1886,21 +1809,16 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
} while (found);
if (have_d)
- buf = date_str(buf, end, tm, raw);
- if (have_d && have_t) {
- if (buf < end)
- *buf = iso8601_separator ? 'T' : ' ';
- buf++;
- }
+ date_str(out, tm, raw);
+ if (have_d && have_t)
+ prt_char(out, iso8601_separator ? 'T' : ' ');
if (have_t)
- buf = time_str(buf, end, tm, raw);
-
- return buf;
+ time_str(out, tm, raw);
}
static noinline_for_stack
-char *time64_str(char *buf, char *end, const time64_t time,
- struct printf_spec spec, const char *fmt)
+void time64_str(struct printbuf *out, const time64_t time,
+ struct printf_spec spec, const char *fmt)
{
struct rtc_time rtc_time;
struct tm tm;
@@ -1918,47 +1836,48 @@ char *time64_str(char *buf, char *end, const time64_t time,
rtc_time.tm_isdst = 0;
- return rtc_str(buf, end, &rtc_time, spec, fmt);
+ rtc_str(out, &rtc_time, spec, fmt);
}
static noinline_for_stack
-char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
- const char *fmt)
+void time_and_date(struct printbuf *out,
+ void *ptr, struct printf_spec spec,
+ const char *fmt)
{
switch (fmt[1]) {
case 'R':
- return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt);
+ return rtc_str(out, (const struct rtc_time *)ptr, spec, fmt);
case 'T':
- return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt);
+ return time64_str(out, *(const time64_t *)ptr, spec, fmt);
default:
- return error_string(buf, end, "(%pt?)", spec);
+ return error_string(out, "(%pt?)", spec);
}
}
static noinline_for_stack
-char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
- const char *fmt)
+void clock(struct printbuf *out, struct clk *clk,
+ struct printf_spec spec, const char *fmt)
{
if (!IS_ENABLED(CONFIG_HAVE_CLK))
- return error_string(buf, end, "(%pC?)", spec);
+ return error_string(out, "(%pC?)", spec);
- if (check_pointer(&buf, end, clk, spec))
- return buf;
+ if (check_pointer(out, clk, spec))
+ return;
switch (fmt[1]) {
case 'n':
default:
#ifdef CONFIG_COMMON_CLK
- return string(buf, end, __clk_get_name(clk), spec);
+ return string(out, __clk_get_name(clk), spec);
#else
- return ptr_to_id(buf, end, clk, spec);
+ return ptr_to_id(out, clk, spec);
#endif
}
}
static
-char *format_flags(char *buf, char *end, unsigned long flags,
- const struct trace_print_flags *names)
+void format_flags(struct printbuf *out, unsigned long flags,
+ const struct trace_print_flags *names)
{
unsigned long mask;
@@ -1967,20 +1886,15 @@ char *format_flags(char *buf, char *end, unsigned long flags,
if ((flags & mask) != mask)
continue;
- buf = string(buf, end, names->name, default_str_spec);
+ string(out, names->name, default_str_spec);
flags &= ~mask;
- if (flags) {
- if (buf < end)
- *buf = '|';
- buf++;
- }
+ if (flags)
+ prt_char(out, '|');
}
if (flags)
- buf = number(buf, end, flags, default_flag_spec);
-
- return buf;
+ number(out, flags, default_flag_spec);
}
struct page_flags_fields {
@@ -2005,20 +1919,18 @@ static const struct page_flags_fields pff[] = {
};
static
-char *format_page_flags(char *buf, char *end, unsigned long flags)
+void format_page_flags(struct printbuf *out, unsigned long flags)
{
unsigned long main_flags = flags & PAGEFLAGS_MASK;
bool append = false;
int i;
- buf = number(buf, end, flags, default_flag_spec);
- if (buf < end)
- *buf = '(';
- buf++;
+ number(out, flags, default_flag_spec);
+ prt_char(out, '(');
/* Page flags from the main area. */
if (main_flags) {
- buf = format_flags(buf, end, main_flags, pageflag_names);
+ format_flags(out, main_flags, pageflag_names);
append = true;
}
@@ -2029,41 +1941,31 @@ char *format_page_flags(char *buf, char *end, unsigned long flags)
continue;
/* Format: Flag Name + '=' (equals sign) + Number + '|' (separator) */
- if (append) {
- if (buf < end)
- *buf = '|';
- buf++;
- }
+ if (append)
+ prt_char(out, '|');
- buf = string(buf, end, pff[i].name, default_str_spec);
- if (buf < end)
- *buf = '=';
- buf++;
- buf = number(buf, end, (flags >> pff[i].shift) & pff[i].mask,
- *pff[i].spec);
+ string(out, pff[i].name, default_str_spec);
+ prt_char(out, '=');
+ number(out, (flags >> pff[i].shift) & pff[i].mask, *pff[i].spec);
append = true;
}
- if (buf < end)
- *buf = ')';
- buf++;
-
- return buf;
+ prt_char(out, ')');
}
static noinline_for_stack
-char *flags_string(char *buf, char *end, void *flags_ptr,
- struct printf_spec spec, const char *fmt)
+void flags_string(struct printbuf *out, void *flags_ptr,
+ struct printf_spec spec, const char *fmt)
{
unsigned long flags;
const struct trace_print_flags *names;
- if (check_pointer(&buf, end, flags_ptr, spec))
- return buf;
+ if (check_pointer(out, flags_ptr, spec))
+ return;
switch (fmt[1]) {
case 'p':
- return format_page_flags(buf, end, *(unsigned long *)flags_ptr);
+ return format_page_flags(out, *(unsigned long *)flags_ptr);
case 'v':
flags = *(unsigned long *)flags_ptr;
names = vmaflag_names;
@@ -2073,15 +1975,15 @@ char *flags_string(char *buf, char *end, void *flags_ptr,
names = gfpflag_names;
break;
default:
- return error_string(buf, end, "(%pG?)", spec);
+ return error_string(out, "(%pG?)", spec);
}
- return format_flags(buf, end, flags, names);
+ return format_flags(out, flags, names);
}
static noinline_for_stack
-char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf,
- char *end)
+void fwnode_full_name_string(struct printbuf *out,
+ struct fwnode_handle *fwnode)
{
int depth;
@@ -2090,25 +1992,23 @@ char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf,
struct fwnode_handle *__fwnode =
fwnode_get_nth_parent(fwnode, depth);
- buf = string(buf, end, fwnode_get_name_prefix(__fwnode),
- default_str_spec);
- buf = string(buf, end, fwnode_get_name(__fwnode),
- default_str_spec);
+ string(out, fwnode_get_name_prefix(__fwnode),
+ default_str_spec);
+ string(out, fwnode_get_name(__fwnode),
+ default_str_spec);
fwnode_handle_put(__fwnode);
}
-
- return buf;
}
static noinline_for_stack
-char *device_node_string(char *buf, char *end, struct device_node *dn,
- struct printf_spec spec, const char *fmt)
+void device_node_string(struct printbuf *out, struct device_node *dn,
+ struct printf_spec spec, const char *fmt)
{
char tbuf[sizeof("xxxx") + 1];
const char *p;
int ret;
- char *buf_start = buf;
+ unsigned start = out->pos;
struct property *prop;
bool has_mult, pass;
@@ -2116,13 +2016,13 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
str_spec.field_width = -1;
if (fmt[0] != 'F')
- return error_string(buf, end, "(%pO?)", spec);
+ return error_string(out, "(%pO?)", spec);
if (!IS_ENABLED(CONFIG_OF))
- return error_string(buf, end, "(%pOF?)", spec);
+ return error_string(out, "(%pOF?)", spec);
- if (check_pointer(&buf, end, dn, spec))
- return buf;
+ if (check_pointer(out, dn, spec))
+ return;
/* simple case without anything any more format specifiers */
fmt++;
@@ -2131,32 +2031,28 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
for (pass = false; strspn(fmt,"fnpPFcC"); fmt++, pass = true) {
int precision;
- if (pass) {
- if (buf < end)
- *buf = ':';
- buf++;
- }
+ if (pass)
+ prt_char(out, ':');
switch (*fmt) {
case 'f': /* full_name */
- buf = fwnode_full_name_string(of_fwnode_handle(dn), buf,
- end);
+ fwnode_full_name_string(out, of_fwnode_handle(dn));
break;
case 'n': /* name */
p = fwnode_get_name(of_fwnode_handle(dn));
precision = str_spec.precision;
str_spec.precision = strchrnul(p, '@') - p;
- buf = string(buf, end, p, str_spec);
+ string(out, p, str_spec);
str_spec.precision = precision;
break;
case 'p': /* phandle */
- buf = number(buf, end, (unsigned int)dn->phandle, default_dec_spec);
+ number(out, (unsigned int)dn->phandle, default_dec_spec);
break;
case 'P': /* path-spec */
p = fwnode_get_name(of_fwnode_handle(dn));
if (!p[1])
p = "/";
- buf = string(buf, end, p, str_spec);
+ string(out, p, str_spec);
break;
case 'F': /* flags */
tbuf[0] = of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-';
@@ -2164,21 +2060,21 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
tbuf[2] = of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-';
tbuf[3] = of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-';
tbuf[4] = 0;
- buf = string_nocheck(buf, end, tbuf, str_spec);
+ string_nocheck(out, tbuf, str_spec);
break;
case 'c': /* major compatible string */
ret = of_property_read_string(dn, "compatible", &p);
if (!ret)
- buf = string(buf, end, p, str_spec);
+ string(out, p, str_spec);
break;
case 'C': /* full compatible string */
has_mult = false;
of_property_for_each_string(dn, "compatible", prop, p) {
if (has_mult)
- buf = string_nocheck(buf, end, ",", str_spec);
- buf = string_nocheck(buf, end, "\"", str_spec);
- buf = string(buf, end, p, str_spec);
- buf = string_nocheck(buf, end, "\"", str_spec);
+ string_nocheck(out, ",", str_spec);
+ string_nocheck(out, "\"", str_spec);
+ string(out, p, str_spec);
+ string_nocheck(out, "\"", str_spec);
has_mult = true;
}
@@ -2188,37 +2084,38 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
}
}
- return widen_string(buf, buf - buf_start, end, spec);
+ widen_string(out, out->pos - start, spec);
}
static noinline_for_stack
-char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
- struct printf_spec spec, const char *fmt)
+void fwnode_string(struct printbuf *out,
+ struct fwnode_handle *fwnode,
+ struct printf_spec spec, const char *fmt)
{
struct printf_spec str_spec = spec;
- char *buf_start = buf;
+ unsigned start = out->pos;
str_spec.field_width = -1;
if (*fmt != 'w')
- return error_string(buf, end, "(%pf?)", spec);
+ return error_string(out, "(%pf?)", spec);
- if (check_pointer(&buf, end, fwnode, spec))
- return buf;
+ if (check_pointer(out, fwnode, spec))
+ return;
fmt++;
switch (*fmt) {
case 'P': /* name */
- buf = string(buf, end, fwnode_get_name(fwnode), str_spec);
+ string(out, fwnode_get_name(fwnode), str_spec);
break;
case 'f': /* full_name */
default:
- buf = fwnode_full_name_string(fwnode, buf, end);
+ fwnode_full_name_string(out, fwnode);
break;
}
- return widen_string(buf, buf - buf_start, end, spec);
+ widen_string(out, out->pos - start, spec);
}
int __init no_hash_pointers_enable(char *str)
@@ -2374,8 +2271,8 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
* rendering it useful as a unique identifier.
*/
static noinline_for_stack
-char *pointer(const char *fmt, char *buf, char *end, void *ptr,
- struct printf_spec spec)
+void pointer(struct printbuf *out, const char *fmt,
+ void *ptr, struct printf_spec spec)
{
switch (*fmt) {
case 'S':
@@ -2383,24 +2280,24 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
ptr = dereference_symbol_descriptor(ptr);
fallthrough;
case 'B':
- return symbol_string(buf, end, ptr, spec, fmt);
+ return symbol_string(out, ptr, spec, fmt);
case 'R':
case 'r':
- return resource_string(buf, end, ptr, spec, fmt);
+ return resource_string(out, ptr, spec, fmt);
case 'h':
- return hex_string(buf, end, ptr, spec, fmt);
+ return hex_string(out, ptr, spec, fmt);
case 'b':
switch (fmt[1]) {
case 'l':
- return bitmap_list_string(buf, end, ptr, spec, fmt);
+ return bitmap_list_string(out, ptr, spec, fmt);
default:
- return bitmap_string(buf, end, ptr, spec, fmt);
+ return bitmap_string(out, ptr, spec, fmt);
}
case 'M': /* Colon separated: 00:01:02:03:04:05 */
case 'm': /* Contiguous: 000102030405 */
/* [mM]F (FDDI) */
/* [mM]R (Reverse order; Bluetooth) */
- return mac_address_string(buf, end, ptr, spec, fmt);
+ return mac_address_string(out, ptr, spec, fmt);
case 'I': /* Formatted IP supported
* 4: 1.2.3.4
* 6: 0001:0203:...:0708
@@ -2410,57 +2307,57 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
* 4: 001.002.003.004
* 6: 000102...0f
*/
- return ip_addr_string(buf, end, ptr, spec, fmt);
+ return ip_addr_string(out, ptr, spec, fmt);
case 'E':
- return escaped_string(buf, end, ptr, spec, fmt);
+ return escaped_string(out, ptr, spec, fmt);
case 'U':
- return uuid_string(buf, end, ptr, spec, fmt);
+ return uuid_string(out, ptr, spec, fmt);
case 'V':
- return va_format(buf, end, ptr, spec, fmt);
+ return va_format(out, ptr, spec, fmt);
case 'K':
- return restricted_pointer(buf, end, ptr, spec);
+ return restricted_pointer(out, ptr, spec);
case 'N':
- return netdev_bits(buf, end, ptr, spec, fmt);
+ return netdev_bits(out, ptr, spec, fmt);
case '4':
- return fourcc_string(buf, end, ptr, spec, fmt);
+ return fourcc_string(out, ptr, spec, fmt);
case 'a':
- return address_val(buf, end, ptr, spec, fmt);
+ return address_val(out, ptr, spec, fmt);
case 'd':
- return dentry_name(buf, end, ptr, spec, fmt);
+ return dentry_name(out, ptr, spec, fmt);
case 't':
- return time_and_date(buf, end, ptr, spec, fmt);
+ return time_and_date(out, ptr, spec, fmt);
case 'C':
- return clock(buf, end, ptr, spec, fmt);
+ return clock(out, ptr, spec, fmt);
case 'D':
- return file_dentry_name(buf, end, ptr, spec, fmt);
+ return file_dentry_name(out, ptr, spec, fmt);
#ifdef CONFIG_BLOCK
case 'g':
- return bdev_name(buf, end, ptr, spec, fmt);
+ return bdev_name(out, ptr, spec, fmt);
#endif
case 'G':
- return flags_string(buf, end, ptr, spec, fmt);
+ return flags_string(out, ptr, spec, fmt);
case 'O':
- return device_node_string(buf, end, ptr, spec, fmt + 1);
+ return device_node_string(out, ptr, spec, fmt + 1);
case 'f':
- return fwnode_string(buf, end, ptr, spec, fmt + 1);
+ return fwnode_string(out, ptr, spec, fmt + 1);
case 'x':
- return pointer_string(buf, end, ptr, spec);
+ return pointer_string(out, ptr, spec);
case 'e':
/* %pe with a non-ERR_PTR gets treated as plain %p */
if (!IS_ERR(ptr))
- return default_pointer(buf, end, ptr, spec);
- return err_ptr(buf, end, ptr, spec);
+ return default_pointer(out, ptr, spec);
+ return err_ptr(out, ptr, spec);
case 'u':
case 'k':
switch (fmt[1]) {
case 's':
- return string(buf, end, ptr, spec);
+ return string(out, ptr, spec);
default:
- return error_string(buf, end, "(einval)", spec);
+ return error_string(out, "(einval)", spec);
}
default:
- return default_pointer(buf, end, ptr, spec);
+ return default_pointer(out, ptr, spec);
}
}
@@ -2682,52 +2579,27 @@ set_precision(struct printf_spec *spec, int prec)
}
/**
- * vsnprintf - Format a string and place it in a buffer
- * @buf: The buffer to place the result into
- * @size: The size of the buffer, including the trailing null space
+ * prt_vprintf - Format a string, outputting to a printbuf
+ * @out: The printbuf to output to
* @fmt: The format string to use
* @args: Arguments for the format string
*
- * This function generally follows C99 vsnprintf, but has some
- * extensions and a few limitations:
- *
- * - ``%n`` is unsupported
- * - ``%p*`` is handled by pointer()
- *
- * See pointer() or Documentation/core-api/printk-formats.rst for more
- * extensive description.
- *
- * **Please update the documentation in both places when making changes**
+ * prt_vprintf works much like the traditional vsnprintf(), but outputs to a
+ * printbuf instead of raw pointer/size.
*
- * The return value is the number of characters which would
- * be generated for the given input, excluding the trailing
- * '\0', as per ISO C99. If you want to have the exact
- * number of characters written into @buf as return value
- * (not including the trailing '\0'), use vscnprintf(). If the
- * return is greater than or equal to @size, the resulting
- * string is truncated.
+ * If you're not already dealing with a va_list consider using prt_printf().
*
- * If you're not already dealing with a va_list consider using snprintf().
+ * See the vsnprintf() documentation for format string extensions over C99.
*/
-int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+void prt_vprintf(struct printbuf *out, const char *fmt, va_list args)
{
unsigned long long num;
- char *str, *end;
struct printf_spec spec = {0};
/* Reject out-of-range values early. Large positive sizes are
used for unknown buffer sizes. */
- if (WARN_ON_ONCE(size > INT_MAX))
- return 0;
-
- str = buf;
- end = buf + size;
-
- /* Make sure end is always >= buf */
- if (end < buf) {
- end = ((void *)-1);
- size = end - buf;
- }
+ if (WARN_ON_ONCE(out->size > INT_MAX))
+ return;
while (*fmt) {
const char *old_fmt = fmt;
@@ -2736,16 +2608,9 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
fmt += read;
switch (spec.type) {
- case FORMAT_TYPE_NONE: {
- int copy = read;
- if (str < end) {
- if (copy > end - str)
- copy = end - str;
- memcpy(str, old_fmt, copy);
- }
- str += read;
+ case FORMAT_TYPE_NONE:
+ prt_bytes(out, old_fmt, read);
break;
- }
case FORMAT_TYPE_WIDTH:
set_field_width(&spec, va_arg(args, int));
@@ -2755,44 +2620,29 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
set_precision(&spec, va_arg(args, int));
break;
- case FORMAT_TYPE_CHAR: {
- char c;
+ case FORMAT_TYPE_CHAR:
+ if (spec.field_width > 0 && !(spec.flags & LEFT))
+ prt_chars(out, spec.field_width, ' ');
- if (!(spec.flags & LEFT)) {
- while (--spec.field_width > 0) {
- if (str < end)
- *str = ' ';
- ++str;
+ __prt_char(out, (unsigned char) va_arg(args, int));
- }
- }
- c = (unsigned char) va_arg(args, int);
- if (str < end)
- *str = c;
- ++str;
- while (--spec.field_width > 0) {
- if (str < end)
- *str = ' ';
- ++str;
- }
+ if (spec.field_width > 0 && (spec.flags & LEFT))
+ prt_chars(out, spec.field_width, ' ');
+ spec.field_width = 0;
break;
- }
case FORMAT_TYPE_STR:
- str = string(str, end, va_arg(args, char *), spec);
+ string(out, va_arg(args, char *), spec);
break;
case FORMAT_TYPE_PTR:
- str = pointer(fmt, str, end, va_arg(args, void *),
- spec);
+ pointer(out, fmt, va_arg(args, void *), spec);
while (isalnum(*fmt))
fmt++;
break;
case FORMAT_TYPE_PERCENT_CHAR:
- if (str < end)
- *str = '%';
- ++str;
+ __prt_char(out, '%');
break;
case FORMAT_TYPE_INVALID:
@@ -2845,21 +2695,70 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
num = va_arg(args, unsigned int);
}
- str = number(str, end, num, spec);
+ number(out, num, spec);
}
}
-
out:
- if (size > 0) {
- if (str < end)
- *str = '\0';
- else
- end[-1] = '\0';
- }
+ printbuf_nul_terminate(out);
+}
+EXPORT_SYMBOL(prt_vprintf);
- /* the trailing null byte doesn't count towards the total */
- return str-buf;
+/**
+ * prt_printf - Format a string, outputting to a printbuf
+ * @out: The printbuf to output to
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ *
+ * prt_printf works much like the traditional sprintf(), but outputs to a
+ * printbuf instead of raw pointer/size.
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
+ */
+void prt_printf(struct printbuf *out, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ prt_vprintf(out, fmt, args);
+ va_end(args);
+}
+EXPORT_SYMBOL(prt_printf);
+
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * This function generally follows C99 vsnprintf, but has some
+ * extensions and a few limitations:
+ *
+ * - ``%n`` is unsupported
+ * - ``%p*`` is handled by pointer()
+ *
+ * See pointer() or Documentation/core-api/printk-formats.rst for more
+ * extensive description.
+ *
+ * **Please update the documentation in both places when making changes**
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf(). If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * If you're not already dealing with a va_list consider using snprintf().
+ */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ struct printbuf out = PRINTBUF_EXTERN(buf, size);
+
+ prt_vprintf(&out, fmt, args);
+ return out.pos;
}
EXPORT_SYMBOL(vsnprintf);
@@ -2997,53 +2896,46 @@ EXPORT_SYMBOL(sprintf);
* bstr_printf() - Binary data to text string
*/
+static inline void printbuf_align(struct printbuf *out, unsigned align)
+{
+ /* Assumes output buffer is correctly aligned: */
+ out->pos += align - 1;
+ out->pos &= ~(align - 1);
+}
+
/**
- * vbin_printf - Parse a format string and place args' binary value in a buffer
- * @bin_buf: The buffer to place args' binary value
- * @size: The size of the buffer(by words(32bits), not characters)
+ * prt_vbinprintf - Parse a format string and place args' binary value in a buffer
+ * @out: The buffer to place args' binary value
* @fmt: The format string to use
* @args: Arguments for the format string
*
* The format follows C99 vsnprintf, except %n is ignored, and its argument
* is skipped.
*
- * The return value is the number of words(32bits) which would be generated for
- * the given input.
- *
* NOTE:
* If the return value is greater than @size, the resulting bin_buf is NOT
* valid for bstr_printf().
*/
-int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
+void prt_vbinprintf(struct printbuf *out, const char *fmt, va_list args)
{
struct printf_spec spec = {0};
- char *str, *end;
int width;
- str = (char *)bin_buf;
- end = (char *)(bin_buf + size);
-
#define save_arg(type) \
({ \
unsigned long long value; \
if (sizeof(type) == 8) { \
- unsigned long long val8; \
- str = PTR_ALIGN(str, sizeof(u32)); \
- val8 = va_arg(args, unsigned long long); \
- if (str + sizeof(type) <= end) { \
- *(u32 *)str = *(u32 *)&val8; \
- *(u32 *)(str + 4) = *((u32 *)&val8 + 1); \
- } \
+ u64 val8 = va_arg(args, u64); \
+ printbuf_align(out, sizeof(u32)); \
+ prt_bytes(out, (u32 *) &val8, 4); \
+ prt_bytes(out, ((u32 *) &val8) + 1, 4); \
value = val8; \
} else { \
- unsigned int val4; \
- str = PTR_ALIGN(str, sizeof(type)); \
- val4 = va_arg(args, int); \
- if (str + sizeof(type) <= end) \
- *(typeof(type) *)str = (type)(long)val4; \
+ u32 val4 = va_arg(args, u32); \
+ printbuf_align(out, sizeof(type)); \
+ prt_bytes(out, &val4, sizeof(type)); \
value = (unsigned long long)val4; \
} \
- str += sizeof(type); \
value; \
})
@@ -3074,16 +2966,12 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
case FORMAT_TYPE_STR: {
const char *save_str = va_arg(args, char *);
const char *err_msg;
- size_t len;
err_msg = check_pointer_msg(save_str);
if (err_msg)
save_str = err_msg;
- len = strlen(save_str) + 1;
- if (str + len < end)
- memcpy(str, save_str, len);
- str += len;
+ prt_str(out, save_str);
break;
}
@@ -3103,12 +2991,7 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
save_arg(void *);
break;
}
- str = pointer(fmt, str, end, va_arg(args, void *),
- spec);
- if (str + 1 < end)
- *str++ = '\0';
- else
- end[-1] = '\0'; /* Must be nul terminated */
+ pointer(out, fmt, va_arg(args, void *), spec);
}
/* skip all alphanumeric pointer suffixes */
while (isalnum(*fmt))
@@ -3146,15 +3029,15 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
}
out:
- return (u32 *)(PTR_ALIGN(str, sizeof(u32))) - bin_buf;
+ printbuf_nul_terminate(out);
+ printbuf_align(out, 4);
#undef save_arg
}
-EXPORT_SYMBOL_GPL(vbin_printf);
+EXPORT_SYMBOL_GPL(prt_vbinprintf);
/**
- * bstr_printf - Format a string from binary arguments and place it in a buffer
+ * prt_bstrprintf - Format a string from binary arguments and place it in a buffer
* @buf: The buffer to place the result into
- * @size: The size of the buffer, including the trailing null space
* @fmt: The format string to use
* @bin_buf: Binary arguments for the format string
*
@@ -3164,26 +3047,14 @@ EXPORT_SYMBOL_GPL(vbin_printf);
*
* The format follows C99 vsnprintf, but has some extensions:
* see vsnprintf comment for details.
- *
- * The return value is the number of characters which would
- * be generated for the given input, excluding the trailing
- * '\0', as per ISO C99. If you want to have the exact
- * number of characters written into @buf as return value
- * (not including the trailing '\0'), use vscnprintf(). If the
- * return is greater than or equal to @size, the resulting
- * string is truncated.
*/
-int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
+void prt_bstrprintf(struct printbuf *out, const char *fmt, const u32 *bin_buf)
{
struct printf_spec spec = {0};
- char *str, *end;
const char *args = (const char *)bin_buf;
- if (WARN_ON_ONCE(size > INT_MAX))
- return 0;
-
- str = buf;
- end = buf + size;
+ if (WARN_ON_ONCE(out->size > INT_MAX))
+ return;
#define get_arg(type) \
({ \
@@ -3200,12 +3071,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
value; \
})
- /* Make sure end is always >= buf */
- if (end < buf) {
- end = ((void *)-1);
- size = end - buf;
- }
-
while (*fmt) {
const char *old_fmt = fmt;
int read = format_decode(fmt, &spec);
@@ -3213,16 +3078,9 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
fmt += read;
switch (spec.type) {
- case FORMAT_TYPE_NONE: {
- int copy = read;
- if (str < end) {
- if (copy > end - str)
- copy = end - str;
- memcpy(str, old_fmt, copy);
- }
- str += read;
+ case FORMAT_TYPE_NONE:
+ prt_bytes(out, old_fmt, read);
break;
- }
case FORMAT_TYPE_WIDTH:
set_field_width(&spec, get_arg(int));
@@ -3232,38 +3090,24 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
set_precision(&spec, get_arg(int));
break;
- case FORMAT_TYPE_CHAR: {
- char c;
-
- if (!(spec.flags & LEFT)) {
- while (--spec.field_width > 0) {
- if (str < end)
- *str = ' ';
- ++str;
- }
- }
- c = (unsigned char) get_arg(char);
- if (str < end)
- *str = c;
- ++str;
- while (--spec.field_width > 0) {
- if (str < end)
- *str = ' ';
- ++str;
- }
+ case FORMAT_TYPE_CHAR:
+ if (!(spec.flags & LEFT))
+ prt_chars(out, spec.field_width, ' ');
+ __prt_char(out, (unsigned char) get_arg(char));
+ if ((spec.flags & LEFT))
+ prt_chars(out, spec.field_width, ' ');
break;
- }
case FORMAT_TYPE_STR: {
const char *str_arg = args;
args += strlen(str_arg) + 1;
- str = string(str, end, (char *)str_arg, spec);
+ string(out, (char *)str_arg, spec);
break;
}
case FORMAT_TYPE_PTR: {
bool process = false;
- int copy, len;
+ int len;
/* Non function dereferences were already done */
switch (*fmt) {
case 'S':
@@ -3279,17 +3123,12 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
break;
}
/* Pointer dereference was already processed */
- if (str < end) {
- len = copy = strlen(args);
- if (copy > end - str)
- copy = end - str;
- memcpy(str, args, copy);
- str += len;
- args += len + 1;
- }
+ len = strlen(args);
+ prt_bytes(out, args, len);
+ args += len + 1;
}
if (process)
- str = pointer(fmt, str, end, get_arg(void *), spec);
+ pointer(out, fmt, get_arg(void *), spec);
while (isalnum(*fmt))
fmt++;
@@ -3297,9 +3136,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
}
case FORMAT_TYPE_PERCENT_CHAR:
- if (str < end)
- *str = '%';
- ++str;
+ __prt_char(out, '%');
break;
case FORMAT_TYPE_INVALID:
@@ -3342,23 +3179,87 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
num = get_arg(int);
}
- str = number(str, end, num, spec);
+ number(out, num, spec);
} /* default: */
} /* switch(spec.type) */
} /* while(*fmt) */
out:
- if (size > 0) {
- if (str < end)
- *str = '\0';
- else
- end[-1] = '\0';
- }
-
#undef get_arg
+ printbuf_nul_terminate(out);
+}
+EXPORT_SYMBOL_GPL(prt_bstrprintf);
+
+/**
+ * prt_bprintf - Parse a format string and place args' binary value in a buffer
+ * @out: The buffer to place args' binary value
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ */
+void prt_bprintf(struct printbuf *out, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ prt_vbinprintf(out, fmt, args);
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(prt_bprintf);
+
+/**
+ * vbin_printf - Parse a format string and place args' binary value in a buffer
+ * @bin_buf: The buffer to place args' binary value
+ * @size: The size of the buffer(by words(32bits), not characters)
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The format follows C99 vsnprintf, except %n is ignored, and its argument
+ * is skipped.
+ *
+ * The return value is the number of words(32bits) which would be generated for
+ * the given input.
+ *
+ * NOTE:
+ * If the return value is greater than @size, the resulting bin_buf is NOT
+ * valid for bstr_printf().
+ */
+int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
+{
+ struct printbuf out = PRINTBUF_EXTERN((char *) bin_buf, size);
+
+ prt_vbinprintf(&out, fmt, args);
+ return out.pos;
+}
+EXPORT_SYMBOL_GPL(vbin_printf);
+
+/**
+ * bstr_printf - Format a string from binary arguments and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @bin_buf: Binary arguments for the format string
+ *
+ * This function like C99 vsnprintf, but the difference is that vsnprintf gets
+ * arguments from stack, and bstr_printf gets arguments from @bin_buf which is
+ * a binary buffer that generated by vbin_printf.
+ *
+ * The format follows C99 vsnprintf, but has some extensions:
+ * see vsnprintf comment for details.
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf(). If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ */
+int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
+{
+ struct printbuf out = PRINTBUF_EXTERN(buf, size);
- /* the trailing null byte doesn't count towards the total */
- return str - buf;
+ prt_bstrprintf(&out, fmt, bin_buf);
+ return out.pos;
}
EXPORT_SYMBOL_GPL(bstr_printf);