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