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