Thu May 23 00:01:10 1996 Ulrich Drepper <drepper@cygnus.com>
[kopensolaris-gnu/glibc.git] / stdio-common / vfscanf.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include "../locale/localeinfo.h"
20 #include <errno.h>
21 #include <limits.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifdef  __GNUC__
29 #define HAVE_LONGLONG
30 #define LONGLONG        long long
31 #else
32 #define LONGLONG        long
33 #endif
34
35 /* Those are flags in the conversion format. */
36 # define LONG           0x001   /* l: long or double */
37 # define LONGDBL        0x002   /* L: long long or long double */
38 # define SHORT          0x004   /* h: short */
39 # define SUPPRESS       0x008   /* *: suppress assignment */
40 # define POINTER        0x010   /* weird %p pointer (`fake hex') */
41 # define NOSKIP         0x020   /* do not skip blanks */
42 # define WIDTH          0x040   /* width was given */
43 # define GROUP          0x080   /* ': group numbers */
44 # define MALLOC         0x100   /* a: malloc strings */
45
46 # define TYPEMOD        (LONG|LONGDBL|SHORT)
47
48
49 #ifdef USE_IN_LIBIO
50 # include <libioP.h>
51 # include <libio.h>
52
53 # define va_list        _IO_va_list
54 # define ungetc(c, s)   _IO_ungetc (c, s)
55 # define inchar()       ((c = _IO_getc_unlocked (s)), (void) ++read_in, c)
56 # define conv_error()   do {                                                  \
57                           if (errp != NULL) *errp |= 2;                       \
58                           if (c != EOF) _IO_ungetc (c, s);                    \
59                           _IO_funlockfile (s);                                \
60                           return done;                                        \
61                         } while (0)
62 # define input_error()  do {                                                  \
63                           _IO_funlockfile (s);                                \
64                           if (errp != NULL) *errp |= 1;                       \
65                           return done ?: EOF;                                 \
66                         } while (0)
67 # define memory_error() do {                                                  \
68                           _IO_funlockfile (s);                                \
69                           errno = ENOMEM;                                     \
70                           return EOF;                                         \
71                         } while (0)
72 # define ARGCHECK(s, format)                                                  \
73   do                                                                          \
74     {                                                                         \
75       /* Check file argument for consistence.  */                             \
76       CHECK_FILE (s, EOF);                                                    \
77       if (s->_flags & _IO_NO_READS || format == NULL)                         \
78        {                                                                      \
79          MAYBE_SET_EINVAL;                                                    \
80          return EOF;                                                          \
81        }                                                                      \
82     } while (0)
83 #else
84 # define inchar()       ((c = getc (s)), (void) ++read_in, c)
85 # define conv_error()   do {                                                  \
86                           funlockfile (s);                                    \
87                           ungetc (c, s);                                      \
88                           return done;                                        \
89                         } while (0)
90 # define input_error()  do {                                                  \
91                           funlockfile (s);                                    \
92                           return done ?: EOF;                                 \
93                         } while (0)
94 # define memory_error() do {                                                  \
95                           funlockfile (s);                                    \
96                           errno = ENOMEM;                                     \
97                           return EOF;                                         \
98                         } while (0)
99 # define ARGCHECK(s, format)                                                  \
100   do                                                                          \
101     {                                                                         \
102       /* Check file argument for consistence.  */                             \
103       if (!__validfp (s) || !s->__mode.__read || format == NULL)              \
104         {                                                                     \
105           errno = EINVAL;                                                     \
106           return EOF;                                                         \
107         }                                                                     \
108     } while (0)
109 # define flockfile(S) /* nothing */
110 # define funlockfile(S) /* nothing */
111 #endif
112
113
114 /* Read formatted input from S according to the format string
115    FORMAT, using the argument list in ARG.
116    Return the number of assignments made, or -1 for an input error.  */
117 #ifdef USE_IN_LIBIO
118 int
119 _IO_vfscanf (s, format, argptr, errp)
120      _IO_FILE *s;
121      const char *format;
122      _IO_va_list argptr;
123      int *errp;
124 #else
125 int
126 __vfscanf (FILE *s, const char *format, va_list argptr)
127 #endif
128 {
129   va_list arg = (va_list) argptr;
130
131   register const char *f = format;
132   register unsigned char fc;    /* Current character of the format.  */
133   register size_t done = 0;     /* Assignments done.  */
134   register size_t read_in = 0;  /* Chars read in.  */
135   register int c;               /* Last char read.  */
136   register int width;           /* Maximum field width.  */
137   register int flags;           /* Modifiers for current format element.  */
138
139   /* Status for reading F-P nums.  */
140   char got_dot, got_e, negative;
141   /* If a [...] is a [^...].  */
142   char not_in;
143   /* Base for integral numbers.  */
144   int base;
145   /* Signedness for integral numbers.  */
146   int number_signed;
147   /* Decimal point character.  */
148   wchar_t decimal;
149   /* The thousands character of the current locale.  */
150   wchar_t thousands;
151   /* Integral holding variables.  */
152   union
153     {
154       long long int q;
155       unsigned long long int uq;
156       long int l;
157       unsigned long int ul;
158     } num;
159   /* Character-buffer pointer.  */
160   register char *str, **strptr;
161   size_t strsize;
162   /* We must not react on white spaces immediately because they can
163      possibly be matched even if in the input stream no character is
164      available anymore.  */
165   int skip_space = 0;
166   /* Workspace.  */
167   char *tw;                     /* Temporary pointer.  */
168   char *wp = NULL;              /* Workspace.  */
169   size_t wpmax = 0;             /* Maximal size of workspace.  */
170   size_t wpsize;                /* Currently used bytes in workspace.  */
171 #define ADDW(Ch)                                                            \
172   do                                                                        \
173     {                                                                       \
174       if (wpsize == wpmax)                                                  \
175         {                                                                   \
176           char *old = wp;                                                   \
177           wpmax = UCHAR_MAX > 2 * wpmax ? UCHAR_MAX : 2 * wpmax;            \
178           wp = (char *) alloca (wpmax);                                     \
179           if (old != NULL)                                                  \
180             memcpy (wp, old, wpsize);                                       \
181         }                                                                   \
182       wp[wpsize++] = (Ch);                                                  \
183     }                                                                       \
184   while (0)
185
186   ARGCHECK (s, format);
187
188   /* Figure out the decimal point character.  */
189   if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT),
190               strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0)
191     decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
192   /* Figure out the thousands separator character.  */
193   if (mbtowc (&thousands, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
194               strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
195     thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
196
197   /* Lock the stream.  */
198   flockfile (s);
199
200   c = inchar ();
201
202   /* Run through the format string.  */
203   while (*f != '\0')
204     {
205       unsigned int argpos;
206       /* Extract the next argument, which is of type TYPE.
207          For a %N$... spec, this is the Nth argument from the beginning;
208          otherwise it is the next argument after the state now in ARG.  */
209 #if 0
210       /* XXX Possible optimization.  */
211 # define ARG(type)      (argpos == 0 ? va_arg (arg, type) :                   \
212                          ({ va_list arg = (va_list) argptr;                   \
213                             arg = (va_list) ((char *) arg                     \
214                                              + (argpos - 1)                   \
215                                              * __va_rounded_size (void *));   \
216                             va_arg (arg, type);                               \
217                          }))
218 #else
219 # define ARG(type)      (argpos == 0 ? va_arg (arg, type) :                   \
220                          ({ unsigned int pos = argpos;                        \
221                             va_list arg = (va_list) argptr;                   \
222                             while (--pos > 0)                                 \
223                               (void) va_arg (arg, void *);                    \
224                             va_arg (arg, type);                               \
225                           }))
226 #endif
227
228       if (!isascii (*f))
229         {
230           /* Non-ASCII, may be a multibyte.  */
231           int len = mblen (f, strlen (f));
232           if (len > 0)
233             {
234               while (len-- > 0)
235                 if (c == EOF)
236                   input_error ();
237                 else if (c == *f++)
238                   (void) inchar ();
239                 else
240                   conv_error ();
241               continue;
242             }
243         }
244
245       fc = *f++;
246       if (fc != '%')
247         {
248           /* Remember to skip spaces.  */
249           if (isspace (fc))
250             {
251               skip_space = 1;
252               continue;
253             }
254
255           /* Characters other than format specs must just match.  */
256           if (c == EOF)
257             input_error ();
258
259           /* We saw white space char as the last character in the format
260              string.  Now it's time to skip all leading white space.  */
261           if (skip_space)
262             {
263               while (isspace (c))
264                 (void) inchar ();
265               skip_space = 0;
266             }
267
268           if (c == fc)
269             (void) inchar ();
270           else
271             conv_error ();
272
273           continue;
274         }
275
276       /* This is the start of the conversion string. */
277       flags = 0;
278
279       /* Initialize state of modifiers.  */
280       argpos = 0;
281
282       /* Prepare temporary buffer.  */
283       wpsize = 0;
284
285       /* Check for a positional parameter specification.  */
286       if (isdigit (*f))
287         {
288           argpos = *f++ - '0';
289           while (isdigit (*f))
290             argpos = argpos * 10 + (*f++ - '0');
291           if (*f == '$')
292             ++f;
293           else
294             {
295               /* Oops; that was actually the field width.  */
296               width = argpos;
297               flags |= WIDTH;
298               argpos = 0;
299               goto got_width;
300             }
301         }
302
303       /* Check for the assignment-suppressant and the number grouping flag.  */
304       while (*f == '*' || *f == '\'')
305         switch (*f++)
306           {
307           case '*':
308             flags |= SUPPRESS;
309             break;
310           case '\'':
311             flags |= GROUP;
312             break;
313           }
314
315       /* We have seen width. */
316       if (isdigit (*f))
317         flags |= WIDTH;
318
319       /* Find the maximum field width.  */
320       width = 0;
321       while (isdigit (*f))
322         {
323           width *= 10;
324           width += *f++ - '0';
325         }
326     got_width:
327       if (width == 0)
328         width = -1;
329
330       /* Check for type modifiers.  */
331       while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a' || *f == 'q')
332         switch (*f++)
333           {
334           case 'h':
335             /* int's are short int's.  */
336             if (flags & TYPEMOD)
337               /* Signal illegal format element.  */
338               conv_error ();
339             flags |= SHORT;
340             break;
341           case 'l':
342             if (flags & (SHORT|LONGDBL))
343               conv_error ();
344             else if (flags & LONG)
345               {
346                 /* A double `l' is equivalent to an `L'.  */
347                 flags &= ~LONG;
348                 flags |= LONGDBL;
349               }
350             else
351               /* int's are long int's.  */
352               flags |= LONG;
353             break;
354           case 'q':
355           case 'L':
356             /* double's are long double's, and int's are long long int's.  */
357             if (flags & TYPEMOD)
358               /* Signal illegal format element.  */
359               conv_error ();
360             flags |= LONGDBL;
361             break;
362           case 'a':
363             if (flags & TYPEMOD)
364               /* Signal illegal format element.  */
365               conv_error ();
366             /* String conversions (%s, %[) take a `char **'
367                arg and fill it in with a malloc'd pointer.  */
368             flags |= MALLOC;
369             break;
370           }
371
372       /* End of the format string?  */
373       if (*f == '\0')
374         conv_error ();
375
376       /* Find the conversion specifier.  */
377       fc = *f++;
378       if (skip_space || (fc != '[' && fc != 'c' && fc != 'n'))
379         {
380           /* Eat whitespace.  */
381           while (isspace (c))
382             (void) inchar ();
383           skip_space = 0;
384         }
385
386       switch (fc)
387         {
388         case '%':       /* Must match a literal '%'.  */
389           if (c != fc)
390             conv_error ();
391           inchar ();
392           break;
393
394         case 'n':       /* Answer number of assignments done.  */
395           /* Corrigendum 1 to ISO C 1990 describes the allowed flags
396              with the 'n' conversion specifier.  */
397           if (!(flags & SUPPRESS))
398             /* Don't count the read-ahead.  */
399             if (flags & LONGDBL)
400               *ARG (long long int *) = read_in - 1;
401             else if (flags & LONG)
402               *ARG (long int *) = read_in - 1;
403             else if (flags & SHORT)
404               *ARG (short int *) = read_in - 1;
405             else
406               *ARG (int *) = read_in - 1;
407           break;
408
409         case 'c':       /* Match characters.  */
410           if (!(flags & SUPPRESS))
411             {
412               str = ARG (char *);
413               if (str == NULL)
414                 conv_error ();
415             }
416
417           if (c == EOF)
418             input_error ();
419
420           if (width == -1)
421             width = 1;
422
423           if (!(flags & SUPPRESS))
424             {
425               do
426                 *str++ = c;
427               while (inchar () != EOF && --width > 0);
428             }
429           else
430             while (inchar () != EOF && --width > 0);
431
432           if (!(flags & SUPPRESS))
433             ++done;
434
435           break;
436
437         case 's':               /* Read a string.  */
438 #define STRING_ARG                                                            \
439           if (!(flags & SUPPRESS))                                            \
440             {                                                                 \
441               if (flags & MALLOC)                                             \
442                 {                                                             \
443                   /* The string is to be stored in a malloc'd buffer.  */     \
444                   strptr = ARG (char **);                                     \
445                   if (strptr == NULL)                                         \
446                     conv_error ();                                            \
447                   /* Allocate an initial buffer.  */                          \
448                   strsize = 100;                                              \
449                   *strptr = str = malloc (strsize);                           \
450                 }                                                             \
451               else                                                            \
452                 str = ARG (char *);                                           \
453               if (str == NULL)                                                \
454                 conv_error ();                                                \
455             }
456           STRING_ARG;
457
458           if (c == EOF)
459             input_error ();
460
461           do
462             {
463               if (isspace (c))
464                 break;
465 #define STRING_ADD_CHAR(c)                                                    \
466               if (!(flags & SUPPRESS))                                        \
467                 {                                                             \
468                   *str++ = c;                                                 \
469                   if ((flags & MALLOC) && str == *strptr + strsize)           \
470                     {                                                         \
471                       /* Enlarge the buffer.  */                              \
472                       str = realloc (*strptr, strsize * 2);                   \
473                       if (str == NULL)                                        \
474                         {                                                     \
475                           /* Can't allocate that much.  Last-ditch effort.  */\
476                           str = realloc (*strptr, strsize + 1);               \
477                           if (str == NULL)                                    \
478                             {                                                 \
479                               /* We lose.  Oh well.                           \
480                                  Terminate the string and stop converting,    \
481                                  so at least we don't skip any input.  */     \
482                               (*strptr)[strsize] = '\0';                      \
483                               ++done;                                         \
484                               conv_error ();                                  \
485                             }                                                 \
486                           else                                                \
487                             {                                                 \
488                               *strptr = str;                                  \
489                               str += strsize;                                 \
490                               ++strsize;                                      \
491                             }                                                 \
492                         }                                                     \
493                       else                                                    \
494                         {                                                     \
495                           *strptr = str;                                      \
496                           str += strsize;                                     \
497                           strsize *= 2;                                       \
498                         }                                                     \
499                     }                                                         \
500                 }
501               STRING_ADD_CHAR (c);
502             } while (inchar () != EOF && (width <= 0 || --width > 0));
503
504           if (!(flags & SUPPRESS))
505             {
506               *str = '\0';
507               ++done;
508             }
509           break;
510
511         case 'x':       /* Hexadecimal integer.  */
512         case 'X':       /* Ditto.  */
513           base = 16;
514           number_signed = 0;
515           goto number;
516
517         case 'o':       /* Octal integer.  */
518           base = 8;
519           number_signed = 0;
520           goto number;
521
522         case 'u':       /* Unsigned decimal integer.  */
523           base = 10;
524           number_signed = 0;
525           goto number;
526
527         case 'd':       /* Signed decimal integer.  */
528           base = 10;
529           number_signed = 1;
530           goto number;
531
532         case 'i':       /* Generic number.  */
533           base = 0;
534           number_signed = 1;
535
536         number:
537           if (c == EOF)
538             input_error ();
539
540           /* Check for a sign.  */
541           if (c == '-' || c == '+')
542             {
543               ADDW (c);
544               if (width > 0)
545                 --width;
546               (void) inchar ();
547             }
548
549           /* Look for a leading indication of base.  */
550           if (width != 0 && c == '0')
551             {
552               if (width > 0)
553                 --width;
554
555               ADDW (c);
556               (void) inchar ();
557
558               if (width != 0 && tolower (c) == 'x')
559                 {
560                   if (base == 0)
561                     base = 16;
562                   if (base == 16)
563                     {
564                       if (width > 0)
565                         --width;
566                       (void) inchar ();
567                     }
568                 }
569               else if (base == 0)
570                 base = 8;
571             }
572
573           if (base == 0)
574             base = 10;
575
576           /* Read the number into workspace.  */
577           while (c != EOF && width != 0)
578             {
579               if (base == 16 ? !isxdigit (c) :
580                   ((!isdigit (c) || c - '0' >= base) &&
581                    !((flags & GROUP) && base == 10 && c == thousands)))
582                 break;
583               ADDW (c);
584               if (width > 0)
585                 --width;
586
587               (void) inchar ();
588             }
589
590           if (wpsize == 0 ||
591               (wpsize == 1 && (wp[0] == '+' || wp[0] == '-')))
592             /* There was no number.  */
593             conv_error ();
594
595           /* Convert the number.  */
596           ADDW ('\0');
597           if (flags & LONGDBL)
598             {
599               if (number_signed)
600                 num.q = __strtoq_internal (wp, &tw, base, flags & GROUP);
601               else
602                 num.uq = __strtouq_internal (wp, &tw, base, flags & GROUP);
603             }
604           else
605             {
606               if (number_signed)
607                 num.l = __strtol_internal (wp, &tw, base, flags & GROUP);
608               else
609                 num.ul = __strtoul_internal (wp, &tw, base, flags & GROUP);
610             }
611           if (wp == tw)
612             conv_error ();
613
614           if (!(flags & SUPPRESS))
615             {
616               if (! number_signed)
617                 {
618                   if (flags & LONGDBL)
619                     *ARG (unsigned LONGLONG int *) = num.uq;
620                   else if (flags & LONG)
621                     *ARG (unsigned long int *) = num.ul;
622                   else if (flags & SHORT)
623                     *ARG (unsigned short int *)
624                       = (unsigned short int) num.ul;
625                   else
626                     *ARG (unsigned int *) = (unsigned int) num.ul;
627                 }
628               else
629                 {
630                   if (flags & LONGDBL)
631                     *ARG (LONGLONG int *) = num.q;
632                   else if (flags & LONG)
633                     *ARG (long int *) = num.l;
634                   else if (flags & SHORT)
635                     *ARG (short int *) = (short int) num.l;
636                   else
637                     *ARG (int *) = (int) num.l;
638                 }
639               ++done;
640             }
641           break;
642
643         case 'e':       /* Floating-point numbers.  */
644         case 'E':
645         case 'f':
646         case 'g':
647         case 'G':
648           if (c == EOF)
649             input_error ();
650
651           /* Check for a sign.  */
652           if (c == '-' || c == '+')
653             {
654               negative = c == '-';
655               if (inchar () == EOF)
656                 /* EOF is only an input error before we read any chars.  */
657                 conv_error ();
658               if (width > 0)
659                 --width;
660             }
661           else
662             negative = 0;
663
664           got_dot = got_e = 0;
665           do
666             {
667               if (isdigit (c))
668                 ADDW (c);
669               else if (got_e && wp[wpsize - 1] == 'e'
670                        && (c == '-' || c == '+'))
671                 ADDW (c);
672               else if (wpsize > 0 && !got_e && tolower (c) == 'e')
673                 {
674                   ADDW ('e');
675                   got_e = got_dot = 1;
676                 }
677               else if (c == decimal && !got_dot)
678                 {
679                   ADDW (c);
680                   got_dot = 1;
681                 }
682               else if ((flags & GROUP) && c == thousands && !got_dot)
683                 ADDW (c);
684               else
685                 break;
686               if (width > 0)
687                 --width;
688             }
689           while (inchar () != EOF && width != 0);
690
691           if (wpsize == 0)
692             conv_error ();
693
694           /* Convert the number.  */
695           ADDW ('\0');
696           if (flags & LONGDBL)
697             {
698               long double d = __strtold_internal (wp, &tw, flags & GROUP);
699               if (!(flags & SUPPRESS) && tw != wp)
700                 *ARG (long double *) = negative ? -d : d;
701             }
702           else if (flags & LONG)
703             {
704               double d = __strtod_internal (wp, &tw, flags & GROUP);
705               if (!(flags & SUPPRESS) && tw != wp)
706                 *ARG (double *) = negative ? -d : d;
707             }
708           else
709             {
710               float d = __strtof_internal (wp, &tw, flags & GROUP);
711               if (!(flags & SUPPRESS) && tw != wp)
712                 *ARG (float *) = negative ? -d : d;
713             }
714
715           if (tw == wp)
716             conv_error ();
717
718           if (!(flags & SUPPRESS))
719             ++done;
720           break;
721
722         case '[':       /* Character class.  */
723           STRING_ARG;
724
725           if (c == EOF)
726             input_error();
727
728           if (*f == '^')
729             {
730               ++f;
731               not_in = 1;
732             }
733           else
734             not_in = 0;
735
736           /* Fill WP with byte flags indexed by character.
737              We will use this flag map for matching input characters.  */
738           if (wpmax < UCHAR_MAX)
739             {
740               wpmax = UCHAR_MAX;
741               wp = (char *) alloca (wpmax);
742             }
743           memset (wp, 0, UCHAR_MAX);
744
745           fc = *f;
746           if (fc == ']' || fc == '-')
747             {
748               /* If ] or - appears before any char in the set, it is not
749                  the terminator or separator, but the first char in the
750                  set.  */
751               wp[fc] = 1;
752               ++f;
753             }
754
755           while ((fc = *f++) != '\0' && fc != ']')
756             {
757               if (fc == '-' && *f != '\0' && *f != ']' &&
758                   (unsigned char) f[-2] <= (unsigned char) *f)
759                 {
760                   /* Add all characters from the one before the '-'
761                      up to (but not including) the next format char.  */
762                   for (fc = f[-2]; fc < *f; ++fc)
763                     wp[fc] = 1;
764                 }
765               else
766                 /* Add the character to the flag map.  */
767                 wp[fc] = 1;
768             }
769           if (fc == '\0')
770             conv_error();
771
772           num.ul = read_in;
773           do
774             {
775               if (wp[c] == not_in)
776                 break;
777               STRING_ADD_CHAR (c);
778               if (width > 0)
779                 --width;
780             }
781           while (inchar () != EOF && width != 0);
782           if (read_in == num.ul)
783             conv_error ();
784
785           if (!(flags & SUPPRESS))
786             {
787               *str = '\0';
788               ++done;
789             }
790           break;
791
792         case 'p':       /* Generic pointer.  */
793           base = 16;
794           /* A PTR must be the same size as a `long int'.  */
795           flags &= ~(SHORT|LONGDBL);
796           flags |= LONG;
797           number_signed = 0;
798           goto number;
799         }
800     }
801
802   /* The last thing we saw int the format string was a white space.
803      Consume the last white spaces.  */
804   if (skip_space)
805     while (isspace (c))
806       (void) inchar ();
807
808   /* Unlock stream.  */
809   funlockfile (s);
810
811   return ((void) (c == EOF || ungetc (c, s)), done);
812 }
813
814 #ifdef USE_IN_LIBIO
815 int
816 __vfscanf (FILE *s, const char *format, va_list argptr)
817 {
818   return _IO_vfscanf (s, format, argptr, NULL);
819 }
820 #endif
821
822 weak_alias (__vfscanf, vfscanf)