Merge pull request #30 from Oichkatzelesfrettschen/eirikr/remove-netbsd-files-from-bin/test

This commit is contained in:
Eirikr Hinngart 2025-05-30 22:51:57 -07:00 committed by GitHub
commit be9ec38ded
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 377 additions and 455 deletions

View File

@ -1,5 +1,5 @@
# $NetBSD: Makefile,v 1.11 2007/06/22 03:24:16 simonb Exp $ # Makefile for the test utility
# @(#)Makefile 8.1 (Berkeley) 5/31/93 # Derived from 4.4BSD sources
PROG= test PROG= test
SRCS= test.c SRCS= test.c

View File

@ -1,4 +1,4 @@
# $NetBSD: TEST.csh,v 1.2 1995/03/21 07:03:59 cgd Exp $ # Test script for the test utility
# @(#)TEST.csh 5.2 (Berkeley) 4/30/93 # @(#)TEST.csh 5.2 (Berkeley) 4/30/93
#alias t '/usr/src/bin/test/obj/test \!*; echo $status' #alias t '/usr/src/bin/test/obj/test \!*; echo $status'

View File

@ -1,4 +1,4 @@
.\" $NetBSD: test.1,v 1.30 2016/08/12 03:17:41 sevan Exp $ .\" Manual page for the test utility
.\" .\"
.\" Copyright (c) 1991, 1993 .\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.

View File

