New patches for expr

Stefan Esser se at freebsd.org
Mon Jul 4 20:39:08 UTC 2011


Am 04.07.2011 12:48, schrieb Bruce Evans:
> On Fri, 1 Jul 2011, Stefan Esser wrote:
>> On 01.07.2011 01:58, Bruce Evans wrote:
>> <<<<<< Aside: Is check_utility_compat() still used at all?
>>
>> It provides compatibility with FreeBSD-4 if the environment variable
>> _COMPAT_FreeBSD_4 exists and contains "expr" as member of a comma
>> separated list, or if the following symlink exists in /etc:
>>
>>     compat-FreeBSD-4-util -> expr
>>
>> In fact, the symlink is also parsed as comma separated list, but expr is
>> the only program in FreeBSD that calls check_utility_compat() ...
>>
>> Since expr is the only user of check_utility_compat() and the same
>> effect can be had by specifying EXPR_COMPAT, the check_utility_compat
>> function should be considered deprecated and only be kept in libc for
>> backwards compatibility of old binaries, IMHO. This function adds one
>> unnecessary call of getenv() and one of readlink() per invocation of
>> expr and adds complexity to the man-page for expr.
> 
> I'd like to remove the symlink.  The symlink for malloc() annoys me
> too.  It pessimizes and bloats malloc() a little, and on FreeBSD cluster
> machines, it breaks support for old binaries by being misconfigured
> to contain flags that are incompatible with ones recognized by old
> versions of malloc().  And there is no way to prevent even current
> malloc() from examining the symlink.  It examines the symlink first
> and spams stderr with messages about any invalid flags in the symlink.
> Then it examines the environment variable, and the environment variable
> can be used to cancel any unportable or otherwise unwanted flags in
> the symlink, but the messages cannot be canceled, and more bogus
> messages may be printed to defeat the next stage.  Then it examines
> the compile-time variable, and this can't completely cancel the previous
> stages, due to the messages.

If there is no opposition to this change, I'd like to remove the call of
check_utility_compat() from expr in a separate patch (to the source file
and the man page), which would make this function obsolete except to
support binaries from older releases that contain the call to this
function (but it could just return 0 without checking for the symlink or
environment variable and it should be made invisible for new code).

If the symlinks exists for "expr" compatibility, then any script using
"expr -e" or "expr --" will break, since there is no way to override the
effect of that symlink (neither a command line option nor an environment
setting will help). The man page of check_utility_compat() describes the
function as a temporary work-around to enforce backwards compatibility
with FreeBSD-4 (but as I said, that function appears to be unused,
except for the one case of "expr").

> The patches seem OK, except:
> - I don't like reversing the semantics of the -e option from "extension"
>   to "compatible"

Hmmm, yes, I can understand your reasoning. But EXPR_COMPAT is meant to
make "expr" backwards compatible with pre-POSIX versions, which did not
accept options and had less strict number syntax rules. Now, the word
compat could be understood to indicate compatibility with POSIX, not
with the old version that was not compliant with POSIX ...

I do consider the non-conflicting extension of the numeric range (in the
sense of making "POSIX undefined" situations return sensible and defined
results in FreeBSD) an "extension", but the backwards compatible mode is
just that (and in conflict with POSIX, when enabled).

Is there some other wording that might be acceptable for the backwards
compatible mode of operation enabled by "EXPR_COMPAT" or the "-e" option?

I thought about "backward-compatible" or "old mode operation", but both
do not really appeal ...

I have renamed the variable "eflag" to "nonposix" to better describe its
meaning.

> - '+<integer>' is now not accepted even in compatibility mode.  Accepting
>   '+<integer>' wasn't documented, but was a side effect of using strto*()
>   for parsing

Well, "+<integer>" is accepted in my local version, which has evolved a
little beyond the files attached to my previous mail.

> - the changes to the man page aren't in the patches.

Yes, I already wrote, that I'd like to have consensus at the
functionality before I submit patches to the man-page.

>> I also include a SHAR of regression tests meant to be committed to
>> /usr/src/tools/regression/bin/expr. All tests pass with the current
>> patched version of expr (while the currently shipped version dumps core
>> in one test case).
> 
> I ran the regression tests on a 2005 version of NetBSD expr and a 2002
> version of Linux expr.  There were the following failures:
> 
> NetBSD:
> % not ok 10 - - (got 0, expected 2)

NetBSD seems to accept "-" as string; POSIX declares the behaviour
undefined if a string argument is identical to one of the operators.
We flag this as "syntax error".

> % not ok 11 -1 - (got 0, expected 2)

NetBSD expr does not support command line options and thus just accepts
the decimal number as valid operand.
We enable POSIX option processing by default (unless EXPR_COMPAT is set
in the environment). This leads to "-1" being treated as unsupported
option.
This command line processing is only needed to support the "-e" option
introduced to return from 64bit arithmetic with overflow checks to 32bit
arithmetic with silent overflow. But we cannot easily go back and
unsupport "-e", since it has been used in scripts that needed the
extended numeric range. Too bad!!!

BTW: NetBSD does support POSIX "option end" specified with "--" in a
special way, which is meant to be backwards compatible: If after removal
of a first argument of "--" the rest still parses, then "--" is
considered as POSIX end of options marker. Else, "--" is the first
string argument of the expression to evaluate.

> % not ok 12 -1 + 1 - (got 1, expected 2)

Same as above. NetBSD just adds -1 to 1 and returns with "0" and an exit
code of 1.

> % not ok 14 "       1" + 1 - (got 0, expected 2)

NetBSD skips leading white space. They do not try to enforce POSIX
compatible arguments (even without the equivalent of EXPR_COMPAT or the
"-e" option). I checked the latest sources, this is still the case,
although there had been an attempt to add POSIX options parsing in 2009
(which was immediatly backed-out again).

> % not ok 15 -e "       1" + 1 - (got 2, expected 0)

NetBSD does not support "-e" and considers it a valid string argument,
but then the expression does not parse.

> % not ok 29 -- -9223372036854775808 / -1 - (got 0, expected 2)
> 
> NetBSD does sloppy parsing using strtoll(), and does all numeric operations
> in type int64_t, and does primitive range checking.  Most of the failures
> are from the sloppy parsing.  #29 is from the range checking being too
> primitive for the delicate case INTMAX_MIN / -1.

Yes, I special cased division of INTMAX_MIN / -1, since this is the only
case of overflow in division (besides the div 0 case).

The NetBSD range checks are incomplete for the sole reason, that they
use one wrong comparison operator (i.e. "a < 0 && b < 0 && r > 0", while
the final test should be "r >= 0" to catch "INTMAX_MIN + INTMAX_MIN).

> Linux:
> % not ok 10 - - (got 0, expected 2)
> % not ok 11 -1 - (got 0, expected 2)
> % not ok 12 -1 + 1 - (got 1, expected 2)
> 
> I don't understand these 3.  Linux expr gives the error when run directly.
> Now I'm confused by the results of NetBSD expr for these tests too.

I just remember that have Linux emulation installed ...

The version I have identifies itself as "expr (GNU coreutils) 6.12" with
Copyright 2008.

Linux expr seems to be as forgiving with regard to strings that look
like command options as NetBSD. "-" is just a string, "-1" a valid first
argument (not an unsupported option) and the expression also just works.

> % not ok 15 -e "       1" + 1 - (got 2, expected 0)
> 
> No -e in Linux.

Same as in NetBSD and about every other POSIX system in the world ;-)

> % not ok 16 "" + 1 - (got 2, expected 0)
> 
> The test seems to be wrong here.  The empty string should only be accepted
> in compat mode, but this test seems to use the default mode.

This was with an earlier version of my patches. I have made the program
and the test case reject interpretation of "" as zero (unless
EXPR_COMPAT or "-e" are given).

