Tcl Source Code

Artifact [fc85cf994b]
Login

Artifact fc85cf994bfe972946396d9d96519361f5e3b144:

Attachment "strtoul-2.patch" to ticket [440916ffff] added by das 2001-09-01 15:27:46.
Index: tcl/compat/strtoul.c
===================================================================
RCS file: /cvsroot/tcl/tcl/compat/strtoul.c,v
retrieving revision 1.2
diff -u -3 -r1.2 strtoul.c
--- tcl/compat/strtoul.c	1998/09/14 18:39:45	1.2
+++ tcl/compat/strtoul.c	2001/09/01 08:13:32
@@ -12,6 +12,18 @@
  * RCS: @(#) $Id: strtoul.c,v 1.2 1998/09/14 18:39:45 stanton Exp $
  */
 
+#include "tcl.h"
+#include "tclPort.h"
+#ifdef NO_LIMITS_H
+#   include "../compat/limits.h"
+#else
+#   include <limits.h>
+#endif
+
+#ifndef ULONG_MAX
+#define ULONG_MAX (((unsigned long)LONG_MAX<<1)+1)
+#endif
+
 #include <ctype.h>
 
 /*
@@ -33,44 +45,29 @@
 
 /*
  *----------------------------------------------------------------------
- *
- * strtoul --
  *
- *	Convert an ASCII string into an integer.
+ * __private_strtoul --
  *
- * Results:
- *	The return value is the integer equivalent of string.  If endPtr
- *	is non-NULL, then *endPtr is filled in with the character
- *	after the last one that was part of the integer.  If string
- *	doesn't contain a valid integer value, then zero is returned
- *	and *endPtr is set to string.
- *
- * Side effects:
- *	None.
+ *	Internal helper proc used in both strtoul and strtol, see below
  *
  *----------------------------------------------------------------------
  */
 
-unsigned long int
-strtoul(string, endPtr, base)
-    char *string;		/* String of ASCII digits, possibly
-				 * preceded by white space.  For bases
-				 * greater than 10, either lower- or
-				 * upper-case digits may be used.
-				 */
-    char **endPtr;		/* Where to store address of terminating
-				 * character, or NULL. */
-    int base;			/* Base for conversion.  Must be less
-				 * than 37.  If 0, then the base is chosen
-				 * from the leading characters of string:
-				 * "0x" means hex, "0" means octal, anything
-				 * else means decimal.
-				 */
+static unsigned long int
+__private_strtoul(string, endPtr, base, negative, overflow)
+    char *string;
+    char **endPtr;
+    int base;
+    int *negative;
+	int *overflow;
 {
     register char *p;
     register unsigned long int result = 0;
     register unsigned digit;
     int anyDigits = 0;
+    
+    *negative=0;
+    *overflow=0;
 
     /*
      * Skip any leading blanks.
@@ -80,6 +77,14 @@
     while (isspace(*p)) {
 	p += 1;
     }
+    if (*p == '-') {
+        *negative = 1;
+        p += 1;
+    } else {
+        if (*p == '+') {
+            p += 1;
+        }
+    }
 
     /*
      * If no base was provided, pick one from the leading characters
@@ -90,7 +95,7 @@
     {
 	if (*p == '0') {
 	    p += 1;
-	    if (*p == 'x') {
+	    if ((*p == 'x') || (*p == 'X')) {
 		p += 1;
 		base = 16;
 	    } else {
@@ -111,7 +116,7 @@
 	 * Skip a leading "0x" from hex numbers.
 	 */
 
-	if ((p[0] == '0') && (p[1] == 'x')) {
+	if ((p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) {
 	    p += 2;
 	}
     }
@@ -127,7 +132,10 @@
 	    if (digit > 7) {
 		break;
 	    }
-	    result = (result << 3) + digit;
+	    if(result > (ULONG_MAX >> 3)) { *overflow=1; }
+	    result = result << 3;
+	    if(digit > (ULONG_MAX - result)) { *overflow=1; }
+	    result += digit;
 	    anyDigits = 1;
 	}
     } else if (base == 10) {
@@ -136,7 +144,10 @@
 	    if (digit > 9) {
 		break;
 	    }
-	    result = (10*result) + digit;
+	    if(result > (ULONG_MAX/10)) { *overflow=1; }
+	    result = (10*result);
+	    if(digit > (ULONG_MAX - result)) { *overflow=1; }
+	    result += digit;
 	    anyDigits = 1;
 	}
     } else if (base == 16) {
@@ -149,10 +160,14 @@
 	    if (digit > 15) {
 		break;
 	    }
-	    result = (result << 4) + digit;
+	    if(result > (ULONG_MAX >> 4)) { *overflow=1; }
+	    result = (result << 4);
+	    if(digit > (ULONG_MAX - result)) { *overflow=1; }
+	    result += digit;
 	    anyDigits = 1;
 	}
     } else {
+		unsigned long int maxres = ULONG_MAX/base;
 	for ( ; ; p += 1) {
 	    digit = *p - '0';
 	    if (digit > ('z' - '0')) {
@@ -162,7 +177,10 @@
 	    if (digit >= base) {
 		break;
 	    }
-	    result = result*base + digit;
+	    if(result > maxres) { *overflow=1; }
+	    result = result*base;
+	    if(digit > (ULONG_MAX - result)) { *overflow=1; }
+	    result += digit;
 	    anyDigits = 1;
 	}
     }
@@ -180,4 +198,118 @@
     }
 
     return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strtoul --
