#include <asm/byteorder.h> /* cpu_to_le16 */
#include <linux/string_helpers.h>
+#include <linux/printf.h>
#include "kstrtox.h"
/**
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)
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)
{
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)
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
*/
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:
goto out;
default:
- switch (spec.type) {
+ switch (state.spec.type) {
case FORMAT_TYPE_LONG_LONG:
num = va_arg(args, long long);
break;
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);
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);
*/
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;
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:
|| (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':
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);
}
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);
*/
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; \
})
/* 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;
}
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;
}
bool process = false;
int copy, len;
/* Non function dereferences were already done */
- switch (*fmt) {
+ switch (*state.fmt) {
case 'S':
case 's':
case 'F':
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:
default: {
unsigned long long num;
- switch (spec.type) {
+ switch (state.spec.type) {
case FORMAT_TYPE_LONG_LONG:
num = get_arg(long long);
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);