> % not ok 18 9223372036854775807 + 1 - (got 0, expected 2)
> % not ok 21 -- -9223372036854775808 \* -1 - (got 0, expected 2)
> % not ok 24 10000000000000000000 + 0 - (got 0, expected 2)
> % not ok 29 -- -9223372036854775808 / -1 - (got 0, expected 2)
> % not ok 32 4611686018427387904 \* 2 - (got 0, expected 2)
> % not ok 35 4611686018427387905 \* -2 - (got 0, expected 2)
> 
> Since Linux does no range checking.

Obviously ...

BTW: Linux "expr" does give "match", "substr", "index" and "length"
special meaning (these are no valid string arguments). And they use "+"
as an escape mechanism to support string these values, anyway.

Thus the token "+" is a valid prefix for any string operand, and
considered an identity operator. This does not lead to ambiguities,
since "+" is not defined as operator on strings. But I do not think that
we should follow that precedent, of course.

> % not ok 55 " 1" + 1 - (got 2, expected 0)
> 
> Since Linux doesn't support EXPR_COMPAT=1 (to allow leading spaces and
> no digits).  This shows an evil of environment variables -- it is not
> obvious what is tested since the environment variable has a critical
> effect but is not mentioned in the test output.

Hmmm, I'll add a marker to the output of the regression test script to
indicate whether EXPR_COMPAT had been set in the environment.

New versions (3 diffs for expr.y, as planned to be committed one after
the other - all relative to the latest version currently in SVN, a patch
for the man page, and the regression tests expr/regress.sh) are attached.

Please let me know, if there is anything else that needs to be changed
before the commit.

Best regards, STefan
-------------- next part --------------
--- expr.1	2011-07-01 20:22:29.213133521 +0200
+++ expr.1.FINAL	2011-07-02 18:57:27.161998563 +0200
@@ -50,25 +50,20 @@
 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.
+All integer operands are interpreted in base 10 and must consist of only
+an optional leading minus sign followed by one or more digits.
 .Pp
-Arithmetic operations are performed using signed integer math.
-If the
-.Fl e
-flag is specified, arithmetic uses the C
+Arithmetic operations are performed using signed integer math with a
+range according to the C
 .Vt intmax_t
-data type (the largest integral type available), and
-.Nm
-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
+data type (the largest signed integral type available).
+All conversions and operations are checked for overflow.
+Overflow results in program termination with an error message on stdout
+and with an error status.
+.Pp
+The
 .Fl e
-is not specified, arithmetic operations and parsing of integer
-arguments will overflow silently according to the rules of the C
-standard, using the
-.Vt long
-data type.
+option is accepted for backwards compatibility but has no effect.
 .Pp
 Operators are listed below in order of increasing precedence; all
 are left-associative.
@@ -82,7 +77,9 @@
 .Ar expr1
 if it is neither an empty string nor zero;
 otherwise, returns the evaluation of
-.Ar expr2 .
+.Ar expr2
+if it is not an empty string;
+otherwise, returns zero.
 .It Ar expr1 Li & Ar expr2
 Return the evaluation of
 .Ar expr1
@@ -270,8 +267,35 @@
 The
 .Nm
 utility conforms to
-.St -p1003.1-2001 ,
+.St -p1003.1 ,
 provided that compatibility mode is not enabled.
+.Pp
+Compatibility mode performs less strict checks of numeric arguments:
+.Bl -bullet
+.It
+An empty operand string is interpreted as 0.
+.El
+.Bl -bullet
+.It
+Leading white space and/or a plus sign before an otherwise valid positive
+numberic operand are allowed and will be ignored.
+.El
+.Pp
+The extended arithmetic range and overflow checks do not conflict with
+POSIX's requirement that arithmetic be done using signed longs, since
+they only make a difference to the result in cases where using signed
+longs would give undefined behavior.
+.Pp
+According to the standard, the use of string arguments
+.Va length ,
+.Va substr ,
+.Va index ,
+or
+.Va match
+produces unspecified results. In this version of
+.Nm ,
+these arguments are treated just as their respective string values.
+.Pp
 The
 .Fl e
 flag is an extension.
-------------- next part --------------
--- expr.y.ORIG	2011-07-01 20:22:29.119607269 +0200
+++ expr.y.NEW1	2011-07-04 17:04:36.120967527 +0200
@@ -42,13 +42,15 @@
 
 struct val *result;
 
+void		assert_to_integer(struct val *);
 int		chk_div(intmax_t, intmax_t);
 int		chk_minus(intmax_t, intmax_t, intmax_t);
 int		chk_plus(intmax_t, intmax_t, intmax_t);
 int		chk_times(intmax_t, intmax_t, intmax_t);
 void		free_value(struct val *);
-int		is_zero_or_null(struct val *);
+int		is_integer(const char *);
 int		isstring(struct val *);
+int		is_zero_or_null(struct val *);
 struct val	*make_integer(intmax_t);
 struct val	*make_str(const char *);
 struct val	*op_and(struct val *, struct val *);
@@ -65,13 +67,13 @@
 struct val	*op_plus(struct val *, struct val *);
 struct val	*op_rem(struct val *, struct val *);
 struct val	*op_times(struct val *, struct val *);
-intmax_t	to_integer(struct val *);
+int		to_integer(struct val *);
 void		to_string(struct val *);
 int		yyerror(const char *);
 int		yylex(void);
 int		yyparse(void);
 
-static int	eflag;
+static int	nonposix;
 char **av;
 %}
 
@@ -134,37 +136,16 @@
 make_str(const char *s)
 {
 	struct val *vp;
-	char *ep;
 
 	vp = (struct val *) malloc (sizeof (*vp));
 	if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
 		errx(ERR_EXIT, "malloc() failed");
 	}
 
-	/*
-	 * Previously we tried to scan the string to see if it ``looked like''
-	 * an integer (erroneously, as it happened).  Let strtoimax() do the
-	 * dirty work.  We could cache the value, except that we are using
-	 * a union and need to preserve the original string form until we
-	 * are certain that it is not needed.
-	 *
-	 * IEEE Std.1003.1-2001 says:
-	 * /integer/ An argument consisting only of an (optional) unary minus  
-	 *	     followed by digits.          
-	 *
-	 * This means that arguments which consist of digits followed by
-	 * non-digits MUST NOT be considered integers.  strtoimax() will
-	 * figure this out for us.
-	 */
-	if (eflag)
-		(void)strtoimax(s, &ep, 10);
+	if (is_integer(s))
+		vp->type = numeric_string;
 	else
-		(void)strtol(s, &ep, 10);
-
-	if (*ep != '\0')
 		vp->type = string;
-	else	
-		vp->type = numeric_string;
 
 	return vp;
 }
@@ -178,31 +159,33 @@
 }
 
 
