summaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
Diffstat (limited to 'libc')
-rw-r--r--libc/inet/addr.c44
-rw-r--r--libc/misc/assert/__assert.c19
-rw-r--r--libc/misc/ctype/ctype.c35
-rw-r--r--libc/misc/internals/Makefile2
-rw-r--r--libc/misc/internals/dtostr.c271
-rw-r--r--libc/misc/internals/itoa.c21
-rw-r--r--libc/misc/internals/lltostr.c39
-rw-r--r--libc/misc/internals/ltoa.c40
-rw-r--r--libc/misc/internals/ltostr.c71
-rw-r--r--libc/misc/internals/ulltostr.c37
-rw-r--r--libc/misc/internals/ultostr.c37
-rw-r--r--libc/misc/internals/zoicheck.c16
-rw-r--r--libc/stdio/printf.c618
-rw-r--r--libc/stdlib/Makefile6
-rw-r--r--libc/stdlib/strto_l.c192
-rw-r--r--libc/stdlib/strtod.c312
-rw-r--r--libc/string/strerror.c61
-rw-r--r--libc/string/strsignal.c75
-rw-r--r--libc/sysdeps/linux/common/Makefile10
19 files changed, 1475 insertions, 431 deletions
diff --git a/libc/inet/addr.c b/libc/inet/addr.c
index 142363ccc..a05a4288d 100644
--- a/libc/inet/addr.c
+++ b/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
diff --git a/libc/misc/assert/__assert.c b/libc/misc/assert/__assert.c
index 905671d98..b98958f43 100644
--- a/libc/misc/assert/__assert.c
+++ b/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 ? "() " : "");
diff --git a/libc/misc/ctype/ctype.c b/libc/misc/ctype/ctype.c
index c7cceada6..fb465838f 100644
--- a/libc/misc/ctype/ctype.c
+++ b/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
diff --git a/libc/misc/internals/Makefile b/libc/misc/internals/Makefile
index 38bf7729f..b9168288e 100644
--- a/libc/misc/internals/Makefile
+++ b/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)
diff --git a/libc/misc/internals/dtostr.c b/libc/misc/internals/dtostr.c
new file mode 100644
index 000000000..d9ab80bbb
--- /dev/null
+++ b/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;
+}
diff --git a/libc/misc/internals/itoa.c b/libc/misc/internals/itoa.c
index a683b8018..e69de29bb 100644
--- a/libc/misc/internals/itoa.c
+++ b/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;
-}
diff --git a/libc/misc/internals/lltostr.c b/libc/misc/internals/lltostr.c
new file mode 100644
index 000000000..2ce359615
--- /dev/null
+++ b/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;
+}
diff --git a/libc/misc/internals/ltoa.c b/libc/misc/internals/ltoa.c
index da8b6d3df..e69de29bb 100644
--- a/libc/misc/internals/ltoa.c
+++ b/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;
-}
diff --git a/libc/misc/internals/ltostr.c b/libc/misc/internals/ltostr.c
index da6fad232..7e45fec30 100644
--- a/libc/misc/internals/ltostr.c
+++ b/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;
}
diff --git a/libc/misc/internals/ulltostr.c b/libc/misc/internals/ulltostr.c
new file mode 100644
index 000000000..50246d3bc
--- /dev/null
+++ b/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;
+}
diff --git a/libc/misc/internals/ultostr.c b/libc/misc/internals/ultostr.c
new file mode 100644
index 000000000..d4663fb39
--- /dev/null
+++ b/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;
+}
diff --git a/libc/misc/internals/zoicheck.c b/libc/misc/internals/zoicheck.c
new file mode 100644
index 000000000..2113c6298
--- /dev/null
+++ b/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 );
+}
diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c
index d987966f6..634885e67 100644
--- a/libc/stdio/printf.c
+++ b/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);
}
diff --git a/libc/stdlib/Makefile b/libc/stdlib/Makefile
index 8514d5c4a..4d16a8585 100644
--- a/libc/stdlib/Makefile
+++ b/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))
diff --git a/libc/stdlib/strto_l.c b/libc/stdlib/strto_l.c
new file mode 100644
index 000000000..a83cf4095
--- /dev/null
+++ b/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
diff --git a/libc/stdlib/strtod.c b/libc/stdlib/strtod.c
index de3aecbd3..dff5aeb73 100644
--- a/libc/stdlib/strtod.c
+++ b/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;
}
diff --git a/libc/string/strerror.c b/libc/string/strerror.c
index 91565965f..3aa8b57e2 100644
--- a/libc/string/strerror.c
+++ b/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
diff --git a/libc/string/strsignal.c b/libc/string/strsignal.c
index 4cadb144a..1a0a6ca47 100644
--- a/libc/string/strsignal.c
+++ b/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
diff --git a/libc/sysdeps/linux/common/Makefile b/libc/sysdeps/linux/common/Makefile
index 0a216f8ff..73190ac9c 100644
--- a/libc/sysdeps/linux/common/Makefile
+++ b/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