mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 02:05:33 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
This commit is contained in:
commit
629d79aa0d
569
lib/vsprintf.c
569
lib/vsprintf.c
@ -160,8 +160,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base)
|
||||
}
|
||||
EXPORT_SYMBOL(simple_strtoll);
|
||||
|
||||
static noinline_for_stack
|
||||
int skip_atoi(const char **s)
|
||||
static inline int skip_atoi(const char **s)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
@ -407,7 +406,7 @@ int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
|
||||
return len + width;
|
||||
}
|
||||
|
||||
#define SIGN 1 /* unsigned/signed, must be 1 */
|
||||
#define SIGN 1 /* unsigned/signed */
|
||||
#define LEFT 2 /* left justified */
|
||||
#define PLUS 4 /* show plus */
|
||||
#define SPACE 8 /* space if plus */
|
||||
@ -415,38 +414,26 @@ int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
|
||||
#define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */
|
||||
#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */
|
||||
|
||||
static_assert(SIGN == 1);
|
||||
static_assert(ZEROPAD == ('0' - ' '));
|
||||
static_assert(SMALL == ('a' ^ 'A'));
|
||||
|
||||
enum format_type {
|
||||
FORMAT_TYPE_NONE, /* Just a string part */
|
||||
FORMAT_TYPE_WIDTH,
|
||||
FORMAT_TYPE_PRECISION,
|
||||
FORMAT_TYPE_CHAR,
|
||||
FORMAT_TYPE_STR,
|
||||
FORMAT_TYPE_PTR,
|
||||
FORMAT_TYPE_PERCENT_CHAR,
|
||||
FORMAT_TYPE_INVALID,
|
||||
FORMAT_TYPE_LONG_LONG,
|
||||
FORMAT_TYPE_ULONG,
|
||||
FORMAT_TYPE_LONG,
|
||||
FORMAT_TYPE_UBYTE,
|
||||
FORMAT_TYPE_BYTE,
|
||||
FORMAT_TYPE_USHORT,
|
||||
FORMAT_TYPE_SHORT,
|
||||
FORMAT_TYPE_UINT,
|
||||
FORMAT_TYPE_INT,
|
||||
FORMAT_TYPE_SIZE_T,
|
||||
FORMAT_TYPE_PTRDIFF
|
||||
enum format_state {
|
||||
FORMAT_STATE_NONE, /* Just a string part */
|
||||
FORMAT_STATE_NUM,
|
||||
FORMAT_STATE_WIDTH,
|
||||
FORMAT_STATE_PRECISION,
|
||||
FORMAT_STATE_CHAR,
|
||||
FORMAT_STATE_STR,
|
||||
FORMAT_STATE_PTR,
|
||||
FORMAT_STATE_PERCENT_CHAR,
|
||||
FORMAT_STATE_INVALID,
|
||||
};
|
||||
|
||||
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 */
|
||||
unsigned char flags; /* flags to number() */
|
||||
unsigned char base; /* number base, 8, 10 or 16 only */
|
||||
short precision; /* # of digits/chars */
|
||||
int field_width; /* width of output field */
|
||||
} __packed;
|
||||
static_assert(sizeof(struct printf_spec) == 8);
|
||||
|
||||
@ -579,7 +566,6 @@ char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
|
||||
{
|
||||
struct printf_spec spec;
|
||||
|
||||
spec.type = FORMAT_TYPE_PTR;
|
||||
spec.field_width = 2 + 2 * size; /* 0x + hex */
|
||||
spec.flags = SPECIAL | SMALL | ZEROPAD;
|
||||
spec.base = 16;
|
||||
@ -2530,6 +2516,26 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
|
||||
}
|
||||
}
|
||||
|
||||
struct fmt {
|
||||
const char *str;
|
||||
unsigned char state; // enum format_state
|
||||
unsigned char size; // size of numbers
|
||||
};
|
||||
|
||||
#define SPEC_CHAR(x, flag) [(x)-32] = flag
|
||||
static unsigned char spec_flag(unsigned char c)
|
||||
{
|
||||
static const unsigned char spec_flag_array[] = {
|
||||
SPEC_CHAR(' ', SPACE),
|
||||
SPEC_CHAR('#', SPECIAL),
|
||||
SPEC_CHAR('+', PLUS),
|
||||
SPEC_CHAR('-', LEFT),
|
||||
SPEC_CHAR('0', ZEROPAD),
|
||||
};
|
||||
c -= 32;
|
||||
return (c < sizeof(spec_flag_array)) ? spec_flag_array[c] : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to decode printf style format.
|
||||
* Each call decode a token from the format and return the
|
||||
@ -2552,181 +2558,140 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
|
||||
* @qualifier: qualifier of a number (long, size_t, ...)
|
||||
*/
|
||||
static noinline_for_stack
|
||||
int format_decode(const char *fmt, struct printf_spec *spec)
|
||||
struct fmt format_decode(struct fmt fmt, struct printf_spec *spec)
|
||||
{
|
||||
const char *start = fmt;
|
||||
char qualifier;
|
||||
const char *start = fmt.str;
|
||||
char flag;
|
||||
|
||||
/* we finished early by reading the field width */
|
||||
if (spec->type == FORMAT_TYPE_WIDTH) {
|
||||
if (unlikely(fmt.state == FORMAT_STATE_WIDTH)) {
|
||||
if (spec->field_width < 0) {
|
||||
spec->field_width = -spec->field_width;
|
||||
spec->flags |= LEFT;
|
||||
}
|
||||
spec->type = FORMAT_TYPE_NONE;
|
||||
fmt.state = FORMAT_STATE_NONE;
|
||||
goto precision;
|
||||
}
|
||||
|
||||
/* we finished early by reading the precision */
|
||||
if (spec->type == FORMAT_TYPE_PRECISION) {
|
||||
if (unlikely(fmt.state == FORMAT_STATE_PRECISION)) {
|
||||
if (spec->precision < 0)
|
||||
spec->precision = 0;
|
||||
|
||||
spec->type = FORMAT_TYPE_NONE;
|
||||
fmt.state = FORMAT_STATE_NONE;
|
||||
goto qualifier;
|
||||
}
|
||||
|
||||
/* By default */
|
||||
spec->type = FORMAT_TYPE_NONE;
|
||||
fmt.state = FORMAT_STATE_NONE;
|
||||
|
||||
for (; *fmt ; ++fmt) {
|
||||
if (*fmt == '%')
|
||||
for (; *fmt.str ; fmt.str++) {
|
||||
if (*fmt.str == '%')
|
||||
break;
|
||||
}
|
||||
|
||||
/* Return the current non-format string */
|
||||
if (fmt != start || !*fmt)
|
||||
return fmt - start;
|
||||
if (fmt.str != start || !*fmt.str)
|
||||
return fmt;
|
||||
|
||||
/* Process flags */
|
||||
/* Process flags. This also skips the first '%' */
|
||||
spec->flags = 0;
|
||||
|
||||
while (1) { /* this also skips first '%' */
|
||||
bool found = true;
|
||||
|
||||
++fmt;
|
||||
|
||||
switch (*fmt) {
|
||||
case '-': spec->flags |= LEFT; break;
|
||||
case '+': spec->flags |= PLUS; break;
|
||||
case ' ': spec->flags |= SPACE; break;
|
||||
case '#': spec->flags |= SPECIAL; break;
|
||||
case '0': spec->flags |= ZEROPAD; break;
|
||||
default: found = false;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
do {
|
||||
/* this also skips first '%' */
|
||||
flag = spec_flag(*++fmt.str);
|
||||
spec->flags |= flag;
|
||||
} while (flag);
|
||||
|
||||
/* get field width */
|
||||
spec->field_width = -1;
|
||||
|
||||
if (isdigit(*fmt))
|
||||
spec->field_width = skip_atoi(&fmt);
|
||||
else if (*fmt == '*') {
|
||||
if (isdigit(*fmt.str))
|
||||
spec->field_width = skip_atoi(&fmt.str);
|
||||
else if (unlikely(*fmt.str == '*')) {
|
||||
/* it's the next argument */
|
||||
spec->type = FORMAT_TYPE_WIDTH;
|
||||
return ++fmt - start;
|
||||
fmt.state = FORMAT_STATE_WIDTH;
|
||||
fmt.str++;
|
||||
return fmt;
|
||||
}
|
||||
|
||||
precision:
|
||||
/* get the precision */
|
||||
spec->precision = -1;
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
if (isdigit(*fmt)) {
|
||||
spec->precision = skip_atoi(&fmt);
|
||||
if (unlikely(*fmt.str == '.')) {
|
||||
fmt.str++;
|
||||
if (isdigit(*fmt.str)) {
|
||||
spec->precision = skip_atoi(&fmt.str);
|
||||
if (spec->precision < 0)
|
||||
spec->precision = 0;
|
||||
} else if (*fmt == '*') {
|
||||
} else if (*fmt.str == '*') {
|
||||
/* it's the next argument */
|
||||
spec->type = FORMAT_TYPE_PRECISION;
|
||||
return ++fmt - start;
|
||||
fmt.state = FORMAT_STATE_PRECISION;
|
||||
fmt.str++;
|
||||
return fmt;
|
||||
}
|
||||
}
|
||||
|
||||
qualifier:
|
||||
/* get the conversion qualifier */
|
||||
qualifier = 0;
|
||||
if (*fmt == 'h' || _tolower(*fmt) == 'l' ||
|
||||
*fmt == 'z' || *fmt == 't') {
|
||||
qualifier = *fmt++;
|
||||
if (unlikely(qualifier == *fmt)) {
|
||||
if (qualifier == 'l') {
|
||||
qualifier = 'L';
|
||||
++fmt;
|
||||
} else if (qualifier == 'h') {
|
||||
qualifier = 'H';
|
||||
++fmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* default base */
|
||||
/* Set up default numeric format */
|
||||
spec->base = 10;
|
||||
switch (*fmt) {
|
||||
case 'c':
|
||||
spec->type = FORMAT_TYPE_CHAR;
|
||||
return ++fmt - start;
|
||||
fmt.state = FORMAT_STATE_NUM;
|
||||
fmt.size = sizeof(int);
|
||||
static const struct format_state {
|
||||
unsigned char state;
|
||||
unsigned char size;
|
||||
unsigned char flags_or_double_size;
|
||||
unsigned char base;
|
||||
} lookup_state[256] = {
|
||||
// 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) },
|
||||
|
||||
case 's':
|
||||
spec->type = FORMAT_TYPE_STR;
|
||||
return ++fmt - start;
|
||||
// Non-numeric formats
|
||||
['c'] = { FORMAT_STATE_CHAR },
|
||||
['s'] = { FORMAT_STATE_STR },
|
||||
['p'] = { FORMAT_STATE_PTR },
|
||||
['%'] = { FORMAT_STATE_PERCENT_CHAR },
|
||||
|
||||
case 'p':
|
||||
spec->type = FORMAT_TYPE_PTR;
|
||||
return ++fmt - start;
|
||||
// Numerics
|
||||
['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, },
|
||||
|
||||
case '%':
|
||||
spec->type = FORMAT_TYPE_PERCENT_CHAR;
|
||||
return ++fmt - start;
|
||||
|
||||
/* integer number formats - set up the flags and "break" */
|
||||
case 'o':
|
||||
spec->base = 8;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
spec->flags |= SMALL;
|
||||
fallthrough;
|
||||
|
||||
case 'X':
|
||||
spec->base = 16;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
spec->flags |= SIGN;
|
||||
break;
|
||||
case 'u':
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
/*
|
||||
* Since %n poses a greater security risk than
|
||||
* utility, treat it as any other invalid or
|
||||
* unsupported format specifier.
|
||||
*/
|
||||
fallthrough;
|
||||
};
|
||||
|
||||
default:
|
||||
WARN_ONCE(1, "Please remove unsupported %%%c in format string\n", *fmt);
|
||||
spec->type = FORMAT_TYPE_INVALID;
|
||||
return fmt - start;
|
||||
const struct format_state *p = lookup_state + (u8)*fmt.str;
|
||||
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 (p->state) {
|
||||
spec->base = p->base;
|
||||
spec->flags |= p->flags_or_double_size;
|
||||
fmt.state = p->state;
|
||||
fmt.str++;
|
||||
return fmt;
|
||||
}
|
||||
|
||||
if (qualifier == 'L')
|
||||
spec->type = FORMAT_TYPE_LONG_LONG;
|
||||
else if (qualifier == 'l') {
|
||||
BUILD_BUG_ON(FORMAT_TYPE_ULONG + SIGN != FORMAT_TYPE_LONG);
|
||||
spec->type = FORMAT_TYPE_ULONG + (spec->flags & SIGN);
|
||||
} else if (qualifier == 'z') {
|
||||
spec->type = FORMAT_TYPE_SIZE_T;
|
||||
} else if (qualifier == 't') {
|
||||
spec->type = FORMAT_TYPE_PTRDIFF;
|
||||
} else if (qualifier == 'H') {
|
||||
BUILD_BUG_ON(FORMAT_TYPE_UBYTE + SIGN != FORMAT_TYPE_BYTE);
|
||||
spec->type = FORMAT_TYPE_UBYTE + (spec->flags & SIGN);
|
||||
} else if (qualifier == 'h') {
|
||||
BUILD_BUG_ON(FORMAT_TYPE_USHORT + SIGN != FORMAT_TYPE_SHORT);
|
||||
spec->type = FORMAT_TYPE_USHORT + (spec->flags & SIGN);
|
||||
} else {
|
||||
BUILD_BUG_ON(FORMAT_TYPE_UINT + SIGN != FORMAT_TYPE_INT);
|
||||
spec->type = FORMAT_TYPE_UINT + (spec->flags & SIGN);
|
||||
}
|
||||
|
||||
return ++fmt - start;
|
||||
WARN_ONCE(1, "Please remove unsupported %%%c in format string\n", *fmt.str);
|
||||
fmt.state = FORMAT_STATE_INVALID;
|
||||
return fmt;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2747,11 +2712,27 @@ set_precision(struct printf_spec *spec, int prec)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn a 1/2/4-byte value into a 64-bit one for printing: truncate
|
||||
* as necessary and deal with signedness.
|
||||
*
|
||||
* 'size' is the size of the value in bytes.
|
||||
*/
|
||||
static unsigned long long convert_num_spec(unsigned int val, int size, struct printf_spec spec)
|
||||
{
|
||||
unsigned int shift = 32 - size*8;
|
||||
|
||||
val <<= shift;
|
||||
if (!(spec.flags & SIGN))
|
||||
return val >> shift;
|
||||
return (int)val >> shift;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @fmt_str: The format string to use
|
||||
* @args: Arguments for the format string
|
||||
*
|
||||
* This function generally follows C99 vsnprintf, but has some
|
||||
@ -2775,11 +2756,14 @@ set_precision(struct printf_spec *spec, int prec)
|
||||
*
|
||||
* 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)
|
||||
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 = {
|
||||
.str = fmt_str,
|
||||
.state = FORMAT_STATE_NONE,
|
||||
};
|
||||
|
||||
/* Reject out-of-range values early. Large positive sizes are
|
||||
used for unknown buffer sizes. */
|
||||
@ -2795,33 +2779,43 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||
size = end - buf;
|
||||
}
|
||||
|
||||
while (*fmt) {
|
||||
const char *old_fmt = fmt;
|
||||
int read = format_decode(fmt, &spec);
|
||||
while (*fmt.str) {
|
||||
const char *old_fmt = fmt.str;
|
||||
|
||||
fmt += read;
|
||||
fmt = format_decode(fmt, &spec);
|
||||
|
||||
switch (spec.type) {
|
||||
case FORMAT_TYPE_NONE: {
|
||||
int copy = read;
|
||||
switch (fmt.state) {
|
||||
case FORMAT_STATE_NONE: {
|
||||
int read = fmt.str - old_fmt;
|
||||
if (str < end) {
|
||||
int copy = read;
|
||||
if (copy > end - str)
|
||||
copy = end - str;
|
||||
memcpy(str, old_fmt, copy);
|
||||
}
|
||||
str += read;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_WIDTH:
|
||||
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));
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_PRECISION:
|
||||
case FORMAT_STATE_PRECISION:
|
||||
set_precision(&spec, va_arg(args, int));
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_CHAR: {
|
||||
case FORMAT_STATE_CHAR: {
|
||||
char c;
|
||||
|
||||
if (!(spec.flags & LEFT)) {
|
||||
@ -2841,27 +2835,27 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||
*str = ' ';
|
||||
++str;
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_STR:
|
||||
case FORMAT_STATE_STR:
|
||||
str = string(str, end, va_arg(args, char *), spec);
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_PTR:
|
||||
str = pointer(fmt, str, end, va_arg(args, void *),
|
||||
case FORMAT_STATE_PTR:
|
||||
str = pointer(fmt.str, str, end, va_arg(args, void *),
|
||||
spec);
|
||||
while (isalnum(*fmt))
|
||||
fmt++;
|
||||
break;
|
||||
while (isalnum(*fmt.str))
|
||||
fmt.str++;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_PERCENT_CHAR:
|
||||
case FORMAT_STATE_PERCENT_CHAR:
|
||||
if (str < end)
|
||||
*str = '%';
|
||||
++str;
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_INVALID:
|
||||
default:
|
||||
/*
|
||||
* Presumably the arguments passed gcc's type
|
||||
* checking, but there is no safe or sane way
|
||||
@ -2871,47 +2865,6 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||
* sync.
|
||||
*/
|
||||
goto out;
|
||||
|
||||
default:
|
||||
switch (spec.type) {
|
||||
case FORMAT_TYPE_LONG_LONG:
|
||||
num = va_arg(args, long long);
|
||||
break;
|
||||
case FORMAT_TYPE_ULONG:
|
||||
num = va_arg(args, unsigned long);
|
||||
break;
|
||||
case FORMAT_TYPE_LONG:
|
||||
num = va_arg(args, long);
|
||||
break;
|
||||
case FORMAT_TYPE_SIZE_T:
|
||||
if (spec.flags & SIGN)
|
||||
num = va_arg(args, ssize_t);
|
||||
else
|
||||
num = va_arg(args, size_t);
|
||||
break;
|
||||
case FORMAT_TYPE_PTRDIFF:
|
||||
num = va_arg(args, ptrdiff_t);
|
||||
break;
|
||||
case FORMAT_TYPE_UBYTE:
|
||||
num = (unsigned char) va_arg(args, int);
|
||||
break;
|
||||
case FORMAT_TYPE_BYTE:
|
||||
num = (signed char) va_arg(args, int);
|
||||
break;
|
||||
case FORMAT_TYPE_USHORT:
|
||||
num = (unsigned short) va_arg(args, int);
|
||||
break;
|
||||
case FORMAT_TYPE_SHORT:
|
||||
num = (short) va_arg(args, int);
|
||||
break;
|
||||
case FORMAT_TYPE_INT:
|
||||
num = (int) va_arg(args, int);
|
||||
break;
|
||||
default:
|
||||
num = va_arg(args, unsigned int);
|
||||
}
|
||||
|
||||
str = number(str, end, num, spec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3067,7 +3020,7 @@ EXPORT_SYMBOL(sprintf);
|
||||
* 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
|
||||
* @fmt_str: The format string to use
|
||||
* @args: Arguments for the format string
|
||||
*
|
||||
* The format follows C99 vsnprintf, except %n is ignored, and its argument
|
||||
@ -3080,8 +3033,12 @@ EXPORT_SYMBOL(sprintf);
|
||||
* 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)
|
||||
int vbin_printf(u32 *bin_buf, size_t size, const char *fmt_str, va_list args)
|
||||
{
|
||||
struct fmt fmt = {
|
||||
.str = fmt_str,
|
||||
.state = FORMAT_STATE_NONE,
|
||||
};
|
||||
struct printf_spec spec = {0};
|
||||
char *str, *end;
|
||||
int width;
|
||||
@ -3113,31 +3070,29 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
|
||||
value; \
|
||||
})
|
||||
|
||||
while (*fmt) {
|
||||
int read = format_decode(fmt, &spec);
|
||||
while (*fmt.str) {
|
||||
fmt = format_decode(fmt, &spec);
|
||||
|
||||
fmt += read;
|
||||
|
||||
switch (spec.type) {
|
||||
case FORMAT_TYPE_NONE:
|
||||
case FORMAT_TYPE_PERCENT_CHAR:
|
||||
switch (fmt.state) {
|
||||
case FORMAT_STATE_NONE:
|
||||
case FORMAT_STATE_PERCENT_CHAR:
|
||||
break;
|
||||
case FORMAT_TYPE_INVALID:
|
||||
case FORMAT_STATE_INVALID:
|
||||
goto out;
|
||||
|
||||
case FORMAT_TYPE_WIDTH:
|
||||
case FORMAT_TYPE_PRECISION:
|
||||
case FORMAT_STATE_WIDTH:
|
||||
case FORMAT_STATE_PRECISION:
|
||||
width = (int)save_arg(int);
|
||||
/* Pointers may require the width */
|
||||
if (*fmt == 'p')
|
||||
if (*fmt.str == 'p')
|
||||
set_field_width(&spec, width);
|
||||
break;
|
||||
|
||||
case FORMAT_TYPE_CHAR:
|
||||
case FORMAT_STATE_CHAR:
|
||||
save_arg(char);
|
||||
break;
|
||||
|
||||
case FORMAT_TYPE_STR: {
|
||||
case FORMAT_STATE_STR: {
|
||||
const char *save_str = va_arg(args, char *);
|
||||
const char *err_msg;
|
||||
size_t len;
|
||||
@ -3153,9 +3108,9 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
|
||||
break;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_PTR:
|
||||
case FORMAT_STATE_PTR:
|
||||
/* Dereferenced pointers must be done now */
|
||||
switch (*fmt) {
|
||||
switch (*fmt.str) {
|
||||
/* Dereference of functions is still OK */
|
||||
case 'S':
|
||||
case 's':
|
||||
@ -3165,11 +3120,11 @@ 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(*fmt.str)) {
|
||||
save_arg(void *);
|
||||
break;
|
||||
}
|
||||
str = pointer(fmt, str, end, va_arg(args, void *),
|
||||
str = pointer(fmt.str, str, end, va_arg(args, void *),
|
||||
spec);
|
||||
if (str + 1 < end)
|
||||
*str++ = '\0';
|
||||
@ -3177,35 +3132,14 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
|
||||
end[-1] = '\0'; /* Must be nul terminated */
|
||||
}
|
||||
/* skip all alphanumeric pointer suffixes */
|
||||
while (isalnum(*fmt))
|
||||
fmt++;
|
||||
while (isalnum(*fmt.str))
|
||||
fmt.str++;
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (spec.type) {
|
||||
|
||||
case FORMAT_TYPE_LONG_LONG:
|
||||
case FORMAT_STATE_NUM:
|
||||
if (fmt.size > sizeof(int)) {
|
||||
save_arg(long long);
|
||||
break;
|
||||
case FORMAT_TYPE_ULONG:
|
||||
case FORMAT_TYPE_LONG:
|
||||
save_arg(unsigned long);
|
||||
break;
|
||||
case FORMAT_TYPE_SIZE_T:
|
||||
save_arg(size_t);
|
||||
break;
|
||||
case FORMAT_TYPE_PTRDIFF:
|
||||
save_arg(ptrdiff_t);
|
||||
break;
|
||||
case FORMAT_TYPE_UBYTE:
|
||||
case FORMAT_TYPE_BYTE:
|
||||
save_arg(char);
|
||||
break;
|
||||
case FORMAT_TYPE_USHORT:
|
||||
case FORMAT_TYPE_SHORT:
|
||||
save_arg(short);
|
||||
break;
|
||||
default:
|
||||
} else {
|
||||
save_arg(int);
|
||||
}
|
||||
}
|
||||
@ -3221,7 +3155,7 @@ 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
|
||||
* @fmt_str: 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
|
||||
@ -3239,8 +3173,12 @@ EXPORT_SYMBOL_GPL(vbin_printf);
|
||||
* 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)
|
||||
int bstr_printf(char *buf, size_t size, const char *fmt_str, const u32 *bin_buf)
|
||||
{
|
||||
struct fmt fmt = {
|
||||
.str = fmt_str,
|
||||
.state = FORMAT_STATE_NONE,
|
||||
};
|
||||
struct printf_spec spec = {0};
|
||||
char *str, *end;
|
||||
const char *args = (const char *)bin_buf;
|
||||
@ -3272,33 +3210,33 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
|
||||
size = end - buf;
|
||||
}
|
||||
|
||||
while (*fmt) {
|
||||
const char *old_fmt = fmt;
|
||||
int read = format_decode(fmt, &spec);
|
||||
while (*fmt.str) {
|
||||
const char *old_fmt = fmt.str;
|
||||
unsigned long long num;
|
||||
|
||||
fmt += read;
|
||||
|
||||
switch (spec.type) {
|
||||
case FORMAT_TYPE_NONE: {
|
||||
int copy = read;
|
||||
fmt = format_decode(fmt, &spec);
|
||||
switch (fmt.state) {
|
||||
case FORMAT_STATE_NONE: {
|
||||
int read = fmt.str - old_fmt;
|
||||
if (str < end) {
|
||||
int copy = read;
|
||||
if (copy > end - str)
|
||||
copy = end - str;
|
||||
memcpy(str, old_fmt, copy);
|
||||
}
|
||||
str += read;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_WIDTH:
|
||||
case FORMAT_STATE_WIDTH:
|
||||
set_field_width(&spec, get_arg(int));
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_PRECISION:
|
||||
case FORMAT_STATE_PRECISION:
|
||||
set_precision(&spec, get_arg(int));
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_CHAR: {
|
||||
case FORMAT_STATE_CHAR: {
|
||||
char c;
|
||||
|
||||
if (!(spec.flags & LEFT)) {
|
||||
@ -3317,21 +3255,21 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
|
||||
*str = ' ';
|
||||
++str;
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_STR: {
|
||||
case FORMAT_STATE_STR: {
|
||||
const char *str_arg = args;
|
||||
args += strlen(str_arg) + 1;
|
||||
str = string(str, end, (char *)str_arg, spec);
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_PTR: {
|
||||
case FORMAT_STATE_PTR: {
|
||||
bool process = false;
|
||||
int copy, len;
|
||||
/* Non function dereferences were already done */
|
||||
switch (*fmt) {
|
||||
switch (*fmt.str) {
|
||||
case 'S':
|
||||
case 's':
|
||||
case 'x':
|
||||
@ -3340,7 +3278,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
|
||||
process = true;
|
||||
break;
|
||||
default:
|
||||
if (!isalnum(*fmt)) {
|
||||
if (!isalnum(*fmt.str)) {
|
||||
process = true;
|
||||
break;
|
||||
}
|
||||
@ -3355,63 +3293,32 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
|
||||
}
|
||||
}
|
||||
if (process)
|
||||
str = pointer(fmt, str, end, get_arg(void *), spec);
|
||||
str = pointer(fmt.str, str, end, get_arg(void *), spec);
|
||||
|
||||
while (isalnum(*fmt))
|
||||
fmt++;
|
||||
break;
|
||||
while (isalnum(*fmt.str))
|
||||
fmt.str++;
|
||||
continue;
|
||||
}
|
||||
|
||||
case FORMAT_TYPE_PERCENT_CHAR:
|
||||
case FORMAT_STATE_PERCENT_CHAR:
|
||||
if (str < end)
|
||||
*str = '%';
|
||||
++str;
|
||||
break;
|
||||
continue;
|
||||
|
||||
case FORMAT_TYPE_INVALID:
|
||||
case FORMAT_STATE_INVALID:
|
||||
goto out;
|
||||
|
||||
default: {
|
||||
unsigned long long num;
|
||||
|
||||
switch (spec.type) {
|
||||
|
||||
case FORMAT_TYPE_LONG_LONG:
|
||||
case FORMAT_STATE_NUM:
|
||||
if (fmt.size > sizeof(int)) {
|
||||
num = get_arg(long long);
|
||||
break;
|
||||
case FORMAT_TYPE_ULONG:
|
||||
case FORMAT_TYPE_LONG:
|
||||
num = get_arg(unsigned long);
|
||||
break;
|
||||
case FORMAT_TYPE_SIZE_T:
|
||||
num = get_arg(size_t);
|
||||
break;
|
||||
case FORMAT_TYPE_PTRDIFF:
|
||||
num = get_arg(ptrdiff_t);
|
||||
break;
|
||||
case FORMAT_TYPE_UBYTE:
|
||||
num = get_arg(unsigned char);
|
||||
break;
|
||||
case FORMAT_TYPE_BYTE:
|
||||
num = get_arg(signed char);
|
||||
break;
|
||||
case FORMAT_TYPE_USHORT:
|
||||
num = get_arg(unsigned short);
|
||||
break;
|
||||
case FORMAT_TYPE_SHORT:
|
||||
num = get_arg(short);
|
||||
break;
|
||||
case FORMAT_TYPE_UINT:
|
||||
num = get_arg(unsigned int);
|
||||
break;
|
||||
default:
|
||||
num = get_arg(int);
|
||||
} else {
|
||||
num = convert_num_spec(get_arg(int), fmt.size, spec);
|
||||
}
|
||||
|
||||
str = number(str, end, num, spec);
|
||||
} /* default: */
|
||||
} /* switch(spec.type) */
|
||||
} /* while(*fmt) */
|
||||
continue;
|
||||
}
|
||||
} /* while(*fmt.str) */
|
||||
|
||||
out:
|
||||
if (size > 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user