The response to my POSIX interpretation request says that `expr'

is required to be oblivious to overflow and to use the data type `long'.
(Division by zero is undefined in ISO C so it's still OK to check for it
here.)  Add a new `-e' flag to get the old, more useful behavior.
This commit is contained in:
Garrett Wollman 2002-05-10 22:59:29 +00:00
parent a81da3c933
commit 1393277e29
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=96367
2 changed files with 88 additions and 46 deletions

View File

@ -30,7 +30,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 22, 2002
.Dd May 10, 2002
.Dt EXPR 1
.Os
.Sh NAME
@ -38,7 +38,7 @@
.Nd evaluate expression
.Sh SYNOPSIS
.Nm
.Op Fl \&-
.Op Fl e
.Ar expression
.Sh DESCRIPTION
The
@ -50,15 +50,25 @@ and writes the result on standard output.
All operators and operands must be passed as separate arguments.
Several of the operators have special meaning to command interpreters
and must therefore be quoted appropriately.
All integer operands are interpreted in base 10.
.Pp
Arithmetic operations are performed using signed integer math,
in the largest integral type available in the C language. The
Arithmetic operations are performed using signed integer math.
If the
.Fl e
flag is specified, arithmetic uses the C
.Ql intmax_t
data type (the largest integral type available), and
.Nm
utility will detect arithmetic overflow and division by zero, and
returns with an exit status of 2 in those cases. If a numeric operand
is specified which is so large as to overflow conversion to an integer,
it is parsed as a string instead. All numeric operands are interpreted
in base 10.
will detect arithmetic overflow and return an error indication.
If a numeric operand is specified which is so large as to overflow
conversion to an integer, it is parsed as a string instead.
If
.Fl e
is not specified, arithmetic operations and parsing of integer
arguments will overflow silently according to the rules of the C
standard, and integer computations will be performed using the
.Ql long
data type.
.Pp
Operators are listed below in order of increasing precedence; all
are left-associative.
@ -174,7 +184,7 @@ command, one might rearrange the expression:
More generally, parenthesize possibly-negative values:
.Dl a=$(expr \e( $a \e) + 1)
.It
The following example prints the filename portion of a pathname stored
This example prints the filename portion of a pathname stored
in variable
.Va a .
Since
@ -231,9 +241,6 @@ utility conforms to
provided that the
.Ev EXPR_COMPAT
environment variable is not defined.
.Tn POSIX
does not specify whether arithmetic overflow is detected, nor does it specify
the possible range of integer arguments to
.Nm ,
so a portable application must assume that the range is small and that
overflow may not be detected.
The
.Fl e
flag is an extension.

View File

@ -70,6 +70,7 @@ int yyerror(const char *);
int yylex(void);
int yyparse(void);
static int eflag;
char **av;
%}
@ -154,7 +155,10 @@ make_str(const char *s)
* non-digits MUST NOT be considered integers. strtoimax() will
* figure this out for us.
*/
(void)strtoimax(s, &ep, 10);
if (eflag)
(void)strtoimax(s, &ep, 10);
else
(void)strtol(s, &ep, 10);
if (*ep != '\0')
vp->type = string;
@ -186,9 +190,13 @@ to_integer(struct val *vp)
/* vp->type == numeric_string, make it numeric */
errno = 0;
i = strtoimax(vp->u.s, (char **)NULL, 10);
if (errno == ERANGE)
err(ERR_EXIT, NULL);
if (eflag) {
i = strtoimax(vp->u.s, (char **)NULL, 10);
if (errno == ERANGE)
err(ERR_EXIT, NULL);
} else {
i = strtol(vp->u.s, (char **)NULL, 10);
}
free (vp->u.s);
vp->u.i = i;
@ -273,10 +281,15 @@ main(int argc, char *argv[])
if (getenv("EXPR_COMPAT") != NULL) {
av = argv + 1;
} else {
while ((c = getopt(argc, argv, "")) != -1)
while ((c = getopt(argc, argv, "e")) != -1)
switch (c) {
case 'e':
eflag = 1;
break;
default:
fprintf(stderr,"usage: expr [--] expression\n");
fprintf(stderr,
"usage: expr [-e] expression\n");
exit(ERR_EXIT);
}
av = argv + optind;
@ -327,7 +340,7 @@ op_and(struct val *a, struct val *b)
struct val *
op_eq(struct val *a, struct val *b)
{
struct val *r;
struct val *r;
if (isstring (a) || isstring (b)) {
to_string (a);
@ -447,6 +460,7 @@ op_ne(struct val *a, struct val *b)
int
chk_plus(intmax_t a, intmax_t b, intmax_t r)
{
/* sum of two positive numbers must be positive */
if (a > 0 && b > 0 && r <= 0)
return 1;
@ -462,14 +476,18 @@ op_plus(struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
if (!to_integer(a) || !to_integer(b)) {
errx(ERR_EXIT, "non-numeric argument");
}
r = make_integer (/*(intmax_t)*/(a->u.i + b->u.i));
if (chk_plus (a->u.i, b->u.i, r->u.i)) {
errx(ERR_EXIT, "overflow");
}
if (eflag) {
r = make_integer(a->u.i + b->u.i);
if (chk_plus(a->u.i, b->u.i, r->u.i)) {
errx(ERR_EXIT, "overflow");
}
} else
r = make_integer((long)a->u.i + (long)b->u.i);
free_value (a);
free_value (b);
return r;
@ -478,6 +496,7 @@ op_plus(struct val *a, struct val *b)
int
chk_minus(intmax_t a, intmax_t b, intmax_t r)
{
/* special case subtraction of INTMAX_MIN */
if (b == INTMAX_MIN) {
if (a >= 0)
@ -494,14 +513,18 @@ op_minus(struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
if (!to_integer(a) || !to_integer(b)) {
errx(ERR_EXIT, "non-numeric argument");
}
r = make_integer (/*(intmax_t)*/(a->u.i - b->u.i));
if (chk_minus (a->u.i, b->u.i, r->u.i)) {
errx(ERR_EXIT, "overflow");
}
if (eflag) {
r = make_integer(a->u.i - b->u.i);
if (chk_minus(a->u.i, b->u.i, r->u.i)) {
errx(ERR_EXIT, "overflow");
}
} else
r = make_integer((long)a->u.i - (long)b->u.i);
free_value (a);
free_value (b);
return r;
@ -524,14 +547,18 @@ op_times(struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
if (!to_integer(a) || !to_integer(b)) {
errx(ERR_EXIT, "non-numeric argument");
}
r = make_integer (/*(intmax_t)*/(a->u.i * b->u.i));
if (chk_times (a->u.i, b->u.i, r->u.i)) {
errx(ERR_EXIT, "overflow");
}
if (eflag) {
r = make_integer(a->u.i * b->u.i);
if (chk_times(a->u.i, b->u.i, r->u.i)) {
errx(ERR_EXIT, "overflow");
}
} else
r = make_integer((long)a->u.i * (long)b->u.i);
free_value (a);
free_value (b);
return (r);
@ -553,7 +580,7 @@ op_div(struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
if (!to_integer(a) || !to_integer(b)) {
errx(ERR_EXIT, "non-numeric argument");
}
@ -561,10 +588,14 @@ op_div(struct val *a, struct val *b)
errx(ERR_EXIT, "division by zero");
}
r = make_integer (/*(intmax_t)*/(a->u.i / b->u.i));
if (chk_div (a->u.i, b->u.i)) {
errx(ERR_EXIT, "overflow");
}
if (eflag) {
r = make_integer(a->u.i / b->u.i);
if (chk_div(a->u.i, b->u.i)) {
errx(ERR_EXIT, "overflow");
}
} else
r = make_integer((long)a->u.i / (long)b->u.i);
free_value (a);
free_value (b);
return r;
@ -575,7 +606,7 @@ op_rem(struct val *a, struct val *b)
{
struct val *r;
if (!to_integer (a) || !to_integer (b)) {
if (!to_integer(a) || !to_integer(b)) {
errx(ERR_EXIT, "non-numeric argument");
}
@ -583,8 +614,12 @@ op_rem(struct val *a, struct val *b)
errx(ERR_EXIT, "division by zero");
}
r = make_integer (/*(intmax_t)*/(a->u.i % b->u.i));
/* chk_rem necessary ??? */
if (eflag)
r = make_integer(a->u.i % b->u.i);
/* chk_rem necessary ??? */
else
r = make_integer((long)a->u.i % (long)b->u.i);
free_value (a);
free_value (b);
return r;