1/* $NetBSD: exp.c,v 1.19 2007/07/16 18:26:10 christos 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
35static char sccsid[] = "@(#)exp.c	8.1 (Berkeley) 5/31/93";
36#else
37__RCSID("$NetBSD: exp.c,v 1.19 2007/07/16 18:26:10 christos 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
72static int exp1(Char ***, int);
73static int csh_exp2(Char ***, int);
74static int exp2a(Char ***, int);
75static int exp2b(Char ***, int);
76static int exp2c(Char ***, int);
77static Char *exp3(Char ***, int);
78static Char *exp3a(Char ***, int);
79static Char *exp4(Char ***, int);
80static Char *exp5(Char ***, int);
81static Char *exp6(Char ***, int);
82static void evalav(Char **);
83static int isa(Char *, int);
84static int egetn(Char *);
85
86#ifdef EDEBUG
87static void etracc(char *, Char *, Char ***);
88static void etraci(char *, int, Char ***);
89#endif
90
91int
92expr(Char ***vp)
93{
94    return (exp0(vp, 0));
95}
96
97int
98exp0(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
119static int
120exp1(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
141static int
142csh_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
163static int
164exp2a(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
185static int
186exp2b(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
207static int
208exp2c(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
249static Char *
250exp3(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
289static Char *
290exp3a(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
317static Char *
318exp4(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
352static Char *
353exp5(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
396static Char *
397exp6(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
559static void
560evalav(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
589static int
590isa(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
636static int
637egetn(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
647static void
648etraci(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}
654static void
655etracc(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