summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--strings/apr_snprintf.c3
-rw-r--r--strings/apr_strings.c4
-rw-r--r--test/teststr.c97
4 files changed, 107 insertions, 3 deletions
diff --git a/CHANGES b/CHANGES
index 0ce48d80c..862de14db 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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;
}