1*02efe53eSSevan Janiyan /* $NetBSD: test.c,v 1.41 2016/09/05 01:00:07 sevan Exp $ */
23a19ae75SLionel Sambuc
33a19ae75SLionel Sambuc /*
43a19ae75SLionel Sambuc * test(1); version 7-like -- author Erik Baalbergen
53a19ae75SLionel Sambuc * modified by Eric Gisin to be used as built-in.
63a19ae75SLionel Sambuc * modified by Arnold Robbins to add SVR3 compatibility
73a19ae75SLionel Sambuc * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
83a19ae75SLionel Sambuc * modified by J.T. Conklin for NetBSD.
93a19ae75SLionel Sambuc *
103a19ae75SLionel Sambuc * This program is in the Public Domain.
113a19ae75SLionel Sambuc */
123a19ae75SLionel Sambuc
133a19ae75SLionel Sambuc #include <sys/cdefs.h>
143a19ae75SLionel Sambuc #ifndef lint
15*02efe53eSSevan Janiyan __RCSID("$NetBSD: test.c,v 1.41 2016/09/05 01:00:07 sevan Exp $");
163a19ae75SLionel Sambuc #endif
173a19ae75SLionel Sambuc
183a19ae75SLionel Sambuc #include <sys/stat.h>
193a19ae75SLionel Sambuc #include <sys/types.h>
203a19ae75SLionel Sambuc
213a19ae75SLionel Sambuc #include <ctype.h>
223a19ae75SLionel Sambuc #include <err.h>
233a19ae75SLionel Sambuc #include <errno.h>
243a19ae75SLionel Sambuc #include <limits.h>
253a19ae75SLionel Sambuc #include <locale.h>
263a19ae75SLionel Sambuc #include <stdio.h>
273a19ae75SLionel Sambuc #include <stdlib.h>
283a19ae75SLionel Sambuc #include <string.h>
293a19ae75SLionel Sambuc #include <unistd.h>
303a19ae75SLionel Sambuc #include <stdarg.h>
313a19ae75SLionel Sambuc
323a19ae75SLionel Sambuc /* test(1) accepts the following grammar:
333a19ae75SLionel Sambuc oexpr ::= aexpr | aexpr "-o" oexpr ;
343a19ae75SLionel Sambuc aexpr ::= nexpr | nexpr "-a" aexpr ;
353a19ae75SLionel Sambuc nexpr ::= primary | "!" primary
363a19ae75SLionel Sambuc primary ::= unary-operator operand
373a19ae75SLionel Sambuc | operand binary-operator operand
383a19ae75SLionel Sambuc | operand
393a19ae75SLionel Sambuc | "(" oexpr ")"
403a19ae75SLionel Sambuc ;
413a19ae75SLionel Sambuc unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
423a19ae75SLionel Sambuc "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
433a19ae75SLionel Sambuc
443a19ae75SLionel Sambuc binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
453a19ae75SLionel Sambuc "-nt"|"-ot"|"-ef";
463a19ae75SLionel Sambuc operand ::= <any legal UNIX file name>
473a19ae75SLionel Sambuc */
483a19ae75SLionel Sambuc
493a19ae75SLionel Sambuc enum token {
503a19ae75SLionel Sambuc EOI,
513a19ae75SLionel Sambuc FILRD,
523a19ae75SLionel Sambuc FILWR,
533a19ae75SLionel Sambuc FILEX,
543a19ae75SLionel Sambuc FILEXIST,
553a19ae75SLionel Sambuc FILREG,
563a19ae75SLionel Sambuc FILDIR,
573a19ae75SLionel Sambuc FILCDEV,
583a19ae75SLionel Sambuc FILBDEV,
593a19ae75SLionel Sambuc FILFIFO,
603a19ae75SLionel Sambuc FILSOCK,
613a19ae75SLionel Sambuc FILSYM,
623a19ae75SLionel Sambuc FILGZ,
633a19ae75SLionel Sambuc FILTT,
643a19ae75SLionel Sambuc FILSUID,
653a19ae75SLionel Sambuc FILSGID,
663a19ae75SLionel Sambuc FILSTCK,
673a19ae75SLionel Sambuc FILNT,
683a19ae75SLionel Sambuc FILOT,
693a19ae75SLionel Sambuc FILEQ,
703a19ae75SLionel Sambuc FILUID,
713a19ae75SLionel Sambuc FILGID,
723a19ae75SLionel Sambuc STREZ,
733a19ae75SLionel Sambuc STRNZ,
743a19ae75SLionel Sambuc STREQ,
753a19ae75SLionel Sambuc STRNE,
763a19ae75SLionel Sambuc STRLT,
773a19ae75SLionel Sambuc STRGT,
783a19ae75SLionel Sambuc INTEQ,
793a19ae75SLionel Sambuc INTNE,
803a19ae75SLionel Sambuc INTGE,
813a19ae75SLionel Sambuc INTGT,
823a19ae75SLionel Sambuc INTLE,
833a19ae75SLionel Sambuc INTLT,
843a19ae75SLionel Sambuc UNOT,
853a19ae75SLionel Sambuc BAND,
863a19ae75SLionel Sambuc BOR,
873a19ae75SLionel Sambuc LPAREN,
883a19ae75SLionel Sambuc RPAREN,
893a19ae75SLionel Sambuc OPERAND
903a19ae75SLionel Sambuc };
913a19ae75SLionel Sambuc
923a19ae75SLionel Sambuc enum token_types {
933a19ae75SLionel Sambuc UNOP,
943a19ae75SLionel Sambuc BINOP,
953a19ae75SLionel Sambuc BUNOP,
963a19ae75SLionel Sambuc BBINOP,
973a19ae75SLionel Sambuc PAREN
983a19ae75SLionel Sambuc };
993a19ae75SLionel Sambuc
1003a19ae75SLionel Sambuc struct t_op {
1013a19ae75SLionel Sambuc const char *op_text;
1023a19ae75SLionel Sambuc short op_num, op_type;
1033a19ae75SLionel Sambuc };
1043a19ae75SLionel Sambuc
1053a19ae75SLionel Sambuc static const struct t_op cop[] = {
1063a19ae75SLionel Sambuc {"!", UNOT, BUNOP},
1073a19ae75SLionel Sambuc {"(", LPAREN, PAREN},
1083a19ae75SLionel Sambuc {")", RPAREN, PAREN},
1093a19ae75SLionel Sambuc {"<", STRLT, BINOP},
1103a19ae75SLionel Sambuc {"=", STREQ, BINOP},
1113a19ae75SLionel Sambuc {">", STRGT, BINOP},
1123a19ae75SLionel Sambuc };
1133a19ae75SLionel Sambuc
1143a19ae75SLionel Sambuc static const struct t_op cop2[] = {
1153a19ae75SLionel Sambuc {"!=", STRNE, BINOP},
1163a19ae75SLionel Sambuc };
1173a19ae75SLionel Sambuc
1183a19ae75SLionel Sambuc static const struct t_op mop3[] = {
1193a19ae75SLionel Sambuc {"ef", FILEQ, BINOP},
1203a19ae75SLionel Sambuc {"eq", INTEQ, BINOP},
1213a19ae75SLionel Sambuc {"ge", INTGE, BINOP},
1223a19ae75SLionel Sambuc {"gt", INTGT, BINOP},
1233a19ae75SLionel Sambuc {"le", INTLE, BINOP},
1243a19ae75SLionel Sambuc {"lt", INTLT, BINOP},
1253a19ae75SLionel Sambuc {"ne", INTNE, BINOP},
1263a19ae75SLionel Sambuc {"nt", FILNT, BINOP},
1273a19ae75SLionel Sambuc {"ot", FILOT, BINOP},
1283a19ae75SLionel Sambuc };
1293a19ae75SLionel Sambuc
1303a19ae75SLionel Sambuc static const struct t_op mop2[] = {
1313a19ae75SLionel Sambuc {"G", FILGID, UNOP},
1323a19ae75SLionel Sambuc {"L", FILSYM, UNOP},
1333a19ae75SLionel Sambuc {"O", FILUID, UNOP},
1343a19ae75SLionel Sambuc {"S", FILSOCK,UNOP},
1353a19ae75SLionel Sambuc {"a", BAND, BBINOP},
1363a19ae75SLionel Sambuc {"b", FILBDEV,UNOP},
1373a19ae75SLionel Sambuc {"c", FILCDEV,UNOP},
1383a19ae75SLionel Sambuc {"d", FILDIR, UNOP},
1393a19ae75SLionel Sambuc {"e", FILEXIST,UNOP},
1403a19ae75SLionel Sambuc {"f", FILREG, UNOP},
1413a19ae75SLionel Sambuc {"g", FILSGID,UNOP},
1423a19ae75SLionel Sambuc {"h", FILSYM, UNOP}, /* for backwards compat */
1433a19ae75SLionel Sambuc {"k", FILSTCK,UNOP},
1443a19ae75SLionel Sambuc {"n", STRNZ, UNOP},
1453a19ae75SLionel Sambuc {"o", BOR, BBINOP},
1463a19ae75SLionel Sambuc {"p", FILFIFO,UNOP},
1473a19ae75SLionel Sambuc {"r", FILRD, UNOP},
1483a19ae75SLionel Sambuc {"s", FILGZ, UNOP},
1493a19ae75SLionel Sambuc {"t", FILTT, UNOP},
1503a19ae75SLionel Sambuc {"u", FILSUID,UNOP},
1513a19ae75SLionel Sambuc {"w", FILWR, UNOP},
1523a19ae75SLionel Sambuc {"x", FILEX, UNOP},
1533a19ae75SLionel Sambuc {"z", STREZ, UNOP},
1543a19ae75SLionel Sambuc };
1553a19ae75SLionel Sambuc
1563a19ae75SLionel Sambuc static char **t_wp;
1573a19ae75SLionel Sambuc static struct t_op const *t_wp_op;
1583a19ae75SLionel Sambuc
1593a19ae75SLionel Sambuc __dead static void syntax(const char *, const char *);
1603a19ae75SLionel Sambuc static int oexpr(enum token);
1613a19ae75SLionel Sambuc static int aexpr(enum token);
1623a19ae75SLionel Sambuc static int nexpr(enum token);
1633a19ae75SLionel Sambuc static int primary(enum token);
1643a19ae75SLionel Sambuc static int binop(void);
1653a19ae75SLionel Sambuc static int test_access(struct stat *, mode_t);
1663a19ae75SLionel Sambuc static int filstat(char *, enum token);
1673a19ae75SLionel Sambuc static enum token t_lex(char *);
1683a19ae75SLionel Sambuc static int isoperand(void);
1693a19ae75SLionel Sambuc static long long getn(const char *);
1703a19ae75SLionel Sambuc static int newerf(const char *, const char *);
1713a19ae75SLionel Sambuc static int olderf(const char *, const char *);
1723a19ae75SLionel Sambuc static int equalf(const char *, const char *);
1733a19ae75SLionel Sambuc
1743a19ae75SLionel Sambuc #if defined(SHELL)
1753a19ae75SLionel Sambuc extern void error(const char *, ...) __dead __printflike(1, 2);
1763a19ae75SLionel Sambuc extern void *ckmalloc(size_t);
1773a19ae75SLionel Sambuc #else
1783a19ae75SLionel Sambuc static void error(const char *, ...) __dead __printflike(1, 2);
1793a19ae75SLionel Sambuc
1803a19ae75SLionel Sambuc static void
error(const char * msg,...)1813a19ae75SLionel Sambuc error(const char *msg, ...)
1823a19ae75SLionel Sambuc {
1833a19ae75SLionel Sambuc va_list ap;
1843a19ae75SLionel Sambuc
1853a19ae75SLionel Sambuc va_start(ap, msg);
1863a19ae75SLionel Sambuc verrx(2, msg, ap);
1873a19ae75SLionel Sambuc /*NOTREACHED*/
1883a19ae75SLionel Sambuc va_end(ap);
1893a19ae75SLionel Sambuc }
1903a19ae75SLionel Sambuc
1913a19ae75SLionel Sambuc static void *ckmalloc(size_t);
1923a19ae75SLionel Sambuc static void *
ckmalloc(size_t nbytes)1933a19ae75SLionel Sambuc ckmalloc(size_t nbytes)
1943a19ae75SLionel Sambuc {
1953a19ae75SLionel Sambuc void *p = malloc(nbytes);
1963a19ae75SLionel Sambuc
1973a19ae75SLionel Sambuc if (!p)
1983a19ae75SLionel Sambuc error("Not enough memory!");
1993a19ae75SLionel Sambuc return p;
2003a19ae75SLionel Sambuc }
2013a19ae75SLionel Sambuc #endif
2023a19ae75SLionel Sambuc
2033a19ae75SLionel Sambuc #ifdef SHELL
2043a19ae75SLionel Sambuc int testcmd(int, char **);
2053a19ae75SLionel Sambuc
2063a19ae75SLionel Sambuc int
testcmd(int argc,char ** argv)2073a19ae75SLionel Sambuc testcmd(int argc, char **argv)
2083a19ae75SLionel Sambuc #else
2093a19ae75SLionel Sambuc int
2103a19ae75SLionel Sambuc main(int argc, char *argv[])
2113a19ae75SLionel Sambuc #endif
2123a19ae75SLionel Sambuc {
2133a19ae75SLionel Sambuc int res;
2143a19ae75SLionel Sambuc const char *argv0;
2153a19ae75SLionel Sambuc
2163a19ae75SLionel Sambuc #ifdef SHELL
2173a19ae75SLionel Sambuc argv0 = argv[0];
2183a19ae75SLionel Sambuc #else
2193a19ae75SLionel Sambuc setprogname(argv[0]);
2203a19ae75SLionel Sambuc (void)setlocale(LC_ALL, "");
2213a19ae75SLionel Sambuc argv0 = getprogname();
2223a19ae75SLionel Sambuc #endif
2233a19ae75SLionel Sambuc if (strcmp(argv0, "[") == 0) {
2243a19ae75SLionel Sambuc if (strcmp(argv[--argc], "]"))
2253a19ae75SLionel Sambuc error("missing ]");
2263a19ae75SLionel Sambuc argv[argc] = NULL;
2273a19ae75SLionel Sambuc }
2283a19ae75SLionel Sambuc
2293a19ae75SLionel Sambuc if (argc < 2)
2303a19ae75SLionel Sambuc return 1;
2313a19ae75SLionel Sambuc
2323a19ae75SLionel Sambuc t_wp = &argv[1];
2333a19ae75SLionel Sambuc res = !oexpr(t_lex(*t_wp));
2343a19ae75SLionel Sambuc
2353a19ae75SLionel Sambuc if (*t_wp != NULL && *++t_wp != NULL)
2363a19ae75SLionel Sambuc syntax(*t_wp, "unexpected operator");
2373a19ae75SLionel Sambuc
2383a19ae75SLionel Sambuc return res;
2393a19ae75SLionel Sambuc }
2403a19ae75SLionel Sambuc
2413a19ae75SLionel Sambuc static void
syntax(const char * op,const char * msg)2423a19ae75SLionel Sambuc syntax(const char *op, const char *msg)
2433a19ae75SLionel Sambuc {
2443a19ae75SLionel Sambuc
2453a19ae75SLionel Sambuc if (op && *op)
2463a19ae75SLionel Sambuc error("%s: %s", op, msg);
2473a19ae75SLionel Sambuc else
2483a19ae75SLionel Sambuc error("%s", msg);
2493a19ae75SLionel Sambuc }
2503a19ae75SLionel Sambuc
2513a19ae75SLionel Sambuc static int
oexpr(enum token n)2523a19ae75SLionel Sambuc oexpr(enum token n)
2533a19ae75SLionel Sambuc {
2543a19ae75SLionel Sambuc int res;
2553a19ae75SLionel Sambuc
2563a19ae75SLionel Sambuc res = aexpr(n);
2573a19ae75SLionel Sambuc if (*t_wp == NULL)
2583a19ae75SLionel Sambuc return res;
2593a19ae75SLionel Sambuc if (t_lex(*++t_wp) == BOR)
2603a19ae75SLionel Sambuc return oexpr(t_lex(*++t_wp)) || res;
2613a19ae75SLionel Sambuc t_wp--;
2623a19ae75SLionel Sambuc return res;
2633a19ae75SLionel Sambuc }
2643a19ae75SLionel Sambuc
2653a19ae75SLionel Sambuc static int
aexpr(enum token n)2663a19ae75SLionel Sambuc aexpr(enum token n)
2673a19ae75SLionel Sambuc {
2683a19ae75SLionel Sambuc int res;
2693a19ae75SLionel Sambuc
2703a19ae75SLionel Sambuc res = nexpr(n);
2713a19ae75SLionel Sambuc if (*t_wp == NULL)
2723a19ae75SLionel Sambuc return res;
2733a19ae75SLionel Sambuc if (t_lex(*++t_wp) == BAND)
2743a19ae75SLionel Sambuc return aexpr(t_lex(*++t_wp)) && res;
2753a19ae75SLionel Sambuc t_wp--;
2763a19ae75SLionel Sambuc return res;
2773a19ae75SLionel Sambuc }
2783a19ae75SLionel Sambuc
2793a19ae75SLionel Sambuc static int
nexpr(enum token n)2803a19ae75SLionel Sambuc nexpr(enum token n)
2813a19ae75SLionel Sambuc {
2823a19ae75SLionel Sambuc
2833a19ae75SLionel Sambuc if (n == UNOT)
2843a19ae75SLionel Sambuc return !nexpr(t_lex(*++t_wp));
2853a19ae75SLionel Sambuc return primary(n);
2863a19ae75SLionel Sambuc }
2873a19ae75SLionel Sambuc
2883a19ae75SLionel Sambuc static int
primary(enum token n)2893a19ae75SLionel Sambuc primary(enum token n)
2903a19ae75SLionel Sambuc {
2913a19ae75SLionel Sambuc enum token nn;
2923a19ae75SLionel Sambuc int res;
2933a19ae75SLionel Sambuc
2943a19ae75SLionel Sambuc if (n == EOI)
2953a19ae75SLionel Sambuc return 0; /* missing expression */
2963a19ae75SLionel Sambuc if (n == LPAREN) {
2973a19ae75SLionel Sambuc if ((nn = t_lex(*++t_wp)) == RPAREN)
2983a19ae75SLionel Sambuc return 0; /* missing expression */
2993a19ae75SLionel Sambuc res = oexpr(nn);
3003a19ae75SLionel Sambuc if (t_lex(*++t_wp) != RPAREN)
3013a19ae75SLionel Sambuc syntax(NULL, "closing paren expected");
3023a19ae75SLionel Sambuc return res;
3033a19ae75SLionel Sambuc }
3043a19ae75SLionel Sambuc if (t_wp_op && t_wp_op->op_type == UNOP) {
3053a19ae75SLionel Sambuc /* unary expression */
3063a19ae75SLionel Sambuc if (*++t_wp == NULL)
3073a19ae75SLionel Sambuc syntax(t_wp_op->op_text, "argument expected");
3083a19ae75SLionel Sambuc switch (n) {
3093a19ae75SLionel Sambuc case STREZ:
3103a19ae75SLionel Sambuc return strlen(*t_wp) == 0;
3113a19ae75SLionel Sambuc case STRNZ:
3123a19ae75SLionel Sambuc return strlen(*t_wp) != 0;
3133a19ae75SLionel Sambuc case FILTT:
3143a19ae75SLionel Sambuc return isatty((int)getn(*t_wp));
3153a19ae75SLionel Sambuc default:
3163a19ae75SLionel Sambuc return filstat(*t_wp, n);
3173a19ae75SLionel Sambuc }
3183a19ae75SLionel Sambuc }
3193a19ae75SLionel Sambuc
3203a19ae75SLionel Sambuc if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
3213a19ae75SLionel Sambuc return binop();
3223a19ae75SLionel Sambuc }
3233a19ae75SLionel Sambuc
3243a19ae75SLionel Sambuc return strlen(*t_wp) > 0;
3253a19ae75SLionel Sambuc }
3263a19ae75SLionel Sambuc
3273a19ae75SLionel Sambuc static int
binop(void)3283a19ae75SLionel Sambuc binop(void)
3293a19ae75SLionel Sambuc {
3303a19ae75SLionel Sambuc const char *opnd1, *opnd2;
3313a19ae75SLionel Sambuc struct t_op const *op;
3323a19ae75SLionel Sambuc
3333a19ae75SLionel Sambuc opnd1 = *t_wp;
3343a19ae75SLionel Sambuc (void) t_lex(*++t_wp);
3353a19ae75SLionel Sambuc op = t_wp_op;
3363a19ae75SLionel Sambuc
3373a19ae75SLionel Sambuc if ((opnd2 = *++t_wp) == NULL)
3383a19ae75SLionel Sambuc syntax(op->op_text, "argument expected");
3393a19ae75SLionel Sambuc
3403a19ae75SLionel Sambuc switch (op->op_num) {
3413a19ae75SLionel Sambuc case STREQ:
3423a19ae75SLionel Sambuc return strcmp(opnd1, opnd2) == 0;
3433a19ae75SLionel Sambuc case STRNE:
3443a19ae75SLionel Sambuc return strcmp(opnd1, opnd2) != 0;
3453a19ae75SLionel Sambuc case STRLT:
3463a19ae75SLionel Sambuc return strcmp(opnd1, opnd2) < 0;
3473a19ae75SLionel Sambuc case STRGT:
3483a19ae75SLionel Sambuc return strcmp(opnd1, opnd2) > 0;
3493a19ae75SLionel Sambuc case INTEQ:
3503a19ae75SLionel Sambuc return getn(opnd1) == getn(opnd2);
3513a19ae75SLionel Sambuc case INTNE:
3523a19ae75SLionel Sambuc return getn(opnd1) != getn(opnd2);
3533a19ae75SLionel Sambuc case INTGE:
3543a19ae75SLionel Sambuc return getn(opnd1) >= getn(opnd2);
3553a19ae75SLionel Sambuc case INTGT:
3563a19ae75SLionel Sambuc return getn(opnd1) > getn(opnd2);
3573a19ae75SLionel Sambuc case INTLE:
3583a19ae75SLionel Sambuc return getn(opnd1) <= getn(opnd2);
3593a19ae75SLionel Sambuc case INTLT:
3603a19ae75SLionel Sambuc return getn(opnd1) < getn(opnd2);
3613a19ae75SLionel Sambuc case FILNT:
3623a19ae75SLionel Sambuc return newerf(opnd1, opnd2);
3633a19ae75SLionel Sambuc case FILOT:
3643a19ae75SLionel Sambuc return olderf(opnd1, opnd2);
3653a19ae75SLionel Sambuc case FILEQ:
3663a19ae75SLionel Sambuc return equalf(opnd1, opnd2);
3673a19ae75SLionel Sambuc default:
3683a19ae75SLionel Sambuc abort();
3693a19ae75SLionel Sambuc /* NOTREACHED */
3703a19ae75SLionel Sambuc }
3713a19ae75SLionel Sambuc }
3723a19ae75SLionel Sambuc
3733a19ae75SLionel Sambuc /*
3743a19ae75SLionel Sambuc * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits,
3753a19ae75SLionel Sambuc * not use access():
3763a19ae75SLionel Sambuc *
3773a19ae75SLionel Sambuc * True shall indicate only that the write flag is on. The file is not
3783a19ae75SLionel Sambuc * writable on a read-only file system even if this test indicates true.
3793a19ae75SLionel Sambuc *
3803a19ae75SLionel Sambuc * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only:
3813a19ae75SLionel Sambuc *
3823a19ae75SLionel Sambuc * True shall indicate that permission to read from file will be granted,
3833a19ae75SLionel Sambuc * as defined in "File Read, Write, and Creation".
3843a19ae75SLionel Sambuc *
3853a19ae75SLionel Sambuc * and that section says:
3863a19ae75SLionel Sambuc *
3873a19ae75SLionel Sambuc * When a file is to be read or written, the file shall be opened with an
3883a19ae75SLionel Sambuc * access mode corresponding to the operation to be performed. If file
3893a19ae75SLionel Sambuc * access permissions deny access, the requested operation shall fail.
3903a19ae75SLionel Sambuc *
3913a19ae75SLionel Sambuc * and of course access permissions are described as one might expect:
3923a19ae75SLionel Sambuc *
3933a19ae75SLionel Sambuc * * If a process has the appropriate privilege:
3943a19ae75SLionel Sambuc *
3953a19ae75SLionel Sambuc * * If read, write, or directory search permission is requested,
3963a19ae75SLionel Sambuc * access shall be granted.
3973a19ae75SLionel Sambuc *
3983a19ae75SLionel Sambuc * * If execute permission is requested, access shall be granted if
3993a19ae75SLionel Sambuc * execute permission is granted to at least one user by the file
4003a19ae75SLionel Sambuc * permission bits or by an alternate access control mechanism;
4013a19ae75SLionel Sambuc * otherwise, access shall be denied.
4023a19ae75SLionel Sambuc *
4033a19ae75SLionel Sambuc * * Otherwise:
4043a19ae75SLionel Sambuc *
4053a19ae75SLionel Sambuc * * The file permission bits of a file contain read, write, and
4063a19ae75SLionel Sambuc * execute/search permissions for the file owner class, file group
4073a19ae75SLionel Sambuc * class, and file other class.
4083a19ae75SLionel Sambuc *
4093a19ae75SLionel Sambuc * * Access shall be granted if an alternate access control mechanism
4103a19ae75SLionel Sambuc * is not enabled and the requested access permission bit is set for
4113a19ae75SLionel Sambuc * the class (file owner class, file group class, or file other class)
4123a19ae75SLionel Sambuc * to which the process belongs, or if an alternate access control
4133a19ae75SLionel Sambuc * mechanism is enabled and it allows the requested access; otherwise,
4143a19ae75SLionel Sambuc * access shall be denied.
4153a19ae75SLionel Sambuc *
4163a19ae75SLionel Sambuc * and when I first read this I thought: surely we can't go about using
4173a19ae75SLionel Sambuc * open(O_WRONLY) to try this test! However the POSIX 1003.1-2001 Rationale
4183a19ae75SLionel Sambuc * section for test does in fact say:
4193a19ae75SLionel Sambuc *
4203a19ae75SLionel Sambuc * On historical BSD systems, test -w directory always returned false
4213a19ae75SLionel Sambuc * because test tried to open the directory for writing, which always
4223a19ae75SLionel Sambuc * fails.
4233a19ae75SLionel Sambuc *
4243a19ae75SLionel Sambuc * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX
4253a19ae75SLionel Sambuc * System III, and thus presumably also for BSD up to and including 4.3.
4263a19ae75SLionel Sambuc *
4273a19ae75SLionel Sambuc * Secondly I remembered why using open() and/or access() are bogus. They
4283a19ae75SLionel Sambuc * don't work right for detecting read and write permissions bits when called
4293a19ae75SLionel Sambuc * by root.
4303a19ae75SLionel Sambuc *
4313a19ae75SLionel Sambuc * Interestingly the 'test' in 4.4BSD was closer to correct (as per
4323a19ae75SLionel Sambuc * 1003.2-1992) and it was implemented efficiently with stat() instead of
4333a19ae75SLionel Sambuc * open().
4343a19ae75SLionel Sambuc *
4353a19ae75SLionel Sambuc * This was apparently broken in NetBSD around about 1994/06/30 when the old
4363a19ae75SLionel Sambuc * 4.4BSD implementation was replaced with a (arguably much better coded)
4373a19ae75SLionel Sambuc * implementation derived from pdksh.
4383a19ae75SLionel Sambuc *
4393a19ae75SLionel Sambuc * Note that modern pdksh is yet different again, but still not correct, at
4403a19ae75SLionel Sambuc * least not w.r.t. 1003.2-1992.
4413a19ae75SLionel Sambuc *
4423a19ae75SLionel Sambuc * As I think more about it and read more of the related IEEE docs I don't like
4433a19ae75SLionel Sambuc * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all. I very
4443a19ae75SLionel Sambuc * much prefer the original wording in 1003.2-1992. It is much more useful,
4453a19ae75SLionel Sambuc * and so that's what I've implemented.
4463a19ae75SLionel Sambuc *
4473a19ae75SLionel Sambuc * (Note that a strictly conforming implementation of 1003.1-2001 is in fact
4483a19ae75SLionel Sambuc * totally useless for the case in question since its 'test -w' and 'test -r'
4493a19ae75SLionel Sambuc * can never fail for root for any existing files, i.e. files for which 'test
4503a19ae75SLionel Sambuc * -e' succeeds.)
4513a19ae75SLionel Sambuc *
4523a19ae75SLionel Sambuc * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
4533a19ae75SLionel Sambuc * 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999),
4543a19ae75SLionel Sambuc * which is the latest copy I have, does carry the same suggested wording as is
4553a19ae75SLionel Sambuc * in 1003.1-2001, with its rationale saying:
4563a19ae75SLionel Sambuc *
4573a19ae75SLionel Sambuc * This change is a clarification and is the result of interpretation
4583a19ae75SLionel Sambuc * request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
4593a19ae75SLionel Sambuc *
4603a19ae75SLionel Sambuc * That interpretation can be found here:
4613a19ae75SLionel Sambuc *
4623a19ae75SLionel Sambuc * http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
4633a19ae75SLionel Sambuc *
4643a19ae75SLionel Sambuc * Not terribly helpful, unfortunately. I wonder who that fence sitter was.
4653a19ae75SLionel Sambuc *
4663a19ae75SLionel Sambuc * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
4673a19ae75SLionel Sambuc * PASC interpretation and appear to be gone against at least one widely used
4683a19ae75SLionel Sambuc * implementation (namely 4.4BSD). The problem is that for file access by root
4693a19ae75SLionel Sambuc * this means that if test '-r' and '-w' are to behave as if open() were called
4703a19ae75SLionel Sambuc * then there's no way for a shell script running as root to check if a file
4713a19ae75SLionel Sambuc * has certain access bits set other than by the grotty means of interpreting
4723a19ae75SLionel Sambuc * the output of 'ls -l'. This was widely considered to be a bug in V7's
4733a19ae75SLionel Sambuc * "test" and is, I believe, one of the reasons why direct use of access() was
4743a19ae75SLionel Sambuc * avoided in some more recent implementations!
4753a19ae75SLionel Sambuc *
4763a19ae75SLionel Sambuc * I have always interpreted '-r' to match '-w' and '-x' as per the original
4773a19ae75SLionel Sambuc * wording in 1003.2-1992, not the other way around. I think 1003.2b goes much
4783a19ae75SLionel Sambuc * too far the wrong way without any valid rationale and that it's best if we
4793a19ae75SLionel Sambuc * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
4803a19ae75SLionel Sambuc * open() since we already know very well how it will work -- existance of the
4813a19ae75SLionel Sambuc * file is all that matters to open() for root.
4823a19ae75SLionel Sambuc *
4833a19ae75SLionel Sambuc * Unfortunately the SVID is no help at all (which is, I guess, partly why
4843a19ae75SLionel Sambuc * we're in this mess in the first place :-).
4853a19ae75SLionel Sambuc *
4863a19ae75SLionel Sambuc * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
4873a19ae75SLionel Sambuc * access(name, 2) even though it also goes to much greater lengths for '-x'
4883a19ae75SLionel Sambuc * matching the 1003.2-1992 definition (which is no doubt where that definition
4893a19ae75SLionel Sambuc * came from).
4903a19ae75SLionel Sambuc *
4913a19ae75SLionel Sambuc * The ksh93 implementation uses access() for '-r' and '-w' if
4923a19ae75SLionel Sambuc * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
4933a19ae75SLionel Sambuc * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
4943a19ae75SLionel Sambuc */
4953a19ae75SLionel Sambuc static int
test_access(struct stat * sp,mode_t stmode)4963a19ae75SLionel Sambuc test_access(struct stat *sp, mode_t stmode)
4973a19ae75SLionel Sambuc {
4983a19ae75SLionel Sambuc gid_t *groups;
4993a19ae75SLionel Sambuc register int n;
5003a19ae75SLionel Sambuc uid_t euid;
5013a19ae75SLionel Sambuc int maxgroups;
5023a19ae75SLionel Sambuc
5033a19ae75SLionel Sambuc /*
5043a19ae75SLionel Sambuc * I suppose we could use access() if not running as root and if we are
5053a19ae75SLionel Sambuc * running with ((euid == uid) && (egid == gid)), but we've already
5063a19ae75SLionel Sambuc * done the stat() so we might as well just test the permissions
5073a19ae75SLionel Sambuc * directly instead of asking the kernel to do it....
5083a19ae75SLionel Sambuc */
5093a19ae75SLionel Sambuc euid = geteuid();
5103a19ae75SLionel Sambuc if (euid == 0) /* any bit is good enough */
5113a19ae75SLionel Sambuc stmode = (stmode << 6) | (stmode << 3) | stmode;
5123a19ae75SLionel Sambuc else if (sp->st_uid == euid)
5133a19ae75SLionel Sambuc stmode <<= 6;
5143a19ae75SLionel Sambuc else if (sp->st_gid == getegid())
5153a19ae75SLionel Sambuc stmode <<= 3;
5163a19ae75SLionel Sambuc else {
5173a19ae75SLionel Sambuc /* XXX stolen almost verbatim from ksh93.... */
5183a19ae75SLionel Sambuc /* on some systems you can be in several groups */
5193a19ae75SLionel Sambuc if ((maxgroups = getgroups(0, NULL)) <= 0)
5203a19ae75SLionel Sambuc maxgroups = NGROUPS_MAX; /* pre-POSIX system? */
5213a19ae75SLionel Sambuc groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
5223a19ae75SLionel Sambuc n = getgroups(maxgroups, groups);
5233a19ae75SLionel Sambuc while (--n >= 0) {
5243a19ae75SLionel Sambuc if (groups[n] == sp->st_gid) {
5253a19ae75SLionel Sambuc stmode <<= 3;
5263a19ae75SLionel Sambuc break;
5273a19ae75SLionel Sambuc }
5283a19ae75SLionel Sambuc }
5293a19ae75SLionel Sambuc free(groups);
5303a19ae75SLionel Sambuc }
5313a19ae75SLionel Sambuc
5323a19ae75SLionel Sambuc return sp->st_mode & stmode;
5333a19ae75SLionel Sambuc }
5343a19ae75SLionel Sambuc
5353a19ae75SLionel Sambuc static int
filstat(char * nm,enum token mode)5363a19ae75SLionel Sambuc filstat(char *nm, enum token mode)
5373a19ae75SLionel Sambuc {
5383a19ae75SLionel Sambuc struct stat s;
5393a19ae75SLionel Sambuc
5403a19ae75SLionel Sambuc if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
5413a19ae75SLionel Sambuc return 0;
5423a19ae75SLionel Sambuc
5433a19ae75SLionel Sambuc switch (mode) {
5443a19ae75SLionel Sambuc case FILRD:
5453a19ae75SLionel Sambuc return test_access(&s, S_IROTH);
5463a19ae75SLionel Sambuc case FILWR:
5473a19ae75SLionel Sambuc return test_access(&s, S_IWOTH);
5483a19ae75SLionel Sambuc case FILEX:
5493a19ae75SLionel Sambuc return test_access(&s, S_IXOTH);
5503a19ae75SLionel Sambuc case FILEXIST:
5513a19ae75SLionel Sambuc return 1; /* the successful lstat()/stat() is good enough */
5523a19ae75SLionel Sambuc case FILREG:
5533a19ae75SLionel Sambuc return S_ISREG(s.st_mode);
5543a19ae75SLionel Sambuc case FILDIR:
5553a19ae75SLionel Sambuc return S_ISDIR(s.st_mode);
5563a19ae75SLionel Sambuc case FILCDEV:
5573a19ae75SLionel Sambuc return S_ISCHR(s.st_mode);
5583a19ae75SLionel Sambuc case FILBDEV:
5593a19ae75SLionel Sambuc return S_ISBLK(s.st_mode);
5603a19ae75SLionel Sambuc case FILFIFO:
5613a19ae75SLionel Sambuc return S_ISFIFO(s.st_mode);
5623a19ae75SLionel Sambuc case FILSOCK:
5633a19ae75SLionel Sambuc return S_ISSOCK(s.st_mode);
5643a19ae75SLionel Sambuc case FILSYM:
5653a19ae75SLionel Sambuc return S_ISLNK(s.st_mode);
5663a19ae75SLionel Sambuc case FILSUID:
5673a19ae75SLionel Sambuc return (s.st_mode & S_ISUID) != 0;
5683a19ae75SLionel Sambuc case FILSGID:
5693a19ae75SLionel Sambuc return (s.st_mode & S_ISGID) != 0;
5703a19ae75SLionel Sambuc case FILSTCK:
5713a19ae75SLionel Sambuc return (s.st_mode & S_ISVTX) != 0;
5723a19ae75SLionel Sambuc case FILGZ:
5733a19ae75SLionel Sambuc return s.st_size > (off_t)0;
5743a19ae75SLionel Sambuc case FILUID:
5753a19ae75SLionel Sambuc return s.st_uid == geteuid();
5763a19ae75SLionel Sambuc case FILGID:
5773a19ae75SLionel Sambuc return s.st_gid == getegid();
5783a19ae75SLionel Sambuc default:
5793a19ae75SLionel Sambuc return 1;
5803a19ae75SLionel Sambuc }
5813a19ae75SLionel Sambuc }
5823a19ae75SLionel Sambuc
5833a19ae75SLionel Sambuc #define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text
5843a19ae75SLionel Sambuc
5853a19ae75SLionel Sambuc static int
compare1(const void * va,const void * vb)5863a19ae75SLionel Sambuc compare1(const void *va, const void *vb)
5873a19ae75SLionel Sambuc {
5883a19ae75SLionel Sambuc const unsigned char *a = va;
5893a19ae75SLionel Sambuc const unsigned char *b = VTOC(vb);
5903a19ae75SLionel Sambuc
5913a19ae75SLionel Sambuc return a[0] - b[0];
5923a19ae75SLionel Sambuc }
5933a19ae75SLionel Sambuc
5943a19ae75SLionel Sambuc static int
compare2(const void * va,const void * vb)5953a19ae75SLionel Sambuc compare2(const void *va, const void *vb)
5963a19ae75SLionel Sambuc {
5973a19ae75SLionel Sambuc const unsigned char *a = va;
5983a19ae75SLionel Sambuc const unsigned char *b = VTOC(vb);
5993a19ae75SLionel Sambuc int z = a[0] - b[0];
6003a19ae75SLionel Sambuc
6013a19ae75SLionel Sambuc return z ? z : (a[1] - b[1]);
6023a19ae75SLionel Sambuc }
6033a19ae75SLionel Sambuc
6043a19ae75SLionel Sambuc static struct t_op const *
findop(const char * s)6053a19ae75SLionel Sambuc findop(const char *s)
6063a19ae75SLionel Sambuc {
6073a19ae75SLionel Sambuc if (s[0] == '-') {
6083a19ae75SLionel Sambuc if (s[1] == '\0')
6093a19ae75SLionel Sambuc return NULL;
6103a19ae75SLionel Sambuc if (s[2] == '\0')
6113a19ae75SLionel Sambuc return bsearch(s + 1, mop2, __arraycount(mop2),
6123a19ae75SLionel Sambuc sizeof(*mop2), compare1);
6133a19ae75SLionel Sambuc else if (s[3] != '\0')
6143a19ae75SLionel Sambuc return NULL;
6153a19ae75SLionel Sambuc else
6163a19ae75SLionel Sambuc return bsearch(s + 1, mop3, __arraycount(mop3),
6173a19ae75SLionel Sambuc sizeof(*mop3), compare2);
6183a19ae75SLionel Sambuc } else {
6193a19ae75SLionel Sambuc if (s[1] == '\0')
6203a19ae75SLionel Sambuc return bsearch(s, cop, __arraycount(cop), sizeof(*cop),
6213a19ae75SLionel Sambuc compare1);
6223a19ae75SLionel Sambuc else if (strcmp(s, cop2[0].op_text) == 0)
6233a19ae75SLionel Sambuc return cop2;
6243a19ae75SLionel Sambuc else
6253a19ae75SLionel Sambuc return NULL;
6263a19ae75SLionel Sambuc }
6273a19ae75SLionel Sambuc }
6283a19ae75SLionel Sambuc
6293a19ae75SLionel Sambuc static enum token
t_lex(char * s)6303a19ae75SLionel Sambuc t_lex(char *s)
6313a19ae75SLionel Sambuc {
6323a19ae75SLionel Sambuc struct t_op const *op;
6333a19ae75SLionel Sambuc
6343a19ae75SLionel Sambuc if (s == NULL) {
6353a19ae75SLionel Sambuc t_wp_op = NULL;
6363a19ae75SLionel Sambuc return EOI;
6373a19ae75SLionel Sambuc }
6383a19ae75SLionel Sambuc
6393a19ae75SLionel Sambuc if ((op = findop(s)) != NULL) {
6403a19ae75SLionel Sambuc if (!((op->op_type == UNOP && isoperand()) ||
6413a19ae75SLionel Sambuc (op->op_num == LPAREN && *(t_wp+1) == 0))) {
6423a19ae75SLionel Sambuc t_wp_op = op;
6433a19ae75SLionel Sambuc return op->op_num;
6443a19ae75SLionel Sambuc }
6453a19ae75SLionel Sambuc }
6463a19ae75SLionel Sambuc t_wp_op = NULL;
6473a19ae75SLionel Sambuc return OPERAND;
6483a19ae75SLionel Sambuc }
6493a19ae75SLionel Sambuc
6503a19ae75SLionel Sambuc static int
isoperand(void)6513a19ae75SLionel Sambuc isoperand(void)
6523a19ae75SLionel Sambuc {
6533a19ae75SLionel Sambuc struct t_op const *op;
6543a19ae75SLionel Sambuc char *s, *t;
6553a19ae75SLionel Sambuc
6563a19ae75SLionel Sambuc if ((s = *(t_wp+1)) == 0)
6573a19ae75SLionel Sambuc return 1;
6583a19ae75SLionel Sambuc if ((t = *(t_wp+2)) == 0)
6593a19ae75SLionel Sambuc return 0;
6603a19ae75SLionel Sambuc if ((op = findop(s)) != NULL)
6613a19ae75SLionel Sambuc return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
6623a19ae75SLionel Sambuc return 0;
6633a19ae75SLionel Sambuc }
6643a19ae75SLionel Sambuc
6653a19ae75SLionel Sambuc /* atoi with error detection */
6663a19ae75SLionel Sambuc static long long
getn(const char * s)6673a19ae75SLionel Sambuc getn(const char *s)
6683a19ae75SLionel Sambuc {
6693a19ae75SLionel Sambuc char *p;
6703a19ae75SLionel Sambuc long long r;
6713a19ae75SLionel Sambuc
6723a19ae75SLionel Sambuc errno = 0;
6733a19ae75SLionel Sambuc r = strtoll(s, &p, 10);
6743a19ae75SLionel Sambuc
6753a19ae75SLionel Sambuc if (errno != 0)
6763a19ae75SLionel Sambuc if (errno == ERANGE && (r == LLONG_MAX || r == LLONG_MIN))
6773a19ae75SLionel Sambuc error("%s: out of range", s);
6783a19ae75SLionel Sambuc
6793a19ae75SLionel Sambuc while (isspace((unsigned char)*p))
6803a19ae75SLionel Sambuc p++;
6813a19ae75SLionel Sambuc
6823a19ae75SLionel Sambuc if (*p || p == s)
6833a19ae75SLionel Sambuc error("%s: bad number", s);
6843a19ae75SLionel Sambuc
6853a19ae75SLionel Sambuc return r;
6863a19ae75SLionel Sambuc }
6873a19ae75SLionel Sambuc
6883a19ae75SLionel Sambuc static int
newerf(const char * f1,const char * f2)6893a19ae75SLionel Sambuc newerf(const char *f1, const char *f2)
6903a19ae75SLionel Sambuc {
6913a19ae75SLionel Sambuc struct stat b1, b2;
6923a19ae75SLionel Sambuc
6933a19ae75SLionel Sambuc return (stat(f1, &b1) == 0 &&
6943a19ae75SLionel Sambuc stat(f2, &b2) == 0 &&
69584d9c625SLionel Sambuc timespeccmp(&b1.st_mtim, &b2.st_mtim, >));
6963a19ae75SLionel Sambuc }
6973a19ae75SLionel Sambuc
6983a19ae75SLionel Sambuc static int
olderf(const char * f1,const char * f2)6993a19ae75SLionel Sambuc olderf(const char *f1, const char *f2)
7003a19ae75SLionel Sambuc {
7013a19ae75SLionel Sambuc struct stat b1, b2;
7023a19ae75SLionel Sambuc
7033a19ae75SLionel Sambuc return (stat(f1, &b1) == 0 &&
7043a19ae75SLionel Sambuc stat(f2, &b2) == 0 &&
70584d9c625SLionel Sambuc timespeccmp(&b1.st_mtim, &b2.st_mtim, <));
7063a19ae75SLionel Sambuc }
7073a19ae75SLionel Sambuc
7083a19ae75SLionel Sambuc static int
equalf(const char * f1,const char * f2)7093a19ae75SLionel Sambuc equalf(const char *f1, const char *f2)
7103a19ae75SLionel Sambuc {
7113a19ae75SLionel Sambuc struct stat b1, b2;
7123a19ae75SLionel Sambuc
7133a19ae75SLionel Sambuc return (stat(f1, &b1) == 0 &&
7143a19ae75SLionel Sambuc stat(f2, &b2) == 0 &&
7153a19ae75SLionel Sambuc b1.st_dev == b2.st_dev &&
7163a19ae75SLionel Sambuc b1.st_ino == b2.st_ino);
7173a19ae75SLionel Sambuc }
718