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