1 /* $NetBSD: arith_token.c,v 1.7 2017/12/17 04:06:03 kre Exp $ */
2
3 /*-
4 * Copyright (c) 2002
5 * Herbert Xu.
6 * Copyright (c) 1991, 1993
7 * The Regents of the University of California. 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, from dash
37 */
38
39 #include <sys/cdefs.h>
40
41 #ifndef lint
42 __RCSID("$NetBSD: arith_token.c,v 1.7 2017/12/17 04:06:03 kre Exp $");
43 #endif /* not lint */
44
45 #include <inttypes.h>
46 #include <limits.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include "shell.h"
51 #include "arith_tokens.h"
52 #include "expand.h"
53 #include "error.h"
54 #include "memalloc.h"
55 #include "parser.h"
56 #include "syntax.h"
57 #include "show.h"
58
59 #if ARITH_BOR + ARITH_ASS_GAP != ARITH_BORASS || \
60 ARITH_ASS + ARITH_ASS_GAP != ARITH_EQ
61 #error Arithmetic tokens are out of order.
62 #endif
63
64 /*
65 * Scan next arithmetic token, return its type,
66 * leave its value (when applicable) in (global) a_t_val.
67 *
68 * Input text is in (global) arith_buf which is updated to
69 * refer to the next char after the token returned, except
70 * on error (ARITH_BAD returned) where arith_buf is not altered.
71 */
72 int
arith_token(void)73 arith_token(void)
74 {
75 int token;
76 const char *buf = arith_buf;
77 char *end;
78 const char *p;
79
80 for (;;) {
81 token = *buf;
82
83 if (isdigit(token)) {
84 /*
85 * Numbers all start with a digit, and nothing
86 * else does, the number ends wherever
87 * strtoimax() stops...
88 */
89 a_t_val.val = strtoimax(buf, &end, 0);
90 if (is_in_name(*end)) {
91 token = *end;
92 while (is_in_name(*++end))
93 continue;
94 error("arithmetic: unexpected '%c' "
95 "(out of range) in numeric constant: "
96 "%.*s", token, (int)(end - buf), buf);
97 }
98 arith_buf = end;
99 VTRACE(DBG_ARITH, ("Arith token ARITH_NUM=%jd\n",
100 a_t_val.val));
101 return ARITH_NUM;
102
103 } else if (is_name(token)) {
104 /*
105 * Variable names all start with an alpha (or '_')
106 * and nothing else does. They continue for the
107 * longest unbroken sequence of alphanumerics ( + _ )
108 */
109 arith_var_lno = arith_lno;
110 p = buf;
111 while (buf++, is_in_name(*buf))
112 ;
113 a_t_val.name = stalloc(buf - p + 1);
114 memcpy(a_t_val.name, p, buf - p);
115 a_t_val.name[buf - p] = '\0';
116 arith_buf = buf;
117 VTRACE(DBG_ARITH, ("Arith token ARITH_VAR=\"%s\"\n",
118 a_t_val.name));
119 return ARITH_VAR;
120
121 } else switch (token) {
122 /*
123 * everything else must be some kind of
124 * operator, white space, or an error.
125 */
126 case '\n':
127 arith_lno++;
128 VTRACE(DBG_ARITH, ("Arith: newline\n"));
129 /* FALLTHROUGH */
130 case ' ':
131 case '\t':
132 buf++;
133 continue;
134
135 default:
136 error("arithmetic: unexpected '%c' (%#x) in expression",
137 token, token);
138 /* NOTREACHED */
139
140 case '=':
141 token = ARITH_ASS;
142 checkeq:
143 buf++;
144 checkeqcur:
145 if (*buf != '=')
146 goto out;
147 token += ARITH_ASS_GAP;
148 break;
149
150 case '>':
151 switch (*++buf) {
152 case '=':
153 token = ARITH_GE;
154 break;
155 case '>':
156 token = ARITH_RSHIFT;
157 goto checkeq;
158 default:
159 token = ARITH_GT;
160 goto out;
161 }
162 break;
163
164 case '<':
165 switch (*++buf) {
166 case '=':
167 token = ARITH_LE;
168 break;
169 case '<':
170 token = ARITH_LSHIFT;
171 goto checkeq;
172 default:
173 token = ARITH_LT;
174 goto out;
175 }
176 break;
177
178 case '|':
179 if (*++buf != '|') {
180 token = ARITH_BOR;
181 goto checkeqcur;
182 }
183 token = ARITH_OR;
184 break;
185
186 case '&':
187 if (*++buf != '&') {
188 token = ARITH_BAND;
189 goto checkeqcur;
190 }
191 token = ARITH_AND;
192 break;
193
194 case '!':
195 if (*++buf != '=') {
196 token = ARITH_NOT;
197 goto out;
198 }
199 token = ARITH_NE;
200 break;
201
202 case 0:
203 goto out;
204
205 case '(':
206 token = ARITH_LPAREN;
207 break;
208 case ')':
209 token = ARITH_RPAREN;
210 break;
211
212 case '*':
213 token = ARITH_MUL;
214 goto checkeq;
215 case '/':
216 token = ARITH_DIV;
217 goto checkeq;
218 case '%':
219 token = ARITH_REM;
220 goto checkeq;
221
222 case '+':
223 if (buf[1] == '+') {
224 buf++;
225 token = ARITH_INCR;
226 break;
227 }
228 token = ARITH_ADD;
229 goto checkeq;
230 case '-':
231 if (buf[1] == '-') {
232 buf++;
233 token = ARITH_DECR;
234 break;
235 }
236 token = ARITH_SUB;
237 goto checkeq;
238 case '~':
239 token = ARITH_BNOT;
240 break;
241 case '^':
242 token = ARITH_BXOR;
243 goto checkeq;
244
245 case '?':
246 token = ARITH_QMARK;
247 break;
248 case ':':
249 token = ARITH_COLON;
250 break;
251 case ',':
252 token = ARITH_COMMA;
253 break;
254 }
255 break;
256 }
257 buf++;
258 out:
259 arith_buf = buf;
260 VTRACE(DBG_ARITH, ("Arith token: %d\n", token));
261 return token;
262 }
263