erikb@botter.UUCP (06/15/87)
Enclosed you find an almost version-7 compatible find.c; the differences are
listed below. Thanks to Menno Wilcke for testing the program on Minix.
Erik Baalbergen
--8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----
/* Minix' find -- Author: Erik Baalbergen */
static char rcsid[] = "$Header: find.c,v 1.6 87/06/15 08:13:22 erikb Exp $";
/*
*** Check the switches in the SWITCHES section below. ***
Differences from UNIX version 7 find(1):
* -name: no name allowed; only uid
* conjunction of predicates: the -a flag is necessary
e.g. find all core files: "find . -name core -a -print"
The "-atime" may not work well on Minix.
Please report bugs and suggestions to erikb@cs.vu.nl
*/
#include <stdio.h>
/*######################## SWITCHES ##############################*/
#ifdef UNIX
#include <sys/types.h>
#include <sys/stat.h>
#define SHELL "/bin/sh"
#else !UNIX
#include <stat.h>
#define SHELL "/usr/bin/sh"
#endif
#define PLEN 256 /* maximum path length; overflows are not detected */
#define DIRSIZ 16 /* size of a directory entry */
#define MAXARG 256 /* maximum length for an argv */
#define NPATHS 256 /* maximum number of paths in path-list */
/*######################## DEFINITIONS ##############################*/
#define SECS_PER_DAY (24L * 60L * 60L) /* check your planet */
struct exec {
int e_cnt;
char *e_vec[MAXARG];
};
#define OP_NAME 1
#define OP_PERM 2
#define OP_TYPE 3
#define OP_LINKS 4
#define OP_USER 5
#define OP_GROUP 6
#define OP_SIZE 7
#define OP_INUM 8
#define OP_ATIME 9
#define OP_MTIME 10
#define OP_EXEC 11
#define OP_OK 12
#define OP_PRINT 13
#define OP_NEWER 14
#define OP_AND 15
#define OP_OR 16
struct oper {
char *op_str;
int op_val;
} ops[] = {
{"name", OP_NAME},
{"perm", OP_PERM},
{"type", OP_TYPE},
{"links", OP_LINKS},
{"user", OP_USER},
{"group", OP_GROUP},
{"size", OP_SIZE},
{"inum", OP_INUM},
{"atime", OP_ATIME},
{"mtime", OP_MTIME},
{"exec", OP_EXEC},
{"ok", OP_OK},
{"print", OP_PRINT},
{"newer", OP_NEWER},
{"a", OP_AND},
{"o", OP_OR},
{0, 0}
};
#define EOI -1
#define NONE 0
#define LPAR 20
#define RPAR 21
#define NOT 22
char *prog, *strcpy(), *Malloc(), *find_bin();
struct node {
int n_type; /* any OP_ or NOT */
union {
char *n_str;
struct {
long n_val;
int n_sign;
} n_int;
struct exec *n_exec;
struct {
struct node *n_left, *n_right;
} n_opnd;
} n_info;
};
struct node *expr();
char **ipp;
int tty; /* fd for /dev/tty when using -ok */
long current_time;
char *
Malloc(n)
{
char *malloc(), *m;
if ((m = malloc(n)) == 0)
fatal("out of memory");
return m;
}
char *
Salloc(s)
char *s;
{
return strcpy(Malloc(strlen(s) + 1), s);
}
main(argc, argv)
char *argv[];
{
char *pathlist[NPATHS];
int pathcnt = 0, i;
struct node *pred;
prog = *argv++;
while (--argc > 0 && **argv != '-')
pathlist[pathcnt++] = *argv++;
if (pathcnt == 0 || argc == 0)
fatal("use: path-list predicate-list");
ipp = argv;
time(¤t_time);
pred = expr(lex(*ipp));
if (lex(*++ipp) != EOI)
fatal("syntax error: garbage at end of predicate");
for (i = 0; i < pathcnt; i++)
find(pathlist[i], pred, "");
exit(0);
}
find(path, pred, last)
char *path, *last;
struct node *pred;
{
char spath[PLEN], ent[DIRSIZ + 1];
struct stat st;
register char *send = spath;
FILE *fp, *fopen();
if (path[1] == '\0' && *path == '/') {
*send++ = '/';
*send = '\0';
}
else
while (*send++ = *path++) {}
if (stat(spath, &st) == -1)
nonfatal("can't get status of %s", spath);
else {
(void) check(spath, &st, pred, last);
if ((st.st_mode & S_IFMT) == S_IFDIR) {
if ((fp = fopen(spath, "r")) == NULL) {
nonfatal("can't read directory %s", spath);
return;
}
send[-1] = '/';
ent[DIRSIZ] = '\0';
while (fread(ent, DIRSIZ, 1, fp) == 1) {
if (!((*ent == '\0' && ent[1] == '\0')
|| (ent[2] == '.') &&
(ent[3] == '\0'
|| (ent[3] == '.' && ent[4] == '\0'))
)) {
strcpy(send, ent + 2);
find(spath, pred, ent + 2);
}
}
fclose(fp);
}
}
}
check(path, st, n, last)
char *path, *last;
struct stat *st;
struct node *n;
{
switch (n->n_type) {
case OP_AND:
return check(path, st, n->n_info.n_opnd.n_left, last) &&
check(path, st, n->n_info.n_opnd.n_right, last);
case OP_OR:
return check(path, st, n->n_info.n_opnd.n_left, last) ||
check(path, st, n->n_info.n_opnd.n_right, last);
case NOT:
return !check(path, st, n->n_info.n_opnd.n_left, last);
case OP_NAME:
return smatch(last, n->n_info.n_str);
case OP_PERM:
if (n->n_info.n_int.n_sign < 0)
return st->st_mode == (int) n->n_info.n_int.n_val;
return (st->st_mode & 0777) == (int) n->n_info.n_int.n_val;
case OP_NEWER:
return st->st_mtime > n->n_info.n_int.n_val;
case OP_TYPE:
return (st->st_mode & S_IFMT) == n->n_info.n_int.n_val;
case OP_LINKS:
return ichk((long)(st->st_nlink), n);
case OP_USER:
return st->st_uid == n->n_info.n_int.n_val;
case OP_GROUP:
return st->st_gid == n->n_info.n_int.n_val;
case OP_SIZE:
return ichk((st->st_size == 0) ? 0L :
((st->st_size - 1) / 1024 + 1), n);
case OP_INUM:
return ichk((long)(st->st_ino), n);
case OP_ATIME:
return ichk(st->st_atime, n);
case OP_MTIME:
return ichk(st->st_mtime, n);
case OP_EXEC:
case OP_OK:
return execute(n->n_type, n->n_info.n_exec, path);
case OP_PRINT:
printf("%s\n", path);
return 1;
}
fatal("ILLEGAL NODE");
}
ichk(val, n)
long val;
struct node *n;
{
switch (n->n_info.n_int.n_sign) {
case 0:
return val == n->n_info.n_int.n_val;
case 1:
return val > n->n_info.n_int.n_val;
case -1:
return val < n->n_info.n_int.n_val;
}
fatal("internal: bad n_sign");
}
lex(str)
char *str;
{
if (str == 0)
return EOI;
if (*str == '-') {
register struct oper *op;
str++;
for (op = ops; op->op_str; op++)
if (strcmp(str, op->op_str) == 0)
break;
return op->op_val;
}
if (str[1] == 0) {
switch(*str) {
case '(':
return LPAR;
case ')':
return RPAR;
case '!':
return NOT;
}
}
return NONE;
}
struct node *
newnode(t)
{
struct node *n = (struct node*) Malloc(sizeof(struct node));
n->n_type = t;
return n;
}
/*########################### PARSER ###################################*/
/* grammar:
expr : primary | primary OR expr;
primary : secondary | secondary AND primary;
secondary : NOT secondary | LPAR expr RPAR | simple;
simple : -OP args...
*/
struct node *expr(), *primary(), *secondary(), *simple();
number(str, base, pl, ps)
char *str;
long *pl;
int *ps;
{
int up = '0' + base - 1;
long val = 0;
*ps = ((*str == '-' || *str == '+') ? ((*str++ == '-') ? -1 : 1) : 0);
while (*str >= '0' && *str <= up)
val = base * val + *str++ - '0';
if (*str)
fatal("syntax error: illegal numeric value");
*pl = val;
}
struct node *
expr(t)
{
struct node *nd, *p, *nd2;
nd = primary(t);
if ((t = lex(*++ipp)) == OP_OR) {
nd2 = expr(lex(*++ipp));
p = newnode(OP_OR);
p->n_info.n_opnd.n_left = nd;
p->n_info.n_opnd.n_right = nd2;
return p;
}
ipp--;
return nd;
}
struct node *
primary(t)
{
struct node *nd, *p, *nd2;
nd = secondary(t);
if ((t = lex(*++ipp)) == OP_AND) {
nd2 = primary(lex(*++ipp));
p = newnode(OP_AND);
p->n_info.n_opnd.n_left = nd;
p->n_info.n_opnd.n_right = nd2;
return p;
}
ipp--;
return nd;
}
struct node *
secondary(t)
{
struct node *n, *p;
if (t == LPAR) {
n = expr(lex(*++ipp));
if (lex(*++ipp) != RPAR)
fatal("syntax error, ) expected");
return n;
}
if (t == NOT) {
n = secondary(lex(*++ipp));
p = newnode(NOT);
p->n_info.n_opnd.n_left = n;
return p;
}
return simple(t);
}
checkarg(arg)
char *arg;
{
if (arg == 0)
fatal("syntax error, argument expected");
}
struct node *
simple(t)
{
struct node *p = newnode(t);
struct exec *e;
struct stat est;
long l;
switch (t) {
case OP_TYPE:
checkarg(*++ipp);
switch (**ipp) {
case 'b':
p->n_info.n_int.n_val = S_IFBLK;
break;
case 'c':
p->n_info.n_int.n_val = S_IFCHR;
break;
case 'd':
p->n_info.n_int.n_val = S_IFDIR;
break;
case 'f':
p->n_info.n_int.n_val = S_IFREG;
break;
default:
fatal("-type needs b, c, d or f");
}
break;
case OP_LINKS:
case OP_USER:
case OP_GROUP:
case OP_SIZE:
case OP_INUM:
case OP_PERM:
checkarg(*++ipp);
number(*ipp, (t == OP_PERM) ? 8 : 10, &(p->n_info.n_int.n_val),
&(p->n_info.n_int.n_sign));
break;
case OP_ATIME:
case OP_MTIME:
checkarg(*++ipp);
number(*ipp, 10, &l, &(p->n_info.n_int.n_sign));
p->n_info.n_int.n_val = current_time - l * SECS_PER_DAY;
/* more than n days old means less than the absolute time */
p->n_info.n_int.n_sign *= -1;
break;
case OP_EXEC:
case OP_OK:
checkarg(*++ipp);
e = (struct exec *)Malloc(sizeof(struct exec));
e->e_cnt = 2;
e->e_vec[0] = SHELL;
p->n_info.n_exec = e;
while (*ipp) {
if (**ipp == ';' && (*ipp)[1] == '\0') {
e->e_vec[e->e_cnt] = 0;
break;
}
e->e_vec[(e->e_cnt)++] =
(**ipp == '{' && (*ipp)[1] == '}'
&& (*ipp)[2] == '\0') ? (char *)(-1) : *ipp;
ipp++;
}
if (*ipp == 0)
fatal("-exec/-ok: ; missing");
if ((e->e_vec[1] = find_bin(e->e_vec[2])) == 0)
fatal("can't find program %s", e->e_vec[2]);
if (t == OP_OK)
if ((tty = open("/dev/tty", 2)) < 0)
fatal("can't open /dev/tty");
break;
case OP_NEWER:
checkarg(*++ipp);
if (stat(*ipp, &est) == -1)
fatal("-newer: can't get status of %s\n", *ipp);
p->n_info.n_int.n_val = est.st_mtime;
break;
case OP_NAME:
checkarg(*++ipp);
p->n_info.n_str = *ipp;
break;
case OP_PRINT:
break;
default:
fatal("syntax error, operator expected");
}
return p;
}
/*######################## DIAGNOSTICS ##############################*/
nonfatal(s, a)
char *s, *a;
{
fprintf(stderr, "%s: ", prog);
fprintf(stderr, s, a);
fprintf(stderr, "\n");
}
fatal(s, a)
char *s, *a;
{
nonfatal(s, a);
exit(1);
}
/*################### SMATCH #########################*/
/* Don't try to understand the following one... */
smatch(s, t) /* shell-like matching */
char *s, *t;
{
register n;
if (*t == '\0')
return *s == '\0';
if (*t == '*') {
++t;
do
if (smatch(s,t))
return 1;
while (*s++ != '\0');
return 0;
}
if (*s == '\0')
return 0;
if (*t == '\\')
return (*s == *++t) ? smatch(++s, ++t) : 0;
if (*t == '?')
return smatch(++s, ++t);
if (*t == '[') {
while (*++t != ']') {
if (*t == '\\')
++t;
if (*(t+1) != '-')
if (*t == *s) {
while (*++t != ']')
if (*t == '\\')
++t;
return smatch(++s, ++t);
}
else
continue;
if (*(t+2) == ']')
return (*s == *t || *s == '-');
n = (*(t+2) == '\\') ? 3 : 2;
if (*s >= *t && *s <= *(t+n)) {
while (*++t != ']')
if (*t == '\\')
++t;
return smatch(++s, ++t);
}
t += n;
}
return 0;
}
return (*s == *t) ? smatch(++s, ++t) : 0;
}
/*####################### EXECUTE ###########################*/
/* do -exec or -ok */
char *epath = 0;
char *
getpath()
{
extern char **environ;
register char **e = environ;
if (epath) /* retrieve PATH only once */
return epath;
while (*e) {
if (strncmp("PATH=", *e, 5) == 0)
return epath = *e + 5;
e++;
}
fatal("can't get PATH from environment");
}
char *
find_bin(s)
char *s;
{
char *f, *l, buf[PLEN];
if (*s == '/') /* absolute path name */
return (access(s, 1) == 0) ? s : 0;
l = f = getpath();
for (;;) {
if (*l == ':' || *l == 0) {
if (l == f) {
if (access(s, 1) == 0)
return Salloc(s);
}
else {
register char *p = buf, *q = s;
while (f != l)
*p++ = *f++;
f++;
*p++ = '/';
while (*p++ = *q++) {}
if (access(buf, 1) == 0)
return Salloc(buf);
}
if (*l == 0)
break;
}
l++;
}
return 0;
}
execute(op, e, path)
struct exec *e;
char *path;
{
int s, pid;
char *argv[MAXARG];
register char **p, **q;
for (p = e->e_vec, q = argv; *p;) /* replace the {}s */
if ((*q++ = *p++) == (char *)-1)
q[-1] = path;
*q = '\0';
if (op == OP_OK) {
char answer[10];
for (p = &argv[2]; *p; p++) {
write(tty, *p, strlen(*p));
write(tty, " ", 1);
}
write(tty, "? ", 2);
if (read(tty, answer, 10) < 2 || *answer != 'y')
return 0;
}
if ((pid = fork()) == -1)
fatal("can't fork");
if (pid == 0) {
register i = 3;
while (close(i++) == 0) {} /* wow!!! */
execv(argv[1], &argv[2]); /* binary itself? */
execv(argv[0], &argv[1]); /* shell command? */
fatal("exec failure: %s", argv[1]); /* none of them! */
exit(127);
}
return wait(&s) == pid && s == 0;
}