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