Module Name:    src
Committed By:   kre
Date:           Mon Mar 20 11:26:07 UTC 2017

Modified Files:
        src/bin/sh: Makefile expand.c expand.h sh.1
Added Files:
        src/bin/sh: arith_token.c arith_tokens.h arithmetic.c arithmetic.h
Removed Files:
        src/bin/sh: arith.y arith_lex.l

Log Message:
Finish support for all required $(( )) (shell arithmetic) operators,
closing PR bin/50958

That meant adding the assignment operators ('=', and all of the +=, *= ...)
Currently, ++, --, and ',' are not implemented (none of those are required
by posix) but support for them (most likely ',' first) might be added later.

To do this, I removed the yacc/lex arithmetic parser completely, and
replaced it with a hand written recursive descent parser, that I obtained
from FreeBSD, who earlier had obtained it from dash (Herbert Xu).

While doing the import, I cleaned up the sources (changed some file names
to avoid requiring a clean build, or signifigant surgery to the obj
directories if "build.sh -u" was to be used - "build.sh -u" should work
fine as it is now) removed some dashisms, applied some KNF, ...


To generate a diff of this commit:
cvs rdiff -u -r1.103 -r1.104 src/bin/sh/Makefile
cvs rdiff -u -r1.25 -r0 src/bin/sh/arith.y
cvs rdiff -u -r1.18 -r0 src/bin/sh/arith_lex.l
cvs rdiff -u -r0 -r1.1 src/bin/sh/arith_token.c src/bin/sh/arith_tokens.h \
    src/bin/sh/arithmetic.c src/bin/sh/arithmetic.h
cvs rdiff -u -r1.102 -r1.103 src/bin/sh/expand.c
cvs rdiff -u -r1.19 -r1.20 src/bin/sh/expand.h
cvs rdiff -u -r1.125 -r1.126 src/bin/sh/sh.1

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/sh/Makefile
diff -u src/bin/sh/Makefile:1.103 src/bin/sh/Makefile:1.104
--- src/bin/sh/Makefile:1.103	Thu Mar 16 13:09:06 2017
+++ src/bin/sh/Makefile	Mon Mar 20 11:26:07 2017
@@ -1,16 +1,15 @@
-#	$NetBSD: Makefile,v 1.103 2017/03/16 13:09:06 kre Exp $
+#	$NetBSD: Makefile,v 1.104 2017/03/20 11:26:07 kre Exp $
 #	@(#)Makefile	8.4 (Berkeley) 5/5/95
 
 .include <bsd.own.mk>
 
-YHEADER=1
 PROG=	sh
-SHSRCS=	alias.c cd.c echo.c error.c eval.c exec.c expand.c \
-	histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \
-	mystring.c options.c parser.c redir.c show.c trap.c output.c var.c \
-	test.c kill.c syntax.c
-GENSRCS=arith.c arith_lex.c builtins.c init.c nodes.c
-GENHDRS=arith.h builtins.h nodes.h token.h nodenames.h
+SHSRCS=	alias.c arith_token.c arithmetic.c cd.c echo.c error.c eval.c exec.c \
+	expand.c histedit.c input.c jobs.c mail.c main.c memalloc.c \
+	miscbltin.c mystring.c options.c parser.c redir.c show.c trap.c \
+	output.c var.c test.c kill.c syntax.c
+GENSRCS=builtins.c init.c nodes.c
+GENHDRS=builtins.h nodes.h token.h nodenames.h
 SRCS=	${SHSRCS} ${GENSRCS}
 
 DPSRCS+=${GENHDRS}
@@ -18,21 +17,11 @@ DPSRCS+=${GENHDRS}
 LDADD+=	-ll -ledit -lterminfo
 DPADD+=	${LIBL} ${LIBEDIT} ${LIBTERMINFO}
 
-LFLAGS=	-8	# 8-bit lex scanner for arithmetic
-
 # Environment for scripts executed during build.
 SCRIPT_ENV= \
 	AWK=${TOOL_AWK:Q} \
 	SED=${TOOL_SED:Q}
 
-# The .depend file can get references to these temporary files
-.OPTIONAL: lex.yy.c y.tab.c
-
-.ifdef CRUNCHEDPROG
-LFLAGS+=-L
-YFLAGS+=-l
-.endif
-
 CPPFLAGS+=-DSHELL -I. -I${.CURDIR}
 #XXX: For testing only.
 #CPPFLAGS+=-DDEBUG=2
