master xplshn/aruu / cmd / posix / sh / arith_yacc.c
  1/*-
  2 * SPDX-License-Identifier: BSD-3-Clause
  3 *
  4 * Copyright (c) 1993
  5 *	The Regents of the University of California.  All rights reserved.
  6 * Copyright (c) 2007
  7 *	Herbert Xu <herbert@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
 37#if defined(__has_include)
 38#if __has_include(<sys/cdefs.h>)
 39#include <sys/cdefs.h>
 40#endif
 41#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
 42#include <sys/cdefs.h>
 43#endif
 44#include "arith.h"
 45#include "arith_yacc.h"
 46#include "error.h"
 47#include "expand.h"
 48#include "memalloc.h"
 49#include "options.h"
 50#include "output.h"
 51#include "shell.h"
 52#include "var.h"
 53#include <errno.h>
 54#include <inttypes.h>
 55#include <limits.h>
 56#include <stdio.h>
 57#include <stdlib.h>
 58
 59#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
 60#error Arithmetic tokens are out of order.
 61#endif
 62
 63static const char *arith_startbuf;
 64
 65const char   *arith_buf;
 66union yystype yylval;
 67
 68static int last_token;
 69
 70#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
 71
 72static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
 73    ARITH_PRECEDENCE(ARITH_MUL, 0),
 74    ARITH_PRECEDENCE(ARITH_DIV, 0),
 75    ARITH_PRECEDENCE(ARITH_REM, 0),
 76    ARITH_PRECEDENCE(ARITH_ADD, 1),
 77    ARITH_PRECEDENCE(ARITH_SUB, 1),
 78    ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
 79    ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
 80    ARITH_PRECEDENCE(ARITH_LT, 3),
 81    ARITH_PRECEDENCE(ARITH_LE, 3),
 82    ARITH_PRECEDENCE(ARITH_GT, 3),
 83    ARITH_PRECEDENCE(ARITH_GE, 3),
 84    ARITH_PRECEDENCE(ARITH_EQ, 4),
 85    ARITH_PRECEDENCE(ARITH_NE, 4),
 86    ARITH_PRECEDENCE(ARITH_BAND, 5),
 87    ARITH_PRECEDENCE(ARITH_BXOR, 6),
 88    ARITH_PRECEDENCE(ARITH_BOR, 7),
 89};
 90
 91#define ARITH_MAX_PREC 8
 92
 93int letcmd(int, char **);
 94
 95static __dead2 void
 96yyerror(const char *s)
 97{
 98  error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
 99  /* NOTREACHED */
