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