-intmax_t
+int
 to_integer(struct val *vp)
 {
 	intmax_t i;
 
-	if (vp->type == integer)
-		return 1;
-
-	if (vp->type == string)
-		return 0;
-
-	/* vp->type == numeric_string, make it numeric */
-	errno = 0;
-	if (eflag) {
+	/* we can only convert numeric_string to integer, here */
+	if (vp->type == numeric_string) {
+		errno = 0;
 		i  = strtoimax(vp->u.s, (char **)NULL, 10);
-		if (errno == ERANGE)
-			err(ERR_EXIT, NULL);
-	} else {
-		i = strtol(vp->u.s, (char **)NULL, 10);
+		/* just keep as numeric_string, if the conversion fails */
+		if (errno != ERANGE) {
+			free (vp->u.s);
+			vp->u.i = i;
+			vp->type = integer;
+		}
 	}
+	return (vp->type == integer);
+}
 
-	free (vp->u.s);
-	vp->u.i = i;
-	vp->type = integer;
-	return 1;
+
+void
+assert_to_integer(struct val *vp)
+{
+	if (vp->type == string)
+		errx(ERR_EXIT, "not a decimal number: '%s'", vp->u.s);
+	if (!to_integer(vp))
+		errx(ERR_EXIT, "operand too large: '%s'", vp->u.s);
 }
 
 void
@@ -230,6 +213,25 @@
 
 
 int
+is_integer(const char *s)
+{
+	if (nonposix) {
+		if (*s == '\0')
+			return (1);
+		while (isspace((unsigned char)*s))
+			s++;
+	}
+	if (*s == '-' || (nonposix && *s == '+'))
+		s++;
+	if (*s == '\0')
+		return (0);
+	while (isdigit((unsigned char)*s))
+		s++;
+	return (*s == '\0');
+}
+
+
+int
 isstring(struct val *vp)
 {
 	/* only TRUE if this string is not a valid integer */
@@ -282,12 +284,12 @@
 	if (getenv("EXPR_COMPAT") != NULL
 	    || check_utility_compat("expr")) {
 		av = argv + 1;
-		eflag = 1;
+		nonposix = 1;
 	} else {
 		while ((c = getopt(argc, argv, "e")) != -1)
 			switch (c) {
 			case 'e':
-				eflag = 1;
+				nonposix = 1;
 				break;
 
 			default:
@@ -318,15 +320,17 @@
 struct val *
 op_or(struct val *a, struct val *b)
 {
-	if (is_zero_or_null (a)) {
-		free_value (a);
-		return (b);
-	} else {
-		free_value (b);
+	if (!is_zero_or_null(a)) {
+		free_value(b);
 		return (a);
 	}
+	free_value(a);
+	if (!is_zero_or_null(b))
+		return (b);
+	free_value(b);
+	return (make_integer((intmax_t)0));
 }
-		
+
 struct val *
 op_and(struct val *a, struct val *b)
 {
@@ -350,8 +354,8 @@
 		to_string (b);	
 		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) == 0));
 	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
+		assert_to_integer(a);
+		assert_to_integer(b);
 		r = make_integer ((intmax_t)(a->u.i == b->u.i));
 	}
 
@@ -370,8 +374,8 @@
 		to_string (b);
 		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) > 0));
 	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
+		assert_to_integer(a);
+		assert_to_integer(b);
 		r = make_integer ((intmax_t)(a->u.i > b->u.i));
 	}
 
@@ -390,8 +394,8 @@
 		to_string (b);
 		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) < 0));
 	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
+		assert_to_integer(a);
+		assert_to_integer(b);
 		r = make_integer ((intmax_t)(a->u.i < b->u.i));
 	}
 
@@ -410,8 +414,8 @@
 		to_string (b);
 		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) >= 0));
 	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
+		assert_to_integer(a);
+		assert_to_integer(b);
 		r = make_integer ((intmax_t)(a->u.i >= b->u.i));
 	}
 
@@ -430,8 +434,8 @@
 		to_string (b);
 		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) <= 0));
 	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
+		assert_to_integer(a);
+		assert_to_integer(b);
 		r = make_integer ((intmax_t)(a->u.i <= b->u.i));
 	}
 
@@ -450,8 +454,8 @@
 		to_string (b);
 		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) != 0));
 	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
+		assert_to_integer(a);
+		assert_to_integer(b);
 		r = make_integer ((intmax_t)(a->u.i != b->u.i));
 	}
 
@@ -479,17 +483,13 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	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");
+	}
 
 	free_value (a);
 	free_value (b);
@@ -516,17 +516,13 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	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");
+	}
 
 	free_value (a);
 	free_value (b);
@@ -550,17 +546,13 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	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");
+	}
 
 	free_value (a);
 	free_value (b);
@@ -583,21 +575,16 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
 	if (b->u.i == 0) {
 		errx(ERR_EXIT, "division by zero");
 	}
-
-	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);
+	if (chk_div(a->u.i, b->u.i)) {
+		errx(ERR_EXIT, "overflow");
+	}
+	r = make_integer(a->u.i / b->u.i);
 
 	free_value (a);
 	free_value (b);
@@ -609,19 +596,12 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
+	assert_to_integer(a);
+	assert_to_integer(b);
 	if (b->u.i == 0) {
 		errx(ERR_EXIT, "division by zero");
 	}
-
-	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);
+	r = make_integer(a->u.i % b->u.i);
 
 	free_value (a);
 	free_value (b);
-------------- next part --------------
--- expr.y.ORIG	2011-07-01 20:22:29.119607269 +0200
+++ expr.y.NEW2	2011-07-04 17:04:54.749968250 +0200
@@ -40,15 +40,20 @@
 	} u;
 } ;
 
+char		**av;
+int		nonposix;
 struct val *result;
 
-int		chk_div(intmax_t, intmax_t);
-int		chk_minus(intmax_t, intmax_t, intmax_t);
-int		chk_plus(intmax_t, intmax_t, intmax_t);
-int		chk_times(intmax_t, intmax_t, intmax_t);
+void		assert_to_integer(struct val *);
+void		assert_div(intmax_t, intmax_t);
+void		assert_minus(intmax_t, intmax_t, intmax_t);
+void		assert_plus(intmax_t, intmax_t, intmax_t);
+void		assert_times(intmax_t, intmax_t, intmax_t);
+int		compare_vals(struct val *, struct val *);
 void		free_value(struct val *);
-int		is_zero_or_null(struct val *);
+int		is_integer(const char *);
 int		isstring(struct val *);
+int		is_zero_or_null(struct val *);
 struct val	*make_integer(intmax_t);
 struct val	*make_str(const char *);
 struct val	*op_and(struct val *, struct val *);
@@ -65,14 +70,12 @@
 struct val	*op_plus(struct val *, struct val *);
 struct val	*op_rem(struct val *, struct val *);
 struct val	*op_times(struct val *, struct val *);
-intmax_t	to_integer(struct val *);
+int		to_integer(struct val *);
 void		to_string(struct val *);
 int		yyerror(const char *);
 int		yylex(void);
 int		yyparse(void);
 
-static int	eflag;
-char **av;
 %}
 
 %union
@@ -134,37 +137,16 @@
 make_str(const char *s)
 {
 	struct val *vp;
-	char *ep;
 
 	vp = (struct val *) malloc (sizeof (*vp));
 	if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
 		errx(ERR_EXIT, "malloc() failed");
 	}
 
-	/*
-	 * Previously we tried to scan the string to see if it ``looked like''
-	 * an integer (erroneously, as it happened).  Let strtoimax() do the
-	 * dirty work.  We could cache the value, except that we are using
-	 * a union and need to preserve the original string form until we
-	 * are certain that it is not needed.
-	 *
-	 * IEEE Std.1003.1-2001 says:
-	 * /integer/ An argument consisting only of an (optional) unary minus  
-	 *	     followed by digits.          
-	 *
-	 * This means that arguments which consist of digits followed by
-	 * non-digits MUST NOT be considered integers.  strtoimax() will
-	 * figure this out for us.
-	 */
-	if (eflag)
-		(void)strtoimax(s, &ep, 10);
+	if (is_integer(s))
+		vp->type = numeric_string;
 	else
-		(void)strtol(s, &ep, 10);
-
-	if (*ep != '\0')
 		vp->type = string;
-	else	
-		vp->type = numeric_string;
 
 	return vp;
 }
@@ -178,31 +160,33 @@
 }
 
 
