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
parsefloat(FILE * fp,char * buf,char * end)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
wparsefloat(FILE * fp,wchar_t * buf,wchar_t * end)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