Browse Source

A number of updates from Manuel Novoa III. Things look good...

Eric Andersen 23 years ago
parent
commit
c6218dbae5

+ 6 - 0
include/math.h

@@ -28,6 +28,11 @@ typedef struct {
 #define M_SQRT1_2	0.70710678118654752440	/* 1/sqrt(2) */
 
 
+#if 1
+extern double sin(double);
+extern double cos(double);
+extern double sqrt(double);
+#else
 extern float acos ( float x );
 extern float acosh ( float xx );
 extern int airy ( float xx, float *ai, float *aip, float *bi, float *bip );
@@ -168,3 +173,4 @@ extern float yn ( int nn, float xx );
 extern float yv ( float vv, float xx );
 extern float zetac ( float xx );
 extern float zeta ( float xx, float qq );
+#endif

+ 2 - 2
include/stdlib.h

@@ -46,8 +46,8 @@ typedef __compar_fn_t comparison_fn_t;
 extern long strtol __P ((const char * nptr, char ** endptr, int base));
 extern unsigned long strtoul __P ((const char * nptr, char ** endptr, int base));
 #ifndef __HAS_NO_FLOATS__
-extern char * gcvt __P ((float number, size_t ndigit, char * buf));
-extern float strtod __P ((const char * nptr, char ** endptr));
+/*TODO: extern char * gcvt __P ((double number, size_t ndigit, char * buf)); */
+extern double strtod __P ((const char * nptr, char ** endptr));
 #endif
 
 

+ 33 - 11
libc/inet/addr.c

@@ -3,6 +3,15 @@
  * under the GNU Library General Public License.
  */
 
+/*
+ * Manuel Novoa III       Dec 2000
+ *
+ * Converted to use my new (un)signed long (long) to string routines, which
+ * are smaller than the previous functions and don't require static buffers.
+ * In the process, removed the reference to strcat and cut object size of
+ * inet_ntoa in half (from 190 bytes down to 94).
+ */
+
 #include <string.h>
 #include <ctype.h>
 #include <netinet/in.h>
@@ -63,22 +72,35 @@ const char *cp;
 
 #ifdef L_inet_ntoa
 
-extern char *itoa(int);
+#include <limits.h>
+
+#if (ULONG_MAX >> 32)
+/* We're set up for 32 bit unsigned longs */
+#error need to check size allocation for static buffer 'buf'
+#endif
+
+extern char *__ultostr(char *buf, unsigned long uval, int base, int uppercase);
 
 char *inet_ntoa(in)
 struct in_addr in;
 {
-	static char buf[18];
-	unsigned long addr = ntohl(in.s_addr);
+	static char buf[16];		/* max 12 digits + 3 '.'s + 1 nul */
 
-	strcpy(buf, itoa((addr >> 24) & 0xff));
-	strcat(buf, ".");
-	strcat(buf, itoa((addr >> 16) & 0xff));
-	strcat(buf, ".");
-	strcat(buf, itoa((addr >> 8) & 0xff));
-	strcat(buf, ".");
-	strcat(buf, itoa(addr & 0xff));
+	unsigned long addr = ntohl(in.s_addr);
+	int i;
+	char *p, *q;
+   
+	q = 0;
+	p = buf + sizeof(buf) - 1;
+	for (i=0 ; i < 4 ; i++ ) {
+		p = __ultostr(p, addr & 0xff, 10, 0 ) - 1;
+		addr >>= 8;
+		if (q) {
+			*q = '.';
+		}
+		q = p;
+	}
 
-	return buf;
+	return p+1;
 }
 #endif

+ 17 - 2
libc/misc/assert/__assert.c

@@ -3,11 +3,24 @@
  * under the GNU Library General Public License.
  */
 
+/*
+ * Manuel Novoa III       Dec 2000
+ *
+ * Converted to use my new (un)signed long (long) to string routines, which
+ * are smaller than the previous functions and don't require static buffers.
+ */
+
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
+#include <limits.h>
 
-extern char *itoa(int);
+#if (INT_MAX >> 31)
+/* We're set up for 32 bit ints */
+#error need to check size allocation for buffer 'buf'
+#endif
+
+extern char *__ltostr(char *buf, unsigned long uval, int base, int uppercase);
 
 static void errput(str)
 const char *str;
@@ -21,9 +34,11 @@ const char *filename;
 int linenumber;
 const char *function;
 {
+	char buf[12];
+
 	errput(filename);
 	errput(":");
-	errput(itoa(linenumber));
+	errput(__ltostr(buf + sizeof(buf) - 1, linenumber, 10, 0));
 	errput(function ? ": " : "");
 	errput(function ? function : "");
 	errput(function ? "() " : "");

+ 34 - 1
libc/misc/ctype/ctype.c

@@ -9,106 +9,139 @@
 #define USE_CTYPE_C_FUNCTIONS
 #include <ctype.h>
 
+#ifdef L_isalnum
 int
 isalnum( int c )
 {
     return (isalpha(c) || isdigit(c));
 }
+#endif
 
+#ifdef L_isalpha
 int
 isalpha( int c )
 {
     return (isupper(c) || islower(c));
 }
+#endif
 
+#ifdef L_isascii
 int
 isascii( int c )
 {
     return (c > 0 && c <= 0x7f);
 }
+#endif
 
+#ifdef L_iscntrl
 int
 iscntrl( int c )
 {
     return ((c > 0) && ((c <= 0x1f) || (c == 0x7f)));
 }
+#endif
 
+#ifdef L_isdigit
 int
 isdigit( int c )
 {
     return (c >= '0' && c <= '9');
 }
+#endif
 
+#ifdef L_isgraph
 int
 isgraph( int c )
 {
     return (c != ' ' && isprint(c));
 }
+#endif
 
+#ifdef L_islower
 int
 islower( int c )
 {
     return (c >=  'a' && c <= 'z');
 }
+#endif
 
+#ifdef L_isprint
 int
 isprint( int c )
 {
     return (c >= ' ' && c <= '~');
 }
+#endif
 
+#ifdef L_ispunct
 int
 ispunct( int c )
 {
     return ((c > ' ' && c <= '~') && !isalnum(c));
 }
+#endif
 
+#ifdef L_isspace
 int
 isspace( int c )
 {
     return (c == ' ' || c == '\f' || c == '\n' || c == '\r' ||
 	    c == '\t' || c == '\v');
 }
+#endif
 
+#ifdef L_isupper
 int
 isupper( int c )
 {
     return (c >=  'A' && c <= 'Z');
 }
+#endif
 
+#ifdef L_isxdigit
 int
 isxdigit( int c )
 {
     return (isxupper(c) || isxlower(c));
 }
+#endif
 
+#ifdef L_isxlower
 int
 isxlower( int c )
 {
     return (isdigit(c) || (c >= 'a' && c <= 'f'));
 }
+#endif
 
+#ifdef L_isxupper
 int
 isxupper( int c )
 {
     return (isdigit(c) || (c >= 'A' && c <= 'F'));
 }
+#endif
 
+#ifdef L_toascii
 int
 toascii( int c )
 {
     return (c & 0x7f);
 }
+#endif
 
+#ifdef L_tolower
 int
 tolower( int c )
 {
     return (isupper(c) ? ( c - 'A' + 'a') : (c));
 }
+#endif
 
+#ifdef L_toupper
 int
 toupper( int c )
 {
     return (islower(c) ? (c - 'a' + 'A') : (c));
 }
-
+#endif

+ 1 - 1
libc/misc/internals/Makefile

@@ -24,7 +24,7 @@ TOPDIR=../../
 include $(TOPDIR)Rules.mak
 LIBC=$(TOPDIR)libc.a
 
-CSRC=itoa.c ltoa.c ltostr.c
+CSRC=ultostr.c ltostr.c ulltostr.c lltostr.c zoicheck.c dtostr.c
 COBJS=$(patsubst %.c,%.o, $(CSRC))
 OBJS=$(COBJS)
 

+ 271 - 0
libc/misc/internals/dtostr.c

@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Function:  const char *__dtostr(double x)
+ *
+ * This was written for uClibc to give it the ability to at least output
+ * floating point values in exponential form.  No formatting is done.
+ * No trailing 0's are removed.  Number of digits generated is not
+ * runtime selectable.  It does however handle +/- infinity and nan on i386.
+ *
+ * The goal was usable floating point %e-type output in minimal size.  For
+ * me, "gcc -c -Os -fomit-frame-pointer dtostr.c && size dtostr.o" gives
+ *   text    data     bss    dec     hex filename
+ *   535       9      26     570     23a dtostr.o    (WANT_EXP_FORM = 1)
+ *   530       9      26     565     235 dtostr.o    (WANT_EXP_FORM = 0)
+ *
+ * Output is of the form [-][#].######{e|E}{+|-}##.  Choices of upper or
+ * lower case exponent character, and initial digit/initial decimal forms
+ * are compile-time options.  Number of digits generated is also selected
+ * at compile time (MAX_DIGITS).
+ *
+ * Notes:
+ *
+ * The primary objective of this implementation was minimal size while
+ * maintaining reasonable accuracy.  It should also be fairly portable,
+ * as not assumptions are made about the bit-layout of doubles.
+ *
+ * It should be too difficult to convert this to handle long doubles on i386.
+ * For information, see the comments below.
+ *
+ * There are 2 compile-time options below, as well as some tuning parameters.
+ *
+ * TODO: 
+ *   long double and/or float version?  (note: for float can trim code some).
+ */
+
+/*****************************************************************************/
+/*                            OPTIONS                                        */
+/*****************************************************************************/
+
+/*
+ * Set this if you want output results with 1 digit before the decimal point.
+ * If this is 0, all digits follow a leading decimal point.
+ */
+#define WANT_EXP_FORM   1
+
+/*
+ * Set if you want exponent character 'E' rather than 'e'.
+ */
+#define EXP_UPPERCASE   1
+
+/*****************************************************************************/
+/* Don't change anything that follows unless you know what you're doing.     */
+/*****************************************************************************/
+
+/*
+ * Configuration for the scaling power table.  Ignoring denormals, you
+ * should have 2**EXP_TABLE_SIZE >= MAX_DBL_EXP >= 2**(EXP_TABLE_SIZE-1).
+ * The minimum for standard C is 6.  For IEEE 8bit doubles, 9 suffices.
+ */
+#define EXP_TABLE_SIZE       9
+
+/* 
+ * Set this to the maximum number of digits you want converted.
+ * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits.
+ * 17 digits suffices to uniquely determine a double on i386.
+ */
+#define MAX_DIGITS          17
+
+/*
+ * This is really only used to check for infinities.  The macro produces
+ * smaller code for i386 and, since this is tested before any floating point
+ * calculations, it doesn't appear to suffer from the excess precision problem
+ * caused by the FPU that strtod had.  If it causes problems, call the function
+ * and compile zoicheck.c with -ffloat-store.
+ */
+#if 1
+#define _zero_or_inf_check(x) ( x == (x/4) )
+#else
+extern int _zero_or_inf_check(double x);
+#endif
+
+/*
+ * Fairly portable nan check.  Bitwise for i386 generated larger code.
+ * If you have a better version, comment this out.
+ */
+#define isnan(x) (x != x)
+
+/*****************************************************************************/
+/* Don't change anything that follows peroid!!!  ;-)                         */
+/*****************************************************************************/
+
+#include <float.h>
+#include <limits.h>
+
+/*
+ * Set things up for the scaling power table.
+ */
+
+#if EXP_TABLE_SIZE < 6
+#error EXP_TABLE_SIZE should be at least 6 to comply with standards
+#endif
+
+#define EXP_TABLE_MAX      (1U<<(EXP_TABLE_SIZE-1))
+
+/*
+ * Only bother checking if this is too small.
+ * Throw in some play for denormals ( roughly O(-324) vs O(-307) on i386 ).
+ */
+
+#if (3+DBL_DIG-DBL_MIN_10_EXP)/2 > EXP_TABLE_MAX
+#error larger EXP_TABLE_SIZE needed
+#endif
+
+/*
+ * With 32 bit ints, we can get 9 digits per block.
+ */
+#define DIGITS_PER_BLOCK     9
+
+#if (INT_MAX >> 30)
+#define DIGIT_BLOCK_TYPE     int
+#elif (LONG_MAX >> 30)
+#define DIGIT_BLOCK_TYPE     long
+#else
+#error need at least 32 bit longs
+#endif
+
+/*
+ * This is kind of a place-holder for LONG_DOUBLE support to show what I
+ * think needs to be changed.  I haven't tried it though.  Changing this
+ * from 3 to 4 and converting double to long double should work on i386.
+ * DON'T FORGET to increase EXP_TABLE_SIZE and MAX_DIGITS.
+ * DON'T FORGET the "larger EXP_TABLE_SIZE needed" check above.
+ */
+#define MAX_EXP_DIGITS 3
+
+/*****************************************************************************/
+
+#define NUM_DIGIT_BLOCKS   ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
+
+static char infstr[] = " inf";	/* save space for a - sign */
+static char nanstr[] = "nan";
+
+/* extra space for '-', '.', 'e+###', and nul */
+/*static char buf[ 5 + MAX_EXP_DIGITS + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK];*/
+#define BUF_SIZE  ( 5 + MAX_EXP_DIGITS + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
+/*****************************************************************************/
+
+const char *__dtostr(char *buf, double x)
+{
+	double exp_table[EXP_TABLE_SIZE];
+	double p10;
+	DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */
+	int i, j;
+	int exp, exp_neg;
+	int negative = 0;
+	char *pos;
+
+	if (isnan(x)) {				/* nan check */
+		return nanstr;
+	}
+
+	if (x == 0) {				/* handle 0 now to avoid false positive */
+		exp = 0;				/* with inf test, and to avoid scaling  */
+		goto GENERATE_DIGITS;	/* note: time vs space tradeoff */
+	}
+
+	if (x < 0) {				/* convert negatives to positives */
+		negative = 1;
+		x = -x;
+	}
+
+	if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */
+		pos = infstr + 1;
+		goto DO_SIGN;
+	}
+
+	/* need to build the scaling table */
+	for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) {
+		exp_table[i] = p10;
+		p10 *= p10;
+	}
+
+	exp_neg = 0;
+	if (x < 1e8) {				/* do we need to scale up or down? */
+		exp_neg = 1;
+	}
+
+#if WANT_EXP_FORM
+	exp = DIGITS_PER_BLOCK - 1;
+#else
+	exp = DIGITS_PER_BLOCK;
+#endif
+
+	i = EXP_TABLE_SIZE;
+	j = EXP_TABLE_MAX;
+	while ( i-- ) {				/* scale x such that 1e8 <= x < 1e9 */
+		if (exp_neg) {
+			if (x * exp_table[i] < 1e9) {
+				x *= exp_table[i];
+				exp -= j;
+			}
+		} else {
+			if (x / exp_table[i] >= 1e8) {
+				x /= exp_table[i];
+				exp += j;
+			}
+		}
+		j >>= 1;
+	}
+
+ GENERATE_DIGITS:
+	pos = buf - BUF_SIZE + 1 + DIGITS_PER_BLOCK + 1; /* leave space for '.' and - */
+
+	for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) {
+		digit_block = (int) x;
+		x = (x - digit_block) * 1e9;
+		for (j = 0 ; j < DIGITS_PER_BLOCK ; j++) {
+			*--pos = '0' + (digit_block % 10);
+			digit_block /= 10;
+		}
+		pos += (2*DIGITS_PER_BLOCK);
+	}
+	pos -= (DIGITS_PER_BLOCK*(NUM_DIGIT_BLOCKS+1))-MAX_DIGITS;
+
+	/* start generating the exponent */
+#if EXP_UPPERCASE
+	*pos = 'E';
+#else
+	*pos = 'e';
+#endif
+	*++pos = '+';
+	if (exp < 0) {
+		*pos = '-';
+		exp = -exp;
+	}
+	pos += 3;					/* WARNING: Assumes max exp < 1000!!! */
+	if (exp >= 100) {
+		++pos;
+#if MAX_EXP_DIGITS > 4
+#error need to modify exponent string generation code
+#elif MAX_EXP_DIGITS > 3
+		if (exp >= 1000) {		/* WARNING: hasn't been checked */
+			++pos;				/*    but should work */
+		}
+#endif
+	}
+	*pos = '\0';
+		
+	for (j = 0 ; (j < 2) || exp ; j++) { /* standard says at least 2 digits */
+		*--pos = '0' + (exp % 10);
+		exp /= 10;
+	}
+
+	/* insert the decimal point */
+	pos = buf - BUF_SIZE + 1;
+
+#if WANT_EXP_FORM
+	*pos = *(pos+1);
+	*(pos+1) = '.';
+#else
+	*pos = '.';
+#endif
+
+ DO_SIGN:
+	if (negative) {
+		*--pos = '-';
+	}
+
+	return pos;
+}

