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