@ -1,19 +1,16 @@
/* $NetBSD: test.c,v 1.41 2016/09/05 01:00:07 sevan Exp $ */ /* MINIX test utility derived from BSD sources */
/* /*
* test(1); version 7-like -- author Erik Baalbergen * test(1); version 7-like -- author Erik Baalbergen
* modified by Eric Gisin to be used as built-in. * modified by Eric Gisin to be used as built-in.
* modified by Arnold Robbins to add SVR3 compatibility * modified by Arnold Robbins to add SVR3 compatibility
* (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
* modified by J.T. Conklin for NetBSD. * modified by J.T. Conklin.
* *
* This program is in the Public Domain. * This program is in the Public Domain.
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: test.c,v 1.41 2016/09/05 01:00:07 sevan Exp $");
#endif
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@ -23,134 +20,107 @@ __RCSID("$NetBSD: test.c,v 1.41 2016/09/05 01:00:07 sevan Exp $");
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <locale.h> #include <locale.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdarg.h>
/* test(1) accepts the following grammar: /* test(1) accepts the following grammar:
oexpr ::= aexpr | aexpr "-o" oexpr ; oexpr ::= aexpr | aexpr "-o" oexpr ;
aexpr ::= nexpr | nexpr "-a" aexpr ; aexpr ::= nexpr | nexpr "-a" aexpr ;
nexpr ::= primary | "!" primary nexpr ::= primary | "!" primary
primary ::= unary-operator operand primary ::= unary-operator operand
| operand binary-operator operand | operand binary-operator operand
| operand | operand
| "(" oexpr ")" | "(" oexpr ")"
; ;
unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
"-nt"|"-ot"|"-ef"; "-nt"|"-ot"|"-ef";
operand ::= <any legal UNIX file name> operand ::= <any legal UNIX file name>
*/ */
enum token { enum token {
EOI, EOI,
FILRD, FILRD,
FILWR, FILWR,
FILEX, FILEX,
FILEXIST, FILEXIST,
FILREG, FILREG,
FILDIR, FILDIR,
FILCDEV, FILCDEV,
FILBDEV, FILBDEV,
FILFIFO, FILFIFO,
FILSOCK, FILSOCK,
FILSYM, FILSYM,
FILGZ, FILGZ,
FILTT, FILTT,
FILSUID, FILSUID,
FILSGID, FILSGID,
FILSTCK, FILSTCK,
FILNT, FILNT,
FILOT, FILOT,
FILEQ, FILEQ,
FILUID, FILUID,
FILGID, FILGID,
STREZ, STREZ,
STRNZ, STRNZ,
STREQ, STREQ,
STRNE, STRNE,
STRLT, STRLT,
STRGT, STRGT,
INTEQ, INTEQ,
INTNE, INTNE,
INTGE, INTGE,
INTGT, INTGT,
INTLE, INTLE,
INTLT, INTLT,
UNOT, UNOT,
BAND, BAND,
BOR, BOR,
LPAREN, LPAREN,
RPAREN, RPAREN,
OPERAND OPERAND
}; };
enum token_types { enum token_types { UNOP, BINOP, BUNOP, BBINOP, PAREN };
UNOP,
BINOP,
BUNOP,
BBINOP,
PAREN
};
struct t_op { struct t_op {
const char *op_text; const char *op_text;
short op_num, op_type; short op_num, op_type;
}; };
static const struct t_op cop[] = { static const struct t_op cop[] = {
{"!", UNOT, BUNOP}, {"!", UNOT, BUNOP}, {"(", LPAREN, PAREN}, {")", RPAREN, PAREN},
{"(", LPAREN, PAREN}, {"<", STRLT, BINOP}, {"=", STREQ, BINOP}, {">", STRGT, BINOP},
{")", RPAREN, PAREN},
{"<", STRLT, BINOP},
{"=", STREQ, BINOP},
{">", STRGT, BINOP},
}; };
static const struct t_op cop2[] = { static const struct t_op cop2[] = {
{"!=", STRNE, BINOP}, {"!=", STRNE, BINOP},
}; };
static const struct t_op mop3[] = { static const struct t_op mop3[] = {
{"ef", FILEQ, BINOP}, {"ef", FILEQ, BINOP}, {"eq", INTEQ, BINOP}, {"ge", INTGE, BINOP},
{"eq", INTEQ, BINOP}, {"gt", INTGT, BINOP}, {"le", INTLE, BINOP}, {"lt", INTLT, BINOP},
{"ge", INTGE, BINOP}, {"ne", INTNE, BINOP}, {"nt", FILNT, BINOP}, {"ot", FILOT, BINOP},
{"gt", INTGT, BINOP},
{"le", INTLE, BINOP},
{"lt", INTLT, BINOP},
{"ne", INTNE, BINOP},
{"nt", FILNT, BINOP},
{"ot", FILOT, BINOP},
}; };
static const struct t_op mop2[] = { static const struct t_op mop2[] = {
{"G", FILGID, UNOP}, {"G", FILGID, UNOP}, {"L", FILSYM, UNOP},
{"L", FILSYM, UNOP}, {"O", FILUID, UNOP}, {"S", FILSOCK, UNOP},
{"O", FILUID, UNOP}, {"a", BAND, BBINOP}, {"b", FILBDEV, UNOP},
{"S", FILSOCK,UNOP}, {"c", FILCDEV, UNOP}, {"d", FILDIR, UNOP},
{"a", BAND, BBINOP}, {"e", FILEXIST, UNOP}, {"f", FILREG, UNOP},
{"b", FILBDEV,UNOP}, {"g", FILSGID, UNOP}, {"h", FILSYM, UNOP}, /* for backwards compat */
{"c", FILCDEV,UNOP}, {"k", FILSTCK, UNOP}, {"n", STRNZ, UNOP},
{"d", FILDIR, UNOP}, {"o", BOR, BBINOP}, {"p", FILFIFO, UNOP},
{"e", FILEXIST,UNOP}, {"r", FILRD, UNOP}, {"s", FILGZ, UNOP},
{"f", FILREG, UNOP}, {"t", FILTT, UNOP}, {"u", FILSUID, UNOP},
{"g", FILSGID,UNOP}, {"w", FILWR, UNOP}, {"x", FILEX, UNOP},
{"h", FILSYM, UNOP}, /* for backwards compat */ {"z", STREZ, UNOP},
{"k", FILSTCK,UNOP},
{"n", STRNZ, UNOP},
{"o", BOR, BBINOP},
{"p", FILFIFO,UNOP},
{"r", FILRD, UNOP},
{"s", FILGZ, UNOP},
{"t", FILTT, UNOP},
{"u", FILSUID,UNOP},
{"w", FILWR, UNOP},
{"x", FILEX, UNOP},
{"z", STREZ, UNOP},
}; };
static char **t_wp; static char **t_wp;
@ -177,197 +147,179 @@ extern void *ckmalloc(size_t);
#else #else
static void error(const char *, ...) __dead __printflike(1, 2); static void error(const char *, ...) __dead __printflike(1, 2);
static void static void error(const char *msg, ...) {
error(const char *msg, ...) va_list ap;
{
va_list ap;
va_start(ap, msg); va_start(ap, msg);
verrx(2, msg, ap); verrx(2, msg, ap);
/*NOTREACHED*/ /*NOTREACHED*/
va_end(ap); va_end(ap);
} }
static void *ckmalloc(size_t); static void *ckmalloc(size_t);
static void * static void *ckmalloc(size_t nbytes) {
ckmalloc(size_t nbytes) void *p = malloc(nbytes);
{
void *p = malloc(nbytes);
if (!p) if (!p)
error("Not enough memory!"); error("Not enough memory!");
return p; return p;
} }
#endif #endif
#ifdef SHELL #ifdef SHELL
int testcmd(int, char **); int testcmd(int, char **);
int int testcmd(int argc, char **argv)
testcmd(int argc, char **argv)
#else #else
int int main(int argc, char *argv[])
main(int argc, char *argv[])
#endif #endif
{ {
int res; int res;
const char *argv0; const char *argv0;
#ifdef SHELL #ifdef SHELL
argv0 = argv[0]; argv0 = argv[0];
#else #else
setprogname(argv[0]); setprogname(argv[0]);
(void)setlocale(LC_ALL, ""); (void)setlocale(LC_ALL, "");
argv0 = getprogname(); argv0 = getprogname();
#endif #endif
if (strcmp(argv0, "[") == 0) { if (strcmp(argv0, "[") == 0) {
if (strcmp(argv[--argc], "]")) if (strcmp(argv[--argc], "]"))
error("missing ]"); error("missing ]");
argv[argc] = NULL; argv[argc] = NULL;
} }
if (argc < 2) if (argc < 2)
return 1; return 1;
t_wp = &argv[1]; t_wp = &argv[1];
res = !oexpr(t_lex(*t_wp)); res = !oexpr(t_lex(*t_wp));
if (*t_wp != NULL && *++t_wp != NULL) if (*t_wp != NULL && *++t_wp != NULL)
syntax(*t_wp, "unexpected operator"); syntax(*t_wp, "unexpected operator");
return res; return res;
} }
static void static void syntax(const char *op, const char *msg) {
syntax(const char *op, const char *msg)
{
if (op && *op) if (op && *op)
error("%s: %s", op, msg); error("%s: %s", op, msg);
else else
error("%s", msg); error("%s", msg);
} }
static int static int oexpr(enum token n) {
oexpr(enum token n) int res;
{
int res;
res = aexpr(n); res = aexpr(n);
if (*t_wp == NULL) if (*t_wp == NULL)
return res; return res;
if (t_lex(*++t_wp) == BOR) if (t_lex(*++t_wp) == BOR)
return oexpr(t_lex(*++t_wp)) || res; return oexpr(t_lex(*++t_wp)) || res;
t_wp--; t_wp--;
return res; return res;
} }
static int static int aexpr(enum token n) {
aexpr(enum token n) int res;
{
int res;
res = nexpr(n); res = nexpr(n);
if (*t_wp == NULL) if (*t_wp == NULL)
return res; return res;
if (t_lex(*++t_wp) == BAND) if (t_lex(*++t_wp) == BAND)
return aexpr(t_lex(*++t_wp)) && res; return aexpr(t_lex(*++t_wp)) && res;
t_wp--; t_wp--;
return res; return res;
} }
static int static int nexpr(enum token n) {
nexpr(enum token n)
{
if (n == UNOT) if (n == UNOT)
return !nexpr(t_lex(*++t_wp)); return !nexpr(t_lex(*++t_wp));
return primary(n); return primary(n);
} }
static int static int primary(enum token n) {
primary(enum token n) enum token nn;
{ int res;
enum token nn;
int res;
if (n == EOI) if (n == EOI)
return 0; /* missing expression */ return 0; /* missing expression */
if (n == LPAREN) { if (n == LPAREN) {
if ((nn = t_lex(*++t_wp)) == RPAREN) if ((nn = t_lex(*++t_wp)) == RPAREN)
return 0; /* missing expression */ return 0; /* missing expression */
res = oexpr(nn); res = oexpr(nn);
if (t_lex(*++t_wp) != RPAREN) if (t_lex(*++t_wp) != RPAREN)
syntax(NULL, "closing paren expected"); syntax(NULL, "closing paren expected");
return res; return res;
} }
if (t_wp_op && t_wp_op->op_type == UNOP) { if (t_wp_op && t_wp_op->op_type == UNOP) {
/* unary expression */ /* unary expression */
if (*++t_wp == NULL) if (*++t_wp == NULL)
syntax(t_wp_op->op_text, "argument expected"); syntax(t_wp_op->op_text, "argument expected");
switch (n) { switch (n) {
case STREZ: case STREZ:
return strlen(*t_wp) == 0; return strlen(*t_wp) == 0;
case STRNZ: case STRNZ:
return strlen(*t_wp) != 0; return strlen(*t_wp) != 0;
case FILTT: case FILTT:
return isatty((int)getn(*t_wp)); return isatty((int)getn(*t_wp));
default: default:
return filstat(*t_wp, n); return filstat(*t_wp, n);
} }
} }
if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
return binop(); return binop();
} }
return strlen(*t_wp) > 0; return strlen(*t_wp) > 0;
} }
static int static int binop(void) {
binop(void) const char *opnd1, *opnd2;
{ struct t_op const *op;
const char *opnd1, *opnd2;
struct t_op const *op;
opnd1 = *t_wp; opnd1 = *t_wp;
(void) t_lex(*++t_wp); (void)t_lex(*++t_wp);
op = t_wp_op; op = t_wp_op;
if ((opnd2 = *++t_wp) == NULL) if ((opnd2 = *++t_wp) == NULL)
syntax(op->op_text, "argument expected"); syntax(op->op_text, "argument expected");
switch (op->op_num) { switch (op->op_num) {
case STREQ: case STREQ:
return strcmp(opnd1, opnd2) == 0; return strcmp(opnd1, opnd2) == 0;
case STRNE: case STRNE:
return strcmp(opnd1, opnd2) != 0; return strcmp(opnd1, opnd2) != 0;
case STRLT: case STRLT:
return strcmp(opnd1, opnd2) < 0; return strcmp(opnd1, opnd2) < 0;
case STRGT: case STRGT:
return strcmp(opnd1, opnd2) > 0; return strcmp(opnd1, opnd2) > 0;
case INTEQ: case INTEQ:
return getn(opnd1) == getn(opnd2); return getn(opnd1) == getn(opnd2);
case INTNE: case INTNE:
return getn(opnd1) != getn(opnd2); return getn(opnd1) != getn(opnd2);
case INTGE: case INTGE:
return getn(opnd1) >= getn(opnd2); return getn(opnd1) >= getn(opnd2);
case INTGT: case INTGT:
return getn(opnd1) > getn(opnd2); return getn(opnd1) > getn(opnd2);
case INTLE: case INTLE:
return getn(opnd1) <= getn(opnd2); return getn(opnd1) <= getn(opnd2);
case INTLT: case INTLT:
return getn(opnd1) < getn(opnd2); return getn(opnd1) < getn(opnd2);
case FILNT: case FILNT:
return newerf(opnd1, opnd2); return newerf(opnd1, opnd2);
case FILOT: case FILOT:
return olderf(opnd1, opnd2); return olderf(opnd1, opnd2);
case FILEQ: case FILEQ:
return equalf(opnd1, opnd2); return equalf(opnd1, opnd2);
default: default:
abort(); abort();
/* NOTREACHED */ /* NOTREACHED */
} }
} }
/* /*
@ -432,9 +384,8 @@ binop(void)
* 1003.2-1992) and it was implemented efficiently with stat() instead of * 1003.2-1992) and it was implemented efficiently with stat() instead of
* open(). * open().
* *
* This was apparently broken in NetBSD around about 1994/06/30 when the old * Earlier implementations diverged from the 4.4BSD version when
* 4.4BSD implementation was replaced with a (arguably much better coded) * the original code was replaced with one derived from pdksh.
* implementation derived from pdksh.
* *
* Note that modern pdksh is yet different again, but still not correct, at * Note that modern pdksh is yet different again, but still not correct, at
* least not w.r.t. 1003.2-1992. * least not w.r.t. 1003.2-1992.
@ -448,21 +399,21 @@ binop(void)
* totally useless for the case in question since its 'test -w' and 'test -r' * totally useless for the case in question since its 'test -w' and 'test -r'
* can never fail for root for any existing files, i.e. files for which 'test * can never fail for root for any existing files, i.e. files for which 'test
* -e' succeeds.) * -e' succeeds.)
* *
* The rationale for 1003.1-2001 suggests that the wording was "clarified" in * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
* 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999), * 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999),
* which is the latest copy I have, does carry the same suggested wording as is * which is the latest copy I have, does carry the same suggested wording as is
* in 1003.1-2001, with its rationale saying: * in 1003.1-2001, with its rationale saying:
* *
* This change is a clarification and is the result of interpretation * This change is a clarification and is the result of interpretation
* request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992. * request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
* *
* That interpretation can be found here: * That interpretation can be found here:
* *
* http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html * http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
* *
* Not terribly helpful, unfortunately. I wonder who that fence sitter was. * Not terribly helpful, unfortunately. I wonder who that fence sitter was.
* *
* Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
* PASC interpretation and appear to be gone against at least one widely used * PASC interpretation and appear to be gone against at least one widely used
* implementation (namely 4.4BSD). The problem is that for file access by root * implementation (namely 4.4BSD). The problem is that for file access by root
@ -472,17 +423,17 @@ binop(void)
* the output of 'ls -l'. This was widely considered to be a bug in V7's * the output of 'ls -l'. This was widely considered to be a bug in V7's
* "test" and is, I believe, one of the reasons why direct use of access() was * "test" and is, I believe, one of the reasons why direct use of access() was
* avoided in some more recent implementations! * avoided in some more recent implementations!
* *
* I have always interpreted '-r' to match '-w' and '-x' as per the original * I have always interpreted '-r' to match '-w' and '-x' as per the original
* wording in 1003.2-1992, not the other way around. I think 1003.2b goes much * wording in 1003.2-1992, not the other way around. I think 1003.2b goes much
* too far the wrong way without any valid rationale and that it's best if we * too far the wrong way without any valid rationale and that it's best if we
* stick with 1003.2-1992 and test the flags, and not mimic the behaviour of * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
* open() since we already know very well how it will work -- existance of the * open() since we already know very well how it will work -- existance of the
* file is all that matters to open() for root. * file is all that matters to open() for root.
* *
* Unfortunately the SVID is no help at all (which is, I guess, partly why * Unfortunately the SVID is no help at all (which is, I guess, partly why
* we're in this mess in the first place :-). * we're in this mess in the first place :-).
* *
* The SysV implementation (at least in the 'test' builtin in /bin/sh) does use * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
* access(name, 2) even though it also goes to much greater lengths for '-x' * access(name, 2) even though it also goes to much greater lengths for '-x'
* matching the 1003.2-1992 definition (which is no doubt where that definition * matching the 1003.2-1992 definition (which is no doubt where that definition
@ -492,226 +443,197 @@ binop(void)
* (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root. * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
* i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b). * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
*/ */
static int static int test_access(struct stat *sp, mode_t stmode) {
test_access(struct stat *sp, mode_t stmode) gid_t *groups;
{ register int n;
gid_t *groups; uid_t euid;
register int n; int maxgroups;
uid_t euid;
int maxgroups;
/* /*
* I suppose we could use access() if not running as root and if we are * I suppose we could use access() if not running as root and if we are
* running with ((euid == uid) && (egid == gid)), but we've already * running with ((euid == uid) && (egid == gid)), but we've already
* done the stat() so we might as well just test the permissions * done the stat() so we might as well just test the permissions
* directly instead of asking the kernel to do it.... * directly instead of asking the kernel to do it....
*/ */
euid = geteuid(); euid = geteuid();
if (euid == 0) /* any bit is good enough */ if (euid == 0) /* any bit is good enough */
stmode = (stmode << 6) | (stmode << 3) | stmode; stmode = (stmode << 6) | (stmode << 3) | stmode;
else if (sp->st_uid == euid) else if (sp->st_uid == euid)
stmode <<= 6; stmode <<= 6;
else if (sp->st_gid == getegid()) else if (sp->st_gid == getegid())
stmode <<= 3; stmode <<= 3;
else { else {
/* XXX stolen almost verbatim from ksh93.... */ /* XXX stolen almost verbatim from ksh93.... */
/* on some systems you can be in several groups */ /* on some systems you can be in several groups */
if ((maxgroups = getgroups(0, NULL)) <= 0) if ((maxgroups = getgroups(0, NULL)) <= 0)
maxgroups = NGROUPS_MAX; /* pre-POSIX system? */ maxgroups = NGROUPS_MAX; /* pre-POSIX system? */
groups = ckmalloc((maxgroups + 1) * sizeof(gid_t)); groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
n = getgroups(maxgroups, groups); n = getgroups(maxgroups, groups);
while (--n >= 0) { while (--n >= 0) {
if (groups[n] == sp->st_gid) { if (groups[n] == sp->st_gid) {
stmode <<= 3; stmode <<= 3;
break; break;
} }
} }
free(groups); free(groups);
} }
return sp->st_mode & stmode; return sp->st_mode & stmode;
} }
static int static int filstat(char *nm, enum token mode) {
filstat(char *nm, enum token mode) struct stat s;
{
struct stat s;
if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
return 0; return 0;
switch (mode) { switch (mode) {
case FILRD: case FILRD:
return test_access(&s, S_IROTH); return test_access(&s, S_IROTH);
case FILWR: case FILWR:
return test_access(&s, S_IWOTH); return test_access(&s, S_IWOTH);
case FILEX: case FILEX:
return test_access(&s, S_IXOTH); return test_access(&s, S_IXOTH);
case FILEXIST: case FILEXIST:
return 1; /* the successful lstat()/stat() is good enough */ return 1; /* the successful lstat()/stat() is good enough */
case FILREG: case FILREG:
return S_ISREG(s.st_mode); return S_ISREG(s.st_mode);
case FILDIR: case FILDIR:
return S_ISDIR(s.st_mode); return S_ISDIR(s.st_mode);
case FILCDEV: case FILCDEV:
return S_ISCHR(s.st_mode); return S_ISCHR(s.st_mode);
case FILBDEV: case FILBDEV:
return S_ISBLK(s.st_mode); return S_ISBLK(s.st_mode);
case FILFIFO: case FILFIFO:
return S_ISFIFO(s.st_mode); return S_ISFIFO(s.st_mode);
case FILSOCK: case FILSOCK:
return S_ISSOCK(s.st_mode); return S_ISSOCK(s.st_mode);
case FILSYM: case FILSYM:
return S_ISLNK(s.st_mode); return S_ISLNK(s.st_mode);
case FILSUID: case FILSUID:
return (s.st_mode & S_ISUID) != 0; return (s.st_mode & S_ISUID) != 0;
case FILSGID: case FILSGID:
return (s.st_mode & S_ISGID) != 0; return (s.st_mode & S_ISGID) != 0;
case FILSTCK: case FILSTCK:
return (s.st_mode & S_ISVTX) != 0; return (s.st_mode & S_ISVTX) != 0;
case FILGZ: case FILGZ:
return s.st_size > (off_t)0; return s.st_size > (off_t)0;
case FILUID: case FILUID:
return s.st_uid == geteuid(); return s.st_uid == geteuid();
case FILGID: case FILGID:
return s.st_gid == getegid(); return s.st_gid == getegid();
default: default:
return 1; return 1;
} }
} }
#define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text #define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text
static int static int compare1(const void *va, const void *vb) {
compare1(const void *va, const void *vb) const unsigned char *a = va;
{ const unsigned char *b = VTOC(vb);
const unsigned char *a = va;
const unsigned char *b = VTOC(vb);
return a[0] - b[0]; return a[0] - b[0];
} }
static int static int compare2(const void *va, const void *vb) {
compare2(const void *va, const void *vb) const unsigned char *a = va;
{ const unsigned char *b = VTOC(vb);
const unsigned char *a = va; int z = a[0] - b[0];
const unsigned char *b = VTOC(vb);
int z = a[0] - b[0];
return z ? z : (a[1] - b[1]); return z ? z : (a[1] - b[1]);
} }
static struct t_op const * static struct t_op const *findop(const char *s) {
findop(const char *s) if (s[0] == '-') {
{ if (s[1] == '\0')
if (s[0] == '-') { return NULL;
if (s[1] == '\0') if (s[2] == '\0')
return NULL; return bsearch(s + 1, mop2, __arraycount(mop2), sizeof(*mop2), compare1);
if (s[2] == '\0') else if (s[3] != '\0')
return bsearch(s + 1, mop2, __arraycount(mop2), return NULL;
sizeof(*mop2), compare1); else
else if (s[3] != '\0') return bsearch(s + 1, mop3, __arraycount(mop3), sizeof(*mop3), compare2);
return NULL; } else {
else if (s[1] == '\0')
return bsearch(s + 1, mop3, __arraycount(mop3), return bsearch(s, cop, __arraycount(cop), sizeof(*cop), compare1);
sizeof(*mop3), compare2); else if (strcmp(s, cop2[0].op_text) == 0)
} else { return cop2;
if (s[1] == '\0') else
return bsearch(s, cop, __arraycount(cop), sizeof(*cop), return NULL;
compare1); }
else if (strcmp(s, cop2[0].op_text) == 0)
return cop2;
else
return NULL;
}
} }
static enum token static enum token t_lex(char *s) {
t_lex(char *s) struct t_op const *op;
{
struct t_op const *op;
if (s == NULL) { if (s == NULL) {
t_wp_op = NULL; t_wp_op = NULL;
return EOI; return EOI;
} }
if ((op = findop(s)) != NULL) { if ((op = findop(s)) != NULL) {
if (!((op->op_type == UNOP && isoperand()) || if (!((op->op_type == UNOP && isoperand()) ||
(op->op_num == LPAREN && *(t_wp+1) == 0))) { (op->op_num == LPAREN && *(t_wp + 1) == 0))) {
t_wp_op = op; t_wp_op = op;
return op->op_num; return op->op_num;
} }
} }
t_wp_op = NULL; t_wp_op = NULL;
return OPERAND; return OPERAND;
} }
static int static int isoperand(void) {
isoperand(void) struct t_op const *op;
{ char *s, *t;
struct t_op const *op;
char *s, *t;
if ((s = *(t_wp+1)) == 0) if ((s = *(t_wp + 1)) == 0)
return 1; return 1;
if ((t = *(t_wp+2)) == 0) if ((t = *(t_wp + 2)) == 0)
return 0; return 0;
if ((op = findop(s)) != NULL) if ((op = findop(s)) != NULL)
return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0'); return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
return 0; return 0;
} }
/* atoi with error detection */ /* atoi with error detection */
static long long static long long getn(const char *s) {
getn(const char *s) char *p;
{ long long r;
char *p;
long long r;
errno = 0; errno = 0;
r = strtoll(s, &p, 10); r = strtoll(s, &p, 10);
if (errno != 0) if (errno != 0)
if (errno == ERANGE && (r == LLONG_MAX || r == LLONG_MIN)) if (errno == ERANGE && (r == LLONG_MAX || r == LLONG_MIN))
error("%s: out of range", s); error("%s: out of range", s);
while (isspace((unsigned char)*p)) while (isspace((unsigned char)*p))
p++; p++;
if (*p || p == s)
error("%s: bad number", s);
return r; if (*p || p == s)
error("%s: bad number", s);
return r;
} }
static int static int newerf(const char *f1, const char *f2) {
newerf(const char *f1, const char *f2) struct stat b1, b2;
{
struct stat b1, b2;
return (stat(f1, &b1) == 0 && return (stat(f1, &b1) == 0 && stat(f2, &b2) == 0 &&
stat(f2, &b2) == 0 && timespeccmp(&b1.st_mtim, &b2.st_mtim, >));
timespeccmp(&b1.st_mtim, &b2.st_mtim, >));
} }
static int static int olderf(const char *f1, const char *f2) {
olderf(const char *f1, const char *f2) struct stat b1, b2;
{
struct stat b1, b2;
return (stat(f1, &b1) == 0 && return (stat(f1, &b1) == 0 && stat(f2, &b2) == 0 &&
stat(f2, &b2) == 0 && timespeccmp(&b1.st_mtim, &b2.st_mtim, <));
timespeccmp(&b1.st_mtim, &b2.st_mtim, <));
} }
static int static int equalf(const char *f1, const char *f2) {
equalf(const char *f1, const char *f2) struct stat b1, b2;
{
struct stat b1, b2;
return (stat(f1, &b1) == 0 && return (stat(f1, &b1) == 0 && stat(f2, &b2) == 0 && b1.st_dev == b2.st_dev &&
stat(f2, &b2) == 0 && b1.st_ino == b2.st_ino);
b1.st_dev == b2.st_dev &&
b1.st_ino == b2.st_ino);
} }