xref: /freebsd/contrib/tcsh/sh.exp.c (revision 3cbb4cc200f8a0ad7ed08233425ea54524a21f1c)
1 /*
2  * sh.exp.c: Expression evaluations
3  */
4 /*-
5  * Copyright (c) 1980, 1991 The Regents of the University of California.
6  * 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 #include "sh.h"
33 #include "tw.h"
34 
35 /*
36  * C shell
37  */
38 
39 #define TEXP_IGNORE 1	/* in ignore, it means to ignore value, just parse */
40 #define TEXP_NOGLOB 2	/* in ignore, it means not to globone */
41 
42 #define	ADDOP	1
43 #define	MULOP	2
44 #define	EQOP	4
45 #define	RELOP	8
46 #define	RESTOP	16
47 #define	ANYOP	31
48 
49 #define	EQEQ	1
50 #define	GTR	2
51 #define	LSS	4
52 #define	NOTEQ	6
53 #define EQMATCH 7
54 #define NOTEQMATCH 8
55 
56 static	int	   sh_access	(const Char *, int);
57 static	tcsh_number_t  exp1		(Char ***, int);
58 static	tcsh_number_t  exp2x	(Char ***, int);
59 static	tcsh_number_t  exp2a	(Char ***, int);
60 static	tcsh_number_t  exp2b	(Char ***, int);
61 static	tcsh_number_t  exp2c	(Char ***, int);
62 static	Char 	  *exp3		(Char ***, int);
63 static	Char 	  *exp3a	(Char ***, int);
64 static	Char 	  *exp4		(Char ***, int);
65 static	Char 	  *exp5		(Char ***, int);
66 static	Char 	  *exp6		(Char ***, int);
67 static	void	   evalav	(Char **);
68 static	int	   isa		(Char *, int);
69 static	tcsh_number_t  egetn	(const Char *);
70 
71 #ifdef EDEBUG
72 static	void	   etracc	(const char *, const Char *, Char ***);
73 static	void	   etraci	(const char *, tcsh_number_t, Char ***);
74 #else /* !EDEBUG */
75 #define etracc(A, B, C) ((void)0)
76 #define etraci(A, B, C) ((void)0)
77 #endif /* !EDEBUG */
78 
79 /*
80  * shell access function according to POSIX and non POSIX
81  * From Beto Appleton (beto@aixwiz.aix.ibm.com)
82  */
83 static int
84 sh_access(const Char *fname, int mode)
85 {
86 #if defined(POSIX) && !defined(USE_ACCESS)
87     struct stat     statb;
88 #endif /* POSIX */
89     char *name = short2str(fname);
90 
91     if (*name == '\0')
92 	return 1;
93 
94 #if !defined(POSIX) || defined(USE_ACCESS)
95     return access(name, mode);
96 #else /* POSIX */
97 
98     /*
99      * POSIX 1003.2-d11.2
100      *	-r file		True if file exists and is readable.
101      *	-w file		True if file exists and is writable.
102      *			True shall indicate only that the write flag is on.
103      *			The file shall not be writable on a read-only file
104      *			system even if this test indicates true.
105      *	-x file		True if file exists and is executable.
106      *			True shall indicate only that the execute flag is on.
107      *			If file is a directory, true indicates that the file
108      *			can be searched.
109      */
110     if (mode != W_OK && mode != X_OK)
111 	return access(name, mode);
112 
113     if (stat(name, &statb) == -1)
114 	return 1;
115 
116     if (access(name, mode) == 0) {
117 #ifdef S_ISDIR
118 	if (S_ISDIR(statb.st_mode) && mode == X_OK)
119 	    return 0;
120 #endif /* S_ISDIR */
121 
122 	/* root needs permission for someone */
123 	switch (mode) {
124 	case W_OK:
125 	    mode = S_IWUSR | S_IWGRP | S_IWOTH;
126 	    break;
127 	case X_OK:
128 	    mode = S_IXUSR | S_IXGRP | S_IXOTH;
129 	    break;
130 	default:
131 	    abort();
132 	    break;
133 	}
134 
135     }
136 
137     else if (euid == statb.st_uid)
138 	mode <<= 6;
139 
140     else if (egid == statb.st_gid)
141 	mode <<= 3;
142 
143 # ifdef NGROUPS_MAX
144     else {
145 	/* you can be in several groups */
146 	long	n;
147 	GETGROUPS_T *groups;
148 
149 	/*
150 	 * Try these things to find a positive maximum groups value:
151 	 *   1) sysconf(_SC_NGROUPS_MAX)
152 	 *   2) NGROUPS_MAX
153 	 *   3) getgroups(0, unused)
154 	 * Then allocate and scan the groups array if one of these worked.
155 	 */
156 #  if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
157 	if ((n = sysconf(_SC_NGROUPS_MAX)) == -1)
158 #  endif /* _SC_NGROUPS_MAX */
159 	    n = NGROUPS_MAX;
160 	if (n <= 0)
161 	    n = getgroups(0, (GETGROUPS_T *) NULL);
162 
163 	if (n > 0) {
164 	    groups = xmalloc(n * sizeof(*groups));
165 	    n = getgroups((int) n, groups);
166 	    while (--n >= 0)
167 		if (groups[n] == statb.st_gid) {
168 		    mode <<= 3;
169 		    break;
170 		}
171 	    xfree(groups);
172 	}
173     }
174 # endif /* NGROUPS_MAX */
175 
176     if (statb.st_mode & mode)
177 	return 0;
178     else
179 	return 1;
180 #endif /* !POSIX */
181 }
182 
183 tcsh_number_t
184 expr(Char ***vp)
185 {
186     return (exp0(vp, 0));
187 }
188 
189 tcsh_number_t
190 exp0(Char ***vp, int ignore)
191 {
192     tcsh_number_t p1 = exp1(vp, ignore);
193 
194     etraci("exp0 p1", p1, vp);
195     while (**vp && eq(**vp, STRor2)) {
196 	int p2;
197 
198 	(*vp)++;
199 
200 	p2 = compat_expr ?
201 	    exp0(vp, (ignore & TEXP_IGNORE) || p1) :
202 	    exp1(vp, (ignore & TEXP_IGNORE) || p1);
203 	if (compat_expr || !(ignore & TEXP_IGNORE))
204 	    p1 = (p1 || p2);
205 	etraci("exp0 p1", p1, vp);
206 	if (compat_expr)
207 	    break;
208     }
209     return (p1);
210 }
211 
212 static tcsh_number_t
213 exp1(Char ***vp, int ignore)
214 {
215     tcsh_number_t p1 = exp2x(vp, ignore);
216 
217     etraci("exp1 p1", p1, vp);
218     while (**vp && eq(**vp, STRand2)) {
219 	tcsh_number_t p2;
220 
221 	(*vp)++;
222 	p2 = compat_expr ?
223 	    exp1(vp, (ignore & TEXP_IGNORE) || !p1) :
224 	    exp2x(vp, (ignore & TEXP_IGNORE) || !p1);
225 
226 	etraci("exp1 p2", p2, vp);
227 	if (compat_expr || !(ignore & TEXP_IGNORE))
228 	    p1 = (p1 && p2);
229 	etraci("exp1 p1", p1, vp);
230 	if (compat_expr)
231 	    break;
232     }
233     return (p1);
234 }
235 
236 static tcsh_number_t
237 exp2x(Char ***vp, int ignore)
238 {
239     tcsh_number_t p1 = exp2a(vp, ignore);
240 
241     etraci("exp2x p1", p1, vp);
242     while (**vp && eq(**vp, STRor)) {
243 	tcsh_number_t p2;
244 
245 	(*vp)++;
246 	p2 = compat_expr ?
247 	    exp2x(vp, ignore) :
248 	    exp2a(vp, ignore);
249 	etraci("exp2x p2", p2, vp);
250 	if (compat_expr || !(ignore & TEXP_IGNORE))
251 		p1 = (p1 | p2);
252 	etraci("exp2x p1", p1, vp);
253 	if (compat_expr)
254 	    break;
255     }
256     return (p1);
257 }
258 
259 static tcsh_number_t
260 exp2a(Char ***vp, int ignore)
261 {
262     tcsh_number_t p1 = exp2b(vp, ignore);
263 
264     etraci("exp2a p1", p1, vp);
265     while (**vp && eq(**vp, STRcaret)) {
266 	tcsh_number_t p2;
267 
268 	(*vp)++;
269 	p2 = compat_expr ?
270 	    exp2a(vp, ignore) :
271 	    exp2b(vp, ignore);
272 	etraci("exp2a p2", p2, vp);
273 	if (compat_expr || !(ignore & TEXP_IGNORE))
274 	    p1 = (p1 ^ p2);
275 	etraci("exp2a p1", p1, vp);
276 	if (compat_expr)
277 	    break;
278     }
279     return (p1);
280 }
281 
282 static tcsh_number_t
283 exp2b(Char ***vp, int ignore)
284 {
285     tcsh_number_t p1 = exp2c(vp, ignore);
286 
287     etraci("exp2b p1", p1, vp);
288     while (**vp && eq(**vp, STRand)) {
289 	tcsh_number_t p2;
290 
291 	(*vp)++;
292 	p2 = compat_expr ?
293 	    exp2b(vp, ignore) :
294 	    exp2c(vp, ignore);
295 	etraci("exp2b p2", p2, vp);
296 	if (compat_expr || !(ignore & TEXP_IGNORE))
297 	    p1 = (p1 & p2);
298 	etraci("exp2b p1", p1, vp);
299 	if (compat_expr)
300 	    break;
301     }
302     return (p1);
303 }
304 
305 static tcsh_number_t
306 exp2c(Char ***vp, int ignore)
307 {
308     Char *p1 = exp3(vp, ignore);
309     Char *p2;
310     tcsh_number_t i;
311 
312     cleanup_push(p1, xfree);
313     etracc("exp2c p1", p1, vp);
314     if ((i = isa(**vp, EQOP)) != 0) {
315 	(*vp)++;
316 	if (i == EQMATCH || i == NOTEQMATCH)
317 	    ignore |= TEXP_NOGLOB;
318 	p2 = exp3(vp, ignore);
319 	cleanup_push(p2, xfree);
320 	etracc("exp2c p2", p2, vp);
321 	if (!(ignore & TEXP_IGNORE))
322 	    switch ((int)i) {
323 
324 	    case EQEQ:
325 		i = eq(p1, p2);
326 		break;
327 
328 	    case NOTEQ:
329 		i = !eq(p1, p2);
330 		break;
331 
332 	    case EQMATCH:
333 		i = Gmatch(p1, p2);
334 		break;
335 
336 	    case NOTEQMATCH:
337 		i = !Gmatch(p1, p2);
338 		break;
339 	    }
340 	cleanup_until(p1);
341 	return (i);
342     }
343     i = egetn(p1);
344     cleanup_until(p1);
345     return (i);
346 }
347 
348 static Char *
349 exp3(Char ***vp, int ignore)
350 {
351     Char *p1, *p2;
352     tcsh_number_t i;
353 
354     p1 = exp3a(vp, ignore);
355     etracc("exp3 p1", p1, vp);
356     while ((i = isa(**vp, RELOP)) != 0) {
357 	(*vp)++;
358 	if (**vp && eq(**vp, STRequal))
359 	    i |= 1, (*vp)++;
360 	cleanup_push(p1, xfree);
361 	p2 = compat_expr ?
362 	    exp3(vp, ignore) :
363 	    exp3a(vp, ignore);
364 	cleanup_push(p2, xfree);
365 	etracc("exp3 p2", p2, vp);
366 	if (!(ignore & TEXP_IGNORE))
367 	    switch ((int)i) {
368 
369 	    case GTR:
370 		i = egetn(p1) > egetn(p2);
371 		break;
372 
373 	    case GTR | 1:
374 		i = egetn(p1) >= egetn(p2);
375 		break;
376 
377 	    case LSS:
378 		i = egetn(p1) < egetn(p2);
379 		break;
380 
381 	    case LSS | 1:
382 		i = egetn(p1) <= egetn(p2);
383 		break;
384 	    }
385 	cleanup_until(p1);
386 	p1 = putn(i);
387 	etracc("exp3 p1", p1, vp);
388 	if (compat_expr)
389 	    break;
390     }
391     return (p1);
392 }
393 
394 static Char *
395 exp3a(Char ***vp, int ignore)
396 {
397     Char *p1, *p2;
398     const Char *op;
399     tcsh_number_t i;
400 
401     p1 = exp4(vp, ignore);
402     etracc("exp3a p1", p1, vp);
403     op = **vp;
404     if (op && any("<>", op[0]) && op[0] == op[1]) {
405 	(*vp)++;
406 	cleanup_push(p1, xfree);
407 	p2 = compat_expr ?
408 	    exp3a(vp, ignore) :
409 	    exp4(vp, ignore);
410 	cleanup_push(p2, xfree);
411 	etracc("exp3a p2", p2, vp);
412 	if (op[0] == '<')
413 	    i = egetn(p1) << egetn(p2);
414 	else
415 	    i = egetn(p1) >> egetn(p2);
416 	cleanup_until(p1);
417 	p1 = putn(i);
418 	etracc("exp3a p1", p1, vp);
419     }
420     return (p1);
421 }
422 
423 static Char *
424 exp4(Char ***vp, int ignore)
425 {
426     Char *p1, *p2;
427     tcsh_number_t i = 0;
428 
429     p1 = exp5(vp, ignore);
430     etracc("exp4 p1", p1, vp);
431     while (isa(**vp, ADDOP)) {
432 	const Char *op = *(*vp)++;
433 
434 	cleanup_push(p1, xfree);
435 	p2 = compat_expr ?
436 	    exp4(vp, ignore) :
437 	    exp5(vp, ignore);
438 	cleanup_push(p2, xfree);
439 	etracc("exp4 p2", p2, vp);
440 	if (!(ignore & TEXP_IGNORE))
441 	    switch (op[0]) {
442 
443 	    case '+':
444 		i = egetn(p1) + egetn(p2);
445 		break;
446 
447 	    case '-':
448 		i = egetn(p1) - egetn(p2);
449 		break;
450 	    }
451 	cleanup_until(p1);
452 	p1 = putn(i);
453 	etracc("exp4 p1", p1, vp);
454 	if (compat_expr)
455 	    break;
456     }
457     return (p1);
458 }
459 
460 static Char *
461 exp5(Char ***vp, int ignore)
462 {
463     Char *p1, *p2;
464     tcsh_number_t i = 0;
465 
466     p1 = exp6(vp, ignore);
467     etracc("exp5 p1", p1, vp);
468 
469     while (isa(**vp, MULOP)) {
470 	const Char *op = *(*vp)++;
471 	if ((ignore & TEXP_NOGLOB) != 0) {
472 	    /*
473 	     * We are just trying to get the right side of
474 	     * a =~ or !~ operator
475 	     */
476 	    xfree(p1);
477 	    return Strsave(op);
478 	}
479 
480 	cleanup_push(p1, xfree);
481 	p2 = compat_expr ?
482 	    exp5(vp, ignore) :
483 	    exp6(vp, ignore);
484 	cleanup_push(p2, xfree);
485 	etracc("exp5 p2", p2, vp);
486 	if (!(ignore & TEXP_IGNORE))
487 	    switch (op[0]) {
488 
489 	    case '*':
490 		i = egetn(p1) * egetn(p2);
491 		break;
492 
493 	    case '/':
494 		i = egetn(p2);
495 		if (i == 0)
496 		    stderror(ERR_DIV0);
497 		i = egetn(p1) / i;
498 		break;
499 
500 	    case '%':
501 		i = egetn(p2);
502 		if (i == 0)
503 		    stderror(ERR_MOD0);
504 		i = egetn(p1) % i;
505 		break;
506 	    }
507 	cleanup_until(p1);
508 	p1 = putn(i);
509 	etracc("exp5 p1", p1, vp);
510 	if (compat_expr)
511 	    break;
512     }
513     return (p1);
514 }
515 
516 static Char *
517 exp6(Char ***vp, int ignore)
518 {
519     tcsh_number_t ccode;
520     tcsh_number_t i = 0;
521     Char *cp;
522 
523     if (**vp == 0)
524 	stderror(ERR_NAME | ERR_EXPRESSION);
525     if (eq(**vp, STRbang)) {
526 	(*vp)++;
527 	cp = exp6(vp, ignore);
528 	cleanup_push(cp, xfree);
529 	etracc("exp6 ! cp", cp, vp);
530 	i = egetn(cp);
531 	cleanup_until(cp);
532 	return (putn(!i));
533     }
534     if (eq(**vp, STRtilde)) {
535 	(*vp)++;
536 	cp = exp6(vp, ignore);
537 	cleanup_push(cp, xfree);
538 	etracc("exp6 ~ cp", cp, vp);
539 	i = egetn(cp);
540 	cleanup_until(cp);
541 	return (putn(~i));
542     }
543     if (eq(**vp, STRLparen)) {
544 	(*vp)++;
545 	ccode = exp0(vp, ignore);
546 	etraci("exp6 () ccode", ccode, vp);
547 	if (**vp == 0 || ***vp != ')')
548 	    stderror(ERR_NAME | ERR_EXPRESSION);
549 	(*vp)++;
550 	return (putn(ccode));
551     }
552     if (eq(**vp, STRLbrace)) {
553 	Char **v;
554 	struct command faket;
555 	Char   *fakecom[2];
556 
557 	faket.t_dtyp = NODE_COMMAND;
558 	faket.t_dflg = F_BACKQ;
559 	faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
560 	faket.t_dcom = fakecom;
561 	fakecom[0] = STRfakecom;
562 	fakecom[1] = NULL;
563 	(*vp)++;
564 	v = *vp;
565 	for (;;) {
566 	    if (!**vp)
567 		stderror(ERR_NAME | ERR_MISSING, '}');
568 	    if (eq(*(*vp)++, STRRbrace))
569 		break;
570 	}
571 	if (ignore & TEXP_IGNORE)
572 	    return (Strsave(STRNULL));
573 	psavejob();
574 	cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */
575 	if (pfork(&faket, -1) == 0) {
576 	    *--(*vp) = 0;
577 	    evalav(v);
578 	    exitstat();
579 	}
580 	pwait();
581 	cleanup_until(&faket);
582 	etraci("exp6 {} status", egetn(varval(STRstatus)), vp);
583 	return (putn(egetn(varval(STRstatus)) == 0));
584     }
585     if (isa(**vp, ANYOP))
586 	return (Strsave(STRNULL));
587     cp = *(*vp)++;
588 #ifdef convex
589 # define FILETESTS "erwxfdzoplstSXLbcugkmKR"
590 #else
591 # define FILETESTS "erwxfdzoplstSXLbcugkmK"
592 #endif /* convex */
593 #define FILEVALS  "ZAMCDIUGNFPL"
594     if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1])))
595         return(filetest(cp, vp, ignore));
596     etracc("exp6 default", cp, vp);
597     return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND));
598 }
599 
600 
601 /*
602  * Extended file tests
603  * From: John Rowe <rowe@excc.exeter.ac.uk>
604  */
605 Char *
606 filetest(Char *cp, Char ***vp, int ignore)
607 {
608 #ifdef convex
609     struct cvxstat stb, *st = NULL;
610 # define TCSH_STAT	stat64
611 #else
612 # define TCSH_STAT	stat
613     struct stat stb, *st = NULL;
614 #endif /* convex */
615 
616 #ifdef S_IFLNK
617 # ifdef convex
618     struct cvxstat lstb, *lst = NULL;
619 #  define TCSH_LSTAT lstat64
620 # else
621 #  define TCSH_LSTAT lstat
622     struct stat lstb, *lst = NULL;
623 # endif /* convex */
624     char *filnam;
625 #endif /* S_IFLNK */
626 
627     tcsh_number_t i = 0;
628     unsigned pmask = 0xffff;
629     int altout = 0;
630     Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0',
631     *errval = STR0;
632     char *string, string0[22 + MB_LEN_MAX + 1];	/* space for 64 bit octal */
633     time_t footime;
634     struct passwd *pw;
635     struct group *gr;
636 
637     while(any(FILETESTS, *++ft))
638 	continue;
639 
640     if (!*ft && *(ft - 1) == 'L')
641 	--ft;
642 
643     if (any(FILEVALS, *ft)) {
644 	valtest = *ft++;
645 	/*
646 	 * Value tests return '-1' on failure as 0 is
647 	 * a legitimate value for many of them.
648 	 * 'F' returns ':' for compatibility.
649 	 */
650 	errval = valtest == 'F' ? STRcolon : STRminus1;
651 
652 	if (valtest == 'P' && *ft >= '0' && *ft <= '7') {
653 	    pmask = (char) *ft - '0';
654 	    while ( *++ft >= '0' && *ft <= '7' )
655 		pmask = 8 * pmask + ((char) *ft - '0');
656 	}
657 	if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) {
658 	    altout = 1;
659 	    ++ft;
660 	}
661     }
662 
663     if (*ft || ft == cp + 1)
664 	stderror(ERR_NAME | ERR_FILEINQ);
665 
666     /*
667      * Detect missing file names by checking for operator in the file name
668      * position.  However, if an operator name appears there, we must make
669      * sure that there's no file by that name (e.g., "/") before announcing
670      * an error.  Even this check isn't quite right, since it doesn't take
671      * globbing into account.
672      */
673 
674     if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb))
675 	stderror(ERR_NAME | ERR_FILENAME);
676 
677     dp = *(*vp)++;
678     if (ignore & TEXP_IGNORE)
679 	return (Strsave(STRNULL));
680     ep = globone(dp, G_APPEND);
681     cleanup_push(ep, xfree);
682     ft = &cp[1];
683     do
684 	switch (*ft) {
685 
686 	case 'r':
687 	    i = !sh_access(ep, R_OK);
688 	    break;
689 
690 	case 'w':
691 	    i = !sh_access(ep, W_OK);
692 	    break;
693 
694 	case 'x':
695 	    i = !sh_access(ep, X_OK);
696 	    break;
697 
698 	case 'X':	/* tcsh extension, name is an executable in the path
699 			 * or a tcsh builtin command
700 			 */
701 	    i = find_cmd(ep, 0);
702 	    break;
703 
704 	case 't':	/* SGI extension, true when file is a tty */
705 	    i = isatty(atoi(short2str(ep)));
706 	    break;
707 
708 	default:
709 
710 #ifdef S_IFLNK
711 	    if (tolower(*ft) == 'l') {
712 		/*
713 		 * avoid convex compiler bug.
714 		 */
715 		if (!lst) {
716 		    lst = &lstb;
717 		    if (TCSH_LSTAT(short2str(ep), lst) == -1) {
718 			cleanup_until(ep);
719 			return (Strsave(errval));
720 		    }
721 		}
722 		if (*ft == 'L')
723 		    st = lst;
724 	    }
725 	    else
726 #endif /* S_IFLNK */
727 		/*
728 		 * avoid convex compiler bug.
729 		 */
730 		if (!st) {
731 		    st = &stb;
732 		    if (TCSH_STAT(short2str(ep), st) == -1) {
733 			cleanup_until(ep);
734 			return (Strsave(errval));
735 		    }
736 		}
737 
738 	    switch (*ft) {
739 
740 	    case 'f':
741 #ifdef S_ISREG
742 		i = S_ISREG(st->st_mode);
743 #else /* !S_ISREG */
744 		i = 0;
745 #endif /* S_ISREG */
746 		break;
747 
748 	    case 'd':
749 #ifdef S_ISDIR
750 		i = S_ISDIR(st->st_mode);
751 #else /* !S_ISDIR */
752 		i = 0;
753 #endif /* S_ISDIR */
754 		break;
755 
756 	    case 'p':
757 #ifdef S_ISFIFO
758 		i = S_ISFIFO(st->st_mode);
759 #else /* !S_ISFIFO */
760 		i = 0;
761 #endif /* S_ISFIFO */
762 		break;
763 
764 	    case 'm' :
765 #ifdef S_ISOFL
766 	      i = S_ISOFL(st->st_dm_mode);
767 #else /* !S_ISOFL */
768 	      i = 0;
769 #endif /* S_ISOFL */
770 	      break ;
771 
772 	    case 'K' :
773 #ifdef S_ISOFL
774 	      i = stb.st_dm_key;
775 #else /* !S_ISOFL */
776 	      i = 0;
777 #endif /* S_ISOFL */
778 	      break ;
779 
780 
781 	    case 'l':
782 #ifdef S_ISLNK
783 		i = S_ISLNK(lst->st_mode);
784 #else /* !S_ISLNK */
785 		i = 0;
786 #endif /* S_ISLNK */
787 		break;
788 
789 	    case 'S':
790 # ifdef S_ISSOCK
791 		i = S_ISSOCK(st->st_mode);
792 # else /* !S_ISSOCK */
793 		i = 0;
794 # endif /* S_ISSOCK */
795 		break;
796 
797 	    case 'b':
798 #ifdef S_ISBLK
799 		i = S_ISBLK(st->st_mode);
800 #else /* !S_ISBLK */
801 		i = 0;
802 #endif /* S_ISBLK */
803 		break;
804 
805 	    case 'c':
806 #ifdef S_ISCHR
807 		i = S_ISCHR(st->st_mode);
808 #else /* !S_ISCHR */
809 		i = 0;
810 #endif /* S_ISCHR */
811 		break;
812 
813 	    case 'u':
814 		i = (S_ISUID & st->st_mode) != 0;
815 		break;
816 
817 	    case 'g':
818 		i = (S_ISGID & st->st_mode) != 0;
819 		break;
820 
821 	    case 'k':
822 		i = (S_ISVTX & st->st_mode) != 0;
823 		break;
824 
825 	    case 'z':
826 		i = st->st_size == 0;
827 		break;
828 
829 #ifdef convex
830 	    case 'R':
831 		i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED;
832 		break;
833 #endif /* convex */
834 
835 	    case 's':
836 		i = stb.st_size != 0;
837 		break;
838 
839 	    case 'e':
840 		i = 1;
841 		break;
842 
843 	    case 'o':
844 		i = st->st_uid == uid;
845 		break;
846 
847 		/*
848 		 * Value operators are a tcsh extension.
849 		 */
850 
851 	    case 'D':
852 		i = (tcsh_number_t) st->st_dev;
853 		break;
854 
855 	    case 'I':
856 		i = (tcsh_number_t) st->st_ino;
857 		break;
858 
859 	    case 'F':
860 		strdev = putn( (int) st->st_dev);
861 		strino = putn( (int) st->st_ino);
862 		strF = xmalloc((2 + Strlen(strdev) + Strlen(strino))
863 			       * sizeof(Char));
864 		(void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino);
865 		xfree(strdev);
866 		xfree(strino);
867 		cleanup_until(ep);
868 		return(strF);
869 
870 	    case 'L':
871 		if ( *(ft + 1) ) {
872 		    i = 1;
873 		    break;
874 		}
875 #ifdef S_ISLNK
876 		filnam = short2str(ep);
877 		string = areadlink(filnam);
878 		strF = string == NULL ? errval : str2short(string);
879 		xfree(string);
880 		cleanup_until(ep);
881 		return(Strsave(strF));
882 
883 #else /* !S_ISLNK */
884 		i = 0;
885 		break;
886 #endif /* S_ISLNK */
887 
888 
889 	    case 'N':
890 		i = (tcsh_number_t) st->st_nlink;
891 		break;
892 
893 	    case 'P':
894 		string = string0 + 1;
895 		(void) xsnprintf(string, sizeof(string0) - 1, "%o",
896 		    pmask & (unsigned int)
897 		    ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode));
898 		if (altout && *string != '0')
899 		    *--string = '0';
900 		cleanup_until(ep);
901 		return(Strsave(str2short(string)));
902 
903 	    case 'U':
904 		if (altout && (pw = xgetpwuid(st->st_uid))) {
905 		    cleanup_until(ep);
906 		    return(Strsave(str2short(pw->pw_name)));
907 		}
908 		i = (tcsh_number_t) st->st_uid;
909 		break;
910 
911 	    case 'G':
912 		if (altout && (gr = xgetgrgid(st->st_gid))) {
913 		    cleanup_until(ep);
914 		    return(Strsave(str2short(gr->gr_name)));
915 		}
916 		i = (tcsh_number_t) st->st_gid;
917 		break;
918 
919 	    case 'Z':
920 		i = (tcsh_number_t) st->st_size;
921 		break;
922 
923 	    case 'A': case 'M': case 'C':
924 		footime = *ft == 'A' ? st->st_atime :
925 		    *ft == 'M' ? st->st_mtime : st->st_ctime;
926 		if (altout) {
927 		    strF = str2short(ctime(&footime));
928 		    if ((str = Strchr(strF, '\n')) != NULL)
929 			*str = (Char) '\0';
930 		    cleanup_until(ep);
931 		    return(Strsave(strF));
932 		}
933 		i = (tcsh_number_t) footime;
934 		break;
935 
936 	    }
937 	}
938     while (*++ft && i);
939     etraci("exp6 -? i", i, vp);
940     cleanup_until(ep);
941     return (putn(i));
942 }
943 
944 
945 static void
946 evalav(Char **v)
947 {
948     struct wordent paraml1;
949     struct wordent *hp = &paraml1;
950     struct command *t;
951     struct wordent *wdp = hp;
952 
953     setcopy(STRstatus, STR0, VAR_READWRITE);
954     initlex(hp);
955     while (*v) {
956 	struct wordent *new = xcalloc(1, sizeof *wdp);
957 
958 	new->prev = wdp;
959 	new->next = hp;
960 	wdp->next = new;
961 	wdp = new;
962 	wdp->word = Strsave(*v++);
963     }
964     hp->prev = wdp;
965     cleanup_push(&paraml1, lex_cleanup);
966     alias(&paraml1);
967     t = syntax(paraml1.next, &paraml1, 0);
968     cleanup_push(t, syntax_cleanup);
969     if (seterr)
970 	stderror(ERR_OLD);
971     execute(t, -1, NULL, NULL, TRUE);
972     cleanup_until(&paraml1);
973 }
974 
975 static int
976 isa(Char *cp, int what)
977 {
978     if (cp == 0)
979 	return ((what & RESTOP) != 0);
980     if (*cp == '\0')
981     	return 0;
982     if (cp[1] == 0) {
983 	if (what & ADDOP && (*cp == '+' || *cp == '-'))
984 	    return (1);
985 	if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
986 	    return (1);
987 	if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
988 			      *cp == '~' || *cp == '^' || *cp == '"'))
989 	    return (1);
990     }
991     else if (cp[2] == 0) {
992 	if (what & RESTOP) {
993 	    if (cp[0] == '|' && cp[1] == '&')
994 		return (1);
995 	    if (cp[0] == '<' && cp[1] == '<')
996 		return (1);
997 	    if (cp[0] == '>' && cp[1] == '>')
998 		return (1);
999 	}
1000 	if (what & EQOP) {
1001 	    if (cp[0] == '=') {
1002 		if (cp[1] == '=')
1003 		    return (EQEQ);
1004 		if (cp[1] == '~')
1005 		    return (EQMATCH);
1006 	    }
1007 	    else if (cp[0] == '!') {
1008 		if (cp[1] == '=')
1009 		    return (NOTEQ);
1010 		if (cp[1] == '~')
1011 		    return (NOTEQMATCH);
1012 	    }
1013 	}
1014     }
1015     if (what & RELOP) {
1016 	if (*cp == '<')
1017 	    return (LSS);
1018 	if (*cp == '>')
1019 	    return (GTR);
1020     }
1021     return (0);
1022 }
1023 
1024 static tcsh_number_t
1025 egetn(const Char *cp)
1026 {
1027     if (*cp && *cp != '-' && !Isdigit(*cp))
1028 	stderror(ERR_NAME | ERR_EXPRESSION);
1029     return (getn(cp));
1030 }
1031 
1032 /* Phew! */
1033 
1034 #ifdef EDEBUG
1035 static void
1036 etraci(const char *str, tcsh_number_t i, Char ***vp)
1037 {
1038 #ifdef HAVE_LONG_LONG
1039     xprintf("%s=%lld\t", str, i);
1040 #else
1041     xprintf("%s=%ld\t", str, i);
1042 #endif
1043     blkpr(*vp);
1044     xputchar('\n');
1045 }
1046 static void
1047 etracc(const char *str, const Char *cp, Char ***vp)
1048 {
1049     xprintf("%s=%S\t", str, cp);
1050     blkpr(*vp);
1051     xputchar('\n');
1052 }
1053 #endif /* EDEBUG */
1054