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