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