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