diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | strings/apr_snprintf.c | 3 | ||||
-rw-r--r-- | strings/apr_strings.c | 4 | ||||
-rw-r--r-- | test/teststr.c | 97 |
4 files changed, 107 insertions, 3 deletions
@@ -1,5 +1,11 @@ Changes with APR 0.9.5 + *) Fix handling of negative numbers in apr_strtoi64() on platforms + without strtoll. [Joe Orton] + + *) Fix printing apr_int64_t values smaller than LONG_MIN on 32-bit + platforms in apr_vformatter. [Joe Orton] + *) Fix apr_socket_opt_set with APR_IPV6_V6ONLY flag. Fixes httpd Listen IPv6 socket behavior on FreeBSD 5.x, OpenBSD, NetBSD. [Justin Erenkrantz] diff --git a/strings/apr_snprintf.c b/strings/apr_snprintf.c index bc0e099e5..9f4225464 100644 --- a/strings/apr_snprintf.c +++ b/strings/apr_snprintf.c @@ -393,7 +393,8 @@ static char *conv_10_quad(widest_int num, register bool_int is_unsigned, * number against the largest long value it can be. If <=, we * punt to the quicker version. */ - if ((num <= ULONG_MAX && is_unsigned) || (num <= LONG_MAX && !is_unsigned)) + if ((num <= ULONG_MAX && is_unsigned) + || (num <= LONG_MAX && num >= LONG_MIN && !is_unsigned)) return(conv_10( (wide_int)num, is_unsigned, is_negative, buf_end, len)); diff --git a/strings/apr_strings.c b/strings/apr_strings.c index ee1c1f1c3..a88f35e09 100644 --- a/strings/apr_strings.c +++ b/strings/apr_strings.c @@ -278,7 +278,7 @@ APR_DECLARE(apr_int64_t) apr_strtoi64(const char *nptr, char **endptr, int base) } /* The classic bsd implementation requires div/mod operators - * to compute a cutoff. Benchmarking proves that iss very, very + * to compute a cutoff. Benchmarking proves that is very, very * evil to some 32 bit processors. Instead, look for underflow * in both the mult and add/sub operation. Unlike the bsd impl, * we also work strictly in a signed int64 word as we haven't @@ -319,7 +319,7 @@ APR_DECLARE(apr_int64_t) apr_strtoi64(const char *nptr, char **endptr, int base) val *= base; if ( (any < 0) /* already noted an over/under flow - short circuit */ || (neg && (val > acc || (val -= c) > acc)) /* underflow */ - || (val < acc || (val += c) < acc)) { /* overflow */ + || (!neg && (val < acc || (val += c) < acc))) { /* overflow */ any = -1; /* once noted, over/underflows never go away */ #ifdef APR_STRTOI64_OVERFLOW_IS_BAD_CHAR break; diff --git a/test/teststr.c b/test/teststr.c index 8ea784dd3..0df64230d 100644 --- a/test/teststr.c +++ b/test/teststr.c @@ -167,6 +167,102 @@ static void string_long(CuTest *tc) apr_psprintf(p, "%s", s); } +/* ### FIXME: apr.h/apr_strings.h should provide these! */ +#define MY_LLONG_MAX (APR_INT64_C(9223372036854775807)) +#define MY_LLONG_MIN (-MY_LLONG_MAX - APR_INT64_C(1)) + +static void string_strtoi64(CuTest *tc) +{ + static const struct { + int errnum, base; + const char *in, *end; + apr_int64_t result; + } ts[] = { + + /* base 10 tests */ + { 0, 10, "123545", NULL, APR_INT64_C(123545) }, + { 0, 10, " 123545", NULL, APR_INT64_C(123545) }, + { 0, 10, " +123545", NULL, APR_INT64_C(123545) }, + { 0, 10, "-123545", NULL, APR_INT64_C(-123545) }, + { 0, 10, " 00000123545", NULL, APR_INT64_C(123545) }, + { 0, 10, "123545ZZZ", "ZZZ", APR_INT64_C(123545) }, + { 0, 10, " 123545 ", " ", APR_INT64_C(123545) }, + + /* base 16 tests */ + { 0, 16, "1E299", NULL, APR_INT64_C(123545) }, + { 0, 16, "1e299", NULL, APR_INT64_C(123545) }, + { 0, 16, "0x1e299", NULL, APR_INT64_C(123545) }, + { 0, 16, "0X1E299", NULL, APR_INT64_C(123545) }, + { 0, 16, "+1e299", NULL, APR_INT64_C(123545) }, + { 0, 16, "-1e299", NULL, APR_INT64_C(-123545) }, + { 0, 16, " -1e299", NULL, APR_INT64_C(-123545) }, + + /* automatic base detection tests */ + { 0, 0, "123545", NULL, APR_INT64_C(123545) }, + { 0, 0, "0x1e299", NULL, APR_INT64_C(123545) }, + { 0, 0, " 0x1e299", NULL, APR_INT64_C(123545) }, + { 0, 0, "+0x1e299", NULL, APR_INT64_C(123545) }, + { 0, 0, "-0x1e299", NULL, APR_INT64_C(-123545) }, + + /* large number tests */ + { 0, 10, "8589934605", NULL, APR_INT64_C(8589934605) }, + { 0, 10, "-8589934605", NULL, APR_INT64_C(-8589934605) }, + { 0, 16, "0x20000000D", NULL, APR_INT64_C(8589934605) }, + { 0, 16, "-0x20000000D", NULL, APR_INT64_C(-8589934605) }, + { 0, 16, " 0x20000000D", NULL, APR_INT64_C(8589934605) }, + { 0, 16, " 0x20000000D", NULL, APR_INT64_C(8589934605) }, + + /* error cases */ + { ERANGE, 10, "999999999999999999999999999999999", "", MY_LLONG_MAX }, + { ERANGE, 10, "-999999999999999999999999999999999", "", MY_LLONG_MIN }, + +#if 0 + /* C99 doesn't require EINVAL for an invalid range. */ + { EINVAL, 99, "", (void *)-1 /* don't care */, 0 }, +#endif + + /* some strtoll implementations give EINVAL when no conversion + * is performed. */ + { -1 /* don't care */, 10, "zzz", "zzz", APR_INT64_C(0) }, + { -1 /* don't care */, 10, "", NULL, APR_INT64_C(0) } + + }; + int n; + + for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) { + char *end = "end ptr not changed"; + apr_int64_t result; + int errnum; + + errno = 0; + result = apr_strtoi64(ts[n].in, &end, ts[n].base); + errnum = errno; + + CuAssert(tc, + apr_psprintf(p, "for '%s': result was %" APR_INT64_T_FMT + " not %" APR_INT64_T_FMT, ts[n].in, + result, ts[n].result), + result == ts[n].result); + + if (ts[n].errnum != -1) { + CuAssert(tc, + apr_psprintf(p, "for '%s': errno was %d not %d", ts[n].in, + errnum, ts[n].errnum), + ts[n].errnum == errnum); + } + + if (ts[n].end == NULL) { + /* end must point to NUL terminator of .in */ + CuAssertPtrEquals(tc, ts[n].in + strlen(ts[n].in), end); + } else if (ts[n].end != (void *)-1) { + CuAssert(tc, + apr_psprintf(p, "for '%s', end was '%s' not '%s'", + ts[n].in, end, ts[n].end), + strcmp(ts[n].end, end) == 0); + } + } +} + CuSuite *teststr(void) { CuSuite *suite = CuSuiteNew("Strings"); @@ -178,6 +274,7 @@ CuSuite *teststr(void) SUITE_ADD_TEST(suite, test_strtok); SUITE_ADD_TEST(suite, string_error); SUITE_ADD_TEST(suite, string_long); + SUITE_ADD_TEST(suite, string_strtoi64); return suite; } |