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