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 <[email protected]>. 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 <[email protected]>. 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 *);