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