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
test_strmatch(Shell_t * shp,const char * str,const char * pat)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
b_test(int argc,char * argv[],Shbltin_t * context)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 */
expr(struct test * tp,register int flag)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
nxtarg(struct test * tp,int mt)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
e3(struct test * tp)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
test_unop(Shell_t * shp,register int op,register const char * arg)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
test_binop(Shell_t * shp,register int op,const char * left,const char * right)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
test_time(const char * file1,const char * file2)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
test_inode(const char * file1,const char * file2)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
sh_access(register const char * name,register int mode)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
test_mode(register const char * file)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 */
test_stat(const char * name,struct stat * buff)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