shell/math: return string error indicator, not integer

function                                             old     new   delta
expand_and_evaluate_arith                             87     106     +19
expand_one_var                                      1563    1570      +7
arith                                                 12      18      +6
evaluate_string                                      678     680      +2
arith_apply                                         1269    1271      +2
builtin_umask                                        133     132      -1
ash_arith                                            118      75     -43
expand_vars_to_list                                 1094    1038     -56
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/3 up/down: 36/-100)           Total: -64 bytes

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
diff --git a/shell/ash.c b/shell/ash.c
index c27ab7d..ec887e0 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5451,15 +5451,8 @@
 
 	INT_OFF;
 	result = arith(&math_state, s);
-	if (math_state.errcode < 0) {
-		if (math_state.errcode == -3)
-			ash_msg_and_raise_error("exponent less than 0");
-		if (math_state.errcode == -2)
-			ash_msg_and_raise_error("divide by zero");
-		if (math_state.errcode == -5)
-			ash_msg_and_raise_error("expression recursion loop detected");
-		raise_error_syntax(s);
-	}
+	if (math_state.errmsg)
+		ash_msg_and_raise_error(math_state.errmsg);
 	INT_ON;
 
 	return result;
diff --git a/shell/ash_test/ash-arith/arith.right b/shell/ash_test/ash-arith/arith.right
index 3ea7ce6..7257cc5 100644
--- a/shell/ash_test/ash-arith/arith.right
+++ b/shell/ash_test/ash-arith/arith.right
@@ -55,28 +55,28 @@
 30 30
 20 20
 30 30
-./arith.tests: line 117: syntax error: 1 ? 20 : x+=2
+./arith.tests: line 117: arithmetic syntax error
 6 6
 6,5,3 6,5,3
 263 263
 255 255
 40 40
-./arith.tests: line 163: syntax error:  7 = 43 
+./arith.tests: line 163: arithmetic syntax error
 ./arith.tests: line 165: divide by zero
-./arith.tests: let: line 166: syntax error: jv += $iv
-./arith.tests: line 167: syntax error:  jv += $iv 
-./arith.tests: let: line 168: syntax error: rv = 7 + (43 * 6
+./arith.tests: let: line 166: arithmetic syntax error
+./arith.tests: line 167: arithmetic syntax error
+./arith.tests: let: line 168: arithmetic syntax error
 abc
 def
 ghi
-./arith.tests: line 191: syntax error:  ( 4 + A ) + 4 
+./arith.tests: line 191: arithmetic syntax error
 16 16
-./arith.tests: line 196: syntax error:  4 ? : 3 + 5 
-./arith.tests: line 197: syntax error:  1 ? 20 
-./arith.tests: line 198: syntax error:  4 ? 20 : 
+./arith.tests: line 196: arithmetic syntax error
+./arith.tests: line 197: arithmetic syntax error
+./arith.tests: line 198: arithmetic syntax error
 9 9
-./arith.tests: line 205: syntax error:  0 && B=42 
-./arith.tests: line 208: syntax error:  1 || B=88 
+./arith.tests: line 205: arithmetic syntax error
+./arith.tests: line 208: arithmetic syntax error
 9 9
 9 9
 9 9
@@ -97,18 +97,18 @@
 3 3
 4 4
 4 4
-./arith.tests: line 257: syntax error:  7-- 
-./arith.tests: line 259: syntax error:  --x=7 
-./arith.tests: line 260: syntax error:  ++x=7 
-./arith.tests: line 262: syntax error:  x++=7 
-./arith.tests: line 263: syntax error:  x--=7 
+./arith.tests: line 257: arithmetic syntax error
+./arith.tests: line 259: arithmetic syntax error
+./arith.tests: line 260: arithmetic syntax error
+./arith.tests: line 262: arithmetic syntax error
+./arith.tests: line 263: arithmetic syntax error
 4 4
 7 7
 -7 -7
-./arith1.sub: line 2: syntax error:  4-- 
-./arith1.sub: line 3: syntax error:  4++ 
-./arith1.sub: line 4: syntax error:  4 -- 
-./arith1.sub: line 5: syntax error:  4 ++ 
+./arith1.sub: line 2: arithmetic syntax error
+./arith1.sub: line 3: arithmetic syntax error
+./arith1.sub: line 4: arithmetic syntax error
+./arith1.sub: line 5: arithmetic syntax error
 6 6
 3 3
 7 7
@@ -119,19 +119,19 @@
 2 2
 -2 -2
 1 1
-./arith1.sub: line 37: syntax error:  +++7 
-./arith2.sub: line 2: syntax error:  --7 
-./arith2.sub: line 3: syntax error:  ++7 
-./arith2.sub: line 4: syntax error:  -- 7 
-./arith2.sub: line 5: syntax error:  ++ 7 
+./arith1.sub: line 37: arithmetic syntax error
+./arith2.sub: line 2: arithmetic syntax error
+./arith2.sub: line 3: arithmetic syntax error
+./arith2.sub: line 4: arithmetic syntax error
+./arith2.sub: line 5: arithmetic syntax error
 5 5
 1 1
 4 4
 0 0
-./arith2.sub: line 42: syntax error:  -- - 7 
-./arith2.sub: line 47: syntax error:  ++ + 7 
+./arith2.sub: line 42: arithmetic syntax error
+./arith2.sub: line 47: arithmetic syntax error
 8 12
-./arith.tests: line 290: syntax error: a b
+./arith.tests: line 290: arithmetic syntax error
 42
 42
 42
diff --git a/shell/hush.c b/shell/hush.c
index 4ca5403..ad30ac1 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -4461,7 +4461,7 @@
 }
 
 #if ENABLE_SH_MATH_SUPPORT
