vsprintf: factorize "(null)" string
[safe/jmp/linux-2.6] / lib / vsprintf.c
index c8f3ed6..e5ab51f 100644 (file)
@@ -546,12 +546,12 @@ static char *number(char *buf, char *end, unsigned long long num,
        return buf;
 }
 
-static char *string(char *buf, char *end, char *s, struct printf_spec spec)
+static char *string(char *buf, char *end, const char *s, struct printf_spec spec)
 {
        int len, i;
 
        if ((unsigned long)s < PAGE_SIZE)
-               s = "<NULL>";
+               s = "(null)";
 
        len = strnlen(s, spec.precision);
 
@@ -595,37 +595,89 @@ static char *symbol_string(char *buf, char *end, void *ptr,
 }
 
 static char *resource_string(char *buf, char *end, struct resource *res,
-                               struct printf_spec spec)
+                               struct printf_spec spec, const char *fmt)
 {
 #ifndef IO_RSRC_PRINTK_SIZE
-#define IO_RSRC_PRINTK_SIZE    4
+#define IO_RSRC_PRINTK_SIZE    6
 #endif
 
 #ifndef MEM_RSRC_PRINTK_SIZE
-#define MEM_RSRC_PRINTK_SIZE   8
+#define MEM_RSRC_PRINTK_SIZE   10
 #endif
-       struct printf_spec num_spec = {
+       struct printf_spec hex_spec = {
                .base = 16,
                .precision = -1,
                .flags = SPECIAL | SMALL | ZEROPAD,
        };
-       /* room for the actual numbers, the two "0x", -, [, ] and the final zero */
-       char sym[4*sizeof(resource_size_t) + 8];
+       struct printf_spec dec_spec = {
+               .base = 10,
+               .precision = -1,
+               .flags = 0,
+       };
+       struct printf_spec str_spec = {
+               .field_width = -1,
+               .precision = 10,
+               .flags = LEFT,
+       };
+       struct printf_spec flag_spec = {
+               .base = 16,
+               .precision = -1,
+               .flags = SPECIAL | SMALL,
+       };
+
+       /* 32-bit res (sizeof==4): 10 chars in dec, 10 in hex ("0x" + 8)
+        * 64-bit res (sizeof==8): 20 chars in dec, 18 in hex ("0x" + 16) */
+#define RSRC_BUF_SIZE          ((2 * sizeof(resource_size_t)) + 4)
+#define FLAG_BUF_SIZE          (2 * sizeof(res->flags))
+#define DECODED_BUF_SIZE       sizeof("[mem - 64bit pref disabled]")
+#define RAW_BUF_SIZE           sizeof("[mem - flags 0x]")
+       char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
+                    2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
+
        char *p = sym, *pend = sym + sizeof(sym);
-       int size = -1;
+       int size = -1, addr = 0;
+       int decode = (fmt[0] == 'R') ? 1 : 0;
 
-       if (res->flags & IORESOURCE_IO)
+       if (res->flags & IORESOURCE_IO) {
                size = IO_RSRC_PRINTK_SIZE;
-       else if (res->flags & IORESOURCE_MEM)
+               addr = 1;
+       } else if (res->flags & IORESOURCE_MEM) {
                size = MEM_RSRC_PRINTK_SIZE;
+               addr = 1;
+       }
 
        *p++ = '[';
-       num_spec.field_width = size;
-       p = number(p, pend, res->start, num_spec);
-       *p++ = '-';
-       p = number(p, pend, res->end, num_spec);
+       if (res->flags & IORESOURCE_IO)
+               p = string(p, pend, "io  ", str_spec);
+       else if (res->flags & IORESOURCE_MEM)
+               p = string(p, pend, "mem ", str_spec);
+       else if (res->flags & IORESOURCE_IRQ)
+               p = string(p, pend, "irq ", str_spec);
+       else if (res->flags & IORESOURCE_DMA)
+               p = string(p, pend, "dma ", str_spec);
+       else {
+               p = string(p, pend, "??? ", str_spec);
+               decode = 0;
+       }
+       hex_spec.field_width = size;
+       p = number(p, pend, res->start, addr ? hex_spec : dec_spec);
+       if (res->start != res->end) {
+               *p++ = '-';
+               p = number(p, pend, res->end, addr ? hex_spec : dec_spec);
+       }
+       if (decode) {
+               if (res->flags & IORESOURCE_MEM_64)
+                       p = string(p, pend, " 64bit", str_spec);
+               if (res->flags & IORESOURCE_PREFETCH)
+                       p = string(p, pend, " pref", str_spec);
+               if (res->flags & IORESOURCE_DISABLED)
+                       p = string(p, pend, " disabled", str_spec);
+       } else {
+               p = string(p, pend, " flags ", str_spec);
+               p = number(p, pend, res->flags, flag_spec);
+       }
        *p++ = ']';
-       *p = 0;
+       *p = '\0';
 
        return string(buf, end, sym, spec);
 }
@@ -671,7 +723,7 @@ static char *ip4_string(char *p, const u8 *addr, bool leading_zeros)
        return p;
 }
 
