Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 3 Sep 2001 11:31:50 +0400
From:      "Andrey A. Chernov" <ache@nagual.pp.ru>
To:        current@freebsd.org, audit@freebsd.org
Subject:   CFR: strtol{l}() fixes
Message-ID:  <20010903113148.A6055@nagual.pp.ru>

next in thread | raw e-mail | index | archive | help
Fixes list:

Locale *is* used in strtol()/strtoll(), at least for isspace(), so remove
'locale not used' statement from comments and BUGS section of manpage.

strtol(): fix non-portable 'cutoff' calculation using the same method as
in strtoll().

Cleanup 'cutoff' calculation, remove unneded casts. Misc. cleanup to
make two functions looks the same.

Implement EINVAL reaction per POSIX, document it in manpage, corresponding
POSIX quotes here:

------------------------------------------------
If the subject sequence is empty or does not have the expected form, no
conversion is performed; the value of str is stored in the object pointed
to by endptr, provided that endptr is not a null pointer.

If no conversion could be performed, 0 shall be returned and errno may be
set to [EINVAL].

[EINVAL] The value of base is not supported.

Since 0, {LONG_MIN} or {LLONG_MIN}, and {LONG_MAX} or {LLONG_MAX} are
returned on error and are also valid returns on success, an application
wishing to check for error situations should set errno to 0, then call
strtol( ) or strtoll ( ), then check errno.
-----------------------------------------------------

--- strtoll.c.old	Tue Feb 27 22:50:52 2001
+++ strtoll.c	Mon Sep  3 11:26:35 2001
@@ -50,7 +50,7 @@
 /*
  * Convert a string to a long long integer.
  *
- * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * Assumes that the upper and lower case
  * alphabets and digits are each contiguous.
  */
 long long
@@ -62,7 +62,7 @@
 	register const char *s;
 	register unsigned long long acc;
 	register unsigned char c;
-	register unsigned long long qbase, cutoff;
+	register unsigned long long cutoff;
 	register int neg, any, cutlim;
 
 	/*
@@ -90,6 +90,9 @@
 	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
+	any = 0;
+	if (base < 2 || base > 36)
+		goto noconv;
 
 	/*
 	 * Compute the cutoff value between legal numbers and illegal
@@ -106,15 +109,14 @@
 	 * next digit is > 7 (or 8), the number is too big, and we will
 	 * return a range error.
 	 *
-	 * Set any if any `digits' consumed; make it negative to indicate
+	 * Set 'any' if any `digits' consumed; make it negative to indicate
 	 * overflow.
 	 */
-	qbase = (unsigned)base;
 	cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
 	    : LLONG_MAX;
-	cutlim = cutoff % qbase;
-	cutoff /= qbase;
-	for (acc = 0, any = 0;; c = *s++) {
+	cutlim = cutoff % base;
+	cutoff /= base;
+	for (acc = 0; ; c = *s++) {
 		if (!isascii(c))
 			break;
 		if (isdigit(c))
@@ -129,16 +131,19 @@
 			any = -1;
 		else {
 			any = 1;
-			acc *= qbase;
+			acc *= base;
 			acc += c;
 		}
 	}
 	if (any < 0) {
 		acc = neg ? LLONG_MIN : LLONG_MAX;
 		errno = ERANGE;
+	} else if (!any) {
+noconv:
+		errno = EINVAL;
 	} else if (neg)
 		acc = -acc;
-	if (endptr != 0)
+	if (endptr != NULL)
 		*endptr = (char *)(any ? s - 1 : nptr);
 	return (acc);
 }
--- strtol.c.old	Fri Jul 12 22:55:24 1996
+++ strtol.c	Mon Sep  3 10:54:32 2001
@@ -44,7 +44,7 @@
 /*
  * Convert a string to a long integer.
  *
- * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * Assumes that the upper and lower case
  * alphabets and digits are each contiguous.
  */
 long
