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