sh.exp.c revision 167466
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
35RCSID("$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
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
76#ifdef EDEBUG
77static	void	 etracc		(char *, Char *, Char ***);
78static	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 */
89static int
90sh_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
188int
189expr(Char ***vp)
190{
191    return (exp0(vp, 0));
192}
193
194int
195exp0(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
211static int
212exp1(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
228static int
229exp2x(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
245static int
246exp2a(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
262static int
263exp2b(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
279static int
280exp2c(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
322static Char *
323exp3(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
363static Char *
364exp3a(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
389static Char *
390exp4(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
421static Char *
422exp5(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
472static Char *
473exp6(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 */
560Char *
561filetest(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
900static void
901evalav(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
931static int
932isa(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
980static int
981egetn(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
991static void
992etraci(char *str, int i, Char ***vp)
993{
994    xprintf("%s=%d\t", str, i);
995    blkpr(*vp);
996    xputchar('\n');
997}
998static void
999etracc(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