From: Erik van der Kouwe Date: Wed, 9 Dec 2009 19:01:38 +0000 (+0000) Subject: Implementation of strto(u)ll, documentation and tests for strto(u)l(l) X-Git-Tag: v3.1.6~158 X-Git-Url: http://zhaoyanbai.com/repos/man.host.html?a=commitdiff_plain;h=6adadade32d69e78cfc2ea27383b5b4cb7c6506e;p=minix.git Implementation of strto(u)ll, documentation and tests for strto(u)l(l) --- diff --git a/include/limits.h b/include/limits.h index 5569a14d0..37ae59ccc 100644 --- a/include/limits.h +++ b/include/limits.h @@ -38,6 +38,19 @@ #define LONG_MAX 2147483647L /* maximum value of a long */ #define ULONG_MAX 0xFFFFFFFFL /* maximum value of an unsigned long */ +/*Definitions about long longs (64 bits, may not be supported). */ +#ifdef __LONG_LONG_SUPPORTED +#define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL-1) /* minimum value of a + * long long + */ +#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL /* maximum value of a + * long long + */ +#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL /* maximum value of an + * unsigned long long + */ +#endif + #include /* Minimum sizes required by the POSIX P1003.1 standard (Table 2-3). */ diff --git a/include/stdlib.h b/include/stdlib.h index 3b3adab96..6dc80237c 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -77,6 +77,14 @@ _PROTOTYPE( int putenv, (char *string) ); _PROTOTYPE( int setenv, (const char *envname, const char *envval, int overwrite) ); _PROTOTYPE( int unsetenv, (const char *name) ); + +#ifdef __LONG_LONG_SUPPORTED +_PROTOTYPE( long long strtoll, (const char *_nptr, char **_endptr, + int _base) ); +_PROTOTYPE( unsigned long long strtoull, (const char *_nptr, + char **_endptr, int _base) ); +#endif + #endif #ifdef _MINIX diff --git a/lib/other/Makefile.in b/lib/other/Makefile.in index 6f191121b..bc7bc570e 100644 --- a/lib/other/Makefile.in +++ b/lib/other/Makefile.in @@ -86,6 +86,7 @@ libc_FILES=" \ strlcat.c \ strlcpy.c \ strtok_r.c \ + strtoll.c \ swab.c \ sys_eniop.c \ syscall.c \ diff --git a/lib/other/strtoll.c b/lib/other/strtoll.c new file mode 100644 index 000000000..b679a45c0 --- /dev/null +++ b/lib/other/strtoll.c @@ -0,0 +1,99 @@ +/* Erik van der Kouwe, 8 December 2009, based on lib/ansi/strtol.c */ + +#include +#include +#include +#include +#include + +#ifdef __LONG_LONG_SUPPORTED + +static unsigned long long string2long(const char *nptr, char **endptr, + int base, int is_signed); + +long long strtoll(const char *nptr, char **endptr, int base) +{ + return (long long) string2long(nptr, endptr, base, 1); +} + +unsigned long long strtoull(const char *nptr, char **endptr, int base) +{ + return (unsigned long long) string2long(nptr, endptr, base, 0); +} + +#define between(a, c, z) \ + ((unsigned long) ((c) - (a)) <= (unsigned long) ((z) - (a))) + +static unsigned long long string2long(const char *nptr, char **const endptr, + int base, int is_signed) +{ + unsigned int v; + unsigned long long val = 0; + int c; + int ovfl = 0, sign = 1; + const char *startnptr = nptr, *nrstart; + + if (endptr) *endptr = (char *)nptr; + while (isspace(*nptr)) nptr++; + c = *nptr; + + if (c == '-' || c == '+') { + if (c == '-') sign = -1; + nptr++; + } + nrstart = nptr; /* start of the number */ + + /* When base is 0, the syntax determines the actual base */ + if (base == 0) + if (*nptr == '0') + if (*++nptr == 'x' || *nptr == 'X') { + base = 16; + nptr++; + } + else base = 8; + else base = 10; + else if (base==16 && *nptr=='0' && (*++nptr =='x' || *nptr =='X')) + nptr++; + + for (;;) { + c = *nptr; + if (between('0', c, '9')) { + v = c - '0'; + } else + if (between('a', c, 'z')) { + v = c - 'a' + 0xa; + } else + if (between('A', c, 'Z')) { + v = c - 'A' + 0xA; + } else { + break; + } + if (v >= base) break; + if (val > (ULLONG_MAX - v) / base) ovfl++; + val = (val * base) + v; + nptr++; + } + if (endptr) { + if (nrstart == nptr) *endptr = (char *)startnptr; + else *endptr = (char *)nptr; + } + + if (!ovfl) { + /* Overflow is only possible when converting a signed long. */ + if (is_signed + && ((sign < 0 && val > -(unsigned long long)LLONG_MIN) + || (sign > 0 && val > LLONG_MAX))) + ovfl++; + } + + if (ovfl) { + errno = ERANGE; + if (is_signed) + if (sign < 0) return LLONG_MIN; + else return LLONG_MAX; + else return ULLONG_MAX; + } + return (long) sign * val; +} + +#endif /* defined(__LONG_LONG_SUPPORTED) */ diff --git a/man/man3/strtol.3 b/man/man3/strtol.3 new file mode 100644 index 000000000..1a4dc272b --- /dev/null +++ b/man/man3/strtol.3 @@ -0,0 +1,33 @@ +.TH STRTOL 3 "December 9, 2009" +.UC 4 +.SH NAME +strtol, strtoll, strtoul, strtoull \- convert string to number +.SH SYNOPSIS +.nf +.ft B +#include + +long strtol(const char *\fInptr\fP, char **\fIendptr\fP, int \fIbase\fP); +unsigned long strtoul(const char *\fInptr\fP, char **\fIendptr\fP, int \fIbase\fP); +#ifdef __LONG_LONG_SUPPORTED +long long strtoll(const char *\fInptr\fP, char **\fIendptr\fP, int \fIbase\fP); +unsigned long long strtoull(const char *\fInptr\fP, char **\fIendptr\fP, int \fIbase\fP); +#endif +.fi +.SH DESCRIPTION +These functions parse as much from the string \fInptr\fP as possible and return +it as an integer. The string should consist of any number of whitespace +characters followed by a sign (either plus or minus) and at least one digit in +the specified \fIbase\fP. The digits of a hexadecimal string may be preceded by +the prefix 0x or 0X, which is ignored. If \fIbase\fP is zero, hexadecimal is +assumed if this prefix is present, octal is assumed if there is a leading zero +and decimal is assumed otherwise. If not zero, \fIbase\fI must be at least 2 +and at most 36. A pointer to the first character following the numeric string is +stored in *\fIendptr\fP. +.PP +Note that the strtoll and strtoull functions, which return 64-bit values, +are supported only on GCC as ACK does not support 64-bit arithmatic. +.SH "RETURN VALUE +The parsed number is returned. +.SH "SEE ALSO" +.BR atoi (3). diff --git a/test/Makefile b/test/Makefile index 8002d590a..9972a7fca 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,19 +1,22 @@ # Makefile for the tests. CC = exec cc +GCC = /usr/gnu/bin/gcc CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +CFLAGS-GCC= $(CFLAGS) -Wall OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \ test10 test12 test13 test14 test15 test16 test17 test18 test19 \ test21 test22 test23 test25 test26 test27 test28 test29 \ test30 test31 test32 test34 test35 test36 test37 test38 \ test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \ - test42 test44 + test42 test44 test45 BIGOBJ= test20 test24 ROOTOBJ= test11 test33 test43 +GCCOBJ= test45-gcc -all: $(OBJ) $(BIGOBJ) $(ROOTOBJ) +all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(ROOTOBJ) chmod 755 *.sh run $(OBJ): @@ -22,6 +25,9 @@ $(OBJ): $(BIGOBJ): $(CC) $(CFLAGS) -o $@ $@.c +$(GCCOBJ): + [ ! -x $(GCC) ] || $(GCC) $(CFLAGS-GCC) -o $@ $< + $(ROOTOBJ): $(CC) $(CFLAGS) $@.c @install -c -o root -m 4755 a.out $@ @@ -29,7 +35,7 @@ $(ROOTOBJ): clean: cd select && make clean - -rm -rf *.o *.s *.bak test? test?? t10a t11a t11b \ + -rm -rf *.o *.s *.bak test? test?? test??-gcc t10a t11a t11b \ t40a t40b t40c t40d t40e t40f t43 DIR* test1: test1.c @@ -85,3 +91,6 @@ test41: test41.c test42: test42.c test43: test43.c test44: test44.c +test45: test45.c test45.h +test45-gcc: test45.c test45.h + diff --git a/test/run b/test/run index c6b33d8b5..9ab891003 100755 --- a/test/run +++ b/test/run @@ -7,6 +7,7 @@ export PATH rm -rf DIR* # remove any old junk lying around passed=`expr 0` # count number of tests run correctly failed=`expr 0` # count number of tests that failed +skipped=`expr 0` # count number of tests that were skipped total=`expr 0` # total number of tests tried badones= # list of tests that failed @@ -18,26 +19,32 @@ echo " " # Run all the tests, keeping track of who failed. for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ - 41 42 43 44 sh1.sh sh2.sh -do total=`expr $total + 1` - FAIL=0 - if [ $USER = root -a \( $i = 11 -o $i = 33 \) ] - then su - ast -c "cd `pwd`; ./test$i" || FAIL=1 - else ./test$i || FAIL=1 - fi - - if [ $FAIL -eq 0 ] - then passed=`expr $passed + 1` - else failed=`expr $failed + 1` - badones=`echo $badones " " $i` + 41 42 43 44 45 45-gcc sh1.sh sh2.sh +do + if [ -x ./test$i ] + then + total=`expr $total + 1` + FAIL=0 + if [ $USER = root -a \( $i = 11 -o $i = 33 \) ] + then su - ast -c "cd `pwd`; ./test$i" || FAIL=1 + else ./test$i || FAIL=1 + fi + if [ $FAIL -eq 0 ] + then passed=`expr $passed + 1` + else failed=`expr $failed + 1` + badones=`echo $badones " " $i` + fi + else + skipped=`expr $skipped + 1` fi done # Print results of the tests. echo " " if test $total = $passed - then echo All $passed tests completed without error. - else echo Testing completed. Score: $passed passed, $failed failed + then echo All $passed tests completed without error \($skipped skipped\). + else echo Testing completed. Score: $passed passed, $failed failed, \ + skipped $skipped echo The following tests failed: $badones fi diff --git a/test/test45.c b/test/test45.c new file mode 100644 index 000000000..c7a2f0837 --- /dev/null +++ b/test/test45.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + +#define MAX_ERROR 4 +static int errct; + +/* test strtol */ +#define TYPE long +#define TYPEU unsigned long +#define TYPE_FUNC strtol +#include "test45.h" +#undef TYPE +#undef TYPEU +#undef TYPE_FUNC + +/* test strtoul */ +#define TYPE unsigned long +#define TYPEU unsigned long +#define TYPE_FUNC strtoul +#include "test45.h" +#undef TYPE +#undef TYPEU +#undef TYPE_FUNC + +#ifdef __LONG_LONG_SUPPORTED + +/* test strtoll */ +#define TYPE long long +#define TYPEU unsigned long long +#define TYPE_FUNC strtoll +#include "test45.h" +#undef TYPE +#undef TYPEU +#undef TYPE_FUNC + +/* test strtoull */ +#define TYPE long long +#define TYPEU unsigned long long +#define TYPE_FUNC strtoull +#include "test45.h" +#undef TYPE +#undef TYPEU +#undef TYPE_FUNC + +#endif /* defined(__LONG_LONG_SUPPORTED) */ + +static void quit(void) +{ + if (errct == 0) + { + printf("ok\n"); + exit(0); + } + else + { + printf("%d errors\n", errct); + exit(1); + } +} + +int main(int argc, char **argv) +{ +#ifdef __LONG_LONG_SUPPORTED + printf("Test 45 (GCC) "); +#else + printf("Test 45 (ACK) "); +#endif + fflush(stdout); + + /* run long/unsigned long tests */ + test_strtol(); + test_strtoul(); + + /* run long long/unsigned long long tests (GCC only) */ +#ifdef __LONG_LONG_SUPPORTED + test_strtoll(); + test_strtoull(); +#endif /* defined(__LONG_LONG_SUPPORTED) */ + + quit(); + return -1; /* never happens */ +} diff --git a/test/test45.h b/test/test45.h new file mode 100644 index 000000000..3e21fb354 --- /dev/null +++ b/test/test45.h @@ -0,0 +1,157 @@ +#define GLUE_HELPER(x, y) x ## _ ## y +#define GLUE(x, y) GLUE_HELPER(x, y) +#define TOSTRING(x) #x + +static const char *GLUE(make_string, TYPE_FUNC)(TYPE value, int base) +{ + static char buffer[66]; + char *s; /* allows 64-bit base 2 value with minus and null */ + TYPEU valuetemp; + + /* build number string in proper base, work backwards, starting with null */ + s = buffer + sizeof(buffer); + *--s = 0; + + /* fill in the digits */ + valuetemp = (value < 0) ? -value : value; + do + { + *--s = "0123456789abcdefghijklmnopqrstuvwxyz"[valuetemp % base]; + valuetemp /= base; + } while (valuetemp); + + /* add sign if needed */ + if (value < 0) + *--s = '-'; + + return s; +} + +static void GLUE(e, TYPE_FUNC)(int n, const char *s, TYPE result, int base) +{ + /* watch out: don't overwrite the static buffer in make_string */ + printf("Subtest %s, error %d, errno=%d, s=\"%s\", base=%d, ", TOSTRING(TYPE_FUNC), n, errno, s, base); + printf("result=%s\n", GLUE(make_string, TYPE_FUNC)(result, base)); + if (errct++ > MAX_ERROR) + { + printf("Too many errors; test aborted\n"); + exit(1); + } +} + +static void GLUE(test_string, TYPE_FUNC)(const char *s, TYPE value, int base) +{ + char *end; + TYPE result; + + /* must convert the entire string, resulting in the requested value */ + result = TYPE_FUNC(s, &end, base); + if (result != value) GLUE(e, TYPE_FUNC)(1, s, result, base); + if (*end) GLUE(e, TYPE_FUNC)(2, s, result, base); +} + +static void GLUE(test_value_with_base, TYPE_FUNC)(TYPE value, int base) +{ + const char *s; + + /* convert to string, then convert back */ + s = GLUE(make_string, TYPE_FUNC)(value, base); + GLUE(test_string, TYPE_FUNC)(s, value, base); +} + +static void GLUE(test_value, TYPE_FUNC)(TYPE value) +{ + int base; + + /* let's get all our bases covered */ + for (base = 2; base <= 36; base++) + GLUE(test_value_with_base, TYPE_FUNC)(value, base); +} + +static void GLUE(test, TYPE_FUNC)(void) +{ + int base, i; + TYPE value, valuenext; + + /* check 0x0000.... and 0xffff.... */ + value = 0; + for (i = 0; i < 0x10000; i++) + { + /* test current value */ + GLUE(test_value, TYPE_FUNC)(value); + GLUE(test_value, TYPE_FUNC)(-value); + value++; + } + + /* check 0x8000.... and 0x7fff.... */ + value = 0; + value = ((~value) << 1) >> 1; + for (i = 0; i < 0x10000; i++) + { + /* test current value */ + GLUE(test_value, TYPE_FUNC)(value); + GLUE(test_value, TYPE_FUNC)(-value); + value++; + } + + /* check powers of possible bases */ + for (base = 2; base <= 36; base++) + { + value = 1; + while (1) + { + /* test current value with offsets */ + for (i = -36; i <= 36; i++) + { + GLUE(test_value, TYPE_FUNC)(value + i); + GLUE(test_value, TYPE_FUNC)(-value + i); + } + + /* stop after overflow */ + valuenext = value * base; + if (valuenext <= value) + break; + + value = valuenext; + } + } + + /* automatic base */ + GLUE(test_string, TYPE_FUNC)("10", 10, 0); + GLUE(test_string, TYPE_FUNC)("010", 010, 0); + GLUE(test_string, TYPE_FUNC)("010", 010, 8); + GLUE(test_string, TYPE_FUNC)("0x10", 0x10, 0); + GLUE(test_string, TYPE_FUNC)("0X10", 0X10, 0); + GLUE(test_string, TYPE_FUNC)("0x10", 0x10, 16); + GLUE(test_string, TYPE_FUNC)("0X10", 0X10, 16); + + /* ignore plus sign, leading spaces and zeroes */ + GLUE(test_string, TYPE_FUNC)("10", 10, 10); + GLUE(test_string, TYPE_FUNC)("010", 10, 10); + GLUE(test_string, TYPE_FUNC)("0010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" 10", 10, 10); + GLUE(test_string, TYPE_FUNC)(" 010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" 0010", 10, 10); + GLUE(test_string, TYPE_FUNC)("\t10", 10, 10); + GLUE(test_string, TYPE_FUNC)("\t010", 10, 10); + GLUE(test_string, TYPE_FUNC)("\t0010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" \t10", 10, 10); + GLUE(test_string, TYPE_FUNC)(" \t010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" \t0010", 10, 10); + GLUE(test_string, TYPE_FUNC)("+10", 10, 10); + GLUE(test_string, TYPE_FUNC)("+010", 10, 10); + GLUE(test_string, TYPE_FUNC)("+0010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" +10", 10, 10); + GLUE(test_string, TYPE_FUNC)(" +010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" +0010", 10, 10); + GLUE(test_string, TYPE_FUNC)("\t+10", 10, 10); + GLUE(test_string, TYPE_FUNC)("\t+010", 10, 10); + GLUE(test_string, TYPE_FUNC)("\t+0010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" \t+10", 10, 10); + GLUE(test_string, TYPE_FUNC)(" \t+010", 10, 10); + GLUE(test_string, TYPE_FUNC)(" \t+0010", 10, 10); +} + +#undef GLUE_HELPER +#undef GLUE +#undef TOSTRING