1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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 <error.h> 33 #include <ls.h> 34 #include <regex.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(Shell_t *shp,const char *str, const char *pat) 86 { 87 regoff_t 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_GROUP|STR_MAXIMAL|STR_LEFT|STR_RIGHT); 104 if(m==0 && n==1) 105 match[1] = strlen(str); 106 if(n) 107 sh_setmatch(shp, str, -1, n, match, 0); 108 return(n); 109 } 110 111 int b_test(int argc, char *argv[],Shbltin_t *context) 112 { 113 struct test tdata; 114 register char *cp = argv[0]; 115 register int not; 116 tdata.sh = context->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(tdata.sh,cp[1],argv[3])!=0); 157 else if(argv[1][0]=='-' && argv[1][2]==0) 158 return(!test_unop(tdata.sh,argv[1][1],cp)); 159 else if(not && c_eq(argv[2],'!')) 160 return(*argv[3]==0); 161 errormsg(SH_DICT,ERROR_exit(2),e_badop,cp); 162 } 163 return(test_binop(tdata.sh,op,argv[1],argv[3])^(argc!=5)); 164 } 165 case 3: 166 if(not) 167 return(*argv[2]!=0); 168 if(cp[0] != '-' || cp[2] || cp[1]=='?') 169 { 170 if(cp[0]=='-' && (cp[1]=='-' || cp[1]=='?') && 171 strcmp(argv[2],"--")==0) 172 { 173 char *av[3]; 174 av[0] = argv[0]; 175 av[1] = argv[1]; 176 av[2] = 0; 177 optget(av,sh_opttest); 178 errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); 179 return(2); 180 } 181 break; 182 } 183 return(!test_unop(tdata.sh,cp[1],argv[2])); 184 case 2: 185 return(*cp==0); 186 } 187 tdata.ac = argc; 188 return(!expr(&tdata,0)); 189 } 190 191 /* 192 * evaluate a test expression. 193 * flag is 0 on outer level 194 * flag is 1 when in parenthesis 195 * flag is 2 when evaluating -a 196 */ 197 static int expr(struct test *tp,register int flag) 198 { 199 register int r; 200 register char *p; 201 r = e3(tp); 202 while(tp->ap < tp->ac) 203 { 204 p = nxtarg(tp,0); 205 /* check for -o and -a */ 206 if(flag && c_eq(p,')')) 207 { 208 tp->ap--; 209 break; 210 } 211 if(*p=='-' && *(p+2)==0) 212 { 213 if(*++p == 'o') 214 { 215 if(flag==2) 216 { 217 tp->ap--; 218 break; 219 } 220 r |= expr(tp,3); 221 continue; 222 } 223 else if(*p == 'a') 224 { 225 r &= expr(tp,2); 226 continue; 227 } 228 } 229 if(flag==0) 230 break; 231 errormsg(SH_DICT,ERROR_exit(2),e_badsyntax); 232 } 233 return(r); 234 } 235 236 static char *nxtarg(struct test *tp,int mt) 237 { 238 if(tp->ap >= tp->ac) 239 { 240 if(mt) 241 { 242 tp->ap++; 243 return(0); 244 } 245 errormsg(SH_DICT,ERROR_exit(2),e_argument); 246 } 247 return(tp->av[tp->ap++]); 248 } 249 250 251 static int e3(struct test *tp) 252 { 253 register char *arg, *cp; 254 register int op; 255 char *binop; 256 arg=nxtarg(tp,0); 257 if(arg && c_eq(arg, '!')) 258 return(!e3(tp)); 259 if(c_eq(arg, '(')) 260 { 261 op = expr(tp,1); 262 cp = nxtarg(tp,0); 263 if(!cp || !c_eq(cp, ')')) 264 errormsg(SH_DICT,ERROR_exit(2),e_missing,"')'"); 265 return(op); 266 } 267 cp = nxtarg(tp,1); 268 if(cp!=0 && (c_eq(cp,'=') || c2_eq(cp,'!','='))) 269 goto skip; 270 if(c2_eq(arg,'-','t')) 271 { 272 if(cp) 273 { 274 op = strtol(cp,&binop, 10); 275 return(*binop?0:tty_check(op)); 276 } 277 else 278 { 279 /* test -t with no arguments */ 280 tp->ap--; 281 return(tty_check(1)); 282 } 283 } 284 if(*arg=='-' && arg[2]==0) 285 { 286 op = arg[1]; 287 if(!cp) 288 { 289 /* for backward compatibility with new flags */ 290 if(op==0 || !strchr(test_opchars+10,op)) 291 return(1); 292 errormsg(SH_DICT,ERROR_exit(2),e_argument); 293 } 294 if(strchr(test_opchars,op)) 295 return(test_unop(tp->sh,op,cp)); 296 } 297 if(!cp) 298 { 299 tp->ap--; 300 return(*arg!=0); 301 } 302 skip: 303 op = sh_lookup(binop=cp,shtab_testops); 304 if(!(op&TEST_BINOP)) 305 cp = nxtarg(tp,0); 306 if(!op) 307 errormsg(SH_DICT,ERROR_exit(2),e_badop,binop); 308 if(op==TEST_AND || op==TEST_OR) 309 tp->ap--; 310 return(test_binop(tp->sh,op,arg,cp)); 311 } 312 313 int test_unop(Shell_t *shp,register int op,register const char *arg) 314 { 315 struct stat statb; 316 int f; 317 switch(op) 318 { 319 case 'r': 320 return(permission(arg, R_OK)); 321 case 'w': 322 return(permission(arg, W_OK)); 323 case 'x': 324 return(permission(arg, X_OK)); 325 case 'V': 326 #if SHOPT_FS_3D 327 { 328 register int offset = staktell(); 329 if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode)) 330 return(0); 331 /* add trailing / */ 332 stakputs(arg); 333 stakputc('/'); 334 stakputc(0); 335 arg = (const char*)stakptr(offset); 336 stakseek(offset); 337 } 338 #else 339 return(0); 340 #endif /* SHOPT_FS_3D */ 341 /* FALLTHROUGH */ 342 case 'd': 343 return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode)); 344 case 'c': 345 return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode)); 346 case 'b': 347 return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode)); 348 case 'f': 349 return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode)); 350 case 'u': 351 return(test_mode(arg)&S_ISUID); 352 case 'g': 353 return(test_mode(arg)&S_ISGID); 354 case 'k': 355 #ifdef S_ISVTX 356 return(test_mode(arg)&S_ISVTX); 357 #else 358 return(0); 359 #endif /* S_ISVTX */ 360 #if SHOPT_TEST_L 361 case 'l': 362 #endif 363 case 'L': 364 case 'h': /* undocumented, and hopefully will disappear */ 365 if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0) 366 return(0); 367 return(S_ISLNK(statb.st_mode)); 368 369 case 'C': 370 #ifdef S_ISCTG 371 return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode)); 372 #else 373 return(0); 374 #endif /* S_ISCTG */ 375 case 'H': 376 #ifdef S_ISCDF 377 { 378 register int offset = staktell(); 379 if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)) 380 return(1); 381 stakputs(arg); 382 stakputc('+'); 383 stakputc(0); 384 arg = (const char*)stakptr(offset); 385 stakseek(offset); 386 return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)); 387 } 388 #else 389 return(0); 390 #endif /* S_ISCDF */ 391 392 case 'S': 393 return(isasock(arg,&statb)); 394 case 'N': 395 return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb)); 396 case 'p': 397 return(isapipe(arg,&statb)); 398 case 'n': 399 return(*arg != 0); 400 case 'z': 401 return(*arg == 0); 402 case 's': 403 sfsync(sfstdout); 404 /* FALLTHROUGH */ 405 case 'O': 406 case 'G': 407 if(*arg==0 || test_stat(arg,&statb)<0) 408 return(0); 409 if(op=='s') 410 return(statb.st_size>0); 411 else if(op=='O') 412 return(statb.st_uid==shp->gd->userid); 413 return(statb.st_gid==shp->gd->groupid); 414 case 'a': 415 case 'e': 416 if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK)) 417 return(1); 418 return(permission(arg, F_OK)); 419 case 'o': 420 f=1; 421 if(*arg=='?') 422 return(sh_lookopt(arg+1,&f)>0); 423 op = sh_lookopt(arg,&f); 424 return(op && (f==(sh_isoption(op)!=0))); 425 case 't': 426 { 427 char *last; 428 op = strtol(arg,&last, 10); 429 return(*last?0:tty_check(op)); 430 } 431 case 'v': 432 case 'R': 433 { 434 Namval_t *np; 435 Namarr_t *ap; 436 int isref; 437 if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF))) 438 return(0); 439 isref = nv_isref(np); 440 if(op=='R') 441 return(isref); 442 if(isref) 443 { 444 if(np->nvalue.cp) 445 np = nv_refnode(np); 446 else 447 return(0); 448 449 } 450 if(ap = nv_arrayptr(np)) 451 return(nv_arrayisset(np,ap)); 452 return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER)); 453 } 454 default: 455 { 456 static char a[3] = "-?"; 457 a[1]= op; 458 errormsg(SH_DICT,ERROR_exit(2),e_badop,a); 459 /* NOTREACHED */ 460 return(0); 461 } 462 } 463 } 464 465 int test_binop(Shell_t *shp,register int op,const char *left,const char *right) 466 { 467 register double lnum,rnum; 468 if(op&TEST_ARITH) 469 { 470 while(*left=='0') 471 left++; 472 while(*right=='0') 473 right++; 474 lnum = sh_arith(shp,left); 475 rnum = sh_arith(shp,right); 476 } 477 switch(op) 478 { 479 /* op must be one of the following values */ 480 case TEST_AND: 481 case TEST_OR: 482 return(*left!=0); 483 case TEST_PEQ: 484 return(test_strmatch(shp, left, right)); 485 case TEST_PNE: 486 return(!test_strmatch(shp, left, right)); 487 case TEST_SGT: 488 return(strcoll(left, right)>0); 489 case TEST_SLT: 490 return(strcoll(left, right)<0); 491 case TEST_SEQ: 492 return(strcmp(left, right)==0); 493 case TEST_SNE: 494 return(strcmp(left, right)!=0); 495 case TEST_EF: 496 return(test_inode(left,right)); 497 case TEST_NT: 498 return(test_time(left,right)>0); 499 case TEST_OT: 500 return(test_time(left,right)<0); 501 case TEST_EQ: 502 return(lnum==rnum); 503 case TEST_NE: 504 return(lnum!=rnum); 505 case TEST_GT: 506 return(lnum>rnum); 507 case TEST_LT: 508 return(lnum<rnum); 509 case TEST_GE: 510 return(lnum>=rnum); 511 case TEST_LE: 512 return(lnum<=rnum); 513 } 514 /* NOTREACHED */ 515 return(0); 516 } 517 518 /* 519 * returns the modification time of f1 - modification time of f2 520 */ 521 522 static time_t test_time(const char *file1,const char *file2) 523 { 524 Time_t t1, t2; 525 struct stat statb1,statb2; 526 int r=test_stat(file2,&statb2); 527 if(test_stat(file1,&statb1)<0) 528 return(r<0?0:-1); 529 if(r<0) 530 return(1); 531 t1 = tmxgetmtime(&statb1); 532 t2 = tmxgetmtime(&statb2); 533 if (t1 > t2) 534 return(1); 535 if (t1 < t2) 536 return(-1); 537 return(0); 538 } 539 540 /* 541 * return true if inode of two files are the same 542 */ 543 544 int test_inode(const char *file1,const char *file2) 545 { 546 struct stat stat1,stat2; 547 if(test_stat(file1,&stat1)>=0 && test_stat(file2,&stat2)>=0) 548 if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino) 549 return(1); 550 return(0); 551 } 552 553 554 /* 555 * This version of access checks against effective uid/gid 556 * The static buffer statb is shared with test_mode. 557 */ 558 559 int sh_access(register const char *name, register int mode) 560 { 561 Shell_t *shp = sh_getinterp(); 562 struct stat statb; 563 if(*name==0) 564 return(-1); 565 if(sh_isdevfd(name)) 566 return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode)); 567 /* can't use access function for execute permission with root */ 568 if(mode==X_OK && shp->gd->euserid==0) 569 goto skip; 570 if(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid) 571 return(access(name,mode)); 572 #ifdef _lib_setreuid 573 /* swap the real uid to effective, check access then restore */ 574 /* first swap real and effective gid, if different */ 575 if(shp->gd->groupid==shp->gd->euserid || setregid(shp->gd->egroupid,shp->gd->groupid)==0) 576 { 577 /* next swap real and effective uid, if needed */ 578 if(shp->gd->userid==shp->gd->euserid || setreuid(shp->gd->euserid,shp->gd->userid)==0) 579 { 580 mode = access(name,mode); 581 /* restore ids */ 582 if(shp->gd->userid!=shp->gd->euserid) 583 setreuid(shp->gd->userid,shp->gd->euserid); 584 if(shp->gd->groupid!=shp->gd->egroupid) 585 setregid(shp->gd->groupid,shp->gd->egroupid); 586 return(mode); 587 } 588 else if(shp->gd->groupid!=shp->gd->egroupid) 589 setregid(shp->gd->groupid,shp->gd->egroupid); 590 } 591 #endif /* _lib_setreuid */ 592 skip: 593 if(test_stat(name, &statb) == 0) 594 { 595 if(mode == F_OK) 596 return(mode); 597 else if(shp->gd->euserid == 0) 598 { 599 if(!S_ISREG(statb.st_mode) || mode!=X_OK) 600 return(0); 601 /* root needs execute permission for someone */ 602 mode = (S_IXUSR|S_IXGRP|S_IXOTH); 603 } 604 else if(shp->gd->euserid == statb.st_uid) 605 mode <<= 6; 606 else if(shp->gd->egroupid == statb.st_gid) 607 mode <<= 3; 608 #ifdef _lib_getgroups 609 /* on some systems you can be in several groups */ 610 else 611 { 612 static int maxgroups; 613 gid_t *groups; 614 register int n; 615 if(maxgroups==0) 616 { 617 /* first time */ 618 if((maxgroups=getgroups(0,(gid_t*)0)) <= 0) 619 { 620 /* pre-POSIX system */ 621 maxgroups=NGROUPS_MAX; 622 } 623 } 624 groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t)); 625 n = getgroups(maxgroups,groups); 626 while(--n >= 0) 627 { 628 if(groups[n] == statb.st_gid) 629 { 630 mode <<= 3; 631 break; 632 } 633 } 634 } 635 # endif /* _lib_getgroups */ 636 if(statb.st_mode & mode) 637 return(0); 638 } 639 return(-1); 640 } 641 642 /* 643 * Return the mode bits of file <file> 644 * If <file> is null, then the previous stat buffer is used. 645 * The mode bits are zero if the file doesn't exist. 646 */ 647 648 static int test_mode(register const char *file) 649 { 650 struct stat statb; 651 statb.st_mode = 0; 652 if(file && (*file==0 || test_stat(file,&statb)<0)) 653 return(0); 654 return(statb.st_mode); 655 } 656 657 /* 658 * do an fstat() for /dev/fd/n, otherwise stat() 659 */ 660 static int test_stat(const char *name,struct stat *buff) 661 { 662 if(*name==0) 663 { 664 errno = ENOENT; 665 return(-1); 666 } 667 if(sh_isdevfd(name)) 668 return(fstat((int)strtol(name+8, (char**)0, 10),buff)); 669 else 670 return(stat(name,buff)); 671 } 672