sh.exp.c revision 316957
1/* $Header: /p/tcsh/cvsroot/tcsh/sh.exp.c,v 3.63 2015/12/09 17:17:43 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.63 2015/12/09 17:17:43 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	    xfree(groups);
176	}
177    }
178# endif /* NGROUPS_MAX */
179
180    if (statb.st_mode & mode)
181	return 0;
182    else
183	return 1;
184#endif /* !POSIX */
185}
186
187tcsh_number_t
188expr(Char ***vp)
189{
190    return (exp0(vp, 0));
191}
192
193tcsh_number_t
194exp0(Char ***vp, int ignore)
195{
196    tcsh_number_t p1 = exp1(vp, ignore);
197
198    etraci("exp0 p1", p1, vp);
199    while (**vp && eq(**vp, STRor2)) {
200	int p2;
201
202	(*vp)++;
203
204	p2 = compat_expr ?
205	    exp0(vp, (ignore & TEXP_IGNORE) || p1) :
206	    exp1(vp, (ignore & TEXP_IGNORE) || p1);
207	if (compat_expr || !(ignore & TEXP_IGNORE))
208	    p1 = (p1 || p2);
209	etraci("exp0 p1", p1, vp);
210	if (compat_expr)
211	    break;
212    }
213    return (p1);
214}
215
216static tcsh_number_t
217exp1(Char ***vp, int ignore)
218{
219    tcsh_number_t p1 = exp2x(vp, ignore);
220
221    etraci("exp1 p1", p1, vp);
222    while (**vp && eq(**vp, STRand2)) {
223	tcsh_number_t p2;
224
225	(*vp)++;
226	p2 = compat_expr ?
227	    exp1(vp, (ignore & TEXP_IGNORE) || !p1) :
228	    exp2x(vp, (ignore & TEXP_IGNORE) || !p1);
229
230	etraci("exp1 p2", p2, vp);
231	if (compat_expr || !(ignore & TEXP_IGNORE))
232	    p1 = (p1 && p2);
233	etraci("exp1 p1", p1, vp);
234	if (compat_expr)
235	    break;
236    }
237    return (p1);
238}
239
240static tcsh_number_t
241exp2x(Char ***vp, int ignore)
242{
243    tcsh_number_t p1 = exp2a(vp, ignore);
244
245    etraci("exp2x p1", p1, vp);
246    while (**vp && eq(**vp, STRor)) {
247	tcsh_number_t p2;
248
249	(*vp)++;
250	p2 = compat_expr ?
251	    exp2x(vp, ignore) :
252	    exp2a(vp, ignore);
253	etraci("exp2x p2", p2, vp);
254	if (compat_expr || !(ignore & TEXP_IGNORE))
255		p1 = (p1 | p2);
256	etraci("exp2x p1", p1, vp);
257	if (compat_expr)
258	    break;
259    }
260    return (p1);
261}
262
263static tcsh_number_t
264exp2a(Char ***vp, int ignore)
265{
266    tcsh_number_t p1 = exp2b(vp, ignore);
267
268    etraci("exp2a p1", p1, vp);
269    while (**vp && eq(**vp, STRcaret)) {
270	tcsh_number_t p2;
271
272	(*vp)++;
273	p2 = compat_expr ?
274	    exp2a(vp, ignore) :
275	    exp2b(vp, ignore);
276	etraci("exp2a p2", p2, vp);
277	if (compat_expr || !(ignore & TEXP_IGNORE))
278	    p1 = (p1 ^ p2);
279	etraci("exp2a p1", p1, vp);
280	if (compat_expr)
281	    break;
282    }
283    return (p1);
284}
285
286static tcsh_number_t
287exp2b(Char ***vp, int ignore)
288{
289    tcsh_number_t p1 = exp2c(vp, ignore);
290
291    etraci("exp2b p1", p1, vp);
292    while (**vp && eq(**vp, STRand)) {
293	tcsh_number_t p2;
294
295	(*vp)++;
296	p2 = compat_expr ?
297	    exp2b(vp, ignore) :
298	    exp2c(vp, ignore);
299	etraci("exp2b p2", p2, vp);
300	if (compat_expr || !(ignore & TEXP_IGNORE))
301	    p1 = (p1 & p2);
302	etraci("exp2b p1", p1, vp);
303	if (compat_expr)
304	    break;
305    }
306    return (p1);
307}
308
309static tcsh_number_t
310exp2c(Char ***vp, int ignore)
311{
312    Char *p1 = exp3(vp, ignore);
313    Char *p2;
314    tcsh_number_t i;
315
316    cleanup_push(p1, xfree);
317    etracc("exp2c p1", p1, vp);
318    if ((i = isa(**vp, EQOP)) != 0) {
319	(*vp)++;
320	if (i == EQMATCH || i == NOTEQMATCH)
321	    ignore |= TEXP_NOGLOB;
322	p2 = exp3(vp, ignore);
323	cleanup_push(p2, xfree);
324	etracc("exp2c p2", p2, vp);
325	if (!(ignore & TEXP_IGNORE))
326	    switch ((int)i) {
327
328	    case EQEQ:
329		i = eq(p1, p2);
330		break;
331
332	    case NOTEQ:
333		i = !eq(p1, p2);
334		break;
335
336	    case EQMATCH:
337		i = Gmatch(p1, p2);
338		break;
339
340	    case NOTEQMATCH:
341		i = !Gmatch(p1, p2);
342		break;
343	    }
344	cleanup_until(p1);
345	return (i);
346    }
347    i = egetn(p1);
348    cleanup_until(p1);
349    return (i);
350}
351
352static Char *
353exp3(Char ***vp, int ignore)
354{
355    Char *p1, *p2;
356    tcsh_number_t i;
357
358    p1 = exp3a(vp, ignore);
359    etracc("exp3 p1", p1, vp);
360    while ((i = isa(**vp, RELOP)) != 0) {
361	(*vp)++;
362	if (**vp && eq(**vp, STRequal))
363	    i |= 1, (*vp)++;
364	cleanup_push(p1, xfree);
365	p2 = compat_expr ?
366	    exp3(vp, ignore) :
367	    exp3a(vp, ignore);
368	cleanup_push(p2, xfree);
369	etracc("exp3 p2", p2, vp);
370	if (!(ignore & TEXP_IGNORE))
371	    switch ((int)i) {
372
373	    case GTR:
374		i = egetn(p1) > egetn(p2);
375		break;
376
377	    case GTR | 1:
378		i = egetn(p1) >= egetn(p2);
379		break;
380
381	    case LSS:
382		i = egetn(p1) < egetn(p2);
383		break;
384
385	    case LSS | 1:
386		i = egetn(p1) <= egetn(p2);
387		break;
388	    }
389	cleanup_until(p1);
390	p1 = putn(i);
391	etracc("exp3 p1", p1, vp);
392	if (compat_expr)
393	    break;
394    }
395    return (p1);
396}
397
398static Char *
399exp3a(Char ***vp, int ignore)
400{
401    Char *p1, *p2;
402    const Char *op;
403    tcsh_number_t i;
404
405    p1 = exp4(vp, ignore);
406    etracc("exp3a p1", p1, vp);
407    op = **vp;
408    if (op && any("<>", op[0]) && op[0] == op[1]) {
409	(*vp)++;
410	cleanup_push(p1, xfree);
411	p2 = compat_expr ?
412	    exp3a(vp, ignore) :
413	    exp4(vp, ignore);
414	cleanup_push(p2, xfree);
415	etracc("exp3a p2", p2, vp);
416	if (op[0] == '<')
417	    i = egetn(p1) << egetn(p2);
418	else
419	    i = egetn(p1) >> egetn(p2);
420	cleanup_until(p1);
421	p1 = putn(i);
422	etracc("exp3a p1", p1, vp);
423    }
424    return (p1);
425}
426
427static Char *
428exp4(Char ***vp, int ignore)
429{
430    Char *p1, *p2;
431    tcsh_number_t i = 0;
432
433    p1 = exp5(vp, ignore);
434    etracc("exp4 p1", p1, vp);
435    while (isa(**vp, ADDOP)) {
436	const Char *op = *(*vp)++;
437
438	cleanup_push(p1, xfree);
439	p2 = compat_expr ?
440	    exp4(vp, ignore) :
441	    exp5(vp, ignore);
442	cleanup_push(p2, xfree);
443	etracc("exp4 p2", p2, vp);
444	if (!(ignore & TEXP_IGNORE))
445	    switch (op[0]) {
446
447	    case '+':
448		i = egetn(p1) + egetn(p2);
449		break;
450
451	    case '-':
452		i = egetn(p1) - egetn(p2);
453		break;
454	    }
455	cleanup_until(p1);
456	p1 = putn(i);
457	etracc("exp4 p1", p1, vp);
458	if (compat_expr)
459	    break;
460    }
461    return (p1);
462}
463
464static Char *
465exp5(Char ***vp, int ignore)
466{
467    Char *p1, *p2;
468    tcsh_number_t i = 0;
469
470    p1 = exp6(vp, ignore);
471    etracc("exp5 p1", p1, vp);
472
473    while (isa(**vp, MULOP)) {
474	const Char *op = *(*vp)++;
475	if ((ignore & TEXP_NOGLOB) != 0) {
476	    /*
477	     * We are just trying to get the right side of
478	     * a =~ or !~ operator
479	     */
480	    xfree(p1);
481	    return Strsave(op);
482	}
483
484	cleanup_push(p1, xfree);
485	p2 = compat_expr ?
486	    exp5(vp, ignore) :
487	    exp6(vp, ignore);
488	cleanup_push(p2, xfree);
489	etracc("exp5 p2", p2, vp);
490	if (!(ignore & TEXP_IGNORE))
491	    switch (op[0]) {
492
493	    case '*':
494		i = egetn(p1) * egetn(p2);
495		break;
496
497	    case '/':
498		i = egetn(p2);
499		if (i == 0)
500		    stderror(ERR_DIV0);
501		i = egetn(p1) / i;
502		break;
503
504	    case '%':
505		i = egetn(p2);
506		if (i == 0)
507		    stderror(ERR_MOD0);
508		i = egetn(p1) % i;
509		break;
510	    }
511	cleanup_until(p1);
512	p1 = putn(i);
513	etracc("exp5 p1", p1, vp);
514	if (compat_expr)
515	    break;
516    }
517    return (p1);
518}
519
520static Char *
521exp6(Char ***vp, int ignore)
522{
523    tcsh_number_t ccode;
524    tcsh_number_t i = 0;
525    Char *cp;
526
527    if (**vp == 0)
528	stderror(ERR_NAME | ERR_EXPRESSION);
529    if (eq(**vp, STRbang)) {
530	(*vp)++;
531	cp = exp6(vp, ignore);
532	cleanup_push(cp, xfree);
533	etracc("exp6 ! cp", cp, vp);
534	i = egetn(cp);
535	cleanup_until(cp);
536	return (putn(!i));
537    }
538    if (eq(**vp, STRtilde)) {
539	(*vp)++;
540	cp = exp6(vp, ignore);
541	cleanup_push(cp, xfree);
542	etracc("exp6 ~ cp", cp, vp);
543	i = egetn(cp);
544	cleanup_until(cp);
545	return (putn(~i));
546    }
547    if (eq(**vp, STRLparen)) {
548	(*vp)++;
549	ccode = exp0(vp, ignore);
550	etraci("exp6 () ccode", ccode, vp);
551	if (**vp == 0 || ***vp != ')')
552	    stderror(ERR_NAME | ERR_EXPRESSION);
553	(*vp)++;
554	return (putn(ccode));
555    }
556    if (eq(**vp, STRLbrace)) {
557	Char **v;
558	struct command faket;
559	Char   *fakecom[2];
560
561	faket.t_dtyp = NODE_COMMAND;
562	faket.t_dflg = F_BACKQ;
563	faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
564	faket.t_dcom = fakecom;
565	fakecom[0] = STRfakecom;
566	fakecom[1] = NULL;
567	(*vp)++;
568	v = *vp;
569	for (;;) {
570	    if (!**vp)
571		stderror(ERR_NAME | ERR_MISSING, '}');
572	    if (eq(*(*vp)++, STRRbrace))
573		break;
574	}
575	if (ignore & TEXP_IGNORE)
576	    return (Strsave(STRNULL));
577	psavejob();
578	cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */
579	if (pfork(&faket, -1) == 0) {
580	    *--(*vp) = 0;
581	    evalav(v);
582	    exitstat();
583	}
584	pwait();
585	cleanup_until(&faket);
586	etraci("exp6 {} status", egetn(varval(STRstatus)), vp);
587	return (putn(egetn(varval(STRstatus)) == 0));
588    }
589    if (isa(**vp, ANYOP))
590	return (Strsave(STRNULL));
591    cp = *(*vp)++;
592#ifdef convex
593# define FILETESTS "erwxfdzoplstSXLbcugkmKR"
594#else
595# define FILETESTS "erwxfdzoplstSXLbcugkmK"
596#endif /* convex */
597#define FILEVALS  "ZAMCDIUGNFPL"
598    if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1])))
599        return(filetest(cp, vp, ignore));
600    etracc("exp6 default", cp, vp);
601    return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND));
602}
603
604
605/*
606 * Extended file tests
607 * From: John Rowe <rowe@excc.exeter.ac.uk>
608 */
609Char *
610filetest(Char *cp, Char ***vp, int ignore)
611{
612#ifdef convex
613    struct cvxstat stb, *st = NULL;
614# define TCSH_STAT	stat64
615#else
616# define TCSH_STAT	stat
617    struct stat stb, *st = NULL;
618#endif /* convex */
619
620#ifdef S_IFLNK
621# ifdef convex
622    struct cvxstat lstb, *lst = NULL;
623#  define TCSH_LSTAT lstat64
624# else
625#  define TCSH_LSTAT lstat
626    struct stat lstb, *lst = NULL;
627# endif /* convex */
628    char *filnam;
629#endif /* S_IFLNK */
630
631    tcsh_number_t i = 0;
632    unsigned pmask = 0xffff;
633    int altout = 0;
634    Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0',
635    *errval = STR0;
636    char *string, string0[22 + MB_LEN_MAX + 1];	/* space for 64 bit octal */
637    time_t footime;
638    struct passwd *pw;
639    struct group *gr;
640
641    while(any(FILETESTS, *++ft))
642	continue;
643
644    if (!*ft && *(ft - 1) == 'L')
645	--ft;
646
647    if (any(FILEVALS, *ft)) {
648	valtest = *ft++;
649	/*
650	 * Value tests return '-1' on failure as 0 is
651	 * a legitimate value for many of them.
652	 * 'F' returns ':' for compatibility.
653	 */
654	errval = valtest == 'F' ? STRcolon : STRminus1;
655
656	if (valtest == 'P' && *ft >= '0' && *ft <= '7') {
657	    pmask = (char) *ft - '0';
658	    while ( *++ft >= '0' && *ft <= '7' )
659		pmask = 8 * pmask + ((char) *ft - '0');
660	}
661	if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) {
662	    altout = 1;
663	    ++ft;
664	}
665    }
666
667    if (*ft || ft == cp + 1)
668	stderror(ERR_NAME | ERR_FILEINQ);
669
670    /*
671     * Detect missing file names by checking for operator in the file name
672     * position.  However, if an operator name appears there, we must make
673     * sure that there's no file by that name (e.g., "/") before announcing
674     * an error.  Even this check isn't quite right, since it doesn't take
675     * globbing into account.
676     */
677
678    if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb))
679	stderror(ERR_NAME | ERR_FILENAME);
680
681    dp = *(*vp)++;
682    if (ignore & TEXP_IGNORE)
683	return (Strsave(STRNULL));
684    ep = globone(dp, G_APPEND);
685    cleanup_push(ep, xfree);
686    ft = &cp[1];
687    do
688	switch (*ft) {
689
690	case 'r':
691	    i = !sh_access(ep, R_OK);
692	    break;
693
694	case 'w':
695	    i = !sh_access(ep, W_OK);
696	    break;
697
698	case 'x':
699	    i = !sh_access(ep, X_OK);
700	    break;
701
702	case 'X':	/* tcsh extension, name is an executable in the path
703			 * or a tcsh builtin command
704			 */
705	    i = find_cmd(ep, 0);
706	    break;
707
708	case 't':	/* SGI extension, true when file is a tty */
709	    i = isatty(atoi(short2str(ep)));
710	    break;
711
712	default:
713
714#ifdef S_IFLNK
715	    if (tolower(*ft) == 'l') {
716		/*
717		 * avoid convex compiler bug.
718		 */
719		if (!lst) {
720		    lst = &lstb;
721		    if (TCSH_LSTAT(short2str(ep), lst) == -1) {
722			cleanup_until(ep);
723			return (Strsave(errval));
724		    }
725		}
726		if (*ft == 'L')
727		    st = lst;
728	    }
729	    else
730#endif /* S_IFLNK */
731		/*
732		 * avoid convex compiler bug.
733		 */
734		if (!st) {
735		    st = &stb;
736		    if (TCSH_STAT(short2str(ep), st) == -1) {
737			cleanup_until(ep);
738			return (Strsave(errval));
739		    }
740		}
741
742	    switch (*ft) {
743
744	    case 'f':
745#ifdef S_ISREG
746		i = S_ISREG(st->st_mode);
747#else /* !S_ISREG */
748		i = 0;
749#endif /* S_ISREG */
750		break;
751
752	    case 'd':
753#ifdef S_ISDIR
754		i = S_ISDIR(st->st_mode);
755#else /* !S_ISDIR */
756		i = 0;
757#endif /* S_ISDIR */
758		break;
759
760	    case 'p':
761#ifdef S_ISFIFO
762		i = S_ISFIFO(st->st_mode);
763#else /* !S_ISFIFO */
764		i = 0;
765#endif /* S_ISFIFO */
766		break;
767
768	    case 'm' :
769#ifdef S_ISOFL
770	      i = S_ISOFL(st->st_dm_mode);
771#else /* !S_ISOFL */
772	      i = 0;
773#endif /* S_ISOFL */
774	      break ;
775
776	    case 'K' :
777#ifdef S_ISOFL
778	      i = stb.st_dm_key;
779#else /* !S_ISOFL */
780	      i = 0;
781#endif /* S_ISOFL */
782	      break ;
783
784
785	    case 'l':
786#ifdef S_ISLNK
787		i = S_ISLNK(lst->st_mode);
788#else /* !S_ISLNK */
789		i = 0;
790#endif /* S_ISLNK */
791		break;
792
793	    case 'S':
794# ifdef S_ISSOCK
795		i = S_ISSOCK(st->st_mode);
796# else /* !S_ISSOCK */
797		i = 0;
798# endif /* S_ISSOCK */
799		break;
800
801	    case 'b':
802#ifdef S_ISBLK
803		i = S_ISBLK(st->st_mode);
804#else /* !S_ISBLK */
805		i = 0;
806#endif /* S_ISBLK */
807		break;
808
809	    case 'c':
810#ifdef S_ISCHR
811		i = S_ISCHR(st->st_mode);
812#else /* !S_ISCHR */
813		i = 0;
814#endif /* S_ISCHR */
815		break;
816
817	    case 'u':
818		i = (S_ISUID & st->st_mode) != 0;
819		break;
820
821	    case 'g':
822		i = (S_ISGID & st->st_mode) != 0;
823		break;
824
825	    case 'k':
826		i = (S_ISVTX & st->st_mode) != 0;
827		break;
828
829	    case 'z':
830		i = st->st_size == 0;
831		break;
832
833#ifdef convex
834	    case 'R':
835		i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED;
836		break;
837#endif /* convex */
838
839	    case 's':
840		i = stb.st_size != 0;
841		break;
842
843	    case 'e':
844		i = 1;
845		break;
846
847	    case 'o':
848		i = st->st_uid == uid;
849		break;
850
851		/*
852		 * Value operators are a tcsh extension.
853		 */
854
855	    case 'D':
856		i = (tcsh_number_t) st->st_dev;
857		break;
858
859	    case 'I':
860		i = (tcsh_number_t) st->st_ino;
861		break;
862
863	    case 'F':
864		strdev = putn( (int) st->st_dev);
865		strino = putn( (int) st->st_ino);
866		strF = xmalloc((2 + Strlen(strdev) + Strlen(strino))
867			       * sizeof(Char));
868		(void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino);
869		xfree(strdev);
870		xfree(strino);
871		cleanup_until(ep);
872		return(strF);
873
874	    case 'L':
875		if ( *(ft + 1) ) {
876		    i = 1;
877		    break;
878		}
879#ifdef S_ISLNK
880		filnam = short2str(ep);
881		string = areadlink(filnam);
882		strF = string == NULL ? errval : str2short(string);
883		xfree(string);
884		cleanup_until(ep);
885		return(Strsave(strF));
886
887#else /* !S_ISLNK */
888		i = 0;
889		break;
890#endif /* S_ISLNK */
891
892
893	    case 'N':
894		i = (tcsh_number_t) st->st_nlink;
895		break;
896
897	    case 'P':
898		string = string0 + 1;
899		(void) xsnprintf(string, sizeof(string0) - 1, "%o",
900		    pmask & (unsigned int)
901		    ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode));
902		if (altout && *string != '0')
903		    *--string = '0';
904		cleanup_until(ep);
905		return(Strsave(str2short(string)));
906
907	    case 'U':
908		if (altout && (pw = xgetpwuid(st->st_uid))) {
909		    cleanup_until(ep);
910		    return(Strsave(str2short(pw->pw_name)));
911		}
912		i = (tcsh_number_t) st->st_uid;
913		break;
914
915	    case 'G':
916		if (altout && (gr = xgetgrgid(st->st_gid))) {
917		    cleanup_until(ep);
918		    return(Strsave(str2short(gr->gr_name)));
919		}
920		i = (tcsh_number_t) st->st_gid;
921		break;
922
923	    case 'Z':
924		i = (tcsh_number_t) st->st_size;
925		break;
926
927	    case 'A': case 'M': case 'C':
928		footime = *ft == 'A' ? st->st_atime :
929		    *ft == 'M' ? st->st_mtime : st->st_ctime;
930		if (altout) {
931		    strF = str2short(ctime(&footime));
932		    if ((str = Strchr(strF, '\n')) != NULL)
933			*str = (Char) '\0';
934		    cleanup_until(ep);
935		    return(Strsave(strF));
936		}
937		i = (tcsh_number_t) footime;
938		break;
939
940	    }
941	}
942    while (*++ft && i);
943    etraci("exp6 -? i", i, vp);
944    cleanup_until(ep);
945    return (putn(i));
946}
947
948
949static void
950evalav(Char **v)
951{
952    struct wordent paraml1;
953    struct wordent *hp = &paraml1;
954    struct command *t;
955    struct wordent *wdp = hp;
956
957    setcopy(STRstatus, STR0, VAR_READWRITE);
958    initlex(hp);
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