Home | History | Annotate | Line # | Download | only in sh
arithmetic.c revision 1.2
      1 /*	$NetBSD: arithmetic.c,v 1.2 2017/05/29 22:54:07 kre Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  * Copyright (c) 2007
      7  *	Herbert Xu <herbert (at) gondor.apana.org.au>.  All rights reserved.
      8  *
      9  * This code is derived from software contributed to Berkeley by
     10  * Kenneth Almquist.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  * 3. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  *
     36  * From FreeBSD, who obtained it from dash, modified both times...
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 
     41 #ifndef lint
     42 __RCSID("$NetBSD: arithmetic.c,v 1.2 2017/05/29 22:54:07 kre Exp $");
     43 #endif /* not lint */
     44 
     45 #include <limits.h>
     46 #include <errno.h>
     47 #include <inttypes.h>
     48 #include <stdlib.h>
     49 #include <stdio.h>
     50 
     51 #include "shell.h"
     52 #include "arithmetic.h"
     53 #include "arith_tokens.h"
     54 #include "expand.h"
     55 #include "error.h"
     56 #include "memalloc.h"
     57 #include "output.h"
     58 #include "options.h"
     59 #include "var.h"
     60 #include "show.h"
     61 
     62 #if ARITH_BOR + ARITH_ASS_GAP != ARITH_BORASS || \
     63 	ARITH_ASS + ARITH_ASS_GAP != ARITH_EQ
     64 #error Arithmetic tokens are out of order.
     65 #endif
     66 
     67 static const char *arith_startbuf;
     68 
     69 const char *arith_buf;
     70 union a_token_val a_t_val;
     71 
     72 static int last_token;
     73 
     74 #define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
     75 
     76 static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
     77 	ARITH_PRECEDENCE(ARITH_MUL, 0),
     78 	ARITH_PRECEDENCE(ARITH_DIV, 0),
     79 	ARITH_PRECEDENCE(ARITH_REM, 0),
     80 	ARITH_PRECEDENCE(ARITH_ADD, 1),
     81 	ARITH_PRECEDENCE(ARITH_SUB, 1),
     82 	ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
     83 	ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
     84 	ARITH_PRECEDENCE(ARITH_LT, 3),
     85 	ARITH_PRECEDENCE(ARITH_LE, 3),
     86 	ARITH_PRECEDENCE(ARITH_GT, 3),
     87 	ARITH_PRECEDENCE(ARITH_GE, 3),
     88 	ARITH_PRECEDENCE(ARITH_EQ, 4),
     89 	ARITH_PRECEDENCE(ARITH_NE, 4),
     90 	ARITH_PRECEDENCE(ARITH_BAND, 5),
     91 	ARITH_PRECEDENCE(ARITH_BXOR, 6),
     92 	ARITH_PRECEDENCE(ARITH_BOR, 7),
     93 };
     94 
     95 #define ARITH_MAX_PREC 8
     96 
     97 int expcmd(int, char **);
     98 
     99 static void __dead
    100 arith_err(const char *s)
    101 {
    102 	error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
    103 	/* NOTREACHED */
    104 }
    105 
    106 static intmax_t
    107 arith_lookupvarint(char *varname)
    108 {
    109 	const char *str;
    110 	char *p;
    111 	intmax_t result;
    112 
    113 	str = lookupvar(varname);
    114 	if (uflag && str == NULL)
    115 		arith_err("variable not set");
    116 	if (str == NULL || *str == '\0')
    117 		str = "0";
    118 	errno = 0;
    119 	result = strtoimax(str, &p, 0);
    120 	if (errno != 0 || *p != '\0')
    121 		arith_err("variable contains non-numeric value");
    122 	return result;
    123 }
    124 
    125 static inline int
    126 arith_prec(int op)
    127 {
    128 
    129 	return prec[op - ARITH_BINOP_MIN];
    130 }
    131 
    132 static inline int
    133 higher_prec(int op1, int op2)
    134 {
    135 
    136 	return arith_prec(op1) < arith_prec(op2);
    137 }
    138 
    139 static intmax_t
    140 do_binop(int op, intmax_t a, intmax_t b)
    141 {
    142 
    143 	VTRACE(DBG_ARITH, ("Arith do binop %d (%jd, %jd)\n", op, a, b));
    144 	switch (op) {
    145 	default:
    146 		arith_err("token error");
    147 	case ARITH_REM:
    148 	case ARITH_DIV:
    149 		if (b == 0)
    150 			arith_err("division by zero");
    151 		if (a == INTMAX_MIN && b == -1)
    152 			arith_err("divide error");
    153 		return op == ARITH_REM ? a % b : a / b;
    154 	case ARITH_MUL:
    155 		return (uintmax_t)a * (uintmax_t)b;
    156 	case ARITH_ADD:
    157 		return (uintmax_t)a + (uintmax_t)b;
    158 	case ARITH_SUB:
    159 		return (uintmax_t)a - (uintmax_t)b;
    160 	case ARITH_LSHIFT:
    161 		return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
    162 	case ARITH_RSHIFT:
    163 		return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
    164 	case ARITH_LT:
    165 		return a < b;
    166 	case ARITH_LE:
    167 		return a <= b;
    168 	case ARITH_GT:
    169 		return a > b;
    170 	case ARITH_GE:
    171 		return a >= b;
    172 	case ARITH_EQ:
    173 		return a == b;
    174 	case ARITH_NE:
    175 		return a != b;
    176 	case ARITH_BAND:
    177 		return a & b;
    178 	case ARITH_BXOR:
    179 		return a ^ b;
    180 	case ARITH_BOR:
    181 		return a | b;
    182 	}
    183 }
    184 
    185 static intmax_t assignment(int var, int noeval);
    186 
    187 static intmax_t
    188 primary(int token, union a_token_val *val, int op, int noeval)
    189 {
    190 	intmax_t result;
    191 
    192 	VTRACE(DBG_ARITH, ("Arith primary: token %d op %d%s\n",
    193 	    token, op, noeval ? " noeval" : ""));
    194 
    195 	switch (token) {
    196 	case ARITH_LPAREN:
    197 		result = assignment(op, noeval);
    198 		if (last_token != ARITH_RPAREN)
    199 			arith_err("expecting ')'");
    200 		last_token = arith_token();
    201 		return result;
    202 	case ARITH_NUM:
    203 		last_token = op;
    204 		return val->val;
    205 	case ARITH_VAR:
    206 		last_token = op;
    207 		return noeval ? val->val : arith_lookupvarint(val->name);
    208 	case ARITH_ADD:
    209 		*val = a_t_val;
    210 		return primary(op, val, arith_token(), noeval);
    211 	case ARITH_SUB:
    212 		*val = a_t_val;
    213 		return -primary(op, val, arith_token(), noeval);
    214 	case ARITH_NOT:
    215 		*val = a_t_val;
    216 		return !primary(op, val, arith_token(), noeval);
    217 	case ARITH_BNOT:
    218 		*val = a_t_val;
    219 		return ~primary(op, val, arith_token(), noeval);
    220 	default:
    221 		arith_err("expecting primary");
    222 	}
    223 	return 0;	/* never reached */
    224 }
    225 
    226 static intmax_t
    227 binop2(intmax_t a, int op, int precedence, int noeval)
    228 {
    229 	union a_token_val val;
    230 	intmax_t b;
    231 	int op2;
    232 	int token;
    233 
    234 	VTRACE(DBG_ARITH, ("Arith: binop2 %jd op %d (P:%d)%s\n",
    235 	    a, op, precedence, noeval ? " noeval" : ""));
    236 
    237 	for (;;) {
    238 		token = arith_token();
    239 		val = a_t_val;
    240 
    241 		b = primary(token, &val, arith_token(), noeval);
    242 
    243 		op2 = last_token;
    244 		if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX &&
    245 		    higher_prec(op2, op)) {
    246 			b = binop2(b, op2, arith_prec(op), noeval);
    247 			op2 = last_token;
    248 		}
    249 
    250 		a = noeval ? b : do_binop(op, a, b);
    251 
    252 		if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX ||
    253 		    arith_prec(op2) >= precedence)
    254 			return a;
    255 
    256 		op = op2;
    257 	}
    258 }
    259 
    260 static intmax_t
    261 binop(int token, union a_token_val *val, int op, int noeval)
    262 {
    263 	intmax_t a = primary(token, val, op, noeval);
    264 
    265 	op = last_token;
    266 	if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
    267 		return a;
    268 
    269 	return binop2(a, op, ARITH_MAX_PREC, noeval);
    270 }
    271 
    272 static intmax_t
    273 and(int token, union a_token_val *val, int op, int noeval)
    274 {
    275 	intmax_t a = binop(token, val, op, noeval);
    276 	intmax_t b;
    277 
    278 	op = last_token;
    279 	if (op != ARITH_AND)
    280 		return a;
    281 
    282 	VTRACE(DBG_ARITH, ("Arith: AND %jd%s\n", a, noeval ? " noeval" : ""));
    283 
    284 	token = arith_token();
    285 	*val = a_t_val;
    286 
    287 	b = and(token, val, arith_token(), noeval | !a);
    288 
    289 	return a && b;
    290 }
    291 
    292 static intmax_t
    293 or(int token, union a_token_val *val, int op, int noeval)
    294 {
    295 	intmax_t a = and(token, val, op, noeval);
    296 	intmax_t b;
    297 
    298 	op = last_token;
    299 	if (op != ARITH_OR)
    300 		return a;
    301 
    302 	VTRACE(DBG_ARITH, ("Arith: OR %jd%s\n", a, noeval ? " noeval" : ""));
    303 
    304 	token = arith_token();
    305 	*val = a_t_val;
    306 
    307 	b = or(token, val, arith_token(), noeval | !!a);
    308 
    309 	return a || b;
    310 }
    311 
    312 static intmax_t
    313 cond(int token, union a_token_val *val, int op, int noeval)
    314 {
    315 	intmax_t a = or(token, val, op, noeval);
    316 	intmax_t b;
    317 	intmax_t c;
    318 
    319 	if (last_token != ARITH_QMARK)
    320 		return a;
    321 
    322 	VTRACE(DBG_ARITH, ("Arith: ?: %jd%s\n", a, noeval ? " noeval" : ""));
    323 
    324 	b = assignment(arith_token(), noeval | !a);
    325 
    326 	if (last_token != ARITH_COLON)
    327 		arith_err("expecting ':'");
    328 
    329 	token = arith_token();
    330 	*val = a_t_val;
    331 
    332 	c = cond(token, val, arith_token(), noeval | !!a);
    333 
    334 	return a ? b : c;
    335 }
    336 
    337 static intmax_t
    338 assignment(int var, int noeval)
    339 {
    340 	union a_token_val val = a_t_val;
    341 	int op = arith_token();
    342 	intmax_t result;
    343 	char sresult[DIGITS(result) + 1];
    344 
    345 
    346 	if (var != ARITH_VAR)
    347 		return cond(var, &val, op, noeval);
    348 
    349 	if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
    350 		return cond(var, &val, op, noeval);
    351 
    352 	VTRACE(DBG_ARITH, ("Arith: %s ASSIGN %d%s\n", val.name, op,
    353 	    noeval ? " noeval" : ""));
    354 
    355 	result = assignment(arith_token(), noeval);
    356 	if (noeval)
    357 		return result;
    358 
    359 	if (op != ARITH_ASS)
    360 		result = do_binop(op - ARITH_ASS_GAP,
    361 		    arith_lookupvarint(val.name), result);
    362 	snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
    363 	setvar(val.name, sresult, 0);
    364 	return result;
    365 }
    366 
    367 intmax_t
    368 arith(const char *s)
    369 {
    370 	struct stackmark smark;
    371 	intmax_t result;
    372 
    373 	setstackmark(&smark);
    374 
    375 	CTRACE(DBG_ARITH, ("Arith(\"%s\")\n", s));
    376 
    377 	arith_buf = arith_startbuf = s;
    378 
    379 	result = assignment(arith_token(), 0);
    380 
    381 	if (last_token)
    382 		arith_err("expecting end of expression");
    383 
    384 	popstackmark(&smark);
    385 
    386 	CTRACE(DBG_ARITH, ("Arith result=%jd\n", result));
    387 
    388 	return result;
    389 }
    390 
    391 /*
    392  *  The let(1)/exp(1) builtin.
    393  */
    394 int
    395 expcmd(int argc, char **argv)
    396 {
    397 	const char *p;
    398 	char *concat;
    399 	char **ap;
    400 	intmax_t i;
    401 
    402 	if (argc > 1) {
    403 		p = argv[1];
    404 		if (argc > 2) {
    405 			/*
    406 			 * Concatenate arguments.
    407 			 */
    408 			STARTSTACKSTR(concat);
    409 			ap = argv + 2;
    410 			for (;;) {
    411 				while (*p)
    412 					STPUTC(*p++, concat);
    413 				if ((p = *ap++) == NULL)
    414 					break;
    415 				STPUTC(' ', concat);
    416 			}
    417 			STPUTC('\0', concat);
    418 			p = grabstackstr(concat);
    419 		}
    420 	} else
    421 		p = "";
    422 
    423 	i = arith(p);
    424 
    425 	out1fmt(ARITH_FORMAT_STR "\n", i);
    426 	return !i;
    427 }
    428