summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/vsprintf.c137
1 files changed, 66 insertions, 71 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 31dca7b8ad90..7a40c8ae2d19 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -419,10 +419,7 @@ static_assert(SMALL == ('a' ^ 'A'));
enum format_state {
FORMAT_STATE_NONE, /* Just a string part */
- FORMAT_STATE_1BYTE = 1, /* char/short/int are their own sizes */
- FORMAT_STATE_2BYTE = 2,
- FORMAT_STATE_8BYTE = 3,
- FORMAT_STATE_4BYTE = 4,
+ FORMAT_STATE_NUM,
FORMAT_STATE_WIDTH,
FORMAT_STATE_PRECISION,
FORMAT_STATE_CHAR,
@@ -432,8 +429,6 @@ enum format_state {
FORMAT_STATE_INVALID,
};
-#define FORMAT_STATE_SIZE(type) (sizeof(type) <= 4 ? sizeof(type) : FORMAT_STATE_8BYTE)
-
struct printf_spec {
unsigned char flags; /* flags to number() */
unsigned char base; /* number base, 8, 10 or 16 only */
@@ -2523,7 +2518,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
struct fmt {
const char *str;
- enum format_state state;
+ unsigned char state; // enum format_state
+ unsigned char size; // size of numbers
};
#define SPEC_CHAR(x, flag) [(x)-32] = flag
@@ -2638,20 +2634,21 @@ precision:
qualifier:
/* Set up default numeric format */
spec->base = 10;
- fmt.state = FORMAT_STATE_SIZE(int);
+ fmt.state = FORMAT_STATE_NUM;
+ fmt.size = sizeof(int);
static const struct format_state {
unsigned char state;
- unsigned char flags_or_double_state;
- unsigned char modifier;
+ unsigned char size;
+ unsigned char flags_or_double_size;
unsigned char base;
} lookup_state[256] = {
- // Qualifiers
- ['l'] = { FORMAT_STATE_SIZE(long), FORMAT_STATE_SIZE(long long), 1 },
- ['L'] = { FORMAT_STATE_SIZE(long long), 0, 1 },
- ['h'] = { FORMAT_STATE_SIZE(short), FORMAT_STATE_SIZE(char), 1 },
- ['H'] = { FORMAT_STATE_SIZE(char), 0, 1 }, // Questionable, historic
- ['z'] = { FORMAT_STATE_SIZE(size_t), 0, 1 },
- ['t'] = { FORMAT_STATE_SIZE(ptrdiff_t), 0, 1 },
+ // Length
+ ['l'] = { 0, sizeof(long), sizeof(long long) },
+ ['L'] = { 0, sizeof(long long) },
+ ['h'] = { 0, sizeof(short), sizeof(char) },
+ ['H'] = { 0, sizeof(char) }, // Questionable historical
+ ['z'] = { 0, sizeof(size_t) },
+ ['t'] = { 0, sizeof(ptrdiff_t) },
// Non-numeric formats
['c'] = { FORMAT_STATE_CHAR },
@@ -2660,12 +2657,12 @@ qualifier:
['%'] = { FORMAT_STATE_PERCENT_CHAR },
// Numerics
- ['o'] = { 0, 0, 0, 8 },
- ['x'] = { 0, SMALL, 0, 16 },
- ['X'] = { 0, 0, 0, 16 },
- ['d'] = { 0, SIGN, 0, 10 },
- ['i'] = { 0, SIGN, 0, 10 },
- ['u'] = { 0, 0, 0, 10, },
+ ['o'] = { FORMAT_STATE_NUM, 0, 0, 8 },
+ ['x'] = { FORMAT_STATE_NUM, 0, SMALL, 16 },
+ ['X'] = { FORMAT_STATE_NUM, 0, 0, 16 },
+ ['d'] = { FORMAT_STATE_NUM, 0, SIGN, 10 },
+ ['i'] = { FORMAT_STATE_NUM, 0, SIGN, 10 },
+ ['u'] = { FORMAT_STATE_NUM, 0, 0, 10, },
/*
* Since %n poses a greater security risk than
@@ -2675,30 +2672,23 @@ qualifier:
};
const struct format_state *p = lookup_state + (u8)*fmt.str;
- if (p->modifier) {
- fmt.state = p->state;
- if (p->flags_or_double_state && fmt.str[0] == fmt.str[1]) {
- fmt.state = p->flags_or_double_state;
+ if (p->size) {
+ fmt.size = p->size;
+ if (p->flags_or_double_size && fmt.str[0] == fmt.str[1]) {
+ fmt.size = p->flags_or_double_size;
fmt.str++;
}
fmt.str++;
p = lookup_state + *fmt.str;
- if (unlikely(p->modifier))
- goto invalid;
- }
- if (p->base) {
- spec->base = p->base;
- spec->flags |= p->flags_or_double_state;
- fmt.str++;
- return fmt;
}
if (p->state) {
+ spec->base = p->base;
+ spec->flags |= p->flags_or_double_size;
fmt.state = p->state;
fmt.str++;
return fmt;
}
-invalid:
WARN_ONCE(1, "Please remove unsupported %%%c in format string\n", *fmt.str);
fmt.state = FORMAT_STATE_INVALID;
return fmt;
@@ -2768,7 +2758,6 @@ static unsigned long long convert_num_spec(unsigned int val, int size, struct pr
*/
int vsnprintf(char *buf, size_t size, const char *fmt_str, va_list args)
{
- unsigned long long num;
char *str, *end;
struct printf_spec spec = {0};
struct fmt fmt = {
@@ -2808,6 +2797,16 @@ int vsnprintf(char *buf, size_t size, const char *fmt_str, va_list args)
continue;
}
+ case FORMAT_STATE_NUM: {
+ unsigned long long num;
+ if (fmt.size <= sizeof(int))
+ num = convert_num_spec(va_arg(args, int), fmt.size, spec);
+ else
+ num = va_arg(args, long long);
+ str = number(str, end, num, spec);
+ continue;
+ }
+
case FORMAT_STATE_WIDTH:
set_field_width(&spec, va_arg(args, int));
continue;
@@ -2856,7 +2855,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt_str, va_list args)
++str;
continue;
- case FORMAT_STATE_INVALID:
+ default:
/*
* Presumably the arguments passed gcc's type
* checking, but there is no safe or sane way
@@ -2866,17 +2865,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt_str, va_list args)
* sync.
*/
goto out;
-
- case FORMAT_STATE_8BYTE:
- num = va_arg(args, long long);
- break;
-
- default:
- num = convert_num_spec(va_arg(args, int), fmt.state, spec);
- break;
}
-
- str = number(str, end, num, spec);
}
out:
@@ -3147,17 +3136,20 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt_str, va_list args)
fmt.str++;
break;
- case FORMAT_STATE_8BYTE:
- save_arg(long long);
- break;
- case FORMAT_STATE_1BYTE:
- save_arg(char);
- break;
- case FORMAT_STATE_2BYTE:
- save_arg(short);
- break;
- default:
- save_arg(int);
+ case FORMAT_STATE_NUM:
+ switch (fmt.size) {
+ case 8:
+ save_arg(long long);
+ break;
+ case 1:
+ save_arg(char);
+ break;
+ case 2:
+ save_arg(short);
+ break;
+ default:
+ save_arg(int);
+ }
}
}
@@ -3325,18 +3317,21 @@ int bstr_printf(char *buf, size_t size, const char *fmt_str, const u32 *bin_buf)
case FORMAT_STATE_INVALID:
goto out;
- case FORMAT_STATE_8BYTE:
- num = get_arg(long long);
- break;
- case FORMAT_STATE_2BYTE:
- num = convert_num_spec(get_arg(short), fmt.state, spec);
- break;
- case FORMAT_STATE_1BYTE:
- num = convert_num_spec(get_arg(char), fmt.state, spec);
- break;
- default:
- num = convert_num_spec(get_arg(int), fmt.state, spec);
- break;
+ case FORMAT_STATE_NUM:
+ switch (fmt.size) {
+ case 8:
+ num = get_arg(long long);
+ break;
+ case 1:
+ num = convert_num_spec(get_arg(char), fmt.size, spec);
+ break;
+ case 2:
+ num = convert_num_spec(get_arg(short), fmt.size, spec);
+ break;
+ default:
+ num = convert_num_spec(get_arg(int), fmt.size, spec);
+ break;
+ }
}
str = number(str, end, num, spec);