@@ -53,7 +42,7 @@ SRCS+=printf.c
 	${NETBSDSRCDIR}/usr.bin/printf \
 	${NETBSDSRCDIR}/bin/kill
 
-CLEANFILES+= ${GENSRCS} ${GENHDRS} y.tab.h sh.html1
+CLEANFILES+= ${GENSRCS} ${GENHDRS} sh.html1
 CLEANFILES+= trace.*
 
 token.h: mktokens

Index: src/bin/sh/expand.c
diff -u src/bin/sh/expand.c:1.102 src/bin/sh/expand.c:1.103
--- src/bin/sh/expand.c:1.102	Sun Mar 12 04:19:29 2017
+++ src/bin/sh/expand.c	Mon Mar 20 11:26:07 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: expand.c,v 1.102 2017/03/12 04:19:29 kre Exp $	*/
+/*	$NetBSD: expand.c,v 1.103 2017/03/20 11:26:07 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
 #else
-__RCSID("$NetBSD: expand.c,v 1.102 2017/03/12 04:19:29 kre Exp $");
+__RCSID("$NetBSD: expand.c,v 1.103 2017/03/20 11:26:07 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -64,6 +64,7 @@ __RCSID("$NetBSD: expand.c,v 1.102 2017/
 #include "eval.h"
 #include "expand.h"
 #include "syntax.h"
+#include "arithmetic.h"
 #include "parser.h"
 #include "jobs.h"
 #include "options.h"
@@ -356,7 +357,7 @@ removerecordregions(int endoff)
 void
 expari(int flag)
 {
-	char *p, *start;
+	char *p, *q, *start;
 	intmax_t result;
 	int adjustment;
 	int begoff;
@@ -375,8 +376,23 @@ expari(int flag)
 	 * have to rescan starting from the beginning since CTLESC
 	 * characters have to be processed left to right.
 	 */
-/* SPACE_NEEDED is enough for all digits, plus possible "-", plus 2 (why?) */
-#define SPACE_NEEDED ((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 1 + 2)
+
+	/*
+	 * SPACE_NEEDED is enough for all possible digits (rounded up)
+	 * plus possible "-", and the terminating '\0', hence, plus 2
+	 *
+	 * The calculation produces the number of bytes needed to
+	 * represent the biggest possible value, in octal.  We only
+	 * generate decimal, which takes (often) less digits (never more)
+	 * so this is safe, if occasionally slightly wasteful.
+	 */
+#define SPACE_NEEDED ((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2)
+	/*
+	 * Only need check for SPACE_NEEDED-2 as we have already
+	 * consumed (but will overwrite) 2 chars, the CTLARI, and the
+	 * flags (quoting) byte (if those had not already been seen,
+	 * we would not be here.)
+	 */
 	CHECKSTRSPACE((int)(SPACE_NEEDED - 2), expdest);
 	USTPUTC('\0', expdest);
 	start = stackblock();
@@ -398,7 +414,9 @@ expari(int flag)
 	removerecordregions(begoff);
 	if (quotes)
 		rmescapes(p+2);
+	q = grabstackstr(expdest);
 	result = arith(p+2);
+	ungrabstackstr(q, expdest);
 	fmtstr(p, SPACE_NEEDED, "%"PRIdMAX, result);
 
 	while (*p++)

Index: src/bin/sh/expand.h
diff -u src/bin/sh/expand.h:1.19 src/bin/sh/expand.h:1.20
--- src/bin/sh/expand.h:1.19	Sat Dec 22 20:15:22 2012
+++ src/bin/sh/expand.h	Mon Mar 20 11:26:07 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: expand.h,v 1.19 2012/12/22 20:15:22 dsl Exp $	*/
+/*	$NetBSD: expand.h,v 1.20 2017/03/20 11:26:07 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -66,8 +66,3 @@ void expari(int);
 int patmatch(char *, char *, int);
 void rmescapes(char *);
 int casematch(union node *, char *);
-
-/* From arith.y */
-intmax_t arith(const char *);
-void arith_lex_reset(void);
-int yylex(void);

