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