aboutsummaryrefslogtreecommitdiff
path: root/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib')
-rw-r--r--stdlib/Makefile2
-rw-r--r--stdlib/grouping.h123
-rw-r--r--stdlib/stdlib.h52
-rw-r--r--stdlib/strtod.c149
-rw-r--r--stdlib/strtol.c87
-rw-r--r--stdlib/wcstombs.c11
-rw-r--r--stdlib/wctomb.c2
7 files changed, 350 insertions, 76 deletions
diff --git a/stdlib/Makefile b/stdlib/Makefile
index 1a1498c662..ea1ffd33e8 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 0000000000..566f6a6109
--- /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 d64a2ffb7c..2a3cf8ec5f 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 2f0e972893..8f71d136de 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 888a94e4d7..1c63afb6fe 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 acaf15a94e..6ddbfb1725 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 78b55b7af5..15f32ab71f 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))
{