1 /* $Header: /src/pub/tcsh/sh.exp.c,v 3.40 2002/03/08 17:36:46 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("$Id: sh.exp.c,v 3.40 2002/03/08 17:36:46 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
__P((Char
*, int));
61 static int exp1
__P((Char
***, bool));
62 static int exp2
__P((Char
***, bool));
63 static int exp2a
__P((Char
***, bool));
64 static int exp2b
__P((Char
***, bool));
65 static int exp2c
__P((Char
***, bool));
66 static Char
*exp3
__P((Char
***, bool));
67 static Char
*exp3a
__P((Char
***, bool));
68 static Char
*exp4
__P((Char
***, bool));
69 static Char
*exp5
__P((Char
***, bool));
70 static Char
*exp6
__P((Char
***, bool));
71 static void evalav
__P((Char
**));
72 static int isa
__P((Char
*, int));
73 static int egetn
__P((Char
*));
77 static void etracc
__P((char *, Char
*, Char
***));
78 static void etraci
__P((char *, int, Char
***));
83 * shell access function according to POSIX and non POSIX
84 * From Beto Appleton (beto@aixwiz.aix.ibm.com)
87 sh_access(fname
, mode
)
91 #if defined(POSIX) && !defined(USE_ACCESS)
94 char *name
= short2str(fname
);
99 #if !defined(POSIX) || defined(USE_ACCESS)
100 return access(name
, mode
);
105 * -r file True if file exists and is readable.
106 * -w file True if file exists and is writable.
107 * True shall indicate only that the write flag is on.
108 * The file shall not be writable on a read-only file
109 * system even if this test indicates true.
110 * -x file True if file exists and is executable.
111 * True shall indicate only that the execute flag is on.
112 * If file is a directory, true indicates that the file
115 if (mode
!= W_OK
&& mode
!= X_OK
)
116 return access(name
, mode
);
118 if (stat(name
, &statb
) == -1)
121 if (access(name
, mode
) == 0) {
123 if (S_ISDIR(statb
.st_mode
) && mode
== X_OK
)
127 /* root needs permission for someone */
130 mode
= S_IWUSR
| S_IWGRP
| S_IWOTH
;
133 mode
= S_IXUSR
| S_IXGRP
| S_IXOTH
;
142 else if (euid
== statb
.st_uid
)
145 else if (egid
== statb
.st_gid
)
150 # if defined(__386BSD__) || defined(BSD4_4)
152 * These two decided that setgroup() should take an array of int's
153 * and they define _SC_NGROUPS_MAX without having sysconf
155 # undef _SC_NGROUPS_MAX
156 # if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__bsdi__)
163 # endif /* __386BSD__ || BSD4_4 */
164 /* you can be in several groups */
169 * Try these things to find a positive maximum groups value:
170 * 1) sysconf(_SC_NGROUPS_MAX)
172 * 3) getgroups(0, unused)
173 * Then allocate and scan the groups array if one of these worked.
175 # ifdef _SC_NGROUPS_MAX
176 if ((n
= sysconf(_SC_NGROUPS_MAX
)) == -1)
177 # endif /* _SC_NGROUPS_MAX */
180 n
= getgroups(0, (GID_T
*) NULL
);
183 groups
= (GID_T
*) xmalloc((size_t) (n
* sizeof(GID_T
)));
184 n
= getgroups((int) n
, groups
);
186 if (groups
[n
] == statb
.st_gid
) {
192 # endif /* NGROUPS_MAX */
194 if (statb
.st_mode
& mode
)
205 return (exp0(vp
, 0));
213 register int p1
= exp1(vp
, ignore
);
216 etraci("exp0 p1", p1
, vp
);
218 if (**vp
&& eq(**vp
, STRor2
)) {
222 p2
= exp0(vp
, (ignore
& TEXP_IGNORE
) || p1
);
224 etraci("exp0 p2", p2
, vp
);
236 register int p1
= exp2(vp
, ignore
);
239 etraci("exp1 p1", p1
, vp
);
241 if (**vp
&& eq(**vp
, STRand2
)) {
245 p2
= exp1(vp
, (ignore
& TEXP_IGNORE
) || !p1
);
247 etraci("exp1 p2", p2
, vp
);
259 register int p1
= exp2a(vp
, ignore
);
262 etraci("exp3 p1", p1
, vp
);
264 if (**vp
&& eq(**vp
, STRor
)) {
268 p2
= exp2(vp
, ignore
);
270 etraci("exp3 p2", p2
, vp
);
282 register int p1
= exp2b(vp
, ignore
);
285 etraci("exp2a p1", p1
, vp
);
287 if (**vp
&& eq(**vp
, STRcaret
)) {
291 p2
= exp2a(vp
, ignore
);
293 etraci("exp2a p2", p2
, vp
);
305 register int p1
= exp2c(vp
, ignore
);
308 etraci("exp2b p1", p1
, vp
);
310 if (**vp
&& eq(**vp
, STRand
)) {
314 p2
= exp2b(vp
, ignore
);
316 etraci("exp2b p2", p2
, vp
);
328 register Char
*p1
= exp3(vp
, ignore
);
333 etracc("exp2c p1", p1
, vp
);
335 if ((i
= isa(**vp
, EQOP
)) != 0) {
337 if (i
== EQMATCH
|| i
== NOTEQMATCH
)
338 ignore
|= TEXP_NOGLOB
;
339 p2
= exp3(vp
, ignore
);
341 etracc("exp2c p2", p2
, vp
);
343 if (!(ignore
& TEXP_IGNORE
))
376 register Char
*p1
, *p2
;
379 p1
= exp3a(vp
, ignore
);
381 etracc("exp3 p1", p1
, vp
);
383 if ((i
= isa(**vp
, RELOP
)) != 0) {
385 if (**vp
&& eq(**vp
, STRequal
))
387 p2
= exp3(vp
, ignore
);
389 etracc("exp3 p2", p2
, vp
);
391 if (!(ignore
& TEXP_IGNORE
))
395 i
= egetn(p1
) > egetn(p2
);
399 i
= egetn(p1
) >= egetn(p2
);
403 i
= egetn(p1
) < egetn(p2
);
407 i
= egetn(p1
) <= egetn(p2
);
422 register Char
*p1
, *p2
, *op
;
425 p1
= exp4(vp
, ignore
);
427 etracc("exp3a p1", p1
, vp
);
430 if (op
&& any("<>", op
[0]) && op
[0] == op
[1]) {
432 p2
= exp3a(vp
, ignore
);
434 etracc("exp3a p2", p2
, vp
);
437 i
= egetn(p1
) << egetn(p2
);
439 i
= egetn(p1
) >> egetn(p2
);
452 register Char
*p1
, *p2
;
455 p1
= exp5(vp
, ignore
);
457 etracc("exp4 p1", p1
, vp
);
459 if (isa(**vp
, ADDOP
)) {
460 register Char
*op
= *(*vp
)++;
462 p2
= exp4(vp
, ignore
);
464 etracc("exp4 p2", p2
, vp
);
466 if (!(ignore
& TEXP_IGNORE
))
470 i
= egetn(p1
) + egetn(p2
);
474 i
= egetn(p1
) - egetn(p2
);
489 register Char
*p1
, *p2
;
492 p1
= exp6(vp
, ignore
);
494 etracc("exp5 p1", p1
, vp
);
497 if (isa(**vp
, MULOP
)) {
498 register Char
*op
= *(*vp
)++;
499 if ((ignore
& TEXP_NOGLOB
) != 0)
501 * We are just trying to get the right side of
502 * a =~ or !~ operator
506 p2
= exp5(vp
, ignore
);
508 etracc("exp5 p2", p2
, vp
);
510 if (!(ignore
& TEXP_IGNORE
))
514 i
= egetn(p1
) * egetn(p2
);
547 stderror(ERR_NAME
| ERR_EXPRESSION
);
548 if (eq(**vp
, STRbang
)) {
550 cp
= exp6(vp
, ignore
);
552 etracc("exp6 ! cp", cp
, vp
);
558 if (eq(**vp
, STRtilde
)) {
560 cp
= exp6(vp
, ignore
);
562 etracc("exp6 ~ cp", cp
, vp
);
568 if (eq(**vp
, STRLparen
)) {
570 ccode
= exp0(vp
, ignore
);
572 etraci("exp6 () ccode", ccode
, vp
);
574 if (*vp
== 0 || **vp
== 0 || ***vp
!= ')')
575 stderror(ERR_NAME
| ERR_EXPRESSION
);
577 return (putn(ccode
));
579 if (eq(**vp
, STRLbrace
)) {
581 struct command faket
;
584 faket
.t_dtyp
= NODE_COMMAND
;
585 faket
.t_dflg
= F_BACKQ
;
586 faket
.t_dcar
= faket
.t_dcdr
= faket
.t_dspr
= NULL
;
587 faket
.t_dcom
= fakecom
;
588 fakecom
[0] = STRfakecom
;
594 stderror(ERR_NAME
| ERR_MISSING
, '}');
595 if (eq(*(*vp
)++, STRRbrace
))
598 if (ignore
& TEXP_IGNORE
)
599 return (Strsave(STRNULL
));
601 if (pfork(&faket
, -1) == 0) {
609 etraci("exp6 {} status", egetn(varval(STRstatus
)), vp
);
611 return (putn(egetn(varval(STRstatus
)) == 0));
613 if (isa(**vp
, ANYOP
))
614 return (Strsave(STRNULL
));
617 # define FILETESTS "erwxfdzoplstSXLbcugkmKR"
619 # define FILETESTS "erwxfdzoplstSXLbcugkmK"
621 #define FILEVALS "ZAMCDIUGNFPL"
622 if (*cp
== '-' && (any(FILETESTS
, cp
[1]) || any(FILEVALS
, cp
[1])))
623 return(filetest(cp
, vp
, ignore
));
625 etracc("exp6 default", cp
, vp
);
627 return (ignore
& TEXP_NOGLOB
? Strsave(cp
) : globone(cp
, G_APPEND
));
632 * Extended file tests
633 * From: John Rowe <rowe@excc.exeter.ac.uk>
636 filetest(cp
, vp
, ignore
)
641 struct cvxstat stb
, *st
= NULL
;
642 # define TCSH_STAT stat64
644 # define TCSH_STAT stat
645 struct stat stb
, *st
= NULL
;
650 struct cvxstat lstb
, *lst
= NULL
;
651 # define TCSH_LSTAT lstat64
653 # define TCSH_LSTAT lstat
654 struct stat lstb
, *lst
= NULL
;
660 unsigned pmask
= 0xffff;
662 Char
*ft
= cp
, *dp
, *ep
, *strdev
, *strino
, *strF
, *str
, valtest
= '\0',
664 char *string
, string0
[8];
669 while(any(FILETESTS
, *++ft
))
672 if (!*ft
&& *(ft
- 1) == 'L')
675 if (any(FILEVALS
, *ft
)) {
678 * Value tests return '-1' on failure as 0 is
679 * a legitimate value for many of them.
680 * 'F' returns ':' for compatibility.
682 errval
= valtest
== 'F' ? STRcolon
: STRminus1
;
684 if (valtest
== 'P' && *ft
>= '0' && *ft
<= '7') {
685 pmask
= (char) *ft
- '0';
686 while ( *++ft
>= '0' && *ft
<= '7' )
687 pmask
= 8 * pmask
+ ((char) *ft
- '0');
689 if (Strcmp(ft
, STRcolon
) == 0 && any("AMCUGP", valtest
)) {
695 if (*ft
|| ft
== cp
+ 1)
696 stderror(ERR_NAME
| ERR_FILEINQ
);
699 * Detect missing file names by checking for operator in the file name
700 * position. However, if an operator name appears there, we must make
701 * sure that there's no file by that name (e.g., "/") before announcing
702 * an error. Even this check isn't quite right, since it doesn't take
703 * globbing into account.
706 if (isa(**vp
, ANYOP
) && TCSH_STAT(short2str(**vp
), &stb
))
707 stderror(ERR_NAME
| ERR_FILENAME
);
710 if (ignore
& TEXP_IGNORE
)
711 return (Strsave(STRNULL
));
712 ep
= globone(dp
, G_APPEND
);
718 i
= !sh_access(ep
, R_OK
);
722 i
= !sh_access(ep
, W_OK
);
726 i
= !sh_access(ep
, X_OK
);
729 case 'X': /* tcsh extension, name is an executable in the path
730 * or a tcsh builtin command
735 case 't': /* SGI extension, true when file is a tty */
736 i
= isatty(atoi(short2str(ep
)));
742 if (tolower(*ft
) == 'l') {
744 * avoid convex compiler bug.
748 if (TCSH_LSTAT(short2str(ep
), lst
) == -1) {
750 return (Strsave(errval
));
759 * avoid convex compiler bug.
763 if (TCSH_STAT(short2str(ep
), st
) == -1) {
765 return (Strsave(errval
));
773 i
= S_ISREG(st
->st_mode
);
781 i
= S_ISDIR(st
->st_mode
);
789 i
= S_ISFIFO(st
->st_mode
);
790 #else /* !S_ISFIFO */
792 #endif /* S_ISFIFO */
797 i
= S_ISOFL(st
->st_dm_mode
);
814 i
= S_ISLNK(lst
->st_mode
);
822 i
= S_ISSOCK(st
->st_mode
);
823 # else /* !S_ISSOCK */
825 # endif /* S_ISSOCK */
830 i
= S_ISBLK(st
->st_mode
);
838 i
= S_ISCHR(st
->st_mode
);
845 i
= (S_ISUID
& st
->st_mode
) != 0;
849 i
= (S_ISGID
& st
->st_mode
) != 0;
853 i
= (S_ISVTX
& st
->st_mode
) != 0;
857 i
= st
->st_size
== 0;
862 i
= (stb
.st_dmonflags
& IMIGRATED
) == IMIGRATED
;
867 i
= stb
.st_size
!= 0;
875 i
= st
->st_uid
== uid
;
879 * Value operators are a tcsh extension.
883 i
= (int) st
->st_dev
;
887 i
= (int) st
->st_ino
;
891 strdev
= putn( (int) st
->st_dev
);
892 strino
= putn( (int) st
->st_ino
);
893 strF
= (Char
*) xmalloc((size_t) (2 + Strlen(strdev
) +
894 Strlen(strino
)) * sizeof(Char
));
895 (void) Strcat(Strcat(Strcpy(strF
, strdev
), STRcolon
), strino
);
896 xfree((ptr_t
) strdev
);
897 xfree((ptr_t
) strino
);
907 filnam
= short2str(ep
);
909 # define MY_PATH_MAX PATH_MAX
910 #else /* !PATH_MAX */
912 * I can't think of any more sensible alterative; readlink doesn't give
913 * us an errno if the buffer isn't large enough :-(
915 # define MY_PATH_MAX 2048
916 #endif /* PATH_MAX */
917 i
= readlink(filnam
, string
= (char *)
918 xmalloc((size_t) (1 + MY_PATH_MAX
) * sizeof(char)),
920 if (i
>= 0 && i
<= MY_PATH_MAX
)
921 string
[i
] = '\0'; /* readlink does not null terminate */
922 strF
= (i
< 0) ? errval
: str2short(string
);
923 xfree((ptr_t
) string
);
925 return(Strsave(strF
));
934 i
= (int) st
->st_nlink
;
938 string
= string0
+ 1;
939 (void) xsnprintf(string
, sizeof(string0
) - 1, "%o",
940 pmask
& (unsigned int)
941 ((S_IRWXU
|S_IRWXG
|S_IRWXO
|S_ISUID
|S_ISGID
) & st
->st_mode
));
942 if (altout
&& *string
!= '0')
945 return(Strsave(str2short(string
)));
948 if (altout
&& (pw
= getpwuid(st
->st_uid
))) {
950 return(Strsave(str2short(pw
->pw_name
)));
952 i
= (int) st
->st_uid
;
956 if ( altout
&& (gr
= getgrgid(st
->st_gid
))) {
958 return(Strsave(str2short(gr
->gr_name
)));
960 i
= (int) st
->st_gid
;
964 i
= (int) st
->st_size
;
967 case 'A': case 'M': case 'C':
968 footime
= *ft
== 'A' ? st
->st_atime
:
969 *ft
== 'M' ? st
->st_mtime
: st
->st_ctime
;
971 strF
= str2short(ctime(&footime
));
972 if ((str
= Strchr(strF
, '\n')) != NULL
)
975 return(Strsave(strF
));
984 etraci("exp6 -? i", i
, vp
);
995 struct wordent paraml1
;
996 register struct wordent
*hp
= ¶ml1
;
998 register struct wordent
*wdp
= hp
;
1000 set(STRstatus
, Strsave(STR0
), VAR_READWRITE
);
1001 hp
->prev
= hp
->next
= hp
;
1004 register struct wordent
*new =
1005 (struct wordent
*) xcalloc(1, sizeof *wdp
);
1011 wdp
->word
= Strsave(*v
++);
1015 t
= syntax(paraml1
.next
, ¶ml1
, 0);
1018 execute(t
, -1, NULL
, NULL
, TRUE
);
1019 freelex(¶ml1
), freesyn(t
);
1028 return ((what
& RESTOP
) != 0);
1032 if (what
& ADDOP
&& (*cp
== '+' || *cp
== '-'))
1034 if (what
& MULOP
&& (*cp
== '*' || *cp
== '/' || *cp
== '%'))
1036 if (what
& RESTOP
&& (*cp
== '(' || *cp
== ')' || *cp
== '!' ||
1037 *cp
== '~' || *cp
== '^' || *cp
== '"'))
1040 else if (cp
[2] == 0) {
1041 if (what
& RESTOP
) {
1042 if (cp
[0] == '|' && cp
[1] == '&')
1044 if (cp
[0] == '<' && cp
[1] == '<')
1046 if (cp
[0] == '>' && cp
[1] == '>')
1056 else if (cp
[0] == '!') {
1060 return (NOTEQMATCH
);
1077 if (*cp
&& *cp
!= '-' && !Isdigit(*cp
))
1078 stderror(ERR_NAME
| ERR_EXPRESSION
);
1091 xprintf("%s=%d\t", str
, i
);
1101 xprintf("%s=%s\t", str
, cp
);