initial import
[glibc.git] / stdio / vfscanf.c
blob681e89819b20d0a72fc5fc7b13cfe5164f53c6fd
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 <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
38 #define inchar() ((c = getc(s)) == EOF ? EOF : (++read_in, c))
39 #define conv_error() return ((c == EOF || ungetc(c, s)), done)
40 #define input_error() return (done == 0 ? EOF : done)
41 #define memory_error() return ((errno = ENOMEM), EOF)
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)
51 va_list arg = (va_list) argptr;
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. */
61 /* Type modifiers. */
62 char is_short, is_long, is_long_double;
63 #ifdef HAVE_LONGLONG
64 /* We use the `L' modifier for `long long int'. */
65 #define is_longlong is_long_double
66 #else
67 #define is_longlong 0
68 #endif
69 int malloc_string; /* Args are char ** to be filled in. */
70 /* Status for reading F-P nums. */
71 char got_dot, got_e;
72 /* If a [...] is a [^...]. */
73 char not_in;
74 /* Base for integral numbers. */
75 int base;
76 /* Signedness for integral numbers. */
77 int number_signed;
78 /* Integral holding variables. */
79 long int num;
80 unsigned long int unum;
81 /* Character-buffer pointer. */
82 register char *str, **strptr;
83 size_t strsize;
84 /* Workspace. */
85 char work[200];
86 char *w; /* Pointer into WORK. */
87 wchar_t decimal; /* Decimal point character. */
89 if (!__validfp(s) || !s->__mode.__read || format == NULL)
91 errno = EINVAL;
92 return EOF;
95 /* Figure out the decimal point character. */
96 if (mbtowc(&decimal, _numeric_info->decimal_point,
97 strlen(_numeric_info->decimal_point)) <= 0)
98 decimal = (wchar_t) *_numeric_info->decimal_point;
100 c = inchar();
102 /* Run through the format string. */
103 while (*f != '\0')
105 if (!isascii(*f))
107 /* Non-ASCII, may be a multibyte. */
108 int len = mblen(f, strlen(f));
109 if (len > 0)
111 while (len-- > 0)
112 if (c == EOF)
113 input_error();
114 else if (c == *f++)
115 (void) inchar();
116 else
117 conv_error();
118 continue;
122 fc = *f++;
123 if (fc != '%')
125 /* Characters other than format specs must just match. */
126 if (c == EOF)
127 input_error();
128 if (isspace(fc))
130 /* Whitespace characters match any amount of whitespace. */
131 while (isspace (c))
132 inchar ();
133 continue;
135 else if (c == fc)
136 (void) inchar();
137 else
138 conv_error();
139 continue;
142 /* Check for the assignment-suppressant. */
143 if (*f == '*')
145 do_assign = 0;
146 ++f;
148 else
149 do_assign = 1;
151 /* Find the maximum field width. */
152 width = 0;
153 while (isdigit(*f))
155 width *= 10;
156 width += *f++ - '0';
158 if (width == 0)
159 width = -1;
161 /* Check for type modifiers. */
162 is_short = is_long = is_long_double = malloc_string = 0;
163 while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a' || *f == 'q')
164 switch (*f++)
166 case 'h':
167 /* int's are short int's. */
168 is_short = 1;
169 break;
170 case 'l':
171 if (is_long)
172 /* A double `l' is equivalent to an `L'. */
173 is_longlong = 1;
174 else
175 /* int's are long int's. */
176 is_long = 1;
177 break;
178 case 'q':
179 case 'L':
180 /* double's are long double's, and int's are long long int's. */
181 is_long_double = 1;
182 break;
183 case 'a':
184 /* String conversions (%s, %[) take a `char **'
185 arg and fill it in with a malloc'd pointer. */
186 malloc_string = 1;
187 break;
190 /* End of the format string? */
191 if (*f == '\0')
192 conv_error();
194 /* Find the conversion specifier. */
195 w = work;
196 fc = *f++;
197 if (fc != '[' && fc != 'c' && fc != 'n')
198 /* Eat whitespace. */
199 while (isspace(c))
200 (void) inchar();
201 switch (fc)
203 case '%': /* Must match a literal '%'. */
204 if (c != fc)
205 conv_error();
206 break;
208 case 'n': /* Answer number of assignments done. */
209 if (do_assign)
210 *va_arg(arg, int *) = read_in;
211 break;
213 case 'c': /* Match characters. */
214 if (do_assign)
216 str = va_arg (arg, char *);
217 if (str == NULL)
218 conv_error ();
221 if (c == EOF)
222 input_error();
224 if (width == -1)
225 width = 1;
227 if (do_assign)
230 *str++ = c;
231 while (inchar() != EOF && --width > 0);
233 else
234 while (inchar() != EOF && width > 0)
235 --width;
237 if (do_assign)
238 ++done;
240 break;
242 case 's': /* Read a string. */
243 #define STRING_ARG \
244 if (do_assign) \
246 if (malloc_string) \
248 /* The string is to be stored in a malloc'd buffer. */ \
249 strptr = va_arg (arg, char **); \
250 if (strptr == NULL) \
251 conv_error (); \
252 /* Allocate an initial buffer. */ \
253 strsize = 100; \
254 *strptr = str = malloc (strsize); \
256 else \
257 str = va_arg (arg, char *); \
258 if (str == NULL) \
259 conv_error (); \
261 STRING_ARG;
263 if (c == EOF)
264 input_error ();
268 if (isspace (c))
269 break;
270 #define STRING_ADD_CHAR(c) \
271 if (do_assign) \
273 *str++ = c; \
274 if (malloc_string && str == *strptr + strsize) \
276 /* Enlarge the buffer. */ \
277 str = realloc (*strptr, strsize * 2); \
278 if (str == NULL) \
280 /* Can't allocate that much. Last-ditch effort. */\
281 str = realloc (*strptr, strsize + 1); \
282 if (str == NULL) \
284 /* We lose. Oh well. \
285 Terminate the string and stop converting, \
286 so at least we don't swallow any input. */ \
287 (*strptr)[strsize] = '\0'; \
288 ++done; \
289 conv_error (); \
291 else \
293 *strptr = str; \
294 str += strsize; \
295 ++strsize; \
298 else \
300 *strptr = str; \
301 str += strsize; \
302 strsize *= 2; \
306 STRING_ADD_CHAR (c);
307 } while (inchar () != EOF && (width <= 0 || --width > 0));
309 if (do_assign)
311 *str = '\0';
312 ++done;
314 break;
316 case 'x': /* Hexadecimal integer. */
317 case 'X': /* Ditto. */
318 base = 16;
319 number_signed = 0;
320 goto number;
322 case 'o': /* Octal integer. */
323 base = 8;
324 number_signed = 0;
325 goto number;
327 case 'u': /* Unsigned decimal integer. */
328 base = 10;
329 number_signed = 0;
330 goto number;
332 case 'd': /* Signed decimal integer. */
333 base = 10;
334 number_signed = 1;
335 goto number;
337 case 'i': /* Generic number. */
338 base = 0;
339 number_signed = 1;
341 number:
342 if (c == EOF)
343 input_error();
345 /* Check for a sign. */
346 if (c == '-' || c == '+')
348 *w++ = c;
349 if (width > 0)
350 --width;
351 (void) inchar();
354 /* Look for a leading indication of base. */
355 if (c == '0')
357 if (width > 0)
358 --width;
359 *w++ = '0';
361 (void) inchar();
363 if (tolower(c) == 'x')
365 if (base == 0)
366 base = 16;
367 if (base == 16)
369 if (width > 0)
370 --width;
371 (void) inchar();
374 else if (base == 0)
375 base = 8;
378 if (base == 0)
379 base = 10;
381 /* Read the number into WORK. */
384 if (base == 16 ? !isxdigit(c) :
385 (!isdigit(c) || c - '0' >= base))
386 break;
387 *w++ = c;
388 if (width > 0)
389 --width;
390 } while (inchar() != EOF && width != 0);
392 if (w == work ||
393 (w - work == 1 && (work[0] == '+' || work[0] == '-')))
394 /* There was on number. */
395 conv_error();
397 /* Convert the number. */
398 *w = '\0';
399 if (number_signed)
400 num = strtol (work, &w, base);
401 else
402 unum = strtoul (work, &w, base);
403 if (w == work)
404 conv_error ();
406 if (do_assign)
408 if (! number_signed)
410 if (is_longlong)
411 *va_arg (arg, unsigned LONGLONG int *) = unum;
412 else if (is_long)
413 *va_arg (arg, unsigned long int *) = unum;
414 else if (is_short)
415 *va_arg (arg, unsigned short int *)
416 = (unsigned short int) unum;
417 else
418 *va_arg(arg, unsigned int *) = (unsigned int) unum;
420 else
422 if (is_longlong)
423 *va_arg(arg, LONGLONG int *) = num;
424 else if (is_long)
425 *va_arg(arg, long int *) = num;
426 else if (is_short)
427 *va_arg(arg, short int *) = (short int) num;
428 else
429 *va_arg(arg, int *) = (int) num;
431 ++done;
433 break;
435 case 'e': /* Floating-point numbers. */
436 case 'E':
437 case 'f':
438 case 'g':
439 case 'G':
440 if (c == EOF)
441 input_error();
443 /* Check for a sign. */
444 if (c == '-' || c == '+')
446 *w++ = c;
447 if (inchar() == EOF)
448 /* EOF is only an input error before we read any chars. */
449 conv_error();
450 if (width > 0)
451 --width;
454 got_dot = got_e = 0;
457 if (isdigit(c))
458 *w++ = c;
459 else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
460 *w++ = c;
461 else if (!got_e && tolower(c) == 'e')
463 *w++ = 'e';
464 got_e = got_dot = 1;
466 else if (c == decimal && !got_dot)
468 *w++ = c;
469 got_dot = 1;
471 else
472 break;
473 if (width > 0)
474 --width;
475 } while (inchar() != EOF && width != 0);
477 if (w == work)
478 conv_error();
479 if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
480 conv_error();
482 /* Convert the number. */
483 *w = '\0';
484 if (is_long_double)
486 long double d = __strtold (work, &w);
487 if (do_assign && w != work)
488 *va_arg (arg, long double *) = d;
490 else if (is_long)
492 double d = strtod (work, &w);
493 if (do_assign && w != work)
494 *va_arg (arg, double *) = d;
496 else
498 float d = __strtof (work, &w);
499 if (do_assign && w != work)
500 *va_arg (arg, float *) = d;
503 if (w == work)
504 conv_error ();
506 if (do_assign)
507 ++done;
508 break;
510 case '[': /* Character class. */
511 STRING_ARG;
513 if (c == EOF)
514 input_error();
516 if (*f == '^')
518 ++f;
519 not_in = 1;
521 else
522 not_in = 0;
524 while ((fc = *f++) != '\0' && fc != ']')
526 if (fc == '-' && *f != '\0' && *f != ']' &&
527 w > work && w[-1] <= *f)
528 /* Add all characters from the one before the '-'
529 up to (but not including) the next format char. */
530 for (fc = w[-1] + 1; fc < *f; ++fc)
531 *w++ = fc;
532 else
533 /* Add the character to the list. */
534 *w++ = fc;
536 if (fc == '\0')
537 conv_error();
539 *w = '\0';
540 unum = read_in;
543 if ((strchr (work, c) == NULL) != not_in)
544 break;
545 STRING_ADD_CHAR (c);
546 if (width > 0)
547 --width;
548 } while (inchar () != EOF && width != 0);
549 if (read_in == unum)
550 conv_error ();
552 if (do_assign)
554 *str = '\0';
555 ++done;
557 break;
559 case 'p': /* Generic pointer. */
560 base = 16;
561 /* A PTR must be the same size as a `long int'. */
562 is_long = 1;
563 goto number;
567 conv_error();
570 weak_alias (__vfscanf, vfscanf)