]> www.infradead.org Git - users/willy/linux.git/commitdiff
Introduce printf_state
authorMatthew Wilcox <willy@infradead.org>
Wed, 12 Dec 2018 16:56:51 +0000 (11:56 -0500)
committerMatthew Wilcox <willy@infradead.org>
Wed, 12 Dec 2018 18:39:39 +0000 (13:39 -0500)
Signed-off-by: Matthew Wilcox <willy@infradead.org>
include/linux/printf.h [new file with mode: 0644]
lib/vsprintf.c

diff --git a/include/linux/printf.h b/include/linux/printf.h
new file mode 100644 (file)
index 0000000..74fe3bf
--- /dev/null
@@ -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
index 37a54a6dd59481f0696d9c365cf0f1e06e762110..277b02c5141b20d2fe6bbd08d9589d73905b4c3a 100644 (file)
@@ -45,6 +45,7 @@
 #include <asm/byteorder.h>     /* cpu_to_le16 */
 
 #include <linux/string_helpers.h>
+#include <linux/printf.h>
 #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);