xref: /openbsd/bin/csh/exp.c (revision 264ca280)
1 /*	$OpenBSD: exp.c,v 1.16 2015/12/26 13:48:38 mestre 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 	free(p1);
187 	free(p2);
188 	return (i);
189     }
190     i = egetn(p1);
191     free(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 	free(p1);
227 	free(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 	free(p1);
249 	free(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 	free(p1);
278 	free(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 	free(p1);
325 	free(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 	free(cp);
344 	return (putn(!i));
345     }
346     if (eq(**vp, STRtilde)) {
347 	(*vp)++;
348 	cp = exp6(vp, ignore);
349 	i = egetn(cp);
350 	free(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 (cp[1] == 'l' ? lstat(short2str(ep), &stb) :
430 		stat(short2str(ep), &stb)) {
431 		free(ep);
432 		return (Strsave(STR0));
433 	    }
434 	    switch (cp[1]) {
435 
436 	    case 'f':
437 		i = S_ISREG(stb.st_mode);
438 		break;
439 
440 	    case 'd':
441 		i = S_ISDIR(stb.st_mode);
442 		break;
443 
444 	    case 'p':
445 		i = S_ISFIFO(stb.st_mode);
446 		break;
447 
448 	    case 'l':
449 		i = S_ISLNK(stb.st_mode);
450 		break;
451 
452 	    case 's':
453 		i = S_ISSOCK(stb.st_mode);
454 		break;
455 
456 	    case 'z':
457 		i = stb.st_size == 0;
458 		break;
459 
460 	    case 'e':
461 		i = 1;
462 		break;
463 
464 	    case 'o':
465 		i = stb.st_uid == uid;
466 		break;
467 	    }
468 	}
469 	free(ep);
470 	return (putn(i));
471     }
472     return (ignore & NOGLOB ? Strsave(cp) : globone(cp, G_ERROR));
473 }
474 
475 static void
476 evalav(Char **v)
477 {
478     struct wordent paraml1;
479     struct wordent *hp = &paraml1;
480     struct command *t;
481     struct wordent *wdp = hp;
482 
483     set(STRstatus, Strsave(STR0));
484     hp->prev = hp->next = hp;
485     hp->word = STRNULL;
486     while (*v) {
487 	struct wordent *new = xcalloc(1, sizeof *wdp);
488 
489 	new->prev = wdp;
490 	new->next = hp;
491 	wdp->next = new;
492 	wdp = new;
493 	wdp->word = Strsave(*v++);
494     }
495     hp->prev = wdp;
496     alias(&paraml1);
497     t = syntax(paraml1.next, &paraml1, 0);
498     if (seterr)
499 	stderror(ERR_OLD);
500     execute(t, -1, NULL, NULL);
501     freelex(&paraml1), freesyn(t);
502 }
503 
504 static int
505 isa(Char *cp, int what)
506 {
507     if (cp == 0)
508 	return ((what & RESTOP) != 0);
509     if (cp[1] == 0) {
510 	if (what & ADDOP && (*cp == '+' || *cp == '-'))
511 	    return (1);
512 	if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
513 	    return (1);
514 	if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
515 			      *cp == '~' || *cp == '^' || *cp == '"'))
516 	    return (1);
517     }
518     else if (cp[2] == 0) {
519 	if (what & RESTOP) {
520 	    if (cp[0] == '|' && cp[1] == '&')
521 		return (1);
522 	    if (cp[0] == '<' && cp[1] == '<')
523 		return (1);
524 	    if (cp[0] == '>' && cp[1] == '>')
525 		return (1);
526 	}
527 	if (what & EQOP) {
528 	    if (cp[0] == '=') {
529 		if (cp[1] == '=')
530 		    return (EQEQ);
531 		if (cp[1] == '~')
532 		    return (EQMATCH);
533 	    }
534 	    else if (cp[0] == '!') {
535 		if (cp[1] == '=')
536 		    return (NOTEQ);
537 		if (cp[1] == '~')
538 		    return (NOTEQMATCH);
539 	    }
540 	}
541     }
542     if (what & RELOP) {
543 	if (*cp == '<')
544 	    return (LSS);
545 	if (*cp == '>')
546 	    return (GTR);
547     }
548     return (0);
549 }
550 
551 static int
552 egetn(Char *cp)
553 {
554     if (*cp && *cp != '-' && !Isdigit(*cp))
555 	stderror(ERR_NAME | ERR_EXPRESSION);
556     return (getn(cp));
557 }
558 
559 /* Phew! */
560