diff options
Diffstat (limited to 'stdlib')
-rw-r--r-- | stdlib/Makefile | 2 | ||||
-rw-r--r-- | stdlib/grouping.h | 123 | ||||
-rw-r--r-- | stdlib/stdlib.h | 52 | ||||
-rw-r--r-- | stdlib/strtod.c | 149 | ||||
-rw-r--r-- | stdlib/strtol.c | 87 | ||||
-rw-r--r-- | stdlib/wcstombs.c | 11 | ||||
-rw-r--r-- | stdlib/wctomb.c | 2 |
7 files changed, 350 insertions, 76 deletions
diff --git a/stdlib/Makefile b/stdlib/Makefile index 1a1498c..ea1ffd3 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -37,7 +37,7 @@ routines := \ strtof strtod strtold \ system -distribute := exit.h +distribute := exit.h grouping.h tests := tst-strtol tst-strtod testmb testrand testsort testdiv include ../Rules diff --git a/stdlib/grouping.h b/stdlib/grouping.h new file mode 100644 index 0000000..566f6a6 --- /dev/null +++ b/stdlib/grouping.h @@ -0,0 +1,123 @@ +/* Internal header for proving correct grouping in strings of numbers. +Copyright (C) 1995 Free Software Foundation, Inc. +Contributed by Ulrich Drepper. + +The GNU C 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. + +The GNU C 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. + +You should have received a copy of the GNU Library General Public +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. */ + +#include <limits.h> + +#ifndef MAX +#define MAX(a,b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \ + _a > _b ? _a : _b; }) +#endif + +/* Find the maximum prefix of the string between BEGIN and END which + satisfies the grouping rules. It is assumed that at least one digit + follows BEGIN directly. */ + +static inline const char * +correctly_grouped_prefix (const char *begin, const char *end, + wchar_t thousands, const char *grouping) +{ + if (! grouping) + return end; + + while (end > begin) + { + const char *cp = end - 1; + const char *gp = grouping; + + /* Check first group. */ + while (cp >= begin && (wchar_t) *cp != thousands) + --cp; + + if (end - cp == (int) *gp + 1) + { + /* This group matches the specification. */ + + const char *new_end; + + if (cp < begin) + /* There is just one complete group. We are done. */ + return end; + + /* CP points to a thousands separator character. The preceding + remainder of the string from BEGIN to NEW_END is the part we + will consider if there is a grouping error in this trailing + portion from CP to END. */ + new_end = cp - 1; + + /* Loop while the grouping is correct. */ + while (1) + { + /* Get the next grouping rule. */ + ++gp; + if (*gp == 0) + /* If end is reached use last rule. */ + --gp; + + /* Skip the thousands separator. */ + --cp; + + if (*gp == CHAR_MAX || *gp < 0) + { + /* No more thousands separators are allowed to follow. */ + while (cp >= begin && (wchar_t) *cp != thousands) + --cp; + + if (cp < begin) + /* OK, only digits followed. */ + return end; + } + else + { + /* Check the next group. */ + const char *group_end = cp; + + while (cp >= begin && (wchar_t) *cp != thousands) + --cp; + + if (cp < begin && group_end - cp <= (int) *gp) + /* Final group is correct. */ + return end; + + if (cp < begin || group_end - cp != (int) *gp) + /* Incorrect group. Punt. */ + break; + } + } + + /* The trailing portion of the string starting at NEW_END + contains a grouping error. So we will look for a correctly + gouped number in the preceding portion instead. */ + end = new_end; + } + else + { + /* Even the first group was wrong; determine maximum shift. */ + if (end - cp > (int) *gp + 1) + end = cp + (int) *gp + 1; + else if (cp < begin) + /* This number does not fill the first group, but is correct. */ + return end; + else + /* CP points to a thousands seperator character. */ + end = cp; + } + } + + return MAX (begin, end); +} diff --git a/stdlib/stdlib.h b/stdlib/stdlib.h index d64a2ff..2a3cf8e 100644 --- a/stdlib/stdlib.h +++ b/stdlib/stdlib.h @@ -78,9 +78,7 @@ extern double strtod __P ((__const char *__nptr, char **__endptr)); #ifdef __USE_GNU /* Likewise for `float' and `long double' sizes of floating-point numbers. */ -extern float __strtof __P ((__const char *__nptr, char **__endptr)); extern float strtof __P ((__const char *__nptr, char **__endptr)); -extern __long_double_t __strtold __P ((__const char *__nptr, char **__endptr)); extern __long_double_t strtold __P ((__const char *__nptr, char **__endptr)); #endif @@ -100,9 +98,57 @@ extern unsigned long long int strtouq __P ((__const char *__nptr, char **__endptr, int __base)); #endif /* GCC and use BSD. */ + +/* The internal entry points for `strtoX' take an extra flag argument + saying whether or not to parse locale-dependent number grouping. */ + +extern double __strtod_internal (__const char *__nptr, + char **__endptr, int __group); +extern float __strtof_internal (__const char *__nptr, char **__endptr, + int __group); +extern __long_double_t __strtold_internal (__const char *__nptr, + char **__endptr, int __group); +extern long int __strtol_internal (__const char *__nptr, char **__endptr, + int __base, int __group); +extern unsigned long int __strtoul_internal (__const char *__nptr, + char **__endptr, int __base, + int __group); +extern long long int __strtoq_internal (__const char *__nptr, char **__endptr, + int __base, int __group); +extern unsigned long long int __strtouq_internal (__const char *__nptr, + char **__endptr, int __base, + int __group); + #if defined (__OPTIMIZE__) && __GNUC__ >= 2 +/* Define inline functions which call the internal entry points. */ + +extern __inline double strtod (__const char *__nptr, char **__endptr) +{ return __strtod_internal (__nptr, __endptr, 0); } +extern __inline long int strtol (__const char *__nptr, + char **__endptr, int __base) +{ return __strtol_internal (__nptr, __endptr, __base, 0); } +extern __inline unsigned long int strtoul (__const char *__nptr, + char **__endptr, int __base) +{ return __strtoul_internal (__nptr, __endptr, __base, 0); } + +#ifdef __USE_GNU +extern __inline float strtof (__const char *__nptr, char **__endptr) +{ return __strtof_internal (__nptr, __endptr, 0); } +extern __inline __long_double_t strtold (__const char *__nptr, char **__endptr) +{ return __strtold_internal (__nptr, __endptr, 0); } +#endif + +#ifdef __USE_BSD +extern __inline long long int strtoq (__const char *__nptr, char **__endptr, + int __base) +{ return __strtoq_internal (__nptr, __endptr, __base, 0); } +extern __inline unsigned long long int strtouq (__const char *__nptr, + char **__endptr, int __base) +{ return __strtouq_internal (__nptr, __endptr, __base, 0); } +#endif + extern __inline double atof (__const char *__nptr) -{ return strtod(__nptr, (char **) NULL); } +{ return strtod (__nptr, (char **) NULL); } extern __inline int atoi (__const char *__nptr) { return (int) strtol (__nptr, (char **) NULL, 10); } extern __inline long int atol (__const char *__nptr) diff --git a/stdlib/strtod.c b/stdlib/strtod.c index 2f0e972..8f71d13 100644 --- a/stdlib/strtod.c +++ b/stdlib/strtod.c @@ -102,10 +102,11 @@ static const mp_limb _tens_in_limb[MAX_DIG_PER_LIMB + 1] = #define RETURN_LIMB_SIZE howmany (MANT_DIG, BITS_PER_MP_LIMB) #define RETURN(val,end) \ - do { if (endptr != 0) *endptr = (char *) end; return val; } while (0) + do { if (endptr != 0) *endptr = (char *) (end); return (val); } while (0) /* Maximum size necessary for mpn integers to hold floating point numbers. */ -#define MPNSIZE (howmany (MAX_EXP + 2 * MANT_DIG, BITS_PER_MP_LIMB) + 2) +#define MPNSIZE (howmany (MAX_EXP + 2 * MANT_DIG, BITS_PER_MP_LIMB) \ + + 2) /* Declare an mpn integer variable that big. */ #define MPN_VAR(name) mp_limb name[MPNSIZE]; mp_size_t name##size /* Copy an mpn integer value. */ @@ -276,15 +277,23 @@ __mpn_lshift_1 (mp_limb *ptr, mp_size_t size, unsigned int count, mp_limb limb) } +#define INTERNAL(x) INTERNAL1(x) +#define INTERNAL1(x) __##x##_internal + +/* This file defines a function to check for correct grouping. */ +#include "grouping.h" + + /* Return a floating point number with the value of the given string NPTR. Set *ENDPTR to the character after the last used one. If the number is smaller than the smallest representable number, set `errno' to ERANGE and return 0.0. If the number is too big to be represented, set `errno' to ERANGE and return HUGE_VAL with the approriate sign. */ FLOAT -STRTOF (nptr, endptr) - const char *nptr; - char **endptr; +INTERNAL (STRTOF) (nptr, endptr, group) + const char *nptr; + char **endptr; + int group; { int negative; /* The sign of the number. */ MPN_VAR (num); /* MP representation of the number. */ @@ -301,9 +310,9 @@ STRTOF (nptr, endptr) int bits; /* Running pointer after the last character processed in the string. */ - const char *cp; + const char *cp, *tp; /* Start of significant part of the number. */ - const char *startp; + const char *startp, *start_of_digits; /* Points at the character following the integer and fractional digits. */ const char *expp; /* Total number of digit and number of digits in integer part. */ @@ -313,60 +322,29 @@ STRTOF (nptr, endptr) /* The radix character of the current locale. */ wchar_t decimal; -#ifdef USE_GROUPING /* The thousands character of the current locale. */ wchar_t thousands; /* The numeric grouping specification of the current locale, in the format described in <locale.h>. */ const char *grouping; - /* Check the grouping of the integer part at [BEGIN,END). - Return zero iff a separator is found out of place. */ - int grouping_ok (const char *begin, const char *end) + if (group) { - if (grouping) - while (end > begin) - { - const char *p = end; - do - --p; - while (*p != thousands && p > begin); - if (end - 1 - p != *grouping++) - return 0; /* Wrong number of digits in this group. */ - end = p; /* Correct group; trim it off the end. */ - - if (*grouping == 0) - --grouping; /* Same grouping repeats in next iteration. */ - else if (*grouping == CHAR_MAX || *grouping < 0) - { - /* No further grouping allowed. */ - while (end > begin) - if (*--end == thousands) - return 0; - } - } - return 1; - } - /* Return with no conversion if the grouping of [STARTP,CP) is bad. */ -#define CHECK_GROUPING if (! grouping_ok (startp, cp)) RETURN (0.0, nptr); else - - grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); - if (*grouping <= 0 || *grouping == CHAR_MAX) - grouping = NULL; - else - { - /* Figure out the thousands seperator character. */ - if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP), - strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0) - thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); - if (thousands == L'\0') + grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); + if (*grouping <= 0 || *grouping == CHAR_MAX) grouping = NULL; + else + { + /* Figure out the thousands separator character. */ + if (mbtowc (&thousands, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP), + strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0) + thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); + if (thousands == L'\0') + grouping = NULL; + } } -#else -#define grouping NULL -#define thousands L'\0' -#define CHECK_GROUPING ((void) 0) -#endif + else + grouping = NULL; /* Find the locale's decimal point character. */ if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT), @@ -402,18 +380,21 @@ STRTOF (nptr, endptr) RETURN (0.0, nptr); /* Record the start of the digits, in case we will check their grouping. */ - startp = cp; + start_of_digits = startp = cp; /* Ignore leading zeroes. This helps us to avoid useless computations. */ while (c == '0' || (thousands != L'\0' && c == thousands)) c = *++cp; - CHECK_GROUPING; - /* If no other digit but a '0' is found the result is 0.0. Return current read pointer. */ if (!isdigit (c) && c != decimal) - RETURN (0.0, cp); + { + tp = correctly_grouped_prefix (start_of_digits, cp, thousands, grouping); + /* If TP is at the start of the digits, there was no correctly + grouped prefix of the string; so no number found. */ + RETURN (0.0, tp == start_of_digits ? nptr : tp); + } /* Remember first significant digit and read following characters until the decimal point, exponent character or any non-FP number character. */ @@ -432,7 +413,37 @@ STRTOF (nptr, endptr) c = *++cp; } - CHECK_GROUPING; + if (grouping && dig_no > 0) + { + /* Check the grouping of the digits. */ + tp = correctly_grouped_prefix (start_of_digits, cp, thousands, grouping); + if (cp != tp) + { + /* Less than the entire string was correctly grouped. */ + + if (tp == start_of_digits) + /* No valid group of numbers at all: no valid number. */ + RETURN (0.0, nptr); + + if (tp < startp) + /* The number is validly grouped, but consists + only of zeroes. The whole value is zero. */ + RETURN (0.0, tp); + + /* Recompute DIG_NO so we won't read more digits than + are properly grouped. */ + cp = tp; + dig_no = 0; + for (tp = startp; tp < cp; ++tp) + if (isdigit (*tp)) + ++dig_no; + + int_no = dig_no; + lead_zero = 0; + + goto number_parsed; + } + } if (dig_no >= NDIG) /* Too many digits to be representable. Assigning this to EXPONENT @@ -528,6 +539,8 @@ STRTOF (nptr, endptr) assert (dig_no >= int_no); } + number_parsed: + /* The whole string is parsed. Store the address of the next character. */ if (endptr) *endptr = (char *) cp; @@ -546,7 +559,7 @@ STRTOF (nptr, endptr) exponent -= incr; } - if (int_no + exponent > MAX_10_EXP) + if (int_no + exponent > MAX_10_EXP + 1) { errno = ERANGE; return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; @@ -607,6 +620,14 @@ STRTOF (nptr, endptr) count_leading_zeros (bits, num[numsize - 1]); bits = numsize * BITS_PER_MP_LIMB - bits; + /* Now we know the exponent of the number in base two. + Check it against the maximum possible exponent. */ + if (bits > MAX_EXP) + { + errno = ERANGE; + return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL; + } + /* We have already the first BITS bits of the result. Together with the information whether more non-zero bits follow this is enough to determine the result. */ @@ -1059,3 +1080,15 @@ STRTOF (nptr, endptr) /* NOTREACHED */ } + +/* External user entry point. */ + +weak_symbol (STRTOF) + +FLOAT +STRTOF (nptr, endptr) + const char *nptr; + char **endptr; +{ + return INTERNAL (STRTOF) (nptr, endptr, 0); +} diff --git a/stdlib/strtol.c b/stdlib/strtol.c index 888a94e..1c63afb 100644 --- a/stdlib/strtol.c +++ b/stdlib/strtol.c @@ -22,11 +22,17 @@ Cambridge, MA 02139, USA. */ #include <stddef.h> #include <stdlib.h> #include <errno.h> +#include "../locale/localeinfo.h" -/* Nonzero if we are defining `strtoul' or `strtouq', operating on unsigned - integers. */ -#ifndef UNSIGNED + +/* Nonzero if we are defining `strtoul' or `strtouq', operating on + unsigned integers. */ +#ifndef UNSIGNED #define UNSIGNED 0 +#define INT LONG int +#else +#define strtol strtoul +#define INT unsigned LONG int #endif /* If QUAD is defined, we are defining `strtoq' or `strtouq', @@ -54,22 +60,27 @@ static const unsigned long long int maxquad = ULONG_LONG_MAX; #define LONG long #endif + +#define INTERNAL(x) INTERNAL1(x) +#define INTERNAL1(x) __##x##_internal + +/* This file defines a function to check for correct grouping. */ +#include "grouping.h" + + /* Convert NPTR to an `unsigned long int' or `long int' in base BASE. If BASE is 0 the base is determined by the presence of a leading zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. If BASE is < 2 or > 36, it is reset to 10. If ENDPTR is not NULL, a pointer to the character after the last one converted is stored in *ENDPTR. */ -#if UNSIGNED -unsigned LONG int -#define strtol strtoul -#else -LONG int -#endif -strtol (nptr, endptr, base) + +INT +INTERNAL (strtol) (nptr, endptr, base, group) const char *nptr; char **endptr; int base; + int group; { int negative; register unsigned LONG int cutoff; @@ -77,9 +88,34 @@ strtol (nptr, endptr, base) register unsigned LONG int i; register const char *s; register unsigned char c; - const char *save; + const char *save, *end; int overflow; + /* The thousands character of the current locale. */ + wchar_t thousands; + /* The numeric grouping specification of the current locale, + in the format described in <locale.h>. */ + const char *grouping; + + if (group) + { + grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); + if (*grouping <= 0 || *grouping == CHAR_MAX) + grouping = NULL; + else + { + /* Figure out the thousands separator character. */ + if (mbtowc (&thousands, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP), + strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0) + thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); + if (thousands == L'\0') + grouping = NULL; + } + } + else + grouping = NULL; + + if (base < 0 || base == 1 || base > 36) base = 10; @@ -126,6 +162,20 @@ strtol (nptr, endptr, base) /* Save the pointer so we can check later if anything happened. */ save = s; + if (group) + { + /* Find the end of the digit string and check its grouping. */ + end = s; + for (c = *end; c != '\0'; c = *++end) + if (c != thousands && !isdigit (c) && + (!isalpha (c) || toupper (c) - 'A' + 10 >= base)) + break; + if (*s == thousands) + end = s; + else + end = correctly_grouped_prefix (s, end, thousands, grouping); + } + cutoff = ULONG_MAX / (unsigned LONG int) base; cutlim = ULONG_MAX % (unsigned LONG int) base; @@ -133,6 +183,8 @@ strtol (nptr, endptr, base) i = 0; for (c = *s; c != '\0'; c = *++s) { + if (group && s == end) + break; if (isdigit (c)) c -= '0'; else if (isalpha (c)) @@ -187,3 +239,16 @@ noconv: *endptr = (char *) nptr; return 0L; } + +/* External user entry point. */ + +weak_symbol (strtol) + +INT +strtol (nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + return INTERNAL (strtol) (nptr, endptr, base, 0); +} diff --git a/stdlib/wcstombs.c b/stdlib/wcstombs.c index acaf15a..6ddbfb1 100644 --- a/stdlib/wcstombs.c +++ b/stdlib/wcstombs.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -17,7 +17,7 @@ not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <ansidecl.h> -#include <localeinfo.h> +#include "../locale/localeinfo.h" #include <ctype.h> #include <stddef.h> #include <stdio.h> @@ -32,8 +32,10 @@ size_t DEFUN(wcstombs, (s, pwcs, n), register char *s AND register CONST wchar_t *pwcs AND register size_t n) { +#if 0 register CONST mb_char *mb; register int shift = 0; +#endif register size_t written = 0; register wchar_t w; @@ -49,6 +51,10 @@ DEFUN(wcstombs, (s, pwcs, n), } else { +#if 1 + written = (size_t) -1; + break; +#else mb = &_ctype_info->mbchar->mb_chars[w + shift]; if (mb->string == NULL || mb->len == 0) { @@ -65,6 +71,7 @@ DEFUN(wcstombs, (s, pwcs, n), written += mb->len; shift += mb->shift; } +#endif } } diff --git a/stdlib/wctomb.c b/stdlib/wctomb.c index 78b55b7..15f32ab 100644 --- a/stdlib/wctomb.c +++ b/stdlib/wctomb.c @@ -53,7 +53,7 @@ DEFUN(wctomb, (s, wchar), register char *s AND wchar_t wchar) *s = '\0'; return 1; } - else if (mb == NULL) + else /* if (mb == NULL) */ { if ((wchar_t) (char) wchar == wchar && isascii ((char) wchar)) { |