-static char *ip6_compressed_string(char *p, const struct in6_addr *addr)
+static char *ip6_compressed_string(char *p, const char *addr)
 {
        int i;
        int j;
@@ -683,7 +735,12 @@ static char *ip6_compressed_string(char *p, const struct in6_addr *addr)
        u8 hi;
        u8 lo;
        bool needcolon = false;
-       bool useIPv4 = ipv6_addr_v4mapped(addr) || ipv6_addr_is_isatap(addr);
+       bool useIPv4;
+       struct in6_addr in6;
+
+       memcpy(&in6, addr, sizeof(struct in6_addr));
+
+       useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6);
 
        memset(zerolength, 0, sizeof(zerolength));
 
@@ -695,7 +752,7 @@ static char *ip6_compressed_string(char *p, const struct in6_addr *addr)
        /* find position of longest 0 run */
        for (i = 0; i < range; i++) {
                for (j = i; j < range; j++) {
-                       if (addr->s6_addr16[j] != 0)
+                       if (in6.s6_addr16[j] != 0)
                                break;
                        zerolength[i]++;
                }
@@ -722,7 +779,7 @@ static char *ip6_compressed_string(char *p, const struct in6_addr *addr)
                        needcolon = false;
                }
                /* hex u16 without leading 0s */
-               word = ntohs(addr->s6_addr16[i]);
+               word = ntohs(in6.s6_addr16[i]);
                hi = word >> 8;
                lo = word & 0xff;
                if (hi) {
@@ -741,19 +798,19 @@ static char *ip6_compressed_string(char *p, const struct in6_addr *addr)
        if (useIPv4) {
                if (needcolon)
                        *p++ = ':';
-               p = ip4_string(p, &addr->s6_addr[12], false);
+               p = ip4_string(p, &in6.s6_addr[12], false);
        }
 
        *p = '\0';
        return p;
 }
 
