about summary refs log tree commit diff
path: root/stdlib
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1995-03-16 05:32:45 +0000
committerRoland McGrath <roland@gnu.org>1995-03-16 05:32:45 +0000
commitf0bf9cb92c28c9ec0ac3376d87055e9d14610082 (patch)
treec058250695dd90b4eb5b870620826727d559f925 /stdlib
parent67a3a8ac7fef11753205c69da9df9c4ef30f6076 (diff)
downloadglibc-f0bf9cb92c28c9ec0ac3376d87055e9d14610082.tar.gz
glibc-f0bf9cb92c28c9ec0ac3376d87055e9d14610082.tar.xz
glibc-f0bf9cb92c28c9ec0ac3376d87055e9d14610082.zip
Thu Mar 16 00:04:41 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
	* locale/C-ctype.c: New correct data generated by drepper.

	* Rules: Don't use $(libc.a).

	Parsing of grouped numbers contributed by Ulrich Drepper.
	* stdlib/strtol.c (__strtol_internal): Renamed from strtol.  Take
 	new flag arg; if nonzero, parse locale-dependent thousands
 	grouping and interpret only the prefix that is correctly grouped.
	(strtol): Define this to call _strtol_internal with zero for the flag.
	Use a weak symbol for the definition.
	* stdlib/strtod.c (strtod, __strtod_internal): Likewise.
  	Check for the exponent of the number overflowing the float format.
	* stdlib/stdlib.h (__strtof, __strtold): Declarations removed.
	(__strto{f,d,ld,l,ul,q,uq}_internal): Declare these functions.
	[__OPTIMIZE__]: Define inline functions calling those.
	* stdlib/grouping.h: New file, written by drepper.
	* stdlib/Makefile (distribute): Add grouping.h.
	* stdio/vfscanf.c: Grok %' modifier to numeric conversions.  Call
 	__strtoX_internal with the grouping flag set iff the modifier is
 	present.

Wed Mar 15 00:40:54 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>

	* sysdeps/generic/memchr.c: Fix typos: limit.h -> limits.h.

	* mach/Machrules: Produce static deps for all object flavors.
	[interface-library]: Remove all these variables and rules.
	($(interface-library)-routines): Define this variable.
	(extra-libs): Append $(interface-library) to this.
	* mach/Makefile (interface-library): Omit .a suffix.
	* hurd/Makefile: Likewise.

Tue Mar 14 23:40:31 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>

	* stdio/internals.c (flushbuf): If !FLUSH_ONLY, don't skip out
 	early if no new data in buffer after priming.

	* Makerules (object-suffixes, libtypes): Move all these variables
 	to Makeconfig.
	* Makeconfig (object-suffixes, libtypes): Moved here from Makerules.

	* Makerules (build-extra-lib): New canned sequence.

	* sysdeps/mach/hurd/euidaccess.c: Include fcntl.h.  Declare ERR;
 	fix uses of FILE and PORT.  Remove bogus weak alias for `access'.

	* sysdeps/mach/hurd/dirfd.c: Include hurd/fd.h and errno.h.
	Add missing semicolon.
	* sysdeps/mach/hurd/opendir.c: Include hurd/fd.h.  Use a `struct
 	hurd_fd *' temp var, since DIRP->__fd is a `void *'.
	* sysdeps/mach/hurd/readdir.c: Include hurd/fd.h.

	* stdlib/wcstombs.c: #if 0 out code for non-ASCII chars until the
 	locale data format is implemented.

	* sysdeps/mach/hurd/i386/trampoline.c (_hurd_setup_sighandler):
 	Pass &__sigreturn on the stack to the trampoline code, so it is
 	not position-dependent.

	* stdio/printf_fp.c (NDEBUG): Define this to disable assert.
  	Don't include <stdarg.h>.
	(__printf_fp): Last arg ARGS is now `const void **const'; 
	dereference ARGS[0] instead of using va_arg.

	* locale/setlocale.c: In LC_ALL case, initialize CATEGORY before
 	loop to install data.

	* locale/loadlocale.c (_nl_category_num_items): Use _NL_ITEM_INDEX
 	to extract number from item code.
	(_nl_load_locale): Close the descriptor when finished.

	* malloc/realloc.c (_realloc_internal): Call _malloc_internal in
 	place of malloc.

	* time/tzfile.c (__tzfile_default): Initialize RULE_STDOFF to zero.
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))
 	{