+ 0 - 21
libc/misc/internals/itoa.c

@@ -1,21 +0,0 @@
-/* itoa.c <ndf@linux.mit.edu> */
-#define __MAX_INT_CHARS 7
-
-char *itoa(int i)
-{
-	static char a[__MAX_INT_CHARS];
-	char *b = a + sizeof(a) - 1;
-	int sign = (i < 0);
-
-	if (sign)
-		i = -i;
-	*b = 0;
-	do {
-		*--b = '0' + (i % 10);
-		i /= 10;
-	}
-	while (i);
-	if (sign)
-		*--b = '-';
-	return b;
-}

+ 39 - 0
libc/misc/internals/lltostr.c

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Note: buf is a pointer to the END of the buffer passed.
+ * Call like this:
+ *     char buf[SIZE], *p;
+ *     p = __lltostr(buf + sizeof(buf) - 1, ...)
+ * For long longs of 64 bits, appropriate buffer sizes are:
+ *     base =  2      66  = 1 (possible -) sign + 64 digits + 1 nul
+ *     base = 10      21  = 1 (possible -) sign + 19 digits + 1 nul
+ *     base = 16      18  = 1 (possible -) sign + 16 hex digits + 1 nul
+ */
+
+extern char *__ulltostr(char *buf, unsigned long long uval, int base, 
+						int uppercase);
+
+char *__lltostr(char *buf, long long val, int base, int uppercase)
+{
+	unsigned long long uval;
+	char *pos;
+    int negative;
+
+	negative = 0;
+    if (val < 0) {
+		negative = 1;
+		uval = ((unsigned long long)(-(1+val))) + 1;
+    } else {
+		uval = val;
+	}
+
+
+    pos = __ulltostr(buf, uval, base, uppercase);
+
+    if (pos && negative) {
+		*--pos = '-';
+    }
+
+    return pos;
+}

