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