Merge branch 'vendor/OPENSSL'
[dragonfly.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  * $FreeBSD: head/bin/test/test.c 251208 2013-05-31 22:54:20Z jilles $
13  */
14 /*
15  * Important: This file is used both as a standalone program /bin/test and
16  * as a builtin for /bin/sh (#define SHELL).
17  */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21
22 #include <ctype.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <inttypes.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #ifdef SHELL
34 #define main testcmd
35 #include "bltin/bltin.h"
36 #include "error.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
47         va_start(ap, msg);
48         verrx(2, msg, ap);
49         /*NOTREACHED*/
50         va_end(ap);
51 }
52 #endif
53
54 /* test(1) accepts the following grammar:
55         oexpr   ::= aexpr | aexpr "-o" oexpr ;
56         aexpr   ::= nexpr | nexpr "-a" aexpr ;
57         nexpr   ::= primary | "!" primary
58         primary ::= unary-operator operand
59                 | operand binary-operator operand
60                 | operand
61                 | "(" oexpr ")"
62                 ;
63         unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
64                 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
65
66         binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
67                         "-nt"|"-ot"|"-ef";
68         operand ::= <any legal UNIX file name>
69 */
70
71 enum token {
72         EOI,
73         FILRD,
74         FILWR,
75         FILEX,
76         FILEXIST,
77         FILREG,
78         FILDIR,
79         FILCDEV,
80         FILBDEV,
81         FILFIFO,
82         FILSOCK,
83         FILSYM,
84         FILGZ,
85         FILTT,
86         FILSUID,
87         FILSGID,
88         FILSTCK,
89         FILNT,
90         FILOT,
91         FILEQ,
92         FILUID,
93         FILGID,
94         STREZ,
95         STRNZ,
96         STREQ,
97         STRNE,
98         STRLT,
99         STRGT,
100         INTEQ,
101         INTNE,
102         INTGE,
103         INTGT,
104         INTLE,
105         INTLT,
106         UNOT,
107         BAND,
108         BOR,
109         LPAREN,
110         RPAREN,
111         OPERAND
112 };
113
114 enum token_types {
115         UNOP,
116         BINOP,
117         BUNOP,
118         BBINOP,
119         PAREN
120 };
121
122 static struct t_op {
123         char op_text[4];
124         short op_num, op_type;
125 } const ops [] = {
126         {"-r",  FILRD,  UNOP},
127         {"-w",  FILWR,  UNOP},
128         {"-x",  FILEX,  UNOP},
129         {"-e",  FILEXIST,UNOP},
130         {"-f",  FILREG, UNOP},
131         {"-d",  FILDIR, UNOP},
132         {"-c",  FILCDEV,UNOP},
133         {"-b",  FILBDEV,UNOP},
134         {"-p",  FILFIFO,UNOP},
135         {"-u",  FILSUID,UNOP},
136         {"-g",  FILSGID,UNOP},
137         {"-k",  FILSTCK,UNOP},
138         {"-s",  FILGZ,  UNOP},
139         {"-t",  FILTT,  UNOP},
140         {"-z",  STREZ,  UNOP},
141         {"-n",  STRNZ,  UNOP},
142         {"-h",  FILSYM, UNOP},          /* for backwards compat */
143         {"-O",  FILUID, UNOP},
144         {"-G",  FILGID, UNOP},
145         {"-L",  FILSYM, UNOP},
146         {"-S",  FILSOCK,UNOP},
147         {"=",   STREQ,  BINOP},
148         {"==",  STREQ,  BINOP},
149         {"!=",  STRNE,  BINOP},
150         {"<",   STRLT,  BINOP},
151         {">",   STRGT,  BINOP},
152         {"-eq", INTEQ,  BINOP},
153         {"-ne", INTNE,  BINOP},
154         {"-ge", INTGE,  BINOP},
155         {"-gt", INTGT,  BINOP},
156         {"-le", INTLE,  BINOP},
157         {"-lt", INTLT,  BINOP},
158         {"-nt", FILNT,  BINOP},
159         {"-ot", FILOT,  BINOP},
160         {"-ef", FILEQ,  BINOP},
161         {"!",   UNOT,   BUNOP},
162         {"-a",  BAND,   BBINOP},
163         {"-o",  BOR,    BBINOP},
164         {"(",   LPAREN, PAREN},
165         {")",   RPAREN, PAREN},
166         {"",    0,      0}
167 };
168
169 static struct t_op const *t_wp_op;
170 static int nargc;
171 static char **t_wp;
172 static int parenlevel;
173
174 static int      aexpr(enum token);
175 static int      binop(void);
176 static int      equalf(const char *, const char *);
177 static int      filstat(char *, enum token);
178 static int      getn(const char *);
179 static intmax_t getq(const char *);
180 static int      intcmp(const char *, const char *);
181 static int      isunopoperand(void);
182 static int      islparenoperand(void);
183 static int      isrparenoperand(void);
184 static int      newerf(const char *, const char *);
185 static int      nexpr(enum token);
186 static int      oexpr(enum token);
187 static int      olderf(const char *, const char *);
188 static int      primary(enum token);
189 static void     syntax(const char *, const char *);
190 static enum     token t_lex(char *);
191
192 int
193 main(int argc, char **argv)
194 {
195         int     res;
196         char    *p;
197
198         if ((p = strrchr(argv[0], '/')) == NULL)
199                 p = argv[0];
200         else
201                 p++;
202         if (strcmp(p, "[") == 0) {
203                 if (strcmp(argv[--argc], "]") != 0)
204                         error("missing ]");
205                 argv[argc] = NULL;
206         }
207
208         /* no expression => false */
209         if (--argc <= 0)
210                 return 1;
211
212 #ifndef SHELL
213         setlocale(LC_CTYPE, "");
214 #endif
215         nargc = argc;
216         t_wp = &argv[1];
217         parenlevel = 0;
218         if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
219                 /* Things like ! "" -o x do not fit in the normal grammar. */
220                 --nargc;
221                 ++t_wp;
222                 res = oexpr(t_lex(*t_wp));
223         } else
224                 res = !oexpr(t_lex(*t_wp));
225
226         if (--nargc > 0)
227                 syntax(*t_wp, "unexpected operator");
228
229         return res;
230 }
231
232 static void
233 syntax(const char *op, const char *msg)
234 {
235
236         if (op && *op)
237                 error("%s: %s", op, msg);
238         else
239                 error("%s", msg);
240 }
241
242 static int
243 oexpr(enum token n)
244 {
245         int res;
246
247         res = aexpr(n);
248         if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
249                 return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
250                     res;
251         t_wp--;
252         nargc++;
253         return res;
254 }
255
256 static int
257 aexpr(enum token n)
258 {
259         int res;
260
261         res = nexpr(n);
262         if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
263                 return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
264                     res;
265         t_wp--;
266         nargc++;
267         return res;
268 }
269
270 static int
271 nexpr(enum token n)
272 {
273         if (n == UNOT)
274                 return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
275         return primary(n);
276 }
277
278 static int
279 primary(enum token n)
280 {
281         enum token nn;
282         int res;
283
284         if (n == EOI)
285                 return 0;               /* missing expression */
286         if (n == LPAREN) {
287                 parenlevel++;
288                 if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
289                     RPAREN) {
290                         parenlevel--;
291                         return 0;       /* missing expression */
292                 }
293                 res = oexpr(nn);
294                 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
295                         syntax(NULL, "closing paren expected");
296                 parenlevel--;
297                 return res;
298         }
299         if (t_wp_op && t_wp_op->op_type == UNOP) {
300                 /* unary expression */
301                 if (--nargc == 0)
302                         syntax(t_wp_op->op_text, "argument expected");
303                 switch (n) {
304                 case STREZ:
305                         return strlen(*++t_wp) == 0;
306                 case STRNZ:
307                         return strlen(*++t_wp) != 0;
308                 case FILTT:
309                         return isatty(getn(*++t_wp));
310                 default:
311                         return filstat(*++t_wp, n);
312                 }
313         }
314
315         if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type ==
316             BINOP) {
317                 return binop();
318         }
319
320         return strlen(*t_wp) > 0;
321 }
322
323 static int
324 binop(void)
325 {
326         const char *opnd1, *opnd2;
327         struct t_op const *op;
328
329         opnd1 = *t_wp;
330         t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL);
331         op = t_wp_op;
332
333         if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
334                 syntax(op->op_text, "argument expected");
335
336         switch (op->op_num) {
337         case STREQ:
338                 return strcmp(opnd1, opnd2) == 0;
339         case STRNE:
340                 return strcmp(opnd1, opnd2) != 0;
341         case STRLT:
342                 return strcmp(opnd1, opnd2) < 0;
343         case STRGT:
344                 return strcmp(opnd1, opnd2) > 0;
345         case INTEQ:
346                 return intcmp(opnd1, opnd2) == 0;
347         case INTNE:
348                 return intcmp(opnd1, opnd2) != 0;
349         case INTGE:
350                 return intcmp(opnd1, opnd2) >= 0;
351         case INTGT:
352                 return intcmp(opnd1, opnd2) > 0;
353         case INTLE:
354                 return intcmp(opnd1, opnd2) <= 0;
355         case INTLT:
356                 return intcmp(opnd1, opnd2) < 0;
357         case FILNT:
358                 return newerf (opnd1, opnd2);
359         case FILOT:
360                 return olderf (opnd1, opnd2);
361         case FILEQ:
362                 return equalf (opnd1, opnd2);
363         default:
364                 abort();
365                 /* NOTREACHED */
366         }
367 }
368
369 static int
370 filstat(char *nm, enum token mode)
371 {
372         struct stat s;
373
374         if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
375                 return 0;
376
377         switch (mode) {
378         case FILRD:
379                 return (eaccess(nm, R_OK) == 0);
380         case FILWR:
381                 return (eaccess(nm, W_OK) == 0);
382         case FILEX:
383                 /* XXX work around eaccess(2) false positives for superuser */
384                 if (eaccess(nm, X_OK) != 0)
385                         return 0;
386                 if (S_ISDIR(s.st_mode) || geteuid() != 0)
387                         return 1;
388                 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
389         case FILEXIST:
390                 return (eaccess(nm, F_OK) == 0);
391         case FILREG:
392                 return S_ISREG(s.st_mode);
393         case FILDIR:
394                 return S_ISDIR(s.st_mode);
395         case FILCDEV:
396                 return S_ISCHR(s.st_mode);
397         case FILBDEV:
398                 return S_ISBLK(s.st_mode);
399         case FILFIFO:
400                 return S_ISFIFO(s.st_mode);
401         case FILSOCK:
402                 return S_ISSOCK(s.st_mode);
403         case FILSYM:
404                 return S_ISLNK(s.st_mode);
405         case FILSUID:
406                 return (s.st_mode & S_ISUID) != 0;
407         case FILSGID:
408                 return (s.st_mode & S_ISGID) != 0;
409         case FILSTCK:
410                 return (s.st_mode & S_ISVTX) != 0;
411         case FILGZ:
412                 return s.st_size > (off_t)0;
413         case FILUID:
414                 return s.st_uid == geteuid();
415         case FILGID:
416                 return s.st_gid == getegid();
417         default:
418                 return 1;
419         }
420 }
421
422 static enum token
423 t_lex(char *s)
424 {
425         struct t_op const *op = ops;
426
427         if (s == NULL) {
428                 t_wp_op = NULL;
429                 return EOI;
430         }
431         while (*op->op_text) {
432                 if (strcmp(s, op->op_text) == 0) {
433                         if (((op->op_type == UNOP || op->op_type == BUNOP)
434                                                 && isunopoperand()) ||
435                             (op->op_num == LPAREN && islparenoperand()) ||
436                             (op->op_num == RPAREN && isrparenoperand()))
437                                 break;
438                         t_wp_op = op;
439                         return op->op_num;
440                 }
441                 op++;
442         }
443         t_wp_op = NULL;
444         return OPERAND;
445 }
446
447 static int
448 isunopoperand(void)
449 {
450         struct t_op const *op = ops;
451         char *s;
452         char *t;
453
454         if (nargc == 1)
455                 return 1;
456         s = *(t_wp + 1);
457         if (nargc == 2)
458                 return parenlevel == 1 && strcmp(s, ")") == 0;
459         t = *(t_wp + 2);
460         while (*op->op_text) {
461                 if (strcmp(s, op->op_text) == 0)
462                         return op->op_type == BINOP &&
463                             (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
464                 op++;
465         }
466         return 0;
467 }
468
469 static int
470 islparenoperand(void)
471 {
472         struct t_op const *op = ops;
473         char *s;
474
475         if (nargc == 1)
476                 return 1;
477         s = *(t_wp + 1);
478         if (nargc == 2)
479                 return parenlevel == 1 && strcmp(s, ")") == 0;
480         if (nargc != 3)
481                 return 0;
482         while (*op->op_text) {
483                 if (strcmp(s, op->op_text) == 0)
484                         return op->op_type == BINOP;
485                 op++;
486         }
487         return 0;
488 }
489
490 static int
491 isrparenoperand(void)
492 {
493         char *s;
494
495         if (nargc == 1)
496                 return 0;
497         s = *(t_wp + 1);
498         if (nargc == 2)
499                 return parenlevel == 1 && strcmp(s, ")") == 0;
500         return 0;
501 }
502
503 /* atoi with error detection */
504 static int
505 getn(const char *s)
506 {
507         char *p;
508         long r;
509
510         errno = 0;
511         r = strtol(s, &p, 10);
512
513         if (s == p)
514                 error("%s: bad number", s);
515
516         if (errno != 0)
517                 error((errno == EINVAL) ? "%s: bad number" :
518                                           "%s: out of range", s);
519
520         while (isspace((unsigned char)*p))
521                 p++;
522
523         if (*p)
524                 error("%s: bad number", s);
525
526         return (int) r;
527 }
528
529 /* atoi with error detection and 64 bit range */
530 static intmax_t
531 getq(const char *s)
532 {
533         char *p;
534         intmax_t r;
535
536         errno = 0;
537         r = strtoimax(s, &p, 10);
538
539         if (s == p)
540                 error("%s: bad number", s);
541
542         if (errno != 0)
543                 error((errno == EINVAL) ? "%s: bad number" :
544                                           "%s: out of range", s);
545
546         while (isspace((unsigned char)*p))
547                 p++;
548
549         if (*p)
550                 error("%s: bad number", s);
551
552         return r;
553 }
554
555 static int
556 intcmp (const char *s1, const char *s2)
557 {
558         intmax_t q1, q2;
559
560
561         q1 = getq(s1);
562         q2 = getq(s2);
563
564         if (q1 > q2)
565                 return 1;
566
567         if (q1 < q2)
568                 return -1;
569
570         return 0;
571 }
572
573 static int
574 newerf (const char *f1, const char *f2)
575 {
576         struct stat b1, b2;
577
578         if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
579                 return 0;
580
581         if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
582                 return 1;
583         if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
584                 return 0;
585
586        return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
587 }
588
589 static int
590 olderf (const char *f1, const char *f2)
591 {
592         return (newerf(f2, f1));
593 }
594
595 static int
596 equalf (const char *f1, const char *f2)
597 {
598         struct stat b1, b2;
599
600         return (stat (f1, &b1) == 0 &&
601                 stat (f2, &b2) == 0 &&
602                 b1.st_dev == b2.st_dev &&
603                 b1.st_ino == b2.st_ino);
604 }