xref: /openbsd-src/bin/csh/exp.c (revision 4e21444b3a08b704a30c9b68838f075366a86ac8)
1 /*	$OpenBSD: exp.c,v 1.14 2015/10/26 21:57:42 naddy Exp $	*/
2 /*	$NetBSD: exp.c,v 1.6 1995/03/21 09:02:51 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <limits.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <stdarg.h>
39 
40 #include "csh.h"
41 #include "extern.h"
42 
43 #define IGNORE	1	/* in ignore, it means to ignore value, just parse */
44 #define NOGLOB	2	/* in ignore, it means not to globone */
45 
46 #define	ADDOP	1
47 #define	MULOP	2
48 #define	EQOP	4
49 #define	RELOP	8
50 #define	RESTOP	16
51 #define	ANYOP	31
52 
53 #define	EQEQ	1
54 #define	GTR	2
55 #define	LSS	4
56 #define	NOTEQ	6
57 #define EQMATCH 7
58 #define NOTEQMATCH 8
59 
60 static int	exp1(Char ***, bool);
61 static int	exp2_(Char ***, bool);
62 static int	exp2a(Char ***, bool);
63 static int	exp2b(Char ***, bool);
64 static int	exp2c(Char ***, bool);
65 static Char *	exp3(Char ***, bool);
66 static Char *	exp3a(Char ***, bool);
67 static Char *	exp4(Char ***, bool);
68 static Char *	exp5(Char ***, bool);
69 static Char *	exp6(Char ***, bool);
70 static void	evalav(Char **);
71 static int	isa(Char *, int);
72 static int	egetn(Char *);
73 
74 int
75 expr(Char ***vp)
76 {
77     return (exp0(vp, 0));
78 }
79 
80 int
81 exp0(Char ***vp, bool ignore)
82 {
83     int p1 = exp1(vp, ignore);
84 
85     if (**vp && eq(**vp, STRor2)) {
86 	int p2;
87 
88 	(*vp)++;
89 	p2 = exp0(vp, (ignore & IGNORE) || p1);
90 	return (p1 || p2);
91     }
92     return (p1);
93 }
94 
95 static int
96 exp1(Char ***vp, bool ignore)
97 {
98     int p1 = exp2_(vp, ignore);
99 
100     if (**vp && eq(**vp, STRand2)) {
101 	int p2;
102 
103 	(*vp)++;
104 	p2 = exp1(vp, (ignore & IGNORE) || !p1);
105 	return (p1 && p2);
106     }
107     return (p1);
108 }
109 
110 static int
111 exp2_(Char ***vp, bool ignore)
112 {
113     int p1 = exp2a(vp, ignore);
114 
115     if (**vp && eq(**vp, STRor)) {
116 	int p2;
117 
118 	(*vp)++;
119 	p2 = exp2_(vp, ignore);
120 	return (p1 | p2);
121     }
122     return (p1);
123 }
124 
125 static int
126 exp2a(Char ***vp, bool ignore)
127 {
128     int p1 = exp2b(vp, ignore);
129 
130     if (**vp && eq(**vp, STRcaret)) {
131 	int p2;
132 
133 	(*vp)++;
134 	p2 = exp2a(vp, ignore);
135 	return (p1 ^ p2);
136     }
137     return (p1);
138 }
139 
140 static int
141 exp2b(Char ***vp, bool ignore)
142 {
143     int p1 = exp2c(vp, ignore);
144 
145     if (**vp && eq(**vp, STRand)) {
146 	int p2;
147 
148 	(*vp)++;
149 	p2 = exp2b(vp, ignore);
150 	return (p1 & p2);
151     }
152     return (p1);
153 }
154 
155 static int
156 exp2c(Char ***vp, bool ignore)
157 {
158     Char *p1 = exp3(vp, ignore);
159     Char *p2;
160     int i;
161 
162     if ((i = isa(**vp, EQOP)) != 0) {
163 	(*vp)++;
164 	if (i == EQMATCH || i == NOTEQMATCH)
165 	    ignore |= NOGLOB;
166 	p2 = exp3(vp, ignore);
167 	if (!(ignore & IGNORE))
168 	    switch (i) {
169 
170 	    case EQEQ:
171 		i = eq(p1, p2);
172 		break;
173 
174 	    case NOTEQ:
175 		i = !eq(p1, p2);
176 		break;
177 
178 	    case EQMATCH:
179 		i = Gmatch(p1, p2);
180 		break;
181 
182 	    case NOTEQMATCH:
183 		i = !Gmatch(p1, p2);
184 		break;
185 	    }
186 	xfree(p1);
187 	xfree(p2);
188 	return (i);
189     }
190     i = egetn(p1);
191     xfree(p1);
192     return (i);
193 }
194 
195 static Char *
196 exp3(Char ***vp, bool ignore)
197 {
198     Char *p1, *p2;
199     int i;
200 
201     p1 = exp3a(vp, ignore);
202     if ((i = isa(**vp, RELOP)) != 0) {
203 	(*vp)++;
204 	if (**vp && eq(**vp, STRequal))
205 	    i |= 1, (*vp)++;
206 	p2 = exp3(vp, ignore);
207 	if (!(ignore & IGNORE))
208 	    switch (i) {
209 
210 	    case GTR:
211 		i = egetn(p1) > egetn(p2);
212 		break;
213 
214 	    case GTR | 1:
215 		i = egetn(p1) >= egetn(p2);
216 		break;
217 
218 	    case LSS:
219 		i = egetn(p1) < egetn(p2);
220 		break;
221 
222 	    case LSS | 1:
223 		i = egetn(p1) <= egetn(p2);
224 		break;
225 	    }
226 	xfree(p1);
227 	xfree(p2);
228 	return (putn(i));
229     }
230     return (p1);
231 }
232 
233 static Char *
234 exp3a(Char ***vp, bool ignore)
235 {
236     Char *p1, *p2, *op;
237     int i;
238 
239     p1 = exp4(vp, ignore);
240     op = **vp;
241     if (op && any("<>", op[0]) && op[0] == op[1]) {
242 	(*vp)++;
243 	p2 = exp3a(vp, ignore);
244 	if (op[0] == '<')
245 	    i = egetn(p1) << egetn(p2);
246 	else
247 	    i = egetn(p1) >> egetn(p2);
248 	xfree(p1);
249 	xfree(p2);
250 	return (putn(i));
251     }
252     return (p1);
253 }
254 
255 static Char *
256 exp4(Char ***vp, bool ignore)
257 {
258     Char *p1, *p2;
259     int i = 0;
260 
261     p1 = exp5(vp, ignore);
262     if (isa(**vp, ADDOP)) {
263 	Char *op = *(*vp)++;
264 
265 	p2 = exp4(vp, ignore);
266 	if (!(ignore & IGNORE))
267 	    switch (op[0]) {
268 
269 	    case '+':
270 		i = egetn(p1) + egetn(p2);
271 		break;
272 
273 	    case '-':
274 		i = egetn(p1) - egetn(p2);
275 		break;
276 	    }
277 	xfree(p1);
278 	xfree(p2);
279 	return (putn(i));
280     }
281     return (p1);
282 }
283 
284 static Char *
285 exp5(Char ***vp, bool ignore)
286 {
287     Char *p1, *p2;
288     int i = 0, l;
289 
290     p1 = exp6(vp, ignore);
291     if (isa(**vp, MULOP)) {
292 	Char *op = *(*vp)++;
293 
294 	p2 = exp5(vp, ignore);
295 	if (!(ignore & IGNORE))
296 	    switch (op[0]) {
297 
298 	    case '*':
299 		i = egetn(p1) * egetn(p2);
300 		break;
301 
302 	    case '/':
303 		i = egetn(p2);
304 		if (i == 0)
305 		    stderror(ERR_DIV0);
306 		l = egetn(p1);
307 		if (l == INT_MIN && i == -1)
308 			i = INT_MIN;
309 		else
310 			i = l / i;
311 		break;
312 
313 	    case '%':
314 		i = egetn(p2);
315 		if (i == 0)
316 		    stderror(ERR_MOD0);
317 		l = egetn(p1);
318 		if (l == INT_MIN && i == -1)
319 			i = 0;
320 		else
321 			i = l % i;
322 		break;
323 	    }
324 	xfree(p1);
325 	xfree(p2);
326 	return (putn(i));
327     }
328     return (p1);
329 }
330 
331 static Char *
332 exp6(Char ***vp, bool ignore)
333 {
334     int     ccode, i = 0;
335     Char *cp, *dp, *ep;
336 
337     if (**vp == 0)
338 	stderror(ERR_NAME | ERR_EXPRESSION);
339     if (eq(**vp, STRbang)) {
340 	(*vp)++;
341 	cp = exp6(vp, ignore);
342 	i = egetn(cp);
343 	xfree(cp);
344 	return (putn(!i));
345     }
346     if (eq(**vp, STRtilde)) {
347 	(*vp)++;
348 	cp = exp6(vp, ignore);
349 	i = egetn(cp);
350 	xfree(cp);
351 	return (putn(~i));
352     }
353     if (eq(**vp, STRLparen)) {
354 	(*vp)++;
355 	ccode = exp0(vp, ignore);
356 	if (*vp == 0 || **vp == 0 || ***vp != ')')
357 	    stderror(ERR_NAME | ERR_EXPRESSION);
358 	(*vp)++;
359 	return (putn(ccode));
360     }
361     if (eq(**vp, STRLbrace)) {
362 	Char **v;
363 	struct command faket;
364 	Char   *fakecom[2];
365 
366 	faket.t_dtyp = NODE_COMMAND;
367 	faket.t_dflg = 0;
368 	faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
369 	faket.t_dcom = fakecom;
370 	fakecom[0] = STRfakecom;
371 	fakecom[1] = NULL;
372 	(*vp)++;
373 	v = *vp;
374 	for (;;) {
375 	    if (!**vp)
376 		stderror(ERR_NAME | ERR_MISSING, '}');
377 	    if (eq(*(*vp)++, STRRbrace))
378 		break;
379 	}
380 	if (ignore & IGNORE)
381 	    return (Strsave(STRNULL));
382 	psavejob();
383 	if (pfork(&faket, -1) == 0) {
384 	    *--(*vp) = 0;
385 	    evalav(v);
386 	    exitstat();
387 	}
388 	pwait();
389 	prestjob();
390 	return (putn(egetn(value(STRstatus)) == 0));
391     }
392     if (isa(**vp, ANYOP))
393 	return (Strsave(STRNULL));
394     cp = *(*vp)++;
395     if (*cp == '-' && any("erwxfdzopls", cp[1])) {
396 	struct stat stb;
397 
398 	if (cp[2] != '\0')
399 	    stderror(ERR_NAME | ERR_FILEINQ);
400 	/*
401 	 * Detect missing file names by checking for operator in the file name
402 	 * position.  However, if an operator name appears there, we must make
403 	 * sure that there's no file by that name (e.g., "/") before announcing
404 	 * an error.  Even this check isn't quite right, since it doesn't take
405 	 * globbing into account.
406 	 */
407 	if (isa(**vp, ANYOP) && stat(short2str(**vp), &stb))
408 	    stderror(ERR_NAME | ERR_FILENAME);
409 
410 	dp = *(*vp)++;
411 	if (ignore & IGNORE)
412 	    return (Strsave(STRNULL));
413 	ep = globone(dp, G_ERROR);
414 	switch (cp[1]) {
415 
416 	case 'r':
417 	    i = !access(short2str(ep), R_OK);
418 	    break;
419 
420 	case 'w':
421 	    i = !access(short2str(ep), W_OK);
422 	    break;
423 
424 	case 'x':
425 	    i = !access(short2str(ep), X_OK);
426 	    break;
427 
428 	default:
429 	    if (
430 #ifdef S_IFLNK
431 		cp[1] == 'l' ? lstat(short2str(ep), &stb) :
432 #endif
433 		stat(short2str(ep), &stb)) {
434 		xfree(ep);
435 		return (Strsave(STR0));
436 	    }
437 	    switch (cp[1]) {
438 
439 	    case 'f':
440 		i = S_ISREG(stb.st_mode);
441 		break;
442 
443 	    case 'd':
444 		i = S_ISDIR(stb.st_mode);
445 		break;
446 
447 	    case 'p':
448 #ifdef S_ISFIFO
449 		i = S_ISFIFO(stb.st_mode);
450 #else
451 		i = 0;
452 #endif
453 		break;
454 
455 	    case 'l':
456 #ifdef S_ISLNK
457 		i = S_ISLNK(stb.st_mode);
458 #else
459 		i = 0;
460 #endif
461 		break;
462 
463 	    case 's':
464 #ifdef S_ISSOCK
465 		i = S_ISSOCK(stb.st_mode);
466 #else
467 		i = 0;
468 #endif
469 		break;
470 
471 	    case 'z':
472 		i = stb.st_size == 0;
473 		break;
474 
475 	    case 'e':
476 		i = 1;
477 		break;
478 
479 	    case 'o':
480 		i = stb.st_uid == uid;
481 		break;
482 	    }
483 	}
484 	xfree(ep);
485 	return (putn(i));
486     }
487     return (ignore & NOGLOB ? Strsave(cp) : globone(cp, G_ERROR));
488 }
489 
490 static void
491 evalav(Char **v)
492 {
493     struct wordent paraml1;
494     struct wordent *hp = &paraml1;
495     struct command *t;
496     struct wordent *wdp = hp;
497 
498     set(STRstatus, Strsave(STR0));
499     hp->prev = hp->next = hp;
500     hp->word = STRNULL;
501     while (*v) {
502 	struct wordent *new = xcalloc(1, sizeof *wdp);
503 
504 	new->prev = wdp;
505 	new->next = hp;
506 	wdp->next = new;
507 	wdp = new;
508 	wdp->word = Strsave(*v++);
509     }
510     hp->prev = wdp;
511     alias(&paraml1);
512     t = syntax(paraml1.next, &paraml1, 0);
513     if (seterr)
514 	stderror(ERR_OLD);
515     execute(t, -1, NULL, NULL);
516     freelex(&paraml1), freesyn(t);
517 }
518 
519 static int
520 isa(Char *cp, int what)
521 {
522     if (cp == 0)
523 	return ((what & RESTOP) != 0);
524     if (cp[1] == 0) {
525 	if (what & ADDOP && (*cp == '+' || *cp == '-'))
526 	    return (1);
527 	if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
528 	    return (1);
529 	if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
530 			      *cp == '~' || *cp == '^' || *cp == '"'))
531 	    return (1);
532     }
533     else if (cp[2] == 0) {
534 	if (what & RESTOP) {
535 	    if (cp[0] == '|' && cp[1] == '&')
536 		return (1);
537 	    if (cp[0] == '<' && cp[1] == '<')
538 		return (1);
539 	    if (cp[0] == '>' && cp[1] == '>')
540 		return (1);
541 	}
542 	if (what & EQOP) {
543 	    if (cp[0] == '=') {
544 		if (cp[1] == '=')
545 		    return (EQEQ);
546 		if (cp[1] == '~')
547 		    return (EQMATCH);
548 	    }
549 	    else if (cp[0] == '!') {
550 		if (cp[1] == '=')
551 		    return (NOTEQ);
552 		if (cp[1] == '~')
553 		    return (NOTEQMATCH);
554 	    }
555 	}
556     }
557     if (what & RELOP) {
558 	if (*cp == '<')
559 	    return (LSS);
560 	if (*cp == '>')
561 	    return (GTR);
562     }
563     return (0);
564 }
565 
566 static int
567 egetn(Char *cp)
568 {
569     if (*cp && *cp != '-' && !Isdigit(*cp))
570 	stderror(ERR_NAME | ERR_EXPRESSION);
571     return (getn(cp));
572 }
573 
574 /* Phew! */
575