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) 64static time_t test_time(const char*, const char*); 65static int test_stat(const char*, struct stat*); 66static 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 73struct test 74{ 75 Shell_t *sh; 76 int ap; 77 int ac; 78 char **av; 79}; 80 81static char *nxtarg(struct test*,int); 82static int expr(struct test*,int); 83static int e3(struct test*); 84 85static 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 111int 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 */ 197static 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 236static 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 251static 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 } 302skip: 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 313int 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 /* FALL THRU */ 338 } 339#else 340 return(0); 341#endif /* SHOPT_FS_3D */ 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 case 'O': 405 case 'G': 406 if(*arg==0 || test_stat(arg,&statb)<0) 407 return(0); 408 if(op=='s') 409 return(statb.st_size>0); 410 else if(op=='O') 411 return(statb.st_uid==shp->gd->userid); 412 return(statb.st_gid==shp->gd->groupid); 413 case 'a': 414 case 'e': 415 if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK)) 416 return(1); 417 return(permission(arg, F_OK)); 418 case 'o': 419 f=1; 420 if(*arg=='?') 421 return(sh_lookopt(arg+1,&f)>0); 422 op = sh_lookopt(arg,&f); 423 return(op && (f==(sh_isoption(op)!=0))); 424 case 't': 425 { 426 char *last; 427 op = strtol(arg,&last, 10); 428 return(*last?0:tty_check(op)); 429 } 430 case 'v': 431 case 'R': 432 { 433 Namval_t *np; 434 Namarr_t *ap; 435 int isref; 436 if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF))) 437 return(0); 438 isref = nv_isref(np); 439 if(op=='R') 440 return(isref); 441 if(isref) 442 { 443 if(np->nvalue.cp) 444 np = nv_refnode(np); 445 else 446 return(0); 447 448 } 449 if(ap = nv_arrayptr(np)) 450 return(nv_arrayisset(np,ap)); 451 return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER)); 452 } 453 default: 454 { 455 static char a[3] = "-?"; 456 a[1]= op; 457 errormsg(SH_DICT,ERROR_exit(2),e_badop,a); 458 /* NOTREACHED */ 459 return(0); 460 } 461 } 462} 463 464int test_binop(Shell_t *shp,register int op,const char *left,const char *right) 465{ 466 register double lnum,rnum; 467 if(op&TEST_ARITH) 468 { 469 while(*left=='0') 470 left++; 471 while(*right=='0') 472 right++; 473 lnum = sh_arith(shp,left); 474 rnum = sh_arith(shp,right); 475 } 476 switch(op) 477 { 478 /* op must be one of the following values */ 479 case TEST_AND: 480 case TEST_OR: 481 return(*left!=0); 482 case TEST_PEQ: 483 return(test_strmatch(shp, left, right)); 484 case TEST_PNE: 485 return(!test_strmatch(shp, left, right)); 486 case TEST_SGT: 487 return(strcoll(left, right)>0); 488 case TEST_SLT: 489 return(strcoll(left, right)<0); 490 case TEST_SEQ: 491 return(strcmp(left, right)==0); 492 case TEST_SNE: 493 return(strcmp(left, right)!=0); 494 case TEST_EF: 495 return(test_inode(left,right)); 496 case TEST_NT: 497 return(test_time(left,right)>0); 498 case TEST_OT: 499 return(test_time(left,right)<0); 500 case TEST_EQ: 501 return(lnum==rnum); 502 case TEST_NE: 503 return(lnum!=rnum); 504 case TEST_GT: 505 return(lnum>rnum); 506 case TEST_LT: 507 return(lnum<rnum); 508 case TEST_GE: 509 return(lnum>=rnum); 510 case TEST_LE: 511 return(lnum<=rnum); 512 } 513 /* NOTREACHED */ 514 return(0); 515} 516 517/* 518 * returns the modification time of f1 - modification time of f2 519 */ 520 521static time_t test_time(const char *file1,const char *file2) 522{ 523 Time_t t1, t2; 524 struct stat statb1,statb2; 525 int r=test_stat(file2,&statb2); 526 if(test_stat(file1,&statb1)<0) 527 return(r<0?0:-1); 528 if(r<0) 529 return(1); 530 t1 = tmxgetmtime(&statb1); 531 t2 = tmxgetmtime(&statb2); 532 if (t1 > t2) 533 return(1); 534 if (t1 < t2) 535 return(-1); 536 return(0); 537} 538 539/* 540 * return true if inode of two files are the same 541 */ 542 543int test_inode(const char *file1,const char *file2) 544{ 545 struct stat stat1,stat2; 546 if(test_stat(file1,&stat1)>=0 && test_stat(file2,&stat2)>=0) 547 if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino) 548 return(1); 549 return(0); 550} 551 552 553/* 554 * This version of access checks against effective uid/gid 555 * The static buffer statb is shared with test_mode. 556 */ 557 558int sh_access(register const char *name, register int mode) 559{ 560 Shell_t *shp = sh_getinterp(); 561 struct stat statb; 562 if(*name==0) 563 return(-1); 564 if(sh_isdevfd(name)) 565 return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode)); 566 /* can't use access function for execute permission with root */ 567 if(mode==X_OK && shp->gd->euserid==0) 568 goto skip; 569 if(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid) 570 return(access(name,mode)); 571#ifdef _lib_setreuid 572 /* swap the real uid to effective, check access then restore */ 573 /* first swap real and effective gid, if different */ 574 if(shp->gd->groupid==shp->gd->euserid || setregid(shp->gd->egroupid,shp->gd->groupid)==0) 575 { 576 /* next swap real and effective uid, if needed */ 577 if(shp->gd->userid==shp->gd->euserid || setreuid(shp->gd->euserid,shp->gd->userid)==0) 578 { 579 mode = access(name,mode); 580 /* restore ids */ 581 if(shp->gd->userid!=shp->gd->euserid) 582 setreuid(shp->gd->userid,shp->gd->euserid); 583 if(shp->gd->groupid!=shp->gd->egroupid) 584 setregid(shp->gd->groupid,shp->gd->egroupid); 585 return(mode); 586 } 587 else if(shp->gd->groupid!=shp->gd->egroupid) 588 setregid(shp->gd->groupid,shp->gd->egroupid); 589 } 590#endif /* _lib_setreuid */ 591skip: 592 if(test_stat(name, &statb) == 0) 593 { 594 if(mode == F_OK) 595 return(mode); 596 else if(shp->gd->euserid == 0) 597 { 598 if(!S_ISREG(statb.st_mode) || mode!=X_OK) 599 return(0); 600 /* root needs execute permission for someone */ 601 mode = (S_IXUSR|S_IXGRP|S_IXOTH); 602 } 603 else if(shp->gd->euserid == statb.st_uid) 604 mode <<= 6; 605 else if(shp->gd->egroupid == statb.st_gid) 606 mode <<= 3; 607#ifdef _lib_getgroups 608 /* on some systems you can be in several groups */ 609 else 610 { 611 static int maxgroups; 612 gid_t *groups; 613 register int n; 614 if(maxgroups==0) 615 { 616 /* first time */ 617 if((maxgroups=getgroups(0,(gid_t*)0)) <= 0) 618 { 619 /* pre-POSIX system */ 620 maxgroups=NGROUPS_MAX; 621 } 622 } 623 groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t)); 624 n = getgroups(maxgroups,groups); 625 while(--n >= 0) 626 { 627 if(groups[n] == statb.st_gid) 628 { 629 mode <<= 3; 630 break; 631 } 632 } 633 } 634# endif /* _lib_getgroups */ 635 if(statb.st_mode & mode) 636 return(0); 637 } 638 return(-1); 639} 640 641/* 642 * Return the mode bits of file <file> 643 * If <file> is null, then the previous stat buffer is used. 644 * The mode bits are zero if the file doesn't exist. 645 */ 646 647static int test_mode(register const char *file) 648{ 649 struct stat statb; 650 statb.st_mode = 0; 651 if(file && (*file==0 || test_stat(file,&statb)<0)) 652 return(0); 653 return(statb.st_mode); 654} 655 656/* 657 * do an fstat() for /dev/fd/n, otherwise stat() 658 */ 659static int test_stat(const char *name,struct stat *buff) 660{ 661 if(*name==0) 662 { 663 errno = ENOENT; 664 return(-1); 665 } 666 if(sh_isdevfd(name)) 667 return(fstat((int)strtol(name+8, (char**)0, 10),buff)); 668 else 669 return(stat(name,buff)); 670} 671