summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2011-02-08 23:18:06 +0000
committerjilles <jilles@FreeBSD.org>2011-02-08 23:18:06 +0000
commit1cbab8a321b4fa3392f1ed1ec381b3fadbc41558 (patch)
treebb9c25c32a33a77e92cd3306937e474e7efe2da0 /bin
parent6543d03b931354fdbab3e83e273b87c539c81231 (diff)
downloadFreeBSD-src-1cbab8a321b4fa3392f1ed1ec381b3fadbc41558.zip
FreeBSD-src-1cbab8a321b4fa3392f1ed1ec381b3fadbc41558.tar.gz
sh: Import arithmetic expression code from dash.
New features: * proper lazy evaluation of || and && * ?: ternary operator * executable is considerably smaller (8K on i386) because lex and yacc are no longer used Differences from dash: * arith_t instead of intmax_t * imaxdiv() not used * unset or null variables default to 0 * let/exp builtin (undocumented, will probably be removed later) Obtained from: dash
Diffstat (limited to 'bin')
-rw-r--r--bin/sh/Makefile10
-rw-r--r--bin/sh/arith.h2
-rw-r--r--bin/sh/arith.y366
-rw-r--r--bin/sh/arith_lex.l143
-rw-r--r--bin/sh/arith_yacc.c376
-rw-r--r--bin/sh/arith_yacc.h91
-rw-r--r--bin/sh/arith_yylex.c244
7 files changed, 716 insertions, 516 deletions
diff --git a/bin/sh/Makefile b/bin/sh/Makefile
index 47642df..afca8a3 100644
--- a/bin/sh/Makefile
+++ b/bin/sh/Makefile
@@ -3,22 +3,22 @@
PROG= sh
INSTALLFLAGS= -S
-SHSRCS= alias.c arith.y arith_lex.l cd.c echo.c error.c eval.c exec.c expand.c \
+SHSRCS= alias.c arith_yacc.c arith_yylex.c cd.c echo.c error.c eval.c \
+ exec.c expand.c \
histedit.c input.c jobs.c kill.c mail.c main.c memalloc.c miscbltin.c \
mystring.c options.c output.c parser.c printf.c redir.c show.c \
test.c trap.c var.c
GENSRCS= builtins.c init.c nodes.c syntax.c
GENHDRS= builtins.h nodes.h syntax.h token.h
-SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} y.tab.h
+SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS}
# MLINKS for Shell built in commands for which there are no userland
# utilities of the same name are handled with the associated manpage,
# builtin.1 in share/man/man1/.
-DPADD= ${LIBL} ${LIBEDIT} ${LIBTERMCAP}
-LDADD= -ll -ledit -ltermcap
+DPADD= ${LIBEDIT} ${LIBTERMCAP}
+LDADD= -ledit -ltermcap
-LFLAGS= -8 # 8-bit lex scanner for arithmetic
CFLAGS+=-DSHELL -I. -I${.CURDIR}
# for debug:
# DEBUG_FLAGS+= -g -DDEBUG=2 -fno-inline
diff --git a/bin/sh/arith.h b/bin/sh/arith.h
index 6a143a4..3a7db9e 100644
--- a/bin/sh/arith.h
+++ b/bin/sh/arith.h
@@ -34,8 +34,6 @@
#define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
-extern const char *arith_buf, *arith_startbuf;
-
arith_t arith(const char *);
void arith_lex_reset(void);
int expcmd(int, char **);
diff --git a/bin/sh/arith.y b/bin/sh/arith.y
deleted file mode 100644
index 28046ba..0000000
--- a/bin/sh/arith.y
+++ /dev/null
@@ -1,366 +0,0 @@
-%{
-/*-
- * Copyright (c) 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.
- * 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.
- */
-
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95";
-#endif
-#endif /* not lint */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <limits.h>
-#include <stdio.h>
-
-#include "arith.h"
-#include "shell.h"
-#include "var.h"
-%}
-%union {
- arith_t l_value;
- char* s_value;
-}
-%token <l_value> ARITH_NUM ARITH_LPAREN ARITH_RPAREN
-%token <s_value> ARITH_VAR
-
-%type <l_value> expr
-%right ARITH_ASSIGN
-%right ARITH_ADDASSIGN ARITH_SUBASSIGN
-%right ARITH_MULASSIGN ARITH_DIVASSIGN ARITH_REMASSIGN
-%right ARITH_RSHASSIGN ARITH_LSHASSIGN
-%right ARITH_BANDASSIGN ARITH_BXORASSIGN ARITH_BORASSIGN
-%left ARITH_OR
-%left ARITH_AND
-%left ARITH_BOR
-%left ARITH_BXOR
-%left ARITH_BAND
-%left ARITH_EQ ARITH_NE
-%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE
-%left ARITH_LSHIFT ARITH_RSHIFT
-%left ARITH_ADD ARITH_SUB
-%left ARITH_MUL ARITH_DIV ARITH_REM
-%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT
-%%
-
-exp:
- expr
- {
- *YYPARSE_PARAM = $1;
- return (0);
- }
- ;
-
-expr:
- ARITH_LPAREN expr ARITH_RPAREN
- { $$ = $2; } |
- expr ARITH_OR expr
- { $$ = $1 || $3; } |
- expr ARITH_AND expr
- { $$ = $1 && $3; } |
- expr ARITH_BOR expr
- { $$ = $1 | $3; } |
- expr ARITH_BXOR expr
- { $$ = $1 ^ $3; } |
- expr ARITH_BAND expr
- { $$ = $1 & $3; } |
- expr ARITH_EQ expr
- { $$ = $1 == $3; } |
- expr ARITH_GT expr
- { $$ = $1 > $3; } |
- expr ARITH_GE expr
- { $$ = $1 >= $3; } |
- expr ARITH_LT expr
- { $$ = $1 < $3; } |
- expr ARITH_LE expr
- { $$ = $1 <= $3; } |
- expr ARITH_NE expr
- { $$ = $1 != $3; } |
- expr ARITH_LSHIFT expr
- { $$ = $1 << $3; } |
- expr ARITH_RSHIFT expr
- { $$ = $1 >> $3; } |
- expr ARITH_ADD expr
- { $$ = $1 + $3; } |
- expr ARITH_SUB expr
- { $$ = $1 - $3; } |
- expr ARITH_MUL expr
- { $$ = $1 * $3; } |
- expr ARITH_DIV expr
- {
- if ($3 == 0)
- yyerror("division by zero");
- $$ = $1 / $3;
- } |
- expr ARITH_REM expr
- {
- if ($3 == 0)
- yyerror("division by zero");
- $$ = $1 % $3;
- } |
- ARITH_NOT expr
- { $$ = !($2); } |
- ARITH_BNOT expr
- { $$ = ~($2); } |
- ARITH_SUB expr %prec ARITH_UNARYMINUS
- { $$ = -($2); } |
- ARITH_ADD expr %prec ARITH_UNARYPLUS
- { $$ = $2; } |
- ARITH_NUM |
- ARITH_VAR
- {
- char *p;
- arith_t arith_val;
- char *str_val;
-
- if (lookupvar($1) == NULL)
- setvarsafe($1, "0", 0);
- str_val = lookupvar($1);
- arith_val = strtoarith_t(str_val, &p, 0);
- /*
- * Conversion is successful only in case
- * we've converted _all_ characters.
- */
- if (*p != '\0')
- yyerror("variable conversion error");
- $$ = arith_val;
- } |
- ARITH_VAR ARITH_ASSIGN expr
- {
- if (arith_assign($1, $3) != 0)
- yyerror("variable assignment error");
- $$ = $3;
- } |
- ARITH_VAR ARITH_ADDASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) + $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_SUBASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) - $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_MULASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) * $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_DIVASSIGN expr
- {
- arith_t value;
-
- if ($3 == 0)
- yyerror("division by zero");
-
- value = atoarith_t(lookupvar($1)) / $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_REMASSIGN expr
- {
- arith_t value;
-
- if ($3 == 0)
- yyerror("division by zero");
-
- value = atoarith_t(lookupvar($1)) % $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_RSHASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) >> $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_LSHASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) << $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_BANDASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) & $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_BXORASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) ^ $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } |
- ARITH_VAR ARITH_BORASSIGN expr
- {
- arith_t value;
-
- value = atoarith_t(lookupvar($1)) | $3;
- if (arith_assign($1, value) != 0)
- yyerror("variable assignment error");
- $$ = value;
- } ;
-%%
-#include "error.h"
-#include "output.h"
-#include "memalloc.h"
-
-#define YYPARSE_PARAM_TYPE arith_t *
-#define YYPARSE_PARAM result
-
-const char *arith_buf, *arith_startbuf;
-
-int yylex(void);
-int yyparse(YYPARSE_PARAM_TYPE);
-
-static int
-arith_assign(char *name, arith_t value)
-{
- char *str;
- int ret;
-
- str = (char *)ckmalloc(DIGITS(value));
- sprintf(str, ARITH_FORMAT_STR, value);
- ret = setvarsafe(name, str, 0);
- free(str);
- return ret;
-}
-
-arith_t
-arith(const char *s)
-{
- arith_t result;
- struct stackmark smark;
-
- setstackmark(&smark);
- arith_buf = arith_startbuf = s;
-
- INTOFF;
- yyparse(&result);
- arith_lex_reset(); /* Reprime lex. */
- INTON;
-
- popstackmark(&smark);
-
- return result;
-}
-
-static void
-yyerror(const char *s)
-{
-
- yyerrok;
- yyclearin;
- arith_lex_reset(); /* Reprime lex. */
- error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
-}
-
-/*
- * The exp(1) builtin.
- */
-int
-expcmd(int argc, char **argv)
-{
- const char *p;
- char *concat;
- char **ap;
- arith_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;
-}
-
-/*************************/
-#ifdef TEST_ARITH
-#include <stdio.h>
-main(int argc, char *argv[])
-{
- printf("%d\n", exp(argv[1]));
-}
-
-error(const char *s)
-{
- fprintf(stderr, "exp: %s\n", s);
- exit(1);
-}
-#endif
diff --git a/bin/sh/arith_lex.l b/bin/sh/arith_lex.l
deleted file mode 100644
index f93608a..0000000
--- a/bin/sh/arith_lex.l
+++ /dev/null
@@ -1,143 +0,0 @@
-%{
-/*-
- * Copyright (c) 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.
- * 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.
- */
-
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95";
-#endif
-#endif /* not lint */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <string.h>
-
-#include "arith.h"
-#include "shell.h"
-#include "y.tab.h"
-#include "error.h"
-#include "memalloc.h"
-#include "var.h"
-
-int yylex(void);
-
-#undef YY_INPUT
-#define YY_INPUT(buf,result,max) \
- do { \
- result = strnlen(arith_buf, max); \
- if (result == 0) \
- result = YY_NULL; \
- else { \
- memcpy(buf, arith_buf, result); \
- arith_buf += result; \
- } \
- } while (0);
-#define YY_NO_UNPUT
-#define YY_NO_INPUT
-%}
-
-%%
-[ \t\n] { ; }
-
-0x[a-fA-F0-9]+ {
- yylval.l_value = strtoarith_t(yytext, NULL, 16);
- return ARITH_NUM;
- }
-
-0[0-7]* {
- yylval.l_value = strtoarith_t(yytext, NULL, 8);
- return ARITH_NUM;
- }
-
-[1-9][0-9]* {
- yylval.l_value = strtoarith_t(yytext, NULL, 10);
- return ARITH_NUM;
- }
-
-[A-Za-z][A-Za-z0-9_]* {
- /*
- * If variable doesn't exist, we should initialize
- * it to zero.
- */
- char *temp;
- if (lookupvar(yytext) == NULL)
- setvarsafe(yytext, "0", 0);
- temp = stalloc(strlen(yytext) + 1);
- yylval.s_value = strcpy(temp, yytext);
-
- return ARITH_VAR;
- }
-
-"(" { return ARITH_LPAREN; }
-")" { return ARITH_RPAREN; }
-"||" { return ARITH_OR; }
-"&&" { return ARITH_AND; }
-"|" { return ARITH_BOR; }
-"^" { return ARITH_BXOR; }
-"&" { return ARITH_BAND; }
-"==" { return ARITH_EQ; }
-"!=" { return ARITH_NE; }
-">" { return ARITH_GT; }
-">=" { return ARITH_GE; }
-"<" { return ARITH_LT; }
-"<=" { return ARITH_LE; }
-"<<" { return ARITH_LSHIFT; }
-">>" { return ARITH_RSHIFT; }
-"*" { return ARITH_MUL; }
-"/" { return ARITH_DIV; }
-"%" { return ARITH_REM; }
-"+" { return ARITH_ADD; }
-"-" { return ARITH_SUB; }
-"~" { return ARITH_BNOT; }
-"!" { return ARITH_NOT; }
-"=" { return ARITH_ASSIGN; }
-"+=" { return ARITH_ADDASSIGN; }
-"-=" { return ARITH_SUBASSIGN; }
-"*=" { return ARITH_MULASSIGN; }
-"/=" { return ARITH_DIVASSIGN; }
-"%=" { return ARITH_REMASSIGN; }
-">>=" { return ARITH_RSHASSIGN; }
-"<<=" { return ARITH_LSHASSIGN; }
-"&=" { return ARITH_BANDASSIGN; }
-"^=" { return ARITH_BXORASSIGN; }
-"|=" { return ARITH_BORASSIGN; }
-. {
- error("arith: syntax error: \"%s\"\n", arith_startbuf);
- }
-%%
-
-void
-arith_lex_reset(void)
-{
- YY_NEW_FILE;
-}
diff --git a/bin/sh/arith_yacc.c b/bin/sh/arith_yacc.c
new file mode 100644
index 0000000..423e4e2
--- /dev/null
+++ b/bin/sh/arith_yacc.c
@@ -0,0 +1,376 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2007
+ * Herbert Xu <herbert@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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/limits.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "arith.h"
+#include "arith_yacc.h"
+#include "expand.h"
+#include "shell.h"
+#include "error.h"
+#include "memalloc.h"
+#include "output.h"
+#include "options.h"
+#include "var.h"
+
+#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
+#error Arithmetic tokens are out of order.
+#endif
+
+static const char *arith_startbuf;
+
+const char *arith_buf;
+union yystype yylval;
+
+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
+
+static __dead2 void yyerror(const char *s)
+{
+ error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+ /* NOTREACHED */
+}
+
+static arith_t arith_lookupvarint(char *varname)
+{
+ const char *str;
+ char *p;
+ arith_t result;
+
+ str = lookupvar(varname);
+ if (str == NULL || *str == '\0')
+ str = "0";
+ errno = 0;
+ result = strtoarith_t(str, &p, 0);
+ if (errno != 0 || *p != '\0')
+ yyerror("variable conversion error");
+ 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 arith_t do_binop(int op, arith_t a, arith_t b)
+{
+
+ switch (op) {
+ default:
+ case ARITH_REM:
+ case ARITH_DIV:
+ if (!b)
+ yyerror("division by zero");
+ return op == ARITH_REM ? a % b : a / b;
+ case ARITH_MUL:
+ return a * b;
+ case ARITH_ADD:
+ return a + b;
+ case ARITH_SUB:
+ return a - b;
+ case ARITH_LSHIFT:
+ return a << b;
+ case ARITH_RSHIFT:
+ return a >> b;
+ 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 arith_t assignment(int var, int noeval);
+
+static arith_t primary(int token, union yystype *val, int op, int noeval)
+{
+ arith_t result;
+
+again:
+ switch (token) {
+ case ARITH_LPAREN:
+ result = assignment(op, noeval);
+ if (last_token != ARITH_RPAREN)
+ yyerror("expecting ')'");
+ last_token = yylex();
+ 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:
+ token = op;
+ *val = yylval;
+ op = yylex();
+ goto again;
+ case ARITH_SUB:
+ *val = yylval;
+ return -primary(op, val, yylex(), noeval);
+ case ARITH_NOT:
+ *val = yylval;
+ return !primary(op, val, yylex(), noeval);
+ case ARITH_BNOT:
+ *val = yylval;
+ return ~primary(op, val, yylex(), noeval);
+ default:
+ yyerror("expecting primary");
+ }
+}
+
+static arith_t binop2(arith_t a, int op, int prec, int noeval)
+{
+ for (;;) {
+ union yystype val;
+ arith_t b;
+ int op2;
+ int token;
+
+ token = yylex();
+ val = yylval;
+
+ b = primary(token, &val, yylex(), 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) >= prec)
+ return a;
+
+ op = op2;
+ }
+}
+
+static arith_t binop(int token, union yystype *val, int op, int noeval)
+{
+ arith_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 arith_t and(int token, union yystype *val, int op, int noeval)
+{
+ arith_t a = binop(token, val, op, noeval);
+ arith_t b;
+
+ op = last_token;
+ if (op != ARITH_AND)
+ return a;
+
+ token = yylex();
+ *val = yylval;
+
+ b = and(token, val, yylex(), noeval | !a);
+
+ return a && b;
+}
+
+static arith_t or(int token, union yystype *val, int op, int noeval)
+{
+ arith_t a = and(token, val, op, noeval);
+ arith_t b;
+
+ op = last_token;
+ if (op != ARITH_OR)
+ return a;
+
+ token = yylex();
+ *val = yylval;
+
+ b = or(token, val, yylex(), noeval | !!a);
+
+ return a || b;
+}
+
+static arith_t cond(int token, union yystype *val, int op, int noeval)
+{
+ arith_t a = or(token, val, op, noeval);
+ arith_t b;
+ arith_t c;
+
+ if (last_token != ARITH_QMARK)
+ return a;
+
+ b = assignment(yylex(), noeval | !a);
+
+ if (last_token != ARITH_COLON)
+ yyerror("expecting ':'");
+
+ token = yylex();
+ *val = yylval;
+
+ c = cond(token, val, yylex(), noeval | !!a);
+
+ return a ? b : c;
+}
+
+static arith_t assignment(int var, int noeval)
+{
+ union yystype val = yylval;
+ int op = yylex();
+ arith_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(yylex(), noeval);
+ if (noeval)
+ return result;
+
+ if (op != ARITH_ASS)
+ result = do_binop(op - 11, arith_lookupvarint(val.name), result);
+ snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
+ setvar(val.name, sresult, 0);
+ return result;
+}
+
+arith_t arith(const char *s)
+{
+ struct stackmark smark;
+ arith_t result;
+
+ setstackmark(&smark);
+
+ arith_buf = arith_startbuf = s;
+
+ result = assignment(yylex(), 0);
+
+ if (last_token)
+ yyerror("expecting EOF");
+
+ popstackmark(&smark);
+
+ return result;
+}
+
+/*
+ * The exp(1) builtin.
+ */
+int
+expcmd(int argc, char **argv)
+{
+ const char *p;
+ char *concat;
+ char **ap;
+ arith_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;
+}
+
diff --git a/bin/sh/arith_yacc.h b/bin/sh/arith_yacc.h
new file mode 100644
index 0000000..5d8354f
--- /dev/null
+++ b/bin/sh/arith_yacc.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2007
+ * Herbert Xu <herbert@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.
+ *
+ * $FreeBSD$
+ */
+
+#define ARITH_ASS 1
+
+#define ARITH_OR 2
+#define ARITH_AND 3
+#define ARITH_BAD 4
+#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
+#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 24
+#define ARITH_REMASS 24
+#define ARITH_BANDASS 25
+#define ARITH_LSHIFTASS 26
+#define ARITH_RSHIFTASS 27
+#define ARITH_MULASS 28
+#define ARITH_ADDASS 29
+#define ARITH_BORASS 30
+#define ARITH_SUBASS 31
+#define ARITH_BXORASS 32
+#define ARITH_DIVASS 33
+#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
+
+union yystype {
+ arith_t val;
+ char *name;
+};
+
+extern union yystype yylval;
+
+int yylex(void);
diff --git a/bin/sh/arith_yylex.c b/bin/sh/arith_yylex.c
new file mode 100644
index 0000000..0d08a83
--- /dev/null
+++ b/bin/sh/arith_yylex.c
@@ -0,0 +1,244 @@
+/*-
+ * Copyright (c) 2002
+ * Herbert Xu.
+ * Copyright (c) 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "shell.h"
+#include "arith_yacc.h"
+#include "expand.h"
+#include "error.h"
+#include "memalloc.h"
+#include "parser.h"
+#include "syntax.h"
+
+#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
+#error Arithmetic tokens are out of order.
+#endif
+
+extern const char *arith_buf;
+
+int
+yylex()
+{
+ int value;
+ const char *buf = arith_buf;
+ const char *p;
+
+ for (;;) {
+ value = *buf;
+ switch (value) {
+ case ' ':
+ case '\t':
+ case '\n':
+ buf++;
+ continue;
+ default:
+ return ARITH_BAD;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ yylval.val = strtoarith_t(buf, (char **)&arith_buf, 0);
+ 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 '_':
+ 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':
+ p = buf;
+ while (buf++, is_in_name(*buf))
+ ;
+ yylval.name = stalloc(buf - p + 1);
+ memcpy(yylval.name, p, buf - p);
+ yylval.name[buf - p] = '\0';
+ value = ARITH_VAR;
+ goto out;
+ case '=':
+ value += ARITH_ASS - '=';
+checkeq:
+ buf++;
+checkeqcur:
+ if (*buf != '=')
+ goto out;
+ value += 11;
+ break;
+ case '>':
+ switch (*++buf) {
+ case '=':
+ value += ARITH_GE - '>';
+ break;
+ case '>':
+ value += ARITH_RSHIFT - '>';
+ goto checkeq;
+ default:
+ value += ARITH_GT - '>';
+ goto out;
+ }
+ break;
+ case '<':
+ switch (*++buf) {
+ case '=':
+ value += ARITH_LE - '<';
+ break;
+ case '<':
+ value += ARITH_LSHIFT - '<';
+ goto checkeq;
+ default:
+ value += ARITH_LT - '<';
+ goto out;
+ }
+ break;
+ case '|':
+ if (*++buf != '|') {
+ value += ARITH_BOR - '|';
+ goto checkeqcur;
+ }
+ value += ARITH_OR - '|';
+ break;
+ case '&':
+ if (*++buf != '&') {
+ value += ARITH_BAND - '&';
+ goto checkeqcur;
+ }
+ value += ARITH_AND - '&';
+ break;
+ case '!':
+ if (*++buf != '=') {
+ value += ARITH_NOT - '!';
+ goto out;
+ }
+ value += ARITH_NE - '!';
+ break;
+ case 0:
+ goto out;
+ case '(':
+ value += ARITH_LPAREN - '(';
+ break;
+ case ')':
+ value += ARITH_RPAREN - ')';
+ break;
+ case '*':
+ value += ARITH_MUL - '*';
+ goto checkeq;
+ case '/':
+ value += ARITH_DIV - '/';
+ goto checkeq;
+ case '%':
+ value += ARITH_REM - '%';
+ goto checkeq;
+ case '+':
+ value += ARITH_ADD - '+';
+ goto checkeq;
+ case '-':
+ value += ARITH_SUB - '-';
+ goto checkeq;
+ case '~':
+ value += ARITH_BNOT - '~';
+ break;
+ case '^':
+ value += ARITH_BXOR - '^';
+ goto checkeq;
+ case '?':
+ value += ARITH_QMARK - '?';
+ break;
+ case ':':
+ value += ARITH_COLON - ':';
+ break;
+ }
+ break;
+ }
+
+ buf++;
+out:
+ arith_buf = buf;
+ return value;
+}
OpenPOWER on IntegriCloud