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