+ 0 - 40
libc/misc/internals/ltoa.c

@@ -1,40 +0,0 @@
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
- * This file is part of the Linux-8086 C library and is distributed
- * under the GNU Library General Public License.
- */
-
-static char buf[12];
-
-extern char *ultoa();
-
-char *ltoa(val)
-long val;
-{
-	char *p;
-	int flg = 0;
-
-	if (val < 0) {
-		flg++;
-		val = -val;
-	}
-	p = ultoa(val);
-	if (flg)
-		*--p = '-';
-	return p;
-}
-
-char *ultoa(val)
-unsigned long val;
-{
-	char *p;
-
-	p = buf + sizeof(buf);
-	*--p = '\0';
-
-	do {
-		*--p = '0' + val % 10;
-		val /= 10;
-	}
-	while (val);
-	return p;
-}

+ 29 - 42
libc/misc/internals/ltostr.c

@@ -1,52 +1,39 @@
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
- * This file is part of the Linux-8086 C library and is distributed
- * under the GNU Library General Public License.
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Note: buf is a pointer to the END of the buffer passed.
+ * Call like this:
+ *     char buf[SIZE], *p;
+ *     p = __ltostr(buf + sizeof(buf) - 1, ...)
+ *
+ * For longs of 32 bits, appropriate buffer sizes are:
+ *     base =  2      34  = 1 (possible -) sign + 32 digits + 1 nul
+ *     base = 10      12  = 1 (possible -) sign + 10 digits + 1 nul
+ *     base = 16      10  = 1 (possible -) sign + 8 hex digits + 1 nul
  */
 
-static char buf[34];
+extern char *__ultostr(char *buf, unsigned long uval, int base, int uppercase);
 
