xref: /openbsd-src/bin/csh/exp.c (revision c38c44c77019175ab39d2b6c2d7c32caedd63b6a)
1 /*	$OpenBSD: exp.c,v 1.13 2015/10/26 15:01:15 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 #ifdef EDEBUG
75 static void	etracc(char *, Char *, Char ***);
76 static void	etraci(char *, int, Char ***);
77 #endif
78 
79 int
80 expr(Char ***vp)
81 {
82     return (exp0(vp, 0));
83 }
84 
85 int
86 exp0(Char ***vp, bool ignore)
87 {
88     int p1 = exp1(vp, ignore);
89 
90 #ifdef EDEBUG
91     etraci("exp0 p1", p1, vp);
92 #endif
93     if (**vp && eq(**vp, STRor2)) {
94 	int p2;
95 
96 	(*vp)++;
97 	p2 = exp0(vp, (ignore & IGNORE) || p1);
98 #ifdef EDEBUG
99 	etraci("exp0 p2", p2, vp);
100 #endif
101 	return (p1 || p2);
102     }
103     return (p1);
104 }
105 
106 static int
107 exp1(Char ***vp, bool ignore)
108 {
109     int p1 = exp2_(vp, ignore);
110 
111 #ifdef EDEBUG
112     etraci("exp1 p1", p1, vp);
113 #endif
114     if (**vp && eq(**vp, STRand2)) {
115 	int p2;
116 
117 	(*vp)++;
118 	p2 = exp1(vp, (ignore & IGNORE) || !p1);
119 #ifdef EDEBUG
120 	etraci("exp1 p2", p2, vp);
121 #endif
122 	return (p1 && p2);
123     }
124     return (p1);
125 }
126 
127 static int
128 exp2_(Char ***vp, bool ignore)
129 {
130     int p1 = exp2a(vp, ignore);
131 
132 #ifdef EDEBUG
133     etraci("exp3 p1", p1, vp);
134 #endif
135     if (**vp && eq(**vp, STRor)) {
136 	int p2;
137 
138 	(*vp)++;
139 	p2 = exp2_(vp, ignore);
140 #ifdef EDEBUG
141 	etraci("exp3 p2", p2, vp);
142 #endif
143 	return (p1 | p2);
144     }
145     return (p1);
146 }
147 
148 static int
149 exp2a(Char ***vp, bool ignore)
150 {
151     int p1 = exp2b(vp, ignore);
152 
153 #ifdef EDEBUG
154     etraci("exp2a p1", p1, vp);
155 #endif
156     if (**vp && eq(**vp, STRcaret)) {
157 	int p2;
158 
159 	(*vp)++;
160 	p2 = exp2a(vp, ignore);
161 #ifdef EDEBUG
162 	etraci("exp2a p2", p2, vp);
163 #endif
164 	return (p1 ^ p2);
165     }
166     return (p1);
167 }
168 
169 static int
170 exp2b(Char ***vp, bool ignore)
171 {
172     int p1 = exp2c(vp, ignore);
173 
174 #ifdef EDEBUG
175     etraci("exp2b p1", p1, vp);
176 #endif
177     if (**vp && eq(**vp, STRand)) {
178 	int p2;
179 
180 	(*vp)++;
181 	p2 = exp2b(vp, ignore);
182 #ifdef EDEBUG
183 	etraci("exp2b p2", p2, vp);
184 #endif
185 	return (p1 & p2);
186     }
187     return (p1);
188 }
189 
190 static int
191 exp2c(Char ***vp, bool ignore)
192 {
193     Char *p1 = exp3(vp, ignore);
194     Char *p2;
195     int i;
196 
197 #ifdef EDEBUG
198     etracc("exp2c p1", p1, vp);
199 #endif
200     if ((i = isa(**vp, EQOP)) != 0) {
201 	(*vp)++;
202 	if (i == EQMATCH || i == NOTEQMATCH)
203 	    ignore |= NOGLOB;
204 	p2 = exp3(vp, ignore);
205 #ifdef EDEBUG
206 	etracc("exp2c p2", p2, vp);
207 #endif
208 	if (!(ignore & IGNORE))
209 	    switch (i) {
210 
211 	    case EQEQ:
212 		i = eq(p1, p2);
213 		break;
214 
215 	    case NOTEQ:
216 		i = !eq(p1, p2);
217 		break;
218 
219 	    case EQMATCH:
220 		i = Gmatch(p1, p2);
221 		break;
222 
223 	    case NOTEQMATCH:
224 		i = !Gmatch(p1, p2);
225 		break;
226 	    }
227 	xfree(p1);
228 	xfree(p2);
229 	return (i);
230     }
231     i = egetn(p1);
232     xfree(p1);
233     return (i);
234 }
235 
236 static Char *
237 exp3(Char ***vp, bool ignore)
238 {
239     Char *p1, *p2;
240     int i;
241 
242     p1 = exp3a(vp, ignore);
243 #ifdef EDEBUG
244     etracc("exp3 p1", p1, vp);
245 #endif
246     if ((i = isa(**vp, RELOP)) != 0) {
247 	(*vp)++;
248 	if (**vp && eq(**vp, STRequal))
249 	    i |= 1, (*vp)++;
250 	p2 = exp3(vp, ignore);
251 #ifdef EDEBUG
252 	etracc("exp3 p2", p2, vp);
253 #endif
254 	if (!(ignore & IGNORE))
255 	    switch (i) {
256 
257 	    case GTR:
258 		i = egetn(p1) > egetn(p2);
259 		break;
260 
261 	    case GTR | 1:
262 		i = egetn(p1) >= egetn(p2);
263 		break;
264 
265 	    case LSS:
266 		i = egetn(p1) < egetn(p2);
267 		break;
268 
269 	    case LSS | 1:
270 		i = egetn(p1) <= egetn(p2);
271 		break;
272 	    }
273 	xfree(p1);
274 	xfree(p2);
275 	return (putn(i));
276     }
277     return (p1);
278 }
279 
280 static Char *
281 exp3a(Char ***vp, bool ignore)
282 {
283     Char *p1, *p2, *op;
284     int i;
285 
286     p1 = exp4(vp, ignore);
287 #ifdef EDEBUG
288     etracc("exp3a p1", p1, vp);
289 #endif
290     op = **vp;
291     if (op && any("<>", op[0]) && op[0] == op[1]) {
292 	(*vp)++;
293 	p2 = exp3a(vp, ignore);
294 #ifdef EDEBUG
295 	etracc("exp3a p2", p2, vp);
296 #endif
297 	if (op[0] == '<')
298 	    i = egetn(p1) << egetn(p2);
299 	else
300 	    i = egetn(p1) >> egetn(p2);
301 	xfree(p1);
302 	xfree(p2);
303 	return (putn(i));
304     }
305     return (p1);
306 }
307 
308 static Char *
309 exp4(Char ***vp, bool ignore)
310 {
311     Char *p1, *p2;
312     int i = 0;
313 
314     p1 = exp5(vp, ignore);
315 #ifdef EDEBUG
316     etracc("exp4 p1", p1, vp);
317 #endif
318     if (isa(**vp, ADDOP)) {
319 	Char *op = *(*vp)++;
320 
321 	p2 = exp4(vp, ignore);
322 #ifdef EDEBUG
323 	etracc("exp4 p2", p2, vp);
324 #endif
325 	if (!(ignore & IGNORE))
326 	    switch (op[0]) {
327 
328 	    case '+':
329 		i = egetn(p1) + egetn(p2);
330 		break;
331 
332 	    case '-':
333 		i = egetn(p1) - egetn(p2);
334 		break;
335 	    }
336 	xfree(p1);
337 	xfree(p2);
338 	return (putn(i));
339     }
340     return (p1);
341 }
342 
343 static Char *
344 exp5(Char ***vp, bool ignore)
345 {
346     Char *p1, *p2;
347     int i = 0, l;
348 
349     p1 = exp6(vp, ignore);
350 #ifdef EDEBUG
351     etracc("exp5 p1", p1, vp);
352 #endif
353     if (isa(**vp, MULOP)) {
354 	Char *op = *(*vp)++;
355 
356 	p2 = exp5(vp, ignore);
357 #ifdef EDEBUG
358 	etracc("exp5 p2", p2, vp);
359 #endif
360 	if (!(ignore & IGNORE))
361 	    switch (op[0]) {
362 
363 	    case '*':
364 		i = egetn(p1) * egetn(p2);
365 		break;
366 
367 	    case '/':
368 		i = egetn(p2);
369 		if (i == 0)
370 		    stderror(ERR_DIV0);
371 		l = egetn(p1);
372 		if (l == INT_MIN && i == -1)
373 			i = INT_MIN;
374 		else
375 			i = l / i;
376 		break;
377 
378 	    case '%':
379 		i = egetn(p2);
380 		if (i == 0)
381 		    stderror(ERR_MOD0);
382 		l = egetn(p1);
383 		if (l == INT_MIN && i == -1)
384 			i = 0;
385 		else
386 			i = l % i;
387 		break;
388 	    }
389 	xfree(p1);
390 	xfree(p2);
391 	return (putn(i));
392     }
393     return (p1);
394 }
395 
396 static Char *
397 exp6(Char ***vp, bool ignore)
398 {
399     int     ccode, i = 0;
400     Char *cp, *dp, *ep;
401 
402     if (**vp == 0)
403 	stderror(ERR_NAME | ERR_EXPRESSION);
404     if (eq(**vp, STRbang)) {
405 	(*vp)++;
406 	cp = exp6(vp, ignore);
407 #ifdef EDEBUG
408 	etracc("exp6 ! cp", cp, vp);
409 #endif
410 	i = egetn(cp);
411 	xfree(cp);
412 	return (putn(!i));
413     }
414     if (eq(**vp, STRtilde)) {
415 	(*vp)++;
416 	cp = exp6(vp, ignore);
417 #ifdef EDEBUG
418 	etracc("exp6 ~ cp", cp, vp);
419 #endif
420 	i = egetn(cp);
421 	xfree(cp);
422 	return (putn(~i));
423     }
424     if (eq(**vp, STRLparen)) {
425 	(*vp)++;
426 	ccode = exp0(vp, ignore);
427 #ifdef EDEBUG
428 	etraci("exp6 () ccode", ccode, vp);
429 #endif
430 	if (*vp == 0 || **vp == 0 || ***vp != ')')
431 	    stderror(ERR_NAME | ERR_EXPRESSION);
432 	(*vp)++;
433 	return (putn(ccode));
434     }
435     if (eq(**vp, STRLbrace)) {
436 	Char **v;
437 	struct command faket;
438 	Char   *fakecom[2];
439 
440 	faket.t_dtyp = NODE_COMMAND;
441 	faket.t_dflg = 0;
442 	faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
443 	faket.t_dcom = fakecom;
444 	fakecom[0] = STRfakecom;
445 	fakecom[1] = NULL;
446 	(*vp)++;
447 	v = *vp;
448 	for (;;) {
449 	    if (!**vp)
450 		stderror(ERR_NAME | ERR_MISSING, '}');
451 	    if (eq(*(*vp)++, STRRbrace))
452 		break;
453 	}
454 	if (ignore & IGNORE)
455 	    return (Strsave(STRNULL));
456 	psavejob();
457 	if (pfork(&faket, -1) == 0) {
458 	    *--(*vp) = 0;
459 	    evalav(v);
460 	    exitstat();
461 	}
462 	pwait();
463 	prestjob();
464 #ifdef EDEBUG
465 	etraci("exp6 {} status", egetn(value(STRstatus)), vp);
466 #endif
467 	return (putn(egetn(value(STRstatus)) == 0));
468     }
469     if (isa(**vp, ANYOP))
470 	return (Strsave(STRNULL));
471     cp = *(*vp)++;
472     if (*cp == '-' && any("erwxfdzopls", cp[1])) {
473 	struct stat stb;
474 
475 	if (cp[2] != '\0')
476 	    stderror(ERR_NAME | ERR_FILEINQ);
477 	/*
478 	 * Detect missing file names by checking for operator in the file name
479 	 * position.  However, if an operator name appears there, we must make
480 	 * sure that there's no file by that name (e.g., "/") before announcing
481 	 * an error.  Even this check isn't quite right, since it doesn't take
482 	 * globbing into account.
483 	 */
484 	if (isa(**vp, ANYOP) && stat(short2str(**vp), &stb))
485 	    stderror(ERR_NAME | ERR_FILENAME);
486 
487 	dp = *(*vp)++;
488 	if (ignore & IGNORE)
489 	    return (Strsave(STRNULL));
490 	ep = globone(dp, G_ERROR);
491 	switch (cp[1]) {
492 
493 	case 'r':
494 	    i = !access(short2str(ep), R_OK);
495 	    break;
496 
497 	case 'w':
498 	    i = !access(short2str(ep), W_OK);
499 	    break;
500 
501 	case 'x':
502 	    i = !access(short2str(ep), X_OK);
503 	    break;
504 
505 	default:
506 	    if (
507 #ifdef S_IFLNK
508 		cp[1] == 'l' ? lstat(short2str(ep), &stb) :
509 #endif
510 		stat(short2str(ep), &stb)) {
511 		xfree(ep);
512 		return (Strsave(STR0));
513 	    }
514 	    switch (cp[1]) {
515 
516 	    case 'f':
517 		i = S_ISREG(stb.st_mode);
518 		break;
519 
520 	    case 'd':
521 		i = S_ISDIR(stb.st_mode);
522 		break;
523 
524 	    case 'p':
525 #ifdef S_ISFIFO
526 		i = S_ISFIFO(stb.st_mode);
527 #else
528 		i = 0;
529 #endif
530 		break;
531 
532 	    case 'l':
533 #ifdef S_ISLNK
534 		i = S_ISLNK(stb.st_mode);
535 #else
536 		i = 0;
537 #endif
538 		break;
539 
540 	    case 's':
541 #ifdef S_ISSOCK
542 		i = S_ISSOCK(stb.st_mode);
543 #else
544 		i = 0;
545 #endif
546 		break;
547 
548 	    case 'z':
549 		i = stb.st_size == 0;
550 		break;
551 
552 	    case 'e':
553 		i = 1;
554 		break;
555 
556 	    case 'o':
557 		i = stb.st_uid == uid;
558 		break;
559 	    }
560 	}
561 #ifdef EDEBUG
562 	etraci("exp6 -? i", i, vp);
563 #endif
564 	xfree(ep);
565 	return (putn(i));
566     }
567 #ifdef EDEBUG
568     etracc("exp6 default", cp, vp);
569 #endif
570     return (ignore & NOGLOB ? Strsave(cp) : globone(cp, G_ERROR));
571 }
572 
573 static void
574 evalav(Char **v)
575 {
576     struct wordent paraml1;
577     struct wordent *hp = &paraml1;
578     struct command *t;
579     struct wordent *wdp = hp;
580 
581     set(STRstatus, Strsave(STR0));
582     hp->prev = hp->next = hp;
583     hp->word = STRNULL;
584     while (*v) {
585 	struct wordent *new = xcalloc(1, sizeof *wdp);
586 
587 	new->prev = wdp;
588 	new->next = hp;
589 	wdp->next = new;
590 	wdp = new;
591 	wdp->word = Strsave(*v++);
592     }
593     hp->prev = wdp;
594     alias(&paraml1);
595     t = syntax(paraml1.next, &paraml1, 0);
596     if (seterr)
597 	stderror(ERR_OLD);
598     execute(t, -1, NULL, NULL);
599     freelex(&paraml1), freesyn(t);
600 }
601 
602 static int
603 isa(Char *cp, int what)
604 {
605     if (cp == 0)
606 	return ((what & RESTOP) != 0);
607     if (cp[1] == 0) {
608 	if (what & ADDOP && (*cp == '+' || *cp == '-'))
609 	    return (1);
610 	if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
611 	    return (1);
612 	if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
613 			      *cp == '~' || *cp == '^' || *cp == '"'))
614 	    return (1);
615     }
616     else if (cp[2] == 0) {
617 	if (what & RESTOP) {
618 	    if (cp[0] == '|' && cp[1] == '&')
619 		return (1);
620 	    if (cp[0] == '<' && cp[1] == '<')
621 		return (1);
622 	    if (cp[0] == '>' && cp[1] == '>')
623 		return (1);
624 	}
625 	if (what & EQOP) {
626 	    if (cp[0] == '=') {
627 		if (cp[1] == '=')
628 		    return (EQEQ);
629 		if (cp[1] == '~')
630 		    return (EQMATCH);
631 	    }
632 	    else if (cp[0] == '!') {
633 		if (cp[1] == '=')
634 		    return (NOTEQ);
635 		if (cp[1] == '~')
636 		    return (NOTEQMATCH);
637 	    }
638 	}
639     }
640     if (what & RELOP) {
641 	if (*cp == '<')
642 	    return (LSS);
643 	if (*cp == '>')
644 	    return (GTR);
645     }
646     return (0);
647 }
648 
649 static int
650 egetn(Char *cp)
651 {
652     if (*cp && *cp != '-' && !Isdigit(*cp))
653 	stderror(ERR_NAME | ERR_EXPRESSION);
654     return (getn(cp));
655 }
656 
657 /* Phew! */
658 
659 #ifdef EDEBUG
660 static void
661 etraci(char *str, int i, Char ***vp)
662 {
663     (void) fprintf(csherr, "%s=%d\t", str, i);
664     blkpr(csherr, *vp);
665     (void) fprintf(csherr, "\n");
666 }
667 static void
668 etracc(char *str, Char *cp, Char ***vp)
669 {
670     (void) fprintf(csherr, "%s=%s\t", str, vis_str(cp));
671     blkpr(csherr, *vp);
672     (void) fprintf(csherr, "\n");
673 }
674 #endif
675