@@ -53,25 +53,29 @@
 	char **endptr;
 	register int base;
 {
-	register const char *s = nptr;
+	register const char *s;
 	register unsigned long acc;
 	register unsigned char c;
 	register unsigned long cutoff;
-	register int neg = 0, any, cutlim;
+	register int neg, any, cutlim;
 
 	/*
 	 * Skip white space and pick up leading +/- sign if any.
 	 * If base is 0, allow 0x for hex and 0 for octal, else
 	 * assume decimal; if base is already 16, allow 0x.
 	 */
+	s = nptr;
 	do {
 		c = *s++;
 	} while (isspace(c));
 	if (c == '-') {
 		neg = 1;
 		c = *s++;
-	} else if (c == '+')
-		c = *s++;
+	} else {
+		neg = 0;
+		if (c == '+')
+			c = *s++;
+	}
 	if ((base == 0 || base == 16) &&
 	    c == '0' && (*s == 'x' || *s == 'X')) {
 		c = s[1];
@@ -80,6 +84,9 @@
 	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
+	any = 0;
+	if (base < 2 || base > 36)
+		goto noconv;
 
 	/*
 	 * Compute the cutoff value between legal numbers and illegal
@@ -95,13 +102,14 @@
 	 * a value > 214748364, or equal but the next digit is > 7 (or 8),
 	 * the number is too big, and we will return a range error.
 	 *
-	 * Set any if any `digits' consumed; make it negative to indicate
+	 * Set 'any' if any `digits' consumed; make it negative to indicate
 	 * overflow.
 	 */
-	cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
-	cutlim = cutoff % (unsigned long)base;
-	cutoff /= (unsigned long)base;
-	for (acc = 0, any = 0;; c = *s++) {
+	cutoff = neg ? (unsigned long)-(LONG_MIN + LONG_MAX) + LONG_MAX
+	    : LONG_MAX;
+	cutlim = cutoff % base;
+	cutoff /= base;
+	for (acc = 0; ; c = *s++) {
 		if (!isascii(c))
 			break;
 		if (isdigit(c))
@@ -123,9 +131,12 @@
 	if (any < 0) {
 		acc = neg ? LONG_MIN : LONG_MAX;
 		errno = ERANGE;
+	} else if (!any) {
+noconv:
+		errno = EINVAL;
 	} else if (neg)
 		acc = -acc;
-	if (endptr != 0)
+	if (endptr != NULL)
 		*endptr = (char *)(any ? s - 1 : nptr);
 	return (acc);
 }
--- strtol.3.old	Thu Mar  1 16:21:48 2001
+++ strtol.3	Mon Sep  3 10:37:53 2001
@@ -144,9 +144,16 @@
 .Sh RETURN VALUES
 The
 .Fn strtol
+or
+.Fn strtoll
 function
 returns the result of the conversion,
 unless the value would underflow or overflow.
+If no conversion could be performed, 0 shall be returned and
+.Va errno
+will be
+set to
+.Er EINVAL .
 If an underflow occurs,
 .Fn strtol
 returns
@@ -155,11 +162,6 @@
 .Fn strtol
 returns
 .Dv LONG_MAX .
-The
-.Fn strtoll
-function
-returns the result of the conversion,
-unless the value would underflow or overflow.
 If an underflow occurs,
 .Fn strtoll
 returns
@@ -174,6 +176,9 @@
 .Er ERANGE .
 .Sh ERRORS
 .Bl -tag -width Er
+.It Bq Er EINVAL
+The value of base is not supported or
+no conversion could be performed.
 .It Bq Er ERANGE
 The given string was out of range; the value converted has been clamped.
 .El
@@ -198,5 +203,3 @@
 .Bx
 .Fn strtoq
 function is deprecated.
-.Sh BUGS
-Ignores the current locale.

-- 
Andrey A. Chernov
http://ache.pp.ru/

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010903113148.A6055>