diff options
Diffstat (limited to 'libc')
-rw-r--r-- | libc/inet/addr.c | 44 | ||||
-rw-r--r-- | libc/misc/assert/__assert.c | 19 | ||||
-rw-r--r-- | libc/misc/ctype/ctype.c | 35 | ||||
-rw-r--r-- | libc/misc/internals/Makefile | 2 | ||||
-rw-r--r-- | libc/misc/internals/dtostr.c | 271 | ||||
-rw-r--r-- | libc/misc/internals/itoa.c | 21 | ||||
-rw-r--r-- | libc/misc/internals/lltostr.c | 39 | ||||
-rw-r--r-- | libc/misc/internals/ltoa.c | 40 | ||||
-rw-r--r-- | libc/misc/internals/ltostr.c | 71 | ||||
-rw-r--r-- | libc/misc/internals/ulltostr.c | 37 | ||||
-rw-r--r-- | libc/misc/internals/ultostr.c | 37 | ||||
-rw-r--r-- | libc/misc/internals/zoicheck.c | 16 | ||||
-rw-r--r-- | libc/stdio/printf.c | 618 | ||||
-rw-r--r-- | libc/stdlib/Makefile | 6 | ||||
-rw-r--r-- | libc/stdlib/strto_l.c | 192 | ||||
-rw-r--r-- | libc/stdlib/strtod.c | 312 | ||||
-rw-r--r-- | libc/string/strerror.c | 61 | ||||
-rw-r--r-- | libc/string/strsignal.c | 75 | ||||
-rw-r--r-- | libc/sysdeps/linux/common/Makefile | 10 |
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 |