Home | History | Annotate | Line # | Download | only in test
test.c revision 1.13
      1  1.13    jtc /*
      2  1.13    jtc  * test(1); version 7-like  --  author Erik Baalbergen
      3  1.13    jtc  * modified by Eric Gisin to be used as built-in.
      4  1.13    jtc  * modified by Arnold Robbins to add SVR3 compatibility
      5  1.13    jtc  * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
      6  1.13    jtc  * modified by J.T. Conklin for NetBSD.
      7   1.1  glass  *
      8  1.13    jtc  * This program is in the Public Domain.
      9   1.1  glass  */
     10   1.1  glass 
     11   1.1  glass #ifndef lint
     12  1.13    jtc static char *rcsid = "$Id: test.c,v 1.13 1994/06/30 05:12:38 jtc Exp $";
     13  1.13    jtc #endif
     14   1.1  glass 
     15   1.1  glass #include <sys/types.h>
     16   1.1  glass #include <sys/stat.h>
     17  1.13    jtc #include <unistd.h>
     18  1.13    jtc #include <ctype.h>
     19   1.1  glass #include <errno.h>
     20  1.13    jtc #include <stdio.h>
     21   1.1  glass #include <stdlib.h>
     22   1.1  glass #include <string.h>
     23  1.13    jtc #include <err.h>
     24   1.1  glass 
     25  1.13    jtc /* test(1) accepts the following grammar:
     26  1.13    jtc 	oexpr	::= aexpr | aexpr "-o" oexpr ;
     27  1.13    jtc 	aexpr	::= nexpr | nexpr "-a" aexpr ;
     28  1.13    jtc 	nexpr	::= primary ! "!" primary
     29  1.13    jtc 	primary	::= unary-operator operand
     30  1.13    jtc 		| operand binary-operator operand
     31  1.13    jtc 		| operand
     32  1.13    jtc 		| "(" oexpr ")"
     33  1.13    jtc 		;
     34  1.13    jtc 	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
     35  1.13    jtc 		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
     36  1.13    jtc 
     37  1.13    jtc 	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
     38  1.13    jtc 			"-nt"|"-ot"|"-ef";
     39  1.13    jtc 	operand ::= <any legal UNIX file name>
     40  1.13    jtc */
     41  1.13    jtc 
     42  1.13    jtc enum token {
     43  1.13    jtc 	EOI,
     44  1.13    jtc 	FILRD,
     45  1.13    jtc 	FILWR,
     46  1.13    jtc 	FILEX,
     47  1.13    jtc 	FILEXIST,
     48  1.13    jtc 	FILREG,
     49  1.13    jtc 	FILDIR,
     50  1.13    jtc 	FILCDEV,
     51  1.13    jtc 	FILBDEV,
     52  1.13    jtc 	FILFIFO,
     53  1.13    jtc 	FILSOCK,
     54  1.13    jtc 	FILSYM,
     55  1.13    jtc 	FILGZ,
     56  1.13    jtc 	FILTT,
     57  1.13    jtc 	FILSUID,
     58  1.13    jtc 	FILSGID,
     59  1.13    jtc 	FILSTCK,
     60  1.13    jtc 	FILNT,
     61  1.13    jtc 	FILOT,
     62  1.13    jtc 	FILEQ,
     63  1.13    jtc 	FILUID,
     64  1.13    jtc 	FILGID,
     65  1.13    jtc 	STREZ,
     66  1.13    jtc 	STRNZ,
     67  1.13    jtc 	STREQ,
     68  1.13    jtc 	STRNE,
     69  1.13    jtc 	STRLT,
     70  1.13    jtc 	STRGT,
     71  1.13    jtc 	INTEQ,
     72  1.13    jtc 	INTNE,
     73  1.13    jtc 	INTGE,
     74  1.13    jtc 	INTGT,
     75  1.13    jtc 	INTLE,
     76  1.13    jtc 	INTLT,
     77  1.13    jtc 	UNOT,
     78  1.13    jtc 	BAND,
     79  1.13    jtc 	BOR,
     80  1.13    jtc 	LPAREN,
     81  1.13    jtc 	RPAREN,
     82  1.13    jtc 	OPERAND
     83  1.13    jtc };
     84   1.1  glass 
     85  1.13    jtc enum token_types {
     86  1.13    jtc 	UNOP,
     87  1.13    jtc 	BINOP,
     88  1.13    jtc 	BUNOP,
     89  1.13    jtc 	BBINOP,
     90  1.13    jtc 	PAREN
     91   1.1  glass };
     92   1.1  glass 
     93  1.13    jtc struct t_op {
     94  1.13    jtc 	const char *op_text;
     95  1.13    jtc 	short op_num, op_type;
     96  1.13    jtc } const ops [] = {
     97  1.13    jtc 	{"-r",	FILRD,	UNOP},
     98  1.13    jtc 	{"-w",	FILWR,	UNOP},
     99  1.13    jtc 	{"-x",	FILEX,	UNOP},
    100  1.13    jtc 	{"-e",	FILEXIST,UNOP},
    101  1.13    jtc 	{"-f",	FILREG,	UNOP},
    102  1.13    jtc 	{"-d",	FILDIR,	UNOP},
    103  1.13    jtc 	{"-c",	FILCDEV,UNOP},
    104  1.13    jtc 	{"-b",	FILBDEV,UNOP},
    105  1.13    jtc 	{"-p",	FILFIFO,UNOP},
    106  1.13    jtc 	{"-u",	FILSUID,UNOP},
    107  1.13    jtc 	{"-g",	FILSGID,UNOP},
    108  1.13    jtc 	{"-k",	FILSTCK,UNOP},
    109  1.13    jtc 	{"-s",	FILGZ,	UNOP},
    110  1.13    jtc 	{"-t",	FILTT,	UNOP},
    111  1.13    jtc 	{"-z",	STREZ,	UNOP},
    112  1.13    jtc 	{"-n",	STRNZ,	UNOP},
    113  1.13    jtc 	{"-h",	FILSYM,	UNOP},		/* for backwards compat */
    114  1.13    jtc 	{"-O",	FILUID,	UNOP},
    115  1.13    jtc 	{"-G",	FILGID,	UNOP},
    116  1.13    jtc 	{"-L",	FILSYM,	UNOP},
    117  1.13    jtc 	{"-S",	FILSOCK,UNOP},
    118  1.13    jtc 	{"=",	STREQ,	BINOP},
    119  1.13    jtc 	{"!=",	STRNE,	BINOP},
    120  1.13    jtc 	{"<",	STRLT,	BINOP},
    121  1.13    jtc 	{">",	STRGT,	BINOP},
    122  1.13    jtc 	{"-eq",	INTEQ,	BINOP},
    123  1.13    jtc 	{"-ne",	INTNE,	BINOP},
    124  1.13    jtc 	{"-ge",	INTGE,	BINOP},
    125  1.13    jtc 	{"-gt",	INTGT,	BINOP},
    126  1.13    jtc 	{"-le",	INTLE,	BINOP},
    127  1.13    jtc 	{"-lt",	INTLT,	BINOP},
    128  1.13    jtc 	{"-nt",	FILNT,	BINOP},
    129  1.13    jtc 	{"-ot",	FILOT,	BINOP},
    130  1.13    jtc 	{"-ef",	FILEQ,	BINOP},
    131  1.13    jtc 	{"!",	UNOT,	BUNOP},
    132  1.13    jtc 	{"-a",	BAND,	BBINOP},
    133  1.13    jtc 	{"-o",	BOR,	BBINOP},
    134  1.13    jtc 	{"(",	LPAREN,	PAREN},
    135  1.13    jtc 	{")",	RPAREN,	PAREN},
    136  1.13    jtc 	{0,	0,	0}
    137   1.1  glass };
    138   1.1  glass 
    139  1.13    jtc char **t_wp;
    140  1.13    jtc struct t_op const *t_wp_op;
    141   1.1  glass 
    142  1.13    jtc static enum token t_lex();
    143  1.13    jtc static int oexpr();
    144  1.13    jtc static int aexpr();
    145  1.13    jtc static int nexpr();
    146  1.13    jtc static int primary();
    147  1.13    jtc static int filstat();
    148  1.13    jtc static int getn();
    149  1.13    jtc static int newerf();
    150  1.13    jtc static int olderf();
    151  1.13    jtc static int equalf();
    152  1.13    jtc static void syntax();
    153   1.1  glass 
    154   1.1  glass int
    155   1.1  glass main(argc, argv)
    156   1.1  glass 	int argc;
    157  1.13    jtc 	char **argv;
    158   1.1  glass {
    159  1.13    jtc 	int	res;
    160   1.1  glass 
    161  1.13    jtc 	t_wp = argv+1;
    162  1.13    jtc 	if (strcmp(argv[0], "[") == 0) {
    163   1.1  glass 		if (strcmp(argv[--argc], "]"))
    164  1.13    jtc 			errx(2, "missing ]");
    165   1.1  glass 		argv[argc] = NULL;
    166   1.1  glass 	}
    167   1.1  glass 
    168  1.13    jtc 	/* Implement special cases from POSIX.2, section 4.62.4 */
    169  1.13    jtc 	switch (argc) {
    170  1.13    jtc 	case 1:
    171  1.13    jtc 		return 1;
    172  1.13    jtc 	case 2:
    173  1.13    jtc 		return (*argv[1] == '\0');
    174  1.13    jtc 	case 3:
    175  1.13    jtc 		if (argv[1][0] == '!' && argv[1][1] == '\0') {
    176  1.13    jtc 			return !(*argv[2] == '\0');
    177   1.1  glass 		}
    178   1.1  glass 	}
    179   1.1  glass 
    180  1.13    jtc 	res = !oexpr(t_lex(*t_wp));
    181  1.13    jtc 
    182  1.13    jtc 	if (*t_wp != NULL && *++t_wp != NULL)
    183  1.13    jtc 		syntax(*t_wp, "unknown operand");
    184  1.13    jtc 
    185  1.13    jtc 	return res;
    186  1.13    jtc }
    187  1.13    jtc 
    188  1.13    jtc static void
    189  1.13    jtc syntax(op, msg)
    190  1.13    jtc 	char	*op;
    191  1.13    jtc 	char	*msg;
    192  1.13    jtc {
    193  1.13    jtc 	if (op && *op)
    194  1.13    jtc 		errx(2, "%s: %s", op, msg);
    195  1.13    jtc 	else
    196  1.13    jtc 		errx(2, "%s", msg);
    197  1.13    jtc }
    198  1.13    jtc 
    199  1.13    jtc static int
    200  1.13    jtc oexpr(n)
    201  1.13    jtc 	enum token n;
    202  1.13    jtc {
    203  1.13    jtc 	int res;
    204  1.13    jtc 
    205  1.13    jtc 	res = aexpr(n);
    206  1.13    jtc 	if (t_lex(*++t_wp) == BOR)
    207  1.13    jtc 		return oexpr(t_lex(*++t_wp)) || res;
    208  1.13    jtc 	t_wp--;
    209  1.13    jtc 	return res;
    210  1.13    jtc }
    211  1.13    jtc 
    212  1.13    jtc static int
    213  1.13    jtc aexpr(n)
    214  1.13    jtc 	enum token n;
    215  1.13    jtc {
    216  1.13    jtc 	int res;
    217  1.13    jtc 
    218  1.13    jtc 	res = nexpr(n);
    219  1.13    jtc 	if (t_lex(*++t_wp) == BAND)
    220  1.13    jtc 		return aexpr(t_lex(*++t_wp)) && res;
    221  1.13    jtc 	t_wp--;
    222  1.13    jtc 	return res;
    223  1.13    jtc }
    224  1.13    jtc 
    225  1.13    jtc static int
    226  1.13    jtc nexpr(n)
    227  1.13    jtc 	enum token n;			/* token */
    228  1.13    jtc {
    229  1.13    jtc 	if (n == UNOT)
    230  1.13    jtc 		return !nexpr(t_lex(*++t_wp));
    231  1.13    jtc 	return primary(n);
    232  1.13    jtc }
    233  1.13    jtc 
    234  1.13    jtc static int
    235  1.13    jtc primary(n)
    236  1.13    jtc 	enum token n;
    237  1.13    jtc {
    238  1.13    jtc 	register char *opnd1, *opnd2;
    239  1.13    jtc 	int res;
    240  1.13    jtc 
    241  1.13    jtc 	if (n == EOI)
    242  1.13    jtc 		syntax(NULL, "argument expected");
    243  1.13    jtc 	if (n == LPAREN) {
    244  1.13    jtc 		res = oexpr(t_lex(*++t_wp));
    245  1.13    jtc 		if (t_lex(*++t_wp) != RPAREN)
    246  1.13    jtc 			syntax(NULL, "closing paren expected");
    247  1.13    jtc 		return res;
    248   1.1  glass 	}
    249  1.13    jtc 	if (t_wp_op && t_wp_op->op_type == UNOP) {
    250  1.13    jtc 		/* unary expression */
    251  1.13    jtc 		if (*++t_wp == NULL)
    252  1.13    jtc 			syntax(t_wp_op->op_text, "argument expected");
    253  1.13    jtc 		switch (n) {
    254  1.13    jtc 		case STREZ:
    255  1.13    jtc 			return strlen(*t_wp) == 0;
    256  1.13    jtc 		case STRNZ:
    257  1.13    jtc 			return strlen(*t_wp) != 0;
    258  1.13    jtc 		case FILTT:
    259  1.13    jtc 			return isatty(getn(*t_wp));
    260  1.13    jtc 		default:
    261  1.13    jtc 			return filstat(*t_wp, n);
    262   1.1  glass 		}
    263  1.13    jtc 	}
    264  1.13    jtc 	opnd1 = *t_wp;
    265  1.13    jtc 	(void) t_lex(*++t_wp);
    266  1.13    jtc 	if (t_wp_op && t_wp_op->op_type == BINOP) {
    267  1.13    jtc 		struct t_op const *op = t_wp_op;
    268  1.13    jtc 
    269  1.13    jtc 		if ((opnd2 = *++t_wp) == (char *)0)
    270  1.13    jtc 			syntax(op->op_text, "argument expected");
    271  1.13    jtc 
    272  1.13    jtc 		switch (op->op_num) {
    273  1.13    jtc 		case STREQ:
    274  1.13    jtc 			return strcmp(opnd1, opnd2) == 0;
    275  1.13    jtc 		case STRNE:
    276  1.13    jtc 			return strcmp(opnd1, opnd2) != 0;
    277  1.13    jtc 		case STRLT:
    278  1.13    jtc 			return strcmp(opnd1, opnd2) < 0;
    279  1.13    jtc 		case STRGT:
    280  1.13    jtc 			return strcmp(opnd1, opnd2) > 0;
    281  1.13    jtc 		case INTEQ:
    282  1.13    jtc 			return getn(opnd1) == getn(opnd2);
    283  1.13    jtc 		case INTNE:
    284  1.13    jtc 			return getn(opnd1) != getn(opnd2);
    285  1.13    jtc 		case INTGE:
    286  1.13    jtc 			return getn(opnd1) >= getn(opnd2);
    287  1.13    jtc 		case INTGT:
    288  1.13    jtc 			return getn(opnd1) > getn(opnd2);
    289  1.13    jtc 		case INTLE:
    290  1.13    jtc 			return getn(opnd1) <= getn(opnd2);
    291  1.13    jtc 		case INTLT:
    292  1.13    jtc 			return getn(opnd1) < getn(opnd2);
    293  1.13    jtc 		case FILNT:
    294  1.13    jtc 			return newerf (opnd1, opnd2);
    295  1.13    jtc 		case FILOT:
    296  1.13    jtc 			return olderf (opnd1, opnd2);
    297  1.13    jtc 		case FILEQ:
    298  1.13    jtc 			return equalf (opnd1, opnd2);
    299   1.1  glass 		}
    300   1.1  glass 	}
    301  1.13    jtc 	t_wp--;
    302  1.13    jtc 	return strlen(opnd1) > 0;
    303   1.1  glass }
    304   1.1  glass 
    305   1.1  glass static int
    306  1.13    jtc filstat(nm, mode)
    307  1.13    jtc 	char *nm;
    308  1.13    jtc 	enum token mode;
    309   1.1  glass {
    310  1.13    jtc 	struct stat s;
    311  1.13    jtc 	int i;
    312  1.13    jtc 
    313  1.13    jtc 	if (mode == FILSYM) {
    314  1.13    jtc #ifdef S_IFLNK
    315  1.13    jtc 		if (lstat(nm, &s) == 0) {
    316  1.13    jtc 			i = S_IFLNK;
    317  1.13    jtc 			goto filetype;
    318  1.13    jtc 		}
    319  1.13    jtc #endif
    320  1.13    jtc 		return 0;
    321   1.1  glass 	}
    322   1.1  glass 
    323  1.13    jtc 	if (stat(nm, &s) != 0)
    324  1.13    jtc 		return 0;
    325   1.1  glass 
    326  1.13    jtc 	switch (mode) {
    327  1.13    jtc 	case FILRD:
    328  1.13    jtc 		return access(nm, R_OK) == 0;
    329  1.13    jtc 	case FILWR:
    330  1.13    jtc 		return access(nm, W_OK) == 0;
    331  1.13    jtc 	case FILEX:
    332  1.13    jtc 		return access(nm, X_OK) == 0;
    333  1.13    jtc 	case FILEXIST:
    334  1.13    jtc 		return access(nm, F_OK) == 0;
    335  1.13    jtc 	case FILREG:
    336   1.1  glass 		i = S_IFREG;
    337   1.1  glass 		goto filetype;
    338  1.13    jtc 	case FILDIR:
    339   1.1  glass 		i = S_IFDIR;
    340   1.1  glass 		goto filetype;
    341  1.13    jtc 	case FILCDEV:
    342   1.1  glass 		i = S_IFCHR;
    343   1.1  glass 		goto filetype;
    344  1.13    jtc 	case FILBDEV:
    345   1.1  glass 		i = S_IFBLK;
    346   1.1  glass 		goto filetype;
    347  1.13    jtc 	case FILFIFO:
    348  1.13    jtc #ifdef S_IFIFO
    349   1.1  glass 		i = S_IFIFO;
    350   1.1  glass 		goto filetype;
    351  1.13    jtc #else
    352  1.13    jtc 		return 0;
    353  1.13    jtc #endif
    354  1.13    jtc 	case FILSOCK:
    355  1.13    jtc #ifdef S_IFSOCK
    356  1.13    jtc 		i = S_IFSOCK;
    357  1.13    jtc 		goto filetype;
    358  1.13    jtc #else
    359  1.13    jtc 		return 0;
    360  1.13    jtc #endif
    361  1.13    jtc 	case FILSUID:
    362   1.1  glass 		i = S_ISUID;
    363   1.1  glass 		goto filebit;
    364  1.13    jtc 	case FILSGID:
    365   1.1  glass 		i = S_ISGID;
    366   1.1  glass 		goto filebit;
    367  1.13    jtc 	case FILSTCK:
    368   1.1  glass 		i = S_ISVTX;
    369  1.13    jtc 		goto filebit;
    370  1.13    jtc 	case FILGZ:
    371  1.13    jtc 		return s.st_size > 0L;
    372  1.13    jtc 	case FILUID:
    373  1.13    jtc 		return s.st_uid == geteuid();
    374  1.13    jtc 	case FILGID:
    375  1.13    jtc 		return s.st_gid == getegid();
    376  1.13    jtc 	default:
    377  1.13    jtc 		return 1;
    378  1.13    jtc 	}
    379   1.1  glass 
    380  1.13    jtc filetype:
    381  1.13    jtc 	return ((s.st_mode & S_IFMT) == i);
    382   1.1  glass 
    383  1.13    jtc filebit:
    384  1.13    jtc 	return ((s.st_mode & i) != 0);
    385   1.1  glass }
    386   1.1  glass 
    387  1.13    jtc static enum token
    388  1.13    jtc t_lex(s)
    389  1.13    jtc 	register char *s;
    390   1.1  glass {
    391  1.13    jtc 	register struct t_op const *op = ops;
    392   1.1  glass 
    393  1.13    jtc 	if (s == 0) {
    394  1.13    jtc 		t_wp_op = (struct t_op *)0;
    395  1.13    jtc 		return EOI;
    396  1.13    jtc 	}
    397  1.13    jtc 	while (op->op_text) {
    398  1.13    jtc 		if (strcmp(s, op->op_text) == 0) {
    399  1.13    jtc 			t_wp_op = op;
    400  1.13    jtc 			return op->op_num;
    401  1.13    jtc 		}
    402  1.13    jtc 		op++;
    403  1.13    jtc 	}
    404  1.13    jtc 	t_wp_op = (struct t_op *)0;
    405  1.13    jtc 	return OPERAND;
    406   1.1  glass }
    407   1.1  glass 
    408  1.13    jtc /* atoi with error detection */
    409   1.1  glass static int
    410  1.13    jtc getn(s)
    411  1.13    jtc 	char *s;
    412   1.1  glass {
    413   1.6    alm 	char *p;
    414   1.6    alm 	long r;
    415   1.1  glass 
    416   1.6    alm 	errno = 0;
    417  1.13    jtc 	r = strtol(s, &p, 10);
    418  1.13    jtc 
    419   1.6    alm 	if (errno != 0)
    420  1.13    jtc 	  errx(2, "%s: out of range", s);
    421  1.13    jtc 
    422   1.5    jtc 	while (isspace(*p))
    423  1.13    jtc 	  p++;
    424  1.13    jtc 
    425  1.13    jtc 	if (*p)
    426  1.13    jtc 	  errx(2, "%s: bad number", s);
    427  1.13    jtc 
    428  1.13    jtc 	return (int) r;
    429   1.1  glass }
    430   1.1  glass 
    431  1.13    jtc static int
    432  1.13    jtc newerf (f1, f2)
    433  1.13    jtc char *f1, *f2;
    434   1.1  glass {
    435  1.13    jtc 	struct stat b1, b2;
    436  1.13    jtc 
    437  1.13    jtc 	return (stat (f1, &b1) == 0 &&
    438  1.13    jtc 		stat (f2, &b2) == 0 &&
    439  1.13    jtc 		b1.st_mtime > b2.st_mtime);
    440   1.1  glass }
    441   1.1  glass 
    442  1.13    jtc static int
    443  1.13    jtc olderf (f1, f2)
    444  1.13    jtc char *f1, *f2;
    445   1.1  glass {
    446  1.13    jtc 	struct stat b1, b2;
    447  1.13    jtc 
    448  1.13    jtc 	return (stat (f1, &b1) == 0 &&
    449  1.13    jtc 		stat (f2, &b2) == 0 &&
    450  1.13    jtc 		b1.st_mtime < b2.st_mtime);
    451   1.1  glass }
    452   1.1  glass 
    453  1.13    jtc static int
    454  1.13    jtc equalf (f1, f2)
    455  1.13    jtc char *f1, *f2;
    456  1.13    jtc {
    457  1.13    jtc 	struct stat b1, b2;
    458   1.1  glass 
    459  1.13    jtc 	return (stat (f1, &b1) == 0 &&
    460  1.13    jtc 		stat (f2, &b2) == 0 &&
    461  1.13    jtc 		b1.st_dev == b2.st_dev &&
    462  1.13    jtc 		b1.st_ino == b2.st_ino);
    463   1.1  glass }
    464