summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2009-11-09 00:58:44 +0000
committerFelix Fietkau <nbd@openwrt.org>2009-11-09 00:58:44 +0000
commitee937398f7919863625558d6a4b6a7fb5cee2186 (patch)
tree0ed82e0fdf6e43e58319e0bd1717267407a963bb
parent498483be78ae4dee93e60734567a175ba000d819 (diff)
downloadopenwrt-ee937398f7919863625558d6a4b6a7fb5cee2186.tar.gz
openwrt-ee937398f7919863625558d6a4b6a7fb5cee2186.tar.bz2
openwrt-ee937398f7919863625558d6a4b6a7fb5cee2186.zip
backport a recent version of vsprintf to linux 2.6.28 to fix mac80211 wifi interface detection
SVN-Revision: 18352
-rw-r--r--target/linux/generic-2.6/patches-2.6.28/981-vsprintf_backport.patch888
1 files changed, 888 insertions, 0 deletions
diff --git a/target/linux/generic-2.6/patches-2.6.28/981-vsprintf_backport.patch b/target/linux/generic-2.6/patches-2.6.28/981-vsprintf_backport.patch
new file mode 100644
index 0000000000..37589b188f
--- /dev/null
+++ b/target/linux/generic-2.6/patches-2.6.28/981-vsprintf_backport.patch
@@ -0,0 +1,888 @@
+--- a/lib/vsprintf.c
++++ b/lib/vsprintf.c
+@@ -170,6 +170,8 @@ int strict_strtoul(const char *cp, unsig
+ return -EINVAL;
+
+ val = simple_strtoul(cp, &tail, base);
++ if (tail == cp)
++ return -EINVAL;
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
+ *res = val;
+@@ -241,6 +243,8 @@ int strict_strtoull(const char *cp, unsi
+ return -EINVAL;
+
+ val = simple_strtoull(cp, &tail, base);
++ if (tail == cp)
++ return -EINVAL;
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
+ *res = val;
+@@ -392,7 +396,38 @@ static noinline char* put_dec(char *buf,
+ #define SMALL 32 /* Must be 32 == 0x20 */
+ #define SPECIAL 64 /* 0x */
+
+-static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)
++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_USHORT,
++ FORMAT_TYPE_SHORT,
++ FORMAT_TYPE_UINT,
++ FORMAT_TYPE_INT,
++ FORMAT_TYPE_NRCHARS,
++ FORMAT_TYPE_SIZE_T,
++ FORMAT_TYPE_PTRDIFF
++};
++
++struct printf_spec {
++ enum format_type type;
++ int flags; /* flags to number() */
++ int field_width; /* width of output field */
++ int base;
++ int precision; /* # of digits/chars */
++ int qualifier;
++};
++
++static char *number(char *buf, char *end, unsigned long long num,
++ struct printf_spec spec)
+ {
+ /* we are called with base 8, 10 or 16, only, thus don't need "G..." */
+ static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
+@@ -400,32 +435,32 @@ static char *number(char *buf, char *end
+ char tmp[66];
+ char sign;
+ char locase;
+- int need_pfx = ((type & SPECIAL) && base != 10);
++ int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
+ int i;
+
+ /* locase = 0 or 0x20. ORing digits or letters with 'locase'
+ * produces same digits or (maybe lowercased) letters */
+- locase = (type & SMALL);
+- if (type & LEFT)
+- type &= ~ZEROPAD;
++ locase = (spec.flags & SMALL);
++ if (spec.flags & LEFT)
++ spec.flags &= ~ZEROPAD;
+ sign = 0;
+- if (type & SIGN) {
++ if (spec.flags & SIGN) {
+ if ((signed long long) num < 0) {
+ sign = '-';
+ num = - (signed long long) num;
+- size--;
+- } else if (type & PLUS) {
++ spec.field_width--;
++ } else if (spec.flags & PLUS) {
+ sign = '+';
+- size--;
+- } else if (type & SPACE) {
++ spec.field_width--;
++ } else if (spec.flags & SPACE) {
+ sign = ' ';
+- size--;
++ spec.field_width--;
+ }
+ }
+ if (need_pfx) {
+- size--;
+- if (base == 16)
+- size--;
++ spec.field_width--;
++ if (spec.base == 16)
++ spec.field_width--;
+ }
+
+ /* generate full string in tmp[], in reverse order */
+@@ -437,10 +472,10 @@ static char *number(char *buf, char *end
+ tmp[i++] = (digits[do_div(num,base)] | locase);
+ } while (num != 0);
+ */
+- else if (base != 10) { /* 8 or 16 */
+- int mask = base - 1;
++ else if (spec.base != 10) { /* 8 or 16 */
++ int mask = spec.base - 1;
+ int shift = 3;
+- if (base == 16) shift = 4;
++ if (spec.base == 16) shift = 4;
+ do {
+ tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
+ num >>= shift;
+@@ -450,12 +485,12 @@ static char *number(char *buf, char *end
+ }
+
+ /* printing 100 using %2d gives "100", not "00" */
+- if (i > precision)
+- precision = i;
++ if (i > spec.precision)
++ spec.precision = i;
+ /* leading space padding */
+- size -= precision;
+- if (!(type & (ZEROPAD+LEFT))) {
+- while(--size >= 0) {
++ spec.field_width -= spec.precision;
++ if (!(spec.flags & (ZEROPAD+LEFT))) {
++ while(--spec.field_width >= 0) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+@@ -472,23 +507,23 @@ static char *number(char *buf, char *end
+ if (buf < end)
+ *buf = '0';
+ ++buf;
+- if (base == 16) {
++ if (spec.base == 16) {
+ if (buf < end)
+ *buf = ('X' | locase);
+ ++buf;
+ }
+ }
+ /* zero or space padding */
+- if (!(type & LEFT)) {
+- char c = (type & ZEROPAD) ? '0' : ' ';
+- while (--size >= 0) {
++ if (!(spec.flags & LEFT)) {
++ char c = (spec.flags & ZEROPAD) ? '0' : ' ';
++ while (--spec.field_width >= 0) {
+ if (buf < end)
+ *buf = c;
+ ++buf;
+ }
+ }
+ /* hmm even more zero padding? */
+- while (i <= --precision) {
++ while (i <= --spec.precision) {
+ if (buf < end)
+ *buf = '0';
+ ++buf;
+@@ -500,7 +535,7 @@ static char *number(char *buf, char *end
+ ++buf;
+ }
+ /* trailing space padding */
+- while (--size >= 0) {
++ while (--spec.field_width >= 0) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+@@ -508,17 +543,17 @@ static char *number(char *buf, char *end
+ return buf;
+ }
+
+-static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
++static char *string(char *buf, char *end, char *s, struct printf_spec spec)
+ {
+ int len, i;
+
+ if ((unsigned long)s < PAGE_SIZE)
+ s = "<NULL>";
+
+- len = strnlen(s, precision);
++ len = strnlen(s, spec.precision);
+
+- if (!(flags & LEFT)) {
+- while (len < field_width--) {
++ if (!(spec.flags & LEFT)) {
++ while (len < spec.field_width--) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+@@ -529,7 +564,7 @@ static char *string(char *buf, char *end
+ *buf = *s;
+ ++buf; ++s;
+ }
+- while (len < field_width--) {
++ while (len < spec.field_width--) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+@@ -537,21 +572,24 @@ static char *string(char *buf, char *end
+ return buf;
+ }
+
+-static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags)
++static char *symbol_string(char *buf, char *end, void *ptr,
++ struct printf_spec spec)
+ {
+ unsigned long value = (unsigned long) ptr;
+ #ifdef CONFIG_KALLSYMS
+ char sym[KSYM_SYMBOL_LEN];
+ sprint_symbol(sym, value);
+- return string(buf, end, sym, field_width, precision, flags);
++ return string(buf, end, sym, spec);
+ #else
+- field_width = 2*sizeof(void *);
+- flags |= SPECIAL | SMALL | ZEROPAD;
+- return number(buf, end, value, 16, field_width, precision, flags);
++ spec.field_width = 2*sizeof(void *);
++ spec.flags |= SPECIAL | SMALL | ZEROPAD;
++ spec.base = 16;
++ return number(buf, end, value, spec);
+ #endif
+ }
+
+-static char *resource_string(char *buf, char *end, struct resource *res, int field_width, int precision, int flags)
++static char *resource_string(char *buf, char *end, struct resource *res,
++ struct printf_spec spec)
+ {
+ #ifndef IO_RSRC_PRINTK_SIZE
+ #define IO_RSRC_PRINTK_SIZE 4
+@@ -560,7 +598,11 @@ static char *resource_string(char *buf,
+ #ifndef MEM_RSRC_PRINTK_SIZE
+ #define MEM_RSRC_PRINTK_SIZE 8
+ #endif
+-
++ struct printf_spec num_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];
+ char *p = sym, *pend = sym + sizeof(sym);
+@@ -572,13 +614,73 @@ static char *resource_string(char *buf,
+ size = MEM_RSRC_PRINTK_SIZE;
+
+ *p++ = '[';
+- p = number(p, pend, res->start, 16, size, -1, SPECIAL | SMALL | ZEROPAD);
++ num_spec.field_width = size;
++ p = number(p, pend, res->start, num_spec);
+ *p++ = '-';
+- p = number(p, pend, res->end, 16, size, -1, SPECIAL | SMALL | ZEROPAD);
++ p = number(p, pend, res->end, num_spec);
+ *p++ = ']';
+ *p = 0;
+
+- return string(buf, end, sym, field_width, precision, flags);
++ return string(buf, end, sym, spec);
++}
++
++static char *mac_address_string(char *buf, char *end, u8 *addr,
++ struct printf_spec spec)
++{
++ char mac_addr[6 * 3]; /* (6 * 2 hex digits), 5 colons and trailing zero */
++ char *p = mac_addr;
++ int i;
++
++ for (i = 0; i < 6; i++) {
++ p = pack_hex_byte(p, addr[i]);
++ if (!(spec.flags & SPECIAL) && i != 5)
++ *p++ = ':';
++ }
++ *p = '\0';
++ spec.flags &= ~SPECIAL;
++
++ return string(buf, end, mac_addr, spec);
++}
++
++static char *ip6_addr_string(char *buf, char *end, u8 *addr,
++ struct printf_spec spec)
++{
++ char ip6_addr[8 * 5]; /* (8 * 4 hex digits), 7 colons and trailing zero */
++ char *p = ip6_addr;
++ int i;
++
++ for (i = 0; i < 8; i++) {
++ p = pack_hex_byte(p, addr[2 * i]);
++ p = pack_hex_byte(p, addr[2 * i + 1]);
++ if (!(spec.flags & SPECIAL) && i != 7)
++ *p++ = ':';
++ }
++ *p = '\0';
++ spec.flags &= ~SPECIAL;
++
++ return string(buf, end, ip6_addr, spec);
++}
++
++static char *ip4_addr_string(char *buf, char *end, u8 *addr,
++ struct printf_spec spec)
++{
++ char ip4_addr[4 * 4]; /* (4 * 3 decimal digits), 3 dots and trailing zero */
++ char temp[3]; /* hold each IP quad in reverse order */
++ char *p = ip4_addr;
++ int i, digits;
++
++ for (i = 0; i < 4; i++) {
++ digits = put_dec_trunc(temp, addr[i]) - temp;
++ /* reverse the digits in the quad */
++ while (digits--)
++ *p++ = temp[digits];
++ if (i != 3)
++ *p++ = '.';
++ }
++ *p = '\0';
++ spec.flags &= ~SPECIAL;
++
++ return string(buf, end, ip4_addr, spec);
+ }
+
+ /*
+@@ -592,28 +694,244 @@ static char *resource_string(char *buf,
+ * - 'S' For symbolic direct pointers
+ * - 'R' For a struct resource pointer, it prints the range of
+ * addresses (not the name nor the flags)
++ * - 'M' For a 6-byte MAC address, it prints the address in the
++ * usual colon-separated hex notation
++ * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated
++ * decimal for v4 and colon separated network-order 16 bit hex for v6)
++ * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
++ * currently the same
+ *
+ * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
+ * function pointers are really function descriptors, which contain a
+ * pointer to the real address.
+ */
+-static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
++static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
++ struct printf_spec spec)
+ {
++ if (!ptr)
++ return string(buf, end, "(null)", spec);
++
+ switch (*fmt) {
+ case 'F':
+ ptr = dereference_function_descriptor(ptr);
+ /* Fallthrough */
+ case 'S':
+- return symbol_string(buf, end, ptr, field_width, precision, flags);
++ return symbol_string(buf, end, ptr, spec);
+ case 'R':
+- return resource_string(buf, end, ptr, field_width, precision, flags);
++ return resource_string(buf, end, ptr, spec);
++ case 'm':
++ spec.flags |= SPECIAL;
++ /* Fallthrough */
++ case 'M':
++ return mac_address_string(buf, end, ptr, spec);
++ case 'i':
++ spec.flags |= SPECIAL;
++ /* Fallthrough */
++ case 'I':
++ if (fmt[1] == '6')
++ return ip6_addr_string(buf, end, ptr, spec);
++ if (fmt[1] == '4')
++ return ip4_addr_string(buf, end, ptr, spec);
++ spec.flags &= ~SPECIAL;
++ break;
+ }
+- flags |= SMALL;
+- if (field_width == -1) {
+- field_width = 2*sizeof(void *);
+- flags |= ZEROPAD;
++ spec.flags |= SMALL;
++ if (spec.field_width == -1) {
++ spec.field_width = 2*sizeof(void *);
++ spec.flags |= ZEROPAD;
+ }
+- return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
++ spec.base = 16;
++
++ return number(buf, end, (unsigned long) ptr, spec);
++}
++
++/*
++ * Helper function to decode printf style format.
++ * Each call decode a token from the format and return the
++ * number of characters read (or likely the delta where it wants
++ * to go on the next call).
++ * The decoded token is returned through the parameters
++ *
++ * 'h', 'l', or 'L' for integer fields
++ * 'z' support added 23/7/1999 S.H.
++ * 'z' changed to 'Z' --davidm 1/25/99
++ * 't' added for ptrdiff_t
++ *
++ * @fmt: the format string
++ * @type of the token returned
++ * @flags: various flags such as +, -, # tokens..
++ * @field_width: overwritten width
++ * @base: base of the number (octal, hex, ...)
++ * @precision: precision of a number
++ * @qualifier: qualifier of a number (long, size_t, ...)
++ */
++static int format_decode(const char *fmt, struct printf_spec *spec)
++{
++ const char *start = fmt;
++
++ /* we finished early by reading the field width */
++ if (spec->type == FORMAT_TYPE_WIDTH) {
++ if (spec->field_width < 0) {
++ spec->field_width = -spec->field_width;
++ spec->flags |= LEFT;
++ }
++ spec->type = FORMAT_TYPE_NONE;
++ goto precision;
++ }
++
++ /* we finished early by reading the precision */
++ if (spec->type == FORMAT_TYPE_PRECISION) {
++ if (spec->precision < 0)
++ spec->precision = 0;
++
++ spec->type = FORMAT_TYPE_NONE;
++ goto qualifier;
++ }
++
++ /* By default */
++ spec->type = FORMAT_TYPE_NONE;
++
++ for (; *fmt ; ++fmt) {
++ if (*fmt == '%')
++ break;
++ }
++
++ /* Return the current non-format string */
++ if (fmt != start || !*fmt)
++ return fmt - start;
++
++ /* Process flags */
++ 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;
++ }
++
++ /* get field width */
++ spec->field_width = -1;
++
++ if (isdigit(*fmt))
++ spec->field_width = skip_atoi(&fmt);
++ else if (*fmt == '*') {
++ /* it's the next argument */
++ spec->type = FORMAT_TYPE_WIDTH;
++ return ++fmt - start;
++ }
++
++precision:
++ /* get the precision */
++ spec->precision = -1;
++ if (*fmt == '.') {
++ ++fmt;
++ if (isdigit(*fmt)) {
++ spec->precision = skip_atoi(&fmt);
++ if (spec->precision < 0)
++ spec->precision = 0;
++ } else if (*fmt == '*') {
++ /* it's the next argument */
++ spec->type = FORMAT_TYPE_PRECISION;
++ return ++fmt - start;
++ }
++ }
++
++qualifier:
++ /* get the conversion qualifier */
++ spec->qualifier = -1;
++ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
++ *fmt == 'Z' || *fmt == 'z' || *fmt == 't') {
++ spec->qualifier = *fmt;
++ ++fmt;
++ if (spec->qualifier == 'l' && *fmt == 'l') {
++ spec->qualifier = 'L';
++ ++fmt;
++ }
++ }
++
++ /* default base */
++ spec->base = 10;
++ switch (*fmt) {
++ case 'c':
++ spec->type = FORMAT_TYPE_CHAR;
++ return ++fmt - start;
++
++ case 's':
++ spec->type = FORMAT_TYPE_STR;
++ return ++fmt - start;
++
++ case 'p':
++ spec->type = FORMAT_TYPE_PTR;
++ return fmt - start;
++ /* skip alnum */
++
++ case 'n':
++ spec->type = FORMAT_TYPE_NRCHARS;
++ return ++fmt - start;
++
++ 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;
++
++ case 'X':
++ spec->base = 16;
++ break;
++
++ case 'd':
++ case 'i':
++ spec->flags |= SIGN;
++ case 'u':
++ break;
++
++ default:
++ spec->type = FORMAT_TYPE_INVALID;
++ return fmt - start;
++ }
++
++ if (spec->qualifier == 'L')
++ spec->type = FORMAT_TYPE_LONG_LONG;
++ else if (spec->qualifier == 'l') {
++ if (spec->flags & SIGN)
++ spec->type = FORMAT_TYPE_LONG;
++ else
++ spec->type = FORMAT_TYPE_ULONG;
++ } else if (spec->qualifier == 'Z' || spec->qualifier == 'z') {
++ spec->type = FORMAT_TYPE_SIZE_T;
++ } else if (spec->qualifier == 't') {
++ spec->type = FORMAT_TYPE_PTRDIFF;
++ } else if (spec->qualifier == 'h') {
++ if (spec->flags & SIGN)
++ spec->type = FORMAT_TYPE_SHORT;
++ else
++ spec->type = FORMAT_TYPE_USHORT;
++ } else {
++ if (spec->flags & SIGN)
++ spec->type = FORMAT_TYPE_INT;
++ else
++ spec->type = FORMAT_TYPE_UINT;
++ }
++
++ return ++fmt - start;
+ }
+
+ /**
+@@ -642,18 +960,9 @@ static char *pointer(const char *fmt, ch
+ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+ {
+ unsigned long long num;
+- int base;
+ char *str, *end, c;
+-
+- int flags; /* flags to number() */
+-
+- int field_width; /* width of output field */
+- int precision; /* min. # of digits for integers; max
+- number of chars for from string */
+- int qualifier; /* 'h', 'l', or 'L' for integer fields */
+- /* 'z' support added 23/7/1999 S.H. */
+- /* 'z' changed to 'Z' --davidm 1/25/99 */
+- /* 't' added for ptrdiff_t */
++ int read;
++ struct printf_spec spec = {0};
+
+ /* Reject out-of-range values early. Large positive sizes are
+ used for unknown buffer sizes. */
+@@ -674,184 +983,137 @@ int vsnprintf(char *buf, size_t size, co
+ size = end - buf;
+ }
+
+- for (; *fmt ; ++fmt) {
+- if (*fmt != '%') {
+- if (str < end)
+- *str = *fmt;
+- ++str;
+- continue;
+- }
++ while (*fmt) {
++ const char *old_fmt = fmt;
+
+- /* process flags */
+- flags = 0;
+- repeat:
+- ++fmt; /* this also skips first '%' */
+- switch (*fmt) {
+- case '-': flags |= LEFT; goto repeat;
+- case '+': flags |= PLUS; goto repeat;
+- case ' ': flags |= SPACE; goto repeat;
+- case '#': flags |= SPECIAL; goto repeat;
+- case '0': flags |= ZEROPAD; goto repeat;
+- }
++ read = format_decode(fmt, &spec);
+
+- /* get field width */
+- field_width = -1;
+- if (isdigit(*fmt))
+- field_width = skip_atoi(&fmt);
+- else if (*fmt == '*') {
+- ++fmt;
+- /* it's the next argument */
+- field_width = va_arg(args, int);
+- if (field_width < 0) {
+- field_width = -field_width;
+- flags |= LEFT;
+- }
+- }
++ fmt += read;
+
+- /* get the precision */
+- precision = -1;
+- if (*fmt == '.') {
+- ++fmt;
+- if (isdigit(*fmt))
+- precision = skip_atoi(&fmt);
+- else if (*fmt == '*') {
+- ++fmt;
+- /* it's the next argument */
+- precision = va_arg(args, int);
++ switch (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 (precision < 0)
+- precision = 0;
++ str += read;
++ break;
+ }
+
+- /* get the conversion qualifier */
+- qualifier = -1;
+- if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+- *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
+- qualifier = *fmt;
+- ++fmt;
+- if (qualifier == 'l' && *fmt == 'l') {
+- qualifier = 'L';
+- ++fmt;
+- }
+- }
++ case FORMAT_TYPE_WIDTH:
++ spec.field_width = va_arg(args, int);
++ break;
+
+- /* default base */
+- base = 10;
++ case FORMAT_TYPE_PRECISION:
++ spec.precision = va_arg(args, int);
++ break;
+
+- switch (*fmt) {
+- case 'c':
+- if (!(flags & LEFT)) {
+- while (--field_width > 0) {
+- if (str < end)
+- *str = ' ';
+- ++str;
+- }
+- }
+- c = (unsigned char) va_arg(args, int);
+- if (str < end)
+- *str = c;
+- ++str;
+- while (--field_width > 0) {
++ case FORMAT_TYPE_CHAR:
++ if (!(spec.flags & LEFT)) {
++ while (--spec.field_width > 0) {
+ if (str < end)
+ *str = ' ';
+ ++str;
+- }
+- continue;
+-
+- case 's':
+- str = string(str, end, va_arg(args, char *), field_width, precision, flags);
+- continue;
+-
+- case 'p':
+- str = pointer(fmt+1, str, end,
+- va_arg(args, void *),
+- field_width, precision, flags);
+- /* Skip all alphanumeric pointer suffixes */
+- while (isalnum(fmt[1]))
+- fmt++;
+- continue;
+
+- case 'n':
+- /* FIXME:
+- * What does C99 say about the overflow case here? */
+- if (qualifier == 'l') {
+- long * ip = va_arg(args, long *);
+- *ip = (str - buf);
+- } else if (qualifier == 'Z' || qualifier == 'z') {
+- size_t * ip = va_arg(args, size_t *);
+- *ip = (str - buf);
+- } else {
+- int * ip = va_arg(args, int *);
+- *ip = (str - buf);
+ }
+- continue;
+-
+- case '%':
++ }
++ c = (unsigned char) va_arg(args, int);
++ if (str < end)
++ *str = c;
++ ++str;
++ while (--spec.field_width > 0) {
+ if (str < end)
+- *str = '%';
++ *str = ' ';
+ ++str;
+- continue;
++ }
++ break;
+
+- /* integer number formats - set up the flags and "break" */
+- case 'o':
+- base = 8;
+- break;
++ case FORMAT_TYPE_STR:
++ str = string(str, end, va_arg(args, char *), spec);
++ break;
+
+- case 'x':
+- flags |= SMALL;
+- case 'X':
+- base = 16;
+- break;
++ case FORMAT_TYPE_PTR:
++ str = pointer(fmt+1, str, end, va_arg(args, void *),
++ spec);
++ while (isalnum(*fmt))
++ fmt++;
++ break;
+
+- case 'd':
+- case 'i':
+- flags |= SIGN;
+- case 'u':
+- break;
++ case FORMAT_TYPE_PERCENT_CHAR:
++ if (str < end)
++ *str = '%';
++ ++str;
++ break;
+
+- default:
+- if (str < end)
+- *str = '%';
+- ++str;
+- if (*fmt) {
+- if (str < end)
+- *str = *fmt;
+- ++str;
+- } else {
+- --fmt;
+- }
+- continue;
++ case FORMAT_TYPE_INVALID:
++ if (str < end)
++ *str = '%';
++ ++str;
++ break;
++
++ case FORMAT_TYPE_NRCHARS: {
++ int qualifier = spec.qualifier;
++
++ if (qualifier == 'l') {
++ long *ip = va_arg(args, long *);
++ *ip = (str - buf);
++ } else if (qualifier == 'Z' ||
++ qualifier == 'z') {
++ size_t *ip = va_arg(args, size_t *);
++ *ip = (str - buf);
++ } else {
++ int *ip = va_arg(args, int *);
++ *ip = (str - buf);
++ }
++ break;
+ }
+- if (qualifier == 'L')
+- num = va_arg(args, long long);
+- else if (qualifier == 'l') {
+- num = va_arg(args, unsigned long);
+- if (flags & SIGN)
+- num = (signed long) num;
+- } else if (qualifier == 'Z' || qualifier == 'z') {
+- num = va_arg(args, size_t);
+- } else if (qualifier == 't') {
+- num = va_arg(args, ptrdiff_t);
+- } else if (qualifier == 'h') {
+- num = (unsigned short) va_arg(args, int);
+- if (flags & SIGN)
+- num = (signed short) num;
+- } else {
+- num = va_arg(args, unsigned int);
+- if (flags & SIGN)
+- num = (signed int) num;
++
++ 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:
++ num = va_arg(args, size_t);
++ break;
++ case FORMAT_TYPE_PTRDIFF:
++ num = va_arg(args, ptrdiff_t);
++ 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);
+ }
+- str = number(str, end, num, base,
+- field_width, precision, flags);
+ }
++
+ if (size > 0) {
+ if (str < end)
+ *str = '\0';
+ else
+ end[-1] = '\0';
+ }
++
+ /* the trailing null byte doesn't count towards the total */
+ return str-buf;
++
+ }
+ EXPORT_SYMBOL(vsnprintf);
+