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