a778346287cdccf3ceae2034a88518f1f88ff4e4
[kopensolaris-gnu/glibc.git] / stdio / vfscanf.c
1 /* Copyright (C) 1991, 1992, 1993, 1994, 1995 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
38 #define inchar()        ((c = getc(s)) == EOF ? EOF : (++read_in, c))
39 #define conv_error()    return (ungetc(c, s), done)
40 #define input_error()   return (done == 0 ? EOF : done)
41 #define memory_error()  return ((errno = ENOMEM), EOF)
42
43
44 /* Read formatted input from S according to the format string
45    FORMAT, using the argument list in ARG.
46    Return the number of assignments made, or -1 for an input error.  */
47 int
48 DEFUN(__vfscanf, (s, format, arg),
49       FILE *s AND CONST char *format AND va_list argptr)
50 {
51   va_list arg = (va_list) argptr;
52
53   register CONST char *f = format;
54   register char fc;             /* Current character of the format.  */
55   register size_t done = 0;     /* Assignments done.  */
56   register size_t read_in = 0;  /* Chars read in.  */
57   register int c;               /* Last char read.  */
58   register int do_assign;       /* Whether to do an assignment.  */
59   register int width;           /* Maximum field width.  */
60   int group_flag;               /* %' modifier flag.  */
61
62   /* Type modifiers.  */
63   int is_short, is_long, is_long_double;
64 #ifdef  HAVE_LONGLONG
65   /* We use the `L' modifier for `long long int'.  */
66 #define is_longlong     is_long_double
67 #else
68 #define is_longlong     0
69 #endif
70   int malloc_string;            /* Args are char ** to be filled in.  */
71   /* Status for reading F-P nums.  */
72   char got_dot, got_e;
73   /* If a [...] is a [^...].  */
74   char not_in;
75   /* Base for integral numbers.  */
76   int base;
77   /* Signedness for integral numbers.  */
78   int number_signed;
79   /* Integral holding variables.  */
80   union
81     {
82       long long int q;
83       unsigned long long int uq;
84       long int l;
85       unsigned long int ul;
86     } num;
87   /* Character-buffer pointer.  */
88   register char *str, **strptr;
89   size_t strsize;
90   /* Workspace.  */
91   char work[200];
92   char *w;                      /* Pointer into WORK.  */
93   wchar_t decimal;              /* Decimal point character.  */
94
95   if (!__validfp(s) || !s->__mode.__read || format == NULL)
96     {
97       errno = EINVAL;
98       return EOF;
99     }
100
101   /* Figure out the decimal point character.  */
102   if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT),
103               strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0)
104     decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
105
106   c = inchar();
107
108   /* Run through the format string.  */
109   while (*f != '\0')
110     {
111       unsigned int argpos;
112       /* Extract the next argument, which is of type TYPE.
113          For a %N$... spec, this is the Nth argument from the beginning;
114          otherwise it is the next argument after the state now in ARG.  */
115 #define ARG(type)       (argpos == 0 ? va_arg (arg, type) :                   \
116                          ({ unsigned int pos = argpos;                        \
117                             va_list arg = (va_list) argptr;                   \
118                             while (--pos > 0)                                 \
119                               (void) va_arg (arg, void *);                    \
120                             va_arg (arg, type);                               \
121                           }))
122
123       if (!isascii (*f))
124         {
125           /* Non-ASCII, may be a multibyte.  */
126           int len = mblen (f, strlen(f));
127           if (len > 0)
128             {
129               while (len-- > 0)
130                 if (c == EOF)
131                   input_error();
132                 else if (c == *f++)
133                   (void) inchar();
134                 else
135                   conv_error();
136               continue;
137             }
138         }
139
140       fc = *f++;
141       if (fc != '%')
142         {
143           /* Characters other than format specs must just match.  */
144           if (c == EOF)
145             input_error();
146           if (isspace(fc))
147             {
148               /* Whitespace characters match any amount of whitespace.  */
149               while (isspace (c))
150                 inchar ();
151               continue;
152             }
153           else if (c == fc)
154             (void) inchar();
155           else
156             conv_error();
157           continue;
158         }
159
160       /* Initialize state of modifiers.  */
161       argpos = 0;
162       do_assign = 1;
163       group_flag = 0;
164       is_short = is_long = is_long_double = malloc_string = 0;
165
166       /* Check for a positional parameter specification.  */
167       if (isdigit (*f))
168         {
169           argpos = *f++ - '0';
170           while (isdigit (*f))
171             argpos = argpos * 10 + (*f++ - '0');
172           if (*f == '$')
173             ++f;
174           else
175             {
176               /* Oops; that was actually the field width.  */
177               width = argpos;
178               argpos = 0;
179               goto got_width;
180             }
181         }
182
183       /* Check for the assignment-suppressant and the number grouping flag.  */
184       while (*f == '*' || *f == '\'')
185         switch (*f++)
186           {
187           case '*':
188             do_assign = 0;
189             break;
190           case '\'':
191             group_flag = 1;
192             break;
193           }
194
195       /* Find the maximum field width.  */
196       width = 0;
197       while (isdigit(*f))
198         {
199           width *= 10;
200           width += *f++ - '0';
201         }
202     got_width:
203       if (width == 0)
204         width = -1;
205
206       /* Check for type modifiers.  */
207       while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a' || *f == 'q')
208         switch (*f++)
209           {
210           case 'h':
211             /* int's are short int's.  */
212             is_short = 1;
213             break;
214           case 'l':
215             if (is_long)
216               /* A double `l' is equivalent to an `L'.  */
217               is_longlong = 1;
218             else
219               /* int's are long int's.  */
220               is_long = 1;
221             break;
222           case 'q':
223           case 'L':
224             /* double's are long double's, and int's are long long int's.  */
225             is_long_double = 1;
226             break;
227           case 'a':
228             /* String conversions (%s, %[) take a `char **'
229                arg and fill it in with a malloc'd pointer.  */
230             malloc_string = 1;
231             break;
232           }
233
234       /* End of the format string?  */
235       if (*f == '\0')
236         conv_error();
237
238       /* Find the conversion specifier.  */
239       w = work;
240       fc = *f++;
241       if (fc != '[' && fc != 'c' && fc != 'n')
242         /* Eat whitespace.  */
243         while (isspace(c))
244           (void) inchar();
245       switch (fc)
246         {
247         case '%':       /* Must match a literal '%'.  */
248           if (c != fc)
249             conv_error();
250           break;
251
252         case 'n':       /* Answer number of assignments done.  */
253           if (do_assign)
254             *ARG (int *) = read_in - 1; /* Don't count the read-ahead.  */
255           break;
256
257         case 'c':       /* Match characters.  */
258           if (do_assign)
259             {
260               str = ARG (char *);
261               if (str == NULL)
262                 conv_error ();
263             }
264
265           if (c == EOF)
266             input_error();
267
268           if (width == -1)
269             width = 1;
270
271           if (do_assign)
272             {
273               do
274                 *str++ = c;
275               while (inchar() != EOF && --width > 0);
276             }
277           else
278             while (inchar() != EOF && --width > 0);
279
280           if (do_assign)
281             ++done;
282
283           break;
284
285         case 's':               /* Read a string.  */
286 #define STRING_ARG                                                            \
287           if (do_assign)                                                      \
288             {                                                                 \
289               if (malloc_string)                                              \
290                 {                                                             \
291                   /* The string is to be stored in a malloc'd buffer.  */     \
292                   strptr = ARG (char **);                             \
293                   if (strptr == NULL)                                         \
294                     conv_error ();                                            \
295                   /* Allocate an initial buffer.  */                          \
296                   strsize = 100;                                              \
297                   *strptr = str = malloc (strsize);                           \
298                 }                                                             \
299               else                                                            \
300                 str = ARG (char *);                                   \
301               if (str == NULL)                                                \
302                 conv_error ();                                                \
303             }
304           STRING_ARG;
305
306           if (c == EOF)
307             input_error ();
308
309           do
310             {
311               if (isspace (c))
312                 break;
313 #define STRING_ADD_CHAR(c)                                                    \
314               if (do_assign)                                                  \
315                 {                                                             \
316                   *str++ = c;                                                 \
317                   if (malloc_string && str == *strptr + strsize)              \
318                     {                                                         \
319                       /* Enlarge the buffer.  */                              \
320                       str = realloc (*strptr, strsize * 2);                   \
321                       if (str == NULL)                                        \
322                         {                                                     \
323                           /* Can't allocate that much.  Last-ditch effort.  */\
324                           str = realloc (*strptr, strsize + 1);               \
325                           if (str == NULL)                                    \
326                             {                                                 \
327                               /* We lose.  Oh well.                           \
328                                  Terminate the string and stop converting,    \
329                                  so at least we don't swallow any input.  */  \
330                               (*strptr)[strsize] = '\0';                      \
331                               ++done;                                         \
332                               conv_error ();                                  \
333                             }                                                 \
334                           else                                                \
335                             {                                                 \
336                               *strptr = str;                                  \
337                               str += strsize;                                 \
338                               ++strsize;                                      \
339                             }                                                 \
340                         }                                                     \
341                       else                                                    \
342                         {                                                     \
343                           *strptr = str;                                      \
344                           str += strsize;                                     \
345                           strsize *= 2;                                       \
346                         }                                                     \
347                     }                                                         \
348                 }
349               STRING_ADD_CHAR (c);
350             } while (inchar () != EOF && (width <= 0 || --width > 0));
351
352           if (do_assign)
353             {
354               *str = '\0';
355               ++done;
356             }
357           break;
358
359         case 'x':       /* Hexadecimal integer.  */
360         case 'X':       /* Ditto.  */ 
361           base = 16;
362           number_signed = 0;
363           goto number;
364
365         case 'o':       /* Octal integer.  */
366           base = 8;
367           number_signed = 0;
368           goto number;
369
370         case 'u':       /* Unsigned decimal integer.  */
371           base = 10;
372           number_signed = 0;
373           goto number;
374
375         case 'd':       /* Signed decimal integer.  */
376           base = 10;
377           number_signed = 1;
378           goto number;
379
380         case 'i':       /* Generic number.  */
381           base = 0;
382           number_signed = 1;
383
384         number:
385           if (c == EOF)
386             input_error();
387
388           /* Check for a sign.  */
389           if (c == '-' || c == '+')
390             {
391               *w++ = c;
392               if (width > 0)
393                 --width;
394               (void) inchar();
395             }
396
397           /* Look for a leading indication of base.  */
398           if (c == '0')
399             {
400               if (width > 0)
401                 --width;
402               *w++ = '0';
403
404               (void) inchar();
405
406               if (tolower(c) == 'x')
407                 {
408                   if (base == 0)
409                     base = 16;
410                   if (base == 16)
411                     {
412                       if (width > 0)
413                         --width;
414                       (void) inchar();
415                     }
416                 }
417               else if (base == 0)
418                 base = 8;
419             }
420
421           if (base == 0)
422             base = 10;
423
424           /* Read the number into WORK.  */
425           while (width != 0 && c != EOF)
426             {
427               if (base == 16 ? !isxdigit(c) :
428                   (!isdigit(c) || c - '0' >= base))
429                 break;
430               *w++ = c;
431               if (width > 0)
432                 --width;
433               (void) inchar ();
434             }
435
436           if (w == work ||
437               (w - work == 1 && (work[0] == '+' || work[0] == '-')))
438             /* There was no number.  */
439             conv_error();
440
441           /* Convert the number.  */
442           *w = '\0';
443           if (is_longlong)
444             {
445               if (number_signed)
446                 num.q = __strtoq_internal (work, &w, base, group_flag);
447               else
448                 num.uq = __strtouq_internal (work, &w, base, group_flag);
449             }
450           else
451             {
452               if (number_signed)
453                 num.l = __strtol_internal (work, &w, base, group_flag);
454               else
455                 num.ul = __strtoul_internal (work, &w, base, group_flag);
456             }
457           if (w == work)
458             conv_error ();
459
460           if (do_assign)
461             {
462               if (! number_signed)
463                 {
464                   if (is_longlong)
465                     *ARG (unsigned LONGLONG int *) = num.uq;
466                   else if (is_long)
467                     *ARG (unsigned long int *) = num.ul;
468                   else if (is_short)
469                     *ARG (unsigned short int *)
470                       = (unsigned short int) num.ul;
471                   else
472                     *ARG (unsigned int *) = (unsigned int) num.ul;
473                 }
474               else
475                 {
476                   if (is_longlong)
477                     *ARG (LONGLONG int *) = num.q;
478                   else if (is_long)
479                     *ARG (long int *) = num.l;
480                   else if (is_short)
481                     *ARG (short int *) = (short int) num.l;
482                   else
483                     *ARG (int *) = (int) num.l;
484                 }
485               ++done;
486             }
487           break;
488
489         case 'e':       /* Floating-point numbers.  */
490         case 'E':
491         case 'f':
492         case 'g':
493         case 'G':
494           if (c == EOF)
495             input_error();
496
497           /* Check for a sign.  */
498           if (c == '-' || c == '+')
499             {
500               *w++ = c;
501               if (inchar() == EOF)
502                 /* EOF is only an input error before we read any chars.  */
503                 conv_error();
504               if (width > 0)
505                 --width;
506             }
507
508           got_dot = got_e = 0;
509           do
510             {
511               if (isdigit(c))
512                 *w++ = c;
513               else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
514                 *w++ = c;
515               else if (!got_e && tolower(c) == 'e')
516                 {
517                   *w++ = 'e';
518                   got_e = got_dot = 1;
519                 }
520               else if (c == decimal && !got_dot)
521                 {
522                   *w++ = c;
523                   got_dot = 1;
524                 }
525               else
526                 break;
527               if (width > 0)
528                 --width;
529             } while (inchar() != EOF && width != 0);
530
531           if (w == work)
532             conv_error();
533           if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
534             conv_error();
535
536           /* Convert the number.  */
537           *w = '\0';
538           if (is_long_double)
539             {
540               long double d = __strtold_internal (work, &w, group_flag);
541               if (do_assign && w != work)
542                 *ARG (long double *) = d;
543             }
544           else if (is_long)
545             {
546               double d = __strtod_internal (work, &w, group_flag);
547               if (do_assign && w != work)
548                 *ARG (double *) = d;
549             }
550           else
551             {
552               float d = __strtof_internal (work, &w, group_flag);
553               if (do_assign && w != work)
554                 *ARG (float *) = d;
555             }
556
557           if (w == work)
558             conv_error ();
559
560           if (do_assign)
561             ++done;
562           break;
563
564         case '[':       /* Character class.  */
565           STRING_ARG;
566
567           if (c == EOF)
568             input_error();
569
570           if (*f == '^')
571             {
572               ++f;
573               not_in = 1;
574             }
575           else
576             not_in = 0;
577
578           while ((fc = *f++) != '\0' && fc != ']')
579             {
580               if (fc == '-' && *f != '\0' && *f != ']' &&
581                   w > work && w[-1] <= *f)
582                 /* Add all characters from the one before the '-'
583                    up to (but not including) the next format char.  */
584                 for (fc = w[-1] + 1; fc < *f; ++fc)
585                   *w++ = fc;
586               else
587                 /* Add the character to the list.  */
588                 *w++ = fc;
589             }
590           if (fc == '\0')
591             conv_error();
592
593           *w = '\0';
594           num.ul = read_in;
595           do
596             {
597               if ((strchr (work, c) == NULL) != not_in)
598                 break;
599               STRING_ADD_CHAR (c);
600               if (width > 0)
601                 --width;
602             } while (inchar () != EOF && width != 0);
603           if (read_in == num.ul)
604             conv_error ();
605
606           if (do_assign)
607             {
608               *str = '\0';
609               ++done;
610             }
611           break;
612
613         case 'p':       /* Generic pointer.  */
614           base = 16;
615           /* A PTR must be the same size as a `long int'.  */
616           is_long = 1;
617           goto number;
618         }
619     }
620
621   conv_error();
622 }
623
624 weak_alias (__vfscanf, vfscanf)