1 /* $NetBSD: test.c,v 1.45 2022/08/27 21:18:39 dholland Exp $ */
2 
3 /*
4  * test(1); version 7-like  --  author Erik Baalbergen
5  * modified by Eric Gisin to be used as built-in.
6  * modified by Arnold Robbins to add SVR3 compatibility
7  * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8  * modified by J.T. Conklin for NetBSD.
9  *
10  * This program is in the Public Domain.
11  */
12 
13 #include <sys/cdefs.h>
14 #ifndef lint
15 __RCSID("$NetBSD: test.c,v 1.45 2022/08/27 21:18:39 dholland Exp $");
16 #endif
17 
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <locale.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <stdarg.h>
31 
32 /* test(1) accepts the following grammar:
33           oexpr     ::= aexpr | aexpr "-o" oexpr ;
34           aexpr     ::= nexpr | nexpr "-a" aexpr ;
35           nexpr     ::= primary | "!" primary
36           primary   ::= unary-operator operand
37                     | operand binary-operator operand
38                     | operand
39                     | "(" oexpr ")"
40                     ;
41           unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
42                     "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
43 
44           binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
45                               "-nt"|"-ot"|"-ef";
46           operand ::= <any legal UNIX file name>
47 */
48 
49 enum token {
50           EOI,
51           FILRD,
52           FILWR,
53           FILEX,
54           FILEXIST,
55           FILREG,
56           FILDIR,
57           FILCDEV,
58           FILBDEV,
59           FILFIFO,
60           FILSOCK,
61           FILSYM,
62           FILGZ,
63           FILTT,
64           FILSUID,
65           FILSGID,
66           FILSTCK,
67           FILNT,
68           FILOT,
69           FILEQ,
70           FILUID,
71           FILGID,
72           STREZ,
73           STRNZ,
74           STREQ,
75           STRNE,
76           STRLT,
77           STRGT,
78           INTEQ,
79           INTNE,
80           INTGE,
81           INTGT,
82           INTLE,
83           INTLT,
84           UNOT,
85           BAND,
86           BOR,
87           LPAREN,
88           RPAREN,
89           OPERAND
90 };
91 
92 enum token_types {
93           UNOP,
94           BINOP
95 #ifndef SMALL
96                     ,
97           BUNOP,
98           BBINOP,
99           PAREN
100 #endif
101 };
102 
103 struct t_op {
104           const char *op_text;
105           short op_num, op_type;
106 };
107 
108 static const struct t_op cop[] = {
109 #ifndef SMALL
110           {"!",     UNOT,     BUNOP},
111           {"(",     LPAREN,   PAREN},
112           {")",     RPAREN,   PAREN},
113 #endif
114           {"<",     STRLT,    BINOP},
115           {"=",     STREQ,    BINOP},
116           {">",     STRGT,    BINOP},
117 };
118 
119 static const struct t_op cop2[] = {
120           {"!=",    STRNE,    BINOP},
121 };
122 
123 static const struct t_op mop3[] = {
124           {"ef",    FILEQ,    BINOP},
125           {"eq",    INTEQ,    BINOP},
126           {"ge",    INTGE,    BINOP},
127           {"gt",    INTGT,    BINOP},
128           {"le",    INTLE,    BINOP},
129           {"lt",    INTLT,    BINOP},
130           {"ne",    INTNE,    BINOP},
131           {"nt",    FILNT,    BINOP},
132           {"ot",    FILOT,    BINOP},
133 };
134 
135 static const struct t_op mop2[] = {
136           {"G",     FILGID,   UNOP},
137           {"L",     FILSYM,   UNOP},
138           {"O",     FILUID,   UNOP},
139           {"S",     FILSOCK,UNOP},
140 #ifndef SMALL
141           {"a",     BAND,     BBINOP},
142 #endif
143           {"b",     FILBDEV,UNOP},
144           {"c",     FILCDEV,UNOP},
145           {"d",     FILDIR,   UNOP},
146           {"e",     FILEXIST,UNOP},
147           {"f",     FILREG,   UNOP},
148           {"g",     FILSGID,UNOP},
149           {"h",     FILSYM,   UNOP},              /* for backwards compat */
150           {"k",     FILSTCK,UNOP},
151           {"n",     STRNZ,    UNOP},
152 #ifndef SMALL
153           {"o",     BOR,      BBINOP},
154 #endif
155           {"p",     FILFIFO,UNOP},
156           {"r",     FILRD,    UNOP},
157           {"s",     FILGZ,    UNOP},
158           {"t",     FILTT,    UNOP},
159           {"u",     FILSUID,UNOP},
160           {"w",     FILWR,    UNOP},
161           {"x",     FILEX,    UNOP},
162           {"z",     STREZ,    UNOP},
163 };
164 
165 #ifndef SMALL
166 static char **t_wp;
167 static struct t_op const *t_wp_op;
168 #endif
169 
170 #ifndef SMALL
171 __dead static void syntax(const char *, const char *);
172 static int oexpr(enum token);
173 static int aexpr(enum token);
174 static int nexpr(enum token);
175 static int primary(enum token);
176 static int binop(void);
177 static enum token t_lex(char *);
178 static int isoperand(void);
179 #endif
180 static struct t_op const *findop(const char *);
181 static int perform_unop(enum token, const char *);
182 static int perform_binop(enum token, const char *, const char *);
183 static int test_access(struct stat *, mode_t);
184 static int filstat(const char *, enum token);
185 static long long getn(const char *);
186 static int newerf(const char *, const char *);
187 static int olderf(const char *, const char *);
188 static int equalf(const char *, const char *);
189 
190 static int one_arg(const char *);
191 static int two_arg(const char *, const char *);
192 static int three_arg(const char *, const char *, const char *);
193 static int four_arg(const char *, const char *, const char *, const char *);
194 
195 #if defined(SHELL)
196 extern void error(const char *, ...) __dead __printflike(1, 2);
197 extern void *ckmalloc(size_t);
198 #else
199 static void error(const char *, ...) __dead __printflike(1, 2);
200 
201 static void
error(const char * msg,...)202 error(const char *msg, ...)
203 {
204           va_list ap;
205 
206           va_start(ap, msg);
207           verrx(2, msg, ap);
208           /*NOTREACHED*/
209           va_end(ap);
210 }
211 
212 static void *ckmalloc(size_t);
213 static void *
ckmalloc(size_t nbytes)214 ckmalloc(size_t nbytes)
215 {
216           void *p = malloc(nbytes);
217 
218           if (!p)
219                     error("Not enough memory!");
220           return p;
221 }
222 #endif
223 
224 #ifdef SHELL
225 int testcmd(int, char **);
226 
227 int
testcmd(int argc,char ** argv)228 testcmd(int argc, char **argv)
229 #else
230 int
231 main(int argc, char *argv[])
232 #endif
233 {
234           int res;
235           const char *argv0;
236 
237 #ifdef SHELL
238           argv0 = argv[0];
239 #else
240           setprogname(argv[0]);
241           (void)setlocale(LC_ALL, "");
242           argv0 = getprogname();
243 #endif
244           if (strcmp(argv0, "[") == 0) {
245                     if (strcmp(argv[--argc], "]"))
246                               error("missing ]");
247                     argv[argc] = NULL;
248           }
249 
250           /*
251            * POSIX defines operations of test for up to 4 args
252            * (depending upon what the args are in some cases)
253            *
254            * arg count does not include the command name, (but argc does)
255            * nor the closing ']' when the command was '[' (removed above)
256            *
257            * None of the following allow -a or -o as an operator (those
258            * only apply in the evaluation of unspeicified expressions)
259            *
260            * Note that the xxx_arg() functions return "shell" true/false
261            * (0 == true, 1 == false) or -1 for "unspecified case"
262            *
263            * Other functions return C true/false (1 == true, 0 == false)
264            *
265            * Hence we simply return the result from xxx_arg(), but
266            * invert the result of oexpr() below before returning it.
267            */
268           switch (argc - 1) {
269           case -1:            /* impossible, but never mind */
270           case 0:                       /* test $a    where a=''    false */
271                     return 1;
272 
273           case 1:                       /* test "$a" */
274                     return one_arg(argv[1]);                /* always works */
275 
276           case 2:                       /* test op "$a" */
277                     res = two_arg(argv[1], argv[2]);
278                     if (res >= 0)
279                               return res;
280                     break;
281 
282           case 3:                       /* test "$a" op "$b" or test ! op "$a" */
283                     res = three_arg(argv[1], argv[2], argv[3]);
284                     if (res >= 0)
285                               return res;
286                     break;
287 
288           case 4:                       /* test ! "$a" op "$b" or test ( op "$a" ) */
289                     res = four_arg(argv[1], argv[2], argv[3], argv[4]);
290                     if (res >= 0)
291                               return res;
292                     break;
293 
294           default:
295                     break;
296           }
297 
298           /*
299            * All other cases produce unspecified results
300            * (including cases above with small arg counts where the
301            * args are not what was expected to be seen)
302            *
303            * We fall back to the old method, of attempting to parse
304            * the expr (highly ambiguous as there is no distinction between
305            * operators and operands that happen to look like operators)
306            */
307 
308 #ifdef SMALL
309           error("unsupported expression when built with -DSMALL");
310 #else
311 
312           t_wp = &argv[1];
313           res = !oexpr(t_lex(*t_wp));
314 
315           if (*t_wp != NULL && *++t_wp != NULL)
316                     syntax(*t_wp, "unexpected operator");
317 
318           return res;
319 #endif
320 }
321 
322 #ifndef SMALL
323 static void
syntax(const char * op,const char * msg)324 syntax(const char *op, const char *msg)
325 {
326 
327           if (op && *op)
328                     error("%s: %s", op, msg);
329           else
330                     error("%s", msg);
331 }
332 #endif
333 
334 static int
one_arg(const char * arg)335 one_arg(const char *arg)
336 {
337           /*
338            * True (exit 0, so false...) if arg is not a null string
339            * False (so exit 1, so true) if it is.
340            */
341           return *arg == '\0';
342 }
343 
344 static int
two_arg(const char * a1,const char * a2)345 two_arg(const char *a1, const char *a2)
346 {
347           static struct t_op const *op;
348 
349           if (a1[0] == '!' && a1[1] == 0)
350                     return !one_arg(a2);
351 
352           op = findop(a1);
353           if (op != NULL && op->op_type == UNOP)
354                     return !perform_unop(op->op_num, a2);
355 
356 #ifndef TINY
357           /*
358            * an extension, but as we've entered the realm of the unspecified
359            * we're allowed...           test ( $a )         where a=''
360            */
361           if (a1[0] == '(' && a2[0] == ')' && (a1[1] | a2[1]) == 0)
362                     return 1;
363 #endif
364 
365           return -1;
366 }
367 
368 static int
three_arg(const char * a1,const char * a2,const char * a3)369 three_arg(const char *a1, const char *a2, const char *a3)
370 {
371           static struct t_op const *op;
372           int res;
373 
374           op = findop(a2);
375           if (op != NULL && op->op_type == BINOP)
376                     return !perform_binop(op->op_num, a1, a3);
377 
378           if (a1[1] != '\0')
379                     return -1;
380 
381           if (a1[0] == '!') {
382                     res = two_arg(a2, a3);
383                     if (res >= 0)
384                               res = !res;
385                     return res;
386           }
387 
388 #ifndef TINY
389           if (a1[0] == '(' && a3[0] == ')' && a3[1] == '\0')
390                     return one_arg(a2);
391 #endif
392 
393           return -1;
394 }
395 
396 static int
four_arg(const char * a1,const char * a2,const char * a3,const char * a4)397 four_arg(const char *a1, const char *a2, const char *a3, const char *a4)
398 {
399           int res;
400 
401           if (a1[1] != '\0')
402                     return -1;
403 
404           if (a1[0] == '!') {
405                     res = three_arg(a2, a3, a4);
406                     if (res >= 0)
407                               res = !res;
408                     return res;
409           }
410 
411 #ifndef TINY
412           if (a1[0] == '(' && a4[0] == ')' && a4[1] == '\0')
413                     return two_arg(a2, a3);
414 #endif
415 
416           return -1;
417 }
418 
419 #ifndef SMALL
420 static int
oexpr(enum token n)421 oexpr(enum token n)
422 {
423           int res;
424 
425           res = aexpr(n);
426           if (*t_wp == NULL)
427                     return res;
428           if (t_lex(*++t_wp) == BOR)
429                     return oexpr(t_lex(*++t_wp)) || res;
430           t_wp--;
431           return res;
432 }
433 
434 static int
aexpr(enum token n)435 aexpr(enum token n)
436 {
437           int res;
438 
439           res = nexpr(n);
440           if (*t_wp == NULL)
441                     return res;
442           if (t_lex(*++t_wp) == BAND)
443                     return aexpr(t_lex(*++t_wp)) && res;
444           t_wp--;
445           return res;
446 }
447 
448 static int
nexpr(enum token n)449 nexpr(enum token n)
450 {
451 
452           if (n == UNOT)
453                     return !nexpr(t_lex(*++t_wp));
454           return primary(n);
455 }
456 
457 static int
primary(enum token n)458 primary(enum token n)
459 {
460           enum token nn;
461           int res;
462 
463           if (n == EOI)
464                     return 0;           /* missing expression */
465           if (n == LPAREN) {
466                     if ((nn = t_lex(*++t_wp)) == RPAREN)
467                               return 0; /* missing expression */
468                     res = oexpr(nn);
469                     if (t_lex(*++t_wp) != RPAREN)
470                               syntax(NULL, "closing paren expected");
471                     return res;
472           }
473           if (t_wp_op && t_wp_op->op_type == UNOP) {
474                     /* unary expression */
475                     if (*++t_wp == NULL)
476                               syntax(t_wp_op->op_text, "argument expected");
477                     return perform_unop(n, *t_wp);
478           }
479 
480           if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
481                     return binop();
482           }
483 
484           return strlen(*t_wp) > 0;
485 }
486 #endif /* !SMALL */
487 
488 static int
perform_unop(enum token n,const char * opnd)489 perform_unop(enum token n, const char *opnd)
490 {
491           switch (n) {
492           case STREZ:
493                     return strlen(opnd) == 0;
494           case STRNZ:
495                     return strlen(opnd) != 0;
496           case FILTT:
497                     return isatty((int)getn(opnd));
498           default:
499                     return filstat(opnd, n);
500           }
501 }
502 
503 #ifndef SMALL
504 static int
binop(void)505 binop(void)
506 {
507           const char *opnd1, *opnd2;
508           struct t_op const *op;
509 
510           opnd1 = *t_wp;
511           (void) t_lex(*++t_wp);
512           op = t_wp_op;
513 
514           if ((opnd2 = *++t_wp) == NULL)
515                     syntax(op->op_text, "argument expected");
516 
517           return perform_binop(op->op_num, opnd1, opnd2);
518 }
519 #endif
520 
521 static int
perform_binop(enum token op_num,const char * opnd1,const char * opnd2)522 perform_binop(enum token op_num, const char *opnd1, const char *opnd2)
523 {
524           switch (op_num) {
525           case STREQ:
526                     return strcmp(opnd1, opnd2) == 0;
527           case STRNE:
528                     return strcmp(opnd1, opnd2) != 0;
529           case STRLT:
530                     return strcmp(opnd1, opnd2) < 0;
531           case STRGT:
532                     return strcmp(opnd1, opnd2) > 0;
533           case INTEQ:
534                     return getn(opnd1) == getn(opnd2);
535           case INTNE:
536                     return getn(opnd1) != getn(opnd2);
537           case INTGE:
538                     return getn(opnd1) >= getn(opnd2);
539           case INTGT:
540                     return getn(opnd1) > getn(opnd2);
541           case INTLE:
542                     return getn(opnd1) <= getn(opnd2);
543           case INTLT:
544                     return getn(opnd1) < getn(opnd2);
545           case FILNT:
546                     return newerf(opnd1, opnd2);
547           case FILOT:
548                     return olderf(opnd1, opnd2);
549           case FILEQ:
550                     return equalf(opnd1, opnd2);
551           default:
552                     abort();
553                     /* NOTREACHED */
554           }
555 }
556 
557 /*
558  * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits,
559  * not use access():
560  *
561  *        True shall indicate only that the write flag is on.  The file is not
562  *        writable on a read-only file system even if this test indicates true.
563  *
564  * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only:
565  *
566  *        True shall indicate that permission to read from file will be granted,
567  *        as defined in "File Read, Write, and Creation".
568  *
569  * and that section says:
570  *
571  *        When a file is to be read or written, the file shall be opened with an
572  *        access mode corresponding to the operation to be performed.  If file
573  *        access permissions deny access, the requested operation shall fail.
574  *
575  * and of course access permissions are described as one might expect:
576  *
577  *     * If a process has the appropriate privilege:
578  *
579  *        * If read, write, or directory search permission is requested,
580  *          access shall be granted.
581  *
582  *        * If execute permission is requested, access shall be granted if
583  *          execute permission is granted to at least one user by the file
584  *          permission bits or by an alternate access control mechanism;
585  *          otherwise, access shall be denied.
586  *
587  *   * Otherwise:
588  *
589  *        * The file permission bits of a file contain read, write, and
590  *          execute/search permissions for the file owner class, file group
591  *          class, and file other class.
592  *
593  *        * Access shall be granted if an alternate access control mechanism
594  *          is not enabled and the requested access permission bit is set for
595  *          the class (file owner class, file group class, or file other class)
596  *          to which the process belongs, or if an alternate access control
597  *          mechanism is enabled and it allows the requested access; otherwise,
598  *          access shall be denied.
599  *
600  * and when I first read this I thought:  surely we can't go about using
601  * open(O_WRONLY) to try this test!  However the POSIX 1003.1-2001 Rationale
602  * section for test does in fact say:
603  *
604  *        On historical BSD systems, test -w directory always returned false
605  *        because test tried to open the directory for writing, which always
606  *        fails.
607  *
608  * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX
609  * System III, and thus presumably also for BSD up to and including 4.3.
610  *
611  * Secondly I remembered why using open() and/or access() are bogus.  They
612  * don't work right for detecting read and write permissions bits when called
613  * by root.
614  *
615  * Interestingly the 'test' in 4.4BSD was closer to correct (as per
616  * 1003.2-1992) and it was implemented efficiently with stat() instead of
617  * open().
618  *
619  * This was apparently broken in NetBSD around about 1994/06/30 when the old
620  * 4.4BSD implementation was replaced with a (arguably much better coded)
621  * implementation derived from pdksh.
622  *
623  * Note that modern pdksh is yet different again, but still not correct, at
624  * least not w.r.t. 1003.2-1992.
625  *
626  * As I think more about it and read more of the related IEEE docs I don't like
627  * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all.  I very
628  * much prefer the original wording in 1003.2-1992.  It is much more useful,
629  * and so that's what I've implemented.
630  *
631  * (Note that a strictly conforming implementation of 1003.1-2001 is in fact
632  * totally useless for the case in question since its 'test -w' and 'test -r'
633  * can never fail for root for any existing files, i.e. files for which 'test
634  * -e' succeeds.)
635  *
636  * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
637  * 1003.1-2001 to align with the 1003.2b draft.  1003.2b Draft 12 (July 1999),
638  * which is the latest copy I have, does carry the same suggested wording as is
639  * in 1003.1-2001, with its rationale saying:
640  *
641  *        This change is a clarification and is the result of interpretation
642  *        request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
643  *
644  * That interpretation can be found here:
645  *
646  *   http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
647  *
648  * Not terribly helpful, unfortunately.  I wonder who that fence sitter was.
649  *
650  * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
651  * PASC interpretation and appear to be gone against at least one widely used
652  * implementation (namely 4.4BSD).  The problem is that for file access by root
653  * this means that if test '-r' and '-w' are to behave as if open() were called
654  * then there's no way for a shell script running as root to check if a file
655  * has certain access bits set other than by the grotty means of interpreting
656  * the output of 'ls -l'.  This was widely considered to be a bug in V7's
657  * "test" and is, I believe, one of the reasons why direct use of access() was
658  * avoided in some more recent implementations!
659  *
660  * I have always interpreted '-r' to match '-w' and '-x' as per the original
661  * wording in 1003.2-1992, not the other way around.  I think 1003.2b goes much
662  * too far the wrong way without any valid rationale and that it's best if we
663  * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
664  * open() since we already know very well how it will work -- existence of the
665  * file is all that matters to open() for root.
666  *
667  * Unfortunately the SVID is no help at all (which is, I guess, partly why
668  * we're in this mess in the first place :-).
669  *
670  * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
671  * access(name, 2) even though it also goes to much greater lengths for '-x'
672  * matching the 1003.2-1992 definition (which is no doubt where that definition
673  * came from).
674  *
675  * The ksh93 implementation uses access() for '-r' and '-w' if
676  * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
677  * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
678  */
679 static int
test_access(struct stat * sp,mode_t stmode)680 test_access(struct stat *sp, mode_t stmode)
681 {
682           gid_t *groups;
683           register int n;
684           uid_t euid;
685           int maxgroups;
686 
687           /*
688            * I suppose we could use access() if not running as root and if we are
689            * running with ((euid == uid) && (egid == gid)), but we've already
690            * done the stat() so we might as well just test the permissions
691            * directly instead of asking the kernel to do it....
692            */
693           euid = geteuid();
694           if (euid == 0)                                    /* any bit is good enough */
695                     stmode = (stmode << 6) | (stmode << 3) | stmode;
696           else if (sp->st_uid == euid)
697                     stmode <<= 6;
698           else if (sp->st_gid == getegid())
699                     stmode <<= 3;
700           else {
701                     /* XXX stolen almost verbatim from ksh93.... */
702                     /* on some systems you can be in several groups */
703                     if ((maxgroups = getgroups(0, NULL)) <= 0)
704                               maxgroups = NGROUPS_MAX;      /* pre-POSIX system? */
705                     groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
706                     n = getgroups(maxgroups, groups);
707                     while (--n >= 0) {
708                               if (groups[n] == sp->st_gid) {
709                                         stmode <<= 3;
710                                         break;
711                               }
712                     }
713                     free(groups);
714           }
715 
716           return sp->st_mode & stmode;
717 }
718 
719 static int
filstat(const char * nm,enum token mode)720 filstat(const char *nm, enum token mode)
721 {
722           struct stat s;
723 
724           if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
725                     return 0;
726 
727           switch (mode) {
728           case FILRD:
729                     return test_access(&s, S_IROTH);
730           case FILWR:
731                     return test_access(&s, S_IWOTH);
732           case FILEX:
733                     return test_access(&s, S_IXOTH);
734           case FILEXIST:
735                     return 1; /* the successful lstat()/stat() is good enough */
736           case FILREG:
737                     return S_ISREG(s.st_mode);
738           case FILDIR:
739                     return S_ISDIR(s.st_mode);
740           case FILCDEV:
741                     return S_ISCHR(s.st_mode);
742           case FILBDEV:
743                     return S_ISBLK(s.st_mode);
744           case FILFIFO:
745                     return S_ISFIFO(s.st_mode);
746           case FILSOCK:
747                     return S_ISSOCK(s.st_mode);
748           case FILSYM:
749                     return S_ISLNK(s.st_mode);
750           case FILSUID:
751                     return (s.st_mode & S_ISUID) != 0;
752           case FILSGID:
753                     return (s.st_mode & S_ISGID) != 0;
754           case FILSTCK:
755                     return (s.st_mode & S_ISVTX) != 0;
756           case FILGZ:
757                     return s.st_size > (off_t)0;
758           case FILUID:
759                     return s.st_uid == geteuid();
760           case FILGID:
761                     return s.st_gid == getegid();
762           default:
763                     return 1;
764           }
765 }
766 
767 #define VTOC(x)     (const unsigned char *)((const struct t_op *)x)->op_text
768 
769 static int
compare1(const void * va,const void * vb)770 compare1(const void *va, const void *vb)
771 {
772           const unsigned char *a = va;
773           const unsigned char *b = VTOC(vb);
774 
775           return a[0] - b[0];
776 }
777 
778 static int
compare2(const void * va,const void * vb)779 compare2(const void *va, const void *vb)
780 {
781           const unsigned char *a = va;
782           const unsigned char *b = VTOC(vb);
783           int z = a[0] - b[0];
784 
785           return z ? z : (a[1] - b[1]);
786 }
787 
788 static struct t_op const *
findop(const char * s)789 findop(const char *s)
790 {
791           if (s[0] == '-') {
792                     if (s[1] == '\0')
793                               return NULL;
794                     if (s[2] == '\0')
795                               return bsearch(s + 1, mop2, __arraycount(mop2),
796                                   sizeof(*mop2), compare1);
797                     else if (s[3] != '\0')
798                               return NULL;
799                     else
800                               return bsearch(s + 1, mop3, __arraycount(mop3),
801                                   sizeof(*mop3), compare2);
802           } else {
803                     if (s[1] == '\0')
804                               return bsearch(s, cop, __arraycount(cop), sizeof(*cop),
805                                   compare1);
806                     else if (strcmp(s, cop2[0].op_text) == 0)
807                               return cop2;
808                     else
809                               return NULL;
810           }
811 }
812 
813 #ifndef SMALL
814 static enum token
t_lex(char * s)815 t_lex(char *s)
816 {
817           struct t_op const *op;
818 
819           if (s == NULL) {
820                     t_wp_op = NULL;
821                     return EOI;
822           }
823 
824           if ((op = findop(s)) != NULL) {
825                     if (!((op->op_type == UNOP && isoperand()) ||
826                         (op->op_num == LPAREN && *(t_wp+1) == 0))) {
827                               t_wp_op = op;
828                               return op->op_num;
829                     }
830           }
831           t_wp_op = NULL;
832           return OPERAND;
833 }
834 
835 static int
isoperand(void)836 isoperand(void)
837 {
838           struct t_op const *op;
839           char *s, *t;
840 
841           if ((s  = *(t_wp+1)) == 0)
842                     return 1;
843           if ((t = *(t_wp+2)) == 0)
844                     return 0;
845           if ((op = findop(s)) != NULL)
846                     return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
847           return 0;
848 }
849 #endif
850 
851 /* atoi with error detection */
852 static long long
getn(const char * s)853 getn(const char *s)
854 {
855           char *p;
856           long long r;
857 
858           errno = 0;
859           r = strtoll(s, &p, 10);
860 
861           if (errno != 0)
862           if (errno == ERANGE && (r == LLONG_MAX || r == LLONG_MIN))
863                 error("%s: out of range", s);
864 
865           if (p != s)
866                     while (isspace((unsigned char)*p))
867                           p++;
868 
869           if (*p || p == s)
870                 error("'%s': bad number", s);
871 
872           return r;
873 }
874 
875 static int
newerf(const char * f1,const char * f2)876 newerf(const char *f1, const char *f2)
877 {
878           struct stat b1, b2;
879 
880           return (stat(f1, &b1) == 0 &&
881                     stat(f2, &b2) == 0 &&
882                     timespeccmp(&b1.st_mtim, &b2.st_mtim, >));
883 }
884 
885 static int
olderf(const char * f1,const char * f2)886 olderf(const char *f1, const char *f2)
887 {
888           struct stat b1, b2;
889 
890           return (stat(f1, &b1) == 0 &&
891                     stat(f2, &b2) == 0 &&
892                     timespeccmp(&b1.st_mtim, &b2.st_mtim, <));
893 }
894 
895 static int
equalf(const char * f1,const char * f2)896 equalf(const char *f1, const char *f2)
897 {
898           struct stat b1, b2;
899 
900           return (stat(f1, &b1) == 0 &&
901                     stat(f2, &b2) == 0 &&
902                     b1.st_dev == b2.st_dev &&
903                     b1.st_ino == b2.st_ino);
904 }
905