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