]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/test/test.c
Import libyaml as libbsdyml (private brand name)
[FreeBSD/FreeBSD.git] / bin / test / test.c
1 /*      $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink 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  * Important: This file is used both as a standalone program /bin/test and
14  * as a builtin for /bin/sh (#define SHELL).
15  */
16
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 #include <ctype.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <inttypes.h>
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #ifdef SHELL
35 #define main testcmd
36 #include "bltin/bltin.h"
37 #else
38 #include <locale.h>
39
40 static void error(const char *, ...) __dead2 __printf0like(1, 2);
41
42 static void
43 error(const char *msg, ...)
44 {
45         va_list ap;
46         va_start(ap, msg);
47         verrx(2, msg, ap);
48         /*NOTREACHED*/
49         va_end(ap);
50 }
51 #endif
52
53 /* test(1) accepts the following grammar:
54         oexpr   ::= aexpr | aexpr "-o" oexpr ;
55         aexpr   ::= nexpr | nexpr "-a" aexpr ;
56         nexpr   ::= primary | "!" primary
57         primary ::= unary-operator operand
58                 | operand binary-operator operand
59                 | operand
60                 | "(" oexpr ")"
61                 ;
62         unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
63                 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
64
65         binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
66                         "-nt"|"-nt[abcm][abcm]"|"-ot"|"-ot[abcm][abcm])"|"-ef";
67         operand ::= <any legal UNIX file name>
68 */
69
70 enum token {
71         EOI,
72         FILRD,
73         FILWR,
74         FILEX,
75         FILEXIST,
76         FILREG,
77         FILDIR,
78         FILCDEV,
79         FILBDEV,
80         FILFIFO,
81         FILSOCK,
82         FILSYM,
83         FILGZ,
84         FILTT,
85         FILSUID,
86         FILSGID,
87         FILSTCK,
88         FILNTAA,
89         FILNTAB,
90         FILNTAC,
91         FILNTAM,
92         FILNTBA,
93         FILNTBB,
94         FILNTBC,
95         FILNTBM,
96         FILNTCA,
97         FILNTCB,
98         FILNTCC,
99         FILNTCM,
100         FILNTMA,
101         FILNTMB,
102         FILNTMC,
103         FILNTMM,
104         FILOTAA,
105         FILOTAB,
106         FILOTAC,
107         FILOTAM,
108         FILOTBA,
109         FILOTBB,
110         FILOTBC,
111         FILOTBM,
112         FILOTCA,
113         FILOTCB,
114         FILOTCC,
115         FILOTCM,
116         FILOTMA,
117         FILOTMB,
118         FILOTMC,
119         FILOTMM,
120         FILEQ,
121         FILUID,
122         FILGID,
123         STREZ,
124         STRNZ,
125         STREQ,
126         STRNE,
127         STRLT,
128         STRGT,
129         INTEQ,
130         INTNE,
131         INTGE,
132         INTGT,
133         INTLE,
134         INTLT,
135         UNOT,
136         BAND,
137         BOR,
138         LPAREN,
139         RPAREN,
140         OPERAND
141 };
142
143 enum token_types {
144         UNOP,
145         BINOP,
146         BUNOP,
147         BBINOP,
148         PAREN
149 };
150
151 enum time_types {
152         ATIME,
153         BTIME,
154         CTIME,
155         MTIME
156 };
157
158 static struct t_op {
159         char op_text[6];
160         char op_num, op_type;
161 } const ops [] = {
162         {"-r",  FILRD,  UNOP},
163         {"-w",  FILWR,  UNOP},
164         {"-x",  FILEX,  UNOP},
165         {"-e",  FILEXIST,UNOP},
166         {"-f",  FILREG, UNOP},
167         {"-d",  FILDIR, UNOP},
168         {"-c",  FILCDEV,UNOP},
169         {"-b",  FILBDEV,UNOP},
170         {"-p",  FILFIFO,UNOP},
171         {"-u",  FILSUID,UNOP},
172         {"-g",  FILSGID,UNOP},
173         {"-k",  FILSTCK,UNOP},
174         {"-s",  FILGZ,  UNOP},
175         {"-t",  FILTT,  UNOP},
176         {"-z",  STREZ,  UNOP},
177         {"-n",  STRNZ,  UNOP},
178         {"-h",  FILSYM, UNOP},          /* for backwards compat */
179         {"-O",  FILUID, UNOP},
180         {"-G",  FILGID, UNOP},
181         {"-L",  FILSYM, UNOP},
182         {"-S",  FILSOCK,UNOP},
183         {"=",   STREQ,  BINOP},
184         {"==",  STREQ,  BINOP},
185         {"!=",  STRNE,  BINOP},
186         {"<",   STRLT,  BINOP},
187         {">",   STRGT,  BINOP},
188         {"-eq", INTEQ,  BINOP},
189         {"-ne", INTNE,  BINOP},
190         {"-ge", INTGE,  BINOP},
191         {"-gt", INTGT,  BINOP},
192         {"-le", INTLE,  BINOP},
193         {"-lt", INTLT,  BINOP},
194         {"-nt", FILNTMM,        BINOP},
195         {"-ntaa",       FILNTAA,        BINOP},
196         {"-ntab",       FILNTAB,        BINOP},
197         {"-ntac",       FILNTAC,        BINOP},
198         {"-ntam",       FILNTAM,        BINOP},
199         {"-ntba",       FILNTBA,        BINOP},
200         {"-ntbb",       FILNTBB,        BINOP},
201         {"-ntbc",       FILNTBC,        BINOP},
202         {"-ntbm",       FILNTBM,        BINOP},
203         {"-ntca",       FILNTCA,        BINOP},
204         {"-ntcb",       FILNTCB,        BINOP},
205         {"-ntcc",       FILNTCC,        BINOP},
206         {"-ntcm",       FILNTCM,        BINOP},
207         {"-ntma",       FILNTMA,        BINOP},
208         {"-ntmb",       FILNTMB,        BINOP},
209         {"-ntmc",       FILNTMC,        BINOP},
210         {"-ntmm",       FILNTMM,        BINOP},
211         {"-ot", FILOTMM,        BINOP},
212         {"-otaa",       FILOTAA,        BINOP},
213         {"-otab",       FILOTBB,        BINOP},
214         {"-otac",       FILOTAC,        BINOP},
215         {"-otam",       FILOTAM,        BINOP},
216         {"-otba",       FILOTBA,        BINOP},
217         {"-otbb",       FILOTBB,        BINOP},
218         {"-otbc",       FILOTBC,        BINOP},
219         {"-otbm",       FILOTBM,        BINOP},
220         {"-otca",       FILOTCA,        BINOP},
221         {"-otcb",       FILOTCB,        BINOP},
222         {"-otcc",       FILOTCC,        BINOP},
223         {"-otcm",       FILOTCM,        BINOP},
224         {"-otma",       FILOTMA,        BINOP},
225         {"-otmb",       FILOTMB,        BINOP},
226         {"-otmc",       FILOTMC,        BINOP},
227         {"-otmm",       FILOTMM,        BINOP},
228         {"-ef", FILEQ,  BINOP},
229         {"!",   UNOT,   BUNOP},
230         {"-a",  BAND,   BBINOP},
231         {"-o",  BOR,    BBINOP},
232         {"(",   LPAREN, PAREN},
233         {")",   RPAREN, PAREN},
234         {"",    0,      0}
235 };
236
237 static struct t_op const *t_wp_op;
238 static int nargc;
239 static char **t_wp;
240 static int parenlevel;
241
242 static int      aexpr(enum token);
243 static int      binop(void);
244 static int      equalf(const char *, const char *);
245 static int      filstat(char *, enum token);
246 static int      getn(const char *);
247 static intmax_t getq(const char *);
248 static int      intcmp(const char *, const char *);
249 static int      isunopoperand(void);
250 static int      islparenoperand(void);
251 static int      isrparenoperand(void);
252 static int      newerf(const char *, const char *, enum time_types,
253                        enum time_types);
254 static int      nexpr(enum token);
255 static int      oexpr(enum token);
256 static int      primary(enum token);
257 static void     syntax(const char *, const char *);
258 static enum     token t_lex(char *);
259
260 int
261 main(int argc, char **argv)
262 {
263         int     res;
264         char    *p;
265
266         if ((p = strrchr(argv[0], '/')) == NULL)
267                 p = argv[0];
268         else
269                 p++;
270         if (strcmp(p, "[") == 0) {
271                 if (strcmp(argv[--argc], "]") != 0)
272                         error("missing ]");
273                 argv[argc] = NULL;
274         }
275
276         /* no expression => false */
277         if (--argc <= 0)
278                 return 1;
279
280 #ifndef SHELL
281         (void)setlocale(LC_CTYPE, "");
282 #endif
283         nargc = argc;
284         t_wp = &argv[1];
285         parenlevel = 0;
286         if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
287                 /* Things like ! "" -o x do not fit in the normal grammar. */
288                 --nargc;
289                 ++t_wp;
290                 res = oexpr(t_lex(*t_wp));
291         } else
292                 res = !oexpr(t_lex(*t_wp));
293
294         if (--nargc > 0)
295                 syntax(*t_wp, "unexpected operator");
296
297         return res;
298 }
299
300 static void
301 syntax(const char *op, const char *msg)
302 {
303
304         if (op && *op)
305                 error("%s: %s", op, msg);
306         else
307                 error("%s", msg);
308 }
309
310 static int
311 oexpr(enum token n)
312 {
313         int res;
314
315         res = aexpr(n);
316         if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
317                 return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
318                     res;
319         t_wp--;
320         nargc++;
321         return res;
322 }
323
324 static int
325 aexpr(enum token n)
326 {
327         int res;
328
329         res = nexpr(n);
330         if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
331                 return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
332                     res;
333         t_wp--;
334         nargc++;
335         return res;
336 }
337
338 static int
339 nexpr(enum token n)
340 {
341         if (n == UNOT)
342                 return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
343         return primary(n);
344 }
345
346 static int
347 primary(enum token n)
348 {
349         enum token nn;
350         int res;
351
352         if (n == EOI)
353                 return 0;               /* missing expression */
354         if (n == LPAREN) {
355                 parenlevel++;
356                 if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
357                     RPAREN) {
358                         parenlevel--;
359                         return 0;       /* missing expression */
360                 }
361                 res = oexpr(nn);
362                 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
363                         syntax(NULL, "closing paren expected");
364                 parenlevel--;
365                 return res;
366         }
367         if (t_wp_op && t_wp_op->op_type == UNOP) {
368                 /* unary expression */
369                 if (--nargc == 0)
370                         syntax(t_wp_op->op_text, "argument expected");
371                 switch (n) {
372                 case STREZ:
373                         return strlen(*++t_wp) == 0;
374                 case STRNZ:
375                         return strlen(*++t_wp) != 0;
376                 case FILTT:
377                         return isatty(getn(*++t_wp));
378                 default:
379                         return filstat(*++t_wp, n);
380                 }
381         }
382
383         if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type ==
384             BINOP) {
385                 return binop();
386         }
387
388         return strlen(*t_wp) > 0;
389 }
390
391 static int
392 binop(void)
393 {
394         const char *opnd1, *opnd2;
395         struct t_op const *op;
396
397         opnd1 = *t_wp;
398         (void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL);
399         op = t_wp_op;
400
401         if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
402                 syntax(op->op_text, "argument expected");
403
404         switch (op->op_num) {
405         case STREQ:
406                 return strcmp(opnd1, opnd2) == 0;
407         case STRNE:
408                 return strcmp(opnd1, opnd2) != 0;
409         case STRLT:
410                 return strcmp(opnd1, opnd2) < 0;
411         case STRGT:
412                 return strcmp(opnd1, opnd2) > 0;
413         case INTEQ:
414                 return intcmp(opnd1, opnd2) == 0;
415         case INTNE:
416                 return intcmp(opnd1, opnd2) != 0;
417         case INTGE:
418                 return intcmp(opnd1, opnd2) >= 0;
419         case INTGT:
420                 return intcmp(opnd1, opnd2) > 0;
421         case INTLE:
422                 return intcmp(opnd1, opnd2) <= 0;
423         case INTLT:
424                 return intcmp(opnd1, opnd2) < 0;
425         case FILNTAA:
426                 return newerf(opnd1, opnd2, ATIME, ATIME);
427         case FILNTAB:
428                 return newerf(opnd1, opnd2, ATIME, BTIME);
429         case FILNTAC:
430                 return newerf(opnd1, opnd2, ATIME, CTIME);
431         case FILNTAM:
432                 return newerf(opnd1, opnd2, ATIME, MTIME);
433         case FILNTBA:
434                 return newerf(opnd1, opnd2, BTIME, ATIME);
435         case FILNTBB:
436                 return newerf(opnd1, opnd2, BTIME, BTIME);
437         case FILNTBC:
438                 return newerf(opnd1, opnd2, BTIME, CTIME);
439         case FILNTBM:
440                 return newerf(opnd1, opnd2, BTIME, MTIME);
441         case FILNTCA:
442                 return newerf(opnd1, opnd2, CTIME, ATIME);
443         case FILNTCB:
444                 return newerf(opnd1, opnd2, CTIME, BTIME);
445         case FILNTCC:
446                 return newerf(opnd1, opnd2, CTIME, CTIME);
447         case FILNTCM:
448                 return newerf(opnd1, opnd2, CTIME, MTIME);
449         case FILNTMA:
450                 return newerf(opnd1, opnd2, MTIME, ATIME);
451         case FILNTMB:
452                 return newerf(opnd1, opnd2, MTIME, BTIME);
453         case FILNTMC:
454                 return newerf(opnd1, opnd2, MTIME, CTIME);
455         case FILNTMM:
456                 return newerf(opnd1, opnd2, MTIME, MTIME);
457         case FILOTAA:
458                 return newerf(opnd2, opnd1, ATIME, ATIME);
459         case FILOTAB:
460                 return newerf(opnd2, opnd1, BTIME, ATIME);
461         case FILOTAC:
462                 return newerf(opnd2, opnd1, CTIME, ATIME);
463         case FILOTAM:
464                 return newerf(opnd2, opnd1, MTIME, ATIME);
465         case FILOTBA:
466                 return newerf(opnd2, opnd1, ATIME, BTIME);
467         case FILOTBB:
468                 return newerf(opnd2, opnd1, BTIME, BTIME);
469         case FILOTBC:
470                 return newerf(opnd2, opnd1, CTIME, BTIME);
471         case FILOTBM:
472                 return newerf(opnd2, opnd1, MTIME, BTIME);
473         case FILOTCA:
474                 return newerf(opnd2, opnd1, ATIME, CTIME);
475         case FILOTCB:
476                 return newerf(opnd2, opnd1, BTIME, CTIME);
477         case FILOTCC:
478                 return newerf(opnd2, opnd1, CTIME, CTIME);
479         case FILOTCM:
480                 return newerf(opnd2, opnd1, MTIME, CTIME);
481         case FILOTMA:
482                 return newerf(opnd2, opnd1, ATIME, MTIME);
483         case FILOTMB:
484                 return newerf(opnd2, opnd1, BTIME, MTIME);
485         case FILOTMC:
486                 return newerf(opnd2, opnd1, CTIME, MTIME);
487         case FILOTMM:
488                 return newerf(opnd2, opnd1, MTIME, MTIME);
489         case FILEQ:
490                 return equalf (opnd1, opnd2);
491         default:
492                 abort();
493                 /* NOTREACHED */
494         }
495 }
496
497 static int
498 filstat(char *nm, enum token mode)
499 {
500         struct stat s;
501
502         if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
503                 return 0;
504
505         switch (mode) {
506         case FILRD:
507                 return (eaccess(nm, R_OK) == 0);
508         case FILWR:
509                 return (eaccess(nm, W_OK) == 0);
510         case FILEX:
511                 /* XXX work around eaccess(2) false positives for superuser */
512                 if (eaccess(nm, X_OK) != 0)
513                         return 0;
514                 if (S_ISDIR(s.st_mode) || geteuid() != 0)
515                         return 1;
516                 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
517         case FILEXIST:
518                 return (eaccess(nm, F_OK) == 0);
519         case FILREG:
520                 return S_ISREG(s.st_mode);
521         case FILDIR:
522                 return S_ISDIR(s.st_mode);
523         case FILCDEV:
524                 return S_ISCHR(s.st_mode);
525         case FILBDEV:
526                 return S_ISBLK(s.st_mode);
527         case FILFIFO:
528                 return S_ISFIFO(s.st_mode);
529         case FILSOCK:
530                 return S_ISSOCK(s.st_mode);
531         case FILSYM:
532                 return S_ISLNK(s.st_mode);
533         case FILSUID:
534                 return (s.st_mode & S_ISUID) != 0;
535         case FILSGID:
536                 return (s.st_mode & S_ISGID) != 0;
537         case FILSTCK:
538                 return (s.st_mode & S_ISVTX) != 0;
539         case FILGZ:
540                 return s.st_size > (off_t)0;
541         case FILUID:
542                 return s.st_uid == geteuid();
543         case FILGID:
544                 return s.st_gid == getegid();
545         default:
546                 return 1;
547         }
548 }
549
550 static enum token
551 t_lex(char *s)
552 {
553         struct t_op const *op = ops;
554
555         if (s == 0) {
556                 t_wp_op = NULL;
557                 return EOI;
558         }
559         while (*op->op_text) {
560                 if (strcmp(s, op->op_text) == 0) {
561                         if (((op->op_type == UNOP || op->op_type == BUNOP)
562                                                 && isunopoperand()) ||
563                             (op->op_num == LPAREN && islparenoperand()) ||
564                             (op->op_num == RPAREN && isrparenoperand()))
565                                 break;
566                         t_wp_op = op;
567                         return op->op_num;
568                 }
569                 op++;
570         }
571         t_wp_op = NULL;
572         return OPERAND;
573 }
574
575 static int
576 isunopoperand(void)
577 {
578         struct t_op const *op = ops;
579         char *s;
580         char *t;
581
582         if (nargc == 1)
583                 return 1;
584         s = *(t_wp + 1);
585         if (nargc == 2)
586                 return parenlevel == 1 && strcmp(s, ")") == 0;
587         t = *(t_wp + 2);
588         while (*op->op_text) {
589                 if (strcmp(s, op->op_text) == 0)
590                         return op->op_type == BINOP &&
591                             (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
592                 op++;
593         }
594         return 0;
595 }
596
597 static int
598 islparenoperand(void)
599 {
600         struct t_op const *op = ops;
601         char *s;
602
603         if (nargc == 1)
604                 return 1;
605         s = *(t_wp + 1);
606         if (nargc == 2)
607                 return parenlevel == 1 && strcmp(s, ")") == 0;
608         if (nargc != 3)
609                 return 0;
610         while (*op->op_text) {
611                 if (strcmp(s, op->op_text) == 0)
612                         return op->op_type == BINOP;
613                 op++;
614         }
615         return 0;
616 }
617
618 static int
619 isrparenoperand(void)
620 {
621         char *s;
622
623         if (nargc == 1)
624                 return 0;
625         s = *(t_wp + 1);
626         if (nargc == 2)
627                 return parenlevel == 1 && strcmp(s, ")") == 0;
628         return 0;
629 }
630
631 /* atoi with error detection */
632 static int
633 getn(const char *s)
634 {
635         char *p;
636         long r;
637
638         errno = 0;
639         r = strtol(s, &p, 10);
640
641         if (s == p)
642                 error("%s: bad number", s);
643
644         if (errno != 0)
645                 error((errno == EINVAL) ? "%s: bad number" :
646                                           "%s: out of range", s);
647
648         while (isspace((unsigned char)*p))
649                 p++;
650
651         if (*p)
652                 error("%s: bad number", s);
653
654         return (int) r;
655 }
656
657 /* atoi with error detection and 64 bit range */
658 static intmax_t
659 getq(const char *s)
660 {
661         char *p;
662         intmax_t r;
663
664         errno = 0;
665         r = strtoimax(s, &p, 10);
666
667         if (s == p)
668                 error("%s: bad number", s);
669
670         if (errno != 0)
671                 error((errno == EINVAL) ? "%s: bad number" :
672                                           "%s: out of range", s);
673
674         while (isspace((unsigned char)*p))
675                 p++;
676
677         if (*p)
678                 error("%s: bad number", s);
679
680         return r;
681 }
682
683 static int
684 intcmp (const char *s1, const char *s2)
685 {
686         intmax_t q1, q2;
687
688
689         q1 = getq(s1);
690         q2 = getq(s2);
691
692         if (q1 > q2)
693                 return 1;
694
695         if (q1 < q2)
696                 return -1;
697
698         return 0;
699 }
700
701 static int
702 newerf (const char *f1, const char *f2, enum time_types t1, enum time_types t2)
703 {
704         struct stat b1, b2;
705         struct timespec *ts1, *ts2;
706
707         if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
708                 return 0;
709
710         switch (t1) {
711         case ATIME:     ts1 = &b1.st_atim;      break;
712         case BTIME:     ts1 = &b1.st_birthtim;  break;
713         case CTIME:     ts1 = &b1.st_ctim;      break;
714         default:        ts1 = &b1.st_mtim;      break;
715         }
716
717         switch (t2) {
718         case ATIME:     ts2 = &b2.st_atim;      break;
719         case BTIME:     ts2 = &b2.st_birthtim;  break;
720         case CTIME:     ts2 = &b2.st_ctim;      break;
721         default:        ts2 = &b2.st_mtim;      break;
722         }
723
724         if (ts1->tv_sec > ts2->tv_sec)
725                 return 1;
726         if (ts1->tv_sec < ts2->tv_sec)
727                 return 0;
728
729        return (ts1->tv_nsec > ts2->tv_nsec);
730 }
731
732 static int
733 equalf (const char *f1, const char *f2)
734 {
735         struct stat b1, b2;
736
737         return (stat (f1, &b1) == 0 &&
738                 stat (f2, &b2) == 0 &&
739                 b1.st_dev == b2.st_dev &&
740                 b1.st_ino == b2.st_ino);
741 }