xref: /freebsd-src/bin/sh/arith_yacc.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
16262b84eSJilles Tjoelker /*-
26262b84eSJilles Tjoelker  * Copyright (c) 1993
36262b84eSJilles Tjoelker  *	The Regents of the University of California.  All rights reserved.
46262b84eSJilles Tjoelker  * Copyright (c) 2007
56262b84eSJilles Tjoelker  *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
66262b84eSJilles Tjoelker  *
76262b84eSJilles Tjoelker  * This code is derived from software contributed to Berkeley by
86262b84eSJilles Tjoelker  * Kenneth Almquist.
96262b84eSJilles Tjoelker  *
106262b84eSJilles Tjoelker  * Redistribution and use in source and binary forms, with or without
116262b84eSJilles Tjoelker  * modification, are permitted provided that the following conditions
126262b84eSJilles Tjoelker  * are met:
136262b84eSJilles Tjoelker  * 1. Redistributions of source code must retain the above copyright
146262b84eSJilles Tjoelker  *    notice, this list of conditions and the following disclaimer.
156262b84eSJilles Tjoelker  * 2. Redistributions in binary form must reproduce the above copyright
166262b84eSJilles Tjoelker  *    notice, this list of conditions and the following disclaimer in the
176262b84eSJilles Tjoelker  *    documentation and/or other materials provided with the distribution.
186262b84eSJilles Tjoelker  * 3. Neither the name of the University nor the names of its contributors
196262b84eSJilles Tjoelker  *    may be used to endorse or promote products derived from this software
206262b84eSJilles Tjoelker  *    without specific prior written permission.
216262b84eSJilles Tjoelker  *
226262b84eSJilles Tjoelker  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
236262b84eSJilles Tjoelker  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
246262b84eSJilles Tjoelker  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
256262b84eSJilles Tjoelker  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
266262b84eSJilles Tjoelker  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
276262b84eSJilles Tjoelker  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
286262b84eSJilles Tjoelker  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
296262b84eSJilles Tjoelker  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
306262b84eSJilles Tjoelker  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
316262b84eSJilles Tjoelker  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
326262b84eSJilles Tjoelker  * SUCH DAMAGE.
336262b84eSJilles Tjoelker  */
346262b84eSJilles Tjoelker 
356262b84eSJilles Tjoelker #include <sys/cdefs.h>
364004e05eSJilles Tjoelker #include <limits.h>
376262b84eSJilles Tjoelker #include <errno.h>
386262b84eSJilles Tjoelker #include <inttypes.h>
396262b84eSJilles Tjoelker #include <stdlib.h>
406262b84eSJilles Tjoelker #include <stdio.h>
416262b84eSJilles Tjoelker #include "arith.h"
426262b84eSJilles Tjoelker #include "arith_yacc.h"
436262b84eSJilles Tjoelker #include "expand.h"
446262b84eSJilles Tjoelker #include "shell.h"
456262b84eSJilles Tjoelker #include "error.h"
466262b84eSJilles Tjoelker #include "memalloc.h"
476262b84eSJilles Tjoelker #include "output.h"
486262b84eSJilles Tjoelker #include "options.h"
496262b84eSJilles Tjoelker #include "var.h"
506262b84eSJilles Tjoelker 
516262b84eSJilles Tjoelker #if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
526262b84eSJilles Tjoelker #error Arithmetic tokens are out of order.
536262b84eSJilles Tjoelker #endif
546262b84eSJilles Tjoelker 
556262b84eSJilles Tjoelker static const char *arith_startbuf;
566262b84eSJilles Tjoelker 
576262b84eSJilles Tjoelker const char *arith_buf;
586262b84eSJilles Tjoelker union yystype yylval;
596262b84eSJilles Tjoelker 
606262b84eSJilles Tjoelker static int last_token;
616262b84eSJilles Tjoelker 
626262b84eSJilles Tjoelker #define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
636262b84eSJilles Tjoelker 
646262b84eSJilles Tjoelker static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
656262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_MUL, 0),
666262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_DIV, 0),
676262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_REM, 0),
686262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_ADD, 1),
696262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_SUB, 1),
706262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
716262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
726262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_LT, 3),
736262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_LE, 3),
746262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_GT, 3),
756262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_GE, 3),
766262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_EQ, 4),
776262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_NE, 4),
786262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_BAND, 5),
796262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_BXOR, 6),
806262b84eSJilles Tjoelker 	ARITH_PRECEDENCE(ARITH_BOR, 7),
816262b84eSJilles Tjoelker };
826262b84eSJilles Tjoelker 
836262b84eSJilles Tjoelker #define ARITH_MAX_PREC 8
846262b84eSJilles Tjoelker 
852fae4c3dSPhilippe Charnier int letcmd(int, char **);
862fae4c3dSPhilippe Charnier 
yyerror(const char * s)876262b84eSJilles Tjoelker static __dead2 void yyerror(const char *s)
886262b84eSJilles Tjoelker {
896262b84eSJilles Tjoelker 	error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
906262b84eSJilles Tjoelker 	/* NOTREACHED */
916262b84eSJilles Tjoelker }
926262b84eSJilles Tjoelker 
arith_lookupvarint(char * varname)936262b84eSJilles Tjoelker static arith_t arith_lookupvarint(char *varname)
946262b84eSJilles Tjoelker {
956262b84eSJilles Tjoelker 	const char *str;
966262b84eSJilles Tjoelker 	char *p;
976262b84eSJilles Tjoelker 	arith_t result;
986262b84eSJilles Tjoelker 
996262b84eSJilles Tjoelker 	str = lookupvar(varname);
1003937fc9cSJilles Tjoelker 	if (uflag && str == NULL)
1013937fc9cSJilles Tjoelker 		yyerror("variable not set");
1026262b84eSJilles Tjoelker 	if (str == NULL || *str == '\0')
1036262b84eSJilles Tjoelker 		str = "0";
1046262b84eSJilles Tjoelker 	errno = 0;
105*aac5464bSJilles Tjoelker 	result = strtoarith_t(str, &p);
1066262b84eSJilles Tjoelker 	if (errno != 0 || *p != '\0')
1076262b84eSJilles Tjoelker 		yyerror("variable conversion error");
1086262b84eSJilles Tjoelker 	return result;
1096262b84eSJilles Tjoelker }
1106262b84eSJilles Tjoelker 
arith_prec(int op)1116262b84eSJilles Tjoelker static inline int arith_prec(int op)
1126262b84eSJilles Tjoelker {
1136262b84eSJilles Tjoelker 	return prec[op - ARITH_BINOP_MIN];
1146262b84eSJilles Tjoelker }
1156262b84eSJilles Tjoelker 
higher_prec(int op1,int op2)1166262b84eSJilles Tjoelker static inline int higher_prec(int op1, int op2)
1176262b84eSJilles Tjoelker {
1186262b84eSJilles Tjoelker 	return arith_prec(op1) < arith_prec(op2);
1196262b84eSJilles Tjoelker }
1206262b84eSJilles Tjoelker 
do_binop(int op,arith_t a,arith_t b)1216262b84eSJilles Tjoelker static arith_t do_binop(int op, arith_t a, arith_t b)
1226262b84eSJilles Tjoelker {
1236262b84eSJilles Tjoelker 
1246262b84eSJilles Tjoelker 	switch (op) {
1256262b84eSJilles Tjoelker 	default:
1266262b84eSJilles Tjoelker 	case ARITH_REM:
1276262b84eSJilles Tjoelker 	case ARITH_DIV:
1286262b84eSJilles Tjoelker 		if (!b)
1296262b84eSJilles Tjoelker 			yyerror("division by zero");
130e9749129SJilles Tjoelker 		if (a == ARITH_MIN && b == -1)
131e9749129SJilles Tjoelker 			yyerror("divide error");
1326262b84eSJilles Tjoelker 		return op == ARITH_REM ? a % b : a / b;
1336262b84eSJilles Tjoelker 	case ARITH_MUL:
134876f9b78SJilles Tjoelker 		return (uintmax_t)a * (uintmax_t)b;
1356262b84eSJilles Tjoelker 	case ARITH_ADD:
136876f9b78SJilles Tjoelker 		return (uintmax_t)a + (uintmax_t)b;
1376262b84eSJilles Tjoelker 	case ARITH_SUB:
138876f9b78SJilles Tjoelker 		return (uintmax_t)a - (uintmax_t)b;
1396262b84eSJilles Tjoelker 	case ARITH_LSHIFT:
140dd6d480aSJilles Tjoelker 		return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
1416262b84eSJilles Tjoelker 	case ARITH_RSHIFT:
142dd6d480aSJilles Tjoelker 		return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
1436262b84eSJilles Tjoelker 	case ARITH_LT:
1446262b84eSJilles Tjoelker 		return a < b;
1456262b84eSJilles Tjoelker 	case ARITH_LE:
1466262b84eSJilles Tjoelker 		return a <= b;
1476262b84eSJilles Tjoelker 	case ARITH_GT:
1486262b84eSJilles Tjoelker 		return a > b;
1496262b84eSJilles Tjoelker 	case ARITH_GE:
1506262b84eSJilles Tjoelker 		return a >= b;
1516262b84eSJilles Tjoelker 	case ARITH_EQ:
1526262b84eSJilles Tjoelker 		return a == b;
1536262b84eSJilles Tjoelker 	case ARITH_NE:
1546262b84eSJilles Tjoelker 		return a != b;
1556262b84eSJilles Tjoelker 	case ARITH_BAND:
1566262b84eSJilles Tjoelker 		return a & b;
1576262b84eSJilles Tjoelker 	case ARITH_BXOR:
1586262b84eSJilles Tjoelker 		return a ^ b;
1596262b84eSJilles Tjoelker 	case ARITH_BOR:
1606262b84eSJilles Tjoelker 		return a | b;
1616262b84eSJilles Tjoelker 	}
1626262b84eSJilles Tjoelker }
1636262b84eSJilles Tjoelker 
1646262b84eSJilles Tjoelker static arith_t assignment(int var, int noeval);
1656262b84eSJilles Tjoelker 
primary(int token,union yystype * val,int op,int noeval)1666262b84eSJilles Tjoelker static arith_t primary(int token, union yystype *val, int op, int noeval)
1676262b84eSJilles Tjoelker {
1686262b84eSJilles Tjoelker 	arith_t result;
1696262b84eSJilles Tjoelker 
1706262b84eSJilles Tjoelker again:
1716262b84eSJilles Tjoelker 	switch (token) {
1726262b84eSJilles Tjoelker 	case ARITH_LPAREN:
1736262b84eSJilles Tjoelker 		result = assignment(op, noeval);
1746262b84eSJilles Tjoelker 		if (last_token != ARITH_RPAREN)
1756262b84eSJilles Tjoelker 			yyerror("expecting ')'");
1766262b84eSJilles Tjoelker 		last_token = yylex();
1776262b84eSJilles Tjoelker 		return result;
1786262b84eSJilles Tjoelker 	case ARITH_NUM:
1796262b84eSJilles Tjoelker 		last_token = op;
1806262b84eSJilles Tjoelker 		return val->val;
1816262b84eSJilles Tjoelker 	case ARITH_VAR:
1826262b84eSJilles Tjoelker 		last_token = op;
1836262b84eSJilles Tjoelker 		return noeval ? val->val : arith_lookupvarint(val->name);
1846262b84eSJilles Tjoelker 	case ARITH_ADD:
1856262b84eSJilles Tjoelker 		token = op;
1866262b84eSJilles Tjoelker 		*val = yylval;
1876262b84eSJilles Tjoelker 		op = yylex();
1886262b84eSJilles Tjoelker 		goto again;
1896262b84eSJilles Tjoelker 	case ARITH_SUB:
1906262b84eSJilles Tjoelker 		*val = yylval;
1916262b84eSJilles Tjoelker 		return -primary(op, val, yylex(), noeval);
1926262b84eSJilles Tjoelker 	case ARITH_NOT:
1936262b84eSJilles Tjoelker 		*val = yylval;
1946262b84eSJilles Tjoelker 		return !primary(op, val, yylex(), noeval);
1956262b84eSJilles Tjoelker 	case ARITH_BNOT:
1966262b84eSJilles Tjoelker 		*val = yylval;
1976262b84eSJilles Tjoelker 		return ~primary(op, val, yylex(), noeval);
1986262b84eSJilles Tjoelker 	default:
1996262b84eSJilles Tjoelker 		yyerror("expecting primary");
2006262b84eSJilles Tjoelker 	}
2016262b84eSJilles Tjoelker }
2026262b84eSJilles Tjoelker 
binop2(arith_t a,int op,int precedence,int noeval)203976018d2SJilles Tjoelker static arith_t binop2(arith_t a, int op, int precedence, int noeval)
2046262b84eSJilles Tjoelker {
2056262b84eSJilles Tjoelker 	for (;;) {
2066262b84eSJilles Tjoelker 		union yystype val;
2076262b84eSJilles Tjoelker 		arith_t b;
2086262b84eSJilles Tjoelker 		int op2;
2096262b84eSJilles Tjoelker 		int token;
2106262b84eSJilles Tjoelker 
2116262b84eSJilles Tjoelker 		token = yylex();
2126262b84eSJilles Tjoelker 		val = yylval;
2136262b84eSJilles Tjoelker 
2146262b84eSJilles Tjoelker 		b = primary(token, &val, yylex(), noeval);
2156262b84eSJilles Tjoelker 
2166262b84eSJilles Tjoelker 		op2 = last_token;
2176262b84eSJilles Tjoelker 		if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX &&
2186262b84eSJilles Tjoelker 		    higher_prec(op2, op)) {
2196262b84eSJilles Tjoelker 			b = binop2(b, op2, arith_prec(op), noeval);
2206262b84eSJilles Tjoelker 			op2 = last_token;
2216262b84eSJilles Tjoelker 		}
2226262b84eSJilles Tjoelker 
2236262b84eSJilles Tjoelker 		a = noeval ? b : do_binop(op, a, b);
2246262b84eSJilles Tjoelker 
2256262b84eSJilles Tjoelker 		if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX ||
226976018d2SJilles Tjoelker 		    arith_prec(op2) >= precedence)
2276262b84eSJilles Tjoelker 			return a;
2286262b84eSJilles Tjoelker 
2296262b84eSJilles Tjoelker 		op = op2;
2306262b84eSJilles Tjoelker 	}
2316262b84eSJilles Tjoelker }
2326262b84eSJilles Tjoelker 
binop(int token,union yystype * val,int op,int noeval)2336262b84eSJilles Tjoelker static arith_t binop(int token, union yystype *val, int op, int noeval)
2346262b84eSJilles Tjoelker {
2356262b84eSJilles Tjoelker 	arith_t a = primary(token, val, op, noeval);
2366262b84eSJilles Tjoelker 
2376262b84eSJilles Tjoelker 	op = last_token;
2386262b84eSJilles Tjoelker 	if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
2396262b84eSJilles Tjoelker 		return a;
2406262b84eSJilles Tjoelker 
2416262b84eSJilles Tjoelker 	return binop2(a, op, ARITH_MAX_PREC, noeval);
2426262b84eSJilles Tjoelker }
2436262b84eSJilles Tjoelker 
and(int token,union yystype * val,int op,int noeval)2446262b84eSJilles Tjoelker static arith_t and(int token, union yystype *val, int op, int noeval)
2456262b84eSJilles Tjoelker {
2466262b84eSJilles Tjoelker 	arith_t a = binop(token, val, op, noeval);
2476262b84eSJilles Tjoelker 	arith_t b;
2486262b84eSJilles Tjoelker 
2496262b84eSJilles Tjoelker 	op = last_token;
2506262b84eSJilles Tjoelker 	if (op != ARITH_AND)
2516262b84eSJilles Tjoelker 		return a;
2526262b84eSJilles Tjoelker 
2536262b84eSJilles Tjoelker 	token = yylex();
2546262b84eSJilles Tjoelker 	*val = yylval;
2556262b84eSJilles Tjoelker 
2566262b84eSJilles Tjoelker 	b = and(token, val, yylex(), noeval | !a);
2576262b84eSJilles Tjoelker 
2586262b84eSJilles Tjoelker 	return a && b;
2596262b84eSJilles Tjoelker }
2606262b84eSJilles Tjoelker 
or(int token,union yystype * val,int op,int noeval)2616262b84eSJilles Tjoelker static arith_t or(int token, union yystype *val, int op, int noeval)
2626262b84eSJilles Tjoelker {
2636262b84eSJilles Tjoelker 	arith_t a = and(token, val, op, noeval);
2646262b84eSJilles Tjoelker 	arith_t b;
2656262b84eSJilles Tjoelker 
2666262b84eSJilles Tjoelker 	op = last_token;
2676262b84eSJilles Tjoelker 	if (op != ARITH_OR)
2686262b84eSJilles Tjoelker 		return a;
2696262b84eSJilles Tjoelker 
2706262b84eSJilles Tjoelker 	token = yylex();
2716262b84eSJilles Tjoelker 	*val = yylval;
2726262b84eSJilles Tjoelker 
2736262b84eSJilles Tjoelker 	b = or(token, val, yylex(), noeval | !!a);
2746262b84eSJilles Tjoelker 
2756262b84eSJilles Tjoelker 	return a || b;
2766262b84eSJilles Tjoelker }
2776262b84eSJilles Tjoelker 
cond(int token,union yystype * val,int op,int noeval)2786262b84eSJilles Tjoelker static arith_t cond(int token, union yystype *val, int op, int noeval)
2796262b84eSJilles Tjoelker {
2806262b84eSJilles Tjoelker 	arith_t a = or(token, val, op, noeval);
2816262b84eSJilles Tjoelker 	arith_t b;
2826262b84eSJilles Tjoelker 	arith_t c;
2836262b84eSJilles Tjoelker 
2846262b84eSJilles Tjoelker 	if (last_token != ARITH_QMARK)
2856262b84eSJilles Tjoelker 		return a;
2866262b84eSJilles Tjoelker 
2876262b84eSJilles Tjoelker 	b = assignment(yylex(), noeval | !a);
2886262b84eSJilles Tjoelker 
2896262b84eSJilles Tjoelker 	if (last_token != ARITH_COLON)
2906262b84eSJilles Tjoelker 		yyerror("expecting ':'");
2916262b84eSJilles Tjoelker 
2926262b84eSJilles Tjoelker 	token = yylex();
2936262b84eSJilles Tjoelker 	*val = yylval;
2946262b84eSJilles Tjoelker 
2956262b84eSJilles Tjoelker 	c = cond(token, val, yylex(), noeval | !!a);
2966262b84eSJilles Tjoelker 
2976262b84eSJilles Tjoelker 	return a ? b : c;
2986262b84eSJilles Tjoelker }
2996262b84eSJilles Tjoelker 
assignment(int var,int noeval)3006262b84eSJilles Tjoelker static arith_t assignment(int var, int noeval)
3016262b84eSJilles Tjoelker {
3026262b84eSJilles Tjoelker 	union yystype val = yylval;
3036262b84eSJilles Tjoelker 	int op = yylex();
3046262b84eSJilles Tjoelker 	arith_t result;
3056262b84eSJilles Tjoelker 	char sresult[DIGITS(result) + 1];
3066262b84eSJilles Tjoelker 
3076262b84eSJilles Tjoelker 	if (var != ARITH_VAR)
3086262b84eSJilles Tjoelker 		return cond(var, &val, op, noeval);
3096262b84eSJilles Tjoelker 
3106262b84eSJilles Tjoelker 	if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
3116262b84eSJilles Tjoelker 		return cond(var, &val, op, noeval);
3126262b84eSJilles Tjoelker 
3136262b84eSJilles Tjoelker 	result = assignment(yylex(), noeval);
3146262b84eSJilles Tjoelker 	if (noeval)
3156262b84eSJilles Tjoelker 		return result;
3166262b84eSJilles Tjoelker 
3176262b84eSJilles Tjoelker 	if (op != ARITH_ASS)
3186262b84eSJilles Tjoelker 		result = do_binop(op - 11, arith_lookupvarint(val.name), result);
3196262b84eSJilles Tjoelker 	snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
3206262b84eSJilles Tjoelker 	setvar(val.name, sresult, 0);
3216262b84eSJilles Tjoelker 	return result;
3226262b84eSJilles Tjoelker }
3236262b84eSJilles Tjoelker 
arith(const char * s)3246262b84eSJilles Tjoelker arith_t arith(const char *s)
3256262b84eSJilles Tjoelker {
3266262b84eSJilles Tjoelker 	struct stackmark smark;
3276262b84eSJilles Tjoelker 	arith_t result;
3286262b84eSJilles Tjoelker 
3296262b84eSJilles Tjoelker 	setstackmark(&smark);
3306262b84eSJilles Tjoelker 
3316262b84eSJilles Tjoelker 	arith_buf = arith_startbuf = s;
3326262b84eSJilles Tjoelker 
3336262b84eSJilles Tjoelker 	result = assignment(yylex(), 0);
3346262b84eSJilles Tjoelker 
3356262b84eSJilles Tjoelker 	if (last_token)
3366262b84eSJilles Tjoelker 		yyerror("expecting EOF");
3376262b84eSJilles Tjoelker 
3386262b84eSJilles Tjoelker 	popstackmark(&smark);
3396262b84eSJilles Tjoelker 
3406262b84eSJilles Tjoelker 	return result;
3416262b84eSJilles Tjoelker }
3426262b84eSJilles Tjoelker 
3436262b84eSJilles Tjoelker /*
3446262b84eSJilles Tjoelker  *  The exp(1) builtin.
3456262b84eSJilles Tjoelker  */
3466262b84eSJilles Tjoelker int
letcmd(int argc,char ** argv)3478d5a1430SJilles Tjoelker letcmd(int argc, char **argv)
3486262b84eSJilles Tjoelker {
3496262b84eSJilles Tjoelker 	const char *p;
3506262b84eSJilles Tjoelker 	char *concat;
3516262b84eSJilles Tjoelker 	char **ap;
3526262b84eSJilles Tjoelker 	arith_t i;
3536262b84eSJilles Tjoelker 
3546262b84eSJilles Tjoelker 	if (argc > 1) {
3556262b84eSJilles Tjoelker 		p = argv[1];
3566262b84eSJilles Tjoelker 		if (argc > 2) {
3576262b84eSJilles Tjoelker 			/*
3586262b84eSJilles Tjoelker 			 * Concatenate arguments.
3596262b84eSJilles Tjoelker 			 */
3606262b84eSJilles Tjoelker 			STARTSTACKSTR(concat);
3616262b84eSJilles Tjoelker 			ap = argv + 2;
3626262b84eSJilles Tjoelker 			for (;;) {
3636262b84eSJilles Tjoelker 				while (*p)
3646262b84eSJilles Tjoelker 					STPUTC(*p++, concat);
3656262b84eSJilles Tjoelker 				if ((p = *ap++) == NULL)
3666262b84eSJilles Tjoelker 					break;
3676262b84eSJilles Tjoelker 				STPUTC(' ', concat);
3686262b84eSJilles Tjoelker 			}
3696262b84eSJilles Tjoelker 			STPUTC('\0', concat);
3706262b84eSJilles Tjoelker 			p = grabstackstr(concat);
3716262b84eSJilles Tjoelker 		}
3726262b84eSJilles Tjoelker 	} else
3736262b84eSJilles Tjoelker 		p = "";
3746262b84eSJilles Tjoelker 
3756262b84eSJilles Tjoelker 	i = arith(p);
3766262b84eSJilles Tjoelker 
3776262b84eSJilles Tjoelker 	out1fmt(ARITH_FORMAT_STR "\n", i);
3786262b84eSJilles Tjoelker 	return !i;
3796262b84eSJilles Tjoelker }
380