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. 16 * 3. 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 33 #include <ctype.h> 34 #include <stdlib.h> 35 36 #include "local.h" 37 38 #define BUF 513 /* Maximum length of numeric string. */ 39 40 size_t parsefloat(FILE *fp, char *buf, char *end) { 41 char *commit, *p; 42 int infnanpos = 0; 43 enum { 44 S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX, 45 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS 46 } state = S_START; 47 unsigned char c; 48 int gotmantdig = 0, ishex = 0; 49 50 /* 51 * We set commit = p whenever the string we have read so far 52 * constitutes a valid representation of a floating point 53 * number by itself. At some point, the parse will complete 54 * or fail, and we will ungetc() back to the last commit point. 55 * To ensure that the file offset gets updated properly, it is 56 * always necessary to read at least one character that doesn't 57 * match; thus, we can't short-circuit "infinity" or "nan(...)". 58 */ 59 commit = buf - 1; 60 for (p = buf; p < end; ) { 61 c = *fp->_p; 62 reswitch: 63 switch (state) { 64 case S_START: 65 state = S_GOTSIGN; 66 if (c == '-' || c == '+') 67 break; 68 else 69 goto reswitch; 70 case S_GOTSIGN: 71 switch (c) { 72 case '0': 73 state = S_MAYBEHEX; 74 commit = p; 75 break; 76 case 'I': 77 case 'i': 78 state = S_INF; 79 break; 80 case 'N': 81 case 'n': 82 state = S_NAN; 83 break; 84 default: 85 state = S_DIGITS; 86 goto reswitch; 87 } 88 break; 89 case S_INF: 90 if (infnanpos > 6 || 91 (c != "nfinity"[infnanpos] && 92 c != "NFINITY"[infnanpos])) 93 goto parsedone; 94 if (infnanpos == 1 || infnanpos == 6) 95 commit = p; /* inf or infinity */ 96 infnanpos++; 97 break; 98 case S_NAN: 99 switch (infnanpos) { 100 case -1: /* XXX kludge to deal with nan(...) */ 101 goto parsedone; 102 case 0: 103 if (c != 'A' && c != 'a') 104 goto parsedone; 105 break; 106 case 1: 107 if (c != 'N' && c != 'n') 108 goto parsedone; 109 else 110 commit = p; 111 break; 112 case 2: 113 if (c != '(') 114 goto parsedone; 115 break; 116 default: 117 if (c == ')') { 118 commit = p; 119 infnanpos = -2; 120 } else if (!isalnum(c) && c != '_') 121 goto parsedone; 122 break; 123 } 124 infnanpos++; 125 break; 126 case S_MAYBEHEX: 127 state = S_DIGITS; 128 if (c == 'X' || c == 'x') { 129 ishex = 1; 130 break; 131 } else { /* we saw a '0', but no 'x' */ 132 gotmantdig = 1; 133 goto reswitch; 134 } 135 case S_DIGITS: 136 if ((ishex && isxdigit(c)) || isdigit(c)) 137 gotmantdig = 1; 138 else { 139 state = S_FRAC; 140 if (c != '.') 141 goto reswitch; 142 } 143 if (gotmantdig) 144 commit = p; 145 break; 146 case S_FRAC: 147 if (((c == 'E' || c == 'e') && !ishex) || 148 ((c == 'P' || c == 'p') && ishex)) { 149 if (!gotmantdig) 150 goto parsedone; 151 else 152 state = S_EXP; 153 } else if ((ishex && isxdigit(c)) || isdigit(c)) { 154 commit = p; 155 gotmantdig = 1; 156 } else 157 goto parsedone; 158 break; 159 case S_EXP: 160 state = S_EXPDIGITS; 161 if (c == '-' || c == '+') 162 break; 163 else 164 goto reswitch; 165 case S_EXPDIGITS: 166 if (isdigit(c)) 167 commit = p; 168 else 169 goto parsedone; 170 break; 171 default: 172 abort(); 173 } 174 *p++ = c; 175 if (--fp->_r > 0) 176 fp->_p++; 177 else if (__srefill(fp)) 178 break; /* EOF */ 179 } 180 181 parsedone: 182 while (commit < --p) 183 (void)ungetc(*(unsigned char *)p, fp); 184 *++commit = '\0'; 185 return commit - buf; 186 } 187 188 size_t wparsefloat(FILE *fp, wchar_t *buf, wchar_t *end) { 189 wchar_t *commit, *p; 190 int infnanpos = 0; 191 enum { 192 S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX, 193 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS 194 } state = S_START; 195 wint_t c; 196 int gotmantdig = 0, ishex = 0; 197 198 /* 199 * We set commit = p whenever the string we have read so far 200 * constitutes a valid representation of a floating point 201 * number by itself. At some point, the parse will complete 202 * or fail, and we will ungetc() back to the last commit point. 203 * To ensure that the file offset gets updated properly, it is 204 * always necessary to read at least one character that doesn't 205 * match; thus, we can't short-circuit "infinity" or "nan(...)". 206 */ 207 commit = buf - 1; 208 c = WEOF; 209 for (p = buf; p < end; ) { 210 if ((c = __fgetwc_unlock(fp)) == WEOF) 211 break; 212 reswitch: 213 switch (state) { 214 case S_START: 215 state = S_GOTSIGN; 216 if (c == '-' || c == '+') 217 break; 218 else 219 goto reswitch; 220 case S_GOTSIGN: 221 switch (c) { 222 case '0': 223 state = S_MAYBEHEX; 224 commit = p; 225 break; 226 case 'I': 227 case 'i': 228 state = S_INF; 229 break; 230 case 'N': 231 case 'n': 232 state = S_NAN; 233 break; 234 default: 235 state = S_DIGITS; 236 goto reswitch; 237 } 238 break; 239 case S_INF: 240 if (infnanpos > 6 || 241 (c != (wint_t)"nfinity"[infnanpos] && 242 c != (wint_t)"NFINITY"[infnanpos])) 243 goto parsedone; 244 if (infnanpos == 1 || infnanpos == 6) 245 commit = p; /* inf or infinity */ 246 infnanpos++; 247 break; 248 case S_NAN: 249 switch (infnanpos) { 250 case -1: /* XXX kludge to deal with nan(...) */ 251 goto parsedone; 252 case 0: 253 if (c != 'A' && c != 'a') 254 goto parsedone; 255 break; 256 case 1: 257 if (c != 'N' && c != 'n') 258 goto parsedone; 259 else 260 commit = p; 261 break; 262 case 2: 263 if (c != '(') 264 goto parsedone; 265 break; 266 default: 267 if (c == ')') { 268 commit = p; 269 infnanpos = -2; 270 } else if (!iswalnum(c) && c != '_') 271 goto parsedone; 272 break; 273 } 274 infnanpos++; 275 break; 276 case S_MAYBEHEX: 277 state = S_DIGITS; 278 if (c == 'X' || c == 'x') { 279 ishex = 1; 280 break; 281 } else { /* we saw a '0', but no 'x' */ 282 gotmantdig = 1; 283 goto reswitch; 284 } 285 case S_DIGITS: 286 if ((ishex && iswxdigit(c)) || iswdigit(c)) 287 gotmantdig = 1; 288 else { 289 state = S_FRAC; 290 if (c != L'.') 291 goto reswitch; 292 } 293 if (gotmantdig) 294 commit = p; 295 break; 296 case S_FRAC: 297 if (((c == 'E' || c == 'e') && !ishex) || 298 ((c == 'P' || c == 'p') && ishex)) { 299 if (!gotmantdig) 300 goto parsedone; 301 else 302 state = S_EXP; 303 } else if ((ishex && iswxdigit(c)) || iswdigit(c)) { 304 commit = p; 305 gotmantdig = 1; 306 } else 307 goto parsedone; 308 break; 309 case S_EXP: 310 state = S_EXPDIGITS; 311 if (c == '-' || c == '+') 312 break; 313 else 314 goto reswitch; 315 case S_EXPDIGITS: 316 if (iswdigit(c)) 317 commit = p; 318 else 319 goto parsedone; 320 break; 321 default: 322 abort(); 323 } 324 *p++ = c; 325 c = WEOF; 326 } 327 328 parsedone: 329 if (c != WEOF) 330 ungetwc(c, fp); 331 while (commit < --p) 332 ungetwc(*p, fp); 333 *++commit = '\0'; 334 return (int)(commit - buf); 335 } 336