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