diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-03-14 05:42:27 +0000 |
---|---|---|
committer | <> | 2013-04-03 16:25:08 +0000 |
commit | c4dd7a1a684490673e25aaf4fabec5df138854c4 (patch) | |
tree | 4d57c44caae4480efff02b90b9be86f44bf25409 /ext/intl/collator/collator_is_numeric.c | |
download | php2-master.tar.gz |
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/intl/collator/collator_is_numeric.c')
-rw-r--r-- | ext/intl/collator/collator_is_numeric.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/ext/intl/collator/collator_is_numeric.c b/ext/intl/collator/collator_is_numeric.c new file mode 100644 index 0000000..a8abfac --- /dev/null +++ b/ext/intl/collator/collator_is_numeric.c @@ -0,0 +1,305 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Vadim Savchuk <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#include "collator_is_numeric.h" + +#if ZEND_MODULE_API_NO < 20071006 +/* not 5.3 */ +#ifndef ALLOCA_FLAG +#define ALLOCA_FLAG(use_heap) +#endif +#define _do_alloca(x, y) do_alloca((x)) +#define _free_alloca(x, y) free_alloca((x)) +#else +#define _do_alloca do_alloca +#define _free_alloca free_alloca +#endif +/* {{{ collator_u_strtod + * Taken from PHP6:zend_u_strtod() + */ +static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */ +{ + const UChar *u = nptr, *nstart; + UChar c = *u; + int any = 0; + ALLOCA_FLAG(use_heap); + + while (u_isspace(c)) { + c = *++u; + } + nstart = u; + + if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) { + c = *++u; + } + + while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { + any = 1; + c = *++u; + } + + if (c == 0x2E /*'.'*/) { + c = *++u; + while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { + any = 1; + c = *++u; + } + } + + if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) { + const UChar *e = u; + int any_exp = 0; + + c = *++u; + if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) { + c = *++u; + } + + while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { + any_exp = 1; + c = *++u; + } + + if (!any_exp) { + u = e; + } + } + + if (any) { + char buf[64], *numbuf, *bufpos; + int length = u - nstart; + double value; + + if (length < sizeof(buf)) { + numbuf = buf; + } else { + numbuf = (char *) _do_alloca(length + 1, use_heap); + } + + bufpos = numbuf; + + while (nstart < u) { + *bufpos++ = (char) *nstart++; + } + + *bufpos = '\0'; + value = zend_strtod(numbuf, NULL); + + if (numbuf != buf) { + _free_alloca(numbuf, use_heap); + } + + if (endptr != NULL) { + *endptr = (UChar *)u; + } + + return value; + } + + if (endptr != NULL) { + *endptr = (UChar *)nptr; + } + + return 0; +} +/* }}} */ + +/* {{{ collator_u_strtol + * Taken from PHP6:zend_u_strtol() + * + * Convert a Unicode string to a long integer. + * + * Ignores `locale' stuff. + */ +static long collator_u_strtol(nptr, endptr, base) + const UChar *nptr; + UChar **endptr; + register int base; +{ + register const UChar *s = nptr; + register unsigned long acc; + register UChar c; + register unsigned long cutoff; + register int neg = 0, any, cutlim; + + if (s == NULL) { + errno = ERANGE; + if (endptr != NULL) { + *endptr = NULL; + } + return 0; + } + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (u_isspace(c)); + if (c == 0x2D /*'-'*/) { + neg = 1; + c = *s++; + } else if (c == 0x2B /*'+'*/) + c = *s++; + if ((base == 0 || base == 16) && + (c == 0x30 /*'0'*/) + && (*s == 0x78 /*'x'*/ || *s == 0x58 /*'X'*/)) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = (c == 0x30 /*'0'*/) ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long)base; + cutoff /= (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) + c -= 0x30 /*'0'*/; + else if (c >= 0x41 /*'A'*/ && c <= 0x5A /*'Z'*/) + c -= 0x41 /*'A'*/ - 10; + else if (c >= 0x61 /*'a'*/ && c <= 0x7A /*'z'*/) + c -= 0x61 /*'a'*/ - 10; + else + break; + if (c >= base) + break; + + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (UChar *)(any ? s - 1 : nptr); + return (acc); +} +/* }}} */ + + +/* {{{ collator_is_numeric] + * Taken from PHP6:is_numeric_unicode() + */ +zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors ) +{ + long local_lval; + double local_dval; + UChar *end_ptr_long, *end_ptr_double; + int conv_base=10; + + if (!length) { + return 0; + } + + /* handle hex numbers */ + if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) { + conv_base=16; + } + + errno=0; + local_lval = collator_u_strtol(str, &end_ptr_long, conv_base); + if (errno != ERANGE) { + if (end_ptr_long == str+length) { /* integer string */ + if (lval) { + *lval = local_lval; + } + return IS_LONG; + } else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */ + return 0; + } + } else { + end_ptr_long = NULL; + } + + if (conv_base == 16) { /* hex string, under UNIX strtod() messes it up */ + /* UTODO: keep compatibility with is_numeric_string() here? */ + return 0; + } + + local_dval = collator_u_strtod(str, &end_ptr_double); + if (local_dval == 0 && end_ptr_double == str) { + end_ptr_double = NULL; + } else { + if (end_ptr_double == str+length) { /* floating point string */ + if (!zend_finite(local_dval)) { + /* "inf","nan" and maybe other weird ones */ + return 0; + } + + if (dval) { + *dval = local_dval; + } + return IS_DOUBLE; + } + } + + if (!allow_errors) { + return 0; + } + if (allow_errors == -1) { + zend_error(E_NOTICE, "A non well formed numeric value encountered"); + } + + if (allow_errors) { + if (end_ptr_double > end_ptr_long && dval) { + *dval = local_dval; + return IS_DOUBLE; + } else if (end_ptr_long && lval) { + *lval = local_lval; + return IS_LONG; + } + } + return 0; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ |