1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | This source file is subject to version 3.01 of the PHP license, |
6 : | that is bundled with this package in the file LICENSE, and is |
7 : | available through the world-wide-web at the following url: |
8 : | http://www.php.net/license/3_01.txt |
9 : | If you did not receive a copy of the PHP license and are unable to |
10 : | obtain it through the world-wide-web, please send a note to |
11 : | license@php.net so we can mail you a copy immediately. |
12 : +----------------------------------------------------------------------+
13 : | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
14 : | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
15 : +----------------------------------------------------------------------+
16 : */
17 :
18 : #include "collator_is_numeric.h"
19 :
20 : #if ZEND_MODULE_API_NO < 20071006
21 : /* not 5.3 */
22 : #ifndef ALLOCA_FLAG
23 : #define ALLOCA_FLAG(use_heap)
24 : #endif
25 : #define _do_alloca(x, y) do_alloca((x))
26 : #define _free_alloca(x, y) free_alloca((x))
27 : #else
28 : #define _do_alloca do_alloca
29 : #define _free_alloca free_alloca
30 : #endif
31 : /* {{{ collator_u_strtod
32 : * Taken from PHP6:zend_u_strtod()
33 : */
34 : static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
35 0 : {
36 0 : const UChar *u = nptr, *nstart;
37 0 : UChar c = *u;
38 0 : int any = 0;
39 : ALLOCA_FLAG(use_heap);
40 :
41 0 : while (u_isspace(c)) {
42 0 : c = *++u;
43 : }
44 0 : nstart = u;
45 :
46 0 : if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
47 0 : c = *++u;
48 : }
49 :
50 0 : while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
51 0 : any = 1;
52 0 : c = *++u;
53 : }
54 :
55 0 : if (c == 0x2E /*'.'*/) {
56 0 : c = *++u;
57 0 : while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
58 0 : any = 1;
59 0 : c = *++u;
60 : }
61 : }
62 :
63 0 : if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) {
64 0 : const UChar *e = u;
65 0 : int any_exp = 0;
66 :
67 0 : c = *++u;
68 0 : if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
69 0 : c = *++u;
70 : }
71 :
72 0 : while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
73 0 : any_exp = 1;
74 0 : c = *++u;
75 : }
76 :
77 0 : if (!any_exp) {
78 0 : u = e;
79 : }
80 : }
81 :
82 0 : if (any) {
83 : char buf[64], *numbuf, *bufpos;
84 0 : int length = u - nstart;
85 : double value;
86 :
87 0 : if (length < sizeof(buf)) {
88 0 : numbuf = buf;
89 : } else {
90 0 : numbuf = (char *) _do_alloca(length + 1, use_heap);
91 : }
92 :
93 0 : bufpos = numbuf;
94 :
95 0 : while (nstart < u) {
96 0 : *bufpos++ = (char) *nstart++;
97 : }
98 :
99 0 : *bufpos = '\0';
100 0 : value = zend_strtod(numbuf, NULL);
101 :
102 0 : if (numbuf != buf) {
103 0 : _free_alloca(numbuf, use_heap);
104 : }
105 :
106 0 : if (endptr != NULL) {
107 0 : *endptr = (UChar *)u;
108 : }
109 :
110 0 : return value;
111 : }
112 :
113 0 : if (endptr != NULL) {
114 0 : *endptr = (UChar *)nptr;
115 : }
116 :
117 0 : return 0;
118 : }
119 : /* }}} */
120 :
121 : /* {{{ collator_u_strtol
122 : * Taken from PHP6:zend_u_strtol()
123 : *
124 : * Convert a Unicode string to a long integer.
125 : *
126 : * Ignores `locale' stuff.
127 : */
128 : static long collator_u_strtol(nptr, endptr, base)
129 : const UChar *nptr;
130 : UChar **endptr;
131 : register int base;
132 204 : {
133 204 : register const UChar *s = nptr;
134 : register unsigned long acc;
135 : register UChar c;
136 : register unsigned long cutoff;
137 204 : register int neg = 0, any, cutlim;
138 :
139 204 : if (s == NULL) {
140 0 : errno = ERANGE;
141 0 : if (endptr != NULL) {
142 0 : *endptr = NULL;
143 : }
144 0 : return 0;
145 : }
146 :
147 : /*
148 : * Skip white space and pick up leading +/- sign if any.
149 : * If base is 0, allow 0x for hex and 0 for octal, else
150 : * assume decimal; if base is already 16, allow 0x.
151 : */
152 : do {
153 210 : c = *s++;
154 210 : } while (u_isspace(c));
155 204 : if (c == 0x2D /*'-'*/) {
156 0 : neg = 1;
157 0 : c = *s++;
158 204 : } else if (c == 0x2B /*'+'*/)
159 0 : c = *s++;
160 204 : if ((base == 0 || base == 16) &&
161 : (c == 0x30 /*'0'*/)
162 : && (*s == 0x78 /*'x'*/ || *s == 0x58 /*'X'*/)) {
163 0 : c = s[1];
164 0 : s += 2;
165 0 : base = 16;
166 : }
167 204 : if (base == 0)
168 0 : base = (c == 0x30 /*'0'*/) ? 8 : 10;
169 :
170 : /*
171 : * Compute the cutoff value between legal numbers and illegal
172 : * numbers. That is the largest legal value, divided by the
173 : * base. An input number that is greater than this value, if
174 : * followed by a legal input character, is too big. One that
175 : * is equal to this value may be valid or not; the limit
176 : * between valid and invalid numbers is then based on the last
177 : * digit. For instance, if the range for longs is
178 : * [-2147483648..2147483647] and the input base is 10,
179 : * cutoff will be set to 214748364 and cutlim to either
180 : * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
181 : * a value > 214748364, or equal but the next digit is > 7 (or 8),
182 : * the number is too big, and we will return a range error.
183 : *
184 : * Set any if any `digits' consumed; make it negative to indicate
185 : * overflow.
186 : */
187 204 : cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
188 204 : cutlim = cutoff % (unsigned long)base;
189 204 : cutoff /= (unsigned long)base;
190 302 : for (acc = 0, any = 0;; c = *s++) {
191 400 : if (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/)
192 98 : c -= 0x30 /*'0'*/;
193 204 : else if (c >= 0x41 /*'A'*/ && c <= 0x5A /*'Z'*/)
194 0 : c -= 0x41 /*'A'*/ - 10;
195 204 : else if (c >= 0x61 /*'a'*/ && c <= 0x7A /*'z'*/)
196 106 : c -= 0x61 /*'a'*/ - 10;
197 : else
198 : break;
199 204 : if (c >= base)
200 106 : break;
201 :
202 98 : if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
203 0 : any = -1;
204 : else {
205 98 : any = 1;
206 98 : acc *= base;
207 98 : acc += c;
208 : }
209 98 : }
210 204 : if (any < 0) {
211 0 : acc = neg ? LONG_MIN : LONG_MAX;
212 0 : errno = ERANGE;
213 204 : } else if (neg)
214 0 : acc = -acc;
215 204 : if (endptr != NULL)
216 204 : *endptr = (UChar *)(any ? s - 1 : nptr);
217 204 : return (acc);
218 : }
219 : /* }}} */
220 :
221 :
222 : /* {{{ collator_is_numeric]
223 : * Taken from PHP6:is_numeric_unicode()
224 : */
225 : zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors )
226 212 : {
227 : long local_lval;
228 : double local_dval;
229 : UChar *end_ptr_long, *end_ptr_double;
230 212 : int conv_base=10;
231 :
232 212 : if (!length) {
233 8 : return 0;
234 : }
235 :
236 : /* handle hex numbers */
237 204 : if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
238 0 : conv_base=16;
239 : }
240 :
241 204 : errno=0;
242 204 : local_lval = collator_u_strtol(str, &end_ptr_long, conv_base);
243 204 : if (errno != ERANGE) {
244 204 : if (end_ptr_long == str+length) { /* integer string */
245 46 : if (lval) {
246 46 : *lval = local_lval;
247 : }
248 46 : return IS_LONG;
249 158 : } else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */
250 158 : return 0;
251 : }
252 : } else {
253 0 : end_ptr_long = NULL;
254 : }
255 :
256 0 : if (conv_base == 16) { /* hex string, under UNIX strtod() messes it up */
257 : /* UTODO: keep compatibility with is_numeric_string() here? */
258 0 : return 0;
259 : }
260 :
261 0 : local_dval = collator_u_strtod(str, &end_ptr_double);
262 0 : if (local_dval == 0 && end_ptr_double == str) {
263 0 : end_ptr_double = NULL;
264 : } else {
265 0 : if (end_ptr_double == str+length) { /* floating point string */
266 0 : if (!zend_finite(local_dval)) {
267 : /* "inf","nan" and maybe other weird ones */
268 0 : return 0;
269 : }
270 :
271 0 : if (dval) {
272 0 : *dval = local_dval;
273 : }
274 0 : return IS_DOUBLE;
275 : }
276 : }
277 :
278 0 : if (!allow_errors) {
279 0 : return 0;
280 : }
281 0 : if (allow_errors == -1) {
282 0 : zend_error(E_NOTICE, "A non well formed numeric value encountered");
283 : }
284 :
285 0 : if (allow_errors) {
286 0 : if (end_ptr_double > end_ptr_long && dval) {
287 0 : *dval = local_dval;
288 0 : return IS_DOUBLE;
289 0 : } else if (end_ptr_long && lval) {
290 0 : *lval = local_lval;
291 0 : return IS_LONG;
292 : }
293 : }
294 0 : return 0;
295 : }
296 : /* }}} */
297 :
298 : /*
299 : * Local variables:
300 : * tab-width: 4
301 : * c-basic-offset: 4
302 : * End:
303 : * vim600: noet sw=4 ts=4 fdm=marker
304 : * vim<600: noet sw=4 ts=4
305 : */
|