diff options
author | wollman <wollman@FreeBSD.org> | 2002-05-10 22:59:29 +0000 |
---|---|---|
committer | wollman <wollman@FreeBSD.org> | 2002-05-10 22:59:29 +0000 |
commit | e11cb46ee8e7eea44531903aaac525bbd80f14c0 (patch) | |
tree | eb552a2a3e43e767f64fbdca6a0411905d9c9852 /bin | |
parent | 6cd7fcea88d41d92ee11ffbf6d144c3ea9696084 (diff) | |
download | FreeBSD-src-e11cb46ee8e7eea44531903aaac525bbd80f14c0.zip FreeBSD-src-e11cb46ee8e7eea44531903aaac525bbd80f14c0.tar.gz |
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.
Diffstat (limited to 'bin')
-rw-r--r-- | bin/expr/expr.1 | 39 | ||||
-rw-r--r-- | bin/expr/expr.y | 95 |
2 files changed, 88 insertions, 46 deletions
diff --git a/bin/expr/expr.1 b/bin/expr/expr.1 index 4669000..1425e00 100644 --- a/bin/expr/expr.1 +++ b/bin/expr/expr.1 @@ -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. diff --git a/bin/expr/expr.y b/bin/expr/expr.y index 3026f52..92c0e5f 100644 --- a/bin/expr/expr.y +++ b/bin/expr/expr.y @@ -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; |