• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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