]> Zhao Yanbai Git Server - minix.git/commitdiff
Implementation of strto(u)ll, documentation and tests for strto(u)l(l)
authorErik van der Kouwe <erik@minix3.org>
Wed, 9 Dec 2009 19:01:38 +0000 (19:01 +0000)
committerErik van der Kouwe <erik@minix3.org>
Wed, 9 Dec 2009 19:01:38 +0000 (19:01 +0000)
include/limits.h
include/stdlib.h
lib/other/Makefile.in
lib/other/strtoll.c [new file with mode: 0644]
man/man3/strtol.3 [new file with mode: 0644]
test/Makefile
test/run
test/test45.c [new file with mode: 0644]
test/test45.h [new file with mode: 0644]

index 5569a14d0d5329e9deb667da636f7d98287bcdbb..37ae59cccb8c798c10916d1ce8884c3bac297e4a 100644 (file)
 #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 <minix/dir.h>
 
 /* Minimum sizes required by the POSIX P1003.1 standard (Table 2-3). */
index 3b3adab961bd84f8aa57b755f6b05c09a4c03977..6dc80237c842bbb1bde6b1d29e152b801f33c027 100644 (file)
@@ -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
index 6f191121bfccc9e33f787fb38a1b9fc3cd831418..bc7bc570e8bc44c6caeb5ac6f57e131e1183905c 100644 (file)
@@ -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 (file)
index 0000000..b679a45
--- /dev/null
@@ -0,0 +1,99 @@
+/* Erik van der Kouwe, 8 December 2009, based on lib/ansi/strtol.c */
+
+#include       <ctype.h>
+#include       <errno.h>
+#include       <limits.h>
+#include       <stdint.h>
+#include       <stdlib.h>
+
+#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 (file)
index 0000000..1a4dc27
--- /dev/null
@@ -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 <stdlib.h>
+
+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).
index 8002d590aac1646b3daf3fde15a5043045bc0de5..9972a7fca1fe393cfcac984bc285608ffcc08184 100644 (file)
@@ -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
+
index c6b33d8b52e619fdcc01885cea8e6739e528d96a..9ab8910030d6755137da2351bdf6036a84b9544a 100755 (executable)
--- 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 (file)
index 0000000..c7a2f08
--- /dev/null
@@ -0,0 +1,85 @@
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..3e21fb3
--- /dev/null
@@ -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