*/
#include <stdio.h>
#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h> // strerror
-#define TST(t, class, base, val, exp, fmt) do {\
- t ret = strto##class(#val, NULL, base); \
+#define STR_(v) #v
+#define STR(v) STR_(v)
+#define TST(t, class, base, val, exp, fmt, ofs, exp_errno) do {\
+ const char *in = val;\
+ char *end;\
+ errno = 0;\
+ t ret = strto##class(in, &end, base); \
if( ret != exp ) \
- printf("FAIL strto"#class"('"#val"') != "#val" (act 0x"fmt")\n", ret);\
+ fprintf(stderr, "FAIL strto"#class"('%s') != "#exp" (act "fmt")\n", in, ret);\
+ if( end != in+ofs ) \
+ fprintf(stderr, "FAIL strto"#class"('%s') returned wrong end: %p (+%zi) instead of %p (+%zi)\n",\
+ in,end,end-in,in+ofs,(size_t)ofs);\
+ if( exp_errno != errno ) \
+ fprintf(stderr, "FAIL strto"#class"('%s') returned wrong errno, exp '%s', got '%s'\n",\
+ in, strerror(exp_errno), strerror(errno));\
}while(0)
+#define PRIMEBUF(fmt, val) buf_len = snprintf(buf, sizeof(buf), fmt, val)
+
int main(int argc, char *argv[])
{
- TST(unsigned long, ul, 0, 0x10ec, 0x10ec, "%lx");
- TST(unsigned long long, ull, 0, 0xffeed10ec, 0xffeed10ec, "%llx");
+ char buf[64];
+ size_t buf_len;
+
+ // Success cases
+ TST(unsigned long, ul, 0, "0x10ec", 0x10ec, "%lx", 2+4, 0);
+ TST(unsigned long long, ull, 0, "0xffeed10ec", 0xffeed10ec, "%llx", 2+9, 0);
+ TST(unsigned long long, ull, 0, "01234567", 01234567, "%llo", 8, 0);
+ TST(unsigned long long, ull, 0, "1234567", 1234567, "%lld", 7, 0);
+ TST(long long, ll, 0, "-1", -1, "%lld", 2, 0); // -1
+
+ // Invalid strings
+ TST(unsigned long long, ull, 0, "0x", 0, "%llx", 1, 0); // Single 0
+ TST(unsigned long long, ull, 0, "0xg", 0, "%llx", 1, 0); // Single 0
+ TST(unsigned long long, ull, 0, "-a", 0, "%llx", 0, 0); // Nothing
+ TST(long long, ll, 0, "-a", 0, "%lld", 0, 0); // Nothing
+ TST(long long, ll, 0, "-1aaatg", -1, "%lld", 2, 0); // -1 (with traling junk)
+ TST(long long, ll, 0, "-+1aaatg", 0, "%lld", 0, 0); // Nothing
+ TST(long long, ll, 0, "- 1", 0, "%lld", 0, 0); // Nothing
+ TST(long long, ll, 0, "01278 1", 0127, "%lld", 4, 0); // 0127 with junk
+
+ // Range edges
+ PRIMEBUF("0x%llx", ULLONG_MAX);
+ TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "0x%llx", buf_len, 0);
+ PRIMEBUF("%llu", ULLONG_MAX);
+ TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "%llu", buf_len, 0);
+ PRIMEBUF("-%llu", (long long)LONG_MAX);
+ TST(long, l, 0, buf, -LONG_MAX, "%ld", buf_len, 0);
+
+ // Out of range
+ // - When the range limit is hit, valid characters should still be consumed (just not used)
+ TST(unsigned long long, ull, 0, "0x10000FFFF0000FFFF", ULLONG_MAX, "%llx", strlen(in), ERANGE);
+ TST(unsigned long, ul, 0, "0x10000FFFF0000FFFF", ULONG_MAX, "%lx", strlen(in), ERANGE);
+ TST(long, l, 0, "0x10000FFFF0000FFFF", LONG_MAX, "%ld", strlen(in), ERANGE);
+ TST(long, l, 0, "-0x10000FFFF0000FFFF", LONG_MIN, "%ld", strlen(in), ERANGE);
+ if( LONG_MIN < -LONG_MAX )
+ {
+ // Ensure that if -LONG_MIN is greater than LONG_MAX, that converting it leaves a range error
+ PRIMEBUF("%ld", LONG_MIN);
+ TST(long, l, 0, buf+1, LONG_MAX, "%ld", buf_len-1, ERANGE);
+ }
}
// Handle base detection for hex
if( base == 0 || base == 16 ) {
- if( *str == '0' && str[1] == 'x' ) {
+ if( *str == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]) ) {
str += 2;
base = 16;
}
}
if( next < 0 )
break;
+
+ if( ret == ULLONG_MAX ) {
+ errno = ERANGE;
+ str ++;
+ // Keep eating until first unrecognised character
+ continue;
+ }
+ if( ret > (ULLONG_MAX-next)/base ) {
+ //_SysDebug("strtoull - Out of range (0x%llx > 0x%llx)", ret, (ULLONG_MAX-next)/base);
+ //_SysDebug("strtoull - (%llu > %llu)", ret, (ULLONG_MAX-next)/base);
+ ret = ULLONG_MAX;
+ errno = ERANGE;
+ str ++;
+ continue;
+ }
ret *= base;
ret += next;
str ++;
long long strtoll(const char *str, char **end, int base)
{
int neg = 0;
- unsigned long long ret;
if( !str ) {
errno = EINVAL;
str++;
// Check for negative (or positive) sign
- if(*str == '-' || *str == '+') {
+ if(*str == '-' || *str == '+')
+ {
+ //_SysDebug("strtoll - str[0:1] = '%.2s'", str);
+ if( !isdigit(str[1]) ) {
+ // Non-digit, invalid string
+ if(end) *end = (char*)str;
+ return 0;
+ }
neg = (*str == '-');
str++;
}
- ret = strtoull(str, end, base);
+ unsigned long long ret = strtoull(str, end, base);
+ //_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret);
- if( neg )
+ if( neg ) {
+ if( -ret < LLONG_MIN ) {
+ errno = ERANGE;
+ return LLONG_MIN;
+ }
return -ret;
+ }
else
+ {
+ if( ret > LLONG_MAX ) {
+ errno = ERANGE;
+ return LLONG_MAX;
+ }
return ret;
+ }
}
long strtol(const char *str, char **end, int base)