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