-extern char *ultostr();
-
-char *ltostr(val, radix, uppercase)
-long val;
-int radix;
-int uppercase;
+char *__ltostr(char *buf, long val, int base, int uppercase)
 {
-	char *p;
-	int flg = 0;
-
-	if (val < 0) {
-		flg++;
-		val = -val;
+	unsigned long uval;
+	char *pos;
+    int negative;
+
+	negative = 0;
+    if (val < 0) {
+		negative = 1;
+		uval = ((unsigned long)(-(1+val))) + 1;
+    } else {
+		uval = val;
 	}
-	p = ultostr(val, radix, uppercase);
-	if (p && flg)
-		*--p = '-';
-	return p;
-}
 
-char *ultostr(val, radix, uppercase)
-unsigned long val;
-int radix;
-int uppercase;
-{
-	register char *p;
-	register int c;
 
-	if (radix > 36 || radix < 2)
-		return 0;
+    pos = __ultostr(buf, uval, base, uppercase);
 
-	p = buf + sizeof(buf);
-	*--p = '\0';
+    if (pos && negative) {
+		*--pos = '-';
+    }
 
-	do {
-		c = val % radix;
-		val /= radix;
-		if (c > 9)
-			*--p = (uppercase ? 'A' : 'a') - 10 + c;
-		else
-			*--p = '0' + c;
-	}
-	while (val);
-	return p;
+    return pos;
 }

+ 37 - 0
libc/misc/internals/ulltostr.c

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Note: buf is a pointer to the END of the buffer passed.
+ * Call like this:
+ *     char buf[SIZE], *p;
+ *     p = __ulltostr(buf + sizeof(buf) - 1, ...)
+ *
+ * For long longs of 64 bits, appropriate buffer sizes are:
+ *     base =  2      65  = 64 digits + 1 nul
+ *     base = 10      20  = 19 digits + 1 nul
+ *     base = 16      17  = 16 hex digits + 1 nul
+ */
+
+char *__ulltostr(char *buf, unsigned long long uval, int base, int uppercase)
+{
+    int digit;
+
+    if ((base < 2) || (base > 36)) {
+		return 0;
+    }
+
+    *buf = '\0';
+
+    do {
+		digit = uval % base;
+		uval /= base;
+
+		/* note: slightly slower but generates less code */
+		*--buf = '0' + digit;
+		if (digit > 9) {
+			*buf = (uppercase ? 'A' : 'a') + digit - 10;
+		}
+    } while (uval);
+
+    return buf;
+}

+ 37 - 0
libc/misc/internals/ultostr.c

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Note: buf is a pointer to the END of the buffer passed.
+ * Call like this;
+ *     char buf[SIZE], *p;
+ *     p = __ultostr(buf + sizeof(buf) - 1, ...)
+ *
+ * For longs of 32 bits, appropriate buffer sizes are:
+ *     base =  2      33  = 32 digits + 1 nul
+ *     base = 10      11  = 10 digits + 1 nul
+ *     base = 16       9  = 8 hex digits + 1 nul
+ */
+
+char *__ultostr(char *buf, unsigned long uval, int base, int uppercase)
+{
+    int digit;
+
+    if ((base < 2) || (base > 36)) {
+		return 0;
+    }
+
+    *buf = '\0';
+
+    do {
+		digit = uval % base;
+		uval /= base;
+
+		/* note: slightly slower but generates less code */
+		*--buf = '0' + digit;
+		if (digit > 9) {
+			*buf = (uppercase ? 'A' : 'a') + digit - 10;
+		}
+    } while (uval);
+
+    return buf;
+}

+ 16 - 0
libc/misc/internals/zoicheck.c

@@ -0,0 +1,16 @@
+
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * This is a utility routine for strtod errno support.
+ * As the name implies, it checks if a double is either 0 or +/-infinity.
+ * Doing this inline doesn't work on i386 because of excess precission
+ * stored in the FPU.
+ *
+ * TODO: Check bitmasks directly?
+ */
+
+int _zero_or_inf_check(double x)
+{
+	return ( x == x/4 );
+}

+ 398 - 220
libc/stdio/printf.c

@@ -17,11 +17,92 @@
  * -RDB
  */
 
+/*
+ *                    Manuel Novoa III   Dec 2000
+ *
+ * The previous vfprintf routine was almost completely rewritten with the
+ * goal of fixing some shortcomings and reducing object size.
+ *
+ * The summary of changes:
+ *
+ * Converted print conversion specification parsing from one big switch
+ *   to a method using string tables.  This new method verifies that the
+ *   conversion flags, field width, precision, qualifier, and specifier
+ *   appear in the correct order.  Many questionable specifications were
+ *   accepted by the previous code.  This new method also resulted in a
+ *   substantial reduction in object size of about 330 bytes (20%) from
+ *   the old version (1627 bytes) on i386, even with the following
+ *   improvements.
+ *
+ *     Implemented %n specifier as required by the standards.
+ *     Implemented proper handling of precision for int types.
+ *     Implemented # for hex and pointer, fixed error for octal rep of 0.
+ *     Implemented return of -1 on stream error.
+ *
+ * Added optional support for the GNU extension %m which prints the string
+ *   corresponding the errno.
+ *
+ * Added optional support for long long ints and unsigned long long ints
+ *   using the conversion qualifiers "ll", "L", or "q" (like glibc).
+ *
+ * Added optional support for doubles in a very limited form.  None of
+ *   the formating options are obeyed.  The string returned by __dtostr
+ *   is printed directly.
+ *
+ * Converted to use my (un)signed long (long) to string routines, which are
+ * smaller than the previous functions and don't require static buffers.
+ *
+ */
+
+/*****************************************************************************/
+/*                            OPTIONS                                        */
+/*****************************************************************************/
+/* The optional support for long longs and doubles comes in two forms.
+ *
+ *   1) Normal (or partial for doubles) output support.  Set to 1 to turn on.
+ *      Adds about 54 byes and about 217 bytes for long longss to the base size
+ *      of 1298.  (Bizarre: both turned on is smaller than WANT_LONG_LONG only.)
+ */
+
+#define WANT_LONG_LONG         0
+#define WANT_DOUBLE            0
+
+/*   2) An error message is inserted into the stream, an arg of the
+ *      appropriate size is removed from the arglist, and processing
+ *      continues.  This is adds less code and may be useful in some
+ *      cases.  Set to 1 to turn on.  Adds about 31 bytes for doubles
+ *      and about 54 bytes for long longs to the base size of 1298.
+ */
+
+#define WANT_LONG_LONG_ERROR   0
+#define WANT_DOUBLE_ERROR      0
+
+/*
+ * Set to support GNU extension of %m to print string corresponding to errno.
+ *
+ * Warning: This adds about 50 bytes (i386) to the code but it also pulls in
+ * strerror and the corresponding string table which together are about 3.8k.
+ */
+
+#define WANT_GNU_ERRNO         0
+
+/*
+ * Use fputc instead of macro putc.  Slower but saves about 36 bytes.
+ */
+
+#define WANT_FPUTC             0
+
+/**************************************************************************/
+
 #include <sys/types.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdlib.h>
 
+#if WANT_GNU_ERRNO
+#include <errno.h>
+#endif
+
 #ifdef __STDC__
 #include <stdarg.h>
 #define va_strt      va_start
@@ -32,14 +113,16 @@
 
 #include "stdio.h"
 
+#if WANT_FPUTC
+#undef putc
+#define putc(c,s) fputc(c,s)
+#endif
 
 
 extern int vfnprintf(FILE * op, size_t max_size,
 					 register __const char *fmt, register va_list ap);
 
 
-
-
 #ifdef L_printf
 int printf(const char *fmt, ...)
 {
@@ -150,93 +233,64 @@ int vsnprintf(char *sp, size_t size, __const char *fmt, va_list ap)
 
 #ifdef L_vfprintf
 
-#if FLOATS
-int _vfprintf_fp_ref = 1;
+extern char *__ultostr(char *buf, unsigned long uval, int base, int uppercase);
+extern char *__ltostr(char *buf, long val, int base, int uppercase);
+extern char *__ulltostr(char *buf, unsigned long long uval, int base, int uppercase);
+extern char *__lltostr(char *buf, long long val, int base, int uppercase);
+extern char *__dtostr(char *buf, double x);
+
+enum {
+	FLAG_PLUS = 0,
+	FLAG_MINUS_LJUSTIFY,
+	FLAG_HASH,
+	FLAG_0_PAD,
+	FLAG_SPACE,
+};
+
+/* layout                   01234  */
+static const char spec[] = "+-#0 ";
+
+#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR
+static const char qual[] = "hlLq";
 #else
-int _vfprintf_fp_ref = 0;
+static const char qual[] = "hl";
 #endif
 
-static int
-printfield(op, buf, ljustf, sign, pad, width, preci, buffer_mode, max_size,
-		   current_size)
-register FILE *op;
-register unsigned char *buf;
-int ljustf;
-register char sign;
-char pad;
-register int width;
-int preci;
-int buffer_mode;
-size_t max_size;
-size_t current_size;
-
-/*
- * Output the given field in the manner specified by the arguments. Return
- * the number of characters output.
- */
-{
-	register int cnt = 0, len;
-	register unsigned char ch;
-
-	len = strlen(buf);
-
-	if (*buf == '-')
-		sign = *buf++;
-	else if (sign)
-		len++;
-
-	if ((preci != -1) && (len > preci))	/* limit max data width */
-		len = preci;
-
-	if (width < len)			/* flexible field width or width overflow */
-		width = len;
-
-	/*
-	 * at this point: width = total field width len   = actual data width
-	 * (including possible sign character)
-	 */
-	cnt = width;
-	width -= len;
-
-	while (width || len) {
-		if (!ljustf && width) {	/* left padding */
-			if (len && sign && (pad == '0'))
-				goto showsign;
-			ch = pad;
-			--width;
-		} else if (len) {
-			if (sign) {
-			  showsign:ch = sign;
-								/* sign */
-				sign = '\0';
-			} else
-				ch = *buf++;	/* main field */
-			--len;
-		} else {
-			ch = pad;			/* right padding */
-			--width;
-		}
-		current_size++;
-		if (max_size > 0 && current_size < max_size)
-			putc(ch, op);
-		if (ch == '\n' && buffer_mode == _IOLBF)
-			fflush(op);
-	}
+#if !WANT_LONG_LONG && WANT_LONG_LONG_ERROR
+static const char ll_err[] = "<LONG-LONG>";
+#endif
 
-	return (cnt);
-}
+#if !WANT_DOUBLE && WANT_DOUBLE_ERROR
+static const char dbl_err[] = "<DOUBLE>";
+#endif
 
+#if WANT_DOUBLE || WANT_DOUBLE_ERROR
+/* layout                     012345678901234567   */
+static const char u_spec[] = "%nbopxXudicsfgGeEaA";
+#else
+/* layout                     0123456789012   */
+static const char u_spec[] = "%nbopxXudics0";
+#endif
 
+/* WARNING: u_spec and u_radix need to stay in agreement!!! */
+/* u_radix[i] <-> u_spec[i+2] for unsigned entries only */
+static const char u_radix[] = "\x02\x08\x10\x10\x10\x0a";
 
-int vfnprintf(FILE * op, size_t max_size, register __const char *fmt,
-			  register va_list ap)
+int vfnprintf(FILE * op, size_t max_size, const char *fmt, va_list ap)
 {
-	register int i, cnt = 0, ljustf, lval;
-	int preci, dpoint, width;
-	char pad, sign, radix, hash;
-	register char *ptmp;
-	char tmp[64], *ltostr(), *ultostr();
+	int i, cnt = 0, lval;
+	char *p;
+	const char *fmt0;
 	int buffer_mode;
+	int preci, width;
+#define upcase i
+	int radix, dpoint /*, upcase*/;
+#if WANT_LONG_LONG
+	char tmp[65];
+#else
+	char tmp[33];
+#endif
+	char flag[sizeof(spec)];
 
 	/* This speeds things up a bit for unbuffered */
 	buffer_mode = (op->mode & __MODE_BUF);
@@ -244,172 +298,296 @@ int vfnprintf(FILE * op, size_t max_size, register __const char *fmt,
 
 	while (*fmt) {
 		if (*fmt == '%') {
-			if (buffer_mode == _IONBF)
+			fmt0 = fmt;			/* save our position in case of bad format */
+			++fmt;
+			if (buffer_mode == _IONBF) {
 				fflush(op);
-			ljustf = 0;			/* left justify flag */
-			sign = '\0';		/* sign char & status */
-			pad = ' ';			/* justification padding char */
+			}
 			width = -1;			/* min field width */
-			dpoint = 0;			/* found decimal point */
-			preci = -1;			/* max data width */
+			preci = -5;			/* max string width or mininum digits */
 			radix = 10;			/* number base */
-			ptmp = tmp;			/* pointer to area to print */
-			hash = 0;
+			dpoint = 0;			/* found decimal point */
 			lval = (sizeof(int) == sizeof(long));	/* long value flaged */
 
-		  fmtnxt:
-			i = 0;
-			for (;;) {
-				++fmt;
-				if (*fmt < '0' || *fmt > '9')
-					break;
-				i = (i * 10) + (*fmt - '0');
-				if (dpoint)
-					preci = i;
-				else if (!i && (pad == ' ')) {
-					pad = '0';
-					goto fmtnxt;
-				} else
-					width = i;
-			}
-
-			switch (*fmt) {
-			case '\0':			/* early EOS */
-				--fmt;
-				goto charout;
+			tmp[1] = 0;			/* set things up for %c -- better done here */
 
-			case '-':			/* left justification */
-				ljustf = 1;
-				goto fmtnxt;
+			/* init flags */
+			for (p =(char *) spec ; *p ; p++) {
+				flag[p-spec] = '\0';
+			}
+			flag[FLAG_0_PAD] = ' ';
 
-			case ' ':
-			case '+':			/* leading sign flag */
-				sign = *fmt;
-				goto fmtnxt;
+			/* process optional flags */
+			for (p = (char *)spec ; *p ; p++) {
+				if (*fmt == *p) {
+					flag[p-spec] = *fmt++;
+					p = (char *)spec; /* restart scan */
+				}
+			}
+			
+			if (!flag[FLAG_PLUS]) {
+				flag[FLAG_PLUS] = flag[FLAG_SPACE];
+			}
 
-			case '*':			/* parameter width value */
-				i = va_arg(ap, int);
+			/* process optional width and precision */
+			do {
+				if (*fmt == '.') {
+					++fmt;
+					dpoint = 1;
+				}
+				if (*fmt == '*') {	/* parameter width value */
+					++fmt;
+					i = va_arg(ap, int);
+				} else {
+					for ( i = 0 ; (*fmt >= '0') && (*fmt <= '9') ; ++fmt ) {
+						i = (i * 10) + (*fmt - '0');
+					}
+				}
 
-				if (dpoint)
+				if (dpoint) {
 					preci = i;
-				else
+					if (i<0) {
+						preci = 0;
+					}
+				} else {
 					width = i;
-				goto fmtnxt;
-
-			case '.':			/* secondary width field */
-				dpoint = 1;
-				goto fmtnxt;
-
-			case 'l':			/* long data */
-				lval = 1;
-				goto fmtnxt;
+					if (i<0) {
+						width = -i;
+						flag[FLAG_MINUS_LJUSTIFY] = 1;
+					}
+				}
+			} while ((*fmt == '.') && !dpoint );
+
+			/* process optional qualifier */
+			for (p = (char *) qual ; *p ; p++) {
+				if (*p == *fmt) {
+					lval = p - qual;
+					++fmt;
+#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR
+					if ((*p == 'l') && (*fmt == *p)) {
+						++lval;
+						++fmt;
+					}
+#endif /* WANT_LONG_LONG || WANT_LONG_LONG_ERROR */
+				}
+			}
 
-			case 'h':			/* short data */
-				lval = 0;
-				goto fmtnxt;
+#if WANT_GNU_ERRNO
+			if (*fmt == 'm') {
+				flag[FLAG_PLUS] = '\0';
+				flag[FLAG_0_PAD] = ' ';
+				p = strerror(errno);
+				goto print;
+			}
+#endif
 
-			case 'd':			/* Signed decimal */
-			case 'i':
-				ptmp = ltostr((long) ((lval)
+			/* process format specifier */
+			for (p = (char *) u_spec ; *p ; p++) {
+				if (*fmt != *p) continue;
+				if (p-u_spec < 1) {	/* print a % */
+					goto charout;
+				}
+				if (p-u_spec < 2) {	/* store output count in int ptr */
+					*(va_arg(ap, int *)) = cnt;
+					goto nextfmt;
+				}
+				if (p-u_spec < 8) { /* unsigned conversion */
+					radix = u_radix[p-u_spec-2];
+					upcase = ((int)'x') - *p;
+					if (*p == 'p') {
+						lval = (sizeof(char *) == sizeof(long));
+						upcase = 0;
+					}
+#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR
+					if (lval >= 2) {
+#if WANT_LONG_LONG
+						p = __ulltostr(tmp + sizeof(tmp) - 1,
+									   va_arg(ap, unsigned long long),
+									   radix, upcase);
+#else
+						(void) va_arg(ap, unsigned long long);	/* cary on */
+						p = (char *) ll_err;
+#endif /* WANT_LONG_LONG */
+					} else {
+#endif /* WANT_LONG_LONG || WANT_LONG_LONG_ERROR */
+						p = __ultostr(tmp + sizeof(tmp) - 1, (unsigned long)
+									  ((lval)
+									   ? va_arg(ap, unsigned long)
+									   : va_arg(ap, unsigned short)),
+									  radix, upcase);
+#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR
+					}
+#endif /* WANT_LONG_LONG || WANT_LONG_LONG_ERROR */
+					flag[FLAG_PLUS] = '\0';	/* meaningless for unsigned */
+					if (flag[FLAG_HASH]) {
+						switch (radix) {
+							case 16:
+								flag[FLAG_PLUS] = '0';
+								*--p = 'x';
+								if (*fmt == 'X') {
+									*p = 'X';
+								}
+								break;
+							case 8:
+								if (*p != '0') { /* if not zero */
+									*--p = '0';	/* add leadding zero */
+								}
+						}
+					}
+				} else if (p-u_spec < 10) { /* signed conversion */
+#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR
+					if (lval >= 2) {
+#if WANT_LONG_LONG
+						p = __lltostr(tmp + sizeof(tmp) - 1,
+									  va_arg(ap, long long), 10, 0);
+#else
+						(void) va_arg(ap, long long); /* carry on */
+						p = (char *) ll_err;
+#endif /* WANT_LONG_LONG */
+					} else {
+#endif /* WANT_LONG_LONG || WANT_LONG_LONG_ERROR */
+						p = __ltostr(tmp + sizeof(tmp) - 1, (long)
+									 ((lval)
 									  ? va_arg(ap, long)
 									  : va_arg(ap, short)), 10, 0);
+#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR
+					}
+#endif /* WANT_LONG_LONG || WANT_LONG_LONG_ERROR */
+				} else if (p-u_spec < 12) {	/* character or string */
+					flag[FLAG_PLUS] = '\0';
+					flag[FLAG_0_PAD] = ' ';
+					if (*p == 'c') {	/* character */
+						p = tmp;
+						*p = va_arg(ap, int);
+					} else {	/* string */
+						p = va_arg(ap, char *);
+					}
+#if WANT_DOUBLE || WANT_DOUBLE_ERROR
+				} else if (p-u_spec < 27) {		/* floating point */
+#endif /* WANT_DOUBLE || WANT_DOUBLE_ERROR */
+#if WANT_DOUBLE
+					p = __dtostr(tmp + sizeof(tmp) - 1, va_arg(ap, double));
+#elif WANT_DOUBLE_ERROR
+					(void) va_arg(ap,double); /* carry on */
+					p = (char *) dbl_err;
+#endif /* WANT_DOUBLE */
+				}
 
-				goto printit;
-
-			case 'b':			/* Unsigned binary */
-				radix = 2;
-				goto usproc;
-
-			case 'o':			/* Unsigned octal */
-				radix = 8;
-				goto usproc;
-
-			case 'p':			/* Pointer */
-				lval = (sizeof(char *) == sizeof(long));
-
-				pad = '0';
-				width = 6;
-				preci = 8;
-				/* fall thru */
-
-			case 'x':			/* Unsigned hexadecimal */
-			case 'X':
-				radix = 16;
-				/* fall thru */
+#if WANT_GNU_ERRNO
+			print:
+#endif
+				{				/* this used to be printfield */
+					int len;
 
-			case 'u':			/* Unsigned decimal */
-			  usproc:
-				ptmp = ultostr((unsigned long) ((lval)
-												? va_arg(ap, unsigned long)
-												: va_arg(ap,
-														 unsigned short)),
-							   radix, (*fmt == 'X') ? 1 : 0);
+					/* cheaper than strlen call */
+					for ( len = 0 ; p[len] ; len++ ) { }
 
-				if (hash && radix == 8) {
-					width = strlen(ptmp) + 1;
-					pad = '0';
-				}
-				goto printit;
-
-			case '#':
-				hash = 1;
-				goto fmtnxt;
-
-			case 'c':			/* Character */
-				ptmp[0] = va_arg(ap, int);
-
-				ptmp[1] = '\0';
-				goto nopad;
-
-			case 's':			/* String */
-				ptmp = va_arg(ap, char *);
-
-			  nopad:
-				sign = '\0';
-				pad = ' ';
-			  printit:
-				cnt += printfield(op, ptmp, ljustf, sign, pad, width,
-								  preci, buffer_mode, max_size, cnt);
-				break;
-
-#if FLOATS
-			case 'e':			/* float */
-			case 'f':
-			case 'g':
-			case 'E':
-			case 'G':
-				fprintf(stderr, "LIBM:PRINTF");
-				gcvt(va_arg(ap, double), preci, ptmp);
-
-				preci = -1;
-				goto printit;
-#else
-			case 'e':			/* float */
-			case 'f':
-			case 'g':
-			case 'E':
-			case 'G':
-				fprintf(stderr, "LIBC:PRINTF");
-				exit(-1);
+					if ((*p == '-')
+#if WANT_GNU_ERRNO
+						&& (*fmt != 'm')
 #endif
-
-			default:			/* unknown character */
-				goto charout;
+						&& (*fmt != 's')) {
+						flag[FLAG_PLUS] = *p++;
+						--len;
+					}
+				    if (flag[FLAG_PLUS]) {
+						++len;
+						++preci;
+						if (flag[FLAG_PLUS] == '0') { /* base 16 */
+							++preci; /* account for x or X */
+						}
+					}
+
+					if (preci >= 0) {
+						if ((*fmt == 's')
+#if WANT_GNU_ERRNO
+						|| (*fmt == 'm')
+#endif
+						) {
+							len = preci;
+						}
+						preci -= len;
+						if (preci < 0) {
+							preci = 0;
+						}
+						width -= preci;
+					}
+
+					width -= len;
+					if (width < 0) {
+						width = 0;
+					}
+
+					if (preci < 0) {
+						preci = 0;
+						if (flag[FLAG_PLUS]
+							&& !flag[FLAG_MINUS_LJUSTIFY]
+							&& (flag[FLAG_0_PAD] == '0')) { 
+							preci = width;
+							width = 0;
+						}
+					}
+
+					while (width + len + preci) {
+						unsigned char ch;
+						/* right padding || left padding */
+						if ((!len && !preci)
+							|| (width && !flag[FLAG_MINUS_LJUSTIFY])) {
+							ch = ' ';
+							--width;
+						} else if (flag[FLAG_PLUS]) {
+							ch = flag[FLAG_PLUS]; /* sign */
+							if (flag[FLAG_PLUS]=='0') {	/* base 16 case */
+								flag[FLAG_PLUS] = *p++;	/* get the x|X */
+							} else {
+								flag[FLAG_PLUS] = '\0';
+							}
+							--len;
+						} else if (preci) {
+							ch = '0';
+							--preci;
+						} else {
+							ch = *p++;	/* main field */
+							--len;
+						}
+
+						if (++cnt < max_size) {
+							putc(ch, op);
+						}
+						if ((ch == '\n') && (buffer_mode == _IOLBF)) {
+							fflush(op);
+						}
+					}
+				}
+				goto nextfmt;
 			}
-		} else {
-		  charout:
-			if (max_size > 0 && ++cnt < max_size)
-				putc(*fmt, op);	/* normal char out */
-			if (*fmt == '\n' && buffer_mode == _IOLBF)
-				fflush(op);
+
+			fmt = fmt0;	/* this was an illegal format */
+		}
+
+		charout:
+		if (++cnt < max_size) {
+			putc(*fmt, op);	/* normal char out */
+		}
+		if ((*fmt == '\n') && (buffer_mode == _IOLBF)) {
+			fflush(op);
 		}
+
+	nextfmt:
 		++fmt;
 	}
+
 	op->mode |= buffer_mode;
-	if (buffer_mode == _IONBF)
+	if (buffer_mode == _IONBF) {
 		fflush(op);
-	if (buffer_mode == _IOLBF)
+	}
+	if (buffer_mode == _IOLBF) {
 		op->bufwrite = op->bufstart;
+	}
+
+	if (ferror(op)) {
+		cnt = -1;
+	}
 	return (cnt);
 }
 

+ 4 - 2
libc/stdlib/Makefile

@@ -26,13 +26,15 @@ LIBC=$(TOPDIR)libc.a
 
 DIRS = $(MALLOC)
 
+MSRC=strto_l.c
+MOBJ=strtol.o strtoul.o strto_l.o
 
 MSRC2=atexit.c
 MOBJ2=on_exit.o atexit.o __do_exit.o exit.o
 
 
-CSRC =	abort.c getenv.c  mktemp.c  qsort.c  realpath.c strtod.c strtoul.c \
-	abs.c   bsearch.c mkstemp.c putenv.c rand.c setenv.c strtol.c system.c
+CSRC =	abort.c getenv.c  mktemp.c  qsort.c  realpath.c strtod.c \
+	abs.c   bsearch.c mkstemp.c putenv.c rand.c setenv.c  system.c
 COBJS=$(patsubst %.c,%.o, $(CSRC))
 
 

+ 192 - 0
libc/stdlib/strto_l.c

@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2000 Manuel Novoa III
+ *
+ * Notes:
+ *
+ * The primary objective of this implementation was minimal size.
+ *
+ * Note: Assumes char layout 0-9.*A-Z.*a-z for ordinals values.
+ *
+ * There are a couple of compile-time options below.
+ *
+ */
+
+/*****************************************************************************/
+/*                            OPTIONS                                        */
+/*****************************************************************************/
+
+/* Set if we want strtod to set errno appropriately. */
+/* NOTE: Implies _STRTO_ENDPTR below */
+#define _STRTO_ERRNO            0
+
+/* Set if we want support for the endptr arg. */
+/* Implied by _STRTO_ERRNO. */
+#define _STRTO_ENDPTR           1
+
+/*****************************************************************************/
+/* Don't change anything that follows.                                       */
+/*****************************************************************************/
+
+#if _STRTO_ERRNO
+#undef _STRTO_ENDPTR
+#define _STRTO_ENDPTR           1
+#endif
+
+/*****************************************************************************/
+
+/* Are there actually any machines where this might fail? */
+#if 'A' > 'a'
+#error ordering assumption violated : 'A' > 'a'
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if _STRTO_ERRNO
+#include <errno.h>
+#endif
+
+unsigned long _strto_l(const char *str, char **endptr, int base, int uflag);
+
+#if L_strto_l
+
+/*
+ * This is the main work fuction which handles both strtol (uflag = 0) and
+ * strtoul (uflag = 1).
+ */
+
+unsigned long _strto_l(const char *str, char **endptr, int base, int uflag)
+{
+    unsigned long number = 0;
+    unsigned long cutoff;
+    char *pos = (char *) str;
+#if _STRTO_ENDPTR
+    char *fail_char = (char *) str;
+#endif
+    int digit, cutoff_digit;
+    int negative;
+
+    while (isspace(*pos)) {	/* skip leading whitespace */
+	++pos;
+    }
+
+    /* handle optional sign */
+    negative = 0;
+    switch(*pos) {
+    case '-': negative = 1;	/* fall through to increment pos */
+    case '+': ++pos;
+    }
+
+    if ((base == 16) && (*pos == '0')) { /* handle option prefix */
+	++pos;
+#if _STRTO_ENDPTR
+	fail_char = pos;
+#endif
+	if ((*pos == 'x') || (*pos == 'X')) {
+	    ++pos;
+	}
+    }
+    
+    if (base == 0) {		/* dynamic base */
+	base = 10;		/* default is 10 */
+	if (*pos == '0') {
+	    ++pos;
+	    base -= 2;		/* now base is 8 (or 16) */
+#if _STRTO_ENDPTR
+	    fail_char = pos;
+#endif
+	    if ((*pos == 'x') || (*pos == 'X')) {
+		base += 8;	/* base is 16 */
+		++pos;
+	    }
+	}
+    }
+
+    if ((base < 2) || (base > 36)) { /* illegal base */
+	goto DONE;
+    }
+
+    cutoff = ULONG_MAX / base;
+    cutoff_digit = ULONG_MAX - cutoff * base;
+
+    while (1) {
+	digit = 40;
+	if ((*pos >= '0') && (*pos <= '9')) {
+	    digit = (*pos - '0');
+	} else if (*pos >= 'a') {
+	    digit = (*pos - 'a' + 10);
+	} else if (*pos >= 'A') {
+	    digit = (*pos - 'A' + 10);
+	} else break;
+
+	if (digit >= base) {
+	    break;
+	}
+
+	++pos;
+#if _STRTO_ENDPTR
+	fail_char = pos;
+#endif
+
+	/* adjust number, with overflow check */
+	if ((number > cutoff)
+	    || ((number == cutoff) && (digit > cutoff_digit))) {
+	    number = ULONG_MAX;
+	    if (uflag) {
+		negative = 0; /* since unsigned returns ULONG_MAX */
+	    }
+#if _STRTO_ERRNO
+	    errno = ERANGE;
+#endif
+	} else {
+	    number = number * base + digit;
+	}
+
+    }
+
+ DONE:
+#if _STRTO_ENDPTR
+    if (endptr) {
+	*endptr = fail_char;
+    }
+#endif
+
+    if (negative) {
+	if (!uflag && (number > ((unsigned long)(-(1+LONG_MIN)))+1)) {
+#if _STRTO_ERRNO
+	    errno = ERANGE;
+#endif
+	    return (unsigned long) LONG_MIN;
+	}
+	return (unsigned long)(-((long)number));
+    } else {
+	if (!uflag && (number > (unsigned long) LONG_MAX)) {
+#if _STRTO_ERRNO
+	    errno = ERANGE;
+#endif
+	    return LONG_MAX;
+	}
+	return number;
+    }
+}
+
+#endif
+
+#if L_strtoul
+
+unsigned long strtoul(const char *str, char **endptr, int base)
+{
+    return _strto_l(str, endptr, base, 1);
+}
+
+#endif
+
+#if L_strtol
+
+long strtol(const char *str, char **endptr, int base)
+{
+    return _strto_l(str, endptr, base, 0);
+}
+
+#endif

+ 246 - 66
libc/stdlib/strtod.c

@@ -1,83 +1,263 @@
 /*
- * strtod.c - This file is part of the libc-8086 package for ELKS,
- * Copyright (C) 1995, 1996 Nat Friedman <ndf@linux.mit.edu>.
- * 
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
+ * Copyright (C) 2000 Manuel Novoa III
  *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
+ * Notes:
  *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free
- *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * The primary objective of this implementation was minimal size while
+ * providing robustness and resonable accuracy.
+ *
+ * This implementation depends on IEEE floating point behavior and expects
+ * to be able to generate +/- infinity as a result.
+ *
+ * There are a number of compile-time options below.
  *
  */
+
+/*****************************************************************************/
+/*                            OPTIONS                                        */
+/*****************************************************************************/
+
+/* Set if we want to scale with a O(log2(exp)) multiplications. */
+#define _STRTOD_LOG_SCALING      1
+
+/* Set if we want strtod to set errno appropriately. */
+/* NOTE: Implies all options below and pulls in _zero_or_inf_check. */
+#define _STRTOD_ERRNO            0
+
+/* Set if we want support for the endptr arg. */
+/* Implied by _STRTOD_ERRNO. */
+#define _STRTOD_ENDPTR           1
+
+/* Set if we want to prevent overflow in accumulating the exponent. */
+#define _STRTOD_RESTRICT_EXP     1
+
+/* Set if we want to process mantissa digits more intelligently. */
+/* Implied by _STRTOD_ERRNO. */
+#define _STRTOD_RESTRICT_DIGITS  1
+
+/* Set if we want to skip scaling 0 for the exponent. */
+/* Implied by _STRTOD_ERRNO. */
+#define _STRTOD_ZERO_CHECK       0
+
+/*****************************************************************************/
+/* Don't change anything that follows.                                       */
+/*****************************************************************************/
+
+#if _STRTOD_ERRNO
+#undef _STRTOD_ENDPTR
+#undef _STRTOD_RESTRICT_EXP
+#undef _STRTOD_RESTRICT_DIGITS
+#undef _STRTOD_ZERO_CHECK
+#define _STRTOD_ENDPTR           1
+#define _STRTOD_RESTRICT_EXP     1
+#define _STRTOD_RESTRICT_DIGITS  1
+#define _STRTOD_ZERO_CHECK       1
+#endif
+
+/*****************************************************************************/
+
 #include <stdlib.h>
+
+#include <float.h>
+
+#if _STRTOD_RESTRICT_DIGITS
+#define MAX_SIG_DIGITS 20
+#define EXP_DENORM_ADJUST MAX_SIG_DIGITS
+#define MAX_ALLOWED_EXP (MAX_SIG_DIGITS  + EXP_DENORM_ADJUST - DBL_MIN_10_EXP)
+
+#if DBL_DIG > MAX_SIG_DIGITS
+#error need to adjust MAX_SIG_DIGITS
+#endif
+
+#include <limits.h>
+#if MAX_ALLOWED_EXP > INT_MAX
+#error size assumption violated for MAX_ALLOWED_EXP
+#endif
+#else
+/* We want some excess if we're not restricting mantissa digits. */
+#define MAX_ALLOWED_EXP ((20 - DBL_MIN_10_EXP) * 2)
+#endif
+
 #include <ctype.h>
+/* Note: For i386 the macro resulted in smaller code than the function call. */
+#if 1
+#undef isdigit
+#define isdigit(x) ( (x >= '0') && (x <= '9') )
+#endif
+
+#if _STRTOD_ERRNO
+#include <errno.h>
+extern int _zero_or_inf_check(double x);
+#endif
 
-float strtod(const char *nptr, char **endptr)
+double strtod(const char *str, char **endptr)
 {
-	unsigned short negative;
-	float number;
-	float fp_part;
-	int exponent;
-	unsigned short exp_negative;
-
-	/* advance beyond any leading whitespace */
-	while (isspace(*nptr))
-		nptr++;
-
-	/* check for optional '+' or '-' */
-	negative = 0;
-	if (*nptr == '-') {
-		negative = 1;
-		nptr++;
-	} else if (*nptr == '+')
-		nptr++;
-
-	number = 0;
-	while (isdigit(*nptr)) {
-		number = number * 10 + (*nptr - '0');
-		nptr++;
+    double number;
+#if _STRTOD_LOG_SCALING
+    double p10;
+#endif
+    char *pos0;
+#if _STRTOD_ENDPTR
+    char *pos1;
+#endif
+    char *pos = (char *) str;
+    int exponent_power;
+    int exponent_temp;
+    int negative;
+#if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR
+    int num_digits;
+#endif
+
+    while (isspace(*pos)) {	/* skip leading whitespace */
+	++pos;
+    }
+
+    negative = 0;
+    switch(*pos) {		/* handle optional sign */
+    case '-': negative = 1;	/* fall through to increment position */
+    case '+': ++pos;
+    }
+
+    number = 0.;
+#if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR
+    num_digits = -1;
+#endif
+    exponent_power = 0;
+    pos0 = NULL;
+
+ LOOP:
+    while (isdigit(*pos)) {	/* process string of digits */
+#if _STRTOD_RESTRICT_DIGITS
+	if (num_digits < 0) {	/* first time through? */
+	    ++num_digits;	/* we've now seen a digit */
+	}
+	if (num_digits || (*pos != '0')) { /* had/have nonzero */
+	    ++num_digits;
+	    if (num_digits <= MAX_SIG_DIGITS) { /* is digit significant */
+		number = number * 10. + (*pos - '0');
+	    }
 	}
+#else
+#if _STRTOD_ENDPTR
+	++num_digits;
+#endif
+	number = number * 10. + (*pos - '0');
+#endif
+	++pos;
+    }
+
+    if ((*pos == '.') && !pos0) { /* is this the first decimal point? */
+	pos0 = ++pos;		/* save position of decimal point */
+	goto LOOP;		/* and process rest of digits */
+    }
+
+#if _STRTOD_ENDPTR
+    if (num_digits<0) {		/* must have at least one digit */
+	pos = (char *) str;
+	goto DONE;
+    }
+#endif
+
+#if _STRTOD_RESTRICT_DIGITS
+    if (num_digits > MAX_SIG_DIGITS) { /* adjust exponent for skipped digits */
+	exponent_power += num_digits - MAX_SIG_DIGITS;
+    }
+#endif
+
+    if (pos0) {
+	exponent_power += pos0 - pos; /* adjust exponent for decimal point */
+    }
+
+    if (negative) {		/* correct for sign */
+	number = -number;
+	negative = 0;		/* reset for exponent processing below */
+    }
 
-	if (*nptr == '.') {
-		nptr++;
-		fp_part = 0;
-		while (isdigit(*nptr)) {
-			fp_part = fp_part / 10.0 + (*nptr - '0') / 10.0;
-			nptr++;
-		}
-		number += fp_part;
+    /* process an exponent string */
+    if (*pos == 'e' || *pos == 'E') {
+#if _STRTOD_ENDPTR
+	pos1 = pos;
+#endif
+	switch(*++pos) {	/* handle optional sign */
+	case '-': negative = 1;	/* fall through to increment pos */
+	case '+': ++pos;
 	}
 
-	if (*nptr == 'e' || *nptr == 'E') {
-		nptr++;
-		exp_negative = 0;
-		if (*nptr == '-') {
-			exp_negative = 1;
-			nptr++;
-		} else if (*nptr == '+')
-			nptr++;
-
-		exponent = 0;
-		while (isdigit(*nptr)) {
-			exponent = exponent * 10 + (*nptr - '0');
-			exponent++;
-		}
+	pos0 = pos;
+	exponent_temp = 0;
+	while (isdigit(*pos)) {	/* process string of digits */
+#if _STRTOD_RESTRICT_EXP
+	    if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */
+		exponent_temp = exponent_temp * 10 + (*pos - '0');
+	    }
+#else
+	    exponent_temp = exponent_temp * 10 + (*pos - '0');
+#endif
+	    ++pos;
 	}
 
-	while (exponent) {
-		if (exp_negative)
-			number /= 10;
-		else
-			number *= 10;
-		exponent--;
+#if _STRTOD_ENDPTR
+	if (pos == pos0) {	/* were there no digits? */
+	    pos = pos1;		/* back up to e|E */
+	} /* else */
+#endif
+	if (negative) {
+	    exponent_power -= exponent_temp;
+	} else {
+	    exponent_power += exponent_temp;
 	}
-	return (negative ? -number : number);
+    }
+
+#if _STRTOD_ZERO_CHECK
+    if (number == 0.) {
+	goto DONE;
+    }
+#endif
+
+    /* scale the result */
+#if _STRTOD_LOG_SCALING
+    exponent_temp = exponent_power;
+    p10 = 10.;
+
+    if (exponent_temp < 0) {
+	exponent_temp = -exponent_temp;
+    }
+
+    while (exponent_temp) {
+	if (exponent_temp & 1) {
+	    if (exponent_power < 0) {
+		number /= p10;
+	    } else {
+		number *= p10;
+	    }
+	}
+	exponent_temp >>= 1;
+	p10 *= p10;
+    }
+#else
+    while (exponent_power) {
+	if (exponent_power < 0) {
+	    number /= 10.;
+	    exponent_power++;
+	} else {
+	    number *= 10.;
+	    exponent_power--;
+	}
+    }
+#endif
+
+#if _STRTOD_ERRNO
+    if (_zero_or_inf_check(number)) {
+	errno=ERANGE;
+    }
+#endif
+
+ DONE:
+#if _STRTOD_ENDPTR
+    if (endptr) {
+	*endptr = pos;
+    }
+#endif
+
+    return number;
 }

+ 50 - 11
libc/string/strerror.c

@@ -16,31 +16,70 @@ License along with the GNU C Library; see the file COPYING.LIB.  If
 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
 Cambridge, MA 02139, USA.  */
 
+/*
+ * Manuel Novoa III       Dec 2000
+ *
+ * Converted to use my new (un)signed long (long) to string routines, which
+ * are smaller than the previous functions and don't require static buffers.
+ * Removed dependence on strcat in the process.
+ *
+ * Also appended a test routine ( -DSTRERROR_TEST ) to allow a quick check
+ * on the buffer length when the sys_errorlist is modified.
+ */
+
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <limits.h>
+
+#if (INT_MAX >> 31)
+/* We're set up for 32 bit ints */
+#error need to check size allocation for static buffer 'retbuf'
+#endif
 
-extern char *itoa(int);
+extern char *__ltostr(char *buf, long uval, int base, int uppercase);
+
+static char retbuf[33];			/* 33 is sufficient for 32 bit ints */
+static const char unknown_error[] = "Unknown Error: errno"; /* = */
 
 /* Return a string descibing the errno code in ERRNUM.
    The storage is good only until the next call to strerror.
    Writing to the storage causes undefined behavior.  */
 char *strerror(int err)
 {
-	static char retbuf[80];
+	char *pos;
 
-	if (sys_nerr) {
-		if (err < 0 || err >= sys_nerr)
-			goto unknown;
+	if ((err >= 0) && (err < sys_nerr)) {
 		strcpy(retbuf, sys_errlist[err]);
 		return retbuf;
 	}
 
-	if (err <= 0)
-		goto unknown;
+	/* unknown error */
+	pos = __ltostr(retbuf + sizeof(retbuf) + 1, err, 10, 0)
+		- sizeof(unknown_error); /* leave space for the '=' */
+	strcpy(pos, unknown_error);
+	*(pos + sizeof(unknown_error) - 1) = '=';
+	return pos;
+}
+
+#if STRERROR_TEST
+/* quick way to check for sufficient buffer length */
+#include <stdio.h>
+#include <stdlib.h>
+int main(void)
+{
+	int max = 0;
+	int i, j;
+	char *p;
+	for ( i=0 ; i < sys_nerr ; i++ ) {
+		j = strlen(sys_errlist[i])+1;
+		if (j > max) max = j;
+	}
+	printf("max len = %i\n", j);
 
-  unknown:
-	strcpy(retbuf, "Unknown Error: errno=");
-	strcat(retbuf, (char *) itoa(err));
-	return retbuf;
+	p = strerror(INT_MIN);
+	printf("<%s>  %d\n", p, strlen(p)+1);
+	printf("current buffer length is %d\n", sizeof(retbuf));
+	return EXIT_SUCCESS;
 }
+#endif

+ 63 - 12
libc/string/strsignal.c

@@ -4,11 +4,25 @@
  * GNU Library General Public License.
  */
 
+/*
+ * Manuel Novoa III       Dec 2000
+ *
+ * Converted to use my new (un)signed long (long) to string routines, which
+ * are smaller than the previous functions and don't require static buffers.
+ * Removed dependence on strcat in the process.
+ * 
+ * Also fixed a bug in the signal name lookup code.  While the table is
+ * declared with dimension > 60, there are currently on 32 signals listed.
+ *
+ * Also appended a test routine ( -DSTRSIGNAL_TEST ) to allow a quick check
+ * on the buffer length when the sys_errorlist is modified.
+ */
+
 #include <string.h>
 #include <malloc.h>
 #include <signal.h>
 
-extern char *itoa(int i);
+extern char *__ltostr(char *buf, long uval, int base, int uppercase);
 
 const char *const sys_siglist[] = {
 	"Unknown signal",
@@ -46,26 +60,63 @@ const char *const sys_siglist[] = {
 	NULL
 };
 
+#include <limits.h>
+
+#if (INT_MAX >> 31)
+/* We're set up for 32 bit ints */
+#error need to check size allocation for static buffer 'retbuf'
+#endif
+
 /********************** Function strsignal ************************************/
 
 char *strsignal(int sig)
 {
-	static char retbuf[80];
+	static char retbuf[28];		/* 28 is sufficient for 32 bit ints */
+	static const char unknown_signal[] = "Unknown Signal:";
+	char *pos;
 
-	if (sys_siglist) {
-		if (sig < 0 || sig >= _NSIG)
-			goto unknown;
+	/* if ((sig >= 0) && (sig < _NSIG)) { */
+	if ((sig >= 0) && (sig < 32)) { /* WARNING!!! NOT ALL _NSIG DEFINED!!! */
 		strcpy(retbuf, sys_siglist[sig]);
 		return retbuf;
 	}
 
-	if (sig <= 0)
-		goto unknown;
-
-  unknown:
-	strcpy(retbuf, "Unknown Signal: ");
-	strcat(retbuf, (char *) itoa(sig));
-	return retbuf;
+	pos = __ltostr(retbuf + sizeof(unknown_signal) + 1, sig, 10, 0)
+		- sizeof(unknown_signal);
+	strcpy(pos, unknown_signal);
+	*(pos + sizeof(unknown_signal) - 1) = ' ';
+	return pos;
 }
 
 /********************** THE END ********************************************/
+
+#if STRSIGNAL_TEST
+/* quick way to check for sufficient buffer length */
+#include <stdio.h>
+#include <stdlib.h>
+int main(void)
+{
+	int max = 0;
+	int i, j;
+	const char *p;
+
+	printf("_NSIG = %d  from headers\n", _NSIG);
+	for ( i=0 ; i < _NSIG ; i++ ) {
+		p = sys_siglist[i];
+		if (!p) {
+			printf("Warning! I only count %d signals!\n", i);
+			break;
+		}
+		j = strlen(sys_siglist[i])+1;
+		if (j > max) max = j;
+	}
+	printf("max len = %i\n", j);
+
+	p = strsignal(INT_MIN);
+	printf("<%s>  %d\n", p, strlen(p)+1);
+
+	p = strsignal(i-1);
+	printf("last signal %d is %s\n", i-1, p);
+	return EXIT_SUCCESS;
+}
+#endif

+ 8 - 2
libc/sysdeps/linux/common/Makefile

@@ -29,14 +29,16 @@ LIBC=$(TOPDIR)libc.a
 CSRC =closedir.c dirfd.c getdents.c getdnnm.c gethstnm.c getpagesize.c \
 	isatty.c kernel_version.c mkfifo.c opendir.c readdir.c rewinddir.c \
 	seekdir.c setegid.c seteuid.c setpgrp.c statfix.c tell.c telldir.c \
-	wait.c wait3.c _xmknod.c _fxstat.c _lxstat.c _xstat.c libc_init.c tcgetatr.c
+	wait.c wait3.c _xmknod.c libc_init.c tcgetatr.c
 COBJS=$(patsubst %.c,%.o, $(CSRC))
 
+NISRC= _fxstat.c _lxstat.c _xstat.c
+NIOBJS=$(patsubst %.c,%.o, $(NISRC))
 
 MSRC=syscalls.c
 MOBJ=$(shell ./list_syscalls.sh)
 
-OBJ=$(COBJS) $(MOBJ)
+OBJ=$(COBJS) $(NIOBJS) $(MOBJ)
 
 all: $(OBJ) $(LIBC)
 
@@ -53,6 +55,10 @@ $(COBJS):
 	$(CC) $(CFLAGS) $< -c $*.c -o $*.o
 	$(STRIPTOOL) -x -R .note -R .comment $*.o
 
+$(NIOBJS):
+	$(CC) $(CFLAGS) $< -c $*.c -o $*.o -fno-inline
+	$(STRIPTOOL) -x -R .note -R .comment $*.o
+
 clean:
 	rm -f *.[oa] *~ core