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