-intmax_t
+int
 to_integer(struct val *vp)
 {
 	intmax_t i;
 
-	if (vp->type == integer)
-		return 1;
-
-	if (vp->type == string)
-		return 0;
-
-	/* vp->type == numeric_string, make it numeric */
-	errno = 0;
-	if (eflag) {
+	/* we can only convert numeric_string to integer, here */
+	if (vp->type == numeric_string) {
+		errno = 0;
 		i  = strtoimax(vp->u.s, (char **)NULL, 10);
-		if (errno == ERANGE)
-			err(ERR_EXIT, NULL);
-	} else {
-		i = strtol(vp->u.s, (char **)NULL, 10);
+		/* just keep as numeric_string, if the conversion fails */
+		if (errno != ERANGE) {
+			free (vp->u.s);
+			vp->u.i = i;
+			vp->type = integer;
+		}
 	}
+	return (vp->type == integer);
+}
 
-	free (vp->u.s);
-	vp->u.i = i;
-	vp->type = integer;
-	return 1;
+
+void
+assert_to_integer(struct val *vp)
+{
+	if (vp->type == string)
+		errx(ERR_EXIT, "not a decimal number: '%s'", vp->u.s);
+	if (!to_integer(vp))
+		errx(ERR_EXIT, "operand too large: '%s'", vp->u.s);
 }
 
 void
@@ -230,6 +214,25 @@
 
 
 int
+is_integer(const char *s)
+{
+	if (nonposix) {
+		if (*s == '\0')
+			return (1);
+		while (isspace((unsigned char)*s))
+			s++;
+	}
+	if (*s == '-' || (nonposix && *s == '+'))
+		s++;
+	if (*s == '\0')
+		return (0);
+	while (isdigit((unsigned char)*s))
+		s++;
+	return (*s == '\0');
+}
+
+
+int
 isstring(struct val *vp)
 {
 	/* only TRUE if this string is not a valid integer */
@@ -282,12 +285,12 @@
 	if (getenv("EXPR_COMPAT") != NULL
 	    || check_utility_compat("expr")) {
 		av = argv + 1;
-		eflag = 1;
+		nonposix = 1;
 	} else {
 		while ((c = getopt(argc, argv, "e")) != -1)
 			switch (c) {
 			case 'e':
-				eflag = 1;
+				nonposix = 1;
 				break;
 
 			default:
@@ -318,15 +321,17 @@
 struct val *
 op_or(struct val *a, struct val *b)
 {
-	if (is_zero_or_null (a)) {
-		free_value (a);
-		return (b);
-	} else {
-		free_value (b);
+	if (!is_zero_or_null(a)) {
+		free_value(b);
 		return (a);
 	}
+	free_value(a);
+	if (!is_zero_or_null(b))
+		return (b);
+	free_value(b);
+	return (make_integer((intmax_t)0));
 }
-		
+
 struct val *
 op_and(struct val *a, struct val *b)
 {
@@ -340,138 +345,77 @@
 	}
 }
 
-struct val *
-op_eq(struct val *a, struct val *b)
+int
+compare_vals(struct val *a, struct val *b)
 {
-	struct val *r;
+	int r;
 
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);	
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) == 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i == b->u.i));
+	if (is_string(a) || is_string(b)) {
+		to_string(a);
+		to_string(b);
+		r = strcoll(a->u.s, b->u.s);
+	} else {
+		assert_to_integer(a);
+		assert_to_integer(b);
+		if (a->u.i > b->u.i)
+			r = 1;
+		else if (a->u.i < b->u.i)
+			r = -1;
+		else
+			r = 0;
 	}
 
-	free_value (a);
-	free_value (b);
-	return r;
+	free_value(a);
+	free_value(b);
+	return (r);
 }
 
 struct val *
-op_gt(struct val *a, struct val *b)
+op_eq(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) > 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i > b->u.i));
-	}
+	return (make_integer((intmax_t)(compare_vals(a, b) == 0)));
+}
 
-	free_value (a);
-	free_value (b);
-	return r;
+struct val *
+op_gt(struct val *a, struct val *b)
+{
+	return (make_integer((intmax_t)(compare_vals(a, b) > 0)));
 }
 
 struct val *
 op_lt(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) < 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i < b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) < 0)));
 }
 
 struct val *
 op_ge(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) >= 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i >= b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) >= 0)));
 }
 
 struct val *
 op_le(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) <= 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i <= b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) <= 0)));
 }
 
 struct val *
 op_ne(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) != 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i != b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) != 0)));
 }
 
-int
-chk_plus(intmax_t a, intmax_t b, intmax_t r)
+void
+assert_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;
-	/* sum of two negative numbers must be negative */
-	if (a < 0 && b < 0 && r >= 0)
-		return 1;
-	/* all other cases are OK */
-	return 0;
+	/*
+	 * sum of two positive numbers must be positive,
+	 * sum of two negative numbers must be negative
+	 */
+	if ((a > 0 && b > 0 && r <= 0) ||
+	    (a < 0 && b < 0 && r >= 0))
+		errx(ERR_EXIT, "overflow");
 }
 
 struct val *
@@ -479,36 +423,26 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	r = make_integer(a->u.i + b->u.i);
+	assert_plus(a->u.i, b->u.i, r->u.i);
 
 	free_value (a);
 	free_value (b);
 	return r;
 }
 
-int
-chk_minus(intmax_t a, intmax_t b, intmax_t r)
+void
+assert_minus(intmax_t a, intmax_t b, intmax_t r)
 {
 
 	/* special case subtraction of INTMAX_MIN */
-	if (b == INTMAX_MIN) {
-		if (a >= 0)
-			return 1;
-		else
-			return 0;
-	}
-	/* this is allowed for b != INTMAX_MIN */
-	return chk_plus (a, -b, r);
+	if (b == INTMAX_MIN && a < 0)
+		errx(ERR_EXIT, "overflow");
+	/* check addition of negative subtrahend */
+	assert_plus(a, -b, r);
 }
 
 struct val *
@@ -516,33 +450,26 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	r = make_integer(a->u.i - b->u.i);
+	assert_minus(a->u.i, b->u.i, r->u.i);
 
 	free_value (a);
 	free_value (b);
 	return r;
 }
 
-int
-chk_times(intmax_t a, intmax_t b, intmax_t r)
+void
+assert_times(intmax_t a, intmax_t b, intmax_t r)
 {
-	/* special case: first operand is 0, no overflow possible */
-	if (a == 0)
-		return 0;
-	/* verify that result of division matches second operand */
-	if (r / a != b)
-		return 1;
-	return 0;
+	/*
+	 * if first operand is 0, no overflow is possible,
+	 * else result of division test must match second operand
+	 */
+	if (a != 0 && r / a != b)
+		errx(ERR_EXIT, "overflow");
 }
 
 struct val *
@@ -550,32 +477,25 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	r = make_integer(a->u.i * b->u.i);
+	assert_times(a->u.i, b->u.i, r->u.i);
 
 	free_value (a);
 	free_value (b);
 	return (r);
 }
 
-int
-chk_div(intmax_t a, intmax_t b)
+void
+assert_div(intmax_t a, intmax_t b)
 {
-	/* div by zero has been taken care of before */
+	if (b == 0)
+		errx(ERR_EXIT, "division by zero");
 	/* only INTMAX_MIN / -1 causes overflow */
 	if (a == INTMAX_MIN && b == -1)
-		return 1;
-	/* everything else is OK */
-	return 0;
+		errx(ERR_EXIT, "overflow");
 }
 
 struct val *
@@ -583,21 +503,12 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	if (b->u.i == 0) {
-		errx(ERR_EXIT, "division by zero");
-	}
+	assert_to_integer(a);
+	assert_to_integer(b);
 
-	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);
+	/* assert based on operands only, not on result */
+	assert_div(a->u.i, b->u.i);
+	r = make_integer(a->u.i / b->u.i);
 
 	free_value (a);
 	free_value (b);
@@ -609,19 +520,11 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	if (b->u.i == 0) {
-		errx(ERR_EXIT, "division by zero");
-	}
-
-	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);
+	assert_to_integer(a);
+	assert_to_integer(b);
+	/* pass a=1 to only check for div by zero */
+	assert_div(1, b->u.i);
+	r = make_integer(a->u.i % b->u.i);
 
 	free_value (a);
 	free_value (b);
