1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23
24 /*
25 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28
29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
31
32 #if defined(sun)
33 #pragma ident "@(#)test.c 1.17 06/06/20 SMI"
34 #endif
35
36 #include "defs.h"
37
38 /*
39 * Copyright 2008-2019 J. Schilling
40 *
41 * @(#)test.c 1.43 19/09/16 2008-2019 J. Schilling
42 */
43 #ifndef lint
44 static UConst char sccsid[] =
45 "@(#)test.c 1.43 19/09/16 2008-2019 J. Schilling";
46 #endif
47
48
49 /*
50 * test expression
51 * [ expression ]
52 */
53
54 #ifdef SCHILY_INCLUDES
55 #include <schily/types.h>
56 #include <schily/fcntl.h>
57 #include <schily/stat.h>
58 #else
59 #include <sys/types.h>
60 #include <fcntl.h>
61 #include <sys/stat.h>
62 #endif
63
64 #ifndef HAVE_LSTAT
65 #define lstat stat
66 #undef AT_SYMLINK_NOFOLLOW
67 #define AT_SYMLINK_NOFOLLOW 0
68 #endif
69 #ifndef HAVE_DECL_STAT
70 extern int stat __PR((const char *, struct stat *));
71 #endif
72 #ifndef HAVE_DECL_LSTAT
73 extern int lstat __PR((const char *, struct stat *));
74 #endif
75
76 #define exp _exp /* Some compilers do not like exp() */
77
78 int test __PR((int argn, unsigned char *com[]));
79 #ifdef DO_SYSATEXPR
80 void expr __PR((int argn, unsigned char *com[]));
81 static long aexpr __PR((struct namnod *n,
82 unsigned char *op, long y));
83 #endif
84 static unsigned char *nxtarg __PR((int mt));
85 static int exp __PR((void));
86 static int e1 __PR((void));
87 static int e2 __PR((void));
88 static int e3 __PR((void));
89 static int test_unary __PR((int op, unsigned char *arg));
90 static int test_binary __PR((unsigned char *arg1, int op,
91 unsigned char *arg2));
92 static int ftype __PR((unsigned char *f, int field));
93 static int filtyp __PR((unsigned char *f, int field));
94 #ifdef DO_EXT_TEST
95 static int fsame __PR((unsigned char *f1, unsigned char *f2));
96 static int ftime __PR((unsigned char *f1, unsigned char *f2));
97 static int fnew __PR((unsigned char *));
98 static int fowner __PR((unsigned char *f, uid_t owner));
99 static int fgroup __PR((unsigned char *f, uid_t owner));
100 #endif
101 static int fsizep __PR((unsigned char *f));
102 static Intmax_t str2imax __PR((unsigned char *a));
103
104 #undef failed
105 #undef bfailed
106 #define failed(s1, s2) bfailed(s1, s2, NULL)
107 static void bfailed __PR((unsigned char *s1,
108 const char *s2,
109 unsigned char *s3));
110
111 #ifdef DO_POSIX_TEST
112 /*
113 * In POSIX mode, we need to overwrite the standard error code with a
114 * value > 1, this is ETEST (2).
115 */
116 #undef ERROR
117 #define ERROR ETEST
118 #endif
119
120 #ifdef DO_SYSATEXPR
121 static int isexpr;
122 #endif
123 static int ap, ac;
124 static unsigned char **av;
125 static jmp_buf testsyntax;
126
127 #define nargs() (ac - ap)
128
129 /*
130 * Set up test_unops[] to avoid calling test_unary() with non-unary
131 * operators. The following #defines influence the number os unary
132 * operators:
133 *
134 * DO_POSIX_TEST -e -S
135 * DO_EXT_TEST -C -D -G -N -O -P
136 * defined(DO_EXT_TEST) && defined(DO_SET_O) -o
137 * normal unaries from old Bourne Shell Lbcdfghknprstuwxz
138 */
139 #ifdef PROTOTYPES
140 const char test_unops[] = "Lbcdfghknprstuwxz"
141 #ifdef DO_POSIX_TEST
142 "eS"
143 #endif
144 #ifdef DO_EXT_TEST
145 "CDGNOP"
146 #endif
147 #if defined(DO_EXT_TEST) && defined(DO_SET_O)
148 "o"
149 #endif
150 "";
151 #else /* !PROTOTYPES */
152 #if defined(DO_POSIX_TEST) || defined(DO_EXT_TEST)
153 const char test_unops[] = "LbcdfghknprstuwxzeSCDGNOPo";
154 #else
155 const char test_unops[] = "Lbcdfghknprstuwxz";
156 #endif
157 #endif /* !PROTOTYPES */
158
159 int
test(argn,com)160 test(argn, com)
161 int argn;
162 unsigned char *com[];
163 {
164 #ifdef DO_POSIX_TEST
165 int not;
166 int inv = 0;
167 int op;
168 #endif
169 ac = argn;
170 av = com;
171 ap = 1;
172 #ifdef DO_SYSATEXPR
173 isexpr = 0;
174 #endif
175
176 if (eq(com[0], "[")) {
177 if (!eq(com[--ac], "]")) {
178 Failure((unsigned char *)"test", nobracket);
179 return (SYNBAD);
180 }
181 }
182 com[ac] = 0;
183 if (ac <= 1) /* POSIX case: 0 args */
184 return (1); /* test exits false */
185 if (setjmp(testsyntax))
186 return (ETEST);
187
188 #ifdef DO_POSIX_TEST
189 not = com[1][0] == '!' && com[1][1] == '\0';
190 com++;
191 switch (ac) {
192
193 default: /* POSIX >4 args unspec */
194 break;
195
196 case 5:
197 if (not) {
198 inv = 1;
199 com++;
200 not = com[0][0] == '!' && com[0][1] == '\0';
201
202 } else if ((com[0][0] == '(' && com[0][1] == '\0') &&
203 (com[3][0] == ')' && com[3][1] == '\0')) {
204 com++;
205 not = com[0][0] == '!' && com[0][1] == '\0';
206 goto two;
207 } else {
208 break; /* Unspecified by POSIX */
209 }
210 /* FALLTHROUGH */
211
212 case 4:
213 op = syslook(com[1], test_ops, no_test_ops);
214 if (op)
215 return (inv ^ !test_binary(com[0], op, com[2]));
216
217 if (not) {
218 inv = 1;
219 com++;
220 not = com[0][0] == '!' && com[0][1] == '\0';
221
222 } else if ((com[0][0] == '(' && com[0][1] == '\0') &&
223 (com[2][0] == ')' && com[2][1] == '\0')) {
224 com++;
225 not = com[0][0] == '!' && com[0][1] == '\0';
226 goto one;
227 } else {
228 break; /* Unspecified by POSIX */
229 }
230 /* FALLTHROUGH */
231 case 3:
232 two:
233 if (not)
234 return (inv ^ (com[1][0] != 0));
235
236 if (com[0][0] == '-' &&
237 com[0][1] != '\0' && com[0][2] == '\0' &&
238 strchr(test_unops, com[0][1]))
239 return (inv ^ !test_unary(com[0][1], com[1]));
240
241 break; /* Unspecified by POSIX */
242 case 2:
243 one:
244 /*
245 * Compatibility for UNIX -t without parameter
246 */
247 if (!(flags2 & posixflg) && eq(com[0], "-t"))
248 break;
249
250 return (inv ^ (com[0][0] == 0));
251 }
252 #endif
253
254 return (exp() ? 0 : 1);
255 }
256
257 #ifdef DO_SYSATEXPR
258 void
expr(argn,com)259 expr(argn, com)
260 int argn;
261 unsigned char *com[];
262 {
263 int incr = 0;
264 struct namnod *n = NULL; /* Make GCC happy */
265 char buf[40];
266
267 ac = argn;
268 av = com;
269 ap = optskip(argn, com, "@ expr");
270 if (ap < 0)
271 return;
272 isexpr = 1;
273
274 if (ac == 2) { /* @ var++ */
275 int len = length(av[ap]);
276
277 if (len > 3) {
278 unsigned char *p = av[ap] + len - 3;
279
280 if (eq(p, "++"))
281 incr = 1;
282 else if (eq(p, "--"))
283 incr = -1;
284 if (incr) {
285 *p = '\0';
286 n = lookup(av[ap]);
287 }
288 }
289 if (incr == 0) {
290 Failure((unsigned char *)"@", noarg);
291 return;
292 }
293 } else if (ac < 4) {
294 Failure((unsigned char *)"@", noarg);
295 return;
296 }
297 if (setjmp(testsyntax))
298 return;
299 if (incr) {
300 snprintf(buf, sizeof (buf), "%ld", aexpr(n, UC "+=", incr));
301 } else {
302 n = lookup(av[ap++]);
303 snprintf(buf, sizeof (buf), "%ld", aexpr(n, av[ap++], exp(0)));
304 }
305 assign(n, UC buf);
306 }
307
308 static long
aexpr(n,op,y)309 aexpr(n, op, y)
310 struct namnod *n;
311 unsigned char *op;
312 long y;
313 {
314 long x;
315 char c = *op;
316
317 if (eq(op, "="))
318 return (y);
319
320 if (c == 0 || op[1] != '=' || op[2])
321 bfailed((unsigned char *)"@", badop, op);
322
323 if (n->namval == NULL)
324 failed((unsigned char *)"@", unset);
325 x = str2imax(n->namval);
326
327 switch (c) {
328
329 case '+': return (x + y);
330 case '-': return (x - y);
331 case '*': return (x * y);
332 case '/':
333 if (y == 0)
334 failed((unsigned char *)"@", divzero);
335 return (x / y);
336 case '%':
337 if (y == 0)
338 failed((unsigned char *)"@", divzero);
339 return (x % y);
340
341 default:
342 bfailed((unsigned char *)"@", badop, op);
343 }
344
345 return (-1);
346 }
347 #endif
348
349 static unsigned char *
nxtarg(mt)350 nxtarg(mt)
351 int mt;
352 {
353 if (ap >= ac) {
354 if (mt) {
355 ap++;
356 return (0);
357 }
358 failed((unsigned char *)"test", noarg);
359 }
360 return (av[ap++]);
361 }
362
363 /*
364 * The main test expression evaluator.
365 * Returns 0 -> FALSE
366 * != 0 -> TRUE
367 */
368 /* ARGSUSED */
369 static int
exp()370 exp()
371 {
372 int p1;
373 unsigned char *p2;
374
375 p1 = e1();
376 p2 = nxtarg(1);
377 if (p2 != 0) {
378 if (eq(p2, "-o"))
379 return (p1 | exp());
380
381 #ifdef __nono__
382 if (!eq(p2, ")"))
383 failed((unsigned char *)"test", synmsg);
384 #endif
385 }
386 ap--;
387 return (p1);
388 }
389
390 static int
e1()391 e1()
392 {
393 int p1;
394 unsigned char *p2;
395
396 p1 = e2();
397 p2 = nxtarg(1);
398
399 if ((p2 != 0) && eq(p2, "-a"))
400 return (p1 & e1());
401 ap--;
402 return (p1);
403 }
404
405 static int
e2()406 e2()
407 {
408 if (eq(nxtarg(0), "!"))
409 return (!e3());
410 ap--;
411 return (e3());
412 }
413
414 static int
e3()415 e3()
416 {
417 int p1;
418 unsigned char *a;
419 unsigned char *p2;
420
421 a = nxtarg(0);
422 if (eq(a, "(")) {
423 p1 = exp();
424 if (!eq(nxtarg(0), ")"))
425 failed((unsigned char *)"test", noparen);
426 return (p1);
427 }
428 p2 = nxtarg(1);
429 ap--;
430 if ((p2 == 0) || (!eq(p2, "=") && !eq(p2, "!="))) {
431 if (eq(a, "-t")) {
432 unsigned char *na;
433
434 if (ap >= ac) /* no args */
435 return (isatty(STDOUT_FILENO));
436 na = nxtarg(0);
437 ap--;
438 if (eq(na, "-a") || eq(na, "-o"))
439 return (isatty(STDOUT_FILENO));
440 }
441 if (a[0] == '-' && a[1] != '\0' && a[2] == '\0' &&
442 strchr(test_unops, a[1]))
443 return (test_unary(a[1], nxtarg(0)));
444 }
445
446 p2 = nxtarg(1);
447 if (p2 == 0) {
448 #ifdef DO_SYSATEXPR
449 if (isexpr && digit(*a)) {
450 ll_1 = str2imax(a);
451 return (ll_1);
452 }
453 #endif
454 return (!eq(a, ""));
455 }
456
457 p1 = syslook(p2, test_ops, no_test_ops);
458 if (p1 == TEST_AND || p1 == TEST_OR) {
459 ap--;
460 return (!eq(a, ""));
461 }
462 if (p1) {
463 return (test_binary(a, p1, nxtarg(0)));
464 }
465
466 #ifdef DO_SYSATEXPR
467 if (isexpr) {
468 char c = *p2;
469
470 if (c && p2[1] == '\0') {
471 if (c == '+')
472 return (ll_1 + ll_2);
473 if (c == '-')
474 return (ll_1 - ll_2);
475 if (c == '*')
476 return (ll_1 * ll_2);
477 if (c == '/') {
478 if (ll_2 == 0)
479 failed((unsigned char *)"@", divzero);
480 return (ll_1 / ll_2);
481 }
482 if (c == '%') {
483 if (ll_2 == 0)
484 failed((unsigned char *)"@", divzero);
485 return (ll_1 % ll_2);
486 }
487 if (c == '&')
488 return (ll_1 & ll_2);
489 if (c == '|')
490 return (ll_1 | ll_2);
491 if (c == '>')
492 return (ll_1 > ll_2);
493 if (c == '<')
494 return (ll_1 < ll_2);
495 }
496 if (eq(p2, "&&"))
497 return (ll_1 && ll_2);
498 if (eq(p2, "||"))
499 return (ll_1 || ll_2);
500 if (eq(p2, ">="))
501 return (ll_1 >= ll_2);
502 if (eq(p2, "<="))
503 return (ll_1 <= ll_2);
504 if (eq(p2, ">>"))
505 return (ll_1 >> ll_2);
506 if (eq(p2, "<<"))
507 return (ll_1 << ll_2);
508 }
509 #endif
510
511 bfailed((unsigned char *)btest, badop, p2);
512 /* NOTREACHED */
513
514 return (0); /* Not reached, but keeps GCC happy */
515 }
516
517 static int
test_unary(op,arg)518 test_unary(op, arg)
519 int op;
520 unsigned char *arg;
521 {
522 switch (op) {
523 #ifdef DO_POSIX_TEST
524 case 'e':
525 return (chk_access(arg, F_OK, 0) == 0);
526 #endif
527 case 'r':
528 return (chk_access(arg, S_IREAD, 0) == 0);
529 case 'w':
530 return (chk_access(arg, S_IWRITE, 0) == 0);
531 case 'x':
532 return (chk_access(arg, S_IEXEC, 0) == 0);
533 case 'd':
534 return (filtyp(arg, S_IFDIR));
535 #ifdef DO_EXT_TEST
536 case 'D':
537 #ifdef S_IFDOOR
538 return (filtyp(arg, S_IFDOOR));
539 #else
540 return (0);
541 #endif
542 case 'C':
543 #ifdef S_IFCTG
544 return (filtyp(arg, S_IFCTG));
545 #else
546 return (0);
547 #endif
548 #endif
549 case 'c':
550 return (filtyp(arg, S_IFCHR));
551 case 'b':
552 return (filtyp(arg, S_IFBLK));
553 case 'f':
554 if (ucb_builtins) {
555 struct stat statb;
556
557 return (lstatat((char *)arg, &statb, 0) >= 0 &&
558 (statb.st_mode & S_IFMT) != S_IFDIR);
559 } else {
560 return (filtyp(arg, S_IFREG));
561 }
562 case 'u':
563 return (ftype(arg, S_ISUID));
564 case 'g':
565 return (ftype(arg, S_ISGID));
566 case 'k':
567 #ifdef S_ISVTX
568 return (ftype(arg, S_ISVTX));
569 #else
570 return (0);
571 #endif
572 #if defined(DO_EXT_TEST) && defined(DO_SET_O)
573 case 'o':
574 if (arg && *arg == '?') {
575 return (lookopt(++arg) != NULL);
576 }
577 if (arg)
578 return (optval(lookopt(arg)));
579 return (0); /* should never happen */
580 #endif
581 case 'p':
582 return (filtyp(arg, S_IFIFO));
583 case 'h':
584 case 'L':
585 return (filtyp(arg, S_IFLNK));
586 #ifdef DO_EXT_TEST
587 case 'P':
588 #if defined(S_IFPORT) && S_IFPORT != S_IFIFO /* Do not use it on Ultrix */
589
590 return (filtyp(arg, S_IFPORT));
591 #else
592 return (0);
593 #endif
594 #endif
595 #ifdef DO_POSIX_TEST
596 case 'S':
597 #ifdef S_IFSOCK
598 return (filtyp(arg, S_IFSOCK));
599 #else
600 return (0);
601 #endif
602 #endif
603 case 's':
604 return (fsizep(arg));
605 case 't':
606 return (isatty(atoi((char *)arg)));
607 #ifdef DO_EXT_TEST
608 case 'O':
609 return (fowner(arg, geteuid()));
610 case 'G':
611 return (fgroup(arg, getegid()));
612 case 'N':
613 return (fnew(arg));
614 #endif
615 case 'n':
616 return (!eq(arg, ""));
617 case 'z':
618 return (eq(arg, ""));
619
620 default:
621 failed((unsigned char *)"test", noarg);
622 return (SYNBAD);
623 }
624 }
625
626 static int
test_binary(arg1,op,arg2)627 test_binary(arg1, op, arg2)
628 unsigned char *arg1;
629 int op;
630 unsigned char *arg2;
631 {
632 Intmax_t ll_1 = 0; /* Avoid warning from silly GCC */
633 Intmax_t ll_2 = 0; /* Avoid warning from silly GCC */
634
635 if (op >= TEST_EQ) {
636 ll_1 = str2imax(arg1);
637 ll_2 = str2imax(arg2);
638 }
639 switch (op) {
640
641 case TEST_AND: return (*arg1 && *arg2);
642 case TEST_OR: return (*arg1 || *arg2);
643
644 #ifdef DO_EXT_TEST
645 case TEST_EF: return (fsame(arg1, arg2));
646 case TEST_NT: return (ftime(arg1, arg2) > 0);
647 case TEST_OT: return (ftime(arg1, arg2) < 0);
648 #endif
649 case TEST_SEQ: return (eq(arg1, arg2));
650 case TEST_SNEQ: return (!eq(arg1, arg2));
651
652 case TEST_EQ: return (ll_1 == ll_2);
653 case TEST_NE: return (ll_1 != ll_2);
654 case TEST_GT: return (ll_1 > ll_2);
655 case TEST_LT: return (ll_1 < ll_2);
656 case TEST_GE: return (ll_1 >= ll_2);
657 case TEST_LE: return (ll_1 <= ll_2);
658
659 default: return (SYNBAD);
660 }
661 }
662
663 static int
ftype(f,field)664 ftype(f, field)
665 unsigned char *f;
666 int field;
667 {
668 struct stat statb;
669
670 if (lstatat((char *)f, &statb, 0) < 0)
671 return (0);
672 if ((statb.st_mode & field) == field)
673 return (1);
674 return (0);
675 }
676
677 static int
filtyp(f,field)678 filtyp(f, field)
679 unsigned char *f;
680 int field;
681 {
682 struct stat statb;
683 int flag = (field == S_IFLNK) ? AT_SYMLINK_NOFOLLOW : 0;
684
685 if (lstatat((char *)f, &statb, flag) < 0)
686 return (0);
687 if ((statb.st_mode & S_IFMT) == field)
688 return (1);
689 else
690 return (0);
691 }
692
693 #ifdef DO_EXT_TEST
694 static int
fsame(f1,f2)695 fsame(f1, f2)
696 unsigned char *f1;
697 unsigned char *f2;
698 {
699 struct stat statb1;
700 struct stat statb2;
701
702 if (lstatat((char *)f1, &statb1, 0) < 0) /* lstat() ??? */
703 return (FALSE);
704 if (lstatat((char *)f2, &statb2, 0) < 0) /* lstat() ??? */
705 return (FALSE);
706 if (statb1.st_ino == statb2.st_ino && statb1.st_dev == statb2.st_dev)
707 return (1);
708 return (FALSE);
709 }
710
711 static int /* mod time f1 - mod time f2 */
ftime(f1,f2)712 ftime(f1, f2)
713 unsigned char *f1;
714 unsigned char *f2;
715 {
716 struct stat statb1;
717 struct stat statb2;
718 int ret;
719
720 ret = lstatat((char *)f1, &statb1, 0); /* lstat() ??? */
721 if (lstatat((char *)f2, &statb2, 0) < 0) /* lstat() ??? */
722 return (ret < 0 ? 0:1);
723 if (ret < 0)
724 return (-1);
725 if (statb1.st_mtime > statb2.st_mtime)
726 return (1);
727 if (statb1.st_mtime == statb2.st_mtime &&
728 stat_mnsecs(&statb1) > stat_mnsecs(&statb2))
729 return (1);
730 if (statb1.st_mtime < statb2.st_mtime)
731 return (-1);
732 if (statb1.st_mtime == statb2.st_mtime &&
733 stat_mnsecs(&statb1) < stat_mnsecs(&statb2))
734 return (-1);
735 return (0);
736 }
737
738 static int
fnew(f)739 fnew(f)
740 unsigned char *f;
741 {
742 struct stat statb;
743
744 if (lstatat((char *)f, &statb, 0) < 0) /* lstat() ??? */
745 return (0);
746
747 if (statb.st_mtime > statb.st_atime)
748 return (1);
749 if (statb.st_mtime == statb.st_atime &&
750 stat_mnsecs(&statb) > stat_ansecs(&statb))
751 return (1);
752 return (0);
753 }
754
755 static int
fowner(f,owner)756 fowner(f, owner)
757 unsigned char *f;
758 uid_t owner;
759 {
760 struct stat statb;
761
762 if (lstatat((char *)f, &statb, 0) < 0) /* lstat() ??? */
763 return (FALSE);
764 return (statb.st_uid == owner);
765 }
766
767 static int
fgroup(f,group)768 fgroup(f, group)
769 unsigned char *f;
770 gid_t group;
771 {
772 struct stat statb;
773
774 if (lstatat((char *)f, &statb, 0) < 0) /* lstat() ??? */
775 return (FALSE);
776 return (statb.st_gid == group);
777 }
778 #endif
779
780 static int
fsizep(f)781 fsizep(f)
782 unsigned char *f;
783 {
784 struct stat statb;
785
786 if (lstatat((char *)f, &statb, 0) < 0)
787 return (0);
788 return (statb.st_size > 0);
789 }
790
791 static Intmax_t
str2imax(a)792 str2imax(a)
793 unsigned char *a;
794 {
795 Intmax_t i;
796 char *ep;
797
798 #ifdef HAVE_STRTOLL
799 i = strtoll((char *)a, &ep, 10);
800 #else
801 i = strtol((char *)a, &ep, 10);
802 #endif
803 #ifdef DO_POSIX_TEST
804 if ((char *)a == ep || *ep != '\0')
805 bfailed((unsigned char *)ep, badnum, NULL);
806 #endif
807 return (i);
808 }
809
810 static void
bfailed(s1,s2,s3)811 bfailed(s1, s2, s3)
812 unsigned char *s1;
813 const char *s2;
814 unsigned char *s3;
815 {
816 #ifdef DO_POSIX_FAILURE
817 failure_real(ETEST, s1, s2, s3, 0);
818 #else
819 failed_real(ETEST, s1, s2, s3);
820 #endif
821 longjmp(testsyntax, 1);
822 }
823