-static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p)
+static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
 {
 	arith_state_t math_state;
 	arith_t res;
@@ -4472,8 +4472,11 @@
 	//math_state.endofname = endofname;
 	exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
 	res = arith(&math_state, exp_str ? exp_str : arg);
-	*errcode_p = math_state.errcode;
 	free(exp_str);
+	if (errmsg_p)
+		*errmsg_p = math_state.errmsg;
+	if (math_state.errmsg)
+		die_if_script(math_state.errmsg);
 	return res;
 }
 #endif
@@ -4714,22 +4717,26 @@
 			 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
 			 */
 			arith_t beg, len;
-			int errcode = 0;
+			const char *errmsg;
 
-			beg = expand_and_evaluate_arith(exp_word, &errcode);
+			beg = expand_and_evaluate_arith(exp_word, &errmsg);
+			if (errmsg)
+				goto arith_err;
 			debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
 			*p++ = SPECIAL_VAR_SYMBOL;
 			exp_word = p;
 			p = strchr(p, SPECIAL_VAR_SYMBOL);
 			*p = '\0';
-			len = expand_and_evaluate_arith(exp_word, &errcode);
+			len = expand_and_evaluate_arith(exp_word, &errmsg);
+			if (errmsg)
+				goto arith_err;
 			debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
-
-			if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
+			if (len >= 0) { /* bash compat: len < 0 is illegal */
 				if (beg < 0) /* bash compat */
 					beg = 0;
 				debug_printf_varexp("from val:'%s'\n", val);
 				if (len == 0 || !val || beg >= strlen(val)) {
+ arith_err:
 					val = NULL;
 				} else {
 					/* Paranoia. What if user entered 9999999999999
@@ -4926,28 +4933,11 @@
 #if ENABLE_SH_MATH_SUPPORT
 		case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
 			arith_t res;
-			int errcode;
 
 			arg++; /* skip '+' */
 			*p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
 			debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
-			res = expand_and_evaluate_arith(arg, &errcode);
-
-			if (errcode < 0) {
-				const char *msg = "error in arithmetic";
-				switch (errcode) {
-				case -3:
-					msg = "exponent less than 0";
-					break;
-				case -2:
-					msg = "divide by 0";
-					break;
-				case -5:
-					msg = "expression recursion loop detected";
-					break;
-				}
-				die_if_script(msg);
-			}
+			res = expand_and_evaluate_arith(arg, NULL);
 			debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
 			sprintf(arith_buf, arith_t_fmt, res);
 			val = arith_buf;
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right
index 718c26a..fd4ea8e 100644
--- a/shell/hush_test/hush-arith/arith.right
+++ b/shell/hush_test/hush-arith/arith.right
@@ -61,7 +61,7 @@
 20 20
 30 30
 check precedence of assignment vs. conditional operator
-hush: error in arithmetic
+hush: arithmetic syntax error
 check precedence of assignment vs. conditional operator
 associativity of assignment-operator operator
 6 6
@@ -70,22 +70,22 @@
 263 263
 255 255
 40 40
-hush: error in arithmetic
-hush: divide by 0
+hush: arithmetic syntax error
+hush: divide by zero
 hush: can't execute 'let': No such file or directory
-hush: error in arithmetic
+hush: arithmetic syntax error
 hush: can't execute 'let': No such file or directory
 abc
 def
 ghi
-hush: error in arithmetic
+hush: arithmetic syntax error
 16 16
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 9 9
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 9 9
 9 9
 9 9
@@ -106,18 +106,18 @@
 3 3
 4 4
 4 4
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 4 4
 7 7
 -7 -7
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 6 6
 3 3
 7 7
@@ -128,19 +128,19 @@
 2 2
 -2 -2
 1 1
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 5 5
 1 1
 4 4
 0 0
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 8 12
-hush: error in arithmetic
+hush: arithmetic syntax error
 42
 42
 42
diff --git a/shell/math.c b/shell/math.c
index 8397157..871c06c 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -26,26 +26,26 @@
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
-
-   Permission is hereby granted, free of charge, to any person obtaining
-   a copy of this software and associated documentation files (the
-   "Software"), to deal in the Software without restriction, including
-   without limitation the rights to use, copy, modify, merge, publish,
-   distribute, sublicense, and/or sell copies of the Software, and to
-   permit persons to whom the Software is furnished to do so, subject to
-   the following conditions:
-
-   The above copyright notice and this permission notice shall be
-   included in all copies or substantial portions of the Software.
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
 
 /* This is my infix parser/evaluator. It is optimized for size, intended
  * as a replacement for yacc-based parsers. However, it may well be faster
@@ -60,9 +60,6 @@
  * this is based (this code differs in that it applies operators immediately
  * to the stack instead of adding them to a queue to end up with an
  * expression).
- *
- * To use the routine, call it with an expression string and error return
- * pointer
  */
 
 /*
@@ -250,7 +247,7 @@
 static arith_t FAST_FUNC
 evaluate_string(arith_state_t *math_state, const char *expr);
 
-static int
+static const char*
 arith_lookup_val(arith_state_t *math_state, v_n_t *t)
 {
 	if (t->var) {
@@ -264,8 +261,8 @@
 			 */
 			for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) {
 				if (strcmp(cur->var, t->var) == 0) {
-					/* Yes. Expression recursion loop detected */
-					return -5;
+					/* Yes */
+					return "expression recursion loop detected";
 				}
 			}
 
@@ -281,7 +278,7 @@
 			/* pop current var name */
 			math_state->list_of_recursed_names = cur;
 
-			return math_state->errcode;
+			return math_state->errmsg;
 		}
 		/* treat undefined var as 0 */
 		t->val = 0;
