(conv_error): Simplify expression to avoid "value computed is not used"
[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           do
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             } while (inchar() != EOF && width != 0);
434
435           if (w == work ||
436               (w - work == 1 && (work[0] == '+' || work[0] == '-')))
437             /* There was on number.  */
438             conv_error();
439
440           /* Convert the number.  */
441           *w = '\0';
442           if (is_longlong)
443             {
444               if (number_signed)
445                 num.q = __strtoq_internal (work, &w, base, group_flag);
446               else
447                 num.uq = __strtouq_internal (work, &w, base, group_flag);
448             }
449           else
450             {
451               if (number_signed)
452                 num.l = __strtol_internal (work, &w, base, group_flag);
453               else
454                 num.ul = __strtoul_internal (work, &w, base, group_flag);
455             }
456           if (w == work)
457             conv_error ();
458
459           if (do_assign)
460             {
461               if (! number_signed)
462                 {
463                   if (is_longlong)
464                     *ARG (unsigned LONGLONG int *) = num.uq;
465                   else if (is_long)
466                     *ARG (unsigned long int *) = num.ul;
467                   else if (is_short)
468                     *ARG (unsigned short int *)
469                       = (unsigned short int) num.ul;
470                   else
471                     *ARG (unsigned int *) = (unsigned int) num.ul;
472                 }
473               else
474                 {
475                   if (is_longlong)
476                     *ARG (LONGLONG int *) = num.q;
477                   else if (is_long)
478                     *ARG (long int *) = num.l;
479                   else if (is_short)
480                     *ARG (short int *) = (short int) num.l;
481                   else
482                     *ARG (int *) = (int) num.l;
483                 }
484               ++done;
485             }
486           break;
487
488         case 'e':       /* Floating-point numbers.  */
489         case 'E':
490         case 'f':
491         case 'g':
492         case 'G':
493           if (c == EOF)
494             input_error();
495
496           /* Check for a sign.  */
497           if (c == '-' || c == '+')
498             {
499               *w++ = c;
500               if (inchar() == EOF)
501                 /* EOF is only an input error before we read any chars.  */
502                 conv_error();
503               if (width > 0)
504                 --width;
505             }
506
507           got_dot = got_e = 0;
508           do
509             {
510               if (isdigit(c))
511                 *w++ = c;
512               else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
513                 *w++ = c;
514               else if (!got_e && tolower(c) == 'e')
515                 {
516                   *w++ = 'e';
517                   got_e = got_dot = 1;
518                 }
519               else if (c == decimal && !got_dot)
520                 {
521                   *w++ = c;
522                   got_dot = 1;
523                 }
524               else
525                 break;
526               if (width > 0)
527                 --width;
528             } while (inchar() != EOF && width != 0);
529
530           if (w == work)
531             conv_error();
532           if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
533             conv_error();
534
535           /* Convert the number.  */
536           *w = '\0';
537           if (is_long_double)
538             {
539               long double d = __strtold_internal (work, &w, group_flag);
540               if (do_assign && w != work)
541                 *ARG (long double *) = d;
542             }
543           else if (is_long)
544             {
545               double d = __strtod_internal (work, &w, group_flag);
546               if (do_assign && w != work)
547                 *ARG (double *) = d;
548             }
549           else
550             {
551               float d = __strtof_internal (work, &w, group_flag);
552               if (do_assign && w != work)
553                 *ARG (float *) = d;
554             }
555
556           if (w == work)
557             conv_error ();
558
559           if (do_assign)
560             ++done;
561           break;
562
563         case '[':       /* Character class.  */
564           STRING_ARG;
565
566           if (c == EOF)
567             input_error();
568
569           if (*f == '^')
570             {
571               ++f;
572               not_in = 1;
573             }
574           else
575             not_in = 0;
576
577           while ((fc = *f++) != '\0' && fc != ']')
578             {
579               if (fc == '-' && *f != '\0' && *f != ']' &&
580                   w > work && w[-1] <= *f)
581                 /* Add all characters from the one before the '-'
582                    up to (but not including) the next format char.  */
583                 for (fc = w[-1] + 1; fc < *f; ++fc)
584                   *w++ = fc;
585               else
586                 /* Add the character to the list.  */
587                 *w++ = fc;
588             }
589           if (fc == '\0')
590             conv_error();
591
592           *w = '\0';
593           num.ul = read_in;
594           do
595             {
596               if ((strchr (work, c) == NULL) != not_in)
597                 break;
598               STRING_ADD_CHAR (c);
599               if (width > 0)
600                 --width;
601             } while (inchar () != EOF && width != 0);
602           if (read_in == num.ul)
603             conv_error ();
604
605           if (do_assign)
606             {
607               *str = '\0';
608               ++done;
609             }
610           break;
611
612         case 'p':       /* Generic pointer.  */
613           base = 16;
614           /* A PTR must be the same size as a `long int'.  */
615           is_long = 1;
616           goto number;
617         }
618     }
619
620   conv_error();
621 }
622
623 weak_alias (__vfscanf, vfscanf)