Implementation of strto(u)ll, documentation and tests for strto(u)l(l)
This commit is contained in:
parent
fcaaad3317
commit
6adadade32
9 changed files with 429 additions and 17 deletions
|
@ -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 <minix/dir.h>
|
||||
|
||||
/* Minimum sizes required by the POSIX P1003.1 standard (Table 2-3). */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -86,6 +86,7 @@ libc_FILES=" \
|
|||
strlcat.c \
|
||||
strlcpy.c \
|
||||
strtok_r.c \
|
||||
strtoll.c \
|
||||
swab.c \
|
||||
sys_eniop.c \
|
||||
syscall.c \
|
||||
|
|
99
lib/other/strtoll.c
Normal file
99
lib/other/strtoll.c
Normal file
|
@ -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) */
|
33
man/man3/strtol.3
Normal file
33
man/man3/strtol.3
Normal file
|
@ -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).
|
|
@ -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
|
||||
|
||||
|
|
35
test/run
35
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
|
||||
|
||||
|
|
85
test/test45.c
Normal file
85
test/test45.c
Normal file
|
@ -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 */
|
||||
}
|
157
test/test45.h
Normal file
157
test/test45.h
Normal file
|
@ -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
|
Loading…
Reference in a new issue