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