From 2e1e55cba9ea1efaa59f816261da9c1609466319 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 12 Dec 2018 11:56:51 -0500 Subject: [PATCH] Introduce printf_state Signed-off-by: Matthew Wilcox --- include/linux/printf.h | 25 ++++ lib/vsprintf.c | 291 +++++++++++++++++++++-------------------- 2 files changed, 174 insertions(+), 142 deletions(-) create mode 100644 include/linux/printf.h diff --git a/include/linux/printf.h b/include/linux/printf.h new file mode 100644 index 000000000000..74fe3bf614db --- /dev/null +++ b/include/linux/printf.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is for extending printf with new things that can be printed. + */ +#ifndef _LINUX_PRINTF +#define _LINUX_PRINTF + +struct printf_spec { + unsigned int type:8; /* format_type enum */ + signed int field_width:24; /* width of output field */ + unsigned int flags:8; /* flags to number() */ + unsigned int base:8; /* number base, 8, 10 or 16 only */ + signed int precision:16; /* # of digits/chars */ +} __packed; +#define FIELD_WIDTH_MAX ((1 << 23) - 1) +#define PRECISION_MAX ((1 << 15) - 1) + +struct printf_state { + char *buf; + char *end; + const char *fmt; + struct printf_spec spec; +}; + +#endif diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 37a54a6dd594..277b02c5141b 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -45,6 +45,7 @@ #include /* cpu_to_le16 */ #include +#include #include "kstrtox.h" /** @@ -397,16 +398,6 @@ enum format_type { FORMAT_TYPE_PTRDIFF }; -struct printf_spec { - unsigned int type:8; /* format_type enum */ - signed int field_width:24; /* width of output field */ - unsigned int flags:8; /* flags to number() */ - unsigned int base:8; /* number base, 8, 10 or 16 only */ - signed int precision:16; /* # of digits/chars */ -} __packed; -#define FIELD_WIDTH_MAX ((1 << 23) - 1) -#define PRECISION_MAX ((1 << 15) - 1) - static noinline_for_stack char *number(char *buf, char *end, unsigned long long num, struct printf_spec spec) @@ -530,6 +521,11 @@ char *number(char *buf, char *end, unsigned long long num, return buf; } +void printf_number(struct printf_state *ps, unsigned long long num) +{ + ps->buf = number(ps->buf, ps->end, num, ps->spec); +} + static noinline_for_stack char *special_hex_number(char *buf, char *end, unsigned long long num, int size) { @@ -612,6 +608,11 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec) return widen_string(buf, len, end, spec); } +static void printf_string(struct printf_state *ps, const char *s) +{ + ps->buf = string(ps->buf, ps->end, s, ps->spec); +} + static noinline_for_stack char *pointer_string(char *buf, char *end, const void *ptr, struct printf_spec spec) @@ -1979,6 +1980,11 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, return ptr_to_id(buf, end, ptr, spec); } +void printf_pointer(struct printf_state *ps, void *ptr) +{ + ps->buf = pointer(ps->fmt, ps->buf, ps->end, ptr, ps->spec); +} + /* * Helper function to decode printf style format. * Each call decode a token from the format and return the @@ -2225,88 +2231,88 @@ set_precision(struct printf_spec *spec, int prec) */ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) { + struct printf_state state = { + .buf = buf, + .end = buf + size, + .fmt = fmt, + .spec = {0}, + }; 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 (state.end < buf) { + state.end = ((void *)-1); + size = state.end - buf; } - while (*fmt) { - const char *old_fmt = fmt; - int read = format_decode(fmt, &spec); + while (*state.fmt) { + const char *old_fmt = state.fmt; + int read = format_decode(state.fmt, &state.spec); - fmt += read; + state.fmt += read; - switch (spec.type) { + switch (state.spec.type) { case FORMAT_TYPE_NONE: { int copy = read; - if (str < end) { - if (copy > end - str) - copy = end - str; - memcpy(str, old_fmt, copy); + if (state.buf < state.end) { + if (copy > state.end - state.buf) + copy = state.end - state.buf; + memcpy(state.buf, old_fmt, copy); } - str += read; + state.buf += read; break; } case FORMAT_TYPE_WIDTH: - set_field_width(&spec, va_arg(args, int)); + set_field_width(&state.spec, va_arg(args, int)); break; case FORMAT_TYPE_PRECISION: - set_precision(&spec, va_arg(args, int)); + set_precision(&state.spec, va_arg(args, int)); break; case FORMAT_TYPE_CHAR: { char c; - if (!(spec.flags & LEFT)) { - while (--spec.field_width > 0) { - if (str < end) - *str = ' '; - ++str; + if (!(state.spec.flags & LEFT)) { + while (--state.spec.field_width > 0) { + if (state.buf < state.end) + *state.buf = ' '; + ++state.buf; } } c = (unsigned char) va_arg(args, int); - if (str < end) - *str = c; - ++str; - while (--spec.field_width > 0) { - if (str < end) - *str = ' '; - ++str; + if (state.buf < state.end) + *state.buf = c; + ++state.buf; + while (--state.spec.field_width > 0) { + if (state.buf < state.end) + *state.buf = ' '; + ++state.buf; } break; } case FORMAT_TYPE_STR: - str = string(str, end, va_arg(args, char *), spec); + printf_string(&state, va_arg(args, char *)); break; case FORMAT_TYPE_PTR: - str = pointer(fmt, str, end, va_arg(args, void *), - spec); - while (isalnum(*fmt)) - fmt++; + printf_pointer(&state, va_arg(args, void *)); + while (isalnum(*state.fmt)) + state.fmt++; break; case FORMAT_TYPE_PERCENT_CHAR: - if (str < end) - *str = '%'; - ++str; + if (state.buf < state.end) + *state.buf = '%'; + ++state.buf; break; case FORMAT_TYPE_INVALID: @@ -2321,7 +2327,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) goto out; default: - switch (spec.type) { + switch (state.spec.type) { case FORMAT_TYPE_LONG_LONG: num = va_arg(args, long long); break; @@ -2332,7 +2338,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) num = va_arg(args, long); break; case FORMAT_TYPE_SIZE_T: - if (spec.flags & SIGN) + if (state.spec.flags & SIGN) num = va_arg(args, ssize_t); else num = va_arg(args, size_t); @@ -2359,20 +2365,20 @@ 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); + printf_number(&state, num); } } out: if (size > 0) { - if (str < end) - *str = '\0'; + if (state.buf < state.end) + *state.buf = '\0'; else - end[-1] = '\0'; + state.end[-1] = '\0'; } /* the trailing null byte doesn't count towards the total */ - return str-buf; + return state.buf - buf; } EXPORT_SYMBOL(vsnprintf); @@ -2528,43 +2534,44 @@ EXPORT_SYMBOL(sprintf); */ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) { - struct printf_spec spec = {0}; - char *str, *end; + struct printf_state state = { + .buf = (char *)bin_buf, + .end = (char *)bin_buf + size, + .fmt = fmt, + .spec = {0}, + }; 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)); \ + state.buf = PTR_ALIGN(state.buf, sizeof(u32)); \ val8 = va_arg(args, unsigned long long); \ - if (str + sizeof(type) <= end) { \ - *(u32 *)str = *(u32 *)&val8; \ - *(u32 *)(str + 4) = *((u32 *)&val8 + 1); \ + if (state.buf + sizeof(type) <= state.end) { \ + *(u32 *)state.buf = *(u32 *)&val8; \ + *(u32 *)(state.buf + 4) = *((u32 *)&val8 + 1); \ } \ value = val8; \ } else { \ unsigned int val4; \ - str = PTR_ALIGN(str, sizeof(type)); \ + state.buf = PTR_ALIGN(state.buf, sizeof(type)); \ val4 = va_arg(args, int); \ - if (str + sizeof(type) <= end) \ - *(typeof(type) *)str = (type)(long)val4; \ + if (state.buf + sizeof(type) <= state.end) \ + *(typeof(type) *)state.buf = (type)(long)val4; \ value = (unsigned long long)val4; \ } \ - str += sizeof(type); \ + state.buf += sizeof(type); \ value; \ }) - while (*fmt) { - int read = format_decode(fmt, &spec); + while (*state.fmt) { + int read = format_decode(state.fmt, &state.spec); - fmt += read; + state.fmt += read; - switch (spec.type) { + switch (state.spec.type) { case FORMAT_TYPE_NONE: case FORMAT_TYPE_PERCENT_CHAR: break; @@ -2575,8 +2582,8 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) case FORMAT_TYPE_PRECISION: width = (int)save_arg(int); /* Pointers may require the width */ - if (*fmt == 'p') - set_field_width(&spec, width); + if (*state.fmt == 'p') + set_field_width(&state.spec, width); break; case FORMAT_TYPE_CHAR: @@ -2591,15 +2598,15 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) || (unsigned long)save_str < PAGE_SIZE) save_str = "(null)"; len = strlen(save_str) + 1; - if (str + len < end) - memcpy(str, save_str, len); - str += len; + if (state.buf + len < state.end) + memcpy(state.buf, save_str, len); + state.buf += len; break; } case FORMAT_TYPE_PTR: /* Dereferenced pointers must be done now */ - switch (*fmt) { + switch (*state.fmt) { /* Dereference of functions is still OK */ case 'S': case 's': @@ -2610,24 +2617,23 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) save_arg(void *); break; default: - if (!isalnum(*fmt)) { + if (!isalnum(*state.fmt)) { save_arg(void *); break; } - str = pointer(fmt, str, end, va_arg(args, void *), - spec); - if (str + 1 < end) - *str++ = '\0'; + printf_pointer(&state, va_arg(args, void *)); + if (state.buf + 1 < state.end) + *state.buf++ = '\0'; else - end[-1] = '\0'; /* Must be nul terminated */ + state.end[-1] = '\0'; /* Must be nul terminated */ } /* skip all alphanumeric pointer suffixes */ - while (isalnum(*fmt)) - fmt++; + while (isalnum(*state.fmt)) + state.fmt++; break; default: - switch (spec.type) { + switch (state.spec.type) { case FORMAT_TYPE_LONG_LONG: save_arg(long long); @@ -2657,7 +2663,7 @@ 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; + return (u32 *)(PTR_ALIGN(state.buf, sizeof(u32))) - bin_buf; #undef save_arg } EXPORT_SYMBOL_GPL(vbin_printf); @@ -2686,16 +2692,17 @@ EXPORT_SYMBOL_GPL(vbin_printf); */ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) { - struct printf_spec spec = {0}; - char *str, *end; + struct printf_state state = { + .buf = buf, + .end = buf + size, + .fmt = fmt, + .spec = {0}, + }; const char *args = (const char *)bin_buf; if (WARN_ON_ONCE(size > INT_MAX)) return 0; - str = buf; - end = buf + size; - #define get_arg(type) \ ({ \ typeof(type) value; \ @@ -2712,55 +2719,55 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) }) /* Make sure end is always >= buf */ - if (end < buf) { - end = ((void *)-1); - size = end - buf; + if (state.end < buf) { + state.end = ((void *)-1); + size = state.end - buf; } - while (*fmt) { - const char *old_fmt = fmt; - int read = format_decode(fmt, &spec); + while (*state.fmt) { + const char *old_fmt = state.fmt; + int read = format_decode(state.fmt, &state.spec); - fmt += read; + state.fmt += read; - switch (spec.type) { + switch (state.spec.type) { case FORMAT_TYPE_NONE: { int copy = read; - if (str < end) { - if (copy > end - str) - copy = end - str; - memcpy(str, old_fmt, copy); + if (state.buf < state.end) { + if (copy > state.end - state.buf) + copy = state.end - state.buf; + memcpy(state.buf, old_fmt, copy); } - str += read; + state.buf += read; break; } case FORMAT_TYPE_WIDTH: - set_field_width(&spec, get_arg(int)); + set_field_width(&state.spec, get_arg(int)); break; case FORMAT_TYPE_PRECISION: - set_precision(&spec, get_arg(int)); + set_precision(&state.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; + if (!(state.spec.flags & LEFT)) { + while (--state.spec.field_width > 0) { + if (state.buf < state.end) + *state.buf = ' '; + ++state.buf; } } c = (unsigned char) get_arg(char); - if (str < end) - *str = c; - ++str; - while (--spec.field_width > 0) { - if (str < end) - *str = ' '; - ++str; + if (state.buf < state.end) + *state.buf = c; + ++state.buf; + while (--state.spec.field_width > 0) { + if (state.buf < state.end) + *state.buf = ' '; + ++state.buf; } break; } @@ -2768,7 +2775,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) case FORMAT_TYPE_STR: { const char *str_arg = args; args += strlen(str_arg) + 1; - str = string(str, end, (char *)str_arg, spec); + printf_string(&state, (char *)str_arg); break; } @@ -2776,7 +2783,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) bool process = false; int copy, len; /* Non function dereferences were already done */ - switch (*fmt) { + switch (*state.fmt) { case 'S': case 's': case 'F': @@ -2786,32 +2793,32 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) process = true; break; default: - if (!isalnum(*fmt)) { + if (!isalnum(*state.fmt)) { process = true; break; } /* Pointer dereference was already processed */ - if (str < end) { + if (state.buf < state.end) { len = copy = strlen(args); - if (copy > end - str) - copy = end - str; - memcpy(str, args, copy); - str += len; + if (copy > state.end - state.buf) + copy = state.end - state.buf; + memcpy(state.buf, args, copy); + state.buf += len; args += len + 1; } } if (process) - str = pointer(fmt, str, end, get_arg(void *), spec); + printf_pointer(&state, get_arg(void *)); - while (isalnum(*fmt)) - fmt++; + while (isalnum(*state.fmt)) + state.fmt++; break; } case FORMAT_TYPE_PERCENT_CHAR: - if (str < end) - *str = '%'; - ++str; + if (state.buf < state.end) + *state.buf = '%'; + ++state.buf; break; case FORMAT_TYPE_INVALID: @@ -2820,7 +2827,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) default: { unsigned long long num; - switch (spec.type) { + switch (state.spec.type) { case FORMAT_TYPE_LONG_LONG: num = get_arg(long long); @@ -2854,23 +2861,23 @@ 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); + printf_number(&state, num); } /* default: */ } /* switch(spec.type) */ } /* while(*fmt) */ out: if (size > 0) { - if (str < end) - *str = '\0'; + if (state.buf < state.end) + *state.buf = '\0'; else - end[-1] = '\0'; + state.end[-1] = '\0'; } #undef get_arg /* the trailing null byte doesn't count towards the total */ - return str - buf; + return state.buf - buf; } EXPORT_SYMBOL_GPL(bstr_printf); -- 2.50.1