[USE_IN_LIBIO] (__vfscanf): New wrapper function.
[glibc/history.git] / stdio-common / vfscanf.c
blobb0e48df4f88826ecf9cbd84d50b6b255e86143ec
1 /* Copyright (C) 1991, 1992, 1993, 1994, 1995 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 <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>
30 #ifdef __GNUC__
31 #define HAVE_LONGLONG
32 #define LONGLONG long long
33 #else
34 #define LONGLONG long
35 #endif
37 #ifdef USE_IN_LIBIO
38 # include <libioP.h>
39 # include <libio.h>
41 /* Those are flags in the conversion format. */
42 # define LONG 0x01 /* l: long or double */
43 # define LONGDBL 0x02 /* L: long long or long double */
44 # define SHORT 0x04 /* h: short */
45 # define SUPPRESS 0x08 /* suppress assignment */
46 # define POINTER 0x10 /* weird %p pointer (`fake hex') */
47 # define NOSKIP 0x20 /* do not skip blanks */
48 # define WIDTH 0x40 /* width */
51 # define va_list _IO_va_list
52 # define ungetc(c, s) _IO_ungetc (c, s)
53 # define inchar() ((c = _IO_getc(s)), ++read_in, c)
54 # define conv_error() return ((errp != NULL && (*errp |= 2)), \
55 (c == EOF || _IO_ungetc(c, s)), done)
57 # define input_error() return ((errp != NULL && (*errp |= 1)), \
58 done == 0 ? EOF : done)
59 # define memory_error() return ((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)) == EOF ? EOF : (++read_in, c))
73 # define conv_error() return (ungetc(c, s), done)
74 # define input_error() return (done == 0 ? EOF : done)
75 # define memory_error() return ((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
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
101 __vfscanf (FILE *s, const char *format, va_list argptr)
102 #endif
104 va_list arg = (va_list) argptr;
106 register const char *f = format;
107 register 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 #ifdef USE_IN_LIBIO
115 int flags; /* Trace flags for current format element. */
116 #endif
118 /* Type modifiers. */
119 int is_short, is_long, is_long_double;
120 #ifdef HAVE_LONGLONG
121 /* We use the `L' modifier for `long long int'. */
122 # define is_longlong is_long_double
123 #else
124 # define is_longlong 0
125 #endif
126 int malloc_string; /* Args are char ** to be filled in. */
127 /* Status for reading F-P nums. */
128 char got_dot, got_e;
129 /* If a [...] is a [^...]. */
130 char not_in;
131 /* Base for integral numbers. */
132 int base;
133 /* Signedness for integral numbers. */
134 int number_signed;
135 /* Integral holding variables. */
136 union
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 /* Workspace. */
147 char work[200];
148 char *w; /* Pointer into WORK. */
149 wchar_t decimal; /* Decimal point character. */
151 ARGCHECK (s, format);
153 /* Figure out the decimal point character. */
154 if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT),
155 strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0)
156 decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
158 c = inchar ();
160 /* Run through the format string. */
161 while (*f != '\0')
163 unsigned int argpos;
164 /* Extract the next argument, which is of type TYPE.
165 For a %N$... spec, this is the Nth argument from the beginning;
166 otherwise it is the next argument after the state now in ARG. */
167 #if 0
168 /* XXX Possible optimization. */
169 # define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
170 ({ va_list arg = (va_list) argptr; \
171 arg = (va_list) ((char *) arg \
172 + (argpos - 1) \
173 * __va_rounded_size (void *)); \
174 va_arg (arg, type); \
176 #else
177 # define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
178 ({ unsigned int pos = argpos; \
179 va_list arg = (va_list) argptr; \
180 while (--pos > 0) \
181 (void) va_arg (arg, void *); \
182 va_arg (arg, type); \
184 #endif
186 if (!isascii (*f))
188 /* Non-ASCII, may be a multibyte. */
189 int len = mblen (f, strlen (f));
190 if (len > 0)
192 while (len-- > 0)
193 if (c == EOF)
194 input_error ();
195 else if (c == *f++)
196 (void) inchar ();
197 else
198 conv_error ();
199 continue;
203 fc = *f++;
204 if (fc != '%')
206 /* Characters other than format specs must just match. */
207 if (c == EOF)
208 input_error ();
209 if (isspace (fc))
211 /* Whitespace characters match any amount of whitespace. */
212 while (isspace (c))
213 inchar ();
214 continue;
216 else if (c == fc)
217 (void) inchar ();
218 else
219 conv_error ();
220 continue;
223 #ifdef USE_IN_LIBIO
224 /* That is the start of the coversion string. */
225 flags = 0;
226 #endif
228 /* Initialize state of modifiers. */
229 argpos = 0;
230 do_assign = 1;
231 group_flag = 0;
232 is_short = is_long = is_long_double = malloc_string = 0;
234 /* Check for a positional parameter specification. */
235 if (isdigit (*f))
237 argpos = *f++ - '0';
238 while (isdigit (*f))
239 argpos = argpos * 10 + (*f++ - '0');
240 if (*f == '$')
241 ++f;
242 else
244 /* Oops; that was actually the field width. */
245 width = argpos;
246 argpos = 0;
247 goto got_width;
251 /* Check for the assignment-suppressant and the number grouping flag. */
252 while (*f == '*' || *f == '\'')
253 switch (*f++)
255 case '*':
256 #ifdef USE_IN_LIBIO
257 flags = SUPPRESS;
258 #endif
259 do_assign = 0;
260 break;
261 case '\'':
262 group_flag = 1;
263 break;
266 #ifdef USE_IN_LIBIO
267 /* We have seen width. */
268 if (isdigit (*f))
269 flags |= WIDTH;
270 #endif
272 /* Find the maximum field width. */
273 width = 0;
274 while (isdigit (*f))
276 width *= 10;
277 width += *f++ - '0';
279 got_width:
280 if (width == 0)
281 width = -1;
283 /* Check for type modifiers. */
284 while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a' || *f == 'q')
285 switch (*f++)
287 case 'h':
288 /* int's are short int's. */
289 #ifdef USE_IN_LIBIO
290 if (flags & ~(SUPPRESS | WIDTH))
291 /* Signal illegal format element. */
292 conv_error ();
293 flags |= SHORT;
294 #endif
295 is_short = 1;
296 break;
297 case 'l':
298 if (is_long)
300 /* A double `l' is equivalent to an `L'. */
301 #ifdef USE_IN_LIBIO
302 if ((flags & ~(SUPPRESS | WIDTH)) && (flags & LONGDBL))
303 conv_error ();
304 flags &= ~LONG;
305 flags |= LONGDBL;
306 #endif
307 is_longlong = 1;
309 else
311 /* int's are long int's. */
312 #ifdef USE_IN_LIBIO
313 flags |= LONG;
314 #endif
315 is_long = 1;
317 break;
318 case 'q':
319 case 'L':
320 /* double's are long double's, and int's are long long int's. */
321 #ifdef USE_IN_LIBIO
322 if (flags & ~(SUPPRESS | WIDTH))
323 /* Signal illegal format element. */
324 conv_error ();
325 flags |= LONGDBL;
326 #endif
327 is_long_double = 1;
328 break;
329 case 'a':
330 /* String conversions (%s, %[) take a `char **'
331 arg and fill it in with a malloc'd pointer. */
332 malloc_string = 1;
333 break;
336 /* End of the format string? */
337 if (*f == '\0')
338 conv_error ();
340 /* Find the conversion specifier. */
341 w = work;
342 fc = *f++;
343 if (fc != '[' && fc != 'c' && fc != 'n')
344 /* Eat whitespace. */
345 while (isspace (c))
346 (void) inchar ();
347 switch (fc)
349 case '%': /* Must match a literal '%'. */
350 if (c != fc)
351 conv_error ();
352 break;
354 case 'n': /* Answer number of assignments done. */
355 if (do_assign)
356 *ARG (int *) = read_in - 1; /* Don't count the read-ahead. */
357 break;
359 case 'c': /* Match characters. */
360 if (do_assign)
362 str = ARG (char *);
363 if (str == NULL)
364 conv_error ();
367 if (c == EOF)
368 input_error ();
370 if (width == -1)
371 width = 1;
373 if (do_assign)
376 *str++ = c;
377 while (inchar () != EOF && --width > 0);
379 else
380 while (inchar () != EOF && --width > 0);
382 if (do_assign)
383 ++done;
385 break;
387 case 's': /* Read a string. */
388 #define STRING_ARG \
389 if (do_assign) \
391 if (malloc_string) \
393 /* The string is to be stored in a malloc'd buffer. */ \
394 strptr = ARG (char **); \
395 if (strptr == NULL) \
396 conv_error (); \
397 /* Allocate an initial buffer. */ \
398 strsize = 100; \
399 *strptr = str = malloc (strsize); \
401 else \
402 str = ARG (char *); \
403 if (str == NULL) \
404 conv_error (); \
406 STRING_ARG;
408 if (c == EOF)
409 input_error ();
413 if (isspace (c))
414 break;
415 #define STRING_ADD_CHAR(c) \
416 if (do_assign) \
418 *str++ = c; \
419 if (malloc_string && str == *strptr + strsize) \
421 /* Enlarge the buffer. */ \
422 str = realloc (*strptr, strsize * 2); \
423 if (str == NULL) \
425 /* Can't allocate that much. Last-ditch effort. */\
426 str = realloc (*strptr, strsize + 1); \
427 if (str == NULL) \
429 /* We lose. Oh well. \
430 Terminate the string and stop converting, \
431 so at least we don't swallow any input. */ \
432 (*strptr)[strsize] = '\0'; \
433 ++done; \
434 conv_error (); \
436 else \
438 *strptr = str; \
439 str += strsize; \
440 ++strsize; \
443 else \
445 *strptr = str; \
446 str += strsize; \
447 strsize *= 2; \
451 STRING_ADD_CHAR (c);
452 } while (inchar () != EOF && (width <= 0 || --width > 0));
454 if (do_assign)
456 *str = '\0';
457 ++done;
459 break;
461 case 'x': /* Hexadecimal integer. */
462 case 'X': /* Ditto. */
463 base = 16;
464 number_signed = 0;
465 goto number;
467 case 'o': /* Octal integer. */
468 base = 8;
469 number_signed = 0;
470 goto number;
472 case 'u': /* Unsigned decimal integer. */
473 base = 10;
474 number_signed = 0;
475 goto number;
477 case 'd': /* Signed decimal integer. */
478 base = 10;
479 number_signed = 1;
480 goto number;
482 case 'i': /* Generic number. */
483 base = 0;
484 number_signed = 1;
486 number:
487 if (c == EOF)
488 input_error ();
490 /* Check for a sign. */
491 if (c == '-' || c == '+')
493 *w++ = c;
494 if (width > 0)
495 --width;
496 (void) inchar ();
499 /* Look for a leading indication of base. */
500 if (c == '0')
502 if (width > 0)
503 --width;
504 *w++ = '0';
506 (void) inchar ();
508 if (tolower (c) == 'x')
510 if (base == 0)
511 base = 16;
512 if (base == 16)
514 if (width > 0)
515 --width;
516 (void) inchar ();
519 else if (base == 0)
520 base = 8;
523 if (base == 0)
524 base = 10;
526 /* Read the number into WORK. */
529 if (base == 16 ? !isxdigit (c) :
530 (!isdigit (c) || c - '0' >= base))
531 break;
532 *w++ = c;
533 if (width > 0)
534 --width;
536 while (inchar () != EOF && width != 0);
538 if (w == work ||
539 (w - work == 1 && (work[0] == '+' || work[0] == '-')))
540 /* There was no number. */
541 conv_error ();
543 /* Convert the number. */
544 *w = '\0';
545 if (is_longlong)
547 if (number_signed)
548 num.q = __strtoq_internal (work, &w, base, group_flag);
549 else
550 num.uq = __strtouq_internal (work, &w, base, group_flag);
552 else
554 if (number_signed)
555 num.l = __strtol_internal (work, &w, base, group_flag);
556 else
557 num.ul = __strtoul_internal (work, &w, base, group_flag);
559 if (w == work)
560 conv_error ();
562 if (do_assign)
564 if (! number_signed)
566 if (is_longlong)
567 *ARG (unsigned LONGLONG int *) = num.uq;
568 else if (is_long)
569 *ARG (unsigned long int *) = num.ul;
570 else if (is_short)
571 *ARG (unsigned short int *)
572 = (unsigned short int) num.ul;
573 else
574 *ARG (unsigned int *) = (unsigned int) num.ul;
576 else
578 if (is_longlong)
579 *ARG (LONGLONG int *) = num.q;
580 else if (is_long)
581 *ARG (long int *) = num.l;
582 else if (is_short)
583 *ARG (short int *) = (short int) num.l;
584 else
585 *ARG (int *) = (int) num.l;
587 ++done;
589 break;
591 case 'e': /* Floating-point numbers. */
592 case 'E':
593 case 'f':
594 case 'g':
595 case 'G':
596 if (c == EOF)
597 input_error ();
599 /* Check for a sign. */
600 if (c == '-' || c == '+')
602 *w++ = c;
603 if (inchar () == EOF)
604 /* EOF is only an input error before we read any chars. */
605 conv_error ();
606 if (width > 0)
607 --width;
610 got_dot = got_e = 0;
613 if (isdigit (c))
614 *w++ = c;
615 else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
616 *w++ = c;
617 else if (!got_e && tolower (c) == 'e')
619 *w++ = 'e';
620 got_e = got_dot = 1;
622 else if (c == decimal && !got_dot)
624 *w++ = c;
625 got_dot = 1;
627 else
628 break;
629 if (width > 0)
630 --width;
631 } while (inchar () != EOF && width != 0);
633 if (w == work)
634 conv_error();
635 if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
636 conv_error ();
638 /* Convert the number. */
639 *w = '\0';
640 if (is_long_double)
642 long double d = __strtold_internal (work, &w, group_flag);
643 if (do_assign && w != work)
644 *ARG (long double *) = d;
646 else if (is_long)
648 double d = __strtod_internal (work, &w, group_flag);
649 if (do_assign && w != work)
650 *ARG (double *) = d;
652 else
654 float d = __strtof_internal (work, &w, group_flag);
655 if (do_assign && w != work)
656 *ARG (float *) = d;
659 if (w == work)
660 conv_error ();
662 if (do_assign)
663 ++done;
664 break;
666 case '[': /* Character class. */
667 STRING_ARG;
669 if (c == EOF)
670 input_error();
672 if (*f == '^')
674 ++f;
675 not_in = 1;
677 else
678 not_in = 0;
680 while ((fc = *f++) != '\0' && fc != ']')
682 if (fc == '-' && *f != '\0' && *f != ']' &&
683 w > work && w[-1] <= *f)
684 /* Add all characters from the one before the '-'
685 up to (but not including) the next format char. */
686 for (fc = w[-1] + 1; fc < *f; ++fc)
687 *w++ = fc;
688 else
689 /* Add the character to the list. */
690 *w++ = fc;
692 if (fc == '\0')
693 conv_error();
695 *w = '\0';
696 num.ul = read_in;
699 if ((strchr (work, c) == NULL) != not_in)
700 break;
701 STRING_ADD_CHAR (c);
702 if (width > 0)
703 --width;
704 } while (inchar () != EOF && width != 0);
705 if (read_in == num.ul)
706 conv_error ();
708 if (do_assign)
710 *str = '\0';
711 ++done;
713 break;
715 case 'p': /* Generic pointer. */
716 base = 16;
717 /* A PTR must be the same size as a `long int'. */
718 is_long = 1;
719 number_signed = 0;
720 goto number;
724 return ((c == EOF || ungetc (c, s)), done);
727 #ifdef USE_IN_LIBIO
729 __vfscanf (FILE *s, const char *format, va_list argptr)
731 return _IO_vfscanf (s, format, argptr, NULL);
733 #endif
735 weak_alias (__vfscanf, vfscanf)