Home | History | Annotate | Line # | Download | only in test
test.c revision 1.33
      1  1.33  christos /* $NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $ */
      2  1.15       cgd 
      3  1.13       jtc /*
      4  1.13       jtc  * test(1); version 7-like  --  author Erik Baalbergen
      5  1.13       jtc  * modified by Eric Gisin to be used as built-in.
      6  1.13       jtc  * modified by Arnold Robbins to add SVR3 compatibility
      7  1.13       jtc  * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
      8  1.13       jtc  * modified by J.T. Conklin for NetBSD.
      9   1.1     glass  *
     10  1.13       jtc  * This program is in the Public Domain.
     11   1.1     glass  */
     12   1.1     glass 
     13  1.17  christos #include <sys/cdefs.h>
     14   1.1     glass #ifndef lint
     15  1.33  christos __RCSID("$NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $");
     16  1.13       jtc #endif
     17   1.1     glass 
     18  1.23       wiz #include <sys/stat.h>
     19   1.1     glass #include <sys/types.h>
     20  1.23       wiz 
     21  1.13       jtc #include <ctype.h>
     22  1.23       wiz #include <err.h>
     23   1.1     glass #include <errno.h>
     24  1.33  christos #include <limits.h>
     25  1.13       jtc #include <stdio.h>
     26   1.1     glass #include <stdlib.h>
     27   1.1     glass #include <string.h>
     28  1.23       wiz #include <unistd.h>
     29  1.22  christos #include <stdarg.h>
     30   1.1     glass 
     31  1.13       jtc /* test(1) accepts the following grammar:
     32  1.13       jtc 	oexpr	::= aexpr | aexpr "-o" oexpr ;
     33  1.13       jtc 	aexpr	::= nexpr | nexpr "-a" aexpr ;
     34  1.14       cgd 	nexpr	::= primary | "!" primary
     35  1.13       jtc 	primary	::= unary-operator operand
     36  1.13       jtc 		| operand binary-operator operand
     37  1.13       jtc 		| operand
     38  1.13       jtc 		| "(" oexpr ")"
     39  1.13       jtc 		;
     40  1.13       jtc 	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
     41  1.13       jtc 		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
     42  1.13       jtc 
     43  1.30   hubertf 	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
     44  1.30   hubertf 			"-nt"|"-ot"|"-ef";
     45  1.13       jtc 	operand ::= <any legal UNIX file name>
     46  1.13       jtc */
     47  1.13       jtc 
     48  1.13       jtc enum token {
     49  1.13       jtc 	EOI,
     50  1.13       jtc 	FILRD,
     51  1.13       jtc 	FILWR,
     52  1.13       jtc 	FILEX,
     53  1.13       jtc 	FILEXIST,
     54  1.13       jtc 	FILREG,
     55  1.13       jtc 	FILDIR,
     56  1.13       jtc 	FILCDEV,
     57  1.13       jtc 	FILBDEV,
     58  1.13       jtc 	FILFIFO,
     59  1.13       jtc 	FILSOCK,
     60  1.13       jtc 	FILSYM,
     61  1.13       jtc 	FILGZ,
     62  1.13       jtc 	FILTT,
     63  1.13       jtc 	FILSUID,
     64  1.13       jtc 	FILSGID,
     65  1.13       jtc 	FILSTCK,
     66  1.13       jtc 	FILNT,
     67  1.13       jtc 	FILOT,
     68  1.13       jtc 	FILEQ,
     69  1.13       jtc 	FILUID,
     70  1.13       jtc 	FILGID,
     71  1.13       jtc 	STREZ,
     72  1.13       jtc 	STRNZ,
     73  1.13       jtc 	STREQ,
     74  1.13       jtc 	STRNE,
     75  1.13       jtc 	STRLT,
     76  1.13       jtc 	STRGT,
     77  1.13       jtc 	INTEQ,
     78  1.13       jtc 	INTNE,
     79  1.13       jtc 	INTGE,
     80  1.13       jtc 	INTGT,
     81  1.13       jtc 	INTLE,
     82  1.13       jtc 	INTLT,
     83  1.13       jtc 	UNOT,
     84  1.13       jtc 	BAND,
     85  1.13       jtc 	BOR,
     86  1.13       jtc 	LPAREN,
     87  1.13       jtc 	RPAREN,
     88  1.13       jtc 	OPERAND
     89  1.13       jtc };
     90   1.1     glass 
     91  1.13       jtc enum token_types {
     92  1.13       jtc 	UNOP,
     93  1.13       jtc 	BINOP,
     94  1.13       jtc 	BUNOP,
     95  1.13       jtc 	BBINOP,
     96  1.13       jtc 	PAREN
     97   1.1     glass };
     98   1.1     glass 
     99  1.31  christos struct t_op {
    100  1.13       jtc 	const char *op_text;
    101  1.13       jtc 	short op_num, op_type;
    102  1.31  christos };
    103  1.31  christos 
    104  1.31  christos static const struct t_op cop[] = {
    105  1.13       jtc 	{"!",	UNOT,	BUNOP},
    106  1.13       jtc 	{"(",	LPAREN,	PAREN},
    107  1.13       jtc 	{")",	RPAREN,	PAREN},
    108  1.31  christos 	{"<",	STRLT,	BINOP},
    109  1.31  christos 	{"=",	STREQ,	BINOP},
    110  1.31  christos 	{">",	STRGT,	BINOP},
    111  1.31  christos };
    112  1.31  christos 
    113  1.31  christos static const struct t_op cop2[] = {
    114  1.31  christos 	{"!=",	STRNE,	BINOP},
    115  1.31  christos };
    116  1.31  christos 
    117  1.31  christos static const struct t_op mop3[] = {
    118  1.31  christos 	{"ef",	FILEQ,	BINOP},
    119  1.31  christos 	{"eq",	INTEQ,	BINOP},
    120  1.31  christos 	{"ge",	INTGE,	BINOP},
    121  1.31  christos 	{"gt",	INTGT,	BINOP},
    122  1.31  christos 	{"le",	INTLE,	BINOP},
    123  1.31  christos 	{"lt",	INTLT,	BINOP},
    124  1.31  christos 	{"ne",	INTNE,	BINOP},
    125  1.31  christos 	{"nt",	FILNT,	BINOP},
    126  1.31  christos 	{"ot",	FILOT,	BINOP},
    127  1.31  christos };
    128  1.31  christos 
    129  1.31  christos static const struct t_op mop2[] = {
    130  1.31  christos 	{"G",	FILGID,	UNOP},
    131  1.31  christos 	{"L",	FILSYM,	UNOP},
    132  1.31  christos 	{"O",	FILUID,	UNOP},
    133  1.31  christos 	{"S",	FILSOCK,UNOP},
    134  1.31  christos 	{"a",	BAND,	BBINOP},
    135  1.31  christos 	{"b",	FILBDEV,UNOP},
    136  1.31  christos 	{"c",	FILCDEV,UNOP},
    137  1.31  christos 	{"d",	FILDIR,	UNOP},
    138  1.31  christos 	{"e",	FILEXIST,UNOP},
    139  1.31  christos 	{"f",	FILREG,	UNOP},
    140  1.31  christos 	{"g",	FILSGID,UNOP},
    141  1.31  christos 	{"h",	FILSYM,	UNOP},		/* for backwards compat */
    142  1.31  christos 	{"k",	FILSTCK,UNOP},
    143  1.31  christos 	{"n",	STRNZ,	UNOP},
    144  1.31  christos 	{"o",	BOR,	BBINOP},
    145  1.31  christos 	{"p",	FILFIFO,UNOP},
    146  1.31  christos 	{"r",	FILRD,	UNOP},
    147  1.31  christos 	{"s",	FILGZ,	UNOP},
    148  1.31  christos 	{"t",	FILTT,	UNOP},
    149  1.31  christos 	{"u",	FILSUID,UNOP},
    150  1.31  christos 	{"w",	FILWR,	UNOP},
    151  1.31  christos 	{"x",	FILEX,	UNOP},
    152  1.31  christos 	{"z",	STREZ,	UNOP},
    153   1.1     glass };
    154   1.1     glass 
    155  1.22  christos static char **t_wp;
    156  1.22  christos static struct t_op const *t_wp_op;
    157   1.1     glass 
    158  1.23       wiz static void syntax(const char *, const char *);
    159  1.23       wiz static int oexpr(enum token);
    160  1.23       wiz static int aexpr(enum token);
    161  1.23       wiz static int nexpr(enum token);
    162  1.23       wiz static int primary(enum token);
    163  1.23       wiz static int binop(void);
    164  1.33  christos static int test_access(struct stat *, mode_t);
    165  1.23       wiz static int filstat(char *, enum token);
    166  1.23       wiz static enum token t_lex(char *);
    167  1.23       wiz static int isoperand(void);
    168  1.23       wiz static int getn(const char *);
    169  1.23       wiz static int newerf(const char *, const char *);
    170  1.23       wiz static int olderf(const char *, const char *);
    171  1.23       wiz static int equalf(const char *, const char *);
    172  1.17  christos 
    173  1.22  christos #if defined(SHELL)
    174  1.23       wiz extern void error(const char *, ...) __attribute__((__noreturn__));
    175  1.33  christos extern void *ckmalloc(size_t);
    176  1.22  christos #else
    177  1.23       wiz static void error(const char *, ...) __attribute__((__noreturn__));
    178  1.22  christos 
    179  1.22  christos static void
    180  1.22  christos error(const char *msg, ...)
    181  1.22  christos {
    182  1.22  christos 	va_list ap;
    183  1.22  christos 
    184  1.22  christos 	va_start(ap, msg);
    185  1.22  christos 	verrx(2, msg, ap);
    186  1.22  christos 	/*NOTREACHED*/
    187  1.22  christos 	va_end(ap);
    188  1.22  christos }
    189  1.33  christos 
    190  1.33  christos static void *ckmalloc(size_t);
    191  1.33  christos static void *
    192  1.33  christos ckmalloc(size_t nbytes)
    193  1.33  christos {
    194  1.33  christos 	void *p = malloc(nbytes);
    195  1.33  christos 
    196  1.33  christos 	if (!p)
    197  1.33  christos 		error("Not enough memory!");
    198  1.33  christos 	return p;
    199  1.33  christos }
    200  1.22  christos #endif
    201  1.22  christos 
    202  1.22  christos #ifdef SHELL
    203  1.23       wiz int testcmd(int, char **);
    204  1.22  christos 
    205  1.22  christos int
    206  1.23       wiz testcmd(int argc, char **argv)
    207  1.22  christos #else
    208  1.24       wiz int main(int, char *[]);
    209   1.1     glass 
    210   1.1     glass int
    211  1.24       wiz main(int argc, char *argv[])
    212  1.22  christos #endif
    213   1.1     glass {
    214  1.24       wiz 	int res;
    215  1.28  christos 	const char *argv0;
    216  1.22  christos 
    217  1.28  christos #ifdef SHELL
    218  1.28  christos 	argv0 = argv[0];
    219  1.28  christos #else
    220  1.24       wiz 	setprogname(argv[0]);
    221  1.28  christos 	argv0 = getprogname();
    222  1.28  christos #endif
    223  1.28  christos 	if (strcmp(argv0, "[") == 0) {
    224   1.1     glass 		if (strcmp(argv[--argc], "]"))
    225  1.22  christos 			error("missing ]");
    226   1.1     glass 		argv[argc] = NULL;
    227   1.1     glass 	}
    228   1.1     glass 
    229  1.22  christos 	if (argc < 2)
    230  1.22  christos 		return 1;
    231  1.22  christos 
    232  1.14       cgd 	t_wp = &argv[1];
    233  1.13       jtc 	res = !oexpr(t_lex(*t_wp));
    234  1.13       jtc 
    235  1.13       jtc 	if (*t_wp != NULL && *++t_wp != NULL)
    236  1.21    kleink 		syntax(*t_wp, "unexpected operator");
    237  1.13       jtc 
    238  1.13       jtc 	return res;
    239  1.13       jtc }
    240  1.13       jtc 
    241  1.13       jtc static void
    242  1.23       wiz syntax(const char *op, const char *msg)
    243  1.13       jtc {
    244  1.26    simonb 
    245  1.13       jtc 	if (op && *op)
    246  1.22  christos 		error("%s: %s", op, msg);
    247  1.13       jtc 	else
    248  1.22  christos 		error("%s", msg);
    249  1.13       jtc }
    250  1.13       jtc 
    251  1.13       jtc static int
    252  1.23       wiz oexpr(enum token n)
    253  1.13       jtc {
    254  1.13       jtc 	int res;
    255  1.13       jtc 
    256  1.13       jtc 	res = aexpr(n);
    257  1.32  christos 	if (*t_wp == NULL)
    258  1.32  christos 		return res;
    259  1.13       jtc 	if (t_lex(*++t_wp) == BOR)
    260  1.13       jtc 		return oexpr(t_lex(*++t_wp)) || res;
    261  1.13       jtc 	t_wp--;
    262  1.13       jtc 	return res;
    263  1.13       jtc }
    264  1.13       jtc 
    265  1.13       jtc static int
    266  1.23       wiz aexpr(enum token n)
    267  1.13       jtc {
    268  1.13       jtc 	int res;
    269  1.13       jtc 
    270  1.13       jtc 	res = nexpr(n);
    271  1.32  christos 	if (*t_wp == NULL)
    272  1.32  christos 		return res;
    273  1.13       jtc 	if (t_lex(*++t_wp) == BAND)
    274  1.13       jtc 		return aexpr(t_lex(*++t_wp)) && res;
    275  1.13       jtc 	t_wp--;
    276  1.13       jtc 	return res;
    277  1.13       jtc }
    278  1.13       jtc 
    279  1.13       jtc static int
    280  1.23       wiz nexpr(enum token n)
    281  1.13       jtc {
    282  1.26    simonb 
    283  1.13       jtc 	if (n == UNOT)
    284  1.13       jtc 		return !nexpr(t_lex(*++t_wp));
    285  1.13       jtc 	return primary(n);
    286  1.13       jtc }
    287  1.13       jtc 
    288  1.13       jtc static int
    289  1.23       wiz primary(enum token n)
    290  1.13       jtc {
    291  1.21    kleink 	enum token nn;
    292  1.13       jtc 	int res;
    293  1.13       jtc 
    294  1.13       jtc 	if (n == EOI)
    295  1.21    kleink 		return 0;		/* missing expression */
    296  1.13       jtc 	if (n == LPAREN) {
    297  1.21    kleink 		if ((nn = t_lex(*++t_wp)) == RPAREN)
    298  1.21    kleink 			return 0;	/* missing expression */
    299  1.21    kleink 		res = oexpr(nn);
    300  1.13       jtc 		if (t_lex(*++t_wp) != RPAREN)
    301  1.13       jtc 			syntax(NULL, "closing paren expected");
    302  1.13       jtc 		return res;
    303   1.1     glass 	}
    304  1.13       jtc 	if (t_wp_op && t_wp_op->op_type == UNOP) {
    305  1.13       jtc 		/* unary expression */
    306  1.13       jtc 		if (*++t_wp == NULL)
    307  1.13       jtc 			syntax(t_wp_op->op_text, "argument expected");
    308  1.13       jtc 		switch (n) {
    309  1.13       jtc 		case STREZ:
    310  1.13       jtc 			return strlen(*t_wp) == 0;
    311  1.13       jtc 		case STRNZ:
    312  1.13       jtc 			return strlen(*t_wp) != 0;
    313  1.13       jtc 		case FILTT:
    314  1.13       jtc 			return isatty(getn(*t_wp));
    315  1.13       jtc 		default:
    316  1.13       jtc 			return filstat(*t_wp, n);
    317   1.1     glass 		}
    318  1.13       jtc 	}
    319  1.14       cgd 
    320  1.14       cgd 	if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
    321  1.14       cgd 		return binop();
    322  1.14       cgd 	}
    323  1.14       cgd 
    324  1.14       cgd 	return strlen(*t_wp) > 0;
    325  1.14       cgd }
    326  1.14       cgd 
    327  1.14       cgd static int
    328  1.23       wiz binop(void)
    329  1.14       cgd {
    330  1.16       tls 	const char *opnd1, *opnd2;
    331  1.14       cgd 	struct t_op const *op;
    332  1.14       cgd 
    333  1.13       jtc 	opnd1 = *t_wp;
    334  1.13       jtc 	(void) t_lex(*++t_wp);
    335  1.14       cgd 	op = t_wp_op;
    336  1.13       jtc 
    337  1.26    simonb 	if ((opnd2 = *++t_wp) == NULL)
    338  1.14       cgd 		syntax(op->op_text, "argument expected");
    339  1.13       jtc 
    340  1.14       cgd 	switch (op->op_num) {
    341  1.14       cgd 	case STREQ:
    342  1.14       cgd 		return strcmp(opnd1, opnd2) == 0;
    343  1.14       cgd 	case STRNE:
    344  1.14       cgd 		return strcmp(opnd1, opnd2) != 0;
    345  1.14       cgd 	case STRLT:
    346  1.14       cgd 		return strcmp(opnd1, opnd2) < 0;
    347  1.14       cgd 	case STRGT:
    348  1.14       cgd 		return strcmp(opnd1, opnd2) > 0;
    349  1.14       cgd 	case INTEQ:
    350  1.14       cgd 		return getn(opnd1) == getn(opnd2);
    351  1.14       cgd 	case INTNE:
    352  1.14       cgd 		return getn(opnd1) != getn(opnd2);
    353  1.14       cgd 	case INTGE:
    354  1.14       cgd 		return getn(opnd1) >= getn(opnd2);
    355  1.14       cgd 	case INTGT:
    356  1.14       cgd 		return getn(opnd1) > getn(opnd2);
    357  1.14       cgd 	case INTLE:
    358  1.14       cgd 		return getn(opnd1) <= getn(opnd2);
    359  1.14       cgd 	case INTLT:
    360  1.14       cgd 		return getn(opnd1) < getn(opnd2);
    361  1.14       cgd 	case FILNT:
    362  1.26    simonb 		return newerf(opnd1, opnd2);
    363  1.14       cgd 	case FILOT:
    364  1.26    simonb 		return olderf(opnd1, opnd2);
    365  1.14       cgd 	case FILEQ:
    366  1.26    simonb 		return equalf(opnd1, opnd2);
    367  1.17  christos 	default:
    368  1.19   mycroft 		abort();
    369  1.17  christos 		/* NOTREACHED */
    370   1.1     glass 	}
    371   1.1     glass }
    372   1.1     glass 
    373  1.33  christos /*
    374  1.33  christos  * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits,
    375  1.33  christos  * not use access():
    376  1.33  christos  *
    377  1.33  christos  *	True shall indicate only that the write flag is on.  The file is not
    378  1.33  christos  *	writable on a read-only file system even if this test indicates true.
    379  1.33  christos  *
    380  1.33  christos  * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only:
    381  1.33  christos  *
    382  1.33  christos  *	True shall indicate that permission to read from file will be granted,
    383  1.33  christos  *	as defined in "File Read, Write, and Creation".
    384  1.33  christos  *
    385  1.33  christos  * and that section says:
    386  1.33  christos  *
    387  1.33  christos  *	When a file is to be read or written, the file shall be opened with an
    388  1.33  christos  *	access mode corresponding to the operation to be performed.  If file
    389  1.33  christos  *	access permissions deny access, the requested operation shall fail.
    390  1.33  christos  *
    391  1.33  christos  * and of course access permissions are described as one might expect:
    392  1.33  christos  *
    393  1.33  christos  *     * If a process has the appropriate privilege:
    394  1.33  christos  *
    395  1.33  christos  *        * If read, write, or directory search permission is requested,
    396  1.33  christos  *          access shall be granted.
    397  1.33  christos  *
    398  1.33  christos  *        * If execute permission is requested, access shall be granted if
    399  1.33  christos  *          execute permission is granted to at least one user by the file
    400  1.33  christos  *          permission bits or by an alternate access control mechanism;
    401  1.33  christos  *          otherwise, access shall be denied.
    402  1.33  christos  *
    403  1.33  christos  *   * Otherwise:
    404  1.33  christos  *
    405  1.33  christos  *        * The file permission bits of a file contain read, write, and
    406  1.33  christos  *          execute/search permissions for the file owner class, file group
    407  1.33  christos  *          class, and file other class.
    408  1.33  christos  *
    409  1.33  christos  *        * Access shall be granted if an alternate access control mechanism
    410  1.33  christos  *          is not enabled and the requested access permission bit is set for
    411  1.33  christos  *          the class (file owner class, file group class, or file other class)
    412  1.33  christos  *          to which the process belongs, or if an alternate access control
    413  1.33  christos  *          mechanism is enabled and it allows the requested access; otherwise,
    414  1.33  christos  *          access shall be denied.
    415  1.33  christos  *
    416  1.33  christos  * and when I first read this I thought:  surely we can't go about using
    417  1.33  christos  * open(O_WRONLY) to try this test!  However the POSIX 1003.1-2001 Rationale
    418  1.33  christos  * section for test does in fact say:
    419  1.33  christos  *
    420  1.33  christos  *	On historical BSD systems, test -w directory always returned false
    421  1.33  christos  *	because test tried to open the directory for writing, which always
    422  1.33  christos  *	fails.
    423  1.33  christos  *
    424  1.33  christos  * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX
    425  1.33  christos  * System III, and thus presumably also for BSD up to and including 4.3.
    426  1.33  christos  *
    427  1.33  christos  * Secondly I remembered why using open() and/or access() are bogus.  They
    428  1.33  christos  * don't work right for detecting read and write permissions bits when called
    429  1.33  christos  * by root.
    430  1.33  christos  *
    431  1.33  christos  * Interestingly the 'test' in 4.4BSD was closer to correct (as per
    432  1.33  christos  * 1003.2-1992) and it was implemented efficiently with stat() instead of
    433  1.33  christos  * open().
    434  1.33  christos  *
    435  1.33  christos  * This was apparently broken in NetBSD around about 1994/06/30 when the old
    436  1.33  christos  * 4.4BSD implementation was replaced with a (arguably much better coded)
    437  1.33  christos  * implementation derived from pdksh.
    438  1.33  christos  *
    439  1.33  christos  * Note that modern pdksh is yet different again, but still not correct, at
    440  1.33  christos  * least not w.r.t. 1003.2-1992.
    441  1.33  christos  *
    442  1.33  christos  * As I think more about it and read more of the related IEEE docs I don't like
    443  1.33  christos  * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all.  I very
    444  1.33  christos  * much prefer the original wording in 1003.2-1992.  It is much more useful,
    445  1.33  christos  * and so that's what I've implemented.
    446  1.33  christos  *
    447  1.33  christos  * (Note that a strictly conforming implementation of 1003.1-2001 is in fact
    448  1.33  christos  * totally useless for the case in question since its 'test -w' and 'test -r'
    449  1.33  christos  * can never fail for root for any existing files, i.e. files for which 'test
    450  1.33  christos  * -e' succeeds.)
    451  1.33  christos  *
    452  1.33  christos  * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
    453  1.33  christos  * 1003.1-2001 to align with the 1003.2b draft.  1003.2b Draft 12 (July 1999),
    454  1.33  christos  * which is the latest copy I have, does carry the same suggested wording as is
    455  1.33  christos  * in 1003.1-2001, with its rationale saying:
    456  1.33  christos  *
    457  1.33  christos  * 	This change is a clarification and is the result of interpretation
    458  1.33  christos  * 	request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
    459  1.33  christos  *
    460  1.33  christos  * That interpretation can be found here:
    461  1.33  christos  *
    462  1.33  christos  *   http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
    463  1.33  christos  *
    464  1.33  christos  * Not terribly helpful, unfortunately.  I wonder who that fence sitter was.
    465  1.33  christos  *
    466  1.33  christos  * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
    467  1.33  christos  * PASC interpretation and appear to be gone against at least one widely used
    468  1.33  christos  * implementation (namely 4.4BSD).  The problem is that for file access by root
    469  1.33  christos  * this means that if test '-r' and '-w' are to behave as if open() were called
    470  1.33  christos  * then there's no way for a shell script running as root to check if a file
    471  1.33  christos  * has certain access bits set other than by the grotty means of interpreting
    472  1.33  christos  * the output of 'ls -l'.  This was widely considered to be a bug in V7's
    473  1.33  christos  * "test" and is, I believe, one of the reasons why direct use of access() was
    474  1.33  christos  * avoided in some more recent implementations!
    475  1.33  christos  *
    476  1.33  christos  * I have always interpreted '-r' to match '-w' and '-x' as per the original
    477  1.33  christos  * wording in 1003.2-1992, not the other way around.  I think 1003.2b goes much
    478  1.33  christos  * too far the wrong way without any valid rationale and that it's best if we
    479  1.33  christos  * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
    480  1.33  christos  * open() since we already know very well how it will work -- existance of the
    481  1.33  christos  * file is all that matters to open() for root.
    482  1.33  christos  *
    483  1.33  christos  * Unfortunately the SVID is no help at all (which is, I guess, partly why
    484  1.33  christos  * we're in this mess in the first place :-).
    485  1.33  christos  *
    486  1.33  christos  * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
    487  1.33  christos  * access(name, 2) even though it also goes to much greater lengths for '-x'
    488  1.33  christos  * matching the 1003.2-1992 definition (which is no doubt where that definition
    489  1.33  christos  * came from).
    490  1.33  christos  *
    491  1.33  christos  * The ksh93 implementation uses access() for '-r' and '-w' if
    492  1.33  christos  * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
    493  1.33  christos  * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
    494  1.33  christos  */
    495  1.33  christos static int
    496  1.33  christos test_access(struct stat *sp, mode_t stmode)
    497  1.33  christos {
    498  1.33  christos 	gid_t *groups;
    499  1.33  christos 	register int n;
    500  1.33  christos 	uid_t euid;
    501  1.33  christos 	int maxgroups;
    502  1.33  christos 
    503  1.33  christos 	/*
    504  1.33  christos 	 * I suppose we could use access() if not running as root and if we are
    505  1.33  christos 	 * running with ((euid == uid) && (egid == gid)), but we've already
    506  1.33  christos 	 * done the stat() so we might as well just test the permissions
    507  1.33  christos 	 * directly instead of asking the kernel to do it....
    508  1.33  christos 	 */
    509  1.33  christos 	euid = geteuid();
    510  1.33  christos 	if (euid == 0)				/* any bit is good enough */
    511  1.33  christos 		stmode = (stmode << 6) | (stmode << 3) | stmode;
    512  1.33  christos  	else if (sp->st_uid == euid)
    513  1.33  christos 		stmode <<= 6;
    514  1.33  christos 	else if (sp->st_gid == getegid())
    515  1.33  christos 		stmode <<= 3;
    516  1.33  christos 	else {
    517  1.33  christos 		/* XXX stolen almost verbatim from ksh93.... */
    518  1.33  christos 		/* on some systems you can be in several groups */
    519  1.33  christos 		if ((maxgroups = getgroups(0, NULL)) <= 0)
    520  1.33  christos 			maxgroups = NGROUPS_MAX;	/* pre-POSIX system? */
    521  1.33  christos 		groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
    522  1.33  christos 		n = getgroups(maxgroups, groups);
    523  1.33  christos 		while (--n >= 0) {
    524  1.33  christos 			if (groups[n] == sp->st_gid) {
    525  1.33  christos 				stmode <<= 3;
    526  1.33  christos 				break;
    527  1.33  christos 			}
    528  1.33  christos 		}
    529  1.33  christos 		free(groups);
    530  1.33  christos 	}
    531  1.33  christos 
    532  1.33  christos 	return sp->st_mode & stmode;
    533  1.33  christos }
    534  1.33  christos 
    535   1.1     glass static int
    536  1.23       wiz filstat(char *nm, enum token mode)
    537   1.1     glass {
    538  1.13       jtc 	struct stat s;
    539  1.13       jtc 
    540  1.18   mycroft 	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
    541  1.13       jtc 		return 0;
    542   1.1     glass 
    543  1.13       jtc 	switch (mode) {
    544  1.13       jtc 	case FILRD:
    545  1.33  christos 		return test_access(&s, S_IROTH);
    546  1.13       jtc 	case FILWR:
    547  1.33  christos 		return test_access(&s, S_IWOTH);
    548  1.13       jtc 	case FILEX:
    549  1.33  christos 		return test_access(&s, S_IXOTH);
    550  1.13       jtc 	case FILEXIST:
    551  1.33  christos 		return 1; /* the successful lstat()/stat() is good enough */
    552  1.13       jtc 	case FILREG:
    553  1.18   mycroft 		return S_ISREG(s.st_mode);
    554  1.13       jtc 	case FILDIR:
    555  1.18   mycroft 		return S_ISDIR(s.st_mode);
    556  1.13       jtc 	case FILCDEV:
    557  1.18   mycroft 		return S_ISCHR(s.st_mode);
    558  1.13       jtc 	case FILBDEV:
    559  1.18   mycroft 		return S_ISBLK(s.st_mode);
    560  1.13       jtc 	case FILFIFO:
    561  1.18   mycroft 		return S_ISFIFO(s.st_mode);
    562  1.13       jtc 	case FILSOCK:
    563  1.18   mycroft 		return S_ISSOCK(s.st_mode);
    564  1.18   mycroft 	case FILSYM:
    565  1.18   mycroft 		return S_ISLNK(s.st_mode);
    566  1.13       jtc 	case FILSUID:
    567  1.18   mycroft 		return (s.st_mode & S_ISUID) != 0;
    568  1.13       jtc 	case FILSGID:
    569  1.18   mycroft 		return (s.st_mode & S_ISGID) != 0;
    570  1.13       jtc 	case FILSTCK:
    571  1.18   mycroft 		return (s.st_mode & S_ISVTX) != 0;
    572  1.13       jtc 	case FILGZ:
    573  1.18   mycroft 		return s.st_size > (off_t)0;
    574  1.13       jtc 	case FILUID:
    575  1.13       jtc 		return s.st_uid == geteuid();
    576  1.13       jtc 	case FILGID:
    577  1.13       jtc 		return s.st_gid == getegid();
    578  1.13       jtc 	default:
    579  1.13       jtc 		return 1;
    580  1.13       jtc 	}
    581   1.1     glass }
    582   1.1     glass 
    583  1.31  christos #define VTOC(x)	(const unsigned char *)((const struct t_op *)x)->op_text
    584  1.31  christos 
    585  1.31  christos static int
    586  1.31  christos compare1(const void *va, const void *vb)
    587  1.31  christos {
    588  1.31  christos 	const unsigned char *a = va;
    589  1.31  christos 	const unsigned char *b = VTOC(vb);
    590  1.31  christos 
    591  1.31  christos 	return a[0] - b[0];
    592  1.31  christos }
    593  1.31  christos 
    594  1.31  christos static int
    595  1.31  christos compare2(const void *va, const void *vb)
    596  1.31  christos {
    597  1.31  christos 	const unsigned char *a = va;
    598  1.31  christos 	const unsigned char *b = VTOC(vb);
    599  1.31  christos 	int z = a[0] - b[0];
    600  1.31  christos 
    601  1.31  christos 	return z ? z : (a[1] - b[1]);
    602  1.31  christos }
    603  1.31  christos 
    604  1.31  christos static struct t_op const *
    605  1.31  christos findop(const char *s)
    606  1.31  christos {
    607  1.31  christos 	if (s[0] == '-') {
    608  1.31  christos 		if (s[1] == '\0')
    609  1.31  christos 			return NULL;
    610  1.31  christos 		if (s[2] == '\0')
    611  1.31  christos 			return bsearch(s + 1, mop2, __arraycount(mop2),
    612  1.31  christos 			    sizeof(*mop2), compare1);
    613  1.31  christos 		else if (s[3] != '\0')
    614  1.31  christos 			return NULL;
    615  1.31  christos 		else
    616  1.31  christos 			return bsearch(s + 1, mop3, __arraycount(mop3),
    617  1.31  christos 			    sizeof(*mop3), compare2);
    618  1.31  christos 	} else {
    619  1.31  christos 		if (s[1] == '\0')
    620  1.31  christos 			return bsearch(s, cop, __arraycount(cop), sizeof(*cop),
    621  1.31  christos 			    compare1);
    622  1.31  christos 		else if (strcmp(s, cop2[0].op_text) == 0)
    623  1.31  christos 			return cop2;
    624  1.31  christos 		else
    625  1.31  christos 			return NULL;
    626  1.31  christos 	}
    627  1.31  christos }
    628  1.31  christos 
    629  1.13       jtc static enum token
    630  1.23       wiz t_lex(char *s)
    631   1.1     glass {
    632  1.24       wiz 	struct t_op const *op;
    633  1.24       wiz 
    634  1.31  christos 	if (s == NULL) {
    635  1.26    simonb 		t_wp_op = NULL;
    636  1.13       jtc 		return EOI;
    637  1.13       jtc 	}
    638  1.31  christos 
    639  1.31  christos 	if ((op = findop(s)) != NULL) {
    640  1.31  christos 		if (!((op->op_type == UNOP && isoperand()) ||
    641  1.31  christos 		    (op->op_num == LPAREN && *(t_wp+1) == 0))) {
    642  1.13       jtc 			t_wp_op = op;
    643  1.13       jtc 			return op->op_num;
    644  1.13       jtc 		}
    645  1.13       jtc 	}
    646  1.26    simonb 	t_wp_op = NULL;
    647  1.13       jtc 	return OPERAND;
    648  1.21    kleink }
    649  1.21    kleink 
    650  1.21    kleink static int
    651  1.23       wiz isoperand(void)
    652  1.21    kleink {
    653  1.24       wiz 	struct t_op const *op;
    654  1.24       wiz 	char *s, *t;
    655  1.21    kleink 
    656  1.21    kleink 	if ((s  = *(t_wp+1)) == 0)
    657  1.21    kleink 		return 1;
    658  1.21    kleink 	if ((t = *(t_wp+2)) == 0)
    659  1.21    kleink 		return 0;
    660  1.31  christos 	if ((op = findop(s)) != NULL)
    661  1.31  christos 		return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
    662  1.21    kleink 	return 0;
    663   1.1     glass }
    664   1.1     glass 
    665  1.13       jtc /* atoi with error detection */
    666   1.1     glass static int
    667  1.23       wiz getn(const char *s)
    668   1.1     glass {
    669   1.6       alm 	char *p;
    670   1.6       alm 	long r;
    671   1.1     glass 
    672   1.6       alm 	errno = 0;
    673  1.13       jtc 	r = strtol(s, &p, 10);
    674  1.13       jtc 
    675   1.6       alm 	if (errno != 0)
    676  1.22  christos 	      error("%s: out of range", s);
    677  1.13       jtc 
    678  1.20  christos 	while (isspace((unsigned char)*p))
    679  1.22  christos 	      p++;
    680  1.13       jtc 
    681  1.13       jtc 	if (*p)
    682  1.22  christos 	      error("%s: bad number", s);
    683  1.13       jtc 
    684  1.13       jtc 	return (int) r;
    685   1.1     glass }
    686   1.1     glass 
    687  1.13       jtc static int
    688  1.26    simonb newerf(const char *f1, const char *f2)
    689   1.1     glass {
    690  1.13       jtc 	struct stat b1, b2;
    691  1.13       jtc 
    692  1.26    simonb 	return (stat(f1, &b1) == 0 &&
    693  1.26    simonb 		stat(f2, &b2) == 0 &&
    694  1.13       jtc 		b1.st_mtime > b2.st_mtime);
    695   1.1     glass }
    696   1.1     glass 
    697  1.13       jtc static int
    698  1.26    simonb olderf(const char *f1, const char *f2)
    699   1.1     glass {
    700  1.13       jtc 	struct stat b1, b2;
    701  1.13       jtc 
    702  1.26    simonb 	return (stat(f1, &b1) == 0 &&
    703  1.26    simonb 		stat(f2, &b2) == 0 &&
    704  1.13       jtc 		b1.st_mtime < b2.st_mtime);
    705   1.1     glass }
    706   1.1     glass 
    707  1.13       jtc static int
    708  1.26    simonb equalf(const char *f1, const char *f2)
    709  1.13       jtc {
    710  1.13       jtc 	struct stat b1, b2;
    711   1.1     glass 
    712  1.26    simonb 	return (stat(f1, &b1) == 0 &&
    713  1.26    simonb 		stat(f2, &b2) == 0 &&
    714  1.13       jtc 		b1.st_dev == b2.st_dev &&
    715  1.13       jtc 		b1.st_ino == b2.st_ino);
    716   1.1     glass }
    717