Merge branch 'parisc-3.20-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
[cascardo/linux.git] / lib / vsprintf.c
index ec337f6..b235c96 100644 (file)
@@ -114,8 +114,9 @@ int skip_atoi(const char **s)
 {
        int i = 0;
 
-       while (isdigit(**s))
+       do {
                i = i*10 + *((*s)++) - '0';
+       } while (isdigit(**s));
 
        return i;
 }
@@ -792,6 +793,87 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
        return buf;
 }
 
+static noinline_for_stack
+char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
+                   struct printf_spec spec, const char *fmt)
+{
+       const int CHUNKSZ = 32;
+       int nr_bits = max_t(int, spec.field_width, 0);
+       int i, chunksz;
+       bool first = true;
+
+       /* reused to print numbers */
+       spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 };
+
+       chunksz = nr_bits & (CHUNKSZ - 1);
+       if (chunksz == 0)
+               chunksz = CHUNKSZ;
+
+       i = ALIGN(nr_bits, CHUNKSZ) - CHUNKSZ;
+       for (; i >= 0; i -= CHUNKSZ) {
+               u32 chunkmask, val;
+               int word, bit;
+
+               chunkmask = ((1ULL << chunksz) - 1);
+               word = i / BITS_PER_LONG;
+               bit = i % BITS_PER_LONG;
+               val = (bitmap[word] >> bit) & chunkmask;
+
+               if (!first) {
+                       if (buf < end)
+                               *buf = ',';
+                       buf++;
+               }
+               first = false;
+
+               spec.field_width = DIV_ROUND_UP(chunksz, 4);
+               buf = number(buf, end, val, spec);
+
+               chunksz = CHUNKSZ;
+       }
+       return buf;
+}
+
+static noinline_for_stack
+char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap,
+                        struct printf_spec spec, const char *fmt)
+{
+       int nr_bits = max_t(int, spec.field_width, 0);
+       /* current bit is 'cur', most recently seen range is [rbot, rtop] */
+       int cur, rbot, rtop;
+       bool first = true;
+
+       /* reused to print numbers */
+       spec = (struct printf_spec){ .base = 10 };
+
+       rbot = cur = find_first_bit(bitmap, nr_bits);
+       while (cur < nr_bits) {
+               rtop = cur;
+               cur = find_next_bit(bitmap, nr_bits, cur + 1);
+               if (cur < nr_bits && cur <= rtop + 1)
+                       continue;
+
+               if (!first) {
+                       if (buf < end)
+                               *buf = ',';
+                       buf++;
+               }
+               first = false;
+
+               buf = number(buf, end, rbot, spec);
+               if (rbot < rtop) {
+                       if (buf < end)
+                               *buf = '-';
+                       buf++;
+
+                       buf = number(buf, end, rtop, spec);
+               }
+
+               rbot = cur;
+       }
+       return buf;
+}
+
 static noinline_for_stack
 char *mac_address_string(char *buf, char *end, u8 *addr,
                         struct printf_spec spec, const char *fmt)
@@ -1257,6 +1339,10 @@ int kptr_restrict __read_mostly;
  * - 'B' For backtraced symbolic direct pointers with 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]
+ * - 'b[l]' For a bitmap, the number of bits is determined by the field
+ *       width which must be explicitly specified either as part of the
+ *       format string '%32b[l]' or through '%*b[l]', [l] selects
+ *       range-list format instead of hex format
  * - '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
@@ -1353,6 +1439,13 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                return resource_string(buf, end, ptr, spec, fmt);
        case 'h':
                return hex_string(buf, end, ptr, spec, fmt);
+       case 'b':
+               switch (fmt[1]) {
+               case 'l':
+                       return bitmap_list_string(buf, end, ptr, spec, fmt);
+               default:
+                       return bitmap_string(buf, end, ptr, spec, fmt);
+               }
        case 'M':                       /* Colon separated: 00:01:02:03:04:05 */
        case 'm':                       /* Contiguous: 000102030405 */
                                        /* [mM]F (FDDI) */
@@ -1604,8 +1697,7 @@ qualifier:
 
        case 'p':
                spec->type = FORMAT_TYPE_PTR;
-               return fmt - start;
-               /* skip alnum */
+               return ++fmt - start;
 
        case '%':
                spec->type = FORMAT_TYPE_PERCENT_CHAR;
@@ -1689,6 +1781,8 @@ qualifier:
  * %pB output the name of a backtrace symbol with its offset
  * %pR output the address range in a struct resource with decoded flags
  * %pr output the address range in a struct resource with raw flags
+ * %pb output the bitmap with field width as the number of bits
+ * %pbl output the bitmap as range list with field width as the number of bits
  * %pM output a 6-byte MAC address with colons
  * %pMR output a 6-byte MAC address with colons in reversed order
  * %pMF output a 6-byte MAC address with dashes
@@ -1728,7 +1822,7 @@ 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 (WARN_ON_ONCE((int) size < 0))
+       if (WARN_ON_ONCE(size > INT_MAX))
                return 0;
 
        str = buf;
@@ -1794,7 +1888,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                        break;
 
                case FORMAT_TYPE_PTR:
-                       str = pointer(fmt+1, str, end, va_arg(args, void *),
+                       str = pointer(fmt, str, end, va_arg(args, void *),
                                      spec);
                        while (isalnum(*fmt))
                                fmt++;
@@ -2232,7 +2326,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
                }
 
                case FORMAT_TYPE_PTR:
-                       str = pointer(fmt+1, str, end, get_arg(void *), spec);
+                       str = pointer(fmt, str, end, get_arg(void *), spec);
                        while (isalnum(*fmt))
                                fmt++;
                        break;