100}
101
102static arith_t
103arith_lookupvarint(char *varname)
104{
105  const char *str;
106  char       *p;
107  arith_t     result;
108
109  str = lookupvar(varname);
110  if (uflag && str == NULL)
111    yyerror("variable not set");
112  if (str == NULL || *str == '\0')
113    str = "0";
114  errno  = 0;
115  result = strtoarith_t(str, &p);
116  if (errno != 0 || *p != '\0')
117    yyerror("variable conversion error");
118  return result;
119}
120
121static inline int
122arith_prec(int op)
123{
124  return prec[op - ARITH_BINOP_MIN];
125}
126
127static inline int
128higher_prec(int op1, int op2)
129{
130  return arith_prec(op1) < arith_prec(op2);
131}
132
133static arith_t
134do_binop(int op, arith_t a, arith_t b)
135{
136  switch (op) {
137    default:
138    case ARITH_REM:
139    case ARITH_DIV:
140      if (!b)
141        yyerror("division by zero");
142      if (a == ARITH_MIN && b == -1)
143        yyerror("divide error");
144      return op == ARITH_REM ? a % b : a / b;
145    case ARITH_MUL:
146      return (uintmax_t)a * (uintmax_t)b;
147    case ARITH_ADD:
148      return (uintmax_t)a + (uintmax_t)b;
149    case ARITH_SUB:
150      return (uintmax_t)a - (uintmax_t)b;
151    case ARITH_LSHIFT:
152      return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
153    case ARITH_RSHIFT:
154      return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
155    case ARITH_LT:
156      return a < b;
157    case ARITH_LE:
158      return a <= b;
159    case ARITH_GT:
160      return a > b;
161    case ARITH_GE:
162      return a >= b;
163    case ARITH_EQ:
164      return a == b;
165    case ARITH_NE:
166      return a != b;
167    case ARITH_BAND:
168      return a & b;
169    case ARITH_BXOR:
170      return a ^ b;
171    case ARITH_BOR:
172      return a | b;
173  }
174}
175
176static arith_t assignment(int var, int noeval);
177
178static arith_t
179primary(int token, union yystype *val, int op, int noeval)
180{
181  arith_t result;
182
183again:
184  switch (token) {
185    case ARITH_LPAREN:
186      result = assignment(op, noeval);
187      if (last_token != ARITH_RPAREN)
188        yyerror("expecting ')'");
189      last_token = yylex();
190      return result;
191    case ARITH_NUM:
192      last_token = op;
193      return val->val;
194    case ARITH_VAR:
195      last_token = op;
196      return noeval ? val->val : arith_lookupvarint(val->name);
197    case ARITH_ADD:
198      token = op;
199      *val  = yylval;
200      op    = yylex();
201      goto again;
202    case ARITH_SUB:
203      *val = yylval;
204      return -primary(op, val, yylex(), noeval);
205    case ARITH_NOT:
206      *val = yylval;
207      return !primary(op, val, yylex(), noeval);
208    case ARITH_BNOT:
209      *val = yylval;
210      return ~primary(op, val, yylex(), noeval);
211    default:
212      yyerror("expecting primary");
213  }
214}
215
216static arith_t
217binop2(arith_t a, int op, int precedence, int noeval)
218{
219  for (;;) {
220    union yystype val;
221    arith_t       b;
222    int           op2;
223    int           token;
224
225    token = yylex();
226    val   = yylval;
227
228    b = primary(token, &val, yylex(), noeval);
229
230    op2 = last_token;
231    if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX && higher_prec(op2, op)) {
232      b   = binop2(b, op2, arith_prec(op), noeval);
233      op2 = last_token;
234    }
235
236    a = noeval ? b : do_binop(op, a, b);
237
238    if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX || arith_prec(op2) >= precedence)
239      return a;
240
241    op = op2;
242  }
243}
244
245static arith_t
246binop(int token, union yystype *val, int op, int noeval)
247{
248  arith_t a = primary(token, val, op, noeval);
249
250  op = last_token;
251  if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
252    return a;
253
254  return binop2(a, op, ARITH_MAX_PREC, noeval);
255}
256
257static arith_t and (int token, union yystype *val, int op, int noeval)
258{
259  arith_t a = binop(token, val, op, noeval);
260  arith_t b;
261
262  op = last_token;
263  if (op != ARITH_AND)
264    return a;
265
266  token = yylex();
267  *val  = yylval;
268
269  b = and(token, val, yylex(), noeval | !a);
270
271  return a && b;
272}
273
274static arith_t or (int token, union yystype *val, int op, int noeval)
275{
276  arith_t a = and(token, val, op, noeval);
277  arith_t b;
278
279  op = last_token;
280  if (op != ARITH_OR)
281    return a;
282
283  token = yylex();
284  *val  = yylval;
285
286  b = or (token, val, yylex(), noeval | !!a);
287
288  return a || b;
289}
290
291static arith_t
292cond(int token, union yystype *val, int op, int noeval)
293{
294  arith_t a = or (token, val, op, noeval);
295  arith_t b;
296  arith_t c;
297
298  if (last_token != ARITH_QMARK)
299    return a;
300
301  b = assignment(yylex(), noeval | !a);
302
303  if (last_token != ARITH_COLON)
304    yyerror("expecting ':'");
305
306  token = yylex();
307  *val  = yylval;
308
309  c = cond(token, val, yylex(), noeval | !!a);
310
311  return a ? b : c;
312}
313
314static arith_t
315assignment(int var, int noeval)
316{
317  union yystype val = yylval;
318  int           op  = yylex();
319  arith_t       result;
320  char          sresult[DIGITS(result) + 1];
321
322  if (var != ARITH_VAR)
323    return cond(var, &val, op, noeval);
324
325  if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
326    return cond(var, &val, op, noeval);
327
328  result = assignment(yylex(), noeval);
329  if (noeval)
330    return result;
331
332  if (op != ARITH_ASS)
333    result = do_binop(op - 11, arith_lookupvarint(val.name), result);
334  snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
335  setvar(val.name, sresult, 0);
336  return result;
337}
338
339arith_t
340arith(const char *s)
341{
342  struct stackmark smark;
343  arith_t          result;
344
345  setstackmark(&smark);
346
347  arith_buf = arith_startbuf = s;
348
349  result = assignment(yylex(), 0);
350
351  if (last_token)
352    yyerror("expecting EOF");
353
354  popstackmark(&smark);
355
356  return result;
357}
358
359/*
360 *  The exp(1) builtin.
361 */
362int
363letcmd(int argc, char **argv)
364{
365  const char *p;
366  char       *concat;
367  char      **ap;
368  arith_t     i;
369
370  if (argc > 1) {
371    p = argv[1];
372    if (argc > 2) {
373      /*
374       * Concatenate arguments.
375       */
376      STARTSTACKSTR(concat);
377      ap = argv + 2;
378      for (;;) {
379        while (*p)
380          STPUTC(*p++, concat);
381        if ((p = *ap++) == NULL)
382          break;
383        STPUTC(' ', concat);
384      }
385      STPUTC('\0', concat);
386      p = grabstackstr(concat);
387    }
388  } else
389    p = "";
390
391  i = arith(p);
392
393  out1fmt(ARITH_FORMAT_STR "\n", i);
394  return !i;
395}