Index: src/bin/sh/sh.1
diff -u src/bin/sh/sh.1:1.125 src/bin/sh/sh.1:1.126
--- src/bin/sh/sh.1:1.125	Sat Feb  4 23:35:15 2017
+++ src/bin/sh/sh.1	Mon Mar 20 11:26:07 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sh.1,v 1.125 2017/02/04 23:35:15 wiz Exp $
+.\"	$NetBSD: sh.1,v 1.126 2017/03/20 11:26:07 kre Exp $
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
 .\"
@@ -1255,6 +1255,39 @@ Shell variables may be referenced by nam
 expression, without needing a
 .Dq \&$
 sign.
+Variables that are not set, or which have an empty (null string) value,
+used this way evaluate as zero (that is,
+.Dq x
+in arithmetic, as an R-Value, is evaluated as
+.Dq ${x:-0} )
+unless the
+.Nm
+.Fl u
+flag is set, in which case a reference to an unset variable is an error.
+Note that unset variables used in the ${var} form expand to a null
+string, which might result in syntax errors.
+Referencing the value of a variable which is not numeric is an error.
+.Pp
+All of the C expression operators applicable to integers are supported,
+and operate as they would in a C expression, except the unary
+.Dq ++
+and
+.Dq --
+operators (in both prefix and postfix forms) and the
+.Dq \&,
+(comma) operator, which are currently not supported.
+.PP
+It should not be necessary to state that the C operators which
+operate on, or produce, pointer types, are not supported.
+Those include unary
+.Dq \&*
+and
+.Dq \&&
+and the struct and array referencing binary operators:
+.Dq \&. ,
+.Dq \&->
+and
+.Dq \&[ .
 .Ss White Space Splitting (Field Splitting)
 After parameter expansion, command substitution, and
 arithmetic expansion the shell scans the results of

Added files:

Index: src/bin/sh/arith_token.c
diff -u /dev/null src/bin/sh/arith_token.c:1.1
--- /dev/null	Mon Mar 20 11:26:07 2017
+++ src/bin/sh/arith_token.c	Mon Mar 20 11:26:07 2017
@@ -0,0 +1,232 @@
+/*	$NetBSD: arith_token.c,v 1.1 2017/03/20 11:26:07 kre Exp $	*/
+
+/*-
+ * Copyright (c) 2002
+ *	Herbert Xu.
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * From FreeBSD, from dash
+ */
+
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: arith_token.c,v 1.1 2017/03/20 11:26:07 kre Exp $");
+#endif /* not lint */
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "shell.h"
+#include "arith_tokens.h"
+#include "expand.h"
+#include "error.h"
+#include "memalloc.h"
+#include "parser.h"
+#include "syntax.h"
+
+#if ARITH_BOR + ARITH_ASS_GAP != ARITH_BORASS || \
+	ARITH_ASS + ARITH_ASS_GAP != ARITH_EQ
+#error Arithmetic tokens are out of order.
+#endif
+
+/*
+ * Scan next arithmetic token, return its type,
+ * leave its value (when applicable) in (global) a_t_val.
+ *
+ * Input text is in (global) arith_buf which is updated to
+ * refer to the next char after the token returned, except
+ * on error (ARITH_BAD returned) where arith_buf is not altered.
+ */
+int
+arith_token(void)
+{
+	int token;
+	const char *buf = arith_buf;
+	char *end;
+	const char *p;
+
+	for (;;) {
+		token = *buf;
+
+		switch (token) {
+		case ' ':
+		case '\t':
+		case '\n':
+			buf++;
+			continue;
+
+		default:
+			error("arithmetic: unexpected '%c' (%#x) in expression",
+			    token, token);
+			/* NOTREACHED */
+
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			a_t_val.val = strtoimax(buf, &end, 0);
+			arith_buf = end;
+			return ARITH_NUM;
+
+		case 'A': case 'B': case 'C': case 'D': case 'E':
+		case 'F': case 'G': case 'H': case 'I': case 'J':
+		case 'K': case 'L': case 'M': case 'N': case 'O':
+		case 'P': case 'Q': case 'R': case 'S': case 'T':
+		case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+		case 'a': case 'b': case 'c': case 'd': case 'e':
+		case 'f': case 'g': case 'h': case 'i': case 'j':
+		case 'k': case 'l': case 'm': case 'n': case 'o':
+		case 'p': case 'q': case 'r': case 's': case 't':
+		case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+		case '_':
+			p = buf;
+			while (buf++, is_in_name(*buf))
+				;
+			a_t_val.name = stalloc(buf - p + 1);
+			memcpy(a_t_val.name, p, buf - p);
+			a_t_val.name[buf - p] = '\0';
+			arith_buf = buf;
+			return ARITH_VAR;
+
+		case '=':
+			token = ARITH_ASS;
+ checkeq:
+			buf++;
+ checkeqcur:
+			if (*buf != '=')
+				goto out;
+			token += ARITH_ASS_GAP;
+			break;
+
+		case '>':
+			switch (*++buf) {
+			case '=':
+				token = ARITH_GE;
+				break;
+			case '>':
+				token = ARITH_RSHIFT;
+				goto checkeq;
+			default:
+				token = ARITH_GT;
+				goto out;
+			}
+			break;
+
+		case '<':
+			switch (*++buf) {
+			case '=':
+				token = ARITH_LE;
+				break;
+			case '<':
+				token = ARITH_LSHIFT;
+				goto checkeq;
+			default:
+				token = ARITH_LT;
+				goto out;
+			}
+			break;
+
+		case '|':
+			if (*++buf != '|') {
+				token = ARITH_BOR;
+				goto checkeqcur;
+			}
+			token = ARITH_OR;
+			break;
+
+		case '&':
+			if (*++buf != '&') {
+				token = ARITH_BAND;
+				goto checkeqcur;
+			}
+			token = ARITH_AND;
+			break;
+
+		case '!':
+			if (*++buf != '=') {
+				token = ARITH_NOT;
+				goto out;
+			}
+			token = ARITH_NE;
+			break;
+
+		case 0:
+			goto out;
+
+		case '(':
+			token = ARITH_LPAREN;
+			break;
+		case ')':
+			token = ARITH_RPAREN;
+			break;
+
+		case '*':
+			token = ARITH_MUL;
+			goto checkeq;
+		case '/':
+			token = ARITH_DIV;
+			goto checkeq;
+		case '%':
+			token = ARITH_REM;
+			goto checkeq;
+
+		case '+':
+			if (buf[1] == '+')
+				error("arithmetic: ++ operator unsupported");
+			token = ARITH_ADD;
+			goto checkeq;
+		case '-':
+			if (buf[1] == '-')
+				error("arithmetic: -- operator unsupported");
+			token = ARITH_SUB;
+			goto checkeq;
+		case '~':
+			token = ARITH_BNOT;
+			break;
+		case '^':
+			token = ARITH_BXOR;
+			goto checkeq;
+
+		case '?':
+			token = ARITH_QMARK;
+			break;
+		case ':':
+			token = ARITH_COLON;
+			break;
+		}
+		break;
+	}
+	buf++;
+ out:
+	arith_buf = buf;
+	return token;
+}
Index: src/bin/sh/arith_tokens.h
diff -u /dev/null src/bin/sh/arith_tokens.h:1.1
--- /dev/null	Mon Mar 20 11:26:07 2017
+++ src/bin/sh/arith_tokens.h	Mon Mar 20 11:26:07 2017
@@ -0,0 +1,115 @@
+/*	$NetBSD: arith_tokens.h,v 1.1 2017/03/20 11:26:07 kre Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2007
+ *	Herbert Xu <herb...@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * From FreeBSD who obtained it from dash (modified both times.)
+ */
+
+/*
+ * Tokens returned from arith_token()
+ *
+ * Caution, values are not arbitrary.
+ */
+
+#define	ARITH_BAD	0
+
+#define	ARITH_ASS	1
+
+#define	ARITH_OR	2
+#define	ARITH_AND	3
+#define	ARITH_NUM	5
+#define	ARITH_VAR	6
+#define	ARITH_NOT	7
+
+#define	ARITH_BINOP_MIN	8
+
+#define	ARITH_LE	8
+#define	ARITH_GE	9
+#define	ARITH_LT	10
+#define	ARITH_GT	11
+#define	ARITH_EQ	12	/* Must be ARITH_ASS + ARITH_ASS_GAP */
+
+#define	ARITH_ASS_BASE	13
+
+#define	ARITH_REM	13
+#define	ARITH_BAND	14
+#define	ARITH_LSHIFT	15
+#define	ARITH_RSHIFT	16
+#define	ARITH_MUL	17
+#define	ARITH_ADD	18
+#define	ARITH_BOR	19
+#define	ARITH_SUB	20
+#define	ARITH_BXOR	21
+#define	ARITH_DIV	22
+
+#define	ARITH_NE	23
+
+#define	ARITH_BINOP_MAX	24
+
+#define	ARITH_ASS_MIN	ARITH_BINOP_MAX
+#define	ARITH_ASS_GAP	(ARITH_ASS_MIN - ARITH_ASS_BASE)
+
+#define	ARITH_REMASS	(ARITH_ASS_GAP + ARITH_REM)
+#define	ARITH_BANDASS	(ARITH_ASS_GAP + ARITH_BAND)
+#define	ARITH_LSHIFTASS	(ARITH_ASS_GAP + ARITH_LSHIFT)
+#define	ARITH_RSHIFTASS	(ARITH_ASS_GAP + ARITH_RSHIFT)
+#define	ARITH_MULASS	(ARITH_ASS_GAP + ARITH_MUL)
+#define	ARITH_ADDASS	(ARITH_ASS_GAP + ARITH_ADD)
+#define	ARITH_BORASS	(ARITH_ASS_GAP + ARITH_BOR)
+#define	ARITH_SUBASS	(ARITH_ASS_GAP + ARITH_SUB)
+#define	ARITH_BXORASS	(ARITH_ASS_GAP + ARITH_BXOR)
+#define	ARITH_DIVASS	(ARITH_ASS_GAP + ARITH_BXOR)
+
+#define	ARITH_ASS_MAX	34
+
+#define	ARITH_LPAREN	34
+#define	ARITH_RPAREN	35
+#define	ARITH_BNOT	36
+#define	ARITH_QMARK	37
+#define	ARITH_COLON	38
+
+/*
+ * Globals shared between arith parser, and lexer
+ */
+
+extern const char *arith_buf;
+
+union a_token_val {
+	intmax_t val;
+	char *name;
+};
+
+extern union a_token_val a_t_val;
+
+int arith_token(void);
Index: src/bin/sh/arithmetic.c
diff -u /dev/null src/bin/sh/arithmetic.c:1.1
--- /dev/null	Mon Mar 20 11:26:07 2017
+++ src/bin/sh/arithmetic.c	Mon Mar 20 11:26:07 2017
@@ -0,0 +1,406 @@
+/*	$NetBSD: arithmetic.c,v 1.1 2017/03/20 11:26:07 kre Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2007
+ *	Herbert Xu <herb...@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * From FreeBSD, who obtained it from dash, modified both times...
+ */
+
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: arithmetic.c,v 1.1 2017/03/20 11:26:07 kre Exp $");
+#endif /* not lint */
+
+#include <limits.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "shell.h"
+#include "arithmetic.h"
+#include "arith_tokens.h"
+#include "expand.h"
+#include "error.h"
+#include "memalloc.h"
+#include "output.h"
+#include "options.h"
+#include "var.h"
+
+#if ARITH_BOR + ARITH_ASS_GAP != ARITH_BORASS || \
+	ARITH_ASS + ARITH_ASS_GAP != ARITH_EQ
+#error Arithmetic tokens are out of order.
+#endif
+
+static const char *arith_startbuf;
+
+const char *arith_buf;
+union a_token_val a_t_val;
+
+static int last_token;
+
+#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
+
+static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
+	ARITH_PRECEDENCE(ARITH_MUL, 0),
+	ARITH_PRECEDENCE(ARITH_DIV, 0),
+	ARITH_PRECEDENCE(ARITH_REM, 0),
+	ARITH_PRECEDENCE(ARITH_ADD, 1),
+	ARITH_PRECEDENCE(ARITH_SUB, 1),
+	ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
+	ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
+	ARITH_PRECEDENCE(ARITH_LT, 3),
+	ARITH_PRECEDENCE(ARITH_LE, 3),
+	ARITH_PRECEDENCE(ARITH_GT, 3),
+	ARITH_PRECEDENCE(ARITH_GE, 3),
+	ARITH_PRECEDENCE(ARITH_EQ, 4),
+	ARITH_PRECEDENCE(ARITH_NE, 4),
+	ARITH_PRECEDENCE(ARITH_BAND, 5),
+	ARITH_PRECEDENCE(ARITH_BXOR, 6),
+	ARITH_PRECEDENCE(ARITH_BOR, 7),
+};
+
+#define ARITH_MAX_PREC 8
+
+int expcmd(int, char **);
+
+static void __dead
+arith_err(const char *s)
+{
+	error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+	/* NOTREACHED */
+}
+
+static intmax_t
+arith_lookupvarint(char *varname)
+{
+	const char *str;
+	char *p;
+	intmax_t result;
+
+	str = lookupvar(varname);
+	if (uflag && str == NULL)
+		arith_err("variable not set");
+	if (str == NULL || *str == '\0')
+		str = "0";
+	errno = 0;
+	result = strtoimax(str, &p, 0);
+	if (errno != 0 || *p != '\0')
+		arith_err("variable contains non-numeric value");
+	return result;
+}
+
+static inline int
+arith_prec(int op)
+{
+
+	return prec[op - ARITH_BINOP_MIN];
+}
+
+static inline int
+higher_prec(int op1, int op2)
+{
+
+	return arith_prec(op1) < arith_prec(op2);
+}
+
+static intmax_t
+do_binop(int op, intmax_t a, intmax_t b)
+{
+
+	switch (op) {
+	default:
+		arith_err("token error");
+	case ARITH_REM:
+	case ARITH_DIV:
+		if (b == 0)
+			arith_err("division by zero");
+		if (a == INTMAX_MIN && b == -1)
+			arith_err("divide error");
+		return op == ARITH_REM ? a % b : a / b;
+	case ARITH_MUL:
+		return (uintmax_t)a * (uintmax_t)b;
+	case ARITH_ADD:
+		return (uintmax_t)a + (uintmax_t)b;
+	case ARITH_SUB:
+		return (uintmax_t)a - (uintmax_t)b;
+	case ARITH_LSHIFT:
+		return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
+	case ARITH_RSHIFT:
+		return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
+	case ARITH_LT:
+		return a < b;
+	case ARITH_LE:
+		return a <= b;
+	case ARITH_GT:
+		return a > b;
+	case ARITH_GE:
+		return a >= b;
+	case ARITH_EQ:
+		return a == b;
+	case ARITH_NE:
+		return a != b;
+	case ARITH_BAND:
+		return a & b;
+	case ARITH_BXOR:
+		return a ^ b;
+	case ARITH_BOR:
+		return a | b;
+	}
+}
+
+static intmax_t assignment(int var, int noeval);
+
+static intmax_t
+primary(int token, union a_token_val *val, int op, int noeval)
+{
+	intmax_t result;
+
+	switch (token) {
+	case ARITH_LPAREN:
+		result = assignment(op, noeval);
+		if (last_token != ARITH_RPAREN)
+			arith_err("expecting ')'");
+		last_token = arith_token();
+		return result;
+	case ARITH_NUM:
+		last_token = op;
+		return val->val;
+	case ARITH_VAR:
+		last_token = op;
+		return noeval ? val->val : arith_lookupvarint(val->name);
+	case ARITH_ADD:
+		*val = a_t_val;
+		return primary(op, val, arith_token(), noeval);
+	case ARITH_SUB:
+		*val = a_t_val;
+		return -primary(op, val, arith_token(), noeval);
+	case ARITH_NOT:
+		*val = a_t_val;
+		return !primary(op, val, arith_token(), noeval);
+	case ARITH_BNOT:
+		*val = a_t_val;
+		return ~primary(op, val, arith_token(), noeval);
+	default:
+		arith_err("expecting primary");
+	}
+	return 0;	/* never reached */
+}
+
+static intmax_t
+binop2(intmax_t a, int op, int precedence, int noeval)
+{
+	union a_token_val val;
+	intmax_t b;
+	int op2;
+	int token;
+
+	for (;;) {
+		token = arith_token();
+		val = a_t_val;
+
+		b = primary(token, &val, arith_token(), noeval);
+
+		op2 = last_token;
+		if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX &&
+		    higher_prec(op2, op)) {
+			b = binop2(b, op2, arith_prec(op), noeval);
+			op2 = last_token;
+		}
+
+		a = noeval ? b : do_binop(op, a, b);
+
+		if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX ||
+		    arith_prec(op2) >= precedence)
+			return a;
+
+		op = op2;
+	}
+}
+
+static intmax_t
+binop(int token, union a_token_val *val, int op, int noeval)
+{
+	intmax_t a = primary(token, val, op, noeval);
+
+	op = last_token;
+	if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
+		return a;
+
+	return binop2(a, op, ARITH_MAX_PREC, noeval);
+}
+
+static intmax_t
+and(int token, union a_token_val *val, int op, int noeval)
+{
+	intmax_t a = binop(token, val, op, noeval);
+	intmax_t b;
+
+	op = last_token;
+	if (op != ARITH_AND)
+		return a;
+
+	token = arith_token();
+	*val = a_t_val;
+
+	b = and(token, val, arith_token(), noeval | !a);
+
+	return a && b;
+}
+
+static intmax_t
+or(int token, union a_token_val *val, int op, int noeval)
+{
+	intmax_t a = and(token, val, op, noeval);
+	intmax_t b;
+
+	op = last_token;
+	if (op != ARITH_OR)
+		return a;
+
+	token = arith_token();
+	*val = a_t_val;
+
+	b = or(token, val, arith_token(), noeval | !!a);
+
+	return a || b;
+}
+
+static intmax_t
+cond(int token, union a_token_val *val, int op, int noeval)
+{
+	intmax_t a = or(token, val, op, noeval);
+	intmax_t b;
+	intmax_t c;
+
+	if (last_token != ARITH_QMARK)
+		return a;
+
+	b = assignment(arith_token(), noeval | !a);
+
+	if (last_token != ARITH_COLON)
+		arith_err("expecting ':'");
+
+	token = arith_token();
+	*val = a_t_val;
+
+	c = cond(token, val, arith_token(), noeval | !!a);
+
+	return a ? b : c;
+}
+
+static intmax_t
+assignment(int var, int noeval)
+{
+	union a_token_val val = a_t_val;
+	int op = arith_token();
+	intmax_t result;
+	char sresult[DIGITS(result) + 1];
+
+
+	if (var != ARITH_VAR)
+		return cond(var, &val, op, noeval);
+
+	if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
+		return cond(var, &val, op, noeval);
+
+	result = assignment(arith_token(), noeval);
+	if (noeval)
+		return result;
+
+	if (op != ARITH_ASS)
+		result = do_binop(op - ARITH_ASS_GAP,
+		    arith_lookupvarint(val.name), result);
+	snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
+	setvar(val.name, sresult, 0);
+	return result;
+}
+
+intmax_t
+arith(const char *s)
+{
+	struct stackmark smark;
+	intmax_t result;
+
+	setstackmark(&smark);
+
+	arith_buf = arith_startbuf = s;
+
+	result = assignment(arith_token(), 0);
+
+	if (last_token)
+		arith_err("expecting end of expression");
+
+	popstackmark(&smark);
+
+	return result;
+}
+
+/*
+ *  The let(1)/exp(1) builtin.
+ */
+int
+expcmd(int argc, char **argv)
+{
+	const char *p;
+	char *concat;
+	char **ap;
+	intmax_t i;
+
+	if (argc > 1) {
+		p = argv[1];
+		if (argc > 2) {
+			/*
+			 * Concatenate arguments.
+			 */
+			STARTSTACKSTR(concat);
+			ap = argv + 2;
+			for (;;) {
+				while (*p)
+					STPUTC(*p++, concat);
+				if ((p = *ap++) == NULL)
+					break;
+				STPUTC(' ', concat);
+			}
+			STPUTC('\0', concat);
+			p = grabstackstr(concat);
+		}
+	} else
+		p = "";
+
+	i = arith(p);
+
+	out1fmt(ARITH_FORMAT_STR "\n", i);
+	return !i;
+}
Index: src/bin/sh/arithmetic.h
diff -u /dev/null src/bin/sh/arithmetic.h:1.1
--- /dev/null	Mon Mar 20 11:26:07 2017
+++ src/bin/sh/arithmetic.h	Mon Mar 20 11:26:07 2017
@@ -0,0 +1,42 @@
+/*	$NetBSD: arithmetic.h,v 1.1 2017/03/20 11:26:07 kre Exp $	*/
+
+/*-
+ * Copyright (c) 1995
+ *      The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ *	@(#)arith.h	1.1 (Berkeley) 5/4/95
+ *
+ * From FreeBSD, from dash
+ */
+
+#include "shell.h"
+
+#define	DIGITS(var)		(3 + (2 + CHAR_BIT * sizeof((var))) / 3)
+
+#define	ARITH_FORMAT_STR	"%" PRIdMAX
+
+intmax_t arith(const char *);

Reply via email to