Implementation of strto(u)ll, documentation and tests for strto(u)l(l)

This commit is contained in:
Erik van der Kouwe 2009-12-09 19:01:38 +00:00
parent fcaaad3317
commit 6adadade32
9 changed files with 429 additions and 17 deletions

View file

@ -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). */

View 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

View file

@ -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
View 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
View 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).

View 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

View file

@ -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
View 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
View 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