-------------- next part --------------
--- expr.y.ORIG	2011-07-01 20:22:29.119607269 +0200
+++ expr.y.FINAL	2011-07-04 17:04:08.677968066 +0200
@@ -1,6 +1,6 @@
 %{
 /*-
- * Written by Pace Willisson (pace at blitz.com) 
+ * Written by Pace Willisson (pace at blitz.com)
  * and placed in the public domain.
  *
  * Largely rewritten by J.T. Conklin (jtc at wimsey.com)
@@ -21,7 +21,7 @@
 #include <string.h>
 #include <regex.h>
 #include <unistd.h>
-  
+
 /*
  * POSIX specifies a specific error code for syntax errors.  We exit
  * with this code for all errors.
@@ -40,15 +40,20 @@
 	} u;
 } ;
 
-struct val *result;
-
-int		chk_div(intmax_t, intmax_t);
-int		chk_minus(intmax_t, intmax_t, intmax_t);
-int		chk_plus(intmax_t, intmax_t, intmax_t);
-int		chk_times(intmax_t, intmax_t, intmax_t);
+char		**av;
+int		nonposix;
+struct val	*result;
+
+void		assert_to_integer(struct val *);
+void		assert_div(intmax_t, intmax_t);
+void		assert_minus(intmax_t, intmax_t, intmax_t);
+void		assert_plus(intmax_t, intmax_t, intmax_t);
+void		assert_times(intmax_t, intmax_t, intmax_t);
+int		compare_vals(struct val *, struct val *);
 void		free_value(struct val *);
+int		is_integer(const char *);
+int		is_string(struct val *);
 int		is_zero_or_null(struct val *);
-int		isstring(struct val *);
 struct val	*make_integer(intmax_t);
 struct val	*make_str(const char *);
 struct val	*op_and(struct val *, struct val *);
@@ -65,14 +70,12 @@
 struct val	*op_plus(struct val *, struct val *);
 struct val	*op_rem(struct val *, struct val *);
 struct val	*op_times(struct val *, struct val *);
-intmax_t	to_integer(struct val *);
+int		to_integer(struct val *);
 void		to_string(struct val *);
 int		yyerror(const char *);
 int		yylex(void);
 int		yyparse(void);
 
-static int	eflag;
-char **av;
 %}
 
 %union
@@ -96,23 +99,22 @@
 
 expr:	TOKEN
 	| '(' expr ')' { $$ = $2; }
-	| expr '|' expr { $$ = op_or ($1, $3); }
-	| expr '&' expr { $$ = op_and ($1, $3); }
-	| expr '=' expr { $$ = op_eq ($1, $3); }
-	| expr '>' expr { $$ = op_gt ($1, $3); }
-	| expr '<' expr { $$ = op_lt ($1, $3); }
-	| expr GE expr  { $$ = op_ge ($1, $3); }
-	| expr LE expr  { $$ = op_le ($1, $3); }
-	| expr NE expr  { $$ = op_ne ($1, $3); }
-	| expr '+' expr { $$ = op_plus ($1, $3); }
-	| expr '-' expr { $$ = op_minus ($1, $3); }
-	| expr '*' expr { $$ = op_times ($1, $3); }
-	| expr '/' expr { $$ = op_div ($1, $3); }
-	| expr '%' expr { $$ = op_rem ($1, $3); }
-	| expr ':' expr { $$ = op_colon ($1, $3); }
+	| expr '|' expr { $$ = op_or($1, $3); }
+	| expr '&' expr { $$ = op_and($1, $3); }
+	| expr '=' expr { $$ = op_eq($1, $3); }
+	| expr '>' expr { $$ = op_gt($1, $3); }
+	| expr '<' expr { $$ = op_lt($1, $3); }
+	| expr GE expr  { $$ = op_ge($1, $3); }
+	| expr LE expr  { $$ = op_le($1, $3); }
+	| expr NE expr  { $$ = op_ne($1, $3); }
+	| expr '+' expr { $$ = op_plus($1, $3); }
+	| expr '-' expr { $$ = op_minus($1, $3); }
+	| expr '*' expr { $$ = op_times($1, $3); }
+	| expr '/' expr { $$ = op_div($1, $3); }
+	| expr '%' expr { $$ = op_rem($1, $3); }
+	| expr ':' expr { $$ = op_colon($1, $3); }
 	;
 
-
 %%
 
 struct val *
@@ -120,89 +122,65 @@
 {
 	struct val *vp;
 
-	vp = (struct val *) malloc (sizeof (*vp));
-	if (vp == NULL) {
+	vp = (struct val *)malloc(sizeof(*vp));
+	if (vp == NULL)
 		errx(ERR_EXIT, "malloc() failed");
-	}
 
 	vp->type = integer;
 	vp->u.i  = i;
-	return vp; 
+	return (vp);
 }
 
 struct val *
 make_str(const char *s)
 {
 	struct val *vp;
-	char *ep;
 
-	vp = (struct val *) malloc (sizeof (*vp));
-	if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
+	vp = (struct val *)malloc(sizeof(*vp));
+	if (vp == NULL || ((vp->u.s = strdup(s)) == NULL))
 		errx(ERR_EXIT, "malloc() failed");
-	}
 
-	/*
-	 * Previously we tried to scan the string to see if it ``looked like''
-	 * an integer (erroneously, as it happened).  Let strtoimax() do the
-	 * dirty work.  We could cache the value, except that we are using
-	 * a union and need to preserve the original string form until we
-	 * are certain that it is not needed.
-	 *
-	 * IEEE Std.1003.1-2001 says:
-	 * /integer/ An argument consisting only of an (optional) unary minus  
-	 *	     followed by digits.          
-	 *
-	 * This means that arguments which consist of digits followed by
-	 * non-digits MUST NOT be considered integers.  strtoimax() will
-	 * figure this out for us.
-	 */
-	if (eflag)
-		(void)strtoimax(s, &ep, 10);
+	if (is_integer(s))
+		vp->type = numeric_string;
 	else
-		(void)strtol(s, &ep, 10);
-
-	if (*ep != '\0')
 		vp->type = string;
-	else	
-		vp->type = numeric_string;
 
-	return vp;
+	return (vp);
 }
 
-
 void
 free_value(struct val *vp)
 {
 	if (vp->type == string || vp->type == numeric_string)
-		free (vp->u.s);	
+		free(vp->u.s);
 }
 
-
-intmax_t
+int
 to_integer(struct val *vp)
 {
 	intmax_t i;
 
-	if (vp->type == integer)
-		return 1;
-
-	if (vp->type == string)
-		return 0;
-
-	/* vp->type == numeric_string, make it numeric */
-	errno = 0;
-	if (eflag) {
+	/* we can only convert numeric_string to integer, here */
+	if (vp->type == numeric_string) {
+		errno = 0;
 		i  = strtoimax(vp->u.s, (char **)NULL, 10);
-		if (errno == ERANGE)
-			err(ERR_EXIT, NULL);
-	} else {
-		i = strtol(vp->u.s, (char **)NULL, 10);
+		/* just keep as numeric_string, if the conversion fails */
+		if (errno != ERANGE) {
+			free(vp->u.s);
+			vp->u.i = i;
+			vp->type = integer;
+		}
 	}
+	return (vp->type == integer);
+}
 
-	free (vp->u.s);
-	vp->u.i = i;
-	vp->type = integer;
-	return 1;
+void
+assert_to_integer(struct val *vp)
+{
+	if (vp->type == string)
+		errx(ERR_EXIT, "not a decimal number: '%s'", vp->u.s);
+	if (!to_integer(vp))
+		errx(ERR_EXIT, "operand too large: '%s'", vp->u.s);
 }
 
 void
@@ -228,15 +206,31 @@
 	vp->u.s  = tmp;
 }
 
