From: Felix Fietkau <nbd@openwrt.org>
Date: Mon, 9 Nov 2009 00:58:44 +0000 (+0000)
Subject: backport a recent version of vsprintf to linux 2.6.28 to fix mac80211 wifi interface... 
X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=ee937398f7919863625558d6a4b6a7fb5cee2186;p=openwrt%2Fstaging%2Flinusw.git

backport a recent version of vsprintf to linux 2.6.28 to fix mac80211 wifi interface detection

SVN-Revision: 18352
---

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);
+