Remove NetBSD references from test utility

This commit is contained in:
Eirikr Hinngart 2025-05-30 22:51:48 -07:00
parent 5188a03964
commit dca7e0efec
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.
@ -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) if (*p || p == s)
error("%s: bad number", s); error("%s: bad number", s);
return r; 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);
} }