1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.exp.c,v 3.61 2012/11/28 13:45:11 christos Exp $ */
3 * sh.exp.c: Expression evaluations
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
35 RCSID("$tcsh: sh.exp.c,v 3.61 2012/11/28 13:45:11 christos Exp $")
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 */
60 static int sh_access (const Char
*, int);
61 static tcsh_number_t
exp1 (Char
***, int);
62 static tcsh_number_t
exp2x (Char
***, int);
63 static tcsh_number_t
exp2a (Char
***, int);
64 static tcsh_number_t
exp2b (Char
***, int);
65 static tcsh_number_t
exp2c (Char
***, int);
66 static Char
*exp3 (Char
***, int);
67 static Char
*exp3a (Char
***, int);
68 static Char
*exp4 (Char
***, int);
69 static Char
*exp5 (Char
***, int);
70 static Char
*exp6 (Char
***, int);
71 static void evalav (Char
**);
72 static int isa (Char
*, int);
73 static tcsh_number_t
egetn (const Char
*);
76 static void etracc (const char *, const Char
*, Char
***);
77 static void etraci (const char *, tcsh_number_t
, Char
***);
79 #define etracc(A, B, C) ((void)0)
80 #define etraci(A, B, C) ((void)0)
84 * shell access function according to POSIX and non POSIX
85 * From Beto Appleton (beto@aixwiz.aix.ibm.com)
88 sh_access(const Char
*fname
, int mode
)
90 #if defined(POSIX) && !defined(USE_ACCESS)
93 char *name
= short2str(fname
);
98 #if !defined(POSIX) || defined(USE_ACCESS)
99 return access(name
, mode
);
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
114 if (mode
!= W_OK
&& mode
!= X_OK
)
115 return access(name
, mode
);
117 if (stat(name
, &statb
) == -1)
120 if (access(name
, mode
) == 0) {
122 if (S_ISDIR(statb
.st_mode
) && mode
== X_OK
)
126 /* root needs permission for someone */
129 mode
= S_IWUSR
| S_IWGRP
| S_IWOTH
;
132 mode
= S_IXUSR
| S_IXGRP
| S_IXOTH
;
141 else if (euid
== statb
.st_uid
)
144 else if (egid
== statb
.st_gid
)
149 /* you can be in several groups */
154 * Try these things to find a positive maximum groups value:
155 * 1) sysconf(_SC_NGROUPS_MAX)
157 * 3) getgroups(0, unused)
158 * Then allocate and scan the groups array if one of these worked.
160 # if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
161 if ((n
= sysconf(_SC_NGROUPS_MAX
)) == -1)
162 # endif /* _SC_NGROUPS_MAX */
165 n
= getgroups(0, (GETGROUPS_T
*) NULL
);
168 groups
= xmalloc(n
* sizeof(*groups
));
169 n
= getgroups((int) n
, groups
);
171 if (groups
[n
] == statb
.st_gid
) {
178 # endif /* NGROUPS_MAX */
180 if (statb
.st_mode
& mode
)
190 return (exp0(vp
, 0));
194 exp0(Char
***vp
, int ignore
)
196 tcsh_number_t p1
= exp1(vp
, ignore
);
198 etraci("exp0 p1", p1
, vp
);
199 while (**vp
&& eq(**vp
, STRor2
)) {
205 exp0(vp
, (ignore
& TEXP_IGNORE
) || p1
) :
206 exp1(vp
, (ignore
& TEXP_IGNORE
) || p1
);
207 if (compat_expr
|| !(ignore
& TEXP_IGNORE
))
209 etraci("exp0 p1", p1
, vp
);
217 exp1(Char
***vp
, int ignore
)
219 tcsh_number_t p1
= exp2x(vp
, ignore
);
221 etraci("exp1 p1", p1
, vp
);
222 while (**vp
&& eq(**vp
, STRand2
)) {
227 exp1(vp
, (ignore
& TEXP_IGNORE
) || !p1
) :
228 exp2x(vp
, (ignore
& TEXP_IGNORE
) || !p1
);
230 etraci("exp1 p2", p2
, vp
);
231 if (compat_expr
|| !(ignore
& TEXP_IGNORE
))
233 etraci("exp1 p1", p1
, vp
);
241 exp2x(Char
***vp
, int ignore
)
243 tcsh_number_t p1
= exp2a(vp
, ignore
);
245 etraci("exp2x p1", p1
, vp
);
246 while (**vp
&& eq(**vp
, STRor
)) {
253 etraci("exp2x p2", p2
, vp
);
254 if (compat_expr
|| !(ignore
& TEXP_IGNORE
))
256 etraci("exp2x p1", p1
, vp
);
264 exp2a(Char
***vp
, int ignore
)
266 tcsh_number_t p1
= exp2b(vp
, ignore
);
268 etraci("exp2a p1", p1
, vp
);
269 while (**vp
&& eq(**vp
, STRcaret
)) {
276 etraci("exp2a p2", p2
, vp
);
277 if (compat_expr
|| !(ignore
& TEXP_IGNORE
))
279 etraci("exp2a p1", p1
, vp
);
287 exp2b(Char
***vp
, int ignore
)
289 tcsh_number_t p1
= exp2c(vp
, ignore
);
291 etraci("exp2b p1", p1
, vp
);
292 while (**vp
&& eq(**vp
, STRand
)) {
299 etraci("exp2b p2", p2
, vp
);
300 if (compat_expr
|| !(ignore
& TEXP_IGNORE
))
302 etraci("exp2b p1", p1
, vp
);
310 exp2c(Char
***vp
, int ignore
)
312 Char
*p1
= exp3(vp
, ignore
);
316 cleanup_push(p1
, xfree
);
317 etracc("exp2c p1", p1
, vp
);
318 if ((i
= isa(**vp
, EQOP
)) != 0) {
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
))
353 exp3(Char
***vp
, int ignore
)
358 p1
= exp3a(vp
, ignore
);
359 etracc("exp3 p1", p1
, vp
);
360 while ((i
= isa(**vp
, RELOP
)) != 0) {
362 if (**vp
&& eq(**vp
, STRequal
))
364 cleanup_push(p1
, xfree
);
368 cleanup_push(p2
, xfree
);
369 etracc("exp3 p2", p2
, vp
);
370 if (!(ignore
& TEXP_IGNORE
))
374 i
= egetn(p1
) > egetn(p2
);
378 i
= egetn(p1
) >= egetn(p2
);
382 i
= egetn(p1
) < egetn(p2
);
386 i
= egetn(p1
) <= egetn(p2
);
391 etracc("exp3 p1", p1
, vp
);
399 exp3a(Char
***vp
, int ignore
)
405 p1
= exp4(vp
, ignore
);
406 etracc("exp3a p1", p1
, vp
);
408 if (op
&& any("<>", op
[0]) && op
[0] == op
[1]) {
410 cleanup_push(p1
, xfree
);
414 cleanup_push(p2
, xfree
);
415 etracc("exp3a p2", p2
, vp
);
417 i
= egetn(p1
) << egetn(p2
);
419 i
= egetn(p1
) >> egetn(p2
);
422 etracc("exp3a p1", p1
, vp
);
428 exp4(Char
***vp
, int ignore
)
433 p1
= exp5(vp
, ignore
);
434 etracc("exp4 p1", p1
, vp
);
435 while (isa(**vp
, ADDOP
)) {
436 const Char
*op
= *(*vp
)++;
438 cleanup_push(p1
, xfree
);
442 cleanup_push(p2
, xfree
);
443 etracc("exp4 p2", p2
, vp
);
444 if (!(ignore
& TEXP_IGNORE
))
448 i
= egetn(p1
) + egetn(p2
);
452 i
= egetn(p1
) - egetn(p2
);
457 etracc("exp4 p1", p1
, vp
);
465 exp5(Char
***vp
, int ignore
)
470 p1
= exp6(vp
, ignore
);
471 etracc("exp5 p1", p1
, vp
);
473 while (isa(**vp
, MULOP
)) {
474 const Char
*op
= *(*vp
)++;
475 if ((ignore
& TEXP_NOGLOB
) != 0) {
477 * We are just trying to get the right side of
478 * a =~ or !~ operator
484 cleanup_push(p1
, xfree
);
488 cleanup_push(p2
, xfree
);
489 etracc("exp5 p2", p2
, vp
);
490 if (!(ignore
& TEXP_IGNORE
))
494 i
= egetn(p1
) * egetn(p2
);
513 etracc("exp5 p1", p1
, vp
);
521 exp6(Char
***vp
, int ignore
)
528 stderror(ERR_NAME
| ERR_EXPRESSION
);
529 if (eq(**vp
, STRbang
)) {
531 cp
= exp6(vp
, ignore
);
532 cleanup_push(cp
, xfree
);
533 etracc("exp6 ! cp", cp
, vp
);
538 if (eq(**vp
, STRtilde
)) {
540 cp
= exp6(vp
, ignore
);
541 cleanup_push(cp
, xfree
);
542 etracc("exp6 ~ cp", cp
, vp
);
547 if (eq(**vp
, STRLparen
)) {
549 ccode
= exp0(vp
, ignore
);
550 etraci("exp6 () ccode", ccode
, vp
);
551 if (**vp
== 0 || ***vp
!= ')')
552 stderror(ERR_NAME
| ERR_EXPRESSION
);
554 return (putn(ccode
));
556 if (eq(**vp
, STRLbrace
)) {
558 struct command faket
;
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
;
571 stderror(ERR_NAME
| ERR_MISSING
, '}');
572 if (eq(*(*vp
)++, STRRbrace
))
575 if (ignore
& TEXP_IGNORE
)
576 return (Strsave(STRNULL
));
578 cleanup_push(&faket
, psavejob_cleanup
); /* faket is only a marker */
579 if (pfork(&faket
, -1) == 0) {
585 cleanup_until(&faket
);
586 etraci("exp6 {} status", egetn(varval(STRstatus
)), vp
);
587 return (putn(egetn(varval(STRstatus
)) == 0));
589 if (isa(**vp
, ANYOP
))
590 return (Strsave(STRNULL
));
593 # define FILETESTS "erwxfdzoplstSXLbcugkmKR"
595 # define FILETESTS "erwxfdzoplstSXLbcugkmK"
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
));
606 * Extended file tests
607 * From: John Rowe <rowe@excc.exeter.ac.uk>
610 filetest(Char
*cp
, Char
***vp
, int ignore
)
613 struct cvxstat stb
, *st
= NULL
;
614 # define TCSH_STAT stat64
616 # define TCSH_STAT stat
617 struct stat stb
, *st
= NULL
;
622 struct cvxstat lstb
, *lst
= NULL
;
623 # define TCSH_LSTAT lstat64
625 # define TCSH_LSTAT lstat
626 struct stat lstb
, *lst
= NULL
;
632 unsigned pmask
= 0xffff;
634 Char
*ft
= cp
, *dp
, *ep
, *strdev
, *strino
, *strF
, *str
, valtest
= '\0',
636 char *string
, string0
[22 + MB_LEN_MAX
+ 1]; /* space for 64 bit octal */
641 while(any(FILETESTS
, *++ft
))
644 if (!*ft
&& *(ft
- 1) == 'L')
647 if (any(FILEVALS
, *ft
)) {
650 * Value tests return '-1' on failure as 0 is
651 * a legitimate value for many of them.
652 * 'F' returns ':' for compatibility.
654 errval
= valtest
== 'F' ? STRcolon
: STRminus1
;
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');
661 if (Strcmp(ft
, STRcolon
) == 0 && any("AMCUGP", valtest
)) {
667 if (*ft
|| ft
== cp
+ 1)
668 stderror(ERR_NAME
| ERR_FILEINQ
);
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.
678 if (isa(**vp
, ANYOP
) && TCSH_STAT(short2str(**vp
), &stb
))
679 stderror(ERR_NAME
| ERR_FILENAME
);
682 if (ignore
& TEXP_IGNORE
)
683 return (Strsave(STRNULL
));
684 ep
= globone(dp
, G_APPEND
);
685 cleanup_push(ep
, xfree
);
691 i
= !sh_access(ep
, R_OK
);
695 i
= !sh_access(ep
, W_OK
);
699 i
= !sh_access(ep
, X_OK
);
702 case 'X': /* tcsh extension, name is an executable in the path
703 * or a tcsh builtin command
708 case 't': /* SGI extension, true when file is a tty */
709 i
= isatty(atoi(short2str(ep
)));
715 if (tolower(*ft
) == 'l') {
717 * avoid convex compiler bug.
721 if (TCSH_LSTAT(short2str(ep
), lst
) == -1) {
723 return (Strsave(errval
));
732 * avoid convex compiler bug.
736 if (TCSH_STAT(short2str(ep
), st
) == -1) {
738 return (Strsave(errval
));
746 i
= S_ISREG(st
->st_mode
);
754 i
= S_ISDIR(st
->st_mode
);
762 i
= S_ISFIFO(st
->st_mode
);
763 #else /* !S_ISFIFO */
765 #endif /* S_ISFIFO */
770 i
= S_ISOFL(st
->st_dm_mode
);
787 i
= S_ISLNK(lst
->st_mode
);
795 i
= S_ISSOCK(st
->st_mode
);
796 # else /* !S_ISSOCK */
798 # endif /* S_ISSOCK */
803 i
= S_ISBLK(st
->st_mode
);
811 i
= S_ISCHR(st
->st_mode
);
818 i
= (S_ISUID
& st
->st_mode
) != 0;
822 i
= (S_ISGID
& st
->st_mode
) != 0;
826 i
= (S_ISVTX
& st
->st_mode
) != 0;
830 i
= st
->st_size
== 0;
835 i
= (stb
.st_dmonflags
& IMIGRATED
) == IMIGRATED
;
840 i
= stb
.st_size
!= 0;
848 i
= st
->st_uid
== uid
;
852 * Value operators are a tcsh extension.
856 i
= (tcsh_number_t
) st
->st_dev
;
860 i
= (tcsh_number_t
) st
->st_ino
;
864 strdev
= putn( (int) st
->st_dev
);
865 strino
= putn( (int) st
->st_ino
);
866 strF
= xmalloc((2 + Strlen(strdev
) + Strlen(strino
))
868 (void) Strcat(Strcat(Strcpy(strF
, strdev
), STRcolon
), strino
);
880 filnam
= short2str(ep
);
881 string
= areadlink(filnam
);
882 strF
= string
== NULL
? errval
: str2short(string
);
885 return(Strsave(strF
));
894 i
= (tcsh_number_t
) st
->st_nlink
;
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')
905 return(Strsave(str2short(string
)));
908 if (altout
&& (pw
= xgetpwuid(st
->st_uid
))) {
910 return(Strsave(str2short(pw
->pw_name
)));
912 i
= (tcsh_number_t
) st
->st_uid
;
916 if (altout
&& (gr
= xgetgrgid(st
->st_gid
))) {
918 return(Strsave(str2short(gr
->gr_name
)));
920 i
= (tcsh_number_t
) st
->st_gid
;
924 i
= (tcsh_number_t
) st
->st_size
;
927 case 'A': case 'M': case 'C':
928 footime
= *ft
== 'A' ? st
->st_atime
:
929 *ft
== 'M' ? st
->st_mtime
: st
->st_ctime
;
931 strF
= str2short(ctime(&footime
));
932 if ((str
= Strchr(strF
, '\n')) != NULL
)
935 return(Strsave(strF
));
937 i
= (tcsh_number_t
) footime
;
943 etraci("exp6 -? i", i
, vp
);
952 struct wordent paraml1
;
953 struct wordent
*hp
= ¶ml1
;
955 struct wordent
*wdp
= hp
;
957 setcopy(STRstatus
, STR0
, VAR_READWRITE
);
958 hp
->prev
= hp
->next
= hp
;
961 struct wordent
*new = xcalloc(1, sizeof *wdp
);
967 wdp
->word
= Strsave(*v
++);
970 cleanup_push(¶ml1
, lex_cleanup
);
972 t
= syntax(paraml1
.next
, ¶ml1
, 0);
973 cleanup_push(t
, syntax_cleanup
);
976 execute(t
, -1, NULL
, NULL
, TRUE
);
977 cleanup_until(¶ml1
);
981 isa(Char
*cp
, int what
)
984 return ((what
& RESTOP
) != 0);
988 if (what
& ADDOP
&& (*cp
== '+' || *cp
== '-'))
990 if (what
& MULOP
&& (*cp
== '*' || *cp
== '/' || *cp
== '%'))
992 if (what
& RESTOP
&& (*cp
== '(' || *cp
== ')' || *cp
== '!' ||
993 *cp
== '~' || *cp
== '^' || *cp
== '"'))
996 else if (cp
[2] == 0) {
998 if (cp
[0] == '|' && cp
[1] == '&')
1000 if (cp
[0] == '<' && cp
[1] == '<')
1002 if (cp
[0] == '>' && cp
[1] == '>')
1012 else if (cp
[0] == '!') {
1016 return (NOTEQMATCH
);
1029 static tcsh_number_t
1030 egetn(const Char
*cp
)
1032 if (*cp
&& *cp
!= '-' && !Isdigit(*cp
))
1033 stderror(ERR_NAME
| ERR_EXPRESSION
);
1041 etraci(const char *str
, tcsh_number_t i
, Char
***vp
)
1043 #ifdef HAVE_LONG_LONG
1044 xprintf("%s=%lld\t", str
, i
);
1046 xprintf("%s=%ld\t", str
, i
);
1052 etracc(const char *str
, const Char
*cp
, Char
***vp
)
1054 xprintf("%s=%S\t", str
, cp
);