Prepare v2024.10
[platform/kernel/u-boot.git] / lib / sscanf.c
1 // SPDX-License-Identifier: BSD-3-Clause
2 /*
3  * Copyright (c) 1990, 1993
4  * The Regents of the University of California. All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Chris Torek.
8  *
9  * Copyright (c) 2011 The FreeBSD Foundation
10  * All rights reserved.
11  * Portions of this software were developed by David Chisnall
12  * under sponsorship from the FreeBSD Foundation.
13  *
14  * Author: Juergen Gross <jgross@suse.com>
15  * Date: Jun 2016
16  */
17
18 #if !defined HAVE_LIBC
19
20 #include <os.h>
21 #include <linux/kernel.h>
22 #include <linux/ctype.h>
23 #include <vsprintf.h>
24 #include <linux/string.h>
25 #include <malloc.h>
26 #define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
27
28 /**
29  * struct str_info - Input string parameters
30  * @neg: negative number or not
31  *       0 - not negative
32  *       1 - negative
33  * @any: set any if any `digits' consumed; make it negative to indicate
34  *       overflow
35  * @acc: accumulated value
36  */
37 struct str_info {
38         int neg, any;
39         u64 acc;
40 };
41
42 /**
43  * str_to_int_convert() - Write string data to structure
44  * @nptr: pointer to string
45  * @base: number's base
46  * @unsign: describes what integer is expected
47  *          0 - not unsigned
48  *          1 - unsigned
49  *
50  * Ignores `locale' stuff.  Assumes that the upper and lower case
51  * alphabets and digits are each contiguous.
52  *
53  * Return: struct str_info *, which contains string data to future process
54  */
55 static struct str_info *
56 str_to_int_convert(const char **nptr, int base, unsigned int unsign)
57 {
58         const char *s = *nptr;
59         u64 acc;
60         unsigned char c;
61         u64 cutoff;
62         int neg, any, cutlim;
63         u64 qbase;
64         struct str_info *info;
65
66         /*
67          * Skip white space and pick up leading +/- sign if any.
68          * If base is 0, allow 0x for hex and 0 for octal, else
69          * assume decimal; if base is already 16, allow 0x.
70          */
71         info = (struct str_info *)malloc(sizeof(struct str_info));
72         if (!info)
73                 return NULL;
74
75         do {
76                 c = *s++;
77         } while (isspace(c));
78         if (c == '-') {
79                 neg = 1;
80                 c = *s++;
81         } else {
82                 neg = 0;
83                 if (c == '+')
84                         c = *s++;
85         }
86         if ((base == 0 || base == 16) &&
87             c == '0' && (*s == 'x' || *s == 'X')) {
88                 c = s[1];
89                 s += 2;
90                 base = 16;
91         }
92         if (base == 0)
93                 base = c == '0' ? 8 : 10;
94
95         /*
96          * Compute the cutoff value between legal numbers and illegal
97          * numbers.  That is the largest legal value, divided by the
98          * base.  An input number that is greater than this value, if
99          * followed by a legal input character, is too big.  One that
100          * is equal to this value may be valid or not; the limit
101          * between valid and invalid numbers is then based on the last
102          * digit.  For instance, if the range for quads is
103          * [-9223372036854775808..9223372036854775807] and the input base
104          * is 10, cutoff will be set to 922337203685477580 and cutlim to
105          * either 7 (neg==0) or 8 (neg==1), meaning that if we have
106          * accumulated a value > 922337203685477580, or equal but the
107          * next digit is > 7 (or 8), the number is too big, and we will
108          * return a range error.
109          *
110          * Set any if any `digits' consumed; make it negative to indicate
111          * overflow.
112          */
113         qbase = (unsigned int)base;
114
115         if (!unsign) {
116                 cutoff = neg ? (u64)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX;
117                 cutlim = cutoff % qbase;
118                 cutoff /= qbase;
119         } else {
120                 cutoff = (u64)ULLONG_MAX / qbase;
121                 cutlim = (u64)ULLONG_MAX % qbase;
122         }
123
124         for (acc = 0, any = 0;; c = *s++) {
125                 if (!isascii(c))
126                         break;
127                 if (isdigit(c))
128                         c -= '0';
129                 else if (isalpha(c))
130                         c -= isupper(c) ? 'A' - 10 : 'a' - 10;
131                 else
132                         break;
133                 if (c >= base)
134                         break;
135                 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) {
136                         any = -1;
137                 } else {
138                         any = 1;
139                         acc *= qbase;
140                         acc += c;
141                 }
142         }
143
144         info->any = any;
145         info->neg = neg;
146         info->acc = acc;
147
148         *nptr = s;
149
150         return info;
151 }
152
153 /**
154  * strtoq() - Convert a string to a quad integer
155  * @nptr: pointer to string
156  * @endptr: pointer to number's end in the string
157  * @base: number's base
158  *
159  * Return: s64 quad integer number converted from input string
160  */
161 static s64
162 strtoq(const char *nptr, char **endptr, int base)
163 {
164         const char *s = nptr;
165         u64 acc;
166         int unsign = 0;
167         struct str_info *info;
168
169         info = str_to_int_convert(&s, base, unsign);
170         if (!info)
171                 return -1;
172
173         acc = info->acc;
174
175         if (info->any < 0)
176                 acc = info->neg ? LLONG_MIN : LLONG_MAX;
177         else if (info->neg)
178                 acc = -acc;
179         if (endptr != 0)
180                 *endptr = __DECONST(char *, info->any ? s - 1 : nptr);
181
182         free(info);
183
184         return acc;
185 }
186
187 /**
188  * strtouq() - Convert a string to an unsigned quad integer
189  * @nptr: pointer to string
190  * @endptr: pointer to number's end in the string
191  * @base: number's base
192  *
193  * Return: s64 unsigned quad integer number converted from
194  *         input string
195  */
196 u64
197 strtouq(const char *nptr, char **endptr, int base)
198 {
199                 const char *s = nptr;
200         u64 acc;
201         int unsign = 1;
202         struct str_info *info;
203
204         info = str_to_int_convert(&s, base, unsign);
205         if (!info)
206                 return -1;
207
208         acc = info->acc;
209
210         if (info->any < 0)
211                 acc = ULLONG_MAX;
212         else if (info->neg)
213                 acc = -acc;
214         if (endptr != 0)
215                 *endptr = __DECONST(char *, info->any ? s - 1 : nptr);
216
217         free(info);
218
219         return acc;
220 }
221
222 /**
223  * __sccl() - Fill in the given table from the scanset at the given format
224  * (just after `[')
225  * @tab: table to fill in
226  * @fmt: format of buffer
227  *
228  * The table has a 1 wherever characters should be considered part of the
229  * scanset.
230  *
231  * Return: pointer to the character past the closing `]'
232  */
233 static const u_char *
234 __sccl(char *tab, const u_char *fmt)
235 {
236         int c, n, v;
237
238         /* first `clear' the whole table */
239         c = *fmt++;             /* first char hat => negated scanset */
240         if (c == '^') {
241                 v = 1;          /* default => accept */
242                 c = *fmt++;     /* get new first char */
243         } else {
244                 v = 0;          /* default => reject */
245         }
246
247         /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
248         for (n = 0; n < 256; n++)
249                 tab[n] = v;        /* memset(tab, v, 256) */
250
251         if (c == 0)
252                 return (fmt - 1);/* format ended before closing ] */
253
254         /*
255          * Now set the entries corresponding to the actual scanset
256          * to the opposite of the above.
257          *
258          * The first character may be ']' (or '-') without being special;
259          * the last character may be '-'.
260          */
261         v = 1 - v;
262         for (;;) {
263                 tab[c] = v;             /* take character c */
264 doswitch:
265                 n = *fmt++;             /* and examine the next */
266                 switch (n) {
267                 case 0:                 /* format ended too soon */
268                         return (fmt - 1);
269
270                 case '-':
271                         /*
272                          * A scanset of the form
273                          *      [01+-]
274                          * is defined as `the digit 0, the digit 1,
275                          * the character +, the character -', but
276                          * the effect of a scanset such as
277                          *      [a-zA-Z0-9]
278                          * is implementation defined.  The V7 Unix
279                          * scanf treats `a-z' as `the letters a through
280                          * z', but treats `a-a' as `the letter a, the
281                          * character -, and the letter a'.
282                          *
283                          * For compatibility, the `-' is not considerd
284                          * to define a range if the character following
285                          * it is either a close bracket (required by ANSI)
286                          * or is not numerically greater than the character
287                          * we just stored in the table (c).
288                          */
289                         n = *fmt;
290                         if (n == ']' || n < c) {
291                                 c = '-';
292                                 break;  /* resume the for(;;) */
293                         }
294                         fmt++;
295                         /* fill in the range */
296                         do {
297                                 tab[++c] = v;
298                         } while (c < n);
299                         c = n;
300                         /*
301                          * Alas, the V7 Unix scanf also treats formats
302                          * such as [a-c-e] as `the letters a through e'.
303                          * This too is permitted by the standard....
304                          */
305                         goto doswitch;
306                         break;
307
308                 case ']':               /* end of scanset */
309                         return (fmt);
310
311                 default:                /* just another character */
312                         c = n;
313                         break;
314                 }
315         }
316         /* NOTREACHED */
317 }
318
319 /**
320  * vsscanf - Unformat a buffer into a list of arguments
321  * @buf:        input buffer
322  * @fmt:        format of buffer
323  * @args:       arguments
324  */
325 #define BUF             32      /* Maximum length of numeric string. */
326
327 /*
328  * Flags used during conversion.
329  */
330 #define LONG            0x01    /* l: long or double */
331 #define SHORT           0x04    /* h: short */
332 #define SUPPRESS        0x08    /* suppress assignment */
333 #define POINTER         0x10    /* weird %p pointer (`fake hex') */
334 #define NOSKIP          0x20    /* do not skip blanks */
335 #define QUAD            0x400
336 #define SHORTSHORT      0x4000  /** hh: char */
337
338 /*
339  * The following are used in numeric conversions only:
340  * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
341  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
342  */
343 #define SIGNOK          0x40    /* +/- is (still) legal */
344 #define NDIGITS         0x80    /* no digits detected */
345
346 #define DPTOK           0x100   /* (float) decimal point is still legal */
347 #define EXPOK           0x200   /* (float) exponent (e+3, etc) still legal */
348
349 #define PFXOK           0x100   /* 0x prefix is (still) legal */
350 #define NZDIGITS        0x200   /* no zero digits detected */
351
352 /*
353  * Conversion types.
354  */
355 #define CT_CHAR         0       /* %c conversion */
356 #define CT_CCL          1       /* %[...] conversion */
357 #define CT_STRING       2       /* %s conversion */
358 #define CT_INT          3       /* integer, i.e., strtoq or strtouq */
359 typedef u64 (*ccfntype)(const char *, char **, int);
360
361 int
362 vsscanf(const char *inp, char const *fmt0, va_list ap)
363 {
364         int inr;
365         const u_char *fmt = (const u_char *)fmt0;
366         int c;                  /* character from format, or conversion */
367         size_t width;           /* field width, or 0 */
368         char *p;                /* points into all kinds of strings */
369         int n;                  /* handy integer */
370         int flags;              /* flags as defined above */
371         char *p0;               /* saves original value of p when necessary */
372         int nassigned;          /* number of fields assigned */
373         int nconversions;       /* number of conversions */
374         int nread;              /* number of characters consumed from fp */
375         int base;               /* base argument to strtoq/strtouq */
376         ccfntype ccfn;          /* conversion function (strtoq/strtouq) */
377         char ccltab[256];       /* character class table for %[...] */
378         char buf[BUF];          /* buffer for numeric conversions */
379
380         /* `basefix' is used to avoid `if' tests in the integer scanner */
381         static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
382                                      12, 13, 14, 15, 16 };
383
384         inr = strlen(inp);
385
386         nassigned = 0;
387         nconversions = 0;
388         nread = 0;
389         base = 0;               /* XXX just to keep gcc happy */
390         ccfn = NULL;            /* XXX just to keep gcc happy */
391         for (;;) {
392                 c = *fmt++;
393                 if (c == 0)
394                         return (nassigned);
395                 if (isspace(c)) {
396                         while (inr > 0 && isspace(*inp))
397                                 nread++, inr--, inp++;
398                         continue;
399                 }
400                 if (c != '%')
401                         goto literal;
402                 width = 0;
403                 flags = 0;
404                 /*
405                  * switch on the format.  continue if done;
406                  * break once format type is derived.
407                  */
408 again:          c = *fmt++;
409                 switch (c) {
410                 case '%':
411 literal:
412                         if (inr <= 0)
413                                 goto input_failure;
414                         if (*inp != c)
415                                 goto match_failure;
416                         inr--, inp++;
417                         nread++;
418                         continue;
419
420                 case '*':
421                         flags |= SUPPRESS;
422                         goto again;
423                 case 'l':
424                         if (flags & LONG) {
425                                 flags &= ~LONG;
426                                 flags |= QUAD;
427                         } else {
428                                 flags |= LONG;
429                         }
430                         goto again;
431                 case 'q':
432                         flags |= QUAD;
433                         goto again;
434                 case 'h':
435                         if (flags & SHORT) {
436                                 flags &= ~SHORT;
437                                 flags |= SHORTSHORT;
438                         } else {
439                                 flags |= SHORT;
440                         }
441                         goto again;
442
443                 case '0': case '1': case '2': case '3': case '4':
444                 case '5': case '6': case '7': case '8': case '9':
445                         width = width * 10 + c - '0';
446                         goto again;
447
448                 /*
449                  * Conversions.
450                  *
451                  */
452                 case 'd':
453                         c = CT_INT;
454                         ccfn = (ccfntype)strtoq;
455                         base = 10;
456                         break;
457
458                 case 'i':
459                         c = CT_INT;
460                         ccfn = (ccfntype)strtoq;
461                         base = 0;
462                         break;
463
464                 case 'o':
465                         c = CT_INT;
466                         ccfn = strtouq;
467                         base = 8;
468                         break;
469
470                 case 'u':
471                         c = CT_INT;
472                         ccfn = strtouq;
473                         base = 10;
474                         break;
475
476                 case 'x':
477                         flags |= PFXOK; /* enable 0x prefixing */
478                         c = CT_INT;
479                         ccfn = strtouq;
480                         base = 16;
481                         break;
482
483                 case 's':
484                         c = CT_STRING;
485                         break;
486
487                 case '[':
488                         fmt = __sccl(ccltab, fmt);
489                         flags |= NOSKIP;
490                         c = CT_CCL;
491                         break;
492
493                 case 'c':
494                         flags |= NOSKIP;
495                         c = CT_CHAR;
496                         break;
497
498                 case 'p':       /* pointer format is like hex */
499                         flags |= POINTER | PFXOK;
500                         c = CT_INT;
501                         ccfn = strtouq;
502                         base = 16;
503                         break;
504
505                 case 'n':
506                         nconversions++;
507                         if (flags & SUPPRESS)   /* ??? */
508                                 continue;
509                         if (flags & SHORTSHORT)
510                                 *va_arg(ap, char *) = nread;
511                         else if (flags & SHORT)
512                                 *va_arg(ap, short *) = nread;
513                         else if (flags & LONG)
514                                 *va_arg(ap, long *) = nread;
515                         else if (flags & QUAD)
516                                 *va_arg(ap, s64 *) = nread;
517                         else
518                                 *va_arg(ap, int *) = nread;
519                         continue;
520                 }
521
522                 /*
523                  * We have a conversion that requires input.
524                  */
525                 if (inr <= 0)
526                         goto input_failure;
527
528                 /*
529                  * Consume leading white space, except for formats
530                  * that suppress this.
531                  */
532                 if ((flags & NOSKIP) == 0) {
533                         while (isspace(*inp)) {
534                                 nread++;
535                                 if (--inr > 0)
536                                         inp++;
537                                 else
538                                         goto input_failure;
539                         }
540                         /*
541                          * Note that there is at least one character in
542                          * the buffer, so conversions that do not set NOSKIP
543                          * can no longer result in an input failure.
544                          */
545                 }
546
547                 /*
548                  * Do the conversion.
549                  */
550                 switch (c) {
551                 case CT_CHAR:
552                         /* scan arbitrary characters (sets NOSKIP) */
553                         if (width == 0)
554                                 width = 1;
555                         if (flags & SUPPRESS) {
556                                 size_t sum = 0;
557
558                                 n = inr;
559                                 if (n < width) {
560                                         sum += n;
561                                         width -= n;
562                                         inp += n;
563                                         if (sum == 0)
564                                                 goto input_failure;
565                                 } else {
566                                         sum += width;
567                                         inr -= width;
568                                         inp += width;
569                                 }
570                                 nread += sum;
571                         } else {
572                                 memcpy(va_arg(ap, char *), inp, width);
573                                 inr -= width;
574                                 inp += width;
575                                 nread += width;
576                                 nassigned++;
577                         }
578                         nconversions++;
579                         break;
580
581                 case CT_CCL:
582                         /* scan a (nonempty) character class (sets NOSKIP) */
583                         if (width == 0)
584                                 width = (size_t)~0;     /* `infinity' */
585                         /* take only those things in the class */
586                         if (flags & SUPPRESS) {
587                                 n = 0;
588                                 while (ccltab[(unsigned char)*inp]) {
589                                         n++, inr--, inp++;
590                                         if (--width == 0)
591                                                 break;
592                                         if (inr <= 0) {
593                                                 if (n == 0)
594                                                         goto input_failure;
595                                                 break;
596                                         }
597                                 }
598                                 if (n == 0)
599                                         goto match_failure;
600                         } else {
601                                 p = va_arg(ap, char *);
602                                 p0 = p;
603                                 while (ccltab[(unsigned char)*inp]) {
604                                         inr--;
605                                         *p++ = *inp++;
606                                         if (--width == 0)
607                                                 break;
608                                         if (inr <= 0) {
609                                                 if (p == p0)
610                                                         goto input_failure;
611                                                 break;
612                                         }
613                                 }
614                                 n = p - p0;
615                                 if (n == 0)
616                                         goto match_failure;
617                                 *p = 0;
618                                 nassigned++;
619                         }
620                         nread += n;
621                         nconversions++;
622                         break;
623
624                 case CT_STRING:
625                         /* like CCL, but zero-length string OK, & no NOSKIP */
626                         if (width == 0)
627                                 width = (size_t)~0;
628                         if (flags & SUPPRESS) {
629                                 n = 0;
630                                 while (!isspace(*inp)) {
631                                         n++, inr--, inp++;
632                                         if (--width == 0)
633                                                 break;
634                                         if (inr <= 0)
635                                                 break;
636                                 }
637                                 nread += n;
638                         } else {
639                                 p = va_arg(ap, char *);
640                                 p0 = p;
641                                 while (!isspace(*inp)) {
642                                         inr--;
643                                         *p++ = *inp++;
644                                         if (--width == 0)
645                                                 break;
646                                         if (inr <= 0)
647                                                 break;
648                                 }
649                                 *p = 0;
650                                 nread += p - p0;
651                                 nassigned++;
652                         }
653                         nconversions++;
654                         continue;
655
656                 case CT_INT:
657                         /* scan an integer as if by strtoq/strtouq */
658 #ifdef hardway
659                         if (width == 0 || width > sizeof(buf) - 1)
660                                 width = sizeof(buf) - 1;
661 #else
662                         /* size_t is unsigned, hence this optimisation */
663                         if (--width > sizeof(buf) - 2)
664                                 width = sizeof(buf) - 2;
665                         width++;
666 #endif
667                         flags |= SIGNOK | NDIGITS | NZDIGITS;
668                         for (p = buf; width; width--) {
669                                 c = *inp;
670                                 /*
671                                  * Switch on the character; `goto ok'
672                                  * if we accept it as a part of number.
673                                  */
674                                 switch (c) {
675                                 /*
676                                  * The digit 0 is always legal, but is
677                                  * special.  For %i conversions, if no
678                                  * digits (zero or nonzero) have been
679                                  * scanned (only signs), we will have
680                                  * base==0.  In that case, we should set
681                                  * it to 8 and enable 0x prefixing.
682                                  * Also, if we have not scanned zero digits
683                                  * before this, do not turn off prefixing
684                                  * (someone else will turn it off if we
685                                  * have scanned any nonzero digits).
686                                  */
687                                 case '0':
688                                         if (base == 0) {
689                                                 base = 8;
690                                                 flags |= PFXOK;
691                                         }
692                                         if (flags & NZDIGITS)
693                                                 flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
694                                         else
695                                                 flags &= ~(SIGNOK | PFXOK | NDIGITS);
696                                         goto ok;
697
698                                 /* 1 through 7 always legal */
699                                 case '1': case '2': case '3':
700                                 case '4': case '5': case '6': case '7':
701                                         base = basefix[base];
702                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
703                                         goto ok;
704
705                                 /* digits 8 and 9 ok iff decimal or hex */
706                                 case '8': case '9':
707                                         base = basefix[base];
708                                         if (base <= 8)
709                                                 break;  /* not legal here */
710                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
711                                         goto ok;
712
713                                 /* letters ok iff hex */
714                                 case 'A': case 'B': case 'C':
715                                 case 'D': case 'E': case 'F':
716                                 case 'a': case 'b': case 'c':
717                                 case 'd': case 'e': case 'f':
718                                         /* no need to fix base here */
719                                         if (base <= 10)
720                                                 break;  /* not legal here */
721                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
722                                         goto ok;
723
724                                 /* sign ok only as first character */
725                                 case '+': case '-':
726                                         if (flags & SIGNOK) {
727                                                 flags &= ~SIGNOK;
728                                                 goto ok;
729                                                 }
730                                         break;
731
732                                 /* x ok iff flag still set & 2nd char */
733                                 case 'x': case 'X':
734                                         if (flags & PFXOK && p == buf + 1) {
735                                                 base = 16;      /* if %i */
736                                                 flags &= ~PFXOK;
737                                                 goto ok;
738                                         }
739                                         break;
740                                 }
741
742                                 /*
743                                  * If we got here, c is not a legal character
744                                  * for a number.  Stop accumulating digits.
745                                  */
746                                 break;
747 ok:
748                                 /*
749                                  * c is legal: store it and look at the next.
750                                  */
751                                 *p++ = c;
752                                 if (--inr > 0)
753                                         inp++;
754                                 else
755                                         break;          /* end of input */
756                         }
757                         /*
758                          * If we had only a sign, it is no good; push
759                          * back the sign.  If the number ends in `x',
760                          * it was [sign] '' 'x', so push back the x
761                          * and treat it as [sign] ''.
762                          */
763                         if (flags & NDIGITS) {
764                                 if (p > buf) {
765                                         inp--;
766                                         inr++;
767                                 }
768                                 goto match_failure;
769                         }
770                         c = ((u_char *)p)[-1];
771                         if (c == 'x' || c == 'X') {
772                                 --p;
773                                 inp--;
774                                 inr++;
775                         }
776                         if ((flags & SUPPRESS) == 0) {
777                                 u64 res;
778
779                                 *p = 0;
780                                 res = (*ccfn)(buf, (char **)NULL, base);
781                                 if (flags & POINTER)
782                                         *va_arg(ap, void **) =
783                                         (void *)(uintptr_t)res;
784                                 else if (flags & SHORTSHORT)
785                                         *va_arg(ap, char *) = res;
786                                 else if (flags & SHORT)
787                                         *va_arg(ap, short *) = res;
788                                 else if (flags & LONG)
789                                         *va_arg(ap, long *) = res;
790                                 else if (flags & QUAD)
791                                         *va_arg(ap, s64 *) = res;
792                                 else
793                                         *va_arg(ap, int *) = res;
794                                 nassigned++;
795                         }
796                         nread += p - buf;
797                         nconversions++;
798                         break;
799                 }
800         }
801 input_failure:
802                 return (nconversions != 0 ? nassigned : -1);
803 match_failure:
804                 return (nassigned);
805 }
806
807 /**
808  * sscanf - Unformat a buffer into a list of arguments
809  * @buf:        input buffer
810  * @fmt:        formatting of buffer
811  * @...:        resulting arguments
812  */
813 int sscanf(const char *buf, const char *fmt, ...)
814 {
815         va_list args;
816         int i;
817
818         va_start(args, fmt);
819         i = vsscanf(buf, fmt, args);
820         va_end(args);
821         return i;
822 }
823
824 #endif