/*- * 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 #include #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); }