From: Nicholas Nethercote Date: Sun, 11 Nov 2007 21:58:21 +0000 (+0000) Subject: Add four 'strtoll' variants, which are like 'atoll' but let you detect if X-Git-Tag: svn/VALGRIND_3_3_0~141 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=13c597f709858c2a4d8b5c33c0c99e2e34c6c59d;p=thirdparty%2Fvalgrind.git Add four 'strtoll' variants, which are like 'atoll' but let you detect if the string converted wasn't entirely numeric. Using them for numeric command-line options -- previously if you had a option "--foo=", where is supposed to be an integer, then "--foo=blah" would be interpreted as "--foo=0", because the "blah" would be converted to zero and the remaining chars wouldn't be noticed. Fixed an incorrect command-line option in two massif tests that this change exposed. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7149 --- diff --git a/coregrind/m_libcbase.c b/coregrind/m_libcbase.c index 698772839c..6331a7a657 100644 --- a/coregrind/m_libcbase.c +++ b/coregrind/m_libcbase.c @@ -50,76 +50,178 @@ Bool VG_(isdigit) ( Char c ) Converting strings to numbers ------------------------------------------------------------------ */ -Long VG_(atoll) ( Char* str ) +static Bool is_oct_digit(Char c, Long* digit) +{ + if (c >= '0' && c <= '7') { *digit = (Long)(c - '0'); return True; } + return False; +} + +static Bool is_dec_digit(Char c, Long* digit) +{ + if (c >= '0' && c <= '9') { *digit = (Long)(c - '0'); return True; } + return False; +} + +static Bool is_hex_digit(Char c, Long* digit) +{ + if (c >= '0' && c <= '9') { *digit = (Long)(c - '0'); return True; } + if (c >= 'A' && c <= 'F') { *digit = (Long)((c - 'A') + 10); return True; } + if (c >= 'a' && c <= 'f') { *digit = (Long)((c - 'a') + 10); return True; } + return False; +} + +static Bool is_base36_digit(Char c, Long* digit) +{ + if (c >= '0' && c <= '9') { *digit = (Long)(c - '0'); return True; } + if (c >= 'A' && c <= 'Z') { *digit = (Long)((c - 'A') + 10); return True; } + if (c >= 'a' && c <= 'z') { *digit = (Long)((c - 'a') + 10); return True; } + return False; +} + +Long VG_(strtoll8) ( Char* str, Char** endptr ) { Bool neg = False; - Long n = 0; - if (*str == '-') { str++; neg = True; }; - while (*str >= '0' && *str <= '9') { - n = 10*n + (Long)(*str - '0'); + Long n = 0, digit; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '-' or '+'. + if (*str == '-') { str++; neg = True; } + else if (*str == '+') { str++; } + + while (is_oct_digit(*str, &digit)) { + n = 8*n + digit; str++; } + if (neg) n = -n; + if (endptr) *endptr = str; // Record first failing character. return n; } -Long VG_(atoll16) ( Char* str ) +Long VG_(strtoll10) ( Char* str, Char** endptr ) { Bool neg = False; - Long n = 0; - if (*str == '-') { str++; neg = True; }; - if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) { + Long n = 0, digit; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '-' or '+'. + if (*str == '-') { str++; neg = True; } + else if (*str == '+') { str++; } + + while (is_dec_digit(*str, &digit)) { + n = 10*n + digit; + str++; + } + + if (neg) n = -n; + if (endptr) *endptr = str; // Record first failing character. + return n; +} + +Long VG_(strtoll16) ( Char* str, Char** endptr ) +{ + Bool neg = False; + Long n = 0, digit; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '-' or '+'. + if (*str == '-') { str++; neg = True; } + else if (*str == '+') { str++; } + + // Allow leading "0x", but only if there's a hex digit + // following it. + if (*str == '0' + && (*(str+1) == 'x' || *(str+1) == 'X') + && is_hex_digit( *(str+2), &digit )) { str += 2; } - while (True) { - Char c = *str; - if (c >= '0' && c <= (Char)'9') { - n = 16*n + (Long)(c - '0'); - } - else - if (c >= 'A' && c <= (Char)'F') { - n = 16*n + (Long)((c - 'A') + 10); - } - else - if (c >= 'a' && c <= (Char)'f') { - n = 16*n + (Long)((c - 'a') + 10); - } - else { - break; - } + + while (is_hex_digit(*str, &digit)) { + n = 16*n + digit; str++; } + if (neg) n = -n; + if (endptr) *endptr = str; // Record first failing character. return n; } -Long VG_(atoll36) ( Char* str ) +Long VG_(strtoll36) ( Char* str, Char** endptr ) { Bool neg = False; - Long n = 0; - if (*str == '-') { str++; neg = True; }; - while (True) { - Char c = *str; - if (c >= '0' && c <= (Char)'9') { - n = 36*n + (Long)(c - '0'); - } - else - if (c >= 'A' && c <= (Char)'Z') { - n = 36*n + (Long)((c - 'A') + 10); - } - else - if (c >= 'a' && c <= (Char)'z') { - n = 36*n + (Long)((c - 'a') + 10); - } - else { - break; - } + Long n = 0, digit; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '-' or '+'. + if (*str == '-') { str++; neg = True; } + else if (*str == '+') { str++; } + + while (is_base36_digit(*str, &digit)) { + n = 36*n + digit; str++; } + if (neg) n = -n; + if (endptr) *endptr = str; // Record first failing character. return n; } +double VG_(strtod) ( Char* str, Char** endptr ) +{ + Bool neg = False; + Long digit; + double n = 0, frac = 0, x = 0.1; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '-' or '+'. + if (*str == '-') { str++; neg = True; } + else if (*str == '+') { str++; } + + while (is_dec_digit(*str, &digit)) { + n = 10*n + digit; + str++; + } + + if (*str == '.') { + str++; + while (is_dec_digit(*str, &digit)) { + frac += x*digit; + x /= 10; + str++; + } + } + + n += frac; + if (neg) n = -n; + if (endptr) *endptr = str; // Record first failing character. + return n; +} + +Long VG_(atoll) ( Char* str ) +{ + return VG_(strtoll10)(str, NULL); +} + +Long VG_(atoll16) ( Char* str ) +{ + return VG_(strtoll16)(str, NULL); +} + +Long VG_(atoll36) ( Char* str ) +{ + return VG_(strtoll36)(str, NULL); +} + /* --------------------------------------------------------------------- String functions ------------------------------------------------------------------ */ diff --git a/include/pub_tool_libcbase.h b/include/pub_tool_libcbase.h index f282bfd755..0be7534e51 100644 --- a/include/pub_tool_libcbase.h +++ b/include/pub_tool_libcbase.h @@ -42,6 +42,27 @@ extern Bool VG_(isdigit) ( Char c ); Converting strings to numbers ------------------------------------------------------------------ */ +// Convert strings to numbers according to various bases. Leading +// whitespace is ignored. A subsequent '-' or '+' is accepted. For strtoll16, +// accepts an initial "0x" or "0X" prefix, but only if it's followed by a +// hex digit (if not, the '0' will be read and then it will stop on the +// "x"/"X".) If 'endptr' isn't NULL, it gets filled in with the first +// non-digit char. None of them test that the number fits into 64 bits. +// +// Nb: if you're wondering why we don't just have a single VG_(strtol) which +// takes a base, it's because I wanted it to assert if it was given a bogus +// base (the standard glibc one sets 'errno' in this case). But +// m_libcbase.c doesn't import any code, not even vg_assert. --njn +extern Long VG_(strtoll8) ( Char* str, Char** endptr ); +extern Long VG_(strtoll10) ( Char* str, Char** endptr ); +extern Long VG_(strtoll16) ( Char* str, Char** endptr ); +extern Long VG_(strtoll36) ( Char* str, Char** endptr ); + + // Convert a string to a double. After leading whitespace is ignored, + // it accepts a non-empty sequence of decimal digits possibly containing + // a '.'. +extern double VG_(strtod) ( Char* str, Char** endptr ); + extern Long VG_(atoll) ( Char* str ); // base 10 extern Long VG_(atoll16) ( Char* str ); // base 16; leading 0x accepted extern Long VG_(atoll36) ( Char* str ); // base 36 diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h index 3c50ce69df..e39526047a 100644 --- a/include/pub_tool_options.h +++ b/include/pub_tool_options.h @@ -52,7 +52,11 @@ #define VG_NUM_CLO(qq_arg, qq_option, qq_var) \ if (VG_CLO_STREQN(VG_(strlen)(qq_option)+1, qq_arg, qq_option"=")) { \ - (qq_var) = (Int)VG_(atoll)( &qq_arg[ VG_(strlen)(qq_option)+1 ] ); \ + Char* s; \ + Long n = VG_(strtoll10)( &qq_arg[ VG_(strlen)(qq_option)+1 ], &s );\ + (qq_var) = n; \ + /* Check for non-numeralness, or overflow */ \ + if ('\0' != s[0] || (qq_var) != n) VG_(err_bad_option)(qq_arg); \ } /* Same as VG_NUM_CLO but does not coerce the result value to 32 bits diff --git a/massif/tests/zero1.post.exp b/massif/tests/zero1.post.exp index 9c6c784600..d27a8628e0 100644 --- a/massif/tests/zero1.post.exp +++ b/massif/tests/zero1.post.exp @@ -1,6 +1,6 @@ -------------------------------------------------------------------------------- Command: ./zero -Massif arguments: --stacks=no --heap-admin=no --time-unit=B +Massif arguments: --stacks=no --heap-admin=0 --time-unit=B ms_print arguments: --threshold=0 massif.out -------------------------------------------------------------------------------- diff --git a/massif/tests/zero1.vgtest b/massif/tests/zero1.vgtest index b07738ba6d..e32c628c14 100644 --- a/massif/tests/zero1.vgtest +++ b/massif/tests/zero1.vgtest @@ -1,4 +1,4 @@ prog: zero -vgopts: --stacks=no --heap-admin=no --time-unit=B +vgopts: --stacks=no --heap-admin=0 --time-unit=B post: perl ../../massif/ms_print --threshold=0 massif.out | ../../tests/filter_addresses cleanup: rm massif.out diff --git a/massif/tests/zero2.post.exp b/massif/tests/zero2.post.exp index d8421699d2..244c60a92e 100644 --- a/massif/tests/zero2.post.exp +++ b/massif/tests/zero2.post.exp @@ -1,6 +1,6 @@ -------------------------------------------------------------------------------- Command: ./zero -Massif arguments: --stacks=no --heap-admin=no --time-unit=B +Massif arguments: --stacks=no --heap-admin=0 --time-unit=B ms_print arguments: massif.out -------------------------------------------------------------------------------- diff --git a/massif/tests/zero2.vgtest b/massif/tests/zero2.vgtest index 2044bba4b3..8cacf37461 100644 --- a/massif/tests/zero2.vgtest +++ b/massif/tests/zero2.vgtest @@ -1,4 +1,4 @@ prog: zero -vgopts: --stacks=no --heap-admin=no --time-unit=B +vgopts: --stacks=no --heap-admin=0 --time-unit=B post: perl ../../massif/ms_print massif.out | ../../tests/filter_addresses cleanup: rm massif.out