summaryrefslogtreecommitdiff
path: root/bin/sh/arithmetic.c
diff options
context:
space:
mode:
authorKiyoshi Aman <kiyoshi.aman+adelie@gmail.com>2019-02-01 22:55:37 +0000
committerKiyoshi Aman <kiyoshi.aman+adelie@gmail.com>2019-02-03 18:22:05 -0600
commit5b57d28ffb6e1ef86b50f7d05d977826eae89bfe (patch)
tree154a22fe556b49e6927197336f8bf91b12eacd5e /bin/sh/arithmetic.c
downloaduserland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.gz
userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.bz2
userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.xz
userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.zip
initial population
Diffstat (limited to 'bin/sh/arithmetic.c')
-rw-r--r--bin/sh/arithmetic.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/bin/sh/arithmetic.c b/bin/sh/arithmetic.c
new file mode 100644
index 0000000..f9c91a4
--- /dev/null
+++ b/bin/sh/arithmetic.c
@@ -0,0 +1,502 @@
+/* $NetBSD: arithmetic.c,v 1.5 2018/04/21 23:01:29 kre Exp $ */
+
+/*-
+ * 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.
+ *
+ * From FreeBSD, who obtained it from dash, modified both times...
+ */
+
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: arithmetic.c,v 1.5 2018/04/21 23:01:29 kre Exp $");
+#endif /* not lint */
+
+#include <limits.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.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"
+#include "show.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
+
+static const char *arith_startbuf;
+
+const char *arith_buf;
+union a_token_val a_t_val;
+
+static int last_token;
+
+int arith_lno, arith_var_lno;
+
+#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;
+ const int oln = line_number;
+
+ VTRACE(DBG_ARITH, ("Arith var lookup(\"%s\") with lno=%d\n", varname,
+ arith_var_lno));
+
+ line_number = arith_var_lno;
+ str = lookupvar(varname);
+ line_number = oln;
+
+ 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') {
+ if (errno == 0) {
+ while (*p != '\0' && is_space(*p))
+ p++;
+ if (*p == '\0')
+ return result;
+ }
+ 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)
+{
+
+ VTRACE(DBG_ARITH, ("Arith do binop %d (%jd, %jd)\n", op, a, 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, int);
+static intmax_t comma_list(int, int);
+
+static intmax_t
+primary(int token, union a_token_val *val, int op, int noeval)
+{
+ intmax_t result;
+ char sresult[DIGITS(result) + 1];
+
+ VTRACE(DBG_ARITH, ("Arith primary: token %d op %d%s\n",
+ token, op, noeval ? " noeval" : ""));
+
+ switch (token) {
+ case ARITH_LPAREN:
+ result = comma_list(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:
+ result = noeval ? val->val : arith_lookupvarint(val->name);
+ if (op == ARITH_INCR || op == ARITH_DECR) {
+ last_token = arith_token();
+ if (noeval)
+ return val->val;
+
+ snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR,
+ result + (op == ARITH_INCR ? 1 : -1));
+ setvar(val->name, sresult, 0);
+ } else
+ last_token = op;
+ return result;
+ 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);
+ case ARITH_INCR:
+ case ARITH_DECR:
+ if (op != ARITH_VAR)
+ arith_err("incr/decr require var name");
+ last_token = arith_token();
+ if (noeval)
+ return val->val;
+ result = arith_lookupvarint(a_t_val.name);
+ snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR,
+ result += (token == ARITH_INCR ? 1 : -1));
+ setvar(a_t_val.name, sresult, 0);
+ return result;
+ 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;
+
+ VTRACE(DBG_ARITH, ("Arith: binop2 %jd op %d (P:%d)%s\n",
+ a, op, precedence, noeval ? " noeval" : ""));
+
+ 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;
+
+ VTRACE(DBG_ARITH, ("Arith: AND %jd%s\n", a, noeval ? " noeval" : ""));
+
+ 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;
+
+ VTRACE(DBG_ARITH, ("Arith: OR %jd%s\n", a, noeval ? " noeval" : ""));
+
+ 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;
+
+ VTRACE(DBG_ARITH, ("Arith: ?: %jd%s\n", a, noeval ? " noeval" : ""));
+
+ 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);
+
+ VTRACE(DBG_ARITH, ("Arith: %s ASSIGN %d%s\n", val.name, op,
+ noeval ? " 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;
+}
+
+static intmax_t
+comma_list(int token, int noeval)
+{
+ intmax_t result = assignment(token, noeval);
+
+ while (last_token == ARITH_COMMA) {
+ VTRACE(DBG_ARITH, ("Arith: comma discarding %jd%s\n", result,
+ noeval ? " noeval" : ""));
+ result = assignment(arith_token(), noeval);
+ }
+
+ return result;
+}
+
+intmax_t
+arith(const char *s, int lno)
+{
+ struct stackmark smark;
+ intmax_t result;
+ const char *p;
+ int nls = 0;
+
+ setstackmark(&smark);
+
+ arith_lno = lno;
+
+ CTRACE(DBG_ARITH, ("Arith(\"%s\", %d) @%d\n", s, lno, arith_lno));
+
+ /* check if it is possible we might reference LINENO */
+ p = s;
+ while ((p = strchr(p, 'L')) != NULL) {
+ if (p[1] == 'I' && p[2] == 'N') {
+ /* if it is possible, we need to correct airth_lno */
+ p = s;
+ while ((p = strchr(p, '\n')) != NULL)
+ nls++, p++;
+ VTRACE(DBG_ARITH, ("Arith found %d newlines\n", nls));
+ arith_lno -= nls;
+ break;
+ }
+ p++;
+ }
+
+ arith_buf = arith_startbuf = s;
+
+ result = comma_list(arith_token(), 0);
+
+ if (last_token)
+ arith_err("expecting end of expression");
+
+ popstackmark(&smark);
+
+ CTRACE(DBG_ARITH, ("Arith result=%jd\n", result));
+
+ 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, line_number);
+
+ out1fmt(ARITH_FORMAT_STR "\n", i);
+ return !i;
+}