| /*- |
| * Copyright (c) 1990, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * This code is derived from software contributed to Berkeley by |
| * Chris Torek. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <ctype.h> |
| #include <stdlib.h> |
| |
| #include "local.h" |
| |
| #define BUF 513 /* Maximum length of numeric string. */ |
| |
| size_t parsefloat(FILE *fp, char *buf, char *end) { |
| char *commit, *p; |
| int infnanpos = 0; |
| enum { |
| S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX, |
| S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS |
| } state = S_START; |
| unsigned char c; |
| int gotmantdig = 0, ishex = 0; |
| |
| /* |
| * We set commit = p whenever the string we have read so far |
| * constitutes a valid representation of a floating point |
| * number by itself. At some point, the parse will complete |
| * or fail, and we will ungetc() back to the last commit point. |
| * To ensure that the file offset gets updated properly, it is |
| * always necessary to read at least one character that doesn't |
| * match; thus, we can't short-circuit "infinity" or "nan(...)". |
| */ |
| commit = buf - 1; |
| for (p = buf; p < end; ) { |
| c = *fp->_p; |
| reswitch: |
| switch (state) { |
| case S_START: |
| state = S_GOTSIGN; |
| if (c == '-' || c == '+') |
| break; |
| else |
| goto reswitch; |
| case S_GOTSIGN: |
| switch (c) { |
| case '0': |
| state = S_MAYBEHEX; |
| commit = p; |
| break; |
| case 'I': |
| case 'i': |
| state = S_INF; |
| break; |
| case 'N': |
| case 'n': |
| state = S_NAN; |
| break; |
| default: |
| state = S_DIGITS; |
| goto reswitch; |
| } |
| break; |
| case S_INF: |
| if (infnanpos > 6 || |
| (c != "nfinity"[infnanpos] && |
| c != "NFINITY"[infnanpos])) |
| goto parsedone; |
| if (infnanpos == 1 || infnanpos == 6) |
| commit = p; /* inf or infinity */ |
| infnanpos++; |
| break; |
| case S_NAN: |
| switch (infnanpos) { |
| case -1: /* XXX kludge to deal with nan(...) */ |
| goto parsedone; |
| case 0: |
| if (c != 'A' && c != 'a') |
| goto parsedone; |
| break; |
| case 1: |
| if (c != 'N' && c != 'n') |
| goto parsedone; |
| else |
| commit = p; |
| break; |
| case 2: |
| if (c != '(') |
| goto parsedone; |
| break; |
| default: |
| if (c == ')') { |
| commit = p; |
| infnanpos = -2; |
| } else if (!isalnum(c) && c != '_') |
| goto parsedone; |
| break; |
| } |
| infnanpos++; |
| break; |
| case S_MAYBEHEX: |
| state = S_DIGITS; |
| if (c == 'X' || c == 'x') { |
| ishex = 1; |
| break; |
| } else { /* we saw a '0', but no 'x' */ |
| gotmantdig = 1; |
| goto reswitch; |
| } |
| case S_DIGITS: |
| if ((ishex && isxdigit(c)) || isdigit(c)) |
| gotmantdig = 1; |
| else { |
| state = S_FRAC; |
| if (c != '.') |
| goto reswitch; |
| } |
| if (gotmantdig) |
| commit = p; |
| break; |
| case S_FRAC: |
| if (((c == 'E' || c == 'e') && !ishex) || |
| ((c == 'P' || c == 'p') && ishex)) { |
| if (!gotmantdig) |
| goto parsedone; |
| else |
| state = S_EXP; |
| } else if ((ishex && isxdigit(c)) || isdigit(c)) { |
| commit = p; |
| gotmantdig = 1; |
| } else |
| goto parsedone; |
| break; |
| case S_EXP: |
| state = S_EXPDIGITS; |
| if (c == '-' || c == '+') |
| break; |
| else |
| goto reswitch; |
| case S_EXPDIGITS: |
| if (isdigit(c)) |
| commit = p; |
| else |
| goto parsedone; |
| break; |
| default: |
| abort(); |
| } |
| *p++ = c; |
| if (--fp->_r > 0) |
| fp->_p++; |
| else if (__srefill(fp)) |
| break; /* EOF */ |
| } |
| |
| parsedone: |
| while (commit < --p) |
| (void)ungetc(*(unsigned char *)p, fp); |
| *++commit = '\0'; |
| return commit - buf; |
| } |
| |
| size_t wparsefloat(FILE *fp, wchar_t *buf, wchar_t *end) { |
| wchar_t *commit, *p; |
| int infnanpos = 0; |
| enum { |
| S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX, |
| S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS |
| } state = S_START; |
| wint_t c; |
| int gotmantdig = 0, ishex = 0; |
| |
| /* |
| * We set commit = p whenever the string we have read so far |
| * constitutes a valid representation of a floating point |
| * number by itself. At some point, the parse will complete |
| * or fail, and we will ungetc() back to the last commit point. |
| * To ensure that the file offset gets updated properly, it is |
| * always necessary to read at least one character that doesn't |
| * match; thus, we can't short-circuit "infinity" or "nan(...)". |
| */ |
| commit = buf - 1; |
| c = WEOF; |
| for (p = buf; p < end; ) { |
| if ((c = __fgetwc_unlock(fp)) == WEOF) |
| break; |
| reswitch: |
| switch (state) { |
| case S_START: |
| state = S_GOTSIGN; |
| if (c == '-' || c == '+') |
| break; |
| else |
| goto reswitch; |
| case S_GOTSIGN: |
| switch (c) { |
| case '0': |
| state = S_MAYBEHEX; |
| commit = p; |
| break; |
| case 'I': |
| case 'i': |
| state = S_INF; |
| break; |
| case 'N': |
| case 'n': |
| state = S_NAN; |
| break; |
| default: |
| state = S_DIGITS; |
| goto reswitch; |
| } |
| break; |
| case S_INF: |
| if (infnanpos > 6 || |
| (c != (wint_t)"nfinity"[infnanpos] && |
| c != (wint_t)"NFINITY"[infnanpos])) |
| goto parsedone; |
| if (infnanpos == 1 || infnanpos == 6) |
| commit = p; /* inf or infinity */ |
| infnanpos++; |
| break; |
| case S_NAN: |
| switch (infnanpos) { |
| case -1: /* XXX kludge to deal with nan(...) */ |
| goto parsedone; |
| case 0: |
| if (c != 'A' && c != 'a') |
| goto parsedone; |
| break; |
| case 1: |
| if (c != 'N' && c != 'n') |
| goto parsedone; |
| else |
| commit = p; |
| break; |
| case 2: |
| if (c != '(') |
| goto parsedone; |
| break; |
| default: |
| if (c == ')') { |
| commit = p; |
| infnanpos = -2; |
| } else if (!iswalnum(c) && c != '_') |
| goto parsedone; |
| break; |
| } |
| infnanpos++; |
| break; |
| case S_MAYBEHEX: |
| state = S_DIGITS; |
| if (c == 'X' || c == 'x') { |
| ishex = 1; |
| break; |
| } else { /* we saw a '0', but no 'x' */ |
| gotmantdig = 1; |
| goto reswitch; |
| } |
| case S_DIGITS: |
| if ((ishex && iswxdigit(c)) || iswdigit(c)) |
| gotmantdig = 1; |
| else { |
| state = S_FRAC; |
| if (c != L'.') |
| goto reswitch; |
| } |
| if (gotmantdig) |
| commit = p; |
| break; |
| case S_FRAC: |
| if (((c == 'E' || c == 'e') && !ishex) || |
| ((c == 'P' || c == 'p') && ishex)) { |
| if (!gotmantdig) |
| goto parsedone; |
| else |
| state = S_EXP; |
| } else if ((ishex && iswxdigit(c)) || iswdigit(c)) { |
| commit = p; |
| gotmantdig = 1; |
| } else |
| goto parsedone; |
| break; |
| case S_EXP: |
| state = S_EXPDIGITS; |
| if (c == '-' || c == '+') |
| break; |
| else |
| goto reswitch; |
| case S_EXPDIGITS: |
| if (iswdigit(c)) |
| commit = p; |
| else |
| goto parsedone; |
| break; |
| default: |
| abort(); |
| } |
| *p++ = c; |
| c = WEOF; |
| } |
| |
| parsedone: |
| if (c != WEOF) |
| ungetwc(c, fp); |
| while (commit < --p) |
| ungetwc(*p, fp); |
| *++commit = '\0'; |
| return (int)(commit - buf); |
| } |