+ *
+ *	Convert an ASCII string into an integer.
+ *
+ * Results:
+ *	The return value is the integer equivalent of string.  If endPtr
+ *	is non-NULL, then *endPtr is filled in with the character
+ *	after the last one that was part of the integer.  If string
+ *	doesn't contain a valid integer value, then zero is returned
+ *	and *endPtr is set to string.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+unsigned long int
+strtoul(string, endPtr, base)
+    char *string;		/* String of ASCII digits, possibly
+				 * preceded by white space.  For bases
+				 * greater than 10, either lower- or
+				 * upper-case digits may be used.
+				 */
+    char **endPtr;		/* Where to store address of terminating
+				 * character, or NULL. */
+    int base;			/* Base for conversion.  Must be less
+				 * than 37.  If 0, then the base is chosen
+				 * from the leading characters of string:
+				 * "0x" means hex, "0" means octal, anything
+				 * else means decimal.
+				 */
+{
+	unsigned long int	value;
+	int					negative, overflow;	
+	
+	value = __private_strtoul(string, endPtr, base, &negative, &overflow);
+		
+	if (overflow)
+	{
+		value = ULONG_MAX;
+		errno = ERANGE;
+	} 
+	else if (negative)
+		value = -value;
+	
+	return(value);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strtol --
+ *
+ *	Convert an ASCII string into a signed integer.
+ *
+ * Results:
+ *	The return value is the signed integer equivalent of string.  If endPtr
+ *	is non-NULL, then *endPtr is filled in with the character
+ *	after the last one that was part of the integer.  If string
+ *	doesn't contain a valid integer value, then zero is returned
+ *	and *endPtr is set to string.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+long int
+strtol(string, endPtr, base)
+    char *string;		/* String of ASCII digits, possibly
+				 * preceded by white space.  For bases
+				 * greater than 10, either lower- or
+				 * upper-case digits may be used.
+				 */
+    char **endPtr;		/* Where to store address of terminating
+				 * character, or NULL. */
+    int base;			/* Base for conversion.  Must be less
+				 * than 37.  If 0, then the base is chosen
+				 * from the leading characters of string:
+				 * "0x" means hex, "0" means octal, anything
+				 * else means decimal.
+				 */
+{
+	unsigned long int	uvalue;
+	long int			svalue;
+	int					negative, overflow;
+		
+	uvalue = __private_strtoul(string, endPtr, base, &negative, &overflow);
+		
+	if (overflow || (!negative && uvalue > LONG_MAX) || (negative && uvalue > -LONG_MIN))
+	{
+		svalue = (negative ? -LONG_MIN : LONG_MAX);
+		errno = ERANGE;
+	} else
+		svalue = (negative ? -uvalue : uvalue);
+	
+	return(svalue);
+}
+
+int atoi(const char * str)
+{
+	return(strtol(str, NULL, 10));
+}
+
+long atol(const char * str)
+{
+	return(strtol(str, NULL, 10));
 }