]> git.saurik.com Git - apple/libc.git/blame - stdio/FreeBSD/vfscanf.c
Libc-763.13.tar.gz
[apple/libc.git] / stdio / FreeBSD / vfscanf.c
CommitLineData
9385eb3d 1/*-
e9ce8d39
A
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
e9ce8d39
A
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
9385eb3d
A
33#if defined(LIBC_SCCS) && !defined(lint)
34static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93";
35#endif /* LIBC_SCCS and not lint */
36#include <sys/cdefs.h>
1f2f436a 37__FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.43 2009/01/19 06:19:51 das Exp $");
e9ce8d39 38
9385eb3d
A
39#include "namespace.h"
40#include <ctype.h>
41#include <inttypes.h>
e9ce8d39
A
42#include <stdio.h>
43#include <stdlib.h>
9385eb3d 44#include <stddef.h>
e9ce8d39 45#include <stdarg.h>
9385eb3d
A
46#include <string.h>
47#include <wchar.h>
48#include <wctype.h>
49#include "un-namespace.h"
50
51#include "collate.h"
52#include "libc_private.h"
e9ce8d39
A
53#include "local.h"
54
3d9156a7 55#ifndef NO_FLOATING_POINT
9385eb3d
A
56#include <locale.h>
57#endif
58
e9ce8d39
A
59#define BUF 513 /* Maximum length of numeric string. */
60
61/*
62 * Flags used during conversion.
63 */
64#define LONG 0x01 /* l: long or double */
9385eb3d 65#define LONGDBL 0x02 /* L: long double */
e9ce8d39 66#define SHORT 0x04 /* h: short */
9385eb3d
A
67#define SUPPRESS 0x08 /* *: suppress assignment */
68#define POINTER 0x10 /* p: void * (as hex) */
69#define NOSKIP 0x20 /* [ or c: do not skip blanks */
70#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
71#define INTMAXT 0x800 /* j: intmax_t */
72#define PTRDIFFT 0x1000 /* t: ptrdiff_t */
73#define SIZET 0x2000 /* z: size_t */
74#define SHORTSHORT 0x4000 /* hh: char */
75#define UNSIGNED 0x8000 /* %[oupxX] conversions */
e9ce8d39
A
76
77/*
9385eb3d
A
78 * The following are used in integral conversions only:
79 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
e9ce8d39
A
80 */
81#define SIGNOK 0x40 /* +/- is (still) legal */
82#define NDIGITS 0x80 /* no digits detected */
e9ce8d39
A
83#define PFXOK 0x100 /* 0x prefix is (still) legal */
84#define NZDIGITS 0x200 /* no zero digits detected */
3d9156a7 85#define HAVESIGN 0x10000 /* sign detected */
e9ce8d39
A
86
87/*
88 * Conversion types.
89 */
90#define CT_CHAR 0 /* %c conversion */
91#define CT_CCL 1 /* %[...] conversion */
92#define CT_STRING 2 /* %s conversion */
9385eb3d
A
93#define CT_INT 3 /* %[dioupxX] conversion */
94#define CT_FLOAT 4 /* %[efgEFG] conversion */
95
96static const u_char *__sccl(char *, const u_char *);
1f2f436a 97#ifndef NO_FLOATING_POINT
9385eb3d 98static int parsefloat(FILE *, char *, char *);
1f2f436a 99#endif
9385eb3d
A
100
101__weak_reference(__vfscanf, vfscanf);
102
103/*
104 * __vfscanf - MT-safe version
105 */
106int
107__vfscanf(FILE *fp, char const *fmt0, va_list ap)
108{
109 int ret;
e9ce8d39 110
9385eb3d
A
111 FLOCKFILE(fp);
112 ret = __svfscanf(fp, fmt0, ap);
113 FUNLOCKFILE(fp);
114 return (ret);
115}
e9ce8d39
A
116
117/*
9385eb3d 118 * __svfscanf - non-MT-safe version of __vfscanf
e9ce8d39
A
119 */
120int
9385eb3d 121__svfscanf(FILE *fp, const char *fmt0, va_list ap)
e9ce8d39 122{
9385eb3d
A
123 const u_char *fmt = (const u_char *)fmt0;
124 int c; /* character from format, or conversion */
125 size_t width; /* field width, or 0 */
126 char *p; /* points into all kinds of strings */
127 int n; /* handy integer */
128 int flags; /* flags as defined above */
129 char *p0; /* saves original value of p when necessary */
e9ce8d39 130 int nassigned; /* number of fields assigned */
9385eb3d 131 int nconversions; /* number of conversions */
e9ce8d39 132 int nread; /* number of characters consumed from fp */
9385eb3d 133 int base; /* base argument to conversion function */
e9ce8d39 134 char ccltab[256]; /* character class table for %[...] */
9385eb3d
A
135 char buf[BUF]; /* buffer for numeric and mb conversions */
136 wchar_t *wcp; /* handy wide character pointer */
9385eb3d 137 size_t nconv; /* length of multibyte sequence converted */
3d9156a7
A
138 static const mbstate_t initial;
139 mbstate_t mbs;
e9ce8d39
A
140
141 /* `basefix' is used to avoid `if' tests in the integer scanner */
142 static short basefix[17] =
143 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
144
9385eb3d
A
145 ORIENT(fp, -1);
146
e9ce8d39 147 nassigned = 0;
9385eb3d 148 nconversions = 0;
e9ce8d39 149 nread = 0;
e9ce8d39
A
150 for (;;) {
151 c = *fmt++;
152 if (c == 0)
153 return (nassigned);
154 if (isspace(c)) {
9385eb3d 155 while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
e9ce8d39 156 nread++, fp->_r--, fp->_p++;
e9ce8d39
A
157 continue;
158 }
159 if (c != '%')
160 goto literal;
161 width = 0;
162 flags = 0;
163 /*
164 * switch on the format. continue if done;
165 * break once format type is derived.
166 */
167again: c = *fmt++;
168 switch (c) {
169 case '%':
170literal:
171 if (fp->_r <= 0 && __srefill(fp))
172 goto input_failure;
173 if (*fp->_p != c)
174 goto match_failure;
175 fp->_r--, fp->_p++;
176 nread++;
177 continue;
178
179 case '*':
180 flags |= SUPPRESS;
181 goto again;
9385eb3d
A
182 case 'j':
183 flags |= INTMAXT;
184 goto again;
e9ce8d39 185 case 'l':
9385eb3d
A
186 if (flags & LONG) {
187 flags &= ~LONG;
188 flags |= LONGLONG;
189 } else
5b2abdfb 190 flags |= LONG;
e9ce8d39 191 goto again;
3b2a1fe8 192 case 'q':
9385eb3d
A
193 flags |= LONGLONG; /* not quite */
194 goto again;
195 case 't':
196 flags |= PTRDIFFT;
3b2a1fe8 197 goto again;
5b2abdfb 198 case 'z':
9385eb3d 199 flags |= SIZET;
5b2abdfb 200 goto again;
e9ce8d39
A
201 case 'L':
202 flags |= LONGDBL;
203 goto again;
204 case 'h':
9385eb3d
A
205 if (flags & SHORT) {
206 flags &= ~SHORT;
207 flags |= SHORTSHORT;
208 } else
209 flags |= SHORT;
e9ce8d39
A
210 goto again;
211
212 case '0': case '1': case '2': case '3': case '4':
213 case '5': case '6': case '7': case '8': case '9':
214 width = width * 10 + c - '0';
215 goto again;
216
217 /*
218 * Conversions.
e9ce8d39 219 */
e9ce8d39
A
220 case 'd':
221 c = CT_INT;
e9ce8d39
A
222 base = 10;
223 break;
224
225 case 'i':
226 c = CT_INT;
e9ce8d39
A
227 base = 0;
228 break;
229
e9ce8d39
A
230 case 'o':
231 c = CT_INT;
9385eb3d 232 flags |= UNSIGNED;
e9ce8d39
A
233 base = 8;
234 break;
235
236 case 'u':
237 c = CT_INT;
9385eb3d 238 flags |= UNSIGNED;
e9ce8d39
A
239 base = 10;
240 break;
241
9385eb3d 242 case 'X':
e9ce8d39
A
243 case 'x':
244 flags |= PFXOK; /* enable 0x prefixing */
245 c = CT_INT;
9385eb3d 246 flags |= UNSIGNED;
e9ce8d39
A
247 base = 16;
248 break;
249
3d9156a7 250#ifndef NO_FLOATING_POINT
9385eb3d
A
251 case 'A': case 'E': case 'F': case 'G':
252 case 'a': case 'e': case 'f': case 'g':
e9ce8d39
A
253 c = CT_FLOAT;
254 break;
255#endif
256
9385eb3d
A
257 case 'S':
258 flags |= LONG;
259 /* FALLTHROUGH */
e9ce8d39
A
260 case 's':
261 c = CT_STRING;
262 break;
263
264 case '[':
265 fmt = __sccl(ccltab, fmt);
266 flags |= NOSKIP;
267 c = CT_CCL;
268 break;
269
9385eb3d
A
270 case 'C':
271 flags |= LONG;
272 /* FALLTHROUGH */
e9ce8d39
A
273 case 'c':
274 flags |= NOSKIP;
275 c = CT_CHAR;
276 break;
277
278 case 'p': /* pointer format is like hex */
279 flags |= POINTER | PFXOK;
9385eb3d
A
280 c = CT_INT; /* assumes sizeof(uintmax_t) */
281 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
e9ce8d39
A
282 base = 16;
283 break;
284
285 case 'n':
9385eb3d 286 nconversions++;
e9ce8d39
A
287 if (flags & SUPPRESS) /* ??? */
288 continue;
9385eb3d
A
289 if (flags & SHORTSHORT)
290 *va_arg(ap, char *) = nread;
291 else if (flags & SHORT)
e9ce8d39
A
292 *va_arg(ap, short *) = nread;
293 else if (flags & LONG)
294 *va_arg(ap, long *) = nread;
9385eb3d
A
295 else if (flags & LONGLONG)
296 *va_arg(ap, long long *) = nread;
297 else if (flags & INTMAXT)
298 *va_arg(ap, intmax_t *) = nread;
299 else if (flags & SIZET)
300 *va_arg(ap, size_t *) = nread;
301 else if (flags & PTRDIFFT)
302 *va_arg(ap, ptrdiff_t *) = nread;
e9ce8d39
A
303 else
304 *va_arg(ap, int *) = nread;
305 continue;
306
9385eb3d
A
307 default:
308 goto match_failure;
309
e9ce8d39 310 /*
9385eb3d 311 * Disgusting backwards compatibility hack. XXX
e9ce8d39
A
312 */
313 case '\0': /* compat */
314 return (EOF);
e9ce8d39
A
315 }
316
317 /*
318 * We have a conversion that requires input.
319 */
320 if (fp->_r <= 0 && __srefill(fp))
321 goto input_failure;
322
323 /*
324 * Consume leading white space, except for formats
325 * that suppress this.
326 */
327 if ((flags & NOSKIP) == 0) {
328 while (isspace(*fp->_p)) {
329 nread++;
330 if (--fp->_r > 0)
331 fp->_p++;
332 else if (__srefill(fp))
333 goto input_failure;
334 }
335 /*
336 * Note that there is at least one character in
337 * the buffer, so conversions that do not set NOSKIP
338 * ca no longer result in an input failure.
339 */
340 }
341
342 /*
343 * Do the conversion.
344 */
345 switch (c) {
346
347 case CT_CHAR:
348 /* scan arbitrary characters (sets NOSKIP) */
349 if (width == 0)
350 width = 1;
9385eb3d
A
351 if (flags & LONG) {
352 if ((flags & SUPPRESS) == 0)
353 wcp = va_arg(ap, wchar_t *);
354 else
355 wcp = NULL;
356 n = 0;
357 while (width != 0) {
358 if (n == MB_CUR_MAX) {
359 fp->_flags |= __SERR;
360 goto input_failure;
361 }
362 buf[n++] = *fp->_p;
363 fp->_p++;
364 fp->_r--;
3d9156a7 365 mbs = initial;
9385eb3d
A
366 nconv = mbrtowc(wcp, buf, n, &mbs);
367 if (nconv == (size_t)-1) {
368 fp->_flags |= __SERR;
369 goto input_failure;
370 }
371 if (nconv == 0 && !(flags & SUPPRESS))
372 *wcp = L'\0';
373 if (nconv != (size_t)-2) {
374 nread += n;
375 width--;
376 if (!(flags & SUPPRESS))
377 wcp++;
378 n = 0;
379 }
380 if (fp->_r <= 0 && __srefill(fp)) {
381 if (n != 0) {
382 fp->_flags |= __SERR;
383 goto input_failure;
384 }
385 break;
386 }
387 }
388 if (!(flags & SUPPRESS))
389 nassigned++;
390 } else if (flags & SUPPRESS) {
e9ce8d39
A
391 size_t sum = 0;
392 for (;;) {
393 if ((n = fp->_r) < width) {
394 sum += n;
395 width -= n;
396 fp->_p += n;
397 if (__srefill(fp)) {
398 if (sum == 0)
399 goto input_failure;
400 break;
401 }
402 } else {
403 sum += width;
404 fp->_r -= width;
405 fp->_p += width;
406 break;
407 }
408 }
409 nread += sum;
410 } else {
1f2f436a 411 size_t r = __fread((void *)va_arg(ap, char *), 1,
e9ce8d39
A
412 width, fp);
413
414 if (r == 0)
415 goto input_failure;
416 nread += r;
417 nassigned++;
418 }
9385eb3d 419 nconversions++;
e9ce8d39
A
420 break;
421
422 case CT_CCL:
423 /* scan a (nonempty) character class (sets NOSKIP) */
424 if (width == 0)
9385eb3d 425 width = (size_t)~0; /* `infinity' */
e9ce8d39 426 /* take only those things in the class */
9385eb3d
A
427 if (flags & LONG) {
428 wchar_t twc;
429 int nchars;
430
431 if ((flags & SUPPRESS) == 0)
1f2f436a 432 wcp = va_arg(ap, wchar_t *);
9385eb3d 433 else
1f2f436a 434 wcp = &twc;
9385eb3d
A
435 n = 0;
436 nchars = 0;
437 while (width != 0) {
438 if (n == MB_CUR_MAX) {
439 fp->_flags |= __SERR;
440 goto input_failure;
441 }
442 buf[n++] = *fp->_p;
443 fp->_p++;
444 fp->_r--;
3d9156a7 445 mbs = initial;
9385eb3d
A
446 nconv = mbrtowc(wcp, buf, n, &mbs);
447 if (nconv == (size_t)-1) {
448 fp->_flags |= __SERR;
449 goto input_failure;
450 }
451 if (nconv == 0)
452 *wcp = L'\0';
453 if (nconv != (size_t)-2) {
454 if (wctob(*wcp) != EOF &&
455 !ccltab[wctob(*wcp)]) {
456 while (n != 0) {
457 n--;
458 __ungetc(buf[n],
459 fp);
460 }
461 break;
462 }
463 nread += n;
464 width--;
465 if (!(flags & SUPPRESS))
466 wcp++;
467 nchars++;
468 n = 0;
469 }
470 if (fp->_r <= 0 && __srefill(fp)) {
471 if (n != 0) {
472 fp->_flags |= __SERR;
473 goto input_failure;
474 }
475 break;
476 }
477 }
478 if (n != 0) {
479 fp->_flags |= __SERR;
480 goto input_failure;
481 }
482 n = nchars;
483 if (n == 0)
484 goto match_failure;
485 if (!(flags & SUPPRESS)) {
486 *wcp = L'\0';
487 nassigned++;
488 }
489 } else if (flags & SUPPRESS) {
e9ce8d39
A
490 n = 0;
491 while (ccltab[*fp->_p]) {
492 n++, fp->_r--, fp->_p++;
493 if (--width == 0)
494 break;
495 if (fp->_r <= 0 && __srefill(fp)) {
496 if (n == 0)
497 goto input_failure;
498 break;
499 }
500 }
501 if (n == 0)
502 goto match_failure;
503 } else {
504 p0 = p = va_arg(ap, char *);
505 while (ccltab[*fp->_p]) {
506 fp->_r--;
507 *p++ = *fp->_p++;
508 if (--width == 0)
509 break;
510 if (fp->_r <= 0 && __srefill(fp)) {
511 if (p == p0)
512 goto input_failure;
513 break;
514 }
515 }
516 n = p - p0;
517 if (n == 0)
518 goto match_failure;
519 *p = 0;
520 nassigned++;
521 }
522 nread += n;
9385eb3d 523 nconversions++;
e9ce8d39
A
524 break;
525
526 case CT_STRING:
527 /* like CCL, but zero-length string OK, & no NOSKIP */
528 if (width == 0)
9385eb3d
A
529 width = (size_t)~0;
530 if (flags & LONG) {
531 wchar_t twc;
532
533 if ((flags & SUPPRESS) == 0)
534 wcp = va_arg(ap, wchar_t *);
535 else
536 wcp = &twc;
537 n = 0;
538 while (!isspace(*fp->_p) && width != 0) {
539 if (n == MB_CUR_MAX) {
540 fp->_flags |= __SERR;
541 goto input_failure;
542 }
543 buf[n++] = *fp->_p;
544 fp->_p++;
545 fp->_r--;
3d9156a7 546 mbs = initial;
9385eb3d
A
547 nconv = mbrtowc(wcp, buf, n, &mbs);
548 if (nconv == (size_t)-1) {
549 fp->_flags |= __SERR;
550 goto input_failure;
551 }
552 if (nconv == 0)
553 *wcp = L'\0';
554 if (nconv != (size_t)-2) {
555 if (iswspace(*wcp)) {
556 while (n != 0) {
557 n--;
558 __ungetc(buf[n],
559 fp);
560 }
561 break;
562 }
563 nread += n;
564 width--;
565 if (!(flags & SUPPRESS))
566 wcp++;
567 n = 0;
568 }
569 if (fp->_r <= 0 && __srefill(fp)) {
570 if (n != 0) {
571 fp->_flags |= __SERR;
572 goto input_failure;
573 }
574 break;
575 }
576 }
577 if (!(flags & SUPPRESS)) {
578 *wcp = L'\0';
579 nassigned++;
580 }
581 } else if (flags & SUPPRESS) {
e9ce8d39
A
582 n = 0;
583 while (!isspace(*fp->_p)) {
584 n++, fp->_r--, fp->_p++;
585 if (--width == 0)
586 break;
587 if (fp->_r <= 0 && __srefill(fp))
588 break;
589 }
590 nread += n;
591 } else {
592 p0 = p = va_arg(ap, char *);
593 while (!isspace(*fp->_p)) {
594 fp->_r--;
595 *p++ = *fp->_p++;
596 if (--width == 0)
597 break;
598 if (fp->_r <= 0 && __srefill(fp))
599 break;
600 }
601 *p = 0;
602 nread += p - p0;
603 nassigned++;
604 }
9385eb3d 605 nconversions++;
e9ce8d39
A
606 continue;
607
608 case CT_INT:
9385eb3d 609 /* scan an integer as if by the conversion function */
e9ce8d39
A
610#ifdef hardway
611 if (width == 0 || width > sizeof(buf) - 1)
612 width = sizeof(buf) - 1;
613#else
614 /* size_t is unsigned, hence this optimisation */
615 if (--width > sizeof(buf) - 2)
616 width = sizeof(buf) - 2;
617 width++;
618#endif
619 flags |= SIGNOK | NDIGITS | NZDIGITS;
620 for (p = buf; width; width--) {
621 c = *fp->_p;
622 /*
623 * Switch on the character; `goto ok'
624 * if we accept it as a part of number.
625 */
626 switch (c) {
627
628 /*
629 * The digit 0 is always legal, but is
630 * special. For %i conversions, if no
631 * digits (zero or nonzero) have been
632 * scanned (only signs), we will have
633 * base==0. In that case, we should set
634 * it to 8 and enable 0x prefixing.
635 * Also, if we have not scanned zero digits
636 * before this, do not turn off prefixing
637 * (someone else will turn it off if we
638 * have scanned any nonzero digits).
639 */
640 case '0':
641 if (base == 0) {
642 base = 8;
643 flags |= PFXOK;
644 }
645 if (flags & NZDIGITS)
646 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
647 else
648 flags &= ~(SIGNOK|PFXOK|NDIGITS);
649 goto ok;
650
651 /* 1 through 7 always legal */
652 case '1': case '2': case '3':
653 case '4': case '5': case '6': case '7':
654 base = basefix[base];
655 flags &= ~(SIGNOK | PFXOK | NDIGITS);
656 goto ok;
657
658 /* digits 8 and 9 ok iff decimal or hex */
659 case '8': case '9':
660 base = basefix[base];
661 if (base <= 8)
662 break; /* not legal here */
663 flags &= ~(SIGNOK | PFXOK | NDIGITS);
664 goto ok;
665
666 /* letters ok iff hex */
667 case 'A': case 'B': case 'C':
668 case 'D': case 'E': case 'F':
669 case 'a': case 'b': case 'c':
670 case 'd': case 'e': case 'f':
671 /* no need to fix base here */
672 if (base <= 10)
673 break; /* not legal here */
674 flags &= ~(SIGNOK | PFXOK | NDIGITS);
675 goto ok;
676
677 /* sign ok only as first character */
678 case '+': case '-':
679 if (flags & SIGNOK) {
680 flags &= ~SIGNOK;
3d9156a7 681 flags |= HAVESIGN;
e9ce8d39
A
682 goto ok;
683 }
684 break;
3d9156a7
A
685
686 /*
687 * x ok iff flag still set & 2nd char (or
688 * 3rd char if we have a sign).
689 */
e9ce8d39 690 case 'x': case 'X':
3d9156a7
A
691 if (flags & PFXOK && p ==
692 buf + 1 + !!(flags & HAVESIGN)) {
e9ce8d39
A
693 base = 16; /* if %i */
694 flags &= ~PFXOK;
695 goto ok;
696 }
697 break;
698 }
699
700 /*
701 * If we got here, c is not a legal character
702 * for a number. Stop accumulating digits.
703 */
704 break;
705 ok:
706 /*
707 * c is legal: store it and look at the next.
708 */
709 *p++ = c;
710 if (--fp->_r > 0)
711 fp->_p++;
712 else if (__srefill(fp))
713 break; /* EOF */
714 }
715 /*
716 * If we had only a sign, it is no good; push
717 * back the sign. If the number ends in `x',
718 * it was [sign] '0' 'x', so push back the x
719 * and treat it as [sign] '0'.
720 */
721 if (flags & NDIGITS) {
722 if (p > buf)
9385eb3d 723 (void) __ungetc(*(u_char *)--p, fp);
e9ce8d39
A
724 goto match_failure;
725 }
726 c = ((u_char *)p)[-1];
727 if (c == 'x' || c == 'X') {
728 --p;
9385eb3d 729 (void) __ungetc(c, fp);
e9ce8d39
A
730 }
731 if ((flags & SUPPRESS) == 0) {
9385eb3d 732 uintmax_t res;
e9ce8d39
A
733
734 *p = 0;
9385eb3d
A
735 if ((flags & UNSIGNED) == 0)
736 res = strtoimax(buf, (char **)NULL, base);
737 else
738 res = strtoumax(buf, (char **)NULL, base);
e9ce8d39 739 if (flags & POINTER)
9385eb3d
A
740 *va_arg(ap, void **) =
741 (void *)(uintptr_t)res;
742 else if (flags & SHORTSHORT)
743 *va_arg(ap, char *) = res;
e9ce8d39
A
744 else if (flags & SHORT)
745 *va_arg(ap, short *) = res;
5b2abdfb
A
746 else if (flags & LONG)
747 *va_arg(ap, long *) = res;
9385eb3d
A
748 else if (flags & LONGLONG)
749 *va_arg(ap, long long *) = res;
750 else if (flags & INTMAXT)
751 *va_arg(ap, intmax_t *) = res;
752 else if (flags & PTRDIFFT)
753 *va_arg(ap, ptrdiff_t *) = res;
754 else if (flags & SIZET)
755 *va_arg(ap, size_t *) = res;
e9ce8d39
A
756 else
757 *va_arg(ap, int *) = res;
758 nassigned++;
759 }
760 nread += p - buf;
9385eb3d 761 nconversions++;
e9ce8d39
A
762 break;
763
3d9156a7 764#ifndef NO_FLOATING_POINT
e9ce8d39
A
765 case CT_FLOAT:
766 /* scan a floating point number as if by strtod */
e9ce8d39
A
767 if (width == 0 || width > sizeof(buf) - 1)
768 width = sizeof(buf) - 1;
9385eb3d
A
769 if ((width = parsefloat(fp, buf, buf + width)) == 0)
770 goto match_failure;
e9ce8d39 771 if ((flags & SUPPRESS) == 0) {
9385eb3d
A
772 if (flags & LONGDBL) {
773 long double res = strtold(buf, &p);
3b2a1fe8 774 *va_arg(ap, long double *) = res;
9385eb3d
A
775 } else if (flags & LONG) {
776 double res = strtod(buf, &p);
e9ce8d39 777 *va_arg(ap, double *) = res;
9385eb3d
A
778 } else {
779 float res = strtof(buf, &p);
e9ce8d39 780 *va_arg(ap, float *) = res;
9385eb3d 781 }
e9ce8d39
A
782 nassigned++;
783 }
9385eb3d
A
784 nread += width;
785 nconversions++;
e9ce8d39 786 break;
3d9156a7 787#endif /* !NO_FLOATING_POINT */
e9ce8d39
A
788 }
789 }
790input_failure:
9385eb3d 791 return (nconversions != 0 ? nassigned : EOF);
e9ce8d39
A
792match_failure:
793 return (nassigned);
794}
795
796/*
797 * Fill in the given table from the scanset at the given format
798 * (just after `['). Return a pointer to the character past the
799 * closing `]'. The table has a 1 wherever characters should be
800 * considered part of the scanset.
801 */
9385eb3d 802static const u_char *
e9ce8d39 803__sccl(tab, fmt)
9385eb3d
A
804 char *tab;
805 const u_char *fmt;
e9ce8d39 806{
9385eb3d 807 int c, n, v, i;
e9ce8d39
A
808
809 /* first `clear' the whole table */
810 c = *fmt++; /* first char hat => negated scanset */
811 if (c == '^') {
812 v = 1; /* default => accept */
813 c = *fmt++; /* get new first char */
814 } else
815 v = 0; /* default => reject */
9385eb3d
A
816
817 /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
818 (void) memset(tab, v, 256);
819
e9ce8d39
A
820 if (c == 0)
821 return (fmt - 1);/* format ended before closing ] */
822
823 /*
824 * Now set the entries corresponding to the actual scanset
825 * to the opposite of the above.
826 *
827 * The first character may be ']' (or '-') without being special;
828 * the last character may be '-'.
829 */
830 v = 1 - v;
831 for (;;) {
832 tab[c] = v; /* take character c */
833doswitch:
834 n = *fmt++; /* and examine the next */
835 switch (n) {
836
837 case 0: /* format ended too soon */
838 return (fmt - 1);
839
840 case '-':
841 /*
842 * A scanset of the form
843 * [01+-]
844 * is defined as `the digit 0, the digit 1,
845 * the character +, the character -', but
846 * the effect of a scanset such as
847 * [a-zA-Z0-9]
848 * is implementation defined. The V7 Unix
849 * scanf treats `a-z' as `the letters a through
850 * z', but treats `a-a' as `the letter a, the
851 * character -, and the letter a'.
852 *
853 * For compatibility, the `-' is not considerd
854 * to define a range if the character following
855 * it is either a close bracket (required by ANSI)
856 * or is not numerically greater than the character
857 * we just stored in the table (c).
858 */
859 n = *fmt;
9385eb3d
A
860 if (n == ']'
861 || (__collate_load_error ? n < c :
862 __collate_range_cmp (n, c) < 0
863 )
864 ) {
e9ce8d39
A
865 c = '-';
866 break; /* resume the for(;;) */
867 }
868 fmt++;
9385eb3d
A
869 /* fill in the range */
870 if (__collate_load_error) {
871 do {
872 tab[++c] = v;
873 } while (c < n);
874 } else {
875 for (i = 0; i < 256; i ++)
876 if ( __collate_range_cmp (c, i) < 0
877 && __collate_range_cmp (i, n) <= 0
878 )
879 tab[i] = v;
880 }
e9ce8d39 881#if 1 /* XXX another disgusting compatibility hack */
9385eb3d 882 c = n;
e9ce8d39
A
883 /*
884 * Alas, the V7 Unix scanf also treats formats
885 * such as [a-c-e] as `the letters a through e'.
886 * This too is permitted by the standard....
887 */
888 goto doswitch;
889#else
890 c = *fmt++;
891 if (c == 0)
892 return (fmt - 1);
893 if (c == ']')
894 return (fmt);
895#endif
896 break;
897
898 case ']': /* end of scanset */
899 return (fmt);
900
901 default: /* just another character */
902 c = n;
903 break;
904 }
905 }
906 /* NOTREACHED */
907}
9385eb3d 908
3d9156a7 909#ifndef NO_FLOATING_POINT
9385eb3d
A
910static int
911parsefloat(FILE *fp, char *buf, char *end)
912{
913 char *commit, *p;
1f2f436a 914 int infnanpos = 0, decptpos = 0;
9385eb3d 915 enum {
1f2f436a
A
916 S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
917 S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS
9385eb3d
A
918 } state = S_START;
919 unsigned char c;
1f2f436a 920 const char *decpt = localeconv()->decimal_point;
9385eb3d
A
921 _Bool gotmantdig = 0, ishex = 0;
922
923 /*
924 * We set commit = p whenever the string we have read so far
925 * constitutes a valid representation of a floating point
926 * number by itself. At some point, the parse will complete
927 * or fail, and we will ungetc() back to the last commit point.
928 * To ensure that the file offset gets updated properly, it is
929 * always necessary to read at least one character that doesn't
930 * match; thus, we can't short-circuit "infinity" or "nan(...)".
931 */
932 commit = buf - 1;
933 for (p = buf; p < end; ) {
934 c = *fp->_p;
935reswitch:
936 switch (state) {
937 case S_START:
938 state = S_GOTSIGN;
939 if (c == '-' || c == '+')
940 break;
941 else
942 goto reswitch;
943 case S_GOTSIGN:
944 switch (c) {
945 case '0':
946 state = S_MAYBEHEX;
947 commit = p;
948 break;
949 case 'I':
950 case 'i':
951 state = S_INF;
952 break;
953 case 'N':
954 case 'n':
955 state = S_NAN;
956 break;
957 default:
958 state = S_DIGITS;
959 goto reswitch;
960 }
961 break;
962 case S_INF:
963 if (infnanpos > 6 ||
964 (c != "nfinity"[infnanpos] &&
965 c != "NFINITY"[infnanpos]))
966 goto parsedone;
967 if (infnanpos == 1 || infnanpos == 6)
968 commit = p; /* inf or infinity */
969 infnanpos++;
970 break;
971 case S_NAN:
972 switch (infnanpos) {
9385eb3d
A
973 case 0:
974 if (c != 'A' && c != 'a')
975 goto parsedone;
976 break;
977 case 1:
978 if (c != 'N' && c != 'n')
979 goto parsedone;
980 else
981 commit = p;
982 break;
983 case 2:
984 if (c != '(')
985 goto parsedone;
986 break;
987 default:
988 if (c == ')') {
989 commit = p;
1f2f436a 990 state = S_DONE;
9385eb3d
A
991 } else if (!isalnum(c) && c != '_')
992 goto parsedone;
993 break;
994 }
995 infnanpos++;
996 break;
1f2f436a
A
997 case S_DONE:
998 goto parsedone;
9385eb3d
A
999 case S_MAYBEHEX:
1000 state = S_DIGITS;
1001 if (c == 'X' || c == 'x') {
1002 ishex = 1;
1003 break;
1004 } else { /* we saw a '0', but no 'x' */
1005 gotmantdig = 1;
1006 goto reswitch;
1007 }
1008 case S_DIGITS:
1f2f436a 1009 if ((ishex && isxdigit(c)) || isdigit(c)) {
9385eb3d 1010 gotmantdig = 1;
1f2f436a
A
1011 commit = p;
1012 break;
1013 } else {
1014 state = S_DECPT;
1015 goto reswitch;
1016 }
1017 case S_DECPT:
1018 if (c == decpt[decptpos]) {
1019 if (decpt[++decptpos] == '\0') {
1020 /* We read the complete decpt seq. */
1021 state = S_FRAC;
1022 if (gotmantdig)
1023 commit = p;
1024 }
1025 break;
1026 } else if (!decptpos) {
1027 /* We didn't read any decpt characters. */
9385eb3d 1028 state = S_FRAC;
1f2f436a
A
1029 goto reswitch;
1030 } else {
1031 /*
1032 * We read part of a multibyte decimal point,
1033 * but the rest is invalid, so bail.
1034 */
1035 goto parsedone;
9385eb3d 1036 }
9385eb3d 1037 case S_FRAC:
3d9156a7
A
1038 if (((c == 'E' || c == 'e') && !ishex) ||
1039 ((c == 'P' || c == 'p') && ishex)) {
9385eb3d
A
1040 if (!gotmantdig)
1041 goto parsedone;
1042 else
1043 state = S_EXP;
3d9156a7 1044 } else if ((ishex && isxdigit(c)) || isdigit(c)) {
9385eb3d
A
1045 commit = p;
1046 gotmantdig = 1;
1047 } else
1048 goto parsedone;
1049 break;
1050 case S_EXP:
1051 state = S_EXPDIGITS;
1052 if (c == '-' || c == '+')
1053 break;
1054 else
1055 goto reswitch;
1056 case S_EXPDIGITS:
1057 if (isdigit(c))
1058 commit = p;
1059 else
1060 goto parsedone;
1061 break;
1062 default:
1063 abort();
1064 }
1065 *p++ = c;
1066 if (--fp->_r > 0)
1067 fp->_p++;
1068 else if (__srefill(fp))
1069 break; /* EOF */
1070 }
1071
1072parsedone:
1073 while (commit < --p)
1074 __ungetc(*(u_char *)p, fp);
1075 *++commit = '\0';
1076 return (commit - buf);
1077}
1078#endif