-static char *ip6_string(char *p, const struct in6_addr *addr, const char *fmt)
+static char *ip6_string(char *p, const char *addr, const char *fmt)
 {
        int i;
        for (i = 0; i < 8; i++) {
-               p = pack_hex_byte(p, addr->s6_addr[2 * i]);
-               p = pack_hex_byte(p, addr->s6_addr[2 * i + 1]);
+               p = pack_hex_byte(p, *addr++);
+               p = pack_hex_byte(p, *addr++);
                if (fmt[0] == 'I' && i != 7)
                        *p++ = ':';
        }
@@ -768,9 +825,9 @@ static char *ip6_addr_string(char *buf, char *end, const u8 *addr,
        char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
 
        if (fmt[0] == 'I' && fmt[2] == 'c')
-               ip6_compressed_string(ip6_addr, (const struct in6_addr *)addr);
+               ip6_compressed_string(ip6_addr, addr);
        else
-               ip6_string(ip6_addr, (const struct in6_addr *)addr, fmt);
+               ip6_string(ip6_addr, addr, fmt);
 
        return string(buf, end, ip6_addr, spec);
 }
@@ -794,9 +851,10 @@ static char *ip4_addr_string(char *buf, char *end, const u8 *addr,
  *
  * - 'F' For symbolic function descriptor pointers with offset
  * - 'f' For simple symbolic function names without offset
- * - 'S' For symbolic direct pointers
- * - 'R' For a struct resource pointer, it prints the range of
- *       addresses (not the name nor the flags)
+ * - 'S' For symbolic direct pointers with offset
+ * - 's' For symbolic direct pointers without offset
+ * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]
+ * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201]
  * - 'M' For a 6-byte MAC address, it prints the address in the
  *       usual colon-separated hex notation
  * - 'm' For a 6-byte MAC address, it prints the hex address without colons
@@ -827,7 +885,8 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
        case 'S':
                return symbol_string(buf, end, ptr, spec, *fmt);
        case 'R':
-               return resource_string(buf, end, ptr, spec);
+       case 'r':
+               return resource_string(buf, end, ptr, spec, fmt);
        case 'M':                       /* Colon separated: 00:01:02:03:04:05 */
        case 'm':                       /* Contiguous: 000102030405 */
                return mac_address_string(buf, end, ptr, spec, fmt);
@@ -1069,6 +1128,7 @@ qualifier:
  * %pF output the name of a function pointer with its offset
  * %pf output the name of a function pointer without its offset
  * %pR output the address range in a struct resource
+ * %n is ignored
  *
  * The return value is the number of characters which would
  * be generated for the given input, excluding the trailing
@@ -1090,13 +1150,8 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 
        /* Reject out-of-range values early.  Large positive sizes are
           used for unknown buffer sizes. */
-       if (unlikely((int) size < 0)) {
-               /* There can be only one.. */
-               static char warn = 1;
-               WARN_ON(warn);
-               warn = 0;
+       if (WARN_ON_ONCE((int) size < 0))
                return 0;
-       }
 
        str = buf;
        end = buf + size;
@@ -1443,7 +1498,7 @@ do {                                                                      \
                        size_t len;
                        if ((unsigned long)save_str > (unsigned long)-PAGE_SIZE
                                        || (unsigned long)save_str < PAGE_SIZE)
-                               save_str = "<NULL>";
+                               save_str = "(null)";
                        len = strlen(save_str);
                        if (str + len + 1 < end)
                                memcpy(str, save_str, len + 1);
@@ -1524,11 +1579,7 @@ EXPORT_SYMBOL_GPL(vbin_printf);
  * a binary buffer that generated by vbin_printf.
  *
  * The format follows C99 vsnprintf, but has some extensions:
- * %pS output the name of a text symbol
- * %pF output the name of a function pointer with its offset
- * %pf output the name of a function pointer without its offset
- * %pR output the address range in a struct resource
- * %n is ignored
+ *  see vsnprintf comment for details.
  *
  * The return value is the number of characters which would
  * be generated for the given input, excluding the trailing
@@ -1546,13 +1597,8 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 
        struct printf_spec spec = {0};
 
-       if (unlikely((int) size < 0)) {
-               /* There can be only one.. */
-               static char warn = 1;
-               WARN_ON(warn);
-               warn = 0;
+       if (WARN_ON_ONCE((int) size < 0))
                return 0;
-       }
 
        str = buf;
        end = buf + size;
@@ -1778,7 +1824,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                 * advance both strings to next white space
                 */
                if (*fmt == '*') {
-                       while (!isspace(*fmt) && *fmt)
+                       while (!isspace(*fmt) && *fmt != '%' && *fmt)
                                fmt++;
                        while (!isspace(*str) && *str)
                                str++;