14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1982-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                  David Korn <dgk@research.att.com>                   *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin /*
224887Schin  * test expression
234887Schin  * [ expression ]
244887Schin  *
254887Schin  *   David Korn
264887Schin  *   AT&T Labs
274887Schin  *
284887Schin  */
294887Schin 
304887Schin 
314887Schin #include	"defs.h"
324887Schin #include	<ctype.h>
334887Schin #include	<error.h>
344887Schin #include	<ls.h>
354887Schin #include	"io.h"
364887Schin #include	"terminal.h"
374887Schin #include	"test.h"
384887Schin #include	"builtins.h"
394887Schin #include	"FEATURE/externs"
404887Schin #include	"FEATURE/poll"
414887Schin #include	<tmx.h>
424887Schin 
434887Schin #if !_lib_setregid
444887Schin #   undef _lib_setreuid
454887Schin #endif /* _lib_setregid */
464887Schin 
474887Schin #ifdef S_ISSOCK
484887Schin #   if _pipe_socketpair
494887Schin #       if _socketpair_shutdown_mode
504887Schin #           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))
514887Schin #       else
524887Schin #           define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino)
534887Schin #       endif
544887Schin #   else
554887Schin #       define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino)
564887Schin #   endif
574887Schin #   define isasock(f,p) (test_stat(f,p)>=0&&S_ISSOCK((p)->st_mode))
584887Schin #else
594887Schin #   define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode))
604887Schin #   define isasock(f,p) (0)
614887Schin #endif
624887Schin 
634887Schin #define	permission(a,f)		(sh_access(a,f)==0)
644887Schin static time_t	test_time(const char*, const char*);
654887Schin static int	test_stat(const char*, struct stat*);
664887Schin static int	test_mode(const char*);
674887Schin 
684887Schin /* single char string compare */
694887Schin #define c_eq(a,c)	(*a==c && *(a+1)==0)
704887Schin /* two character string compare */
714887Schin #define c2_eq(a,c1,c2)	(*a==c1 && *(a+1)==c2 && *(a+2)==0)
724887Schin 
734887Schin struct test
744887Schin {
754887Schin         Shell_t *sh;
764887Schin         int     ap;
774887Schin         int     ac;
784887Schin         char    **av;
794887Schin };
804887Schin 
814887Schin static char *nxtarg(struct test*,int);
824887Schin static int expr(struct test*,int);
834887Schin static int e3(struct test*);
844887Schin 
854887Schin static int test_strmatch(const char *str, const char *pat)
864887Schin {
874887Schin 	int match[2*(MATCH_MAX+1)],n;
884887Schin 	register int c, m=0;
894887Schin 	register const char *cp=pat;
904887Schin 	while(c = *cp++)
914887Schin 	{
924887Schin 		if(c=='(')
934887Schin 			m++;
944887Schin 		if(c=='\\' && *cp)
954887Schin 			cp++;
964887Schin 	}
974887Schin 	if(m)
984887Schin 		m++;
994887Schin 	else
1004887Schin 		match[0] = 0;
1014887Schin 	if(m >  elementsof(match)/2)
1024887Schin 		m = elementsof(match)/2;
1034887Schin 	n = strgrpmatch(str, pat, match, m, STR_MAXIMAL|STR_LEFT|STR_RIGHT);
1044887Schin 	if(m==0 && n==1)
1054887Schin 		match[1] = strlen(str);
1064887Schin 	if(n)
1074887Schin 		sh_setmatch(str, -1, n, match);
1084887Schin 	return(n);
1094887Schin }
1104887Schin 
1114887Schin int b_test(int argc, char *argv[],void *extra)
1124887Schin {
1134887Schin 	struct test tdata;
1144887Schin 	register char *cp = argv[0];
1154887Schin 	register int not;
116*8462SApril.Chin@Sun.COM 	tdata.sh = ((Shbltin_t*)extra)->shp;
1174887Schin 	tdata.av = argv;
1184887Schin 	tdata.ap = 1;
1194887Schin 	if(c_eq(cp,'['))
1204887Schin 	{
1214887Schin 		cp = argv[--argc];
1224887Schin 		if(!c_eq(cp, ']'))
1234887Schin 			errormsg(SH_DICT,ERROR_exit(2),e_missing,"']'");
1244887Schin 	}
1254887Schin 	if(argc <= 1)
1264887Schin 		return(1);
1274887Schin 	cp = argv[1];
128*8462SApril.Chin@Sun.COM 	if(c_eq(cp,'(') && argc<=6 && c_eq(argv[argc-1],')'))
129*8462SApril.Chin@Sun.COM 	{
130*8462SApril.Chin@Sun.COM 		/* special case  ( binop ) to conform with standard */
131*8462SApril.Chin@Sun.COM 		if(!(argc==4  && (not=sh_lookup(cp=argv[2],shtab_testops))))
132*8462SApril.Chin@Sun.COM 		{
133*8462SApril.Chin@Sun.COM 			cp =  (++argv)[1];
134*8462SApril.Chin@Sun.COM 			argc -= 2;
135*8462SApril.Chin@Sun.COM 		}
136*8462SApril.Chin@Sun.COM 	}
1374887Schin 	not = c_eq(cp,'!');
1384887Schin 	/* posix portion for test */
1394887Schin 	switch(argc)
1404887Schin 	{
1414887Schin 		case 5:
1424887Schin 			if(!not)
1434887Schin 				break;
1444887Schin 			argv++;
1454887Schin 			/* fall through */
1464887Schin 		case 4:
1474887Schin 		{
1484887Schin 			register int op = sh_lookup(cp=argv[2],shtab_testops);
1494887Schin 			if(op&TEST_BINOP)
1504887Schin 				break;
1514887Schin 			if(!op)
1524887Schin 			{
1534887Schin 				if(argc==5)
1544887Schin 					break;
1554887Schin 				if(not && cp[0]=='-' && cp[2]==0)
1564887Schin 					return(test_unop(cp[1],argv[3])!=0);
1574887Schin 				else if(argv[1][0]=='-' && argv[1][2]==0)
1584887Schin 					return(!test_unop(argv[1][1],cp));
1594887Schin 				errormsg(SH_DICT,ERROR_exit(2),e_badop,cp);
1604887Schin 			}
1614887Schin 			return(test_binop(op,argv[1],argv[3])^(argc!=5));
1624887Schin 		}
1634887Schin 		case 3:
1644887Schin 			if(not)
1654887Schin 				return(*argv[2]!=0);
1664887Schin 			if(cp[0] != '-' || cp[2] || cp[1]=='?')
1674887Schin 			{
1684887Schin 				if(cp[0]=='-' && (cp[1]=='-' || cp[1]=='?') &&
1694887Schin 					strcmp(argv[2],"--")==0)
1704887Schin 				{
1714887Schin 					char *av[3];
1724887Schin 					av[0] = argv[0];
1734887Schin 					av[1] = argv[1];
1744887Schin 					av[2] = 0;
1754887Schin 					optget(av,sh_opttest);
1764887Schin 					errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
1774887Schin 					return(2);
1784887Schin 				}
1794887Schin 				break;
1804887Schin 			}
1814887Schin 			return(!test_unop(cp[1],argv[2]));
1824887Schin 		case 2:
1834887Schin 			return(*cp==0);
1844887Schin 	}
1854887Schin 	tdata.ac = argc;
1864887Schin 	return(!expr(&tdata,0));
1874887Schin }
1884887Schin 
1894887Schin /*
1904887Schin  * evaluate a test expression.
1914887Schin  * flag is 0 on outer level
1924887Schin  * flag is 1 when in parenthesis
1934887Schin  * flag is 2 when evaluating -a
1944887Schin  */
1954887Schin static int expr(struct test *tp,register int flag)
1964887Schin {
1974887Schin 	register int r;
1984887Schin 	register char *p;
1994887Schin 	r = e3(tp);
2004887Schin 	while(tp->ap < tp->ac)
2014887Schin 	{
2024887Schin 		p = nxtarg(tp,0);
2034887Schin 		/* check for -o and -a */
2044887Schin 		if(flag && c_eq(p,')'))
2054887Schin 		{
2064887Schin 			tp->ap--;
2074887Schin 			break;
2084887Schin 		}
2094887Schin 		if(*p=='-' && *(p+2)==0)
2104887Schin 		{
2114887Schin 			if(*++p == 'o')
2124887Schin 			{
2134887Schin 				if(flag==2)
2144887Schin 				{
2154887Schin 					tp->ap--;
2164887Schin 					break;
2174887Schin 				}
2184887Schin 				r |= expr(tp,3);
2194887Schin 				continue;
2204887Schin 			}
2214887Schin 			else if(*p == 'a')
2224887Schin 			{
2234887Schin 				r &= expr(tp,2);
2244887Schin 				continue;
2254887Schin 			}
2264887Schin 		}
2274887Schin 		if(flag==0)
2284887Schin 			break;
2294887Schin 		errormsg(SH_DICT,ERROR_exit(2),e_badsyntax);
2304887Schin 	}
2314887Schin 	return(r);
2324887Schin }
2334887Schin 
2344887Schin static char *nxtarg(struct test *tp,int mt)
2354887Schin {
2364887Schin 	if(tp->ap >= tp->ac)
2374887Schin 	{
2384887Schin 		if(mt)
2394887Schin 		{
2404887Schin 			tp->ap++;
2414887Schin 			return(0);
2424887Schin 		}
2434887Schin 		errormsg(SH_DICT,ERROR_exit(2),e_argument);
2444887Schin 	}
2454887Schin 	return(tp->av[tp->ap++]);
2464887Schin }
2474887Schin 
2484887Schin 
2494887Schin static int e3(struct test *tp)
2504887Schin {
2514887Schin 	register char *arg, *cp;
2524887Schin 	register int op;
2534887Schin 	char *binop;
2544887Schin 	arg=nxtarg(tp,0);
2554887Schin 	if(arg && c_eq(arg, '!'))
2564887Schin 		return(!e3(tp));
2574887Schin 	if(c_eq(arg, '('))
2584887Schin 	{
2594887Schin 		op = expr(tp,1);
2604887Schin 		cp = nxtarg(tp,0);
2614887Schin 		if(!cp || !c_eq(cp, ')'))
2624887Schin 			errormsg(SH_DICT,ERROR_exit(2),e_missing,"')'");
2634887Schin 		return(op);
2644887Schin 	}
2654887Schin 	cp = nxtarg(tp,1);
2664887Schin 	if(cp!=0 && (c_eq(cp,'=') || c2_eq(cp,'!','=')))
2674887Schin 		goto skip;
2684887Schin 	if(c2_eq(arg,'-','t'))
2694887Schin 	{
2704887Schin 		if(cp && isdigit(*cp))
2714887Schin 			 return(*(cp+1)?0:tty_check(*cp-'0'));
2724887Schin 		else
2734887Schin 		{
2744887Schin 		/* test -t with no arguments */
2754887Schin 			tp->ap--;
2764887Schin 			return(tty_check(1));
2774887Schin 		}
2784887Schin 	}
2794887Schin 	if(*arg=='-' && arg[2]==0)
2804887Schin 	{
2814887Schin 		op = arg[1];
2824887Schin 		if(!cp)
2834887Schin 		{
2844887Schin 			/* for backward compatibility with new flags */
2854887Schin 			if(op==0 || !strchr(test_opchars+10,op))
2864887Schin 				return(1);
2874887Schin 			errormsg(SH_DICT,ERROR_exit(2),e_argument);
2884887Schin 		}
2894887Schin 		if(strchr(test_opchars,op))
2904887Schin 			return(test_unop(op,cp));
2914887Schin 	}
2924887Schin 	if(!cp)
2934887Schin 	{
2944887Schin 		tp->ap--;
2954887Schin 		return(*arg!=0);
2964887Schin 	}
2974887Schin skip:
2984887Schin 	op = sh_lookup(binop=cp,shtab_testops);
2994887Schin 	if(!(op&TEST_BINOP))
3004887Schin 		cp = nxtarg(tp,0);
3014887Schin 	if(!op)
3024887Schin 		errormsg(SH_DICT,ERROR_exit(2),e_badop,binop);
3034887Schin 	if(op==TEST_AND | op==TEST_OR)
3044887Schin 		tp->ap--;
3054887Schin 	return(test_binop(op,arg,cp));
3064887Schin }
3074887Schin 
3084887Schin int test_unop(register int op,register const char *arg)
3094887Schin {
3104887Schin 	struct stat statb;
3114887Schin 	int f;
3124887Schin 	switch(op)
3134887Schin 	{
3144887Schin 	    case 'r':
3154887Schin 		return(permission(arg, R_OK));
3164887Schin 	    case 'w':
3174887Schin 		return(permission(arg, W_OK));
3184887Schin 	    case 'x':
3194887Schin 		return(permission(arg, X_OK));
3204887Schin 	    case 'V':
3214887Schin #if SHOPT_FS_3D
3224887Schin 	    {
3234887Schin 		register int offset = staktell();
3244887Schin 		if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode))
3254887Schin 			return(0);
3264887Schin 		/* add trailing / */
3274887Schin 		stakputs(arg);
3284887Schin 		stakputc('/');
3294887Schin 		stakputc(0);
3304887Schin 		arg = (const char*)stakptr(offset);
3314887Schin 		stakseek(offset);
3324887Schin 		/* FALL THRU */
3334887Schin 	    }
3344887Schin #else
3354887Schin 		return(0);
3364887Schin #endif /* SHOPT_FS_3D */
3374887Schin 	    case 'd':
3384887Schin 		return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode));
3394887Schin 	    case 'c':
3404887Schin 		return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode));
3414887Schin 	    case 'b':
3424887Schin 		return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode));
3434887Schin 	    case 'f':
3444887Schin 		return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode));
3454887Schin 	    case 'u':
3464887Schin 		return(test_mode(arg)&S_ISUID);
3474887Schin 	    case 'g':
3484887Schin 		return(test_mode(arg)&S_ISGID);
3494887Schin 	    case 'k':
3504887Schin #ifdef S_ISVTX
3514887Schin 		return(test_mode(arg)&S_ISVTX);
3524887Schin #else
3534887Schin 		return(0);
3544887Schin #endif /* S_ISVTX */
3554887Schin #if SHOPT_TEST_L
3564887Schin 	    case 'l':
3574887Schin #endif
3584887Schin 	    case 'L':
3594887Schin 	    case 'h': /* undocumented, and hopefully will disappear */
3604887Schin 		if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0)
3614887Schin 			return(0);
3624887Schin 		return(S_ISLNK(statb.st_mode));
3634887Schin 
3644887Schin 	    case 'C':
3654887Schin #ifdef S_ISCTG
3664887Schin 		return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode));
3674887Schin #else
3684887Schin 		return(0);
3694887Schin #endif	/* S_ISCTG */
3704887Schin 	    case 'H':
3714887Schin #ifdef S_ISCDF
3724887Schin 	    {
3734887Schin 		register int offset = staktell();
3744887Schin 		if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode))
3754887Schin 			return(1);
3764887Schin 		stakputs(arg);
3774887Schin 		stakputc('+');
3784887Schin 		stakputc(0);
3794887Schin 		arg = (const char*)stakptr(offset);
3804887Schin 		stakseek(offset);
3814887Schin 		return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode));
3824887Schin 	    }
3834887Schin #else
3844887Schin 		return(0);
3854887Schin #endif	/* S_ISCDF */
3864887Schin 
3874887Schin 	    case 'S':
3884887Schin 		return(isasock(arg,&statb));
3894887Schin 	    case 'N':
3904887Schin 		return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb));
3914887Schin 	    case 'p':
3924887Schin 		return(isapipe(arg,&statb));
3934887Schin 	    case 'n':
3944887Schin 		return(*arg != 0);
3954887Schin 	    case 'z':
3964887Schin 		return(*arg == 0);
3974887Schin 	    case 's':
3984887Schin 		sfsync(sfstdout);
3994887Schin 	    case 'O':
4004887Schin 	    case 'G':
4014887Schin 		if(*arg==0 || test_stat(arg,&statb)<0)
4024887Schin 			return(0);
4034887Schin 		if(op=='s')
4044887Schin 			return(statb.st_size>0);
4054887Schin 		else if(op=='O')
4064887Schin 			return(statb.st_uid==sh.userid);
4074887Schin 		return(statb.st_gid==sh.groupid);
4084887Schin 	    case 'a':
4094887Schin 	    case 'e':
4104887Schin 		return(permission(arg, F_OK));
4114887Schin 	    case 'o':
4124887Schin 		f=1;
4134887Schin 		if(*arg=='?')
4144887Schin 			return(sh_lookopt(arg+1,&f)>0);
4154887Schin 		op = sh_lookopt(arg,&f);
4164887Schin 		return(op && (f==(sh_isoption(op)!=0)));
4174887Schin 	    case 't':
4184887Schin 		if(isdigit(*arg) && arg[1]==0)
4194887Schin 			 return(tty_check(*arg-'0'));
4204887Schin 		return(0);
4214887Schin 	    default:
4224887Schin 	    {
4234887Schin 		static char a[3] = "-?";
4244887Schin 		a[1]= op;
4254887Schin 		errormsg(SH_DICT,ERROR_exit(2),e_badop,a);
4264887Schin 		/* NOTREACHED  */
4274887Schin 		return(0);
4284887Schin 	    }
4294887Schin 	}
4304887Schin }
4314887Schin 
4324887Schin int test_binop(register int op,const char *left,const char *right)
4334887Schin {
4344887Schin 	register double lnum,rnum;
4354887Schin 	if(op&TEST_ARITH)
4364887Schin 	{
4374887Schin 		while(*left=='0')
4384887Schin 			left++;
4394887Schin 		while(*right=='0')
4404887Schin 			right++;
4414887Schin 		lnum = sh_arith(left);
4424887Schin 		rnum = sh_arith(right);
4434887Schin 	}
4444887Schin 	switch(op)
4454887Schin 	{
4464887Schin 		/* op must be one of the following values */
4474887Schin 		case TEST_AND:
4484887Schin 		case TEST_OR:
4494887Schin 			return(*left!=0);
4504887Schin 		case TEST_PEQ:
4514887Schin 			return(test_strmatch(left, right));
4524887Schin 		case TEST_PNE:
4534887Schin 			return(!test_strmatch(left, right));
4544887Schin 		case TEST_SGT:
4554887Schin 			return(strcoll(left, right)>0);
4564887Schin 		case TEST_SLT:
4574887Schin 			return(strcoll(left, right)<0);
4584887Schin 		case TEST_SEQ:
4594887Schin 			return(strcmp(left, right)==0);
4604887Schin 		case TEST_SNE:
4614887Schin 			return(strcmp(left, right)!=0);
4624887Schin 		case TEST_EF:
4634887Schin 			return(test_inode(left,right));
4644887Schin 		case TEST_NT:
4654887Schin 			return(test_time(left,right)>0);
4664887Schin 		case TEST_OT:
4674887Schin 			return(test_time(left,right)<0);
4684887Schin 		case TEST_EQ:
4694887Schin 			return(lnum==rnum);
4704887Schin 		case TEST_NE:
4714887Schin 			return(lnum!=rnum);
4724887Schin 		case TEST_GT:
4734887Schin 			return(lnum>rnum);
4744887Schin 		case TEST_LT:
4754887Schin 			return(lnum<rnum);
4764887Schin 		case TEST_GE:
4774887Schin 			return(lnum>=rnum);
4784887Schin 		case TEST_LE:
4794887Schin 			return(lnum<=rnum);
4804887Schin 	}
4814887Schin 	/* NOTREACHED */
4824887Schin 	return(0);
4834887Schin }
4844887Schin 
4854887Schin /*
4864887Schin  * returns the modification time of f1 - modification time of f2
4874887Schin  */
4884887Schin 
4894887Schin static time_t test_time(const char *file1,const char *file2)
4904887Schin {
4914887Schin 	Time_t t1, t2;
4924887Schin 	struct stat statb1,statb2;
4934887Schin 	int r=test_stat(file2,&statb2);
4944887Schin 	if(test_stat(file1,&statb1)<0)
4954887Schin 		return(r<0?0:-1);
4964887Schin 	if(r<0)
4974887Schin 		return(1);
4984887Schin 	t1 = tmxgetmtime(&statb1);
4994887Schin 	t2 = tmxgetmtime(&statb2);
5004887Schin 	if (t1 > t2)
5014887Schin 		return(1);
5024887Schin 	if (t1 < t2)
5034887Schin 		return(-1);
5044887Schin 	return(0);
5054887Schin }
5064887Schin 
5074887Schin /*
5084887Schin  * return true if inode of two files are the same
5094887Schin  */
5104887Schin 
5114887Schin int test_inode(const char *file1,const char *file2)
5124887Schin {
5134887Schin 	struct stat stat1,stat2;
5144887Schin 	if(test_stat(file1,&stat1)>=0  && test_stat(file2,&stat2)>=0)
5154887Schin 		if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino)
5164887Schin 			return(1);
5174887Schin 	return(0);
5184887Schin }
5194887Schin 
5204887Schin 
5214887Schin /*
5224887Schin  * This version of access checks against effective uid/gid
5234887Schin  * The static buffer statb is shared with test_mode.
5244887Schin  */
5254887Schin 
5264887Schin int sh_access(register const char *name, register int mode)
5274887Schin {
5284887Schin 	struct stat statb;
5294887Schin 	if(*name==0)
5304887Schin 		return(-1);
5314887Schin 	if(strmatch(name,(char*)e_devfdNN))
5324887Schin 		return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode));
5334887Schin 	/* can't use access function for execute permission with root */
5344887Schin 	if(mode==X_OK && sh.euserid==0)
5354887Schin 		goto skip;
5364887Schin 	if(sh.userid==sh.euserid && sh.groupid==sh.egroupid)
5374887Schin 		return(access(name,mode));
5384887Schin #ifdef _lib_setreuid
5394887Schin 	/* swap the real uid to effective, check access then restore */
5404887Schin 	/* first swap real and effective gid, if different */
5414887Schin 	if(sh.groupid==sh.euserid || setregid(sh.egroupid,sh.groupid)==0)
5424887Schin 	{
5434887Schin 		/* next swap real and effective uid, if needed */
5444887Schin 		if(sh.userid==sh.euserid || setreuid(sh.euserid,sh.userid)==0)
5454887Schin 		{
5464887Schin 			mode = access(name,mode);
5474887Schin 			/* restore ids */
5484887Schin 			if(sh.userid!=sh.euserid)
5494887Schin 				setreuid(sh.userid,sh.euserid);
5504887Schin 			if(sh.groupid!=sh.egroupid)
5514887Schin 				setregid(sh.groupid,sh.egroupid);
5524887Schin 			return(mode);
5534887Schin 		}
5544887Schin 		else if(sh.groupid!=sh.egroupid)
5554887Schin 			setregid(sh.groupid,sh.egroupid);
5564887Schin 	}
5574887Schin #endif /* _lib_setreuid */
5584887Schin skip:
5594887Schin 	if(test_stat(name, &statb) == 0)
5604887Schin 	{
5614887Schin 		if(mode == F_OK)
5624887Schin 			return(mode);
5634887Schin 		else if(sh.euserid == 0)
5644887Schin 		{
5654887Schin 			if(!S_ISREG(statb.st_mode) || mode!=X_OK)
5664887Schin 				return(0);
5674887Schin 		    	/* root needs execute permission for someone */
5684887Schin 			mode = (S_IXUSR|S_IXGRP|S_IXOTH);
5694887Schin 		}
5704887Schin 		else if(sh.euserid == statb.st_uid)
5714887Schin 			mode <<= 6;
5724887Schin 		else if(sh.egroupid == statb.st_gid)
5734887Schin 			mode <<= 3;
5744887Schin #ifdef _lib_getgroups
5754887Schin 		/* on some systems you can be in several groups */
5764887Schin 		else
5774887Schin 		{
5784887Schin 			static int maxgroups;
5794887Schin 			gid_t *groups;
5804887Schin 			register int n;
5814887Schin 			if(maxgroups==0)
5824887Schin 			{
5834887Schin 				/* first time */
5844887Schin 				if((maxgroups=getgroups(0,(gid_t*)0)) <= 0)
5854887Schin 				{
5864887Schin 					/* pre-POSIX system */
5874887Schin 					maxgroups=NGROUPS_MAX;
5884887Schin 				}
5894887Schin 			}
5904887Schin 			groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t));
5914887Schin 			n = getgroups(maxgroups,groups);
5924887Schin 			while(--n >= 0)
5934887Schin 			{
5944887Schin 				if(groups[n] == statb.st_gid)
5954887Schin 				{
5964887Schin 					mode <<= 3;
5974887Schin 					break;
5984887Schin 				}
5994887Schin 			}
6004887Schin 		}
6014887Schin #   endif /* _lib_getgroups */
6024887Schin 		if(statb.st_mode & mode)
6034887Schin 			return(0);
6044887Schin 	}
6054887Schin 	return(-1);
6064887Schin }
6074887Schin 
6084887Schin /*
6094887Schin  * Return the mode bits of file <file>
6104887Schin  * If <file> is null, then the previous stat buffer is used.
6114887Schin  * The mode bits are zero if the file doesn't exist.
6124887Schin  */
6134887Schin 
6144887Schin static int test_mode(register const char *file)
6154887Schin {
6164887Schin 	struct stat statb;
6174887Schin 	if(file && (*file==0 || test_stat(file,&statb)<0))
6184887Schin 		return(0);
6194887Schin 	return(statb.st_mode);
6204887Schin }
6214887Schin 
6224887Schin /*
6234887Schin  * do an fstat() for /dev/fd/n, otherwise stat()
6244887Schin  */
6254887Schin static int test_stat(const char *name,struct stat *buff)
6264887Schin {
6274887Schin 	if(*name==0)
6284887Schin 	{
6294887Schin 		errno = ENOENT;
6304887Schin 		return(-1);
6314887Schin 	}
6324887Schin 	if(strmatch(name,(char*)e_devfdNN))
6334887Schin 		return(fstat((int)strtol(name+8, (char**)0, 10),buff));
6344887Schin 	else
6354887Schin 		return(stat(name,buff));
6364887Schin }
637