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