@@ -292,14 +289,14 @@
 /* "Applying" a token means performing it on the top elements on the integer
  * stack. For an unary operator it will only change the top element, but a
  * binary operator will pop two arguments and push the result */
-static NOINLINE int
+static NOINLINE const char*
 arith_apply(arith_state_t *math_state, operator op, v_n_t *numstack, v_n_t **numstackptr)
 {
 #define NUMPTR (*numstackptr)
 
 	v_n_t *numptr_m1;
 	arith_t numptr_val, rez;
-	int err;
+	const char *err;
 
 	/* There is no operator that can work without arguments */
 	if (NUMPTR == numstack)
@@ -399,13 +396,13 @@
 		} else if (op == TOK_EXPONENT) {
 			arith_t c;
 			if (numptr_val < 0)
-				return -3;      /* exponent less than 0 */
+				return "exponent less than 0";
 			c = 1;
 			while (--numptr_val >= 0)
 			    c *= rez;
 			rez = c;
-		} else if (numptr_val==0)          /* zero divisor check */
-			return -2;
+		} else if (numptr_val == 0)
+			return "divide by zero";
 		else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
 			rez /= numptr_val;
 		else if (op == TOK_REM || op == TOK_REM_ASSIGN)
@@ -430,9 +427,9 @@
 	numptr_m1->val = rez;
 	/* erase var name, it is just a number now */
 	numptr_m1->var = NULL;
-	return 0;
+	return NULL;
  err:
-	return -1;
+	return "arithmetic syntax error";
 #undef NUMPTR
 }
 
@@ -498,7 +495,7 @@
 evaluate_string(arith_state_t *math_state, const char *expr)
 {
 	operator lasttok;
-	int errcode;
+	const char *errmsg;
 	const char *start_expr = expr = skip_whitespace(expr);
 	unsigned expr_len = strlen(expr) + 2;
 	/* Stack of integers */
@@ -512,7 +509,7 @@
 	operator *stackptr = stack;
 
 	*stackptr++ = lasttok = TOK_LPAREN;     /* start off with a left paren */
-	errcode = 0;
+	errmsg = NULL;
 
 	while (1) {
 		const char *p;
@@ -548,7 +545,7 @@
 			}
 			if (numstack->var) {
 				/* expression is $((var)) only, lookup now */
-				errcode = arith_lookup_val(math_state, numstack);
+				errmsg = arith_lookup_val(math_state, numstack);
 			}
 			goto ret;
 		}
@@ -663,8 +660,8 @@
 						break;
 					}
 				}
-				errcode = arith_apply(math_state, prev_op, numstack, &numstackptr);
-				if (errcode)
+				errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
+				if (errmsg)
 					goto ret;
 			}
 			if (op == TOK_RPAREN) {
@@ -678,15 +675,17 @@
 	} /* while (1) */
 
  err:
-	numstack->val = errcode = -1;
+	numstack->val = -1;
+	errmsg = "arithmetic syntax error";
  ret:
-	math_state->errcode = errcode;
+	math_state->errmsg = errmsg;
 	return numstack->val;
 }
 
 arith_t FAST_FUNC
 arith(arith_state_t *math_state, const char *expr)
 {
+	math_state->errmsg = NULL;
 	math_state->list_of_recursed_names = NULL;
 	return evaluate_string(math_state, expr);
 }
diff --git a/shell/math.h b/shell/math.h
index e34b65d..2dcab13 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -95,7 +95,7 @@
 //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
 
 typedef struct arith_state_t {
-	int                   errcode;
+	const char           *errmsg;
 	arith_var_lookup_t    lookupvar;
 	arith_var_set_t       setvar;
 //	arith_var_endofname_t endofname;