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