sh.exp.c revision 195609
1/* $Header: /p/tcsh/cvsroot/tcsh/sh.exp.c,v 3.53 2007/10/01 19:09:28 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.53 2007/10/01 19:09:28 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	int	 exp1		(Char ***, int);
62static	int	 exp2x		(Char ***, int);
63static	int	 exp2a		(Char ***, int);
64static	int	 exp2b		(Char ***, int);
65static	int	 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	int	 egetn		(Char *);
74
75#ifdef EDEBUG
76static	void	 etracc		(const char *, const Char *, Char ***);
77static	void	 etraci		(const char *, int, 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
186int
187expr(Char ***vp)
188{
189    return (exp0(vp, 0));
190}
191
192int
193exp0(Char ***vp, int ignore)
194{
195    int 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 int
216exp1(Char ***vp, int ignore)
217{
218    int p1 = exp2x(vp, ignore);
219
220    etraci("exp1 p1", p1, vp);
221    while (**vp && eq(**vp, STRand2)) {
222	int 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 int
240exp2x(Char ***vp, int ignore)
241{
242    int p1 = exp2a(vp, ignore);
243
244    etraci("exp2x p1", p1, vp);
245    while (**vp && eq(**vp, STRor)) {
246	int 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 int
263exp2a(Char ***vp, int ignore)
264{
265    int p1 = exp2b(vp, ignore);
266
267    etraci("exp2a p1", p1, vp);
268    while (**vp && eq(**vp, STRcaret)) {
269	int 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 int
286exp2b(Char ***vp, int ignore)
287{
288    int p1 = exp2c(vp, ignore);
289
290    etraci("exp2b p1", p1, vp);
291    while (**vp && eq(**vp, STRand)) {
292	int 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 int
309exp2c(Char ***vp, int ignore)
310{
311    Char *p1 = exp3(vp, ignore);
312    Char *p2;
313    int 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    int 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    int 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    int 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    int 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    int     ccode, i = 0;
523    Char *cp;
524
525    if (**vp == 0)
526	stderror(ERR_NAME | ERR_EXPRESSION);
527    if (eq(**vp, STRbang)) {
528	(*vp)++;
529	cp = exp6(vp, ignore);
530	cleanup_push(cp, xfree);
531	etracc("exp6 ! cp", cp, vp);
532	i = egetn(cp);
533	cleanup_until(cp);
534	return (putn(!i));
535    }
536    if (eq(**vp, STRtilde)) {
537	(*vp)++;
538	cp = exp6(vp, ignore);
539	cleanup_push(cp, xfree);
540	etracc("exp6 ~ cp", cp, vp);
541	i = egetn(cp);
542	cleanup_until(cp);
543	return (putn(~i));
544    }
545    if (eq(**vp, STRLparen)) {
546	(*vp)++;
547	ccode = exp0(vp, ignore);
548	etraci("exp6 () ccode", ccode, vp);
549	if (**vp == 0 || ***vp != ')')
550	    stderror(ERR_NAME | ERR_EXPRESSION);
551	(*vp)++;
552	return (putn(ccode));
553    }
554    if (eq(**vp, STRLbrace)) {
555	Char **v;
556	struct command faket;
557	Char   *fakecom[2];
558
559	faket.t_dtyp = NODE_COMMAND;
560	faket.t_dflg = F_BACKQ;
561	faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
562	faket.t_dcom = fakecom;
563	fakecom[0] = STRfakecom;
564	fakecom[1] = NULL;
565	(*vp)++;
566	v = *vp;
567	for (;;) {
568	    if (!**vp)
569		stderror(ERR_NAME | ERR_MISSING, '}');
570	    if (eq(*(*vp)++, STRRbrace))
571		break;
572	}
573	if (ignore & TEXP_IGNORE)
574	    return (Strsave(STRNULL));
575	psavejob();
576	cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */
577	if (pfork(&faket, -1) == 0) {
578	    *--(*vp) = 0;
579	    evalav(v);
580	    exitstat();
581	}
582	pwait();
583	cleanup_until(&faket);
584	etraci("exp6 {} status", egetn(varval(STRstatus)), vp);
585	return (putn(egetn(varval(STRstatus)) == 0));
586    }
587    if (isa(**vp, ANYOP))
588	return (Strsave(STRNULL));
589    cp = *(*vp)++;
590#ifdef convex
591# define FILETESTS "erwxfdzoplstSXLbcugkmKR"
592#else
593# define FILETESTS "erwxfdzoplstSXLbcugkmK"
594#endif /* convex */
595#define FILEVALS  "ZAMCDIUGNFPL"
596    if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1])))
597        return(filetest(cp, vp, ignore));
598    etracc("exp6 default", cp, vp);
599    return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND));
600}
601
602
603/*
604 * Extended file tests
605 * From: John Rowe <rowe@excc.exeter.ac.uk>
606 */
607Char *
608filetest(Char *cp, Char ***vp, int ignore)
609{
610#ifdef convex
611    struct cvxstat stb, *st = NULL;
612# define TCSH_STAT	stat64
613#else
614# define TCSH_STAT	stat
615    struct stat stb, *st = NULL;
616#endif /* convex */
617
618#ifdef S_IFLNK
619# ifdef convex
620    struct cvxstat lstb, *lst = NULL;
621#  define TCSH_LSTAT lstat64
622# else
623#  define TCSH_LSTAT lstat
624    struct stat lstb, *lst = NULL;
625# endif /* convex */
626    char *filnam;
627#endif /* S_IFLNK */
628
629    int i = 0;
630    unsigned pmask = 0xffff;
631    int altout = 0;
632    Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0',
633    *errval = STR0;
634    char *string, string0[8];
635    time_t footime;
636    struct passwd *pw;
637    struct group *gr;
638
639    while(any(FILETESTS, *++ft))
640	continue;
641
642    if (!*ft && *(ft - 1) == 'L')
643	--ft;
644
645    if (any(FILEVALS, *ft)) {
646	valtest = *ft++;
647	/*
648	 * Value tests return '-1' on failure as 0 is
649	 * a legitimate value for many of them.
650	 * 'F' returns ':' for compatibility.
651	 */
652	errval = valtest == 'F' ? STRcolon : STRminus1;
653
654	if (valtest == 'P' && *ft >= '0' && *ft <= '7') {
655	    pmask = (char) *ft - '0';
656	    while ( *++ft >= '0' && *ft <= '7' )
657		pmask = 8 * pmask + ((char) *ft - '0');
658	}
659	if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) {
660	    altout = 1;
661	    ++ft;
662	}
663    }
664
665    if (*ft || ft == cp + 1)
666	stderror(ERR_NAME | ERR_FILEINQ);
667
668    /*
669     * Detect missing file names by checking for operator in the file name
670     * position.  However, if an operator name appears there, we must make
671     * sure that there's no file by that name (e.g., "/") before announcing
672     * an error.  Even this check isn't quite right, since it doesn't take
673     * globbing into account.
674     */
675
676    if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb))
677	stderror(ERR_NAME | ERR_FILENAME);
678
679    dp = *(*vp)++;
680    if (ignore & TEXP_IGNORE)
681	return (Strsave(STRNULL));
682    ep = globone(dp, G_APPEND);
683    cleanup_push(ep, xfree);
684    ft = &cp[1];
685    do
686	switch (*ft) {
687
688	case 'r':
689	    i = !sh_access(ep, R_OK);
690	    break;
691
692	case 'w':
693	    i = !sh_access(ep, W_OK);
694	    break;
695
696	case 'x':
697	    i = !sh_access(ep, X_OK);
698	    break;
699
700	case 'X':	/* tcsh extension, name is an executable in the path
701			 * or a tcsh builtin command
702			 */
703	    i = find_cmd(ep, 0);
704	    break;
705
706	case 't':	/* SGI extension, true when file is a tty */
707	    i = isatty(atoi(short2str(ep)));
708	    break;
709
710	default:
711
712#ifdef S_IFLNK
713	    if (tolower(*ft) == 'l') {
714		/*
715		 * avoid convex compiler bug.
716		 */
717		if (!lst) {
718		    lst = &lstb;
719		    if (TCSH_LSTAT(short2str(ep), lst) == -1) {
720			cleanup_until(ep);
721			return (Strsave(errval));
722		    }
723		}
724		if (*ft == 'L')
725		    st = lst;
726	    }
727	    else
728#endif /* S_IFLNK */
729		/*
730		 * avoid convex compiler bug.
731		 */
732		if (!st) {
733		    st = &stb;
734		    if (TCSH_STAT(short2str(ep), st) == -1) {
735			cleanup_until(ep);
736			return (Strsave(errval));
737		    }
738		}
739
740	    switch (*ft) {
741
742	    case 'f':
743#ifdef S_ISREG
744		i = S_ISREG(st->st_mode);
745#else /* !S_ISREG */
746		i = 0;
747#endif /* S_ISREG */
748		break;
749
750	    case 'd':
751#ifdef S_ISDIR
752		i = S_ISDIR(st->st_mode);
753#else /* !S_ISDIR */
754		i = 0;
755#endif /* S_ISDIR */
756		break;
757
758	    case 'p':
759#ifdef S_ISFIFO
760		i = S_ISFIFO(st->st_mode);
761#else /* !S_ISFIFO */
762		i = 0;
763#endif /* S_ISFIFO */
764		break;
765
766	    case 'm' :
767#ifdef S_ISOFL
768	      i = S_ISOFL(st->st_dm_mode);
769#else /* !S_ISOFL */
770	      i = 0;
771#endif /* S_ISOFL */
772	      break ;
773
774	    case 'K' :
775#ifdef S_ISOFL
776	      i = stb.st_dm_key;
777#else /* !S_ISOFL */
778	      i = 0;
779#endif /* S_ISOFL */
780	      break ;
781
782
783	    case 'l':
784#ifdef S_ISLNK
785		i = S_ISLNK(lst->st_mode);
786#else /* !S_ISLNK */
787		i = 0;
788#endif /* S_ISLNK */
789		break;
790
791	    case 'S':
792# ifdef S_ISSOCK
793		i = S_ISSOCK(st->st_mode);
794# else /* !S_ISSOCK */
795		i = 0;
796# endif /* S_ISSOCK */
797		break;
798
799	    case 'b':
800#ifdef S_ISBLK
801		i = S_ISBLK(st->st_mode);
802#else /* !S_ISBLK */
803		i = 0;
804#endif /* S_ISBLK */
805		break;
806
807	    case 'c':
808#ifdef S_ISCHR
809		i = S_ISCHR(st->st_mode);
810#else /* !S_ISCHR */
811		i = 0;
812#endif /* S_ISCHR */
813		break;
814
815	    case 'u':
816		i = (S_ISUID & st->st_mode) != 0;
817		break;
818
819	    case 'g':
820		i = (S_ISGID & st->st_mode) != 0;
821		break;
822
823	    case 'k':
824		i = (S_ISVTX & st->st_mode) != 0;
825		break;
826
827	    case 'z':
828		i = st->st_size == 0;
829		break;
830
831#ifdef convex
832	    case 'R':
833		i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED;
834		break;
835#endif /* convex */
836
837	    case 's':
838		i = stb.st_size != 0;
839		break;
840
841	    case 'e':
842		i = 1;
843		break;
844
845	    case 'o':
846		i = st->st_uid == uid;
847		break;
848
849		/*
850		 * Value operators are a tcsh extension.
851		 */
852
853	    case 'D':
854		i = (int) st->st_dev;
855		break;
856
857	    case 'I':
858		i = (int) st->st_ino;
859		break;
860
861	    case 'F':
862		strdev = putn( (int) st->st_dev);
863		strino = putn( (int) st->st_ino);
864		strF = xmalloc((2 + Strlen(strdev) + Strlen(strino))
865			       * sizeof(Char));
866		(void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino);
867		xfree(strdev);
868		xfree(strino);
869		cleanup_until(ep);
870		return(strF);
871
872	    case 'L':
873		if ( *(ft + 1) ) {
874		    i = 1;
875		    break;
876		}
877#ifdef S_ISLNK
878		filnam = short2str(ep);
879		string = areadlink(filnam);
880		strF = string == NULL ? errval : str2short(string);
881		xfree(string);
882		cleanup_until(ep);
883		return(Strsave(strF));
884
885#else /* !S_ISLNK */
886		i = 0;
887		break;
888#endif /* S_ISLNK */
889
890
891	    case 'N':
892		i = (int) st->st_nlink;
893		break;
894
895	    case 'P':
896		string = string0 + 1;
897		(void) xsnprintf(string, sizeof(string0) - 1, "%o",
898		    pmask & (unsigned int)
899		    ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode));
900		if (altout && *string != '0')
901		    *--string = '0';
902		cleanup_until(ep);
903		return(Strsave(str2short(string)));
904
905	    case 'U':
906		if (altout && (pw = xgetpwuid(st->st_uid))) {
907		    cleanup_until(ep);
908		    return(Strsave(str2short(pw->pw_name)));
909		}
910		i = (int) st->st_uid;
911		break;
912
913	    case 'G':
914		if (altout && (gr = xgetgrgid(st->st_gid))) {
915		    cleanup_until(ep);
916		    return(Strsave(str2short(gr->gr_name)));
917		}
918		i = (int) st->st_gid;
919		break;
920
921	    case 'Z':
922		i = (int) st->st_size;
923		break;
924
925	    case 'A': case 'M': case 'C':
926		footime = *ft == 'A' ? st->st_atime :
927		    *ft == 'M' ? st->st_mtime : st->st_ctime;
928		if (altout) {
929		    strF = str2short(ctime(&footime));
930		    if ((str = Strchr(strF, '\n')) != NULL)
931			*str = (Char) '\0';
932		    cleanup_until(ep);
933		    return(Strsave(strF));
934		}
935		i = (int) footime;
936		break;
937
938	    }
939	}
940    while (*++ft && i);
941    etraci("exp6 -? i", i, vp);
942    cleanup_until(ep);
943    return (putn(i));
944}
945
946
947static void
948evalav(Char **v)
949{
950    struct wordent paraml1;
951    struct wordent *hp = &paraml1;
952    struct command *t;
953    struct wordent *wdp = hp;
954
955    setcopy(STRstatus, STR0, VAR_READWRITE);
956    hp->prev = hp->next = hp;
957    hp->word = STRNULL;
958    while (*v) {
959	struct wordent *new = xcalloc(1, sizeof *wdp);
960
961	new->prev = wdp;
962	new->next = hp;
963	wdp->next = new;
964	wdp = new;
965	wdp->word = Strsave(*v++);
966    }
967    hp->prev = wdp;
968    cleanup_push(&paraml1, lex_cleanup);
969    alias(&paraml1);
970    t = syntax(paraml1.next, &paraml1, 0);
971    cleanup_push(t, syntax_cleanup);
972    if (seterr)
973	stderror(ERR_OLD);
974    execute(t, -1, NULL, NULL, TRUE);
975    cleanup_until(&paraml1);
976}
977
978static int
979isa(Char *cp, int what)
980{
981    if (cp == 0)
982	return ((what & RESTOP) != 0);
983    if (*cp == '\0')
984    	return 0;
985    if (cp[1] == 0) {
986	if (what & ADDOP && (*cp == '+' || *cp == '-'))
987	    return (1);
988	if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
989	    return (1);
990	if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
991			      *cp == '~' || *cp == '^' || *cp == '"'))
992	    return (1);
993    }
994    else if (cp[2] == 0) {
995	if (what & RESTOP) {
996	    if (cp[0] == '|' && cp[1] == '&')
997		return (1);
998	    if (cp[0] == '<' && cp[1] == '<')
999		return (1);
1000	    if (cp[0] == '>' && cp[1] == '>')
1001		return (1);
1002	}
1003	if (what & EQOP) {
1004	    if (cp[0] == '=') {
1005		if (cp[1] == '=')
1006		    return (EQEQ);
1007		if (cp[1] == '~')
1008		    return (EQMATCH);
1009	    }
1010	    else if (cp[0] == '!') {
1011		if (cp[1] == '=')
1012		    return (NOTEQ);
1013		if (cp[1] == '~')
1014		    return (NOTEQMATCH);
1015	    }
1016	}
1017    }
1018    if (what & RELOP) {
1019	if (*cp == '<')
1020	    return (LSS);
1021	if (*cp == '>')
1022	    return (GTR);
1023    }
1024    return (0);
1025}
1026
1027static int
1028egetn(Char *cp)
1029{
1030    if (*cp && *cp != '-' && !Isdigit(*cp))
1031	stderror(ERR_NAME | ERR_EXPRESSION);
1032    return (getn(cp));
1033}
1034
1035/* Phew! */
1036
1037#ifdef EDEBUG
1038static void
1039etraci(const char *str, int i, Char ***vp)
1040{
1041    xprintf("%s=%d\t", str, i);
1042    blkpr(*vp);
1043    xputchar('\n');
1044}
1045static void
1046etracc(const char *str, const Char *cp, Char ***vp)
1047{
1048    xprintf("%s=%S\t", str, cp);
1049    blkpr(*vp);
1050    xputchar('\n');
1051}
1052#endif /* EDEBUG */
1053