1 /* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */
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.
10 * This program is in the Public Domain.
13 * Important: This file is used both as a standalone program /bin/test and
14 * as a builtin for /bin/sh (#define SHELL).
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
20 #include <sys/types.h>
36 #include "bltin/bltin.h"
40 static void error(const char *, ...) __dead2 __printf0like(1, 2);
43 error(const char *msg, ...)
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
62 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
63 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
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>
160 char op_num, op_type;
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},
178 {"-h", FILSYM, UNOP}, /* for backwards compat */
179 {"-O", FILUID, UNOP},
180 {"-G", FILGID, UNOP},
181 {"-L", FILSYM, UNOP},
182 {"-S", FILSOCK,UNOP},
184 {"==", STREQ, BINOP},
185 {"!=", STRNE, 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},
230 {"-a", BAND, BBINOP},
232 {"(", LPAREN, PAREN},
233 {")", RPAREN, PAREN},
237 static struct t_op const *t_wp_op;
240 static int parenlevel;
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,
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 *);
261 main(int argc, char **argv)
266 if ((p = strrchr(argv[0], '/')) == NULL)
270 if (strcmp(p, "[") == 0) {
271 if (strcmp(argv[--argc], "]") != 0)
276 /* no expression => false */
281 (void)setlocale(LC_CTYPE, "");
286 if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
287 /* Things like ! "" -o x do not fit in the normal grammar. */
290 res = oexpr(t_lex(*t_wp));
292 res = !oexpr(t_lex(*t_wp));
295 syntax(*t_wp, "unexpected operator");
301 syntax(const char *op, const char *msg)
305 error("%s: %s", op, msg);
316 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
317 return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
330 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
331 return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
342 return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
347 primary(enum token n)
353 return 0; /* missing expression */
356 if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
359 return 0; /* missing expression */
362 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
363 syntax(NULL, "closing paren expected");
367 if (t_wp_op && t_wp_op->op_type == UNOP) {
368 /* unary expression */
370 syntax(t_wp_op->op_text, "argument expected");
373 return strlen(*++t_wp) == 0;
375 return strlen(*++t_wp) != 0;
377 return isatty(getn(*++t_wp));
379 return filstat(*++t_wp, n);
383 if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type ==
388 return strlen(*t_wp) > 0;
394 const char *opnd1, *opnd2;
395 struct t_op const *op;
398 (void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL);
401 if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
402 syntax(op->op_text, "argument expected");
404 switch (op->op_num) {
406 return strcmp(opnd1, opnd2) == 0;
408 return strcmp(opnd1, opnd2) != 0;
410 return strcmp(opnd1, opnd2) < 0;
412 return strcmp(opnd1, opnd2) > 0;
414 return intcmp(opnd1, opnd2) == 0;
416 return intcmp(opnd1, opnd2) != 0;
418 return intcmp(opnd1, opnd2) >= 0;
420 return intcmp(opnd1, opnd2) > 0;
422 return intcmp(opnd1, opnd2) <= 0;
424 return intcmp(opnd1, opnd2) < 0;
426 return newerf(opnd1, opnd2, ATIME, ATIME);
428 return newerf(opnd1, opnd2, ATIME, BTIME);
430 return newerf(opnd1, opnd2, ATIME, CTIME);
432 return newerf(opnd1, opnd2, ATIME, MTIME);
434 return newerf(opnd1, opnd2, BTIME, ATIME);
436 return newerf(opnd1, opnd2, BTIME, BTIME);
438 return newerf(opnd1, opnd2, BTIME, CTIME);
440 return newerf(opnd1, opnd2, BTIME, MTIME);
442 return newerf(opnd1, opnd2, CTIME, ATIME);
444 return newerf(opnd1, opnd2, CTIME, BTIME);
446 return newerf(opnd1, opnd2, CTIME, CTIME);
448 return newerf(opnd1, opnd2, CTIME, MTIME);
450 return newerf(opnd1, opnd2, MTIME, ATIME);
452 return newerf(opnd1, opnd2, MTIME, BTIME);
454 return newerf(opnd1, opnd2, MTIME, CTIME);
456 return newerf(opnd1, opnd2, MTIME, MTIME);
458 return newerf(opnd2, opnd1, ATIME, ATIME);
460 return newerf(opnd2, opnd1, BTIME, ATIME);
462 return newerf(opnd2, opnd1, CTIME, ATIME);
464 return newerf(opnd2, opnd1, MTIME, ATIME);
466 return newerf(opnd2, opnd1, ATIME, BTIME);
468 return newerf(opnd2, opnd1, BTIME, BTIME);
470 return newerf(opnd2, opnd1, CTIME, BTIME);
472 return newerf(opnd2, opnd1, MTIME, BTIME);
474 return newerf(opnd2, opnd1, ATIME, CTIME);
476 return newerf(opnd2, opnd1, BTIME, CTIME);
478 return newerf(opnd2, opnd1, CTIME, CTIME);
480 return newerf(opnd2, opnd1, MTIME, CTIME);
482 return newerf(opnd2, opnd1, ATIME, MTIME);
484 return newerf(opnd2, opnd1, BTIME, MTIME);
486 return newerf(opnd2, opnd1, CTIME, MTIME);
488 return newerf(opnd2, opnd1, MTIME, MTIME);
490 return equalf (opnd1, opnd2);
498 filstat(char *nm, enum token mode)
502 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
507 return (eaccess(nm, R_OK) == 0);
509 return (eaccess(nm, W_OK) == 0);
511 /* XXX work around eaccess(2) false positives for superuser */
512 if (eaccess(nm, X_OK) != 0)
514 if (S_ISDIR(s.st_mode) || geteuid() != 0)
516 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
518 return (eaccess(nm, F_OK) == 0);
520 return S_ISREG(s.st_mode);
522 return S_ISDIR(s.st_mode);
524 return S_ISCHR(s.st_mode);
526 return S_ISBLK(s.st_mode);
528 return S_ISFIFO(s.st_mode);
530 return S_ISSOCK(s.st_mode);
532 return S_ISLNK(s.st_mode);
534 return (s.st_mode & S_ISUID) != 0;
536 return (s.st_mode & S_ISGID) != 0;
538 return (s.st_mode & S_ISVTX) != 0;
540 return s.st_size > (off_t)0;
542 return s.st_uid == geteuid();
544 return s.st_gid == getegid();
553 struct t_op const *op = ops;
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()))
578 struct t_op const *op = ops;
586 return parenlevel == 1 && strcmp(s, ")") == 0;
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');
598 islparenoperand(void)
600 struct t_op const *op = ops;
607 return parenlevel == 1 && strcmp(s, ")") == 0;
610 while (*op->op_text) {
611 if (strcmp(s, op->op_text) == 0)
612 return op->op_type == BINOP;
619 isrparenoperand(void)
627 return parenlevel == 1 && strcmp(s, ")") == 0;
631 /* atoi with error detection */
639 r = strtol(s, &p, 10);
642 error("%s: bad number", s);
645 error((errno == EINVAL) ? "%s: bad number" :
646 "%s: out of range", s);
648 while (isspace((unsigned char)*p))
652 error("%s: bad number", s);
657 /* atoi with error detection and 64 bit range */
665 r = strtoimax(s, &p, 10);
668 error("%s: bad number", s);
671 error((errno == EINVAL) ? "%s: bad number" :
672 "%s: out of range", s);
674 while (isspace((unsigned char)*p))
678 error("%s: bad number", s);
684 intcmp (const char *s1, const char *s2)
702 newerf (const char *f1, const char *f2, enum time_types t1, enum time_types t2)
705 struct timespec *ts1, *ts2;
707 if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
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;
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;
724 if (ts1->tv_sec > ts2->tv_sec)
726 if (ts1->tv_sec < ts2->tv_sec)
729 return (ts1->tv_nsec > ts2->tv_nsec);
733 equalf (const char *f1, const char *f2)
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);