+int
+is_integer(const char *s)
+{
+	if (nonposix) {
+		if (*s == '\0')
+			return (1);
+		while (isspace((unsigned char)*s))
+			s++;
+	}
+	if (*s == '-' || (nonposix && *s == '+'))
+		s++;
+	if (*s == '\0')
+		return (0);
+	while (isdigit((unsigned char)*s))
+		s++;
+	return (*s == '\0');
+}
 
 int
-isstring(struct val *vp)
+is_string(struct val *vp)
 {
 	/* only TRUE if this string is not a valid integer */
 	return (vp->type == string);
 }
 
-
 int
 yylex(void)
 {
@@ -247,10 +241,10 @@
 
 	p = *av++;
 
-	if (strlen (p) == 1) {
-		if (strchr ("|&=<>+-*/%:()", *p))
+	if (strlen(p) == 1) {
+		if (strchr("|&=<>+-*/%:()", *p))
 			return (*p);
-	} else if (strlen (p) == 2 && p[1] == '=') {
+	} else if (strlen(p) == 2 && p[1] == '=') {
 		switch (*p) {
 		case '>': return (GE);
 		case '<': return (LE);
@@ -258,19 +252,17 @@
 		}
 	}
 
-	yylval.val = make_str (p);
+	yylval.val = make_str(p);
 	return (TOKEN);
 }
 
 int
 is_zero_or_null(struct val *vp)
 {
-	if (vp->type == integer) {
+	if (vp->type == integer)
 		return (vp->u.i == 0);
-	} else {
-		return (*vp->u.s == 0 || (to_integer (vp) && vp->u.i == 0));
-	}
-	/* NOTREACHED */
+
+	return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0));
 }
 
 int
@@ -278,23 +270,22 @@
 {
 	int c;
 
-	setlocale (LC_ALL, "");
+	setlocale(LC_ALL, "");
 	if (getenv("EXPR_COMPAT") != NULL
 	    || check_utility_compat("expr")) {
 		av = argv + 1;
-		eflag = 1;
+		nonposix = 1;
 	} else {
-		while ((c = getopt(argc, argv, "e")) != -1)
+		while ((c = getopt(argc, argv, "e")) != -1) {
 			switch (c) {
 			case 'e':
-				eflag = 1;
+				nonposix = 1;
 				break;
-
 			default:
-				fprintf(stderr,
+				errx(ERR_EXIT,
 				    "usage: expr [-e] expression\n");
-				exit(ERR_EXIT);
 			}
+		}
 		av = argv + optind;
 	}
 
@@ -314,164 +305,104 @@
 	errx(ERR_EXIT, "syntax error");
 }
 
-
 struct val *
 op_or(struct val *a, struct val *b)
 {
-	if (is_zero_or_null (a)) {
-		free_value (a);
-		return (b);
-	} else {
-		free_value (b);
+	if (!is_zero_or_null(a)) {
+		free_value(b);
 		return (a);
 	}
+	free_value(a);
+	if (!is_zero_or_null(b))
+		return (b);
+	free_value(b);
+	return (make_integer((intmax_t)0));
 }
-		
+
 struct val *
 op_and(struct val *a, struct val *b)
 {
-	if (is_zero_or_null (a) || is_zero_or_null (b)) {
-		free_value (a);
-		free_value (b);
-		return (make_integer ((intmax_t)0));
+	if (is_zero_or_null(a) || is_zero_or_null(b)) {
+		free_value(a);
+		free_value(b);
+		return (make_integer((intmax_t)0));
 	} else {
-		free_value (b);
+		free_value(b);
 		return (a);
 	}
 }
 
-struct val *
-op_eq(struct val *a, struct val *b)
+int
+compare_vals(struct val *a, struct val *b)
 {
-	struct val *r;
+	int r;
 
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);	
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) == 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i == b->u.i));
+	if (is_string(a) || is_string(b)) {
+		to_string(a);
+		to_string(b);
+		r = strcoll(a->u.s, b->u.s);
+	} else {
+		assert_to_integer(a);
+		assert_to_integer(b);
+		if (a->u.i > b->u.i)
+			r = 1;
+		else if (a->u.i < b->u.i)
+			r = -1;
+		else
+			r = 0;
 	}
 
-	free_value (a);
-	free_value (b);
-	return r;
+	free_value(a);
+	free_value(b);
+	return (r);
 }
 
 struct val *
-op_gt(struct val *a, struct val *b)
+op_eq(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) > 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i > b->u.i));
-	}
+	return (make_integer((intmax_t)(compare_vals(a, b) == 0)));
+}
 
-	free_value (a);
-	free_value (b);
-	return r;
+struct val *
+op_gt(struct val *a, struct val *b)
+{
+	return (make_integer((intmax_t)(compare_vals(a, b) > 0)));
 }
 
 struct val *
 op_lt(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) < 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i < b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) < 0)));
 }
 
 struct val *
 op_ge(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) >= 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i >= b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) >= 0)));
 }
 
 struct val *
 op_le(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) <= 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i <= b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) <= 0)));
 }
 
 struct val *
 op_ne(struct val *a, struct val *b)
 {
-	struct val *r;
-
-	if (isstring (a) || isstring (b)) {
-		to_string (a);
-		to_string (b);
-		r = make_integer ((intmax_t)(strcoll (a->u.s, b->u.s) != 0));
-	} else {
-		(void)to_integer(a);
-		(void)to_integer(b);
-		r = make_integer ((intmax_t)(a->u.i != b->u.i));
-	}
-
-	free_value (a);
-	free_value (b);
-	return r;
+	return (make_integer((intmax_t)(compare_vals(a, b) != 0)));
 }
 
-int
-chk_plus(intmax_t a, intmax_t b, intmax_t r)
+void
+assert_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;
-	/* sum of two negative numbers must be negative */
-	if (a < 0 && b < 0 && r >= 0)
-		return 1;
-	/* all other cases are OK */
-	return 0;
+	/*
+	 * sum of two positive numbers must be positive,
+	 * sum of two negative numbers must be negative
+	 */
+	if ((a > 0 && b > 0 && r <= 0) ||
+	    (a < 0 && b < 0 && r >= 0))
+		errx(ERR_EXIT, "overflow");
 }
 
 struct val *
@@ -479,36 +410,24 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	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);
+	assert_to_integer(a);
+	assert_to_integer(b);
+	r = make_integer(a->u.i + b->u.i);
+	assert_plus(a->u.i, b->u.i, r->u.i);
 
-	free_value (a);
-	free_value (b);
-	return r;
+	free_value(a);
+	free_value(b);
+	return (r);
 }
 
-int
-chk_minus(intmax_t a, intmax_t b, intmax_t r)
+void
+assert_minus(intmax_t a, intmax_t b, intmax_t r)
 {
-
 	/* special case subtraction of INTMAX_MIN */
-	if (b == INTMAX_MIN) {
-		if (a >= 0)
-			return 1;
-		else
-			return 0;
-	}
-	/* this is allowed for b != INTMAX_MIN */
-	return chk_plus (a, -b, r);
+	if (b == INTMAX_MIN && a < 0)
+		errx(ERR_EXIT, "overflow");
+	/* check addition of negative subtrahend */
+	assert_plus(a, -b, r);
 }
 
 struct val *
@@ -516,33 +435,25 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	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);
+	assert_to_integer(a);
+	assert_to_integer(b);
+	r = make_integer(a->u.i - b->u.i);
+	assert_minus(a->u.i, b->u.i, r->u.i);
 
-	free_value (a);
-	free_value (b);
-	return r;
+	free_value(a);
+	free_value(b);
+	return (r);
 }
 
