]> git.saurik.com Git - apple/libc.git/blame - stdio/FreeBSD/vfwscanf.c
Libc-997.1.1.tar.gz
[apple/libc.git] / stdio / FreeBSD / vfwscanf.c
CommitLineData
9385eb3d
A
1/*-
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.
9385eb3d
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 0
34#if defined(LIBC_SCCS) && !defined(lint)
35static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93";
36#endif /* LIBC_SCCS and not lint */
9385eb3d 37#endif
3d9156a7 38#include <sys/cdefs.h>
1f2f436a 39__FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.17 2009/01/19 06:19:51 das Exp $");
9385eb3d 40
ad3c9f2a
A
41#include "xlocale_private.h"
42
9385eb3d
A
43#include "namespace.h"
44#include <ctype.h>
45#include <inttypes.h>
1f2f436a 46#include <limits.h>
9385eb3d
A
47#include <stdio.h>
48#include <stdlib.h>
49#include <stddef.h>
50#include <stdarg.h>
51#include <string.h>
52#include <wchar.h>
53#include <wctype.h>
54#include "un-namespace.h"
55
56#include "libc_private.h"
57#include "local.h"
58
3d9156a7 59#ifndef NO_FLOATING_POINT
9385eb3d
A
60#include <locale.h>
61#endif
62
63#define BUF 513 /* Maximum length of numeric string. */
64
65/*
66 * Flags used during conversion.
67 */
68#define LONG 0x01 /* l: long or double */
69#define LONGDBL 0x02 /* L: long double */
70#define SHORT 0x04 /* h: short */
71#define SUPPRESS 0x08 /* *: suppress assignment */
72#define POINTER 0x10 /* p: void * (as hex) */
73#define NOSKIP 0x20 /* [ or c: do not skip blanks */
74#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
75#define INTMAXT 0x800 /* j: intmax_t */
76#define PTRDIFFT 0x1000 /* t: ptrdiff_t */
77#define SIZET 0x2000 /* z: size_t */
78#define SHORTSHORT 0x4000 /* hh: char */
79#define UNSIGNED 0x8000 /* %[oupxX] conversions */
80
81/*
82 * The following are used in integral conversions only:
83 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
84 */
85#define SIGNOK 0x40 /* +/- is (still) legal */
86#define NDIGITS 0x80 /* no digits detected */
87#define PFXOK 0x100 /* 0x prefix is (still) legal */
88#define NZDIGITS 0x200 /* no zero digits detected */
3d9156a7 89#define HAVESIGN 0x10000 /* sign detected */
9385eb3d
A
90
91/*
92 * Conversion types.
93 */
94#define CT_CHAR 0 /* %c conversion */
95#define CT_CCL 1 /* %[...] conversion */
96#define CT_STRING 2 /* %s conversion */
97#define CT_INT 3 /* %[dioupxX] conversion */
98#define CT_FLOAT 4 /* %[efgEFG] conversion */
99
1f2f436a 100#ifndef NO_FLOATING_POINT
ad3c9f2a 101static int parsefloat(FILE *, wchar_t **, size_t, locale_t loc);
1f2f436a 102#endif
9385eb3d
A
103
104#define INCCL(_c) \
105 (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
106 (wmemchr(ccls, (_c), ccle - ccls) != NULL))
107
1f2f436a
A
108static const mbstate_t initial_mbs;
109
9385eb3d
A
110/*
111 * MT-safe version.
112 */
113int
114vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
115{
116 int ret;
117
118 FLOCKFILE(fp);
119 ORIENT(fp, 1);
ad3c9f2a
A
120 ret = __vfwscanf(fp, __current_locale(), fmt, ap);
121 FUNLOCKFILE(fp);
122 return (ret);
123}
124
125int
126vfwscanf_l(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
127 va_list ap)
128{
129 int ret;
130
131 NORMALIZE_LOCALE(loc);
132 FLOCKFILE(fp);
133 ORIENT(fp, 1);
134 ret = __vfwscanf(fp, loc, fmt, ap);
9385eb3d
A
135 FUNLOCKFILE(fp);
136 return (ret);
137}
138
139/*
140 * Non-MT-safe version.
141 */
ad3c9f2a
A
142__private_extern__ int
143__vfwscanf(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
144 va_list ap)
9385eb3d
A
145{
146 wint_t c; /* character from format, or conversion */
147 size_t width; /* field width, or 0 */
148 wchar_t *p; /* points into all kinds of strings */
149 int n; /* handy integer */
150 int flags; /* flags as defined above */
151 wchar_t *p0; /* saves original value of p when necessary */
152 int nassigned; /* number of fields assigned */
9385eb3d
A
153 int nread; /* number of characters consumed from fp */
154 int base; /* base argument to conversion function */
155 wchar_t buf[BUF]; /* buffer for numeric conversions */
156 const wchar_t *ccls; /* character class start */
157 const wchar_t *ccle; /* character class end */
158 int cclcompl; /* ccl is complemented? */
159 wint_t wi; /* handy wint_t */
160 char *mbp; /* multibyte string pointer for %c %s %[ */
161 size_t nconv; /* number of bytes in mb. conversion */
9385eb3d 162 char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
ad3c9f2a
A
163 int index; /* for %index$ */
164 va_list ap_orig; /* to reset ap to first argument */
3d9156a7 165 mbstate_t mbs;
ad3c9f2a 166 int mb_cur_max = MB_CUR_MAX_L(loc);
9385eb3d
A
167
168 /* `basefix' is used to avoid `if' tests in the integer scanner */
6465356a 169 static const short basefix[17] =
9385eb3d
A
170 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
171
172 nassigned = 0;
9385eb3d
A
173 nread = 0;
174 ccls = ccle = NULL;
ad3c9f2a 175 va_copy(ap_orig, ap);
9385eb3d
A
176 for (;;) {
177 c = *fmt++;
178 if (c == 0)
179 return (nassigned);
ad3c9f2a
A
180 if (iswspace_l(c, loc)) {
181 while ((c = __fgetwc(fp, loc)) != WEOF &&
182 iswspace_l(c, loc))
9385eb3d
A
183 ;
184 if (c != WEOF)
ad3c9f2a 185 __ungetwc(c, fp, loc);
9385eb3d
A
186 continue;
187 }
ad3c9f2a
A
188 if (c != '%') {
189 if ((wi = __fgetwc(fp, loc)) == WEOF)
190 goto input_failure;
9385eb3d 191 goto literal;
ad3c9f2a 192 }
9385eb3d
A
193 width = 0;
194 flags = 0;
195 /*
196 * switch on the format. continue if done;
197 * break once format type is derived.
198 */
199again: c = *fmt++;
200 switch (c) {
201 case '%':
ad3c9f2a
A
202 /* Consume leading white space */
203 for(;;) {
204 if ((wi = __fgetwc(fp, loc)) == WEOF)
205 goto input_failure;
206 if (!iswspace_l(wi, loc))
207 break;
208 nread++;
209 }
9385eb3d 210literal:
9385eb3d 211 if (wi != c) {
ad3c9f2a
A
212 __ungetwc(wi, fp, loc);
213 goto match_failure;
9385eb3d
A
214 }
215 nread++;
216 continue;
217
ad3c9f2a
A
218 case '$':
219 index = width;
220 if (index < 1 || index > NL_ARGMAX || fmt[-3] != '%') {
221 goto input_failure;
222 }
223 width = 0;
224 va_end(ap);
225 va_copy(ap, ap_orig); /* reset to %1$ */
226 for (; index > 1; index--) {
227 va_arg(ap, void*);
228 }
229 goto again;
9385eb3d
A
230 case '*':
231 flags |= SUPPRESS;
232 goto again;
233 case 'j':
234 flags |= INTMAXT;
235 goto again;
236 case 'l':
237 if (flags & LONG) {
238 flags &= ~LONG;
239 flags |= LONGLONG;
240 } else
241 flags |= LONG;
242 goto again;
243 case 'q':
244 flags |= LONGLONG; /* not quite */
245 goto again;
246 case 't':
247 flags |= PTRDIFFT;
248 goto again;
249 case 'z':
250 flags |= SIZET;
251 goto again;
252 case 'L':
253 flags |= LONGDBL;
254 goto again;
255 case 'h':
256 if (flags & SHORT) {
257 flags &= ~SHORT;
258 flags |= SHORTSHORT;
259 } else
260 flags |= SHORT;
261 goto again;
262
263 case '0': case '1': case '2': case '3': case '4':
264 case '5': case '6': case '7': case '8': case '9':
265 width = width * 10 + c - '0';
266 goto again;
267
268 /*
269 * Conversions.
270 */
271 case 'd':
272 c = CT_INT;
273 base = 10;
274 break;
275
276 case 'i':
277 c = CT_INT;
278 base = 0;
279 break;
280
281 case 'o':
282 c = CT_INT;
283 flags |= UNSIGNED;
284 base = 8;
285 break;
286
287 case 'u':
288 c = CT_INT;
289 flags |= UNSIGNED;
290 base = 10;
291 break;
292
293 case 'X':
294 case 'x':
295 flags |= PFXOK; /* enable 0x prefixing */
296 c = CT_INT;
297 flags |= UNSIGNED;
298 base = 16;
299 break;
300
3d9156a7 301#ifndef NO_FLOATING_POINT
9385eb3d
A
302 case 'A': case 'E': case 'F': case 'G':
303 case 'a': case 'e': case 'f': case 'g':
304 c = CT_FLOAT;
305 break;
306#endif
307
308 case 'S':
309 flags |= LONG;
310 /* FALLTHROUGH */
311 case 's':
312 c = CT_STRING;
313 break;
314
315 case '[':
316 ccls = fmt;
317 if (*fmt == '^') {
318 cclcompl = 1;
319 fmt++;
320 } else
321 cclcompl = 0;
322 if (*fmt == ']')
323 fmt++;
324 while (*fmt != '\0' && *fmt != ']')
325 fmt++;
326 ccle = fmt;
327 fmt++;
328 flags |= NOSKIP;
329 c = CT_CCL;
330 break;
331
332 case 'C':
333 flags |= LONG;
334 /* FALLTHROUGH */
335 case 'c':
336 flags |= NOSKIP;
337 c = CT_CHAR;
338 break;
339
340 case 'p': /* pointer format is like hex */
341 flags |= POINTER | PFXOK;
342 c = CT_INT; /* assumes sizeof(uintmax_t) */
343 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
344 base = 16;
345 break;
346
347 case 'n':
ad3c9f2a
A
348 {
349 void *ptr = va_arg(ap, void *);
350 if ((ptr == NULL) || (flags & SUPPRESS)) /* ??? */
9385eb3d 351 continue;
ad3c9f2a
A
352 else if (flags & SHORTSHORT)
353 *(char *)ptr = nread;
9385eb3d 354 else if (flags & SHORT)
ad3c9f2a 355 *(short *)ptr = nread;
9385eb3d 356 else if (flags & LONG)
ad3c9f2a 357 *(long *)ptr = nread;
9385eb3d 358 else if (flags & LONGLONG)
ad3c9f2a 359 *(long long *)ptr = nread;
9385eb3d 360 else if (flags & INTMAXT)
ad3c9f2a 361 *(intmax_t *)ptr = nread;
9385eb3d 362 else if (flags & SIZET)
ad3c9f2a 363 *(size_t *)ptr = nread;
9385eb3d 364 else if (flags & PTRDIFFT)
ad3c9f2a 365 *(ptrdiff_t *)ptr = nread;
9385eb3d 366 else
ad3c9f2a 367 *(int *)ptr = nread;
9385eb3d 368 continue;
ad3c9f2a 369 }
9385eb3d
A
370 default:
371 goto match_failure;
372
373 /*
374 * Disgusting backwards compatibility hack. XXX
375 */
376 case '\0': /* compat */
377 return (EOF);
378 }
379
380 /*
381 * Consume leading white space, except for formats
382 * that suppress this.
383 */
384 if ((flags & NOSKIP) == 0) {
ad3c9f2a 385 while ((wi = __fgetwc(fp, loc)) != WEOF && iswspace_l(wi, loc))
9385eb3d
A
386 nread++;
387 if (wi == WEOF)
388 goto input_failure;
ad3c9f2a 389 __ungetwc(wi, fp, loc);
9385eb3d
A
390 }
391
392 /*
393 * Do the conversion.
394 */
395 switch (c) {
396
397 case CT_CHAR:
398 /* scan arbitrary characters (sets NOSKIP) */
399 if (width == 0)
400 width = 1;
401 if (flags & LONG) {
402 if (!(flags & SUPPRESS))
403 p = va_arg(ap, wchar_t *);
404 n = 0;
405 while (width-- != 0 &&
ad3c9f2a 406 (wi = __fgetwc(fp, loc)) != WEOF) {
9385eb3d
A
407 if (!(flags & SUPPRESS))
408 *p++ = (wchar_t)wi;
409 n++;
410 }
411 if (n == 0)
412 goto input_failure;
413 nread += n;
414 if (!(flags & SUPPRESS))
415 nassigned++;
416 } else {
417 if (!(flags & SUPPRESS))
418 mbp = va_arg(ap, char *);
419 n = 0;
1f2f436a 420 mbs = initial_mbs;
9385eb3d 421 while (width != 0 &&
ad3c9f2a
A
422 (wi = __fgetwc(fp, loc)) != WEOF) {
423 if (width >= mb_cur_max &&
9385eb3d 424 !(flags & SUPPRESS)) {
ad3c9f2a 425 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
9385eb3d
A
426 if (nconv == (size_t)-1)
427 goto input_failure;
428 } else {
ad3c9f2a
A
429 nconv = wcrtomb_l(mbbuf, wi,
430 &mbs, loc);
9385eb3d
A
431 if (nconv == (size_t)-1)
432 goto input_failure;
433 if (nconv > width) {
ad3c9f2a 434 __ungetwc(wi, fp, loc);
9385eb3d
A
435 break;
436 }
437 if (!(flags & SUPPRESS))
438 memcpy(mbp, mbbuf,
439 nconv);
440 }
441 if (!(flags & SUPPRESS))
442 mbp += nconv;
443 width -= nconv;
444 n++;
445 }
446 if (n == 0)
447 goto input_failure;
448 nread += n;
449 if (!(flags & SUPPRESS))
450 nassigned++;
451 }
9385eb3d
A
452 break;
453
454 case CT_CCL:
455 /* scan a (nonempty) character class (sets NOSKIP) */
456 if (width == 0)
457 width = (size_t)~0; /* `infinity' */
458 /* take only those things in the class */
459 if ((flags & SUPPRESS) && (flags & LONG)) {
460 n = 0;
ad3c9f2a 461 while ((wi = __fgetwc(fp, loc)) != WEOF &&
9385eb3d
A
462 width-- != 0 && INCCL(wi))
463 n++;
464 if (wi != WEOF)
ad3c9f2a 465 __ungetwc(wi, fp, loc);
9385eb3d
A
466 if (n == 0)
467 goto match_failure;
468 } else if (flags & LONG) {
469 p0 = p = va_arg(ap, wchar_t *);
ad3c9f2a 470 while ((wi = __fgetwc(fp, loc)) != WEOF &&
9385eb3d
A
471 width-- != 0 && INCCL(wi))
472 *p++ = (wchar_t)wi;
473 if (wi != WEOF)
ad3c9f2a 474 __ungetwc(wi, fp, loc);
9385eb3d
A
475 n = p - p0;
476 if (n == 0)
477 goto match_failure;
478 *p = 0;
479 nassigned++;
480 } else {
481 if (!(flags & SUPPRESS))
482 mbp = va_arg(ap, char *);
483 n = 0;
1f2f436a 484 mbs = initial_mbs;
ad3c9f2a 485 while ((wi = __fgetwc(fp, loc)) != WEOF &&
9385eb3d 486 width != 0 && INCCL(wi)) {
ad3c9f2a 487 if (width >= mb_cur_max &&
9385eb3d 488 !(flags & SUPPRESS)) {
ad3c9f2a 489 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
9385eb3d
A
490 if (nconv == (size_t)-1)
491 goto input_failure;
492 } else {
ad3c9f2a
A
493 nconv = wcrtomb_l(mbbuf, wi,
494 &mbs, loc);
9385eb3d
A
495 if (nconv == (size_t)-1)
496 goto input_failure;
497 if (nconv > width)
498 break;
499 if (!(flags & SUPPRESS))
500 memcpy(mbp, mbbuf,
501 nconv);
502 }
503 if (!(flags & SUPPRESS))
504 mbp += nconv;
505 width -= nconv;
506 n++;
507 }
508 if (wi != WEOF)
ad3c9f2a
A
509 __ungetwc(wi, fp, loc);
510 if (n == 0)
511 goto match_failure;
9385eb3d
A
512 if (!(flags & SUPPRESS)) {
513 *mbp = 0;
514 nassigned++;
515 }
516 }
517 nread += n;
9385eb3d
A
518 break;
519
520 case CT_STRING:
521 /* like CCL, but zero-length string OK, & no NOSKIP */
522 if (width == 0)
523 width = (size_t)~0;
524 if ((flags & SUPPRESS) && (flags & LONG)) {
ad3c9f2a 525 while ((wi = __fgetwc(fp, loc)) != WEOF &&
9385eb3d 526 width-- != 0 &&
ad3c9f2a 527 !iswspace_l(wi, loc))
9385eb3d
A
528 nread++;
529 if (wi != WEOF)
ad3c9f2a 530 __ungetwc(wi, fp, loc);
9385eb3d
A
531 } else if (flags & LONG) {
532 p0 = p = va_arg(ap, wchar_t *);
ad3c9f2a 533 while ((wi = __fgetwc(fp, loc)) != WEOF &&
9385eb3d 534 width-- != 0 &&
ad3c9f2a 535 !iswspace_l(wi, loc)) {
9385eb3d
A
536 *p++ = (wchar_t)wi;
537 nread++;
538 }
539 if (wi != WEOF)
ad3c9f2a 540 __ungetwc(wi, fp, loc);
9385eb3d
A
541 *p = '\0';
542 nassigned++;
543 } else {
544 if (!(flags & SUPPRESS))
545 mbp = va_arg(ap, char *);
1f2f436a 546 mbs = initial_mbs;
ad3c9f2a 547 while ((wi = __fgetwc(fp, loc)) != WEOF &&
9385eb3d 548 width != 0 &&
ad3c9f2a
A
549 !iswspace_l(wi, loc)) {
550 if (width >= mb_cur_max &&
9385eb3d 551 !(flags & SUPPRESS)) {
ad3c9f2a 552 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
9385eb3d
A
553 if (nconv == (size_t)-1)
554 goto input_failure;
555 } else {
ad3c9f2a
A
556 nconv = wcrtomb_l(mbbuf, wi,
557 &mbs, loc);
9385eb3d
A
558 if (nconv == (size_t)-1)
559 goto input_failure;
560 if (nconv > width)
561 break;
562 if (!(flags & SUPPRESS))
563 memcpy(mbp, mbbuf,
564 nconv);
565 }
566 if (!(flags & SUPPRESS))
567 mbp += nconv;
568 width -= nconv;
569 nread++;
570 }
571 if (wi != WEOF)
ad3c9f2a 572 __ungetwc(wi, fp, loc);
9385eb3d
A
573 if (!(flags & SUPPRESS)) {
574 *mbp = 0;
575 nassigned++;
576 }
577 }
9385eb3d
A
578 continue;
579
580 case CT_INT:
581 /* scan an integer as if by the conversion function */
582 if (width == 0 || width > sizeof(buf) /
583 sizeof(*buf) - 1)
584 width = sizeof(buf) / sizeof(*buf) - 1;
585 flags |= SIGNOK | NDIGITS | NZDIGITS;
586 for (p = buf; width; width--) {
ad3c9f2a 587 c = __fgetwc(fp, loc);
9385eb3d
A
588 /*
589 * Switch on the character; `goto ok'
590 * if we accept it as a part of number.
591 */
592 switch (c) {
593
594 /*
595 * The digit 0 is always legal, but is
596 * special. For %i conversions, if no
597 * digits (zero or nonzero) have been
598 * scanned (only signs), we will have
599 * base==0. In that case, we should set
600 * it to 8 and enable 0x prefixing.
601 * Also, if we have not scanned zero digits
602 * before this, do not turn off prefixing
603 * (someone else will turn it off if we
604 * have scanned any nonzero digits).
605 */
606 case '0':
607 if (base == 0) {
608 base = 8;
609 flags |= PFXOK;
610 }
611 if (flags & NZDIGITS)
612 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
613 else
614 flags &= ~(SIGNOK|PFXOK|NDIGITS);
615 goto ok;
616
617 /* 1 through 7 always legal */
618 case '1': case '2': case '3':
619 case '4': case '5': case '6': case '7':
620 base = basefix[base];
621 flags &= ~(SIGNOK | PFXOK | NDIGITS);
622 goto ok;
623
624 /* digits 8 and 9 ok iff decimal or hex */
625 case '8': case '9':
626 base = basefix[base];
627 if (base <= 8)
628 break; /* not legal here */
629 flags &= ~(SIGNOK | PFXOK | NDIGITS);
630 goto ok;
631
632 /* letters ok iff hex */
633 case 'A': case 'B': case 'C':
634 case 'D': case 'E': case 'F':
635 case 'a': case 'b': case 'c':
636 case 'd': case 'e': case 'f':
637 /* no need to fix base here */
638 if (base <= 10)
639 break; /* not legal here */
640 flags &= ~(SIGNOK | PFXOK | NDIGITS);
641 goto ok;
642
643 /* sign ok only as first character */
644 case '+': case '-':
645 if (flags & SIGNOK) {
646 flags &= ~SIGNOK;
3d9156a7 647 flags |= HAVESIGN;
9385eb3d
A
648 goto ok;
649 }
650 break;
3d9156a7
A
651
652 /*
653 * x ok iff flag still set & 2nd char (or
654 * 3rd char if we have a sign).
655 */
9385eb3d 656 case 'x': case 'X':
3d9156a7
A
657 if (flags & PFXOK && p ==
658 buf + 1 + !!(flags & HAVESIGN)) {
9385eb3d
A
659 base = 16; /* if %i */
660 flags &= ~PFXOK;
661 goto ok;
662 }
663 break;
664 }
665
666 /*
667 * If we got here, c is not a legal character
668 * for a number. Stop accumulating digits.
669 */
670 if (c != WEOF)
ad3c9f2a 671 __ungetwc(c, fp, loc);
9385eb3d
A
672 break;
673 ok:
674 /*
675 * c is legal: store it and look at the next.
676 */
677 *p++ = (wchar_t)c;
678 }
679 /*
680 * If we had only a sign, it is no good; push
681 * back the sign. If the number ends in `x',
682 * it was [sign] '0' 'x', so push back the x
683 * and treat it as [sign] '0'.
684 */
685 if (flags & NDIGITS) {
686 if (p > buf)
ad3c9f2a 687 __ungetwc(*--p, fp, loc);
9385eb3d
A
688 goto match_failure;
689 }
690 c = p[-1];
691 if (c == 'x' || c == 'X') {
692 --p;
ad3c9f2a 693 __ungetwc(c, fp, loc);
9385eb3d
A
694 }
695 if ((flags & SUPPRESS) == 0) {
696 uintmax_t res;
697
698 *p = 0;
699 if ((flags & UNSIGNED) == 0)
ad3c9f2a 700 res = wcstoimax_l(buf, NULL, base, loc);
9385eb3d 701 else
ad3c9f2a 702 res = wcstoumax_l(buf, NULL, base, loc);
9385eb3d
A
703 if (flags & POINTER)
704 *va_arg(ap, void **) =
705 (void *)(uintptr_t)res;
706 else if (flags & SHORTSHORT)
707 *va_arg(ap, char *) = res;
708 else if (flags & SHORT)
709 *va_arg(ap, short *) = res;
710 else if (flags & LONG)
711 *va_arg(ap, long *) = res;
712 else if (flags & LONGLONG)
713 *va_arg(ap, long long *) = res;
714 else if (flags & INTMAXT)
715 *va_arg(ap, intmax_t *) = res;
716 else if (flags & PTRDIFFT)
717 *va_arg(ap, ptrdiff_t *) = res;
718 else if (flags & SIZET)
719 *va_arg(ap, size_t *) = res;
720 else
721 *va_arg(ap, int *) = res;
722 nassigned++;
723 }
724 nread += p - buf;
9385eb3d
A
725 break;
726
3d9156a7 727#ifndef NO_FLOATING_POINT
9385eb3d 728 case CT_FLOAT:
ad3c9f2a
A
729 {
730 wchar_t *pbuf;
9385eb3d 731 /* scan a floating point number as if by strtod */
ad3c9f2a 732 if ((width = parsefloat(fp, &pbuf, width, loc)) == 0)
9385eb3d
A
733 goto match_failure;
734 if ((flags & SUPPRESS) == 0) {
735 if (flags & LONGDBL) {
ad3c9f2a 736 long double res = wcstold_l(pbuf, &p, loc);
9385eb3d
A
737 *va_arg(ap, long double *) = res;
738 } else if (flags & LONG) {
ad3c9f2a 739 double res = wcstod_l(pbuf, &p, loc);
9385eb3d
A
740 *va_arg(ap, double *) = res;
741 } else {
ad3c9f2a 742 float res = wcstof_l(pbuf, &p, loc);
9385eb3d
A
743 *va_arg(ap, float *) = res;
744 }
9385eb3d
A
745 nassigned++;
746 }
747 nread += width;
9385eb3d 748 break;
ad3c9f2a 749 }
3d9156a7 750#endif /* !NO_FLOATING_POINT */
9385eb3d
A
751 }
752 }
753input_failure:
ad3c9f2a 754 return (nassigned ? nassigned : EOF);
9385eb3d
A
755match_failure:
756 return (nassigned);
757}
758
3d9156a7 759#ifndef NO_FLOATING_POINT
ad3c9f2a
A
760extern char *__parsefloat_buf(size_t s); /* see vfscanf-fbsd.c */
761
9385eb3d 762static int
ad3c9f2a 763parsefloat(FILE *fp, wchar_t **buf, size_t width, locale_t loc)
9385eb3d 764{
1f2f436a
A
765 mbstate_t mbs;
766 size_t nconv;
9385eb3d
A
767 wchar_t *commit, *p;
768 int infnanpos = 0;
769 enum {
1f2f436a 770 S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
9385eb3d
A
771 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
772 } state = S_START;
773 wchar_t c;
1f2f436a 774 wchar_t decpt;
9385eb3d 775 _Bool gotmantdig = 0, ishex = 0;
ad3c9f2a
A
776 wchar_t *b;
777 wchar_t *e;
778 size_t s;
9385eb3d 779
1f2f436a 780 mbs = initial_mbs;
ad3c9f2a
A
781
782 nconv = mbrtowc_l(&decpt, localeconv()->decimal_point, MB_CUR_MAX_L(loc), &mbs, loc);
1f2f436a 783 if (nconv == (size_t)-1 || nconv == (size_t)-2)
ad3c9f2a 784 decpt = '.'; /* failsafe */
1f2f436a 785
ad3c9f2a
A
786 s = (width == 0 ? BUF : (width + 1));
787 if ((b = (wchar_t *)__parsefloat_buf(s * sizeof(wchar_t))) == NULL) {
788 *buf = NULL;
789 return 0;
790 }
791 e = b + (s - 1);
9385eb3d
A
792 /*
793 * We set commit = p whenever the string we have read so far
794 * constitutes a valid representation of a floating point
795 * number by itself. At some point, the parse will complete
796 * or fail, and we will ungetc() back to the last commit point.
797 * To ensure that the file offset gets updated properly, it is
798 * always necessary to read at least one character that doesn't
799 * match; thus, we can't short-circuit "infinity" or "nan(...)".
800 */
ad3c9f2a 801 commit = b - 1;
9385eb3d 802 c = WEOF;
ad3c9f2a
A
803 for (p = b; width == 0 || p < e; ) {
804 if ((c = __fgetwc(fp, loc)) == WEOF)
9385eb3d
A
805 break;
806reswitch:
807 switch (state) {
808 case S_START:
809 state = S_GOTSIGN;
810 if (c == '-' || c == '+')
811 break;
812 else
813 goto reswitch;
814 case S_GOTSIGN:
815 switch (c) {
816 case '0':
817 state = S_MAYBEHEX;
818 commit = p;
819 break;
820 case 'I':
821 case 'i':
822 state = S_INF;
823 break;
824 case 'N':
825 case 'n':
826 state = S_NAN;
827 break;
828 default:
829 state = S_DIGITS;
830 goto reswitch;
831 }
832 break;
833 case S_INF:
834 if (infnanpos > 6 ||
835 (c != "nfinity"[infnanpos] &&
836 c != "NFINITY"[infnanpos]))
837 goto parsedone;
838 if (infnanpos == 1 || infnanpos == 6)
839 commit = p; /* inf or infinity */
840 infnanpos++;
841 break;
842 case S_NAN:
843 switch (infnanpos) {
9385eb3d
A
844 case 0:
845 if (c != 'A' && c != 'a')
846 goto parsedone;
847 break;
848 case 1:
849 if (c != 'N' && c != 'n')
850 goto parsedone;
851 else
852 commit = p;
853 break;
854 case 2:
855 if (c != '(')
856 goto parsedone;
857 break;
858 default:
859 if (c == ')') {
860 commit = p;
1f2f436a 861 state = S_DONE;
ad3c9f2a 862 } else if (!iswalnum_l(c, loc) && c != '_')
9385eb3d
A
863 goto parsedone;
864 break;
865 }
866 infnanpos++;
867 break;
1f2f436a
A
868 case S_DONE:
869 goto parsedone;
9385eb3d
A
870 case S_MAYBEHEX:
871 state = S_DIGITS;
872 if (c == 'X' || c == 'x') {
873 ishex = 1;
874 break;
875 } else { /* we saw a '0', but no 'x' */
876 gotmantdig = 1;
877 goto reswitch;
878 }
879 case S_DIGITS:
ad3c9f2a 880 if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc))
9385eb3d
A
881 gotmantdig = 1;
882 else {
883 state = S_FRAC;
884 if (c != decpt)
885 goto reswitch;
886 }
887 if (gotmantdig)
888 commit = p;
889 break;
890 case S_FRAC:
3d9156a7
A
891 if (((c == 'E' || c == 'e') && !ishex) ||
892 ((c == 'P' || c == 'p') && ishex)) {
9385eb3d
A
893 if (!gotmantdig)
894 goto parsedone;
895 else
896 state = S_EXP;
ad3c9f2a 897 } else if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc)) {
9385eb3d
A
898 commit = p;
899 gotmantdig = 1;
900 } else
901 goto parsedone;
902 break;
903 case S_EXP:
904 state = S_EXPDIGITS;
905 if (c == '-' || c == '+')
906 break;
907 else
908 goto reswitch;
909 case S_EXPDIGITS:
ad3c9f2a 910 if (iswdigit_l(c, loc))
9385eb3d
A
911 commit = p;
912 else
913 goto parsedone;
914 break;
915 default:
ad3c9f2a
A
916 LIBC_ABORT("unknown state %d", state);
917 }
918 if (p >= e) {
919 ssize_t diff = (p - b);
920 ssize_t com = (commit - b);
921 s += BUF;
922 b = (wchar_t *)__parsefloat_buf(s * sizeof(wchar_t));
923 if (b == NULL) {
924 *buf = NULL;
925 return 0;
926 }
927 e = b + (s - 1);
928 p = b + diff;
929 commit = b + com;
9385eb3d
A
930 }
931 *p++ = c;
932 c = WEOF;
933 }
934
935parsedone:
936 if (c != WEOF)
ad3c9f2a 937 __ungetwc(c, fp, loc);
9385eb3d 938 while (commit < --p)
ad3c9f2a 939 __ungetwc(*p, fp, loc);
9385eb3d 940 *++commit = '\0';
ad3c9f2a
A
941 *buf = b;
942 return (commit - b);
9385eb3d
A
943}
944#endif