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