backport a recent version of vsprintf to linux 2.6.28 to fix mac80211 wifi interface...
[openwrt.git] / target / linux / generic-2.6 / patches-2.6.28 / 981-vsprintf_backport.patch
1 --- a/lib/vsprintf.c
2 +++ b/lib/vsprintf.c
3 @@ -170,6 +170,8 @@ int strict_strtoul(const char *cp, unsig
4                 return -EINVAL;
5  
6         val = simple_strtoul(cp, &tail, base);
7 +       if (tail == cp)
8 +               return -EINVAL;
9         if ((*tail == '\0') ||
10                 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
11                 *res = val;
12 @@ -241,6 +243,8 @@ int strict_strtoull(const char *cp, unsi
13                 return -EINVAL;
14  
15         val = simple_strtoull(cp, &tail, base);
16 +       if (tail == cp)
17 +               return -EINVAL;
18         if ((*tail == '\0') ||
19                 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
20                 *res = val;
21 @@ -392,7 +396,38 @@ static noinline char* put_dec(char *buf,
22  #define SMALL  32              /* Must be 32 == 0x20 */
23  #define SPECIAL        64              /* 0x */
24  
25 -static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)
26 +enum format_type {
27 +       FORMAT_TYPE_NONE, /* Just a string part */
28 +       FORMAT_TYPE_WIDTH,
29 +       FORMAT_TYPE_PRECISION,
30 +       FORMAT_TYPE_CHAR,
31 +       FORMAT_TYPE_STR,
32 +       FORMAT_TYPE_PTR,
33 +       FORMAT_TYPE_PERCENT_CHAR,
34 +       FORMAT_TYPE_INVALID,
35 +       FORMAT_TYPE_LONG_LONG,
36 +       FORMAT_TYPE_ULONG,
37 +       FORMAT_TYPE_LONG,
38 +       FORMAT_TYPE_USHORT,
39 +       FORMAT_TYPE_SHORT,
40 +       FORMAT_TYPE_UINT,
41 +       FORMAT_TYPE_INT,
42 +       FORMAT_TYPE_NRCHARS,
43 +       FORMAT_TYPE_SIZE_T,
44 +       FORMAT_TYPE_PTRDIFF
45 +};
46 +
47 +struct printf_spec {
48 +       enum format_type        type;
49 +       int                     flags;          /* flags to number() */
50 +       int                     field_width;    /* width of output field */
51 +       int                     base;
52 +       int                     precision;      /* # of digits/chars */
53 +       int                     qualifier;
54 +};
55 +
56 +static char *number(char *buf, char *end, unsigned long long num,
57 +                       struct printf_spec spec)
58  {
59         /* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
60         static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
61 @@ -400,32 +435,32 @@ static char *number(char *buf, char *end
62         char tmp[66];
63         char sign;
64         char locase;
65 -       int need_pfx = ((type & SPECIAL) && base != 10);
66 +       int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
67         int i;
68  
69         /* locase = 0 or 0x20. ORing digits or letters with 'locase'
70          * produces same digits or (maybe lowercased) letters */
71 -       locase = (type & SMALL);
72 -       if (type & LEFT)
73 -               type &= ~ZEROPAD;
74 +       locase = (spec.flags & SMALL);
75 +       if (spec.flags & LEFT)
76 +               spec.flags &= ~ZEROPAD;
77         sign = 0;
78 -       if (type & SIGN) {
79 +       if (spec.flags & SIGN) {
80                 if ((signed long long) num < 0) {
81                         sign = '-';
82                         num = - (signed long long) num;
83 -                       size--;
84 -               } else if (type & PLUS) {
85 +                       spec.field_width--;
86 +               } else if (spec.flags & PLUS) {
87                         sign = '+';
88 -                       size--;
89 -               } else if (type & SPACE) {
90 +                       spec.field_width--;
91 +               } else if (spec.flags & SPACE) {
92                         sign = ' ';
93 -                       size--;
94 +                       spec.field_width--;
95                 }
96         }
97         if (need_pfx) {
98 -               size--;
99 -               if (base == 16)
100 -                       size--;
101 +               spec.field_width--;
102 +               if (spec.base == 16)
103 +                       spec.field_width--;
104         }
105  
106         /* generate full string in tmp[], in reverse order */
107 @@ -437,10 +472,10 @@ static char *number(char *buf, char *end
108                 tmp[i++] = (digits[do_div(num,base)] | locase);
109         } while (num != 0);
110         */
111 -       else if (base != 10) { /* 8 or 16 */
112 -               int mask = base - 1;
113 +       else if (spec.base != 10) { /* 8 or 16 */
114 +               int mask = spec.base - 1;
115                 int shift = 3;
116 -               if (base == 16) shift = 4;
117 +               if (spec.base == 16) shift = 4;
118                 do {
119                         tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
120                         num >>= shift;
121 @@ -450,12 +485,12 @@ static char *number(char *buf, char *end
122         }
123  
124         /* printing 100 using %2d gives "100", not "00" */
125 -       if (i > precision)
126 -               precision = i;
127 +       if (i > spec.precision)
128 +               spec.precision = i;
129         /* leading space padding */
130 -       size -= precision;
131 -       if (!(type & (ZEROPAD+LEFT))) {
132 -               while(--size >= 0) {
133 +       spec.field_width -= spec.precision;
134 +       if (!(spec.flags & (ZEROPAD+LEFT))) {
135 +               while(--spec.field_width >= 0) {
136                         if (buf < end)
137                                 *buf = ' ';
138                         ++buf;
139 @@ -472,23 +507,23 @@ static char *number(char *buf, char *end
140                 if (buf < end)
141                         *buf = '0';
142                 ++buf;
143 -               if (base == 16) {
144 +               if (spec.base == 16) {
145                         if (buf < end)
146                                 *buf = ('X' | locase);
147                         ++buf;
148                 }
149         }
150         /* zero or space padding */
151 -       if (!(type & LEFT)) {
152 -               char c = (type & ZEROPAD) ? '0' : ' ';
153 -               while (--size >= 0) {
154 +       if (!(spec.flags & LEFT)) {
155 +               char c = (spec.flags & ZEROPAD) ? '0' : ' ';
156 +               while (--spec.field_width >= 0) {
157                         if (buf < end)
158                                 *buf = c;
159                         ++buf;
160                 }
161         }
162         /* hmm even more zero padding? */
163 -       while (i <= --precision) {
164 +       while (i <= --spec.precision) {
165                 if (buf < end)
166                         *buf = '0';
167                 ++buf;
168 @@ -500,7 +535,7 @@ static char *number(char *buf, char *end
169                 ++buf;
170         }
171         /* trailing space padding */
172 -       while (--size >= 0) {
173 +       while (--spec.field_width >= 0) {
174                 if (buf < end)
175                         *buf = ' ';
176                 ++buf;
177 @@ -508,17 +543,17 @@ static char *number(char *buf, char *end
178         return buf;
179  }
180  
181 -static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
182 +static char *string(char *buf, char *end, char *s, struct printf_spec spec)
183  {
184         int len, i;
185  
186         if ((unsigned long)s < PAGE_SIZE)
187                 s = "<NULL>";
188  
189 -       len = strnlen(s, precision);
190 +       len = strnlen(s, spec.precision);
191  
192 -       if (!(flags & LEFT)) {
193 -               while (len < field_width--) {
194 +       if (!(spec.flags & LEFT)) {
195 +               while (len < spec.field_width--) {
196                         if (buf < end)
197                                 *buf = ' ';
198                         ++buf;
199 @@ -529,7 +564,7 @@ static char *string(char *buf, char *end
200                         *buf = *s;
201                 ++buf; ++s;
202         }
203 -       while (len < field_width--) {
204 +       while (len < spec.field_width--) {
205                 if (buf < end)
206                         *buf = ' ';
207                 ++buf;
208 @@ -537,21 +572,24 @@ static char *string(char *buf, char *end
209         return buf;
210  }
211  
212 -static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags)
213 +static char *symbol_string(char *buf, char *end, void *ptr,
214 +                               struct printf_spec spec)
215  {
216         unsigned long value = (unsigned long) ptr;
217  #ifdef CONFIG_KALLSYMS
218         char sym[KSYM_SYMBOL_LEN];
219         sprint_symbol(sym, value);
220 -       return string(buf, end, sym, field_width, precision, flags);
221 +       return string(buf, end, sym, spec);
222  #else
223 -       field_width = 2*sizeof(void *);
224 -       flags |= SPECIAL | SMALL | ZEROPAD;
225 -       return number(buf, end, value, 16, field_width, precision, flags);
226 +       spec.field_width = 2*sizeof(void *);
227 +       spec.flags |= SPECIAL | SMALL | ZEROPAD;
228 +       spec.base = 16;
229 +       return number(buf, end, value, spec);
230  #endif
231  }
232  
233 -static char *resource_string(char *buf, char *end, struct resource *res, int field_width, int precision, int flags)
234 +static char *resource_string(char *buf, char *end, struct resource *res,
235 +                               struct printf_spec spec)
236  {
237  #ifndef IO_RSRC_PRINTK_SIZE
238  #define IO_RSRC_PRINTK_SIZE    4
239 @@ -560,7 +598,11 @@ static char *resource_string(char *buf, 
240  #ifndef MEM_RSRC_PRINTK_SIZE
241  #define MEM_RSRC_PRINTK_SIZE   8
242  #endif
243 -
244 +       struct printf_spec num_spec = {
245 +               .base = 16,
246 +               .precision = -1,
247 +               .flags = SPECIAL | SMALL | ZEROPAD,
248 +       };
249         /* room for the actual numbers, the two "0x", -, [, ] and the final zero */
250         char sym[4*sizeof(resource_size_t) + 8];
251         char *p = sym, *pend = sym + sizeof(sym);
252 @@ -572,13 +614,73 @@ static char *resource_string(char *buf, 
253                 size = MEM_RSRC_PRINTK_SIZE;
254  
255         *p++ = '[';
256 -       p = number(p, pend, res->start, 16, size, -1, SPECIAL | SMALL | ZEROPAD);
257 +       num_spec.field_width = size;
258 +       p = number(p, pend, res->start, num_spec);
259         *p++ = '-';
260 -       p = number(p, pend, res->end, 16, size, -1, SPECIAL | SMALL | ZEROPAD);
261 +       p = number(p, pend, res->end, num_spec);
262         *p++ = ']';
263         *p = 0;
264  
265 -       return string(buf, end, sym, field_width, precision, flags);
266 +       return string(buf, end, sym, spec);
267 +}
268 +
269 +static char *mac_address_string(char *buf, char *end, u8 *addr,
270 +                               struct printf_spec spec)
271 +{
272 +       char mac_addr[6 * 3]; /* (6 * 2 hex digits), 5 colons and trailing zero */
273 +       char *p = mac_addr;
274 +       int i;
275 +
276 +       for (i = 0; i < 6; i++) {
277 +               p = pack_hex_byte(p, addr[i]);
278 +               if (!(spec.flags & SPECIAL) && i != 5)
279 +                       *p++ = ':';
280 +       }
281 +       *p = '\0';
282 +       spec.flags &= ~SPECIAL;
283 +
284 +       return string(buf, end, mac_addr, spec);
285 +}
286 +
287 +static char *ip6_addr_string(char *buf, char *end, u8 *addr,
288 +                               struct printf_spec spec)
289 +{
290 +       char ip6_addr[8 * 5]; /* (8 * 4 hex digits), 7 colons and trailing zero */
291 +       char *p = ip6_addr;
292 +       int i;
293 +
294 +       for (i = 0; i < 8; i++) {
295 +               p = pack_hex_byte(p, addr[2 * i]);
296 +               p = pack_hex_byte(p, addr[2 * i + 1]);
297 +               if (!(spec.flags & SPECIAL) && i != 7)
298 +                       *p++ = ':';
299 +       }
300 +       *p = '\0';
301 +       spec.flags &= ~SPECIAL;
302 +
303 +       return string(buf, end, ip6_addr, spec);
304 +}
305 +
306 +static char *ip4_addr_string(char *buf, char *end, u8 *addr,
307 +                               struct printf_spec spec)
308 +{
309 +       char ip4_addr[4 * 4]; /* (4 * 3 decimal digits), 3 dots and trailing zero */
310 +       char temp[3];   /* hold each IP quad in reverse order */
311 +       char *p = ip4_addr;
312 +       int i, digits;
313 +
314 +       for (i = 0; i < 4; i++) {
315 +               digits = put_dec_trunc(temp, addr[i]) - temp;
316 +               /* reverse the digits in the quad */
317 +               while (digits--)
318 +                       *p++ = temp[digits];
319 +               if (i != 3)
320 +                       *p++ = '.';
321 +       }
322 +       *p = '\0';
323 +       spec.flags &= ~SPECIAL;
324 +
325 +       return string(buf, end, ip4_addr, spec);
326  }
327  
328  /*
329 @@ -592,28 +694,244 @@ static char *resource_string(char *buf, 
330   * - 'S' For symbolic direct pointers
331   * - 'R' For a struct resource pointer, it prints the range of
332   *       addresses (not the name nor the flags)
333 + * - 'M' For a 6-byte MAC address, it prints the address in the
334 + *       usual colon-separated hex notation
335 + * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated
336 + *       decimal for v4 and colon separated network-order 16 bit hex for v6)
337 + * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
338 + *       currently the same
339   *
340   * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
341   * function pointers are really function descriptors, which contain a
342   * pointer to the real address.
343   */
344 -static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
345 +static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
346 +                       struct printf_spec spec)
347  {
348 +       if (!ptr)
349 +               return string(buf, end, "(null)", spec);
350 +
351         switch (*fmt) {
352         case 'F':
353                 ptr = dereference_function_descriptor(ptr);
354                 /* Fallthrough */
355         case 'S':
356 -               return symbol_string(buf, end, ptr, field_width, precision, flags);
357 +               return symbol_string(buf, end, ptr, spec);
358         case 'R':
359 -               return resource_string(buf, end, ptr, field_width, precision, flags);
360 +               return resource_string(buf, end, ptr, spec);
361 +       case 'm':
362 +               spec.flags |= SPECIAL;
363 +               /* Fallthrough */
364 +       case 'M':
365 +               return mac_address_string(buf, end, ptr, spec);
366 +       case 'i':
367 +               spec.flags |= SPECIAL;
368 +               /* Fallthrough */
369 +       case 'I':
370 +               if (fmt[1] == '6')
371 +                       return ip6_addr_string(buf, end, ptr, spec);
372 +               if (fmt[1] == '4')
373 +                       return ip4_addr_string(buf, end, ptr, spec);
374 +               spec.flags &= ~SPECIAL;
375 +               break;
376         }
377 -       flags |= SMALL;
378 -       if (field_width == -1) {
379 -               field_width = 2*sizeof(void *);
380 -               flags |= ZEROPAD;
381 +       spec.flags |= SMALL;
382 +       if (spec.field_width == -1) {
383 +               spec.field_width = 2*sizeof(void *);
384 +               spec.flags |= ZEROPAD;
385         }
386 -       return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
387 +       spec.base = 16;
388 +
389 +       return number(buf, end, (unsigned long) ptr, spec);
390 +}
391 +
392 +/*
393 + * Helper function to decode printf style format.
394 + * Each call decode a token from the format and return the
395 + * number of characters read (or likely the delta where it wants
396 + * to go on the next call).
397 + * The decoded token is returned through the parameters
398 + *
399 + * 'h', 'l', or 'L' for integer fields
400 + * 'z' support added 23/7/1999 S.H.
401 + * 'z' changed to 'Z' --davidm 1/25/99
402 + * 't' added for ptrdiff_t
403 + *
404 + * @fmt: the format string
405 + * @type of the token returned
406 + * @flags: various flags such as +, -, # tokens..
407 + * @field_width: overwritten width
408 + * @base: base of the number (octal, hex, ...)
409 + * @precision: precision of a number
410 + * @qualifier: qualifier of a number (long, size_t, ...)
411 + */
412 +static int format_decode(const char *fmt, struct printf_spec *spec)
413 +{
414 +       const char *start = fmt;
415 +
416 +       /* we finished early by reading the field width */
417 +       if (spec->type == FORMAT_TYPE_WIDTH) {
418 +               if (spec->field_width < 0) {
419 +                       spec->field_width = -spec->field_width;
420 +                       spec->flags |= LEFT;
421 +               }
422 +               spec->type = FORMAT_TYPE_NONE;
423 +               goto precision;
424 +       }
425 +
426 +       /* we finished early by reading the precision */
427 +       if (spec->type == FORMAT_TYPE_PRECISION) {
428 +               if (spec->precision < 0)
429 +                       spec->precision = 0;
430 +
431 +               spec->type = FORMAT_TYPE_NONE;
432 +               goto qualifier;
433 +       }
434 +
435 +       /* By default */
436 +       spec->type = FORMAT_TYPE_NONE;
437 +
438 +       for (; *fmt ; ++fmt) {
439 +               if (*fmt == '%')
440 +                       break;
441 +       }
442 +
443 +       /* Return the current non-format string */
444 +       if (fmt != start || !*fmt)
445 +               return fmt - start;
446 +
447 +       /* Process flags */
448 +       spec->flags = 0;
449 +
450 +       while (1) { /* this also skips first '%' */
451 +               bool found = true;
452 +
453 +               ++fmt;
454 +
455 +               switch (*fmt) {
456 +               case '-': spec->flags |= LEFT;    break;
457 +               case '+': spec->flags |= PLUS;    break;
458 +               case ' ': spec->flags |= SPACE;   break;
459 +               case '#': spec->flags |= SPECIAL; break;
460 +               case '0': spec->flags |= ZEROPAD; break;
461 +               default:  found = false;
462 +               }
463 +
464 +               if (!found)
465 +                       break;
466 +       }
467 +
468 +       /* get field width */
469 +       spec->field_width = -1;
470 +
471 +       if (isdigit(*fmt))
472 +               spec->field_width = skip_atoi(&fmt);
473 +       else if (*fmt == '*') {
474 +               /* it's the next argument */
475 +               spec->type = FORMAT_TYPE_WIDTH;
476 +               return ++fmt - start;
477 +       }
478 +
479 +precision:
480 +       /* get the precision */
481 +       spec->precision = -1;
482 +       if (*fmt == '.') {
483 +               ++fmt;
484 +               if (isdigit(*fmt)) {
485 +                       spec->precision = skip_atoi(&fmt);
486 +                       if (spec->precision < 0)
487 +                               spec->precision = 0;
488 +               } else if (*fmt == '*') {
489 +                       /* it's the next argument */
490 +                       spec->type = FORMAT_TYPE_PRECISION;
491 +                       return ++fmt - start;
492 +               }
493 +       }
494 +
495 +qualifier:
496 +       /* get the conversion qualifier */
497 +       spec->qualifier = -1;
498 +       if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
499 +           *fmt == 'Z' || *fmt == 'z' || *fmt == 't') {
500 +               spec->qualifier = *fmt;
501 +               ++fmt;
502 +               if (spec->qualifier == 'l' && *fmt == 'l') {
503 +                       spec->qualifier = 'L';
504 +                       ++fmt;
505 +               }
506 +       }
507 +
508 +       /* default base */
509 +       spec->base = 10;
510 +       switch (*fmt) {
511 +       case 'c':
512 +               spec->type = FORMAT_TYPE_CHAR;
513 +               return ++fmt - start;
514 +
515 +       case 's':
516 +               spec->type = FORMAT_TYPE_STR;
517 +               return ++fmt - start;
518 +
519 +       case 'p':
520 +               spec->type = FORMAT_TYPE_PTR;
521 +               return fmt - start;
522 +               /* skip alnum */
523 +
524 +       case 'n':
525 +               spec->type = FORMAT_TYPE_NRCHARS;
526 +               return ++fmt - start;
527 +
528 +       case '%':
529 +               spec->type = FORMAT_TYPE_PERCENT_CHAR;
530 +               return ++fmt - start;
531 +
532 +       /* integer number formats - set up the flags and "break" */
533 +       case 'o':
534 +               spec->base = 8;
535 +               break;
536 +
537 +       case 'x':
538 +               spec->flags |= SMALL;
539 +
540 +       case 'X':
541 +               spec->base = 16;
542 +               break;
543 +
544 +       case 'd':
545 +       case 'i':
546 +               spec->flags |= SIGN;
547 +       case 'u':
548 +               break;
549 +
550 +       default:
551 +               spec->type = FORMAT_TYPE_INVALID;
552 +               return fmt - start;
553 +       }
554 +
555 +       if (spec->qualifier == 'L')
556 +               spec->type = FORMAT_TYPE_LONG_LONG;
557 +       else if (spec->qualifier == 'l') {
558 +               if (spec->flags & SIGN)
559 +                       spec->type = FORMAT_TYPE_LONG;
560 +               else
561 +                       spec->type = FORMAT_TYPE_ULONG;
562 +       } else if (spec->qualifier == 'Z' || spec->qualifier == 'z') {
563 +               spec->type = FORMAT_TYPE_SIZE_T;
564 +       } else if (spec->qualifier == 't') {
565 +               spec->type = FORMAT_TYPE_PTRDIFF;
566 +       } else if (spec->qualifier == 'h') {
567 +               if (spec->flags & SIGN)
568 +                       spec->type = FORMAT_TYPE_SHORT;
569 +               else
570 +                       spec->type = FORMAT_TYPE_USHORT;
571 +       } else {
572 +               if (spec->flags & SIGN)
573 +                       spec->type = FORMAT_TYPE_INT;
574 +               else
575 +                       spec->type = FORMAT_TYPE_UINT;
576 +       }
577 +
578 +       return ++fmt - start;
579  }
580  
581  /**
582 @@ -642,18 +960,9 @@ static char *pointer(const char *fmt, ch
583  int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
584  {
585         unsigned long long num;
586 -       int base;
587         char *str, *end, c;
588 -
589 -       int flags;              /* flags to number() */
590 -
591 -       int field_width;        /* width of output field */
592 -       int precision;          /* min. # of digits for integers; max
593 -                                  number of chars for from string */
594 -       int qualifier;          /* 'h', 'l', or 'L' for integer fields */
595 -                               /* 'z' support added 23/7/1999 S.H.    */
596 -                               /* 'z' changed to 'Z' --davidm 1/25/99 */
597 -                               /* 't' added for ptrdiff_t */
598 +       int read;
599 +       struct printf_spec spec = {0};
600  
601         /* Reject out-of-range values early.  Large positive sizes are
602            used for unknown buffer sizes. */
603 @@ -674,184 +983,137 @@ int vsnprintf(char *buf, size_t size, co
604                 size = end - buf;
605         }
606  
607 -       for (; *fmt ; ++fmt) {
608 -               if (*fmt != '%') {
609 -                       if (str < end)
610 -                               *str = *fmt;
611 -                       ++str;
612 -                       continue;
613 -               }
614 +       while (*fmt) {
615 +               const char *old_fmt = fmt;
616  
617 -               /* process flags */
618 -               flags = 0;
619 -               repeat:
620 -                       ++fmt;          /* this also skips first '%' */
621 -                       switch (*fmt) {
622 -                               case '-': flags |= LEFT; goto repeat;
623 -                               case '+': flags |= PLUS; goto repeat;
624 -                               case ' ': flags |= SPACE; goto repeat;
625 -                               case '#': flags |= SPECIAL; goto repeat;
626 -                               case '0': flags |= ZEROPAD; goto repeat;
627 -                       }
628 +               read = format_decode(fmt, &spec);
629  
630 -               /* get field width */
631 -               field_width = -1;
632 -               if (isdigit(*fmt))
633 -                       field_width = skip_atoi(&fmt);
634 -               else if (*fmt == '*') {
635 -                       ++fmt;
636 -                       /* it's the next argument */
637 -                       field_width = va_arg(args, int);
638 -                       if (field_width < 0) {
639 -                               field_width = -field_width;
640 -                               flags |= LEFT;
641 -                       }
642 -               }
643 +               fmt += read;
644  
645 -               /* get the precision */
646 -               precision = -1;
647 -               if (*fmt == '.') {
648 -                       ++fmt;  
649 -                       if (isdigit(*fmt))
650 -                               precision = skip_atoi(&fmt);
651 -                       else if (*fmt == '*') {
652 -                               ++fmt;
653 -                               /* it's the next argument */
654 -                               precision = va_arg(args, int);
655 +               switch (spec.type) {
656 +               case FORMAT_TYPE_NONE: {
657 +                       int copy = read;
658 +                       if (str < end) {
659 +                               if (copy > end - str)
660 +                                       copy = end - str;
661 +                               memcpy(str, old_fmt, copy);
662                         }
663 -                       if (precision < 0)
664 -                               precision = 0;
665 +                       str += read;
666 +                       break;
667                 }
668  
669 -               /* get the conversion qualifier */
670 -               qualifier = -1;
671 -               if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
672 -                   *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
673 -                       qualifier = *fmt;
674 -                       ++fmt;
675 -                       if (qualifier == 'l' && *fmt == 'l') {
676 -                               qualifier = 'L';
677 -                               ++fmt;
678 -                       }
679 -               }
680 +               case FORMAT_TYPE_WIDTH:
681 +                       spec.field_width = va_arg(args, int);
682 +                       break;
683  
684 -               /* default base */
685 -               base = 10;
686 +               case FORMAT_TYPE_PRECISION:
687 +                       spec.precision = va_arg(args, int);
688 +                       break;
689  
690 -               switch (*fmt) {
691 -                       case 'c':
692 -                               if (!(flags & LEFT)) {
693 -                                       while (--field_width > 0) {
694 -                                               if (str < end)
695 -                                                       *str = ' ';
696 -                                               ++str;
697 -                                       }
698 -                               }
699 -                               c = (unsigned char) va_arg(args, int);
700 -                               if (str < end)
701 -                                       *str = c;
702 -                               ++str;
703 -                               while (--field_width > 0) {
704 +               case FORMAT_TYPE_CHAR:
705 +                       if (!(spec.flags & LEFT)) {
706 +                               while (--spec.field_width > 0) {
707                                         if (str < end)
708                                                 *str = ' ';
709                                         ++str;
710 -                               }
711 -                               continue;
712 -
713 -                       case 's':
714 -                               str = string(str, end, va_arg(args, char *), field_width, precision, flags);
715 -                               continue;
716 -
717 -                       case 'p':
718 -                               str = pointer(fmt+1, str, end,
719 -                                               va_arg(args, void *),
720 -                                               field_width, precision, flags);
721 -                               /* Skip all alphanumeric pointer suffixes */
722 -                               while (isalnum(fmt[1]))
723 -                                       fmt++;
724 -                               continue;
725  
726 -                       case 'n':
727 -                               /* FIXME:
728 -                               * What does C99 say about the overflow case here? */
729 -                               if (qualifier == 'l') {
730 -                                       long * ip = va_arg(args, long *);
731 -                                       *ip = (str - buf);
732 -                               } else if (qualifier == 'Z' || qualifier == 'z') {
733 -                                       size_t * ip = va_arg(args, size_t *);
734 -                                       *ip = (str - buf);
735 -                               } else {
736 -                                       int * ip = va_arg(args, int *);
737 -                                       *ip = (str - buf);
738                                 }
739 -                               continue;
740 -
741 -                       case '%':
742 +                       }
743 +                       c = (unsigned char) va_arg(args, int);
744 +                       if (str < end)
745 +                               *str = c;
746 +                       ++str;
747 +                       while (--spec.field_width > 0) {
748                                 if (str < end)
749 -                                       *str = '%';
750 +                                       *str = ' ';
751                                 ++str;
752 -                               continue;
753 +                       }
754 +                       break;
755  
756 -                               /* integer number formats - set up the flags and "break" */
757 -                       case 'o':
758 -                               base = 8;
759 -                               break;
760 +               case FORMAT_TYPE_STR:
761 +                       str = string(str, end, va_arg(args, char *), spec);
762 +                       break;
763  
764 -                       case 'x':
765 -                               flags |= SMALL;
766 -                       case 'X':
767 -                               base = 16;
768 -                               break;
769 +               case FORMAT_TYPE_PTR:
770 +                       str = pointer(fmt+1, str, end, va_arg(args, void *),
771 +                                     spec);
772 +                       while (isalnum(*fmt))
773 +                               fmt++;
774 +                       break;
775  
776 -                       case 'd':
777 -                       case 'i':
778 -                               flags |= SIGN;
779 -                       case 'u':
780 -                               break;
781 +               case FORMAT_TYPE_PERCENT_CHAR:
782 +                       if (str < end)
783 +                               *str = '%';
784 +                       ++str;
785 +                       break;
786  
787 -                       default:
788 -                               if (str < end)
789 -                                       *str = '%';
790 -                               ++str;
791 -                               if (*fmt) {
792 -                                       if (str < end)
793 -                                               *str = *fmt;
794 -                                       ++str;
795 -                               } else {
796 -                                       --fmt;
797 -                               }
798 -                               continue;
799 +               case FORMAT_TYPE_INVALID:
800 +                       if (str < end)
801 +                               *str = '%';
802 +                       ++str;
803 +                       break;
804 +
805 +               case FORMAT_TYPE_NRCHARS: {
806 +                       int qualifier = spec.qualifier;
807 +
808 +                       if (qualifier == 'l') {
809 +                               long *ip = va_arg(args, long *);
810 +                               *ip = (str - buf);
811 +                       } else if (qualifier == 'Z' ||
812 +                                       qualifier == 'z') {
813 +                               size_t *ip = va_arg(args, size_t *);
814 +                               *ip = (str - buf);
815 +                       } else {
816 +                               int *ip = va_arg(args, int *);
817 +                               *ip = (str - buf);
818 +                       }
819 +                       break;
820                 }
821 -               if (qualifier == 'L')
822 -                       num = va_arg(args, long long);
823 -               else if (qualifier == 'l') {
824 -                       num = va_arg(args, unsigned long);
825 -                       if (flags & SIGN)
826 -                               num = (signed long) num;
827 -               } else if (qualifier == 'Z' || qualifier == 'z') {
828 -                       num = va_arg(args, size_t);
829 -               } else if (qualifier == 't') {
830 -                       num = va_arg(args, ptrdiff_t);
831 -               } else if (qualifier == 'h') {
832 -                       num = (unsigned short) va_arg(args, int);
833 -                       if (flags & SIGN)
834 -                               num = (signed short) num;
835 -               } else {
836 -                       num = va_arg(args, unsigned int);
837 -                       if (flags & SIGN)
838 -                               num = (signed int) num;
839 +
840 +               default:
841 +                       switch (spec.type) {
842 +                       case FORMAT_TYPE_LONG_LONG:
843 +                               num = va_arg(args, long long);
844 +                               break;
845 +                       case FORMAT_TYPE_ULONG:
846 +                               num = va_arg(args, unsigned long);
847 +                               break;
848 +                       case FORMAT_TYPE_LONG:
849 +                               num = va_arg(args, long);
850 +                               break;
851 +                       case FORMAT_TYPE_SIZE_T:
852 +                               num = va_arg(args, size_t);
853 +                               break;
854 +                       case FORMAT_TYPE_PTRDIFF:
855 +                               num = va_arg(args, ptrdiff_t);
856 +                               break;
857 +                       case FORMAT_TYPE_USHORT:
858 +                               num = (unsigned short) va_arg(args, int);
859 +                               break;
860 +                       case FORMAT_TYPE_SHORT:
861 +                               num = (short) va_arg(args, int);
862 +                               break;
863 +                       case FORMAT_TYPE_INT:
864 +                               num = (int) va_arg(args, int);
865 +                               break;
866 +                       default:
867 +                               num = va_arg(args, unsigned int);
868 +                       }
869 +
870 +                       str = number(str, end, num, spec);
871                 }
872 -               str = number(str, end, num, base,
873 -                               field_width, precision, flags);
874         }
875 +
876         if (size > 0) {
877                 if (str < end)
878                         *str = '\0';
879                 else
880                         end[-1] = '\0';
881         }
882 +
883         /* the trailing null byte doesn't count towards the total */
884         return str-buf;
885 +
886  }
887  EXPORT_SYMBOL(vsnprintf);
888