1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
18da2e3ebdSchin *                                                                      *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin  * test expression
23da2e3ebdSchin  * [ expression ]
24da2e3ebdSchin  *
25da2e3ebdSchin  *   David Korn
26da2e3ebdSchin  *   AT&T Labs
27da2e3ebdSchin  *
28da2e3ebdSchin  */
29da2e3ebdSchin 
30da2e3ebdSchin 
31da2e3ebdSchin #include	"defs.h"
32da2e3ebdSchin #include	<error.h>
33da2e3ebdSchin #include	<ls.h>
34*b30d1939SAndy Fiddaman #include	<regex.h>
35da2e3ebdSchin #include	"io.h"
36da2e3ebdSchin #include	"terminal.h"
37da2e3ebdSchin #include	"test.h"
38da2e3ebdSchin #include	"builtins.h"
39da2e3ebdSchin #include	"FEATURE/externs"
40da2e3ebdSchin #include	"FEATURE/poll"
41da2e3ebdSchin #include	<tmx.h>
42da2e3ebdSchin 
43da2e3ebdSchin #if !_lib_setregid
44da2e3ebdSchin #   undef _lib_setreuid
45da2e3ebdSchin #endif /* _lib_setregid */
46da2e3ebdSchin 
47da2e3ebdSchin #ifdef S_ISSOCK
48da2e3ebdSchin #   if _pipe_socketpair
49da2e3ebdSchin #       if _socketpair_shutdown_mode
50da2e3ebdSchin #           define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino&&((p)->st_mode&(S_IRUSR|S_IWUSR))!=(S_IRUSR|S_IWUSR))
51da2e3ebdSchin #       else
52da2e3ebdSchin #           define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino)
53da2e3ebdSchin #       endif
54da2e3ebdSchin #   else
55da2e3ebdSchin #       define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino)
56da2e3ebdSchin #   endif
57da2e3ebdSchin #   define isasock(f,p) (test_stat(f,p)>=0&&S_ISSOCK((p)->st_mode))
58da2e3ebdSchin #else
59da2e3ebdSchin #   define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode))
60da2e3ebdSchin #   define isasock(f,p) (0)
61da2e3ebdSchin #endif
62da2e3ebdSchin 
63da2e3ebdSchin #define	permission(a,f)		(sh_access(a,f)==0)
64da2e3ebdSchin static time_t	test_time(const char*, const char*);
65da2e3ebdSchin static int	test_stat(const char*, struct stat*);
66da2e3ebdSchin static int	test_mode(const char*);
67da2e3ebdSchin 
68da2e3ebdSchin /* single char string compare */
69da2e3ebdSchin #define c_eq(a,c)	(*a==c && *(a+1)==0)
70da2e3ebdSchin /* two character string compare */
71da2e3ebdSchin #define c2_eq(a,c1,c2)	(*a==c1 && *(a+1)==c2 && *(a+2)==0)
72da2e3ebdSchin 
73da2e3ebdSchin struct test
74da2e3ebdSchin {
75da2e3ebdSchin         Shell_t *sh;
76da2e3ebdSchin         int     ap;
77da2e3ebdSchin         int     ac;
78da2e3ebdSchin         char    **av;
79da2e3ebdSchin };
80da2e3ebdSchin 
81da2e3ebdSchin static char *nxtarg(struct test*,int);
82da2e3ebdSchin static int expr(struct test*,int);
83da2e3ebdSchin static int e3(struct test*);
84da2e3ebdSchin 
test_strmatch(Shell_t * shp,const char * str,const char * pat)85*b30d1939SAndy Fiddaman static int test_strmatch(Shell_t *shp,const char *str, const char *pat)
86da2e3ebdSchin {
87*b30d1939SAndy Fiddaman 	regoff_t match[2*(MATCH_MAX+1)],n;
88da2e3ebdSchin 	register int c, m=0;
89da2e3ebdSchin 	register const char *cp=pat;
90da2e3ebdSchin 	while(c = *cp++)
91da2e3ebdSchin 	{
92da2e3ebdSchin 		if(c=='(')
93da2e3ebdSchin 			m++;
94da2e3ebdSchin 		if(c=='\\' && *cp)
95da2e3ebdSchin 			cp++;
96da2e3ebdSchin 	}
97da2e3ebdSchin 	if(m)
98da2e3ebdSchin 		m++;
99da2e3ebdSchin 	else
100da2e3ebdSchin 		match[0] = 0;
101da2e3ebdSchin 	if(m >  elementsof(match)/2)
102da2e3ebdSchin 		m = elementsof(match)/2;
103*b30d1939SAndy Fiddaman 	n = strgrpmatch(str, pat, match, m, STR_GROUP|STR_MAXIMAL|STR_LEFT|STR_RIGHT);
104da2e3ebdSchin 	if(m==0 && n==1)
105da2e3ebdSchin 		match[1] = strlen(str);
106da2e3ebdSchin 	if(n)
107*b30d1939SAndy Fiddaman 		sh_setmatch(shp, str, -1, n, match, 0);
108da2e3ebdSchin 	return(n);
109da2e3ebdSchin }
110da2e3ebdSchin 
b_test(int argc,char * argv[],Shbltin_t * context)111*b30d1939SAndy Fiddaman int b_test(int argc, char *argv[],Shbltin_t *context)
112da2e3ebdSchin {
113da2e3ebdSchin 	struct test tdata;
114da2e3ebdSchin 	register char *cp = argv[0];
115da2e3ebdSchin 	register int not;
116*b30d1939SAndy Fiddaman 	tdata.sh = context->shp;
117da2e3ebdSchin 	tdata.av = argv;
118da2e3ebdSchin 	tdata.ap = 1;
119da2e3ebdSchin 	if(c_eq(cp,'['))
120da2e3ebdSchin 	{
121da2e3ebdSchin 		cp = argv[--argc];
122da2e3ebdSchin 		if(!c_eq(cp, ']'))
123da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(2),e_missing,"']'");
124da2e3ebdSchin 	}
125da2e3ebdSchin 	if(argc <= 1)
126da2e3ebdSchin 		return(1);
127da2e3ebdSchin 	cp = argv[1];
1287c2fbfb3SApril Chin 	if(c_eq(cp,'(') && argc<=6 && c_eq(argv[argc-1],')'))
1297c2fbfb3SApril Chin 	{
1307c2fbfb3SApril Chin 		/* special case  ( binop ) to conform with standard */
1317c2fbfb3SApril Chin 		if(!(argc==4  && (not=sh_lookup(cp=argv[2],shtab_testops))))
1327c2fbfb3SApril Chin 		{
1337c2fbfb3SApril Chin 			cp =  (++argv)[1];
1347c2fbfb3SApril Chin 			argc -= 2;
1357c2fbfb3SApril Chin 		}
1367c2fbfb3SApril Chin 	}
137da2e3ebdSchin 	not = c_eq(cp,'!');
138da2e3ebdSchin 	/* posix portion for test */
139da2e3ebdSchin 	switch(argc)
140da2e3ebdSchin 	{
141da2e3ebdSchin 		case 5:
142da2e3ebdSchin 			if(!not)
143da2e3ebdSchin 				break;
144da2e3ebdSchin 			argv++;
145da2e3ebdSchin 			/* fall through */
146da2e3ebdSchin 		case 4:
147da2e3ebdSchin 		{
148da2e3ebdSchin 			register int op = sh_lookup(cp=argv[2],shtab_testops);
149da2e3ebdSchin 			if(op&TEST_BINOP)
150da2e3ebdSchin 				break;
151da2e3ebdSchin 			if(!op)
152da2e3ebdSchin 			{
153da2e3ebdSchin 				if(argc==5)
154da2e3ebdSchin 					break;
155da2e3ebdSchin 				if(not && cp[0]=='-' && cp[2]==0)
156*b30d1939SAndy Fiddaman 					return(test_unop(tdata.sh,cp[1],argv[3])!=0);
157da2e3ebdSchin 				else if(argv[1][0]=='-' && argv[1][2]==0)
158*b30d1939SAndy Fiddaman 					return(!test_unop(tdata.sh,argv[1][1],cp));
159*b30d1939SAndy Fiddaman 				else if(not && c_eq(argv[2],'!'))
160*b30d1939SAndy Fiddaman 					return(*argv[3]==0);
161da2e3ebdSchin 				errormsg(SH_DICT,ERROR_exit(2),e_badop,cp);
162da2e3ebdSchin 			}
163*b30d1939SAndy Fiddaman 			return(test_binop(tdata.sh,op,argv[1],argv[3])^(argc!=5));
164da2e3ebdSchin 		}
165da2e3ebdSchin 		case 3:
166da2e3ebdSchin 			if(not)
167da2e3ebdSchin 				return(*argv[2]!=0);
168da2e3ebdSchin 			if(cp[0] != '-' || cp[2] || cp[1]=='?')
169da2e3ebdSchin 			{
170da2e3ebdSchin 				if(cp[0]=='-' && (cp[1]=='-' || cp[1]=='?') &&
171da2e3ebdSchin 					strcmp(argv[2],"--")==0)
172da2e3ebdSchin 				{
173da2e3ebdSchin 					char *av[3];
174da2e3ebdSchin 					av[0] = argv[0];
175da2e3ebdSchin 					av[1] = argv[1];
176da2e3ebdSchin 					av[2] = 0;
177da2e3ebdSchin 					optget(av,sh_opttest);
178da2e3ebdSchin 					errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
179da2e3ebdSchin 					return(2);
180da2e3ebdSchin 				}
181da2e3ebdSchin 				break;
182da2e3ebdSchin 			}
183*b30d1939SAndy Fiddaman 			return(!test_unop(tdata.sh,cp[1],argv[2]));
184da2e3ebdSchin 		case 2:
185da2e3ebdSchin 			return(*cp==0);
186da2e3ebdSchin 	}
187da2e3ebdSchin 	tdata.ac = argc;
188da2e3ebdSchin 	return(!expr(&tdata,0));
189da2e3ebdSchin }
190da2e3ebdSchin 
191da2e3ebdSchin /*
192da2e3ebdSchin  * evaluate a test expression.
193da2e3ebdSchin  * flag is 0 on outer level
194da2e3ebdSchin  * flag is 1 when in parenthesis
195da2e3ebdSchin  * flag is 2 when evaluating -a
196da2e3ebdSchin  */
expr(struct test * tp,register int flag)197da2e3ebdSchin static int expr(struct test *tp,register int flag)
198da2e3ebdSchin {
199da2e3ebdSchin 	register int r;
200da2e3ebdSchin 	register char *p;
201da2e3ebdSchin 	r = e3(tp);
202da2e3ebdSchin 	while(tp->ap < tp->ac)
203da2e3ebdSchin 	{
204da2e3ebdSchin 		p = nxtarg(tp,0);
205da2e3ebdSchin 		/* check for -o and -a */
206da2e3ebdSchin 		if(flag && c_eq(p,')'))
207da2e3ebdSchin 		{
208da2e3ebdSchin 			tp->ap--;
209da2e3ebdSchin 			break;
210da2e3ebdSchin 		}
211da2e3ebdSchin 		if(*p=='-' && *(p+2)==0)
212da2e3ebdSchin 		{
213da2e3ebdSchin 			if(*++p == 'o')
214da2e3ebdSchin 			{
215da2e3ebdSchin 				if(flag==2)
216da2e3ebdSchin 				{
217da2e3ebdSchin 					tp->ap--;
218da2e3ebdSchin 					break;
219da2e3ebdSchin 				}
220da2e3ebdSchin 				r |= expr(tp,3);
221da2e3ebdSchin 				continue;
222da2e3ebdSchin 			}
223da2e3ebdSchin 			else if(*p == 'a')
224da2e3ebdSchin 			{
225da2e3ebdSchin 				r &= expr(tp,2);
226da2e3ebdSchin 				continue;
227da2e3ebdSchin 			}
228da2e3ebdSchin 		}
229da2e3ebdSchin 		if(flag==0)
230da2e3ebdSchin 			break;
231da2e3ebdSchin 		errormsg(SH_DICT,ERROR_exit(2),e_badsyntax);
232da2e3ebdSchin 	}
233da2e3ebdSchin 	return(r);
234da2e3ebdSchin }
235da2e3ebdSchin 
nxtarg(struct test * tp,int mt)236da2e3ebdSchin static char *nxtarg(struct test *tp,int mt)
237da2e3ebdSchin {
238da2e3ebdSchin 	if(tp->ap >= tp->ac)
239da2e3ebdSchin 	{
240da2e3ebdSchin 		if(mt)
241da2e3ebdSchin 		{
242da2e3ebdSchin 			tp->ap++;
243da2e3ebdSchin 			return(0);
244da2e3ebdSchin 		}
245da2e3ebdSchin 		errormsg(SH_DICT,ERROR_exit(2),e_argument);
246da2e3ebdSchin 	}
247da2e3ebdSchin 	return(tp->av[tp->ap++]);
248da2e3ebdSchin }
249da2e3ebdSchin 
250da2e3ebdSchin 
e3(struct test * tp)251da2e3ebdSchin static int e3(struct test *tp)
252da2e3ebdSchin {
253da2e3ebdSchin 	register char *arg, *cp;
254da2e3ebdSchin 	register int op;
255da2e3ebdSchin 	char *binop;
256da2e3ebdSchin 	arg=nxtarg(tp,0);
257da2e3ebdSchin 	if(arg && c_eq(arg, '!'))
258da2e3ebdSchin 		return(!e3(tp));
259da2e3ebdSchin 	if(c_eq(arg, '('))
260da2e3ebdSchin 	{
261da2e3ebdSchin 		op = expr(tp,1);
262da2e3ebdSchin 		cp = nxtarg(tp,0);
263da2e3ebdSchin 		if(!cp || !c_eq(cp, ')'))
264da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(2),e_missing,"')'");
265da2e3ebdSchin 		return(op);
266da2e3ebdSchin 	}
267da2e3ebdSchin 	cp = nxtarg(tp,1);
268da2e3ebdSchin 	if(cp!=0 && (c_eq(cp,'=') || c2_eq(cp,'!','=')))
269da2e3ebdSchin 		goto skip;
270da2e3ebdSchin 	if(c2_eq(arg,'-','t'))
271da2e3ebdSchin 	{
27234f9b3eeSRoland Mainz 		if(cp)
27334f9b3eeSRoland Mainz 		{
27434f9b3eeSRoland Mainz 			op = strtol(cp,&binop, 10);
27534f9b3eeSRoland Mainz 			return(*binop?0:tty_check(op));
27634f9b3eeSRoland Mainz 		}
277da2e3ebdSchin 		else
278da2e3ebdSchin 		{
279da2e3ebdSchin 		/* test -t with no arguments */
280da2e3ebdSchin 			tp->ap--;
281da2e3ebdSchin 			return(tty_check(1));
282da2e3ebdSchin 		}
283da2e3ebdSchin 	}
284da2e3ebdSchin 	if(*arg=='-' && arg[2]==0)
285da2e3ebdSchin 	{
286da2e3ebdSchin 		op = arg[1];
287da2e3ebdSchin 		if(!cp)
288da2e3ebdSchin 		{
289da2e3ebdSchin 			/* for backward compatibility with new flags */
290da2e3ebdSchin 			if(op==0 || !strchr(test_opchars+10,op))
291da2e3ebdSchin 				return(1);
292da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(2),e_argument);
293da2e3ebdSchin 		}
294da2e3ebdSchin 		if(strchr(test_opchars,op))
295*b30d1939SAndy Fiddaman 			return(test_unop(tp->sh,op,cp));
296da2e3ebdSchin 	}
297da2e3ebdSchin 	if(!cp)
298da2e3ebdSchin 	{
299da2e3ebdSchin 		tp->ap--;
300da2e3ebdSchin 		return(*arg!=0);
301da2e3ebdSchin 	}
302da2e3ebdSchin skip:
303da2e3ebdSchin 	op = sh_lookup(binop=cp,shtab_testops);
304da2e3ebdSchin 	if(!(op&TEST_BINOP))
305da2e3ebdSchin 		cp = nxtarg(tp,0);
306da2e3ebdSchin 	if(!op)
307da2e3ebdSchin 		errormsg(SH_DICT,ERROR_exit(2),e_badop,binop);
30834f9b3eeSRoland Mainz 	if(op==TEST_AND || op==TEST_OR)
309da2e3ebdSchin 		tp->ap--;
310*b30d1939SAndy Fiddaman 	return(test_binop(tp->sh,op,arg,cp));
311da2e3ebdSchin }
312da2e3ebdSchin 
test_unop(Shell_t * shp,register int op,register const char * arg)313*b30d1939SAndy Fiddaman int test_unop(Shell_t *shp,register int op,register const char *arg)
314da2e3ebdSchin {
315da2e3ebdSchin 	struct stat statb;
316da2e3ebdSchin 	int f;
317da2e3ebdSchin 	switch(op)
318da2e3ebdSchin 	{
319da2e3ebdSchin 	    case 'r':
320da2e3ebdSchin 		return(permission(arg, R_OK));
321da2e3ebdSchin 	    case 'w':
322da2e3ebdSchin 		return(permission(arg, W_OK));
323da2e3ebdSchin 	    case 'x':
324da2e3ebdSchin 		return(permission(arg, X_OK));
325da2e3ebdSchin 	    case 'V':
326da2e3ebdSchin #if SHOPT_FS_3D
327da2e3ebdSchin 	    {
328da2e3ebdSchin 		register int offset = staktell();
329da2e3ebdSchin 		if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode))
330da2e3ebdSchin 			return(0);
331da2e3ebdSchin 		/* add trailing / */
332da2e3ebdSchin 		stakputs(arg);
333da2e3ebdSchin 		stakputc('/');
334da2e3ebdSchin 		stakputc(0);
335da2e3ebdSchin 		arg = (const char*)stakptr(offset);
336da2e3ebdSchin 		stakseek(offset);
337da2e3ebdSchin 	    }
338da2e3ebdSchin #else
339da2e3ebdSchin 		return(0);
340da2e3ebdSchin #endif /* SHOPT_FS_3D */
3415ae8bd53SToomas Soome 		/* FALLTHROUGH */
342da2e3ebdSchin 	    case 'd':
343da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode));
344da2e3ebdSchin 	    case 'c':
345da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode));
346da2e3ebdSchin 	    case 'b':
347da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode));
348da2e3ebdSchin 	    case 'f':
349da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode));
350da2e3ebdSchin 	    case 'u':
351da2e3ebdSchin 		return(test_mode(arg)&S_ISUID);
352da2e3ebdSchin 	    case 'g':
353da2e3ebdSchin 		return(test_mode(arg)&S_ISGID);
354da2e3ebdSchin 	    case 'k':
355da2e3ebdSchin #ifdef S_ISVTX
356da2e3ebdSchin 		return(test_mode(arg)&S_ISVTX);
357da2e3ebdSchin #else
358da2e3ebdSchin 		return(0);
359da2e3ebdSchin #endif /* S_ISVTX */
360da2e3ebdSchin #if SHOPT_TEST_L
361da2e3ebdSchin 	    case 'l':
362da2e3ebdSchin #endif
363da2e3ebdSchin 	    case 'L':
364da2e3ebdSchin 	    case 'h': /* undocumented, and hopefully will disappear */
365da2e3ebdSchin 		if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0)
366da2e3ebdSchin 			return(0);
367da2e3ebdSchin 		return(S_ISLNK(statb.st_mode));
368da2e3ebdSchin 
369da2e3ebdSchin 	    case 'C':
370da2e3ebdSchin #ifdef S_ISCTG
371da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode));
372da2e3ebdSchin #else
373da2e3ebdSchin 		return(0);
374da2e3ebdSchin #endif	/* S_ISCTG */
375da2e3ebdSchin 	    case 'H':
376da2e3ebdSchin #ifdef S_ISCDF
377da2e3ebdSchin 	    {
378da2e3ebdSchin 		register int offset = staktell();
379da2e3ebdSchin 		if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode))
380da2e3ebdSchin 			return(1);
381da2e3ebdSchin 		stakputs(arg);
382da2e3ebdSchin 		stakputc('+');
383da2e3ebdSchin 		stakputc(0);
384da2e3ebdSchin 		arg = (const char*)stakptr(offset);
385da2e3ebdSchin 		stakseek(offset);
386da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode));
387da2e3ebdSchin 	    }
388da2e3ebdSchin #else
389da2e3ebdSchin 		return(0);
390da2e3ebdSchin #endif	/* S_ISCDF */
391da2e3ebdSchin 
392da2e3ebdSchin 	    case 'S':
393da2e3ebdSchin 		return(isasock(arg,&statb));
394da2e3ebdSchin 	    case 'N':
395da2e3ebdSchin 		return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb));
396da2e3ebdSchin 	    case 'p':
397da2e3ebdSchin 		return(isapipe(arg,&statb));
398da2e3ebdSchin 	    case 'n':
399da2e3ebdSchin 		return(*arg != 0);
400da2e3ebdSchin 	    case 'z':
401da2e3ebdSchin 		return(*arg == 0);
402da2e3ebdSchin 	    case 's':
403da2e3ebdSchin 		sfsync(sfstdout);
4045ae8bd53SToomas Soome 		/* FALLTHROUGH */
405da2e3ebdSchin 	    case 'O':
406da2e3ebdSchin 	    case 'G':
407da2e3ebdSchin 		if(*arg==0 || test_stat(arg,&statb)<0)
408da2e3ebdSchin 			return(0);
409da2e3ebdSchin 		if(op=='s')
410da2e3ebdSchin 			return(statb.st_size>0);
411da2e3ebdSchin 		else if(op=='O')
412*b30d1939SAndy Fiddaman 			return(statb.st_uid==shp->gd->userid);
413*b30d1939SAndy Fiddaman 		return(statb.st_gid==shp->gd->groupid);
414da2e3ebdSchin 	    case 'a':
415da2e3ebdSchin 	    case 'e':
416*b30d1939SAndy Fiddaman 		if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK))
417*b30d1939SAndy Fiddaman 			return(1);
418da2e3ebdSchin 		return(permission(arg, F_OK));
419da2e3ebdSchin 	    case 'o':
420da2e3ebdSchin 		f=1;
421da2e3ebdSchin 		if(*arg=='?')
422da2e3ebdSchin 			return(sh_lookopt(arg+1,&f)>0);
423da2e3ebdSchin 		op = sh_lookopt(arg,&f);
424da2e3ebdSchin 		return(op && (f==(sh_isoption(op)!=0)));
425da2e3ebdSchin 	    case 't':
42634f9b3eeSRoland Mainz 	    {
42734f9b3eeSRoland Mainz 		char *last;
42834f9b3eeSRoland Mainz 		op = strtol(arg,&last, 10);
42934f9b3eeSRoland Mainz 		return(*last?0:tty_check(op));
43034f9b3eeSRoland Mainz 	    }
43134f9b3eeSRoland Mainz 	    case 'v':
43234f9b3eeSRoland Mainz 	    case 'R':
43334f9b3eeSRoland Mainz 	    {
43434f9b3eeSRoland Mainz 		Namval_t *np;
43534f9b3eeSRoland Mainz 		Namarr_t *ap;
43634f9b3eeSRoland Mainz 		int isref;
437*b30d1939SAndy Fiddaman 		if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF)))
43834f9b3eeSRoland Mainz 			return(0);
43934f9b3eeSRoland Mainz 		isref = nv_isref(np);
44034f9b3eeSRoland Mainz 		if(op=='R')
44134f9b3eeSRoland Mainz 			return(isref);
44234f9b3eeSRoland Mainz 		if(isref)
44334f9b3eeSRoland Mainz 		{
44434f9b3eeSRoland Mainz 			if(np->nvalue.cp)
44534f9b3eeSRoland Mainz 				np = nv_refnode(np);
44634f9b3eeSRoland Mainz 			else
44734f9b3eeSRoland Mainz 				return(0);
44834f9b3eeSRoland Mainz 
44934f9b3eeSRoland Mainz 		}
45034f9b3eeSRoland Mainz 		if(ap = nv_arrayptr(np))
45134f9b3eeSRoland Mainz 			return(nv_arrayisset(np,ap));
45234f9b3eeSRoland Mainz 		return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER));
45334f9b3eeSRoland Mainz 	    }
454da2e3ebdSchin 	    default:
455da2e3ebdSchin 	    {
456da2e3ebdSchin 		static char a[3] = "-?";
457da2e3ebdSchin 		a[1]= op;
458da2e3ebdSchin 		errormsg(SH_DICT,ERROR_exit(2),e_badop,a);
459da2e3ebdSchin 		/* NOTREACHED  */
460da2e3ebdSchin 		return(0);
461da2e3ebdSchin 	    }
462da2e3ebdSchin 	}
463da2e3ebdSchin }
464da2e3ebdSchin 
test_binop(Shell_t * shp,register int op,const char * left,const char * right)465*b30d1939SAndy Fiddaman int test_binop(Shell_t *shp,register int op,const char *left,const char *right)
466da2e3ebdSchin {
467da2e3ebdSchin 	register double lnum,rnum;
468da2e3ebdSchin 	if(op&TEST_ARITH)
469da2e3ebdSchin 	{
470da2e3ebdSchin 		while(*left=='0')
471da2e3ebdSchin 			left++;
472da2e3ebdSchin 		while(*right=='0')
473da2e3ebdSchin 			right++;
474*b30d1939SAndy Fiddaman 		lnum = sh_arith(shp,left);
475*b30d1939SAndy Fiddaman 		rnum = sh_arith(shp,right);
476da2e3ebdSchin 	}
477da2e3ebdSchin 	switch(op)
478da2e3ebdSchin 	{
479da2e3ebdSchin 		/* op must be one of the following values */
480da2e3ebdSchin 		case TEST_AND:
481da2e3ebdSchin 		case TEST_OR:
482da2e3ebdSchin 			return(*left!=0);
483da2e3ebdSchin 		case TEST_PEQ:
484*b30d1939SAndy Fiddaman 			return(test_strmatch(shp, left, right));
485da2e3ebdSchin 		case TEST_PNE:
486*b30d1939SAndy Fiddaman 			return(!test_strmatch(shp, left, right));
487da2e3ebdSchin 		case TEST_SGT:
488da2e3ebdSchin 			return(strcoll(left, right)>0);
489da2e3ebdSchin 		case TEST_SLT:
490da2e3ebdSchin 			return(strcoll(left, right)<0);
491da2e3ebdSchin 		case TEST_SEQ:
492da2e3ebdSchin 			return(strcmp(left, right)==0);
493da2e3ebdSchin 		case TEST_SNE:
494da2e3ebdSchin 			return(strcmp(left, right)!=0);
495da2e3ebdSchin 		case TEST_EF:
496da2e3ebdSchin 			return(test_inode(left,right));
497da2e3ebdSchin 		case TEST_NT:
498da2e3ebdSchin 			return(test_time(left,right)>0);
499da2e3ebdSchin 		case TEST_OT:
500da2e3ebdSchin 			return(test_time(left,right)<0);
501da2e3ebdSchin 		case TEST_EQ:
502da2e3ebdSchin 			return(lnum==rnum);
503da2e3ebdSchin 		case TEST_NE:
504da2e3ebdSchin 			return(lnum!=rnum);
505da2e3ebdSchin 		case TEST_GT:
506da2e3ebdSchin 			return(lnum>rnum);
507da2e3ebdSchin 		case TEST_LT:
508da2e3ebdSchin 			return(lnum<rnum);
509da2e3ebdSchin 		case TEST_GE:
510da2e3ebdSchin 			return(lnum>=rnum);
511da2e3ebdSchin 		case TEST_LE:
512da2e3ebdSchin 			return(lnum<=rnum);
513da2e3ebdSchin 	}
514da2e3ebdSchin 	/* NOTREACHED */
515da2e3ebdSchin 	return(0);
516da2e3ebdSchin }
517da2e3ebdSchin 
518da2e3ebdSchin /*
519da2e3ebdSchin  * returns the modification time of f1 - modification time of f2
520da2e3ebdSchin  */
521da2e3ebdSchin 
test_time(const char * file1,const char * file2)522da2e3ebdSchin static time_t test_time(const char *file1,const char *file2)
523da2e3ebdSchin {
524da2e3ebdSchin 	Time_t t1, t2;
525da2e3ebdSchin 	struct stat statb1,statb2;
526da2e3ebdSchin 	int r=test_stat(file2,&statb2);
527da2e3ebdSchin 	if(test_stat(file1,&statb1)<0)
528da2e3ebdSchin 		return(r<0?0:-1);
529da2e3ebdSchin 	if(r<0)
530da2e3ebdSchin 		return(1);
531da2e3ebdSchin 	t1 = tmxgetmtime(&statb1);
532da2e3ebdSchin 	t2 = tmxgetmtime(&statb2);
533da2e3ebdSchin 	if (t1 > t2)
534da2e3ebdSchin 		return(1);
535da2e3ebdSchin 	if (t1 < t2)
536da2e3ebdSchin 		return(-1);
537da2e3ebdSchin 	return(0);
538da2e3ebdSchin }
539da2e3ebdSchin 
540da2e3ebdSchin /*
541da2e3ebdSchin  * return true if inode of two files are the same
542da2e3ebdSchin  */
543da2e3ebdSchin 
test_inode(const char * file1,const char * file2)544da2e3ebdSchin int test_inode(const char *file1,const char *file2)
545da2e3ebdSchin {
546da2e3ebdSchin 	struct stat stat1,stat2;
547da2e3ebdSchin 	if(test_stat(file1,&stat1)>=0  && test_stat(file2,&stat2)>=0)
548da2e3ebdSchin 		if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino)
549da2e3ebdSchin 			return(1);
550da2e3ebdSchin 	return(0);
551da2e3ebdSchin }
552da2e3ebdSchin 
553da2e3ebdSchin 
554da2e3ebdSchin /*
555da2e3ebdSchin  * This version of access checks against effective uid/gid
556da2e3ebdSchin  * The static buffer statb is shared with test_mode.
557da2e3ebdSchin  */
558da2e3ebdSchin 
sh_access(register const char * name,register int mode)559da2e3ebdSchin int sh_access(register const char *name, register int mode)
560da2e3ebdSchin {
561*b30d1939SAndy Fiddaman 	Shell_t	*shp = sh_getinterp();
562da2e3ebdSchin 	struct stat statb;
563da2e3ebdSchin 	if(*name==0)
564da2e3ebdSchin 		return(-1);
565*b30d1939SAndy Fiddaman 	if(sh_isdevfd(name))
566da2e3ebdSchin 		return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode));
567da2e3ebdSchin 	/* can't use access function for execute permission with root */
568*b30d1939SAndy Fiddaman 	if(mode==X_OK && shp->gd->euserid==0)
569da2e3ebdSchin 		goto skip;
570*b30d1939SAndy Fiddaman 	if(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid)
571da2e3ebdSchin 		return(access(name,mode));
572da2e3ebdSchin #ifdef _lib_setreuid
573da2e3ebdSchin 	/* swap the real uid to effective, check access then restore */
574da2e3ebdSchin 	/* first swap real and effective gid, if different */
575*b30d1939SAndy Fiddaman 	if(shp->gd->groupid==shp->gd->euserid || setregid(shp->gd->egroupid,shp->gd->groupid)==0)
576da2e3ebdSchin 	{
577da2e3ebdSchin 		/* next swap real and effective uid, if needed */
578*b30d1939SAndy Fiddaman 		if(shp->gd->userid==shp->gd->euserid || setreuid(shp->gd->euserid,shp->gd->userid)==0)
579da2e3ebdSchin 		{
580da2e3ebdSchin 			mode = access(name,mode);
581da2e3ebdSchin 			/* restore ids */
582*b30d1939SAndy Fiddaman 			if(shp->gd->userid!=shp->gd->euserid)
583*b30d1939SAndy Fiddaman 				setreuid(shp->gd->userid,shp->gd->euserid);
584*b30d1939SAndy Fiddaman 			if(shp->gd->groupid!=shp->gd->egroupid)
585*b30d1939SAndy Fiddaman 				setregid(shp->gd->groupid,shp->gd->egroupid);
586da2e3ebdSchin 			return(mode);
587da2e3ebdSchin 		}
588*b30d1939SAndy Fiddaman 		else if(shp->gd->groupid!=shp->gd->egroupid)
589*b30d1939SAndy Fiddaman 			setregid(shp->gd->groupid,shp->gd->egroupid);
590da2e3ebdSchin 	}
591da2e3ebdSchin #endif /* _lib_setreuid */
592da2e3ebdSchin skip:
593da2e3ebdSchin 	if(test_stat(name, &statb) == 0)
594da2e3ebdSchin 	{
595da2e3ebdSchin 		if(mode == F_OK)
596da2e3ebdSchin 			return(mode);
597*b30d1939SAndy Fiddaman 		else if(shp->gd->euserid == 0)
598da2e3ebdSchin 		{
599da2e3ebdSchin 			if(!S_ISREG(statb.st_mode) || mode!=X_OK)
600da2e3ebdSchin 				return(0);
601da2e3ebdSchin 		    	/* root needs execute permission for someone */
602da2e3ebdSchin 			mode = (S_IXUSR|S_IXGRP|S_IXOTH);
603da2e3ebdSchin 		}
604*b30d1939SAndy Fiddaman 		else if(shp->gd->euserid == statb.st_uid)
605da2e3ebdSchin 			mode <<= 6;
606*b30d1939SAndy Fiddaman 		else if(shp->gd->egroupid == statb.st_gid)
607da2e3ebdSchin 			mode <<= 3;
608da2e3ebdSchin #ifdef _lib_getgroups
609da2e3ebdSchin 		/* on some systems you can be in several groups */
610da2e3ebdSchin 		else
611da2e3ebdSchin 		{
612da2e3ebdSchin 			static int maxgroups;
613da2e3ebdSchin 			gid_t *groups;
614da2e3ebdSchin 			register int n;
615da2e3ebdSchin 			if(maxgroups==0)
616da2e3ebdSchin 			{
617da2e3ebdSchin 				/* first time */
618da2e3ebdSchin 				if((maxgroups=getgroups(0,(gid_t*)0)) <= 0)
619da2e3ebdSchin 				{
620da2e3ebdSchin 					/* pre-POSIX system */
621da2e3ebdSchin 					maxgroups=NGROUPS_MAX;
622da2e3ebdSchin 				}
623da2e3ebdSchin 			}
624da2e3ebdSchin 			groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t));
625da2e3ebdSchin 			n = getgroups(maxgroups,groups);
626da2e3ebdSchin 			while(--n >= 0)
627da2e3ebdSchin 			{
628da2e3ebdSchin 				if(groups[n] == statb.st_gid)
629da2e3ebdSchin 				{
630da2e3ebdSchin 					mode <<= 3;
631da2e3ebdSchin 					break;
632da2e3ebdSchin 				}
633da2e3ebdSchin 			}
634da2e3ebdSchin 		}
635da2e3ebdSchin #   endif /* _lib_getgroups */
636da2e3ebdSchin 		if(statb.st_mode & mode)
637da2e3ebdSchin 			return(0);
638da2e3ebdSchin 	}
639da2e3ebdSchin 	return(-1);
640da2e3ebdSchin }
641da2e3ebdSchin 
642da2e3ebdSchin /*
643da2e3ebdSchin  * Return the mode bits of file <file>
644da2e3ebdSchin  * If <file> is null, then the previous stat buffer is used.
645da2e3ebdSchin  * The mode bits are zero if the file doesn't exist.
646da2e3ebdSchin  */
647da2e3ebdSchin 
test_mode(register const char * file)648da2e3ebdSchin static int test_mode(register const char *file)
649da2e3ebdSchin {
650da2e3ebdSchin 	struct stat statb;
651*b30d1939SAndy Fiddaman 	statb.st_mode = 0;
652da2e3ebdSchin 	if(file && (*file==0 || test_stat(file,&statb)<0))
653da2e3ebdSchin 		return(0);
654da2e3ebdSchin 	return(statb.st_mode);
655da2e3ebdSchin }
656da2e3ebdSchin 
657da2e3ebdSchin /*
658da2e3ebdSchin  * do an fstat() for /dev/fd/n, otherwise stat()
659da2e3ebdSchin  */
test_stat(const char * name,struct stat * buff)660da2e3ebdSchin static int test_stat(const char *name,struct stat *buff)
661da2e3ebdSchin {
662da2e3ebdSchin 	if(*name==0)
663da2e3ebdSchin 	{
664da2e3ebdSchin 		errno = ENOENT;
665da2e3ebdSchin 		return(-1);
666da2e3ebdSchin 	}
667*b30d1939SAndy Fiddaman 	if(sh_isdevfd(name))
668da2e3ebdSchin 		return(fstat((int)strtol(name+8, (char**)0, 10),buff));
669da2e3ebdSchin 	else
670da2e3ebdSchin 		return(stat(name,buff));
671da2e3ebdSchin }
672