1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 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
test_strmatch(const char * str,const char * pat)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
b_test(int argc,char * argv[],void * extra)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 */
expr(struct test * tp,register int flag)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
nxtarg(struct test * tp,int mt)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
e3(struct test * tp)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
test_unop(register int op,register const char * arg)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
test_binop(register int op,const char * left,const char * right)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
test_time(const char * file1,const char * file2)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
test_inode(const char * file1,const char * file2)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
sh_access(register const char * name,register int mode)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
test_mode(register const char * file)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 */
test_stat(const char * name,struct stat * buff)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