• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1990, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Copyright (c) 2011 The FreeBSD Foundation
8  * All rights reserved.
9  * Portions of this software were developed by David Chisnall
10  * under sponsorship from the FreeBSD Foundation.
11  *
12  * This code is derived from software contributed to Berkeley by
13  * Chris Torek.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  * 3. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include <ctype.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include "shgetc.h"
44 #include "stdio_impl.h"
45 
parsefloat(FILE * f,char * buf,char * end)46 int parsefloat(FILE *f, char *buf, char *end)
47 {
48 	char *commit, *p;
49 	int infnanpos = 0, decptpos = 0;
50 	enum {
51 		S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
52 		S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS
53 	} state = S_START;
54 	unsigned char c;
55 	int gotmantdig = 0, ishex = 0;
56 	const char *decpt = "";
57 
58 	/*
59 	 * We set commit = p whenever the string we have read so far
60 	 * constitutes a valid representation of a floating point
61 	 * number by itself.  At some point, the parse will complete
62 	 * or fail, and we will ungetc() back to the last commit point.
63 	 * To ensure that the file offset gets updated properly, it is
64 	 * always necessary to read at least one character that doesn't
65 	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
66 	 */
67 	commit = buf - 1;
68 	for (p = buf; p < end; ) {
69 		// When file buffer has no content to read, it need to refill buf again.
70 		if (shgetc(f) < 0) {
71 			break;
72 		} else {
73 			shunget(f);
74 		}
75 		c = *f->rpos;
76 reswitch:
77 		switch (state) {
78 		case S_START:
79 			state = S_GOTSIGN;
80 			if (c == '-' || c == '+')
81 				break;
82 			else
83 				goto reswitch;
84 		case S_GOTSIGN:
85 			switch (c) {
86 			case '0':
87 				state = S_MAYBEHEX;
88 				commit = p;
89 				break;
90 			case 'I':
91 			case 'i':
92 				state = S_INF;
93 				break;
94 			case 'N':
95 			case 'n':
96 				state = S_NAN;
97 				break;
98 			default:
99 				state = S_DIGITS;
100 				goto reswitch;
101 			}
102 			break;
103 		case S_INF:
104 			if (infnanpos > 6 ||
105 			    (c != "nfinity"[infnanpos] &&
106 			     c != "NFINITY"[infnanpos]))
107 				goto parsedone;
108 			if (infnanpos == 1 || infnanpos == 6)
109 				commit = p;	/* inf or infinity */
110 			infnanpos++;
111 			break;
112 		case S_NAN:
113 			switch (infnanpos) {
114 			case 0:
115 				if (c != 'A' && c != 'a')
116 					goto parsedone;
117 				break;
118 			case 1:
119 				if (c != 'N' && c != 'n')
120 					goto parsedone;
121 				else
122 					commit = p;
123 				break;
124 			case 2:
125 				if (c != '(')
126 					goto parsedone;
127 				break;
128 			default:
129 				if (c == ')') {
130 					commit = p;
131 					infnanpos = -2;
132 				} else if (!isalnum(c) && c != '_')
133 					goto parsedone;
134 				break;
135 			}
136 			infnanpos++;
137 			break;
138 		case S_DONE:
139 			goto parsedone;
140 		case S_MAYBEHEX:
141 			state = S_DIGITS;
142 			if (c == 'X' || c == 'x') {
143 				ishex = 1;
144 				break;
145 			} else {	/* we saw a '0', but no 'x' */
146 				gotmantdig = 1;
147 				goto reswitch;
148 			}
149 		case S_DIGITS:
150 			if ((ishex && isxdigit(c)) || isdigit(c)) {
151 				gotmantdig = 1;
152 			} else {
153 				state = S_FRAC;
154 				if (c != '.')
155 					goto reswitch;
156 			}
157 			if (gotmantdig)
158 				commit = p;
159 			break;
160 		case S_DECPT:
161 			if (c == decpt[decptpos]) {
162 				if (decpt[++decptpos] == '\0') {
163 					/* We read the complete decpt seq. */
164 					state = S_FRAC;
165 					if (gotmantdig)
166 						commit = p;
167 				}
168 				break;
169 			} else if (!decptpos) {
170 				/* We didn't read any decpt characters. */
171 				state = S_FRAC;
172 				goto reswitch;
173 			} else {
174 				/*
175 				 * We read part of a multibyte decimal point,
176 				 * but the rest is invalid, so bail.
177 				 */
178 				goto parsedone;
179 			}
180 		case S_FRAC:
181 			if (((c == 'E' || c == 'e') && !ishex) ||
182 			    ((c == 'P' || c == 'p') && ishex)) {
183 				if (!gotmantdig)
184 					goto parsedone;
185 				else
186 					state = S_EXP;
187 			} else if ((ishex && isxdigit(c)) || isdigit(c)) {
188 				commit = p;
189 				gotmantdig = 1;
190 			} else
191 				goto parsedone;
192 			break;
193 		case S_EXP:
194 			state = S_EXPDIGITS;
195 			if (c == '-' || c == '+')
196 				break;
197 			else
198 				goto reswitch;
199 		case S_EXPDIGITS:
200 			if (isdigit(c))
201 				commit = p;
202 			else
203 				goto parsedone;
204 			break;
205 		default:
206 			abort();
207 		}
208 		*p++ = c;
209 		// When file buffer has no content to read, it need to refill buf again.
210 		if (shgetc(f) < 0) {
211 			break;
212 		} else {
213 			shunget(f);
214 		}
215 		f->rpos++;
216 	}
217 
218 parsedone:
219 	while (commit < --p)
220                 shunget(f);
221 	*++commit = '\0';
222 	return (commit - buf);
223 }
224