summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2005-01-14 14:15:25 +0000
committerJim Meyering <jim@meyering.net>2005-01-14 14:15:25 +0000
commit7eff5901c419afb8945bd96a5ed12d196802bc79 (patch)
tree4b14cd3abd5b554d54531af4c4b0937ac2815bfa /src
parent9eea26656326f27517c042a88d817a4a32ab938b (diff)
downloadcoreutils-7eff5901c419afb8945bd96a5ed12d196802bc79.tar.xz
(toarith): Rewrite to detect/diagnose integer overflow,
rather than suffering silently. Before, expr would silently overflow and wrap around: $ expr 9223372036854775808 = 0 # $(echo 2^63|bc) 1 Now it detects the problem and exits nonzero: $ ./expr $(echo 2^63|bc) = 0 ./expr: 9223372036854775808: integer is too large
Diffstat (limited to 'src')
-rw-r--r--src/expr.c54
1 files changed, 32 insertions, 22 deletions
diff --git a/src/expr.c b/src/expr.c
index 2ba533bc8..5cc068a51 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1,5 +1,5 @@
/* expr -- evaluate expressions.
- Copyright (C) 86, 1991-1997, 1999-2004 Free Software Foundation, Inc.
+ Copyright (C) 86, 1991-1997, 1999-2005 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -322,34 +322,44 @@ tostring (VALUE *v)
static bool
toarith (VALUE *v)
{
- intmax_t i;
- bool neg;
- char *cp;
-
switch (v->type)
{
case integer:
return true;
case string:
- i = 0;
- cp = v->u.s;
- neg = (*cp == '-');
- if (neg)
- cp++;
+ {
+ intmax_t value = 0;
+ char *cp = v->u.s;
+ int sign = (*cp == '-' ? -1 : 1);
- do
- {
- if (ISDIGIT (*cp))
- i = i * 10 + *cp - '0';
- else
- return false;
- }
- while (*++cp);
+ if (sign < 0)
+ cp++;
- free (v->u.s);
- v->u.i = i * (neg ? -1 : 1);
- v->type = integer;
- return true;
+ do
+ {
+ if (ISDIGIT (*cp))
+ {
+ intmax_t new_v = 10 * value + sign * (*cp - '0');
+ if (0 < sign
+ ? (INTMAX_MAX / 10 < value || new_v < 0)
+ : (value < INTMAX_MIN / 10 || 0 < new_v))
+ error (EXPR_FAILURE, 0,
+ (0 < sign
+ ? _("integer is too large: %s")
+ : _("integer is too small: %s")),
+ quotearg_colon (v->u.s));
+ value = new_v;
+ }
+ else
+ return false;
+ }
+ while (*++cp);
+
+ free (v->u.s);
+ v->u.i = value * sign;
+ v->type = integer;
+ return true;
+ }
default:
abort ();
}