xref: /titanic_44/usr/src/lib/libshell/common/bltins/test.c (revision 67dbe2be0c0f1e2eb428b89088bb5667e8f0b9f6)
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@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