|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 13993 (0x36a9)
Types: TextFile
Notes: UNIX file
Names: »find.y«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/find/find.y«
%{
/*
* Find all files in the given
* directory hierarchies that
* satisfy the given expression
* primaries.
*/
#include <stdio.h>
#include <stat.h>
#include <dir.h>
#include <pwd.h>
#include <grp.h>
#include "findnode.h"
#define NPRIM (sizeof(primaries)/sizeof(primaries[0]))
#define NARG 50
#define NRECUR 14 /* Maximum recursion depth before forking */
#define NFNAME 600 /* size of filename buffer */
#define FILEARG ((char *)EOF)
#define DAYSEC (60L*60L*24L) /* seconds in a day */
#define inode(f,v) lnode(FUN,f,v,NULL)
#define snode(f,s) lnode(FUN,f,0,s)
%}
%start command
%union {
NODE *nodeptr;
}
%left OR
%left AND
%left '!'
%token <nodeptr> NAME PERM TYPE LINKS USER GROUP SIZE INUM
%token <nodeptr> ATIME CTIME MTIME EXEC OK PRINT NEWER FUN NOP
%type <nodeptr> exp
%%
command:
exp '\n' { if (seflag)
code = $1; else
code = bnode(AND,$1,snode(xprint,NULL));
return;
}
| '\n' { code = snode(xprint, NULL); return; }
;
exp:
'(' exp ')' { $$ = $2; }
| '!' exp { $$ = bnode('!', $2, NULL); }
| exp OR exp { $$ = bnode(OR, $1, $3); }
| exp AND exp { $$ = bnode(AND, $1, $3); }
| NAME { $$ = snode(xname, next()); }
| PERM { $$ = onode(xperm); }
| TYPE { $$ = snode(xtype, next()); }
| LINKS { $$ = nnode(xlinks); }
| USER { $$ = getuser(); }
| GROUP { $$ = getgroup(); }
| SIZE { $$ = nnode(xsize); }
| INUM { $$ = nnode(xinum); }
| ATIME { $$ = nnode(xatime); }
| CTIME { $$ = nnode(xctime); }
| MTIME { $$ = nnode(xmtime); }
| EXEC { $$ = enode(0); }
| OK { $$ = enode(1); }
| PRINT { $$ = snode(xprint, NULL); seflag++; }
| NEWER { $$ = getnewer(); }
| NOP { $$ = snode(xnop, NULL); seflag++; }
;
%%
struct primary {
char *p_name;
int p_lval;
} primaries[] = {
"-name", NAME,
"-perm", PERM,
"-type", TYPE,
"-links", LINKS,
"-user", USER,
"-group", GROUP,
"-size", SIZE,
"-inum", INUM,
"-atime", ATIME,
"-ctime", CTIME,
"-mtime", MTIME,
"-exec", EXEC,
"-ok", OK,
"-print", PRINT,
"-newer", NEWER,
"-nop", NOP,
"-o", OR,
"-a", AND,
};
char **gav;
int gac;
int depth; /* Recursive depth */
struct stat sb;
char fname[NFNAME];
char *prompt;
char toodeep[] = "directory structure too deep to traverse";
char nospace[] = "out of memory";
time_t curtime;
NODE *code;
int seflag; /* Set if a side effect (print, exec) found */
char *next();
NODE *bnode();
NODE *enode();
NODE *lnode();
NODE *nnode();
NODE *onode();
NODE *getuser();
NODE *getgroup();
NODE *getnewer();
int xname();
int xperm();
int xtype();
int xlinks();
int xuser();
int xgroup();
int xsize();
int xinum();
int xatime();
int xctime();
int xmtime();
int xnewer();
int xexec();
int xprint();
int xnop();
char *getenv();
main(argc, argv)
char *argv[];
{
register int i;
register char *ap;
register int eargc;
for (i=1; i<argc; i++) {
ap = argv[i];
if (*ap == '-')
break;
if (ap[1]=='\0' && (*ap=='!' || *ap=='('))
break;
}
if ((eargc=i) < 2)
usage();
gav = argv+i;
gac = argc-i;
yyparse();
time(&curtime);
if ((prompt = getenv("PS1")) == NULL)
prompt = "> ";
for (i=1; i<eargc; i++)
find(argv[i]);
}
/*
* Lexical analyser
*/
yylex()
{
static int binop = 0;
static int ntoken = 0;
register char *ap;
struct primary *pp;
register int token;
if (ntoken) {
token = ntoken;
ntoken = 0;
} else if ((ap = next()) == NULL)
token = '\n';
else if (ap[1] == '\0')
token = ap[0];
else if (*ap == '-') {
for (pp = primaries; pp < &primaries[NPRIM]; pp++)
if (strcmp(pp->p_name, ap) == 0) {
token = pp->p_lval;
break;
}
if (pp >= &primaries[NPRIM])
ferr("`%s' is an illegal primary", ap);
} else
ferr("Illegal expression");
if (binop && token!=')' && token!='\n' && token!=OR && token!=AND) {
binop = 0;
ntoken = token;
return (AND);
}
if (token!=OR && token!=AND && token!='!' && token!='\n' && token!='(')
binop = 1; else
binop = 0;
return (token);
}
yyerror()
{
fprintf(stderr, "Primary expression syntax error\n");
usage();
}
/*
* Return the next argument from the arg list.
*/
char *
next()
{
if (gac < 1)
return (NULL);
gac--;
return (*gav++);
}
/*
* Produce a node consisting
* of an octal number.
*/
NODE *
onode(fun)
int (*fun)();
{
register char *ap;
register int num;
register NODE *np;
register int type;
char *aap;
if ((ap = next()) == NULL)
ferr("Missing octal permission");
aap = ap;
if (*ap == '-') {
ap++;
type = -1;
} else
type = 0;
num = 0;
while (*ap>='0' && *ap<='7')
num = num*8 + *ap++-'0';
if (*ap != '\0')
ferr("%s: bad octal permission", aap);
np = inode(fun, num);
np->n_un.n_val = num;
np->n_type = type;
return (np);
}
/*
* Get a number -- it also may be
* prefixed by `+' or `-' to
* represent quantities greater or
* less.
*/
NODE *
nnode(fun)
int (*fun);
{
register char *ap;
register int num = 0;
register int type = 0;
register NODE *np;
char *aap;
if ((ap = next()) == NULL)
ferr("Missing number");
aap = ap;
if (*ap == '+') {
type = 1;
ap++;
} else if (*ap == '-') {
type = -1;
ap++;
}
while (*ap>='0' && *ap<='9')
num = num*10 + *ap++ - '0';
if (*ap != '\0')
ferr("%s: invalid number", aap);
np = inode(fun, num);
np->n_type = type;
return (np);
}
/*
* Get a user name or number.
*/
NODE *
getuser()
{
register struct passwd *pwp;
register char *cp;
register int uid;
if ((cp = next()) == NULL)
ferr("Missing username");
if (*cp>='0' && *cp<='9')
uid = atoi(cp);
else {
if ((pwp = getpwnam(cp)) == NULL)
ferr("%s: bad user name", cp);
uid = pwp->pw_uid;
}
return (lnode(FUN, xuser, uid, NULL));
}
/*
* Get group
*/
NODE *
getgroup()
{
register struct group *grp;
register char *cp;
register int gid;
if ((cp = next()) == NULL)
ferr("Missing group name");
if (*cp>='0' && *cp<='9')
gid = atoi(cp);
else {
if ((grp = getgrnam(cp)) == NULL)
ferr("%s: bad group name", cp);
gid = grp->gr_gid;
}
return (lnode(FUN, xgroup, gid, NULL));
}
/*
* Get the time for the file used in
* the `-newer' primary.
*/
NODE *
getnewer()
{
register NODE *np;
register char *fn;
if ((fn = next()) == NULL)
ferr("Missing filename for `-newer'");
if (stat(fn, &sb) < 0)
ferr("%s: nonexistent", fn);
np = inode(xnewer, 0);
np->n_un.n_time = sb.st_mtime;
return (np);
}
/*
* Build an expression tree node (non-leaf).
*/
NODE *
bnode(op, left, right)
int op;
NODE *left, *right;
{
register NODE *np;
if ((np = malloc(sizeof (NODE))) == NULL)
ferr(nospace);
np->n_op = op;
np->n_left = left;
np->n_right = right;
np->n_un.n_val = 0;
return (np);
}
/*
* Build a leaf node in expression tree.
*/
NODE *
lnode(op, fn, val, str)
int (*fn)();
char *str;
{
register NODE *np;
if ((np = malloc(sizeof (NODE))) == NULL)
ferr(nospace);
np->n_left = np->n_right = NULL;
np->n_op = op;
np->n_fun = fn;
if (str != NULL)
np->n_un.n_str = str; else
np->n_un.n_val = val;
return (np);
}
/*
* Build an execution node
* for -ok or -exec.
*/
NODE *
enode(type)
{
register NODE *np;
register char **app;
register char *ap;
seflag++;
np = snode(xexec, NULL);
np->n_type = type;
if ((np->n_un.n_strp = (char**)malloc(sizeof(char*[NARG])))==NULL)
ferr(nospace);
app = np->n_un.n_strp;
for (;;) {
if ((ap = next()) == NULL)
ferr("Non-terminated -exec or -ok command list");
if (strcmp(ap, "{}") == 0)
ap = FILEARG;
else if (strcmp(ap, ";") == 0)
break;
if (app-np->n_un.n_strp >= NARG-1)
ferr("Too many -exec or -ok command arguments");
*app++ = ap;
}
*app = NULL;
return (np);
}
/*
* Execute find on a single
* pathname hierarchy.
*/
find(dir)
char *dir;
{
register char *ep, *cp;
cp = dir;
ep = fname;
while (*cp)
*ep++ = *cp++;
*ep = '\0';
if (stat(dir, &sb) < 0)
ferr("Cannot find directory `%s'", dir);
if ((sb.st_mode&S_IFMT) != S_IFDIR)
ferr("%s: not a directory", dir);
fentry(ep, &sb);
}
/*
* The pointer is the end pointer
* into the fname buffer.
* And the stat buffer is passed to this
* which traverses the directory hierarchy.
*/
fentry(ep, sbp)
char *ep;
struct stat *sbp;
{
char *buildname();
register char *np;
register struct direct *dp;
register int nb;
int fd;
int dirflag;
char *iobuf;
if (sbp != NULL) {
dirflag = (sbp->st_mode&S_IFMT)==S_IFDIR;
execute(code);
} else
dirflag = 1;
if (dirflag) {
if (++depth >= NRECUR) {
depth = 0;
ffork(ep, sbp);
return;
}
if ((fd = open(fname, 0)) < 0) {
fmsg("%s: cannot open directory", fname);
return;
}
if ((iobuf = malloc(BUFSIZ)) == NULL)
ferr(nospace);
while ((nb = read(fd, iobuf, BUFSIZ)) > 0)
for (dp = iobuf; dp < &iobuf[nb]; dp++) {
if (dp->d_ino == 0)
continue;
np = dp->d_name;
if (*np++=='.'
&& (*np=='\0' || (*np++=='.' && *np=='\0')))
continue;
np = buildname(dp, ep);
if (stat(fname, &sb) < 0) {
fmsg("%s: cannot stat", fname);
continue;
}
fentry(np, &sb);
}
if (nb < 0)
fmsg("%s: directory read error", fname);
free(iobuf);
*ep = '\0';
close(fd);
depth--;
}
}
/*
* Fork to do a find on recursive directory
* structure that is too deep to fit into
* user's open files.
*/
ffork(ep, sbp)
char *ep;
struct stat *sbp;
{
register int i;
register int pid;
int status;
fflush(stdout);
if ((pid = fork()) < 0) {
fmsg(toodeep);
return;
}
if (pid) {
while (wait(&status) >= 0)
;
if (status)
fmsg("panic: child failed: %o", status);
return;
}
for (i=3; i<_NFILE; i++)
close(i);
fentry(ep, (struct stat *)NULL);
fflush(stdout);
exit(0);
}
/*
* Build up the next entry
* in the name.
*/
char *
buildname(dp, ep)
struct direct *dp;
register char *ep;
{
register char *cp = dp->d_name;
register unsigned n = DIRSIZ;
if (ep+DIRSIZ+2 >= &fname[NFNAME]) {
fmsg(toodeep);
return (NULL);
}
if (ep>fname && ep[-1]!='/')
*ep++ = '/';
do {
if (*cp == '\0')
break;
*ep++ = *cp++;
} while (--n);
*ep = '\0';
return (ep);
}
/*
* Execute compiled code.
*/
execute(np)
register NODE *np;
{
switch (np->n_op) {
case AND:
if (execute(np->n_left) && execute(np->n_right))
return (1);
return (0);
case OR:
if (execute(np->n_left) || execute(np->n_right))
return (1);
return (0);
case '!':
return (!execute(np->n_left));
case FUN:
return ((*np->n_fun)(np));
default:
ferr("Panic: bad expression tree (op %d)", np->n_op);
}
/* NOTREACHED */
}
/*
* Check for a match on the filename
*/
xname(np)
NODE *np;
{
register char *ep;
ep = fname;
while (*ep != '\0')
ep++;
while (ep>fname && *--ep!='/')
;
if (*ep == '/')
ep++;
return (pnmatch(ep, np->n_un.n_str, 0));
}
/*
* Compare the mode for a match again
* octal number `np->n_un.n_val'.
*/
xperm(np)
NODE *np;
{
register int onum;
register int mode;
mode = np->n_type<0 ? sb.st_mode&017777 : sb.st_mode&0777;
onum = np->n_un.n_val;
if (np->n_type < 0)
return ((mode&onum) == onum);
return (mode == onum);
}
/*
* Compare again filetypes
*/
xtype(np)
NODE *np;
{
register char *type;
register int ftype;
type = np->n_un.n_str;
ftype = sb.st_mode&S_IFMT;
if (type[1] == '\0')
switch (type[0]) {
case 'b':
return (ftype == S_IFBLK);
case 'c':
return (ftype == S_IFCHR);
case 'd':
return (ftype == S_IFDIR);
case 'f':
return (ftype == S_IFREG);
case 'm':
return (ftype==S_IFMPB || ftype==S_IFMPC);
case 'p':
return (ftype == S_IFPIP);
}
ferr("Bad file type ▶08◀ `%s'", type);
}
/*
* Compare link counts.
*/
xlinks(np)
NODE *np;
{
return (ncomp(np, sb.st_nlink));
}
/*
* Compare uid.
*/
xuser(np)
NODE *np;
{
return (np->n_un.n_val == sb.st_uid);
}
/*
* Compare group id of file
* with given one.
*/
xgroup(np)
NODE *np;
{
return (np->n_un.n_val == sb.st_gid);
}
/*
* Compare size of file in blocks
* with given.
*/
xsize(np)
NODE *np;
{
register int fsize;
fsize = (sb.st_size+BUFSIZ-1)/BUFSIZ;
return (ncomp(np, fsize));
}
/*
* Compare the i-number of the file
* with that given.
*/
xinum(np)
NODE *np;
{
return (ncomp(np, sb.st_ino));
}
/*
* Return true if file has been accessed
* in `n' days.
*/
xatime(np)
NODE *np;
{
return (ndays(np, sb.st_atime));
}
/*
* Return non-zero if file has been created
* in `n' days.
*/
xctime(np)
NODE *np;
{
return (ndays(np, sb.st_ctime));
}
/*
* Return true if file has been modified
* in `n' days.
*/
xmtime(np)
NODE *np;
{
return (ndays(np, sb.st_mtime));
}
/*
* Execute a command based on the filename
*/
xexec(np)
NODE *np;
{
static char command[200];
register char *ap, **app;
register int c;
int ok;
command[0] = '\0';
app = np->n_un.n_strp;
while (*app != NULL) {
if ((ap = *app++) == FILEARG)
ap = fname;
strcat(command, ap);
if (*app != NULL)
strcat(command, " ");
}
if (np->n_type) {
printf("%s%s? ", prompt, command);
ok = (c = getchar()) == 'y';
while (c!='\n' && c!=EOF)
c = getchar();
if (!ok)
return (0);
}
return (!system(command));
}
/*
* Print the filename.
*/
/* ARGSUSED */
xprint(np)
NODE *np;
{
printf("%s\n", fname);
return (1);
}
xnop(np)
NODE *np;
{
return (1);
}
/*
* Return true if the file is newer than
* the given one.
*/
xnewer(np)
register NODE *np;
{
return (sb.st_mtime > np->n_un.n_time);
}
/*
* Do a numerical comparison on dates.
*/
ndays(np, t)
register NODE *np;
time_t t;
{
register int days;
days = (curtime-t+DAYSEC/2)/DAYSEC;
return (ncomp(np, days));
}
/*
* Numerical compare.
*/
ncomp(np, val)
register NODE *np;
register unsigned val;
{
if (np->n_type == 0)
return (np->n_un.n_val == val);
if (np->n_type > 0)
return (val > np->n_un.n_val);
return (val < np->n_un.n_val);
}
/*
* Errors
*/
/* VARARGS */
ferr(x)
{
fprintf(stderr, "find: %r\n", &x);
exit (1);
}
/* VARARGS */
fmsg(x)
{
fprintf(stderr, "find: %r\n", &x);
}
usage()
{
fprintf(stderr, "Usage: find directory ... [ expression ]\n");
}