-int
-chk_times(intmax_t a, intmax_t b, intmax_t r)
+void
+assert_times(intmax_t a, intmax_t b, intmax_t r)
 {
-	/* special case: first operand is 0, no overflow possible */
-	if (a == 0)
-		return 0;
-	/* verify that result of division matches second operand */
-	if (r / a != b)
-		return 1;
-	return 0;
+	/*
+	 * if first operand is 0, no overflow is possible,
+	 * else result of division test must match second operand
+	 */
+	if (a != 0 && r / a != b)
+		errx(ERR_EXIT, "overflow");
 }
 
 struct val *
@@ -550,32 +461,24 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	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);
+	assert_to_integer(a);
+	assert_to_integer(b);
+	r = make_integer(a->u.i * b->u.i);
+	assert_times(a->u.i, b->u.i, r->u.i);
 
-	free_value (a);
-	free_value (b);
+	free_value(a);
+	free_value(b);
 	return (r);
 }
 
-int
-chk_div(intmax_t a, intmax_t b)
+void
+assert_div(intmax_t a, intmax_t b)
 {
-	/* div by zero has been taken care of before */
+	if (b == 0)
+		errx(ERR_EXIT, "division by zero");
 	/* only INTMAX_MIN / -1 causes overflow */
 	if (a == INTMAX_MIN && b == -1)
-		return 1;
-	/* everything else is OK */
-	return 0;
+		errx(ERR_EXIT, "overflow");
 }
 
 struct val *
@@ -583,51 +486,33 @@
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	if (b->u.i == 0) {
-		errx(ERR_EXIT, "division by zero");
-	}
-
-	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);
+	assert_to_integer(a);
+	assert_to_integer(b);
+	/* assert based on operands only, not on result */
+	assert_div(a->u.i, b->u.i);
+	r = make_integer(a->u.i / b->u.i);
 
-	free_value (a);
-	free_value (b);
-	return r;
+	free_value(a);
+	free_value(b);
+	return (r);
 }
-	
+
 struct val *
 op_rem(struct val *a, struct val *b)
 {
 	struct val *r;
 
-	if (!to_integer(a) || !to_integer(b)) {
-		errx(ERR_EXIT, "non-numeric argument");
-	}
-
-	if (b->u.i == 0) {
-		errx(ERR_EXIT, "division by zero");
-	}
-
-	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);
+	assert_to_integer(a);
+	assert_to_integer(b);
+	/* pass a=1 to only check for div by zero */
+	assert_div(1, b->u.i);
+	r = make_integer(a->u.i % b->u.i);
 
-	free_value (a);
-	free_value (b);
-	return r;
+	free_value(a);
+	free_value(b);
+	return (r);
 }
-	
+
 struct val *
 op_colon(struct val *a, struct val *b)
 {
@@ -642,33 +527,30 @@
 	to_string(b);
 
 	/* compile regular expression */
-	if ((eval = regcomp (&rp, b->u.s, 0)) != 0) {
-		regerror (eval, &rp, errbuf, sizeof(errbuf));
+	if ((eval = regcomp(&rp, b->u.s, 0)) != 0) {
+		regerror(eval, &rp, errbuf, sizeof(errbuf));
 		errx(ERR_EXIT, "%s", errbuf);
 	}
 
 	/* compare string against pattern */
 	/* remember that patterns are anchored to the beginning of the line */
-	if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
+	if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0)
 		if (rm[1].rm_so >= 0) {
 			*(a->u.s + rm[1].rm_eo) = '\0';
-			v = make_str (a->u.s + rm[1].rm_so);
+			v = make_str(a->u.s + rm[1].rm_so);
 
-		} else {
-			v = make_integer ((intmax_t)(rm[0].rm_eo - rm[0].rm_so));
-		}
-	} else {
-		if (rp.re_nsub == 0) {
-			v = make_integer ((intmax_t)0);
-		} else {
-			v = make_str ("");
-		}
-	}
+		} else
+			v = make_integer((intmax_t)(rm[0].rm_eo - rm[0].rm_so));
+	else
+		if (rp.re_nsub == 0)
+			v = make_integer((intmax_t)0);
+		else
+			v = make_str("");
 
 	/* free arguments and pattern buffer */
-	free_value (a);
-	free_value (b);
-	regfree (&rp);
+	free_value(a);
+	free_value(b);
+	regfree(&rp);
 
-	return v;
+	return (v);
 }
-------------- next part --------------
#!/bin/sh

#-
# Copyright (c) 2011 Stefan Esser <se at freebsd.org>
# All rights reserved. 
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

#
# expr.sh - check if test(1) or builtin test works
#
# $FreeBSD:$

# force a specified test program, e.g. `env test=/bin/expr sh regress.sh'
: ${test=expr}

t ()
{
	# $1 -> exit code
	# $2 -> result written to stdout
	# $3 -> $test expression

	count=$((count+1))
	compat=${EXPR_COMPAT:+ (COMPAT)}
	# check for syntax errors
	output="`eval $test $3 2>/dev/null`"
	ret=$?
	if [ "$ret" -ne "$1" ]; then
		printf "not ok %d %s %s  - (got %s, expected %s)%s\n" \
			"$count" "$test" "$3" "$ret" "$1" "$compat"
	elif [ "$output" != "$2" ]; then
		printf "not ok %d %s %s  - (unexpected output, got '%s', expected '%s')%s\n" \
			"$count" "$test" "$3" "$output" "$2" "$compat"
	elif [ "$ret" -ge 2 ]; then
		printf "ok %d %s %s  - is correctly detected as error%s\n" \
			"$count" "$test" "$3" "$compat"
	else
		printf "ok %d %s %s  - is correct result%s\n" \
			"$count" "$test" "$3 = '$output'" "$compat"
	fi
}

# decimal constants (assuming type of "intmax_t" is "signed 64 bit integer")
MAXINT_PLUS1=9223372036854775808
MAXINT=9223372036854775807
MAXINT_MINUS1=9223372036854775806
MAXINT_HALF=4611686018427387903
MININT_HALF=-4611686018427387904
MININT_PLUS2=-9223372036854775806
MININT_PLUS1=-9223372036854775807
MININT=-9223372036854775808
MININT_MINUS1=-9223372036854775809

count=0
echo "1..155"

# EXPR_COMPAT is equivalent to "-e" and "--" being passed as first arguments.
# If EXPR_COMPAT was specified in the environment, "-e" processing would
# break in the following tests.
unset EXPR_COMPAT

# Basic tests for string operands
t 0 ' '				'" "'
t 0 000100000			'000100000'
t 0 00010000000000000000000	'00010000000000000000000'
t 0 xyzzy			'\( xyzzy \)'
t 2 ''				'xyzzy + 0'
t 2 ''				'-'		# syntax error (string == op)
t 2 ''				'--'		# no expression arguments
t 2 ''				'-e'		# no expression arguments
t 2 ''				'-e --'		# no expression arguments
t 0 '--'			'-- --'
t 0 '--'			'-e -- --'

# Simple arithmetic operations
t 0 1		'0 + 1'
t 1 0		'0 + 0'
t 0 2		'1 + 1'
t 0 -2		'-- -1 + -1'
t 1 0		'-- -1 + 1'
t 2 ''		'-'		# syntax error
t 2 ''		'-1'		# syntax error
t 2 ''		'-1 + 1'	# syntax error
t 1 0		'-- -1 + 1'
t 2 ''		'" 1" + 1'	# syntax error
t 2 ''		'"	1" + 1'	# syntax error
t 2 ''		'"1 " + 1'	# syntax error
t 2 ''		'"1	" + 1'	# syntax error
t 2 ''		'"1 1" + 1'	# syntax error
t 2 ''		'"1	1" + 1'	# syntax error
t 0 '1 1'	'"1 1"'		# syntax error
t 2 ''		'- : -'		# "-" is an operator and not a valid "string"!
t 2 ''		'-- - : -'	# "-" is an operator and not a valid "string"!
t 0 2		'-- -- : --'	# perfectly legal, returns length of "--"

