ast@cs.vu.nl (Andy Tanenbaum) (06/15/87)
Here is a version of find written by Alistair Crooks.
Andy Tanenbaum (ast@cs.vu.nl)
------------------------ cut here for manual --------------------------------
NAME
find - find files
SYNOPSIS
find pathname-list expression
find name
DESCRIPTION
find can be used to find a (number of) file(s) in a file system
It is modelled after 4.3BSD's find, but has certain limitations.
These are listed at the end of this page. There are two forms
that the find command can take - the shortform, which is basically
find / -name "*name*" -print
in the longer form. The longer form has the following valid
expressions :
-type x
where x is bcdfgu for block special file, character
special file, directory, regular file, setgid file
and setuid file.
-name s
where the name of the file is s, which can contain
wile cards, same as sh(1).
-inum n
where n is the inode number of the file.
-mtime [+-]n
where the modification time of the file is n. +n means
more than n, -n means less than n, and n means exactly n.
n is the number of days.
-atime [+-]n
where the access time of the file is n. +n means
more than n, -n means less than n, and n means exactly n.
n is the number of days.
-ctime [+-]n
where the change time of the file is n. +n means
more than n, -n means less than n, and n means exactly n.
n is the number of days.
-size [+-]n[c]
where the size of the file is n. Default radix is blocks.
If the number is followed by the character 'c', the size
unit of bytes is used. +n means more than n (blocks), -n
means less than n (blocks), n means exactly n (blocks).
-links [+-]n
where the number of links possessed by a file is n. +n
means more than n, -n means less than n, and n means exactly
n.
-depth
Does a depth first search of the file system. Default is
to search a directory's entries after processing the directory
itself. This expression means the directory entries are
processed before the directory.
-prune
Does not progress the search into directories.
-newer f
where f is a file. Finds files that are newer than f.
-perm [-]n
where n is an octal number of the file permission form.
If the number is preceded by '-', a long permission
mode is used.
-user u
where u is a login name, or a uid. Finds files belonging
to user u.
-nouser
finds bastard files (files whose owners are not in
/etc/passwd).
-group a
where g is a group name or gid. Finds files belonging
to group g.
-nogroup
finds group-delinquent files. (files whose group is no
longer in /etc/group).
In addition, the expressions can have the following Boolean
operators:
-a
Boolean and operator.
-o
Boolean or operator.
( and )
group expressions together
!
Boolean not operator
Find also has the following actions:
-print
print the file name on standard output.
-exec
execute a Minix command. The file name can be interpreted
here by a pair of braces ("{}"). The command should (but
not necessarily be concluded by a quoted semi-colon ("\;").
-ok
prints the command on standard output, and waits for the
user to enter a character from the keyboard. If the character
is 'y', the command will be executed, otherwise it will not.
-ls
provides a long listing of all salient points about the file,
and is equivalent to the 4.3BSD Unix ls -gilds.
BUGS AND ENHANCEMENTS
1. Only one directory can be used as the starting point for the
search. This is relatively simple to cure.
2. Only one action can be specified (in contrast to the BSD
version) which views everything as an expression, and actions
as true expressions. This should be simple to cure as well.
3. The building of the internal search tree leaves a little to
be desired. I'm not sure bracketed groups are done in the right
way, and the code is a mess.
4. Build to run under many more different flavours of Unix.
AUTHOR
Alistair G. Crooks, Joypace Ltd, London, UK, 1987.
(agc@ist.co.uk, until July).
------------------------ cut here for find.c --------------------------------
/*
* find.c 1.1 7/6/87 agc Joypace Ltd.
*
* Copyright 1987, Joypace Ltd., London UK. All rights reserved.
* This code may be freely distributed, provided that this notice
* remains attached.
*
* find - a public domain interpretation of find(1).
*
* Compile-time flags: -DMINIX for Minix (V7 file system, etc)
* -DSUN for SunOS (dp_ino or dp_fileno)
* default env: BSD 4.3
*/
#include <stdio.h>
#ifdef MINIX
#include <blocksize.h>
#include <stat.h>
struct passwd {
char *pw_name;
char *pw_passwd;
int pw_uid;
int pw_gid;
char *pw_gecos;
char *pw_dir;
char *pw_shell;
};
struct group {
char *gr_name;
char *gr_passwd;
int gr_gid;
};
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <pwd.h>
#include <grp.h>
#define BLOCK_SIZE 1024
#endif
p
#define LINELEN 256 /* max no of chars in a line */
#define SHORTLINE 80 /* a shorter line */
#define DIRNAMELEN 14 /* max no of chars in a dir entry */
#define MAXCRIT 10 /* max no of search criteria */
#define MODELEN 11 /* length of mode readout */
#define NAMELEN 8 /* length of a user name readout */
#define SHORTMASK 0777 /* short file permission mask */
#define LONGMASK 017777 /* long file permission mask */
#define ISDIGIT(x) ((x) >= '0' && (x) <= '9')
#define ISOCT(c) ((c) >= '0' && (c) <= '7')
#define CR_TYPE 1 /* -type criterion */
#define CR_NAME 2 /* -name criterion */
#define CR_INUM 3 /* -inum criterion */
#define CR_MTIME 4 /* -mtime criterion */
#define CR_ATIME 5 /* -atime criterion */
#define CR_CTIME 6 /* -ctime criterion */
#define CR_SIZE 7 /* -size criterion */
#define CR_LINKS 8 /* -links criterion */
#define CR_DEPTH 9 /* -depth criterion */
#define CR_PRUNE 10 /* -prune criterion */
#define CR_NEWER 11 /* -newer criterion */
#define CR_PERM 12 /* -perm criterion */
#define CR_USER 13 /* -user criterion */
#define CR_NOUSER 14 /* -nouser criterion */
#define CR_GROUP 15 /* -group criterion */
#define CR_NOGROUP 16 /* -nogroup criterion */
#define ACT_PRINT 17 /* -print action */
#define ACT_EXEC 18 /* -exec action */
#define ACT_OK 19 /* -ok action */
#define ACT_LS 20 /* -ls action */
#define AR_LT 21 /* arithmetical < */
#define AR_EQ 22 /* arithmetical == */
#define AR_GT 23 /* arithmetical > */
#define OP_AND 24 /* and operator for node */
#define OP_OR 25 /* or operator for node */
#define OP_OPENB 26 /* open bracket operator for node */
#define OP_CLOSEB 27 /* close bracket operator for node */
#define OP_NOT 28 /* ! operator for node */
#define ND_OP 29 /* operator node */
#define ND_CRIT 30 /* criterion node */
p
typedef struct num {
long nm_num;
char nm_op;
} NUM;
typedef struct _critstr {
char cr_type;
char cr_not;
union {
char *cr_name;
NUM cr_n;
} cr_un;
} CRIT;
typedef struct _nodestr {
char nd_type;
union {
CRIT *nd_crit;
int nd_op;
} nd_un;
struct _nodestr *nd_left;
struct _nodestr *nd_right;
} NODE;
typedef struct _argstr {
char *ar_name;
int ar_argc;
int ar_ndtype;
int ar_attr;
} ARG;
#ifdef MINIX
typedef struct dir {
short inum;
char d_name[DIRNAMELEN];
} DIR;
DIR dir;
#endif
p
char *prog; /* program name */
char *startdir; /* dir to start from */
char cmd[LINELEN]; /* command to be executed */
char expanded[LINELEN]; /* command with expanded {}'s */
int act; /* type of action to take */
int bracec = 0; /* no of braces in cmd */
NODE *root; /* root of search criteria tree */
CRIT criteria[MAXCRIT]; /* search criteria */
int critc = 0; /* number of criteria */
int depthfirst = 0; /* depth first search of tree */
int prune = 0; /* stop at directories */
extern long time(); /* no see */
extern char *malloc(); /* shut lint up */
extern char *ctime(); /* ditto */
extern long atol(); /* nearly there */
p
ARG argtab[] = {
{"-o", 1, ND_OP, OP_OR},
{"-a", 1, ND_OP, OP_AND},
{"(", 1, ND_OP, OP_OPENB},
{")", 1, ND_OP, OP_CLOSEB},
{"!", 1, ND_OP, OP_NOT},
{"-name", 2, ND_CRIT, CR_NAME},
{"-type", 2, ND_CRIT, CR_TYPE},
{"-inum", 2, ND_CRIT, CR_INUM},
{"-mtime", 2, ND_CRIT, CR_MTIME},
{"-ctime", 2, ND_CRIT, CR_CTIME},
{"-atime", 2, ND_CRIT, CR_ATIME},
{"-size", 2, ND_CRIT, CR_SIZE},
{"-depth", 1, ND_CRIT, CR_DEPTH},
{"-prune", 1, ND_CRIT, CR_PRUNE},
{"-links", 2, ND_CRIT, CR_LINKS},
{"-newer", 2, ND_CRIT, CR_NEWER},
{"-perm", 2, ND_CRIT, CR_PERM},
{"-user", 2, ND_CRIT, CR_USER},
{"-nouser", 1, ND_CRIT, CR_NOUSER},
{"-group", 2, ND_CRIT, CR_GROUP},
{"-nogroup", 1, ND_CRIT, CR_NOGROUP},
{NULL, 0, 0, 0}
};
p
/*
* myindex - index gets corrupted by that used in scanf.
*/
char *
myindex(s, c)
register char *s;
register char c;
{
for ( ; *s ; s++)
if (*s == c)
return(s);
return(NULL);
}
p
#ifdef MINIX
/*
* system - re-implemented for Minix. Possibly quicker to use execl
* rather than splitting the command line, but seems to be quick
* enough.
*/
int
system(s)
char *s;
{
int pid;
int status;
switch(pid = fork()) {
case -1:
(void) fprintf(stderr, "%s: fork failed\n", prog);
return(127);
case 0 :
execl("/bin/sh", "sh", "-c", s, 0);
(void) fprintf(stderr, "%s: exec failed\n", prog);
return(127);
default :
while (wait(&status) != pid)
;
}
return(status);
}
#endif
p
#ifdef MINIX
/*
* getpwuid - get the passwd file entry with the same uid as usrid.
* Returns the address in static area if found, NULL otherwise.
*/
struct passwd *
getpwuid(usrid)
int usrid;
{
static struct passwd pw;
static char lbuf[100];
char *ptr;
int bin;
FILE *fp, *fopen();
if ((fp = fopen("/etc/passwd", "r")) == (FILE *) NULL)
return(NULL);
while (fgets(lbuf, sizeof(lbuf), fp) != NULL) {
for (ptr = pw.pw_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
for (*ptr++ = 0, pw.pw_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++)
bin = 10*bin + (*ptr - '0');
if (bin != usrid)
continue;
pw.pw_uid = bin;
for (bin = 0, ++ptr; *ptr && ISDIGIT(*ptr) ; ptr++)
bin = 10*bin + (*ptr - '0');
pw.pw_gid = bin;
for (pw.pw_gecos = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++)
;
for (*ptr = 0, pw.pw_dir = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++)
;
*ptr = 0 ;
for (pw.pw_shell = ++ptr; *ptr && *ptr != '\n' ; ptr++)
;
*ptr = '\0';
fclose(fp);
return(&pw);
}
fclose(fp);
return(NULL);
}
#endif
p
#ifdef MINIX
/*
* getpwnam - get passwd entry with same login name as s.
* Returns address in static area if found, NULL otherwise.
*/
struct passwd *
getpwnam(s)
char *s;
{
static struct passwd pw;
static char lbuf[100];
char *ptr;
int bin;
FILE *fp, *fopen();
if ((fp = fopen("/etc/passwd", "r")) == (FILE *) NULL)
return(NULL);
while (fgets(lbuf, sizeof(lbuf), fp) != NULL) {
for (ptr = pw.pw_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
*ptr++ = 0;
if (strcmp(s, pw.pw_name) != 0)
continue;
for (pw.pw_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++)
bin = 10*bin + (*ptr - '0');
pw.pw_uid = bin;
for (bin = 0, ++ptr; *ptr && ISDIGIT(*ptr) ; ptr++)
bin = 10*bin + (*ptr - '0');
pw.pw_gid = bin;
for (pw.pw_gecos = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++)
;
for (*ptr = 0, pw.pw_dir = ++ptr; *ptr && *ptr != ':' && *ptr != '\n' ; ptr++)
;
*ptr = 0 ;
pw.pw_shell = ++ptr;
fclose(fp);
return(&pw);
}
fclose(fp);
return(NULL);
}
#endif
p
#ifdef MINIX
/*
* getgrgid - get group entry with same uid as passed to it.
* Returns address of entry in static area if found, NULL otherwise.
*/
struct group *
getgrgid(gid)
int gid;
{
static struct group gp;
static char lbuf[100];
char *ptr;
int bin;
FILE *fp, *fopen();
if ((fp = fopen("/etc/group", "r")) == (FILE *) NULL)
return(NULL);
while (fgets(lbuf, sizeof(lbuf), fp) != NULL) {
for (ptr = gp.gr_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
for (*ptr++ = 0, gp.gr_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++)
bin = 10*bin + (*ptr - '0');
if (bin != gid)
continue;
gp.gr_gid = bin;
fclose(fp);
return(&gp);
}
fclose(fp);
return(NULL);
}
#endif
p
#ifdef MINIX
/*
* getgrnam - get the group file entry corresponding to group name s.
* Returns address of entry in static store if found, NULL otherwise.
*/
struct group *
getgrnam(s)
char *s;
{
static struct group gp;
static char lbuf[100];
char *ptr;
int bin;
FILE *fp, *fopen();
if ((fp = fopen("/etc/group", "r")) == (FILE *) NULL)
return(NULL);
while (fgets(lbuf, sizeof(lbuf), fp) != NULL) {
for (ptr = gp.gr_name = lbuf; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
*ptr++ = 0;
if (strcmp(s, gp.gr_name) != 0)
continue;
for (gp.gr_passwd = ptr ; *ptr != 0 && *ptr != ':' ; ptr++)
;
if (*ptr == 0)
continue;
for (bin = 0, *ptr++ = 0 ; *ptr && ISDIGIT(*ptr) ; ptr++)
bin = 10*bin + (*ptr - '0');
gp.gr_gid = bin;
fclose(fp);
return(&gp);
}
fclose(fp);
return(NULL);
}
#endif
#ifdef MINIX
/*
* ctime - doesn't seem to be in libc on Minix. Used to format
* time, passed as pointer to long (number of seconds). Returns
* pointer to static char string containing formatted time. Code
* stolen and hacked from date.c.
*/
int days_per_month[] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
char *months[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
char *days[] =
{ "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed" };
struct {
int year, month, day, hour, min, sec;
} tm;
#define S_P_MIN 60L
#define S_P_HOUR 60L * 60L
#define S_P_DAY 24L * 60L * 60L
#define S_P_YEAR 365L * 24L * 60L * 60L
char *
ctime(tp)
long *tp;
{
static char out[26];
long t = *tp;
tm.year = 0;
tm.month = 0;
tm.day = 1;
tm.hour = 0;
tm.min = 0;
tm.sec = 0;
while (t >= S_P_YEAR) {
if (((tm.year + 2) % 4) == 0)
t -= S_P_DAY;
tm.year += 1;
t -= S_P_YEAR;
}
if (((tm.year + 2) % 4) == 0)
days_per_month[1]++;
tm.year += 1970;
while (t >= (days_per_month[tm.month] * S_P_DAY))
t -= days_per_month[tm.month++] * S_P_DAY;
while (t >= S_P_DAY) {
t -= S_P_DAY;
tm.day++;
}
while (t >= S_P_HOUR) {
t -= S_P_HOUR;
tm.hour++;
}
while (t >= S_P_MIN) {
t -= S_P_MIN;
tm.min++;
}
tm.sec = (int) t;
sprintf(out, "%s %s %2d %02d:%02d:%02d %d\n",
days[(int)((t / S_P_DAY) % 7)],
months[tm.month], tm.day, tm.hour, tm.min, tm.sec, tm.year);
return(out);
}
#endif
p
#ifdef MINIX
/*
* LSTAT - Minix's V7 File system has no symbolic links,
* so a crude work around is used here.
*/
#define LSTAT stat
#else
#define LSTAT lstat
#endif
p
/*
* fatal - report an error, and exit
*/
void
fatal(s)
char *s;
{
(void) fprintf(stderr, "%s: %s\n", prog, s);
exit(1);
}
p
/*
* newnode - create a new node, with the arguments passed to it.
*/
NODE *
newnode(typ, op, crp, left, right)
int typ;
int op;
CRIT *crp;
NODE *left;
NODE *right;
{
NODE *np;
if ((np = (NODE *) malloc(sizeof(NODE))) == (NODE *) NULL)
fatal("can't malloc a node");
np->nd_type = typ;
switch (typ) {
case ND_CRIT:
np->nd_un.nd_crit = crp;
break;
case ND_OP:
np->nd_un.nd_op = op;
break;
}
np->nd_left = left;
np->nd_right = right;
return(np);
}
p
/*
* dotim - calculate the time stamp desired.
*/
void
dotim(s, np)
char *s;
NUM *np;
{
np->nm_op = (*s == '+') ? AR_LT : (*s == '-') ? AR_GT : AR_EQ;
(void) time(&(np->nm_num));
np->nm_num -= (atol(++s) * 24L * 60L *60L);
}
p
/*
* nextcrit - return the address of the next criterion, assuming
* that we have some left.
*/
CRIT *
nextcrit()
{
if (critc == MAXCRIT)
fatal("too many search criteria");
return(&criteria[critc++]);
}
p
/*
* octal - convert s to an octal number. Used for file permissions.
* Returns the long octal number derived from s.
*/
long
octal(s)
char *s;
{
long l;
for (l = 0; *s && ISOCT(*s) ; s++)
l = l * 8L + *s - '0';
return(l);
}
p
/*
* finduid - get the uid corresponding to the argument string s.
* Could be numeric or username.
*/
long
finduid(s)
char *s;
{
register char *cp;
struct passwd *pwp;
int numeric;
for (cp = s, numeric = 1 ; *cp && numeric ; cp++)
if (!ISDIGIT(*cp))
numeric = 0;
if (numeric)
return(atol(s));
if ((pwp = getpwnam(s)) == NULL)
fatal("user not found");
return((long) pwp->pw_uid);
}
p
/*
* findgid - get the gid corresponding to the argument string s.
* Could be numeric or group name.
*/
long
findgid(s)
char *s;
{
register char *cp;
struct group *grp;
int numeric;
for (cp = s, numeric = 1 ; *cp ; cp++)
if (!ISDIGIT(*cp))
numeric = 0;
if (numeric)
return(atol(s));
if ((grp = getgrnam(s)) == NULL)
fatal("group not found");
return((long) grp->gr_gid);
}
p
/*
* fillcmd - fill the string cmd with the command to be executed,
* starting at argv[i], until all arguments done.
*/
void
fillcmd(argc, argv, i)
int argc;
char **argv;
int i;
{
char *cp = cmd;
char *cp2 = argv[i];
for ( ; i < argc ; cp2 = argv[++i], *cp++ = ' ')
while (*cp2)
if ((*cp++ = *cp2++) == '{')
bracec++;
while (*(cp-1) == ' ')
cp--;
*cp = '\0';
}
p
/*
* argfind - takes the string s and returns its place in
* the argument table. NULL if not found.
*/
ARG *
argfind(s)
char *s;
{
ARG *ap;
for (ap = argtab ; ap && ap->ar_name ; ap++)
if (strcmp(s, ap->ar_name) == 0)
return(ap);
return(NULL);
}
p
/*
* getcrit - recognise one criterion from the command line.
* pi will be incremented by number of arguments used in the
* criterion. Returns the address of the new node, if the
* criterion is not an operator, otherwise NULL, and status
* is filled with the operator type.
*/
NODE *
getcrit(pi, argv)
int *pi;
char **argv;
{
struct stat s;
CRIT *crp;
char *cp;
long l;
ARG *ap;
if ((ap = argfind(argv[++*pi])) == NULL) {
*pi--;
return(NULL);
}
if (ap->ar_ndtype != ND_CRIT)
return(NULL);
switch(ap->ar_attr) {
case CR_NAME:
crp = nextcrit();
crp->cr_type = CR_NAME;
crp->cr_un.cr_name = argv[++*pi];
break;
case CR_TYPE:
crp = nextcrit();
crp->cr_type = CR_TYPE;
crp->cr_un.cr_name = argv[++*pi];
if (myindex("bcdfgu", *argv[*pi]) == NULL)
fatal("bad type identifier");
break;
case CR_INUM:
crp = nextcrit();
crp->cr_type = CR_INUM;
if ((crp->cr_un.cr_n.nm_num = atoi(argv[++*pi])) <= 0)
fatal("Bad inode number");
break;
case CR_MTIME:
crp = nextcrit();
crp->cr_type = CR_MTIME;
dotim(argv[++*pi], &(crp->cr_un.cr_n));
break;
case CR_CTIME:
crp = nextcrit();
crp->cr_type = CR_CTIME;
dotim(argv[++*pi], &(crp->cr_un.cr_n));
break;
case CR_ATIME:
crp = nextcrit();
crp->cr_type = CR_ATIME;
dotim(argv[++*pi], &(crp->cr_un.cr_n));
break;
case CR_SIZE:
crp = nextcrit();
crp->cr_type = CR_SIZE;
cp = argv[++*pi];
crp->cr_un.cr_n.nm_op = (*cp == '+') ? AR_GT : (*cp == '-') ? AR_LT : AR_EQ;
if (*cp == '+' || *cp == '-')
cp++;
for (l = 0L ; *cp && ISDIGIT(*cp);)
l = l * 10L + (long) (*cp++ - '0');
crp->cr_un.cr_n.nm_num = (*cp && *cp == 'c') ? l : l * (long) BLOCK_SIZE;
break;
case CR_DEPTH:
depthfirst = 1;
break;
case CR_PRUNE:
prune = 1;
break;
case CR_LINKS:
crp = nextcrit();
crp->cr_type = CR_LINKS;
cp = argv[++*pi];
crp->cr_un.cr_n.nm_op = (*cp == '+') ? AR_GT : (*cp == '-') ? AR_LT : AR_EQ;
if (*cp == '+' || *cp == '-')
cp++;
if ((crp->cr_un.cr_n.nm_num = atol(cp)) <= 0)
fatal("Bad number of links");
break;
case CR_NEWER:
if (LSTAT(argv[++*pi], &s) < 0)
fatal("cannot stat file");
crp = nextcrit();
crp->cr_type = CR_NEWER;
crp->cr_un.cr_n.nm_num = s.st_ctime;
crp->cr_un.cr_n.nm_op = AR_GT;
break;
case CR_PERM:
crp = nextcrit();
crp->cr_type = CR_PERM;
cp = argv[++*pi];
crp->cr_un.cr_n.nm_op = (*cp == '-') ? AR_LT : AR_EQ;
if (*cp == '-')
cp++;
crp->cr_un.cr_n.nm_num = octal(cp);
break;
case CR_USER:
crp = nextcrit();
crp->cr_type = CR_USER;
crp->cr_un.cr_n.nm_num = finduid(argv[++*pi]);
break;
case CR_NOUSER:
crp = nextcrit();
crp->cr_type = CR_NOUSER;
break;
case CR_GROUP:
crp = nextcrit();
crp->cr_type = CR_GROUP;
crp->cr_un.cr_n.nm_num = findgid(argv[++*pi]);
break;
case CR_NOGROUP:
crp = nextcrit();
crp->cr_type = CR_NOGROUP;
break;
}
return(newnode(ND_CRIT, 0, crp, (NODE *)NULL, (NODE *)NULL));
}
p
/*
* getacts - recognise the actions from the command line.
*/
void
getacts(i, argc, argv)
int i;
int argc;
char **argv;
{
if (strcmp(argv[++i], "-print") == 0)
act = ACT_PRINT;
else if (strcmp(argv[i], "-exec") == 0) {
act = ACT_EXEC;
fillcmd(argc, argv, ++i);
} else if (strcmp(argv[i], "-ls") == 0) {
act = ACT_LS;
fillcmd(argc, argv, ++i);
} else if (strcmp(argv[i], "-ok") == 0) {
act = ACT_OK;
fillcmd(argc, argv, ++i);
} else
fatal("bad action");
}
p
/*
* getncrit - recognise n criteria, up to the start of the
* actions, or until a closing bracket received. Returns the
* top of the tree created by it.
*/
NODE *
getncrit(ip, argc, argv)
int *ip;
int argc;
char **argv;
{
NODE *new;
NODE *top;
char prev = 0;
ARG *ap;
for (top = NULL ; (ap = argfind(argv[*ip+1])) != NULL ; ) {
if ((new = getcrit(ip, argv)) == NULL) {
switch(ap->ar_attr) {
case OP_OPENB :
top = getncrit(ip, argc, argv);
break;
case OP_CLOSEB:
return(top);
case OP_NOT:
prev = OP_NOT;
break;
case OP_AND:
case OP_OR:
if (argfind(argv[*ip+1]) == NULL)
fatal("missing search criterion");
top = newnode(ND_OP, ap->ar_attr,
(CRIT *)NULL, top, (NODE *)NULL);
}
} else {
if (prev == OP_NOT) {
new->nd_un.nd_crit->cr_not = 1;
prev = 0;
}
if (top == NULL)
top = new;
else if (top->nd_type == ND_CRIT)
top = newnode(ND_OP, OP_AND, (CRIT *)NULL,
top, new);
else
top->nd_right = new;
}
}
return(top);
}
p
/*
* doargs - parse the command line arguments.
*/
void
doargs(argc, argv)
int argc;
char **argv;
{
int i = 1;
startdir = argv[i];
if (access(startdir, 01) < 0)
fatal("cannot access start directory");
root = getncrit(&i, argc, argv);
getacts(i, argc, argv);
}
p
/*
* expand - take the command line, and replace all the occurrences
* of braces with the file name s.
*/
char *
expand(s)
char *s;
{
char *cp;
char *cp2;
char *fn;
for (cp2 = cmd, cp = expanded ; *cp2 && *cp2 != ';' ; ) {
switch(*cp2) {
case '{' :
if (*(cp2+1) == '}') {
for (fn = s ; *fn ; *cp++ = *fn++)
;
cp2 += 2;
} else
*cp++ = *cp2++;
break;
default :
*cp++ = *cp2++;
}
}
while (*(cp-1) == ' ')
cp--;
*cp = '\0';
return(expanded);
}
p
/*
* prompt - prompt the user for input, and return char presented.
*/
int
prompt(s)
char *s;
{
int c;
(void) printf("%s [y/n]? ", s);
(void) fflush(stdout);
while ((c = getchar()) == '\n')
;
return(c);
}
p
/*
* fmtmode - format file permission mode (bit mask s) and print it.
* Returns 1 if a [char,block] special file, 0 otherwise.
*/
char
fmtmode(s)
int s;
{
char ret = 0;
switch(s & S_IFMT) {
case S_IFDIR :
putchar('d');
break;
case S_IFCHR :
putchar('c');
ret = 1;
break;
case S_IFBLK :
putchar('b');
ret = 1;
break;
default :
putchar('-');
break;
}
putchar((s & S_IREAD) ? 'r' : '-');
putchar((s & S_IWRITE) ? 'w' : '-');
putchar((s & S_ISUID) ? 's' : (s & S_IEXEC) ? 'x' : '-');
putchar((s & (S_IREAD >> 3)) ? 'r' : '-');
putchar((s & (S_IWRITE >> 3)) ? 'w' : '-');
putchar((s & S_ISGID) ? 's' : (s & (S_IEXEC >> 3)) ? 'x' : '-');
putchar((s & (S_IREAD >> 6)) ? 'r' : '-');
putchar((s & (S_IWRITE >> 6)) ? 'w' : '-');
putchar((s & (S_IEXEC >> 6)) ? 'x' : '-');
return(ret);
}
p
/*
* ls - perform the -ls function
*/
void
ls(fname)
char *fname;
{
struct stat s;
struct passwd *pwp;
struct group *grp;
char special;
if (LSTAT(fname, &s) < 0) {
(void) fprintf(stderr, "Bad status <%s>\n", fname);
return;
}
(void) printf("%5d %4d ", s.st_ino, s.st_size/BLOCK_SIZE);
special = fmtmode((int)s.st_mode);
(void) printf(" %2d ", s.st_nlink);
if ((pwp = getpwuid(s.st_uid)) == NULL)
(void) printf("%-8d ", s.st_uid);
else
(void) printf("%-8.8s ", pwp->pw_name);
if ((grp = getgrgid(s.st_gid)) == NULL)
(void) printf("%-8d ", s.st_gid);
else
(void) printf("%-8.8s ", grp->gr_name);
if (special)
(void) printf("%2d, %4d ", s.st_rdev / 256, s.st_rdev % 256);
else
(void) printf("%8D ", s.st_size);
(void) printf("%.12s %s\n", &(ctime(&s.st_mtime))[4], fname);
#ifdef MINIX
(void) fflush(stdout); /* otherwise nothing comes out??? */
#endif
}
p
/*
* action - take the action (held in act). Possible expansion of
* braces necessary.
*/
void
action(fname)
char *fname;
{
char *cp;
switch(act) {
case ACT_PRINT :
(void) printf("%s\n", fname);
return;
case ACT_EXEC :
cp = (bracec > 0) ? expand(fname) : cmd;
(void) system(cp);
return;
case ACT_OK :
cp = (bracec > 0) ? expand(fname) : cmd;
switch(prompt(cp)) {
case 'y' :
case 'Y' :
(void) system(cp);
return;
default :
return;
}
case ACT_LS :
ls(fname);
return;
default :
return;
}
}
p
/*
* arithcmp - do an arithmetical comparison of the number held
* in the number struct, and the parameter n. Note long integers used.
* Returns 1 if comparison true, 0 otherwise.
*/
int
arithcmp(np, n)
NUM *np;
long n;
{
switch(np->nm_op) {
case AR_LT :
return(n < np->nm_num);
case AR_EQ :
return(n == np->nm_num);
case AR_GT :
return(n > np->nm_num);
default :
return(0);
}
}
p
/*
* singlematch - does the file with (complete) file name fname,
* and inode number inum meet the search criterion pointed to
* by crp. Returns 1 if so, 0 otherwise.
*/
int
singlematch(fname, inum, crp)
char *fname;
int inum;
CRIT *crp;
{
struct stat s;
char *cp;
char *cp2;
char *cp3;
int fnd;
switch(crp->cr_type) {
case CR_INUM :
return(crp->cr_un.cr_n.nm_num == inum);
case CR_NAME :
for (cp = &fname[strlen(fname)-1] ; *cp && *cp != '/' ; cp--)
;
cp++;
if (myindex(crp->cr_un.cr_name, '*') != NULL ||
myindex(crp->cr_un.cr_name, '?') != NULL ||
myindex(crp->cr_un.cr_name, '[') != NULL) {
if (strcmp(crp->cr_un.cr_name, "*") == 0)
return(1);
for (cp2 = crp->cr_un.cr_name ; *cp2 ; ) {
switch(*cp2) {
case '?' :
cp2++ ;
break;
case '*' :
if (*++cp2 == '\0')
return(1);
while (*cp && *cp != *cp2)
cp++;
if (*cp == '\0')
return(0);
break;
case '[' :
if ((cp3 = myindex(++cp2, ']')) == NULL)
return(0);
*cp3 = '\0';
fnd = myindex(cp2, *cp++)!=NULL? 1 : 0;
cp2 = cp3 + 1;
*cp3 = ']';
if (!fnd)
return(0);
break;
default :
if (*cp++ != *cp2++)
return(0);
}
}
return(*cp == '\0' ? 1 : 0);
}
return(strcmp(crp->cr_un.cr_name, cp) == 0 ? 1 : 0);
case CR_MTIME :
case CR_CTIME :
case CR_ATIME :
case CR_SIZE :
case CR_TYPE :
case CR_LINKS :
case CR_NEWER :
case CR_PERM :
case CR_USER :
case CR_NOUSER :
case CR_GROUP :
case CR_NOGROUP :
if (LSTAT(fname, &s) < 0) {
(void) fprintf(stderr, "Bad status <%s>\n", fname);
return(0);
}
switch(crp->cr_type) {
case CR_ATIME :
return(arithcmp(&(crp->cr_un.cr_n), s.st_atime));
case CR_CTIME :
return(arithcmp(&(crp->cr_un.cr_n), s.st_ctime));
case CR_MTIME :
return(arithcmp(&(crp->cr_un.cr_n), s.st_mtime));
case CR_SIZE :
return(arithcmp(&(crp->cr_un.cr_n), (long) s.st_size));
case CR_LINKS :
return(arithcmp(&(crp->cr_un.cr_n), (long) s.st_nlink));
case CR_NEWER :
return(arithcmp(&(crp->cr_un.cr_n), s.st_ctime));
case CR_PERM :
fnd = crp->cr_un.cr_n.nm_op == AR_LT ? LONGMASK : SHORTMASK;
return((s.st_mode & fnd) == crp->cr_un.cr_n.nm_num);
case CR_USER :
return(s.st_uid == (short) crp->cr_un.cr_n.nm_num);
case CR_NOUSER :
return(getpwuid(s.st_uid) == NULL);
case CR_GROUP :
return(s.st_gid == (short) crp->cr_un.cr_n.nm_num);
case CR_NOGROUP :
return(getgrgid(s.st_gid) == NULL);
case CR_TYPE :
switch(*(crp->cr_un.cr_name)) {
case 'b' :
return((s.st_mode & S_IFMT) == S_IFBLK);
case 'c' :
return((s.st_mode & S_IFMT) == S_IFCHR);
case 'd' :
return((s.st_mode & S_IFMT) == S_IFDIR);
case 'f' :
return((s.st_mode & S_IFMT) == S_IFREG);
case 'g' :
return(s.st_mode & S_ISGID);
case 'u' :
return(s.st_mode & S_ISUID);
}
}
default :
return(0);
}
}
p
/*
* match - does the file fname, inode inum match the criteria tree
* staarting at np? Returns 1 for yes, 0 otherwise.
*/
int
match(fname, inum, np)
char *fname;
int inum;
NODE *np;
{
int ret;
if (np == NULL)
return(0);
switch(np->nd_type) {
case ND_CRIT :
ret = singlematch(fname, inum, np->nd_un.nd_crit);
return(np->nd_un.nd_crit->cr_not ? 1 - ret : ret);
case ND_OP :
switch(np->nd_un.nd_op) {
case OP_AND :
return(match(fname, inum, np->nd_left) &&
match(fname, inum, np->nd_right));
case OP_OR :
return(match(fname, inum, np->nd_left) ||
match(fname, inum, np->nd_right));
case OP_CLOSEB :
fatal("close bracket in match operator");
}
default :
(void) printf("unusual type %d\n", np->nd_type);
}
return(0);
}
p
/*
* makedname - make the full file name from the directory name,
* file name, and place it in out. If this would overflow, 0
* is returned, 1 otherwise.
*/
int
makedname(d, f, out, outlen)
char *d;
char *f;
char *out;
int outlen;
{
register char *cp;
register char *dp;
if ((strlen(d) + strlen(f) + 2) > outlen)
return(0);
for (cp = out, dp = d ; *dp ; *cp++ = *dp++)
;
if (d[strlen(d)-1] != '/')
*cp++ = '/';
while (*f)
*cp++ = *f++;
*cp = '\0';
return(1);
}
p
#ifdef MINIX
/*
* dodir - process the directory dname. Minix uses the V7 file
* system, hence the hacked-in ifdefs.
*/
void
dodir(dname)
char *dname;
{
struct stat s;
char dirent[LINELEN];
int fd;
#define ERR {\
(void) fprintf(stderr, "Bad status <%s>\n", dname);\
return;\
}
if (LSTAT(dname, &s) < 0)
ERR;
if ((s.st_mode & S_IFMT) != S_IFDIR)
ERR;
if ((fd = open(dname, 0)) < 0)
ERR;
while (read(fd, &dir, sizeof(dir)) > 0) {
if (strcmp(dir.d_name, ".") == 0 ||
strcmp(dir.d_name, "..") == 0)
continue;
if (dir.inum == 0)
continue;
if (!makedname(dname, dir.d_name, dirent, sizeof(dirent)))
continue;
if (LSTAT(dirent, &s) < 0) {
close(fd);
ERR;
}
if (((s.st_mode & S_IFMT) == S_IFDIR) && depthfirst && !prune)
dodir(dirent);
if (root == NULL || match(dirent, (int) dir.inum, root))
action(dirent);
if (((s.st_mode & S_IFMT) == S_IFDIR) && !depthfirst && !prune)
dodir(dirent);
}
close(fd);
}
p
#else
/*
* dodir - process the directory dname. This function uses the
* directory processing functions.
*/
void
dodir(dname)
char *dname;
{
struct direct *dp;
struct stat s;
char dent[LINELEN];
DIR *dirp;
#define ERR {\
(void) fprintf(stderr, "Bad status <%s>\n", dname);\
return;\
}
if (LSTAT(dname, &s) < 0)
ERR;
if ((s.st_mode & S_IFMT) != S_IFDIR)
ERR;
if ((dirp = opendir(dname)) == NULL)
ERR;
while ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, "..") == 0)
continue;
if (!makedname(dname, dp->d_name, dent, sizeof(dent)))
continue;
if (LSTAT(dent, &s) < 0) {
closedir(dirp);
ERR;
}
if (((s.st_mode & S_IFMT) == S_IFDIR) && depthfirst && !prune)
dodir(dent);
#ifdef SUN
if (root == NULL || match(dent, (int)dp->d_fileno, root))
#else
if (root == NULL || match(dent, (int)dp->d_ino, root))
#endif
action(dent);
if (((s.st_mode & S_IFMT) == S_IFDIR) && !depthfirst && !prune)
dodir(dent);
}
closedir(dirp);
}
#endif
p
/*
* shortform - 4.3 BSD has a shortform of find, "find name" which is
* "find / -name "*name*" -print.
*/
void
shortform(s)
char *s;
{
CRIT *crp;
char name[SHORTLINE];
critc = 1;
crp = criteria;
crp->cr_type = CR_NAME;
(void) sprintf(name, "*%s*", s);
crp->cr_un.cr_name = name;
act = ACT_PRINT;
root = newnode(ND_CRIT, 0, crp, (NODE *)NULL, (NODE *)NULL);
dodir("/");
exit(0);
}
p
/*
* At last...
*/
main(argc, argv)
int argc;
char **argv;
{
prog = argv[0];
if (argc == 1) {
(void) fprintf(stderr,
"Usage: %s name, or %s path-list predicate-list\n",
prog, prog);
exit(1);
} else if (argc == 2)
shortform(argv[1]);
doargs(argc, argv);
dodir(startdir);
exit(0);
}