# Some tests for more lenient number syntax in compat mode (-e option)
t 2 ''		'-e'		# no expression given
t 0 2		'-e " 1" + 1'	# allow leading white space with -e
t 0 2		'-e "	1" + 1' # allow leading white space with -e
t 0 1		'-e "" + 1'	# empty string is interpreted as 0
t 2 ''		'-e " " + 1'	# white space without digits is not a number

# The following are allowed to fail by POSIX.1, but we don't treat them special
t 0 1	'substr = substr'	# we do not special case "substr"
t 0 1	'length = length'	# we do not special case "length"
t 0 1	'match = match'		# we do not special case "match"
t 0 1	'index = index'		# we do not special case "index"

# Check numeric range and overflow detection
t 0 $MAXINT		'$MAXINT + 0'
t 2 ''			'$MAXINT + 1'
t 0 $MININT		'-- $MININT_PLUS1 - 1'
t 0 $MININT		'-- $MININT \* 1'
t 2 ''			'-- $MININT \* -1'
t 0 $MAXINT		'-- $MININT_PLUS1 \* -1'
t 0 $MAXINT_PLUS1	'$MAXINT_PLUS1'
t 2 ''			'$MAXINT_PLUS1 + 0'
t 0 100001		'000100000 + 1'
t 0 $MAXINT		'-- $MININT_HALF \* -1 - 1 - $MININT_HALF'
t 0 $MININT		'-- $MININT_HALF + $MININT_HALF'
t 0 $MININT		'-- $MININT / 1'
t 2 ''			'-- $MININT / -1'
t 0 $MININT_PLUS1	'$MAXINT / -1'
t 0 $MAXINT_MINUS1	'$MAXINT_HALF \* 2'
t 2 ''			'\( $MAXINT_HALF + 1 \) \* 2'
t 0 $MININT_PLUS2	'$MAXINT_HALF \* -2'
t 0 $MININT		'\( $MAXINT_HALF + 1 \) \* -2'
t 2 ''			'\( $MAXINT_HALF + 2 \) \* -2'
t 0 -7			'-- $MININT_PLUS1 % 10'
t 0 -7			'-- $MININT_PLUS1 % -10'
t 0 7			'$MAXINT % 10'
t 0 7			'$MAXINT % -10'
t 2 ''			'$MININT + $MININT'

# Simple comparison expressions
t 1 0			'2 + 0 "<" 1'
t 0 1			'0 "<" 1 + 2'
t 0 3			'\( 0 "<" 1 \) + 2'

# Comparison operators applied to boundary values
t 0 1			'-- $MININT "<" $MAXINT'
t 2 ''			'-- $MININT "<" $MAXINT + 1'
t 0 1			'-- $MININT "<=" $MAXINT'
t 2 ''			'-- $MININT "<=" $MAXINT + 1'
t 1 0			'-- $MININT ">" $MAXINT'
t 2 ''			'-- $MININT ">" $MAXINT + 1'
t 1 0			'-- $MININT ">=" $MAXINT'
t 2 ''			'-- $MININT ">=" $MAXINT + 1'
t 1 0			'-- $MININT "=" $MAXINT'
t 2 ''			'-- $MININT "=" $MAXINT + 1'
t 0 1			'-- $MININT "!=" $MAXINT'
t 2 ''			'-- $MININT "!=" $MAXINT + 1'

# Pattern match operator
t 0 abc			'123abc123 : ".*\(a[^0-9]*\)"'
t 0 124			'123abc123 : ".*a[^0-9]*\(.*\)" + 1'
t 0 1			'123abc123 : ".*a[^0-9]*\(.*\)" = 123'
t 0 7			'123abc123 : ".*a[^0-9]*" + 1'
t 1 0			'123Abc123 : ".*a[^0-9]*"'
t 0 1			'123Abc123 : ".*a[^0-9]*" + 1'
t 0 1			'123Abc123 : ".*a[^0-9]*" = 0'

# Logical operators
t 0 a			'a "&" b'
t 1 0			'a "&" ""'
t 1 0			'"" "&" b'
t 1 0			'"" "&" ""'
t 1 0			'a "&" 0'
t 1 0			'0 "&" b'
t 1 0			'0 "&" 0'


t 0 a			'a "|" b'
t 0 a			'a "|" ""'
t 0 b			'"" "|" b'
t 1 0			'"" "|" ""'
t 0 a			'a "|" 0'
t 0 b			'0 "|" b'
t 1 0			'0 "|" 0'
t 0 -1			'0 "|" -1'

t 1 0			'"(" 0 "|" 1 ")" "<" "(" 0 "&" 1 ")"'
t 0 1			'"(" a "|" b ")" "=" "(" a "&" b ")"'

# Comparison results depend on arguments being accepted as decimal numbers
t 0 1			'" 2" "<" "2"'
t 0 1			'"2" "<" "11"'
t 1 0			'-- "-2" "<" "-11"'
t 1 0			'" 2" "<" " 11"'
t 1 0			'" -2" "<" " -11"'
t 1 0			'"2 " "<" "11 "'
t 1 0			'-- "-2 " "<" "-11 "'
t 1 0			'" 2 " "<" " 11 "'
t 1 0			'" -2 " "<" " -11 "'
t 1 0			'"+2" "<" "+11"'
t 1 0			'" +2" "<" " +11"'

# Operator precedence:  |  &  =  >  >=  <  <=  !=  +  -  *  /  %  : ( )
t 0 'true'		'true "|" 0 "&" 1'
t 0 'true'		'true "|" 0 + 1'
t 2 ''			'"(" true "|" 0 ")" + 1'
t 0 1			'true ">" 0 + 1'
t 0 2			'"(" true ">" 0 ")" + 1'
t 1 0			'0 "|" 1 = 0 "&" 1'
t 1 0			'2 - 1 "*" 2'
t 0 4			'2 + 1 "*" 2'
t 1 0			'2 - 4 "/" 2'
t 0 1			'4 - 5 + 2'
t 0 1			'2 \* 4 / 7'
t 0 1			'2 \* 4 % 7'
t 0 8			'true : .\* \* 2'
t 0 20			'\( 2 \* 2 \) + \( 2 \* 2 \) \* \( 2 \* 2 \)'
t 0 10			'5 \* 7 - 5 \* 5'
t 0 50			'5 \* \( 7 - 5 \) \* 5'

# Some tests for compat mode, expecting it only changes the
# supported syntax of numeric arguments and the option processing
EXPR_COMPAT=1; export EXPR_COMPAT

# Arguments processing
t 0 '-e'		'-e'
t 0 '--'		'--'
t 2 ''			'-e --'
t 2 ''			'-- --'
t 2 ''			'-e -- --'
t 0 1			'-e != --'
t 1 0			'-e != -e'
t 0 -1			'-1'

# More lenient number parsing leads to more comparisons by numeric value
t 0 -2			'-1 + -1'
t 1 0			'-1 + 1'
t 0 2			'" 1" + 1'
t 0 2			'+1 + 1'
t 0 2			'" +1" + 1'
t 2 ''			'" -" + 1'
t 2 ''			'" +" + 1'
t 1 0			'" 2" "<" "2"'
t 0 1			'"2" "<" "11"'
t 1 0			'"-2" "<" "-11"'
t 0 1			'" 2" "<" " 11"'
t 1 0			'" -2" "<" " -11"'
t 1 0			'"2 " "<" "11 "'
t 1 0			'"-2 " "<" "-11 "'
t 1 0			'" 2 " "<" " 11 "'
t 1 0			'" -2 " "<" " -11 "'
t 0 1			'"+2" "<" "+11"'
t 0 1			'" +2" "<" " +11"'


More information about the freebsd-standards mailing list