/* find COMPILE: cc -o find -s -O -i find.c -lS */
/*
* Changes by Gunnar Ritter, Freiburg i. Br., Germany, September 2003.
*/
/* from Unix 7th Edition /usr/src/cmd/find.c */
/*
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* Redistributions of source code and documentation must retain the
* above copyright notice, this list of conditions and the following
* disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed or owned by Caldera
* International, Inc.
* Neither the name of Caldera International, Inc. nor the names of
* other contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
#define USED __attribute__ ((used))
#elif defined __GNUC__
#define USED __attribute__ ((unused))
#else
#define USED
#endif
#if defined (SU3)
static const char sccsid[] USED = "@(#)find_su3.sl 1.45 (gritter) 5/8/06";
#elif defined (SUS)
static const char sccsid[] USED = "@(#)find_sus.sl 1.45 (gritter) 5/8/06";
#else
static const char sccsid[] USED = "@(#)find.sl 1.45 (gritter) 5/8/06";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <time.h>
#include <grp.h>
#include <stdarg.h>
#include <libgen.h>
#include <errno.h>
#include <locale.h>
#include <signal.h>
#include <fnmatch.h>
#include <mntent.h>
#ifndef major
#include <sys/mkdev.h>
#endif
#if __NetBSD_Version__>= 300000000
#include <sys/statvfs.h>
#define statfs statvfs
#endif
#include "getdir.h"
#define A_DAY 86400L /* a day full of seconds */
#define EQ(x, y) (strcmp(x, y)==0)
#ifndef MNTTYPE_IGNORE
#define MNTTYPE_IGNORE ""
#endif
#ifndef S_IFDOOR
#define S_IFDOOR 0xD000
#endif
#ifndef S_IFNWK
#define S_IFNWK 0x9000
#endif
#undef ctime
#define ctime find_ctime
static char *Pathname;
struct aggregate { /* for exec ... {} + */
long a_cnt; /* count of arguments */
long a_cur; /* current position in aggregate */
long a_csz; /* aggregate current length */
long a_msz; /* aggregate maximum length */
char **a_vec; /* arguments */
char *a_spc; /* aggregate space */
long a_maxarg; /* maximum arguments in e_vec */
};
struct anode {
int (*F)(struct anode *);
union anode_l {
struct anode *L;
char *pat;
time_t t;
uid_t u;
gid_t g;
ino_t i;
nlink_t link;
off_t sz;
mode_t per;
int com;
FILE *fp;
char *fstype;
} l;
union anode_r {
struct anode *R;
int s;
pid_t pid;
struct aggregate *a;
} r;
};
static char *Fname;
static time_t Now;
static int Argc,
Ai,
Pi;
static char **Argv;
/* cpio stuff */
static int Cpio;
static struct stat Statb;
/*
* Keep track of all visited directories, to avoid loops caused by
* symbolic links and to free storage and close files after fork().
*/
static struct visit {
struct getdb *v_db; /* getdb struct for this level */
ino_t v_ino; /* inode number */
int v_fd; /* file descriptor */
dev_t v_dev; /* device id */
} *visited;
static int vismax; /* number of members in visited */
/*
* For -fstype, keep track of all filesystem types known to the system. If
* we had st_fstype in struct stat as SVR4 does, this would be far more
* reliable.
*/
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
static struct fstype {
dev_t fsdev; /* device id of filesystem */
char *fstype; /* filesystem type */
} *fstypes, *fscur;
#endif /* __linux__ || _AIX || __hpux */
static int Home = -1;
static int wanthome;
static mode_t um; /* user's umask */
static const char *progname;
static int status; /* exit status */
static int depth; /* -depth flag */
static int Print = 1; /* implicit -print */
static int Prune; /* -prune at this point */
static int Mount; /* -mount, -xdev */
static int Execplus; /* have a -exec command {} + node */
static int HLflag; /* -H or -L option given */
static char *Statfs; /* result of statfs() on FreeBSD */
static int incomplete; /* encountered an incomplete statement */
static int sysv3;
static int (*statfn)(const char *, struct stat *) = lstat;
static struct anode *expr(void);
static struct anode *e1(void);
static struct anode *e2(void);
static struct anode *e3(void);
static struct anode *mk(struct anode *);
static void oper(const char **);
static char *nxtarg(int);
static int and(struct anode *);
static int or(struct anode *);
static int not(struct anode *);
static int glob(struct anode *);
static int print(struct anode *);
static int prune(struct anode *);
static int null(struct anode *);
static int mtime(struct anode *);
static int atime(struct anode *);
static int ctime(struct anode *);
static int user(struct anode *);
static int ino(struct anode *);
static int group(struct anode *);
static int nogroup(struct anode *);
static int nouser(struct anode *);
static int links(struct anode *);
static int size(struct anode *);
static int sizec(struct anode *);
static int perm(struct anode *);
static int type(struct anode *);
static int exeq(struct anode *);
static int ok(struct anode *);
static int cpio(struct anode *);
static int newer(struct anode *);
static int cnewer(struct anode *);
static int anewer(struct anode *);
static int fstype(struct anode *);
static int local(struct anode *);
static int scomp(long long, long long, char);
static int doex(int, struct aggregate *);
static struct aggregate *mkagg(long);
static uid_t getunum(const char *);
static gid_t getgnum(const char *);
static const char *getuser(uid_t);
static const char *getgroup(gid_t);
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
static void getfscur(dev_t);
static void getfstypes(void);
#endif /* __linux__ || _AIX || __hpux */
static int descend(char *, struct anode *, int);
static int descend1(char *, struct anode *, int);
static int descend2(char *, struct anode *, int);
static void setpath(char *, const char *, int);
static void pr(const char *, ...);
static void er(const char *, ...);
static void usage(void);
static void *srealloc(void *, size_t);
static void mkcpio(struct anode *, const char *, int);
static void trailer(struct anode *, int);
static void mknewer(struct anode *, const char *, int (*)(struct anode *));
static mode_t newmode(const char *ms, const mode_t pm);
int
main(int argc, char **argv)
{
struct anode *exlist;
struct anode nlist = { null, { 0 }, { 0 } };
int paths;
register char *sp = 0;
int i, j;
time(&Now);
umask(um = umask(0));
progname = basename(argv[0]);
setlocale(LC_COLLATE, "");
setlocale(LC_CTYPE, "");
if (getenv("SYSV3") != NULL)
sysv3 = 1;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-' || argv[i][1] == '\0')
break;
if (argv[i][1] == '-') {
i++;
break;
}
for (j = 1; argv[i][j]; j++)
if (argv[i][j] != 'H' && argv[i][j] != 'L')
goto brk;
for (j = 1; argv[i][j]; j++)
HLflag = argv[i][j];
}
brk: if (HLflag == 'L')
statfn = stat;
argc -= i - 1;
argv += i - 1;
Argc = argc; Argv = argv;
if(argc<2) {
pr("insufficient number of arguments");
usage();
}
for(Ai = paths = 1; Ai < argc; ++Ai, ++paths)
if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!"))
break;
if(paths == 1) /* no path-list */
usage();
if(Ai<argc) {
if(!(exlist = expr())) /* parse and compile the arguments */
er("find: parsing error");
if(Ai<argc) {
pr("bad option %s", argv[Ai]);
usage();
}
} else
exlist = &nlist;
if (paths > 2)
wanthome = 1;
if (wanthome && (Home = open(".", O_RDONLY)) < 0)
er("bad starting directory");
for(Pi = 1; Pi < paths; ++Pi) {
if (Pi > 1 && Home >= 0 && fchdir(Home) < 0)
er("bad starting directory");
setpath(Pathname, Argv[Pi], 0);
Fname = sp = Pathname;
do
if (sp[0] == '/')
Fname = &sp[1];
while (*sp++);
descend(Pathname, exlist, 0); /* to find files that match */
}
if(Cpio || Execplus)
trailer(exlist, 1);
exit(status);
}
/* compile time functions: priority is expr()<e1()<e2()<e3() */
/*ARGSUSED*/
static struct anode *expr(void) { /* parse ALTERNATION (-o) */
register struct anode * p1;
struct anode n = { 0, { 0 }, { 0 } };
p1 = e1() /* get left operand */ ;
if(EQ(nxtarg(0), "-o")) {
const char *ops[] = { "-o", "-a", 0 };
oper(ops);
n.F = or, n.l.L = p1, n.r.R = expr();
return(mk(&n));
}
else if(Ai <= Argc) --Ai;
return(p1);
}
static struct anode *e1(void) { /* parse CONCATENATION (formerly -a) */
register struct anode * p1;
register char *a;
struct anode n = { 0, { 0 }, { 0 } };
p1 = e2();
a = nxtarg(0);
if(EQ(a, "-a")) {
const char *ops[] = { "-o", "-a", 0 };
oper(ops);
And:
n.F = and, n.l.L = p1, n.r.R = e1();
return(mk(&n));
} else if(EQ(a, "(") || EQ(a, "!") || (*a=='-' && !EQ(a, "-o"))) {
--Ai;
goto And;
} else if(Ai <= Argc) --Ai;
return(p1);
}
static struct anode *e2(void) { /* parse NOT (!) */
struct anode n = { 0, { 0 }, { 0 } };
if(EQ(nxtarg(0), "!")) {
const char *ops[] = { "-o", "-a", "!", 0 };
oper(ops);
n.F = not, n.l.L = e3();
return(mk(&n));
}
else if(Ai <= Argc) --Ai;
return(e3());
}
static struct anode *e3(void) { /* parse parens and predicates */
struct anode *p1;
struct anode n = { 0, { 0 }, { 0 } };
long i, k;
register char *a, *b, s, *p, *q;
a = nxtarg(0);
if(EQ(a, "(")) {
const char *ops[] = { "-o", "-a", 0 };
oper(ops);
p1 = expr();
a = nxtarg(1);
if(!EQ(a, ")")) goto err;
return(p1);
}
else if(EQ(a, "-depth")) {
depth = 1;
n.F = null;
} else if(EQ(a, "-follow")) {
statfn = stat;
n.F = null;
} else if(EQ(a, "-mount") || EQ(a, "-xdev")) {
Mount = 1;
n.F = null;
} else if(EQ(a, "-print")) {
Print = 0;
n.F = print;
} else if(EQ(a, "-prune"))
n.F = prune;
else if(EQ(a, "-nogroup"))
n.F = nogroup;
else if(EQ(a, "-nouser"))
n.F = nouser;
else if(EQ(a, "-local")) {
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
getfstypes();
#endif /* __linux__ || _AIX || __hpux */
n.F = local;
Statfs = a;
}
if (n.F)
return mk(&n);
b = nxtarg(2);
s = *b;
/*if(s=='+') b++;*/
if(EQ(a, "-name"))
n.F = glob, n.l.pat = b;
else if(EQ(a, "-mtime"))
n.F = mtime, n.l.t = atol(b), n.r.s = s;
else if(EQ(a, "-atime"))
n.F = atime, n.l.t = atol(b), n.r.s = s;
else if(EQ(a, "-ctime"))
n.F = ctime, n.l.t = atol(b), n.r.s = s;
else if(EQ(a, "-user"))
n.F = user, n.l.u = getunum(b), n.r.s = s;
else if(EQ(a, "-inum"))
n.F = ino, n.l.i = atoll(b), n.r.s = s;
else if(EQ(a, "-group"))
n.F = group, n.l.g = getgnum(b), n.r.s = s;
else if(EQ(a, "-size")) {
n.l.sz = atoll(b), n.r.s = s;
while (b[0] && b[1])
b++;
if (b[0] == 'c')
n.F = sizec;
else
n.F = size;
}
else if(EQ(a, "-links"))
n.F = links, n.l.link = atol(b), n.r.s = s;
else if(EQ(a, "-perm")) {
while (*b == '-')
b++;
n.F = perm, n.l.per = newmode(b, 0), n.r.s = s;
#if defined (SUS) || defined (SU3)
if (s == '-')
n.l.per &= 07777;
#endif
}
else if(EQ(a, "-type")) {
i = b[0] == '-' || b[0] == '+' ? b[1] : b[0];
i = i=='d' ? S_IFDIR :
i=='b' ? S_IFBLK :
i=='c' ? S_IFCHR :
i=='D' ? S_IFDOOR :
i=='f' ? S_IFREG :
i=='l' ? S_IFLNK :
i=='n' ? S_IFNWK :
i=='p' ? S_IFIFO :
i=='s' ? S_IFSOCK :
0;
n.F = type, n.l.per = i;
}
else if (EQ(a, "-exec")) {
Print = 0;
wanthome = 1;
i = Ai - 1;
q = "";
k = 0;
while(!EQ(p = nxtarg(1), ";")) {
if (EQ(p, "+") && EQ(q, "{}")) {
n.r.a = mkagg(k);
break;
}
q = p;
k += strlen(p) + 1;
}
n.F = exeq, n.l.com = i;
}
else if (EQ(a, "-ok")) {
Print = 0;
wanthome = 1;
i = Ai - 1;
while(!EQ(p = nxtarg(1), ";"));
n.F = ok, n.l.com = i;
}
else if(EQ(a, "-cpio"))
mkcpio(&n, b, 0);
else if(EQ(a, "-ncpio"))
mkcpio(&n, b, 1);
else if(EQ(a, "-newer"))
mknewer(&n, b, newer);
else if(EQ(a, "-anewer"))
mknewer(&n, b, anewer);
else if(EQ(a, "-cnewer"))
mknewer(&n, b, cnewer);
else if(EQ(a, "-fstype")) {
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
getfstypes();
#endif /* __linux__ || _AIX || __hpux */
n.F = fstype, n.l.fstype = b;
Statfs = a;
}
if (n.F) {
if (incomplete)
nxtarg(1);
return mk(&n);
}
err: pr("bad option %s", a);
usage();
/*NOTREACHED*/
return 0;
}
static struct anode *mk(struct anode *p)
{
struct anode *n;
n = srealloc(NULL, sizeof *n);
*n = *p;
return(n);
}
static void oper(const char **ops)
{
char *a;
a = nxtarg(-1);
while (*ops)
if (EQ(a, *ops++))
er("operand follows operand");
Ai--;
}
static char *nxtarg(int must) { /* get next arg from command line */
static int strikes = 0;
if(must==1 && Ai>=Argc || strikes==3)
er("incomplete statement");
if(Ai>=Argc) {
if (must >= 0)
strikes++;
incomplete = 1;
Ai = Argc + 1;
return("");
}
return(Argv[Ai++]);
}
/* execution time functions */
static int and(register struct anode *p)
{
return(((*p->l.L->F)(p->l.L)) && ((*p->r.R->F)(p->r.R))?1:0);
}
static int or(register struct anode *p)
{
return(((*p->l.L->F)(p->l.L)) || ((*p->r.R->F)(p->r.R))?1:0);
}
static int not(register struct anode *p)
{
return( !((*p->l.L->F)(p->l.L)));
}
static int glob(register struct anode *p)
{
int val;
#ifdef __GLIBC__
/* avoid glibc's broken [^...] */
extern char **environ;
char **savenv = environ;
char *newenv[] = { "POSIXLY_CORRECT=", NULL };
environ = newenv;
#endif /* __GLIBC__ */
val = fnmatch(p->l.pat, Fname, FNM_PATHNAME) == 0;
#ifdef __GLIBC__
environ = savenv;
#endif /* __GLIBC__ */
return val;
}
/*ARGSUSED*/
static int print(register struct anode *p)
{
puts(Pathname);
return(1);
}
/*ARGSUSED*/
static int prune(register struct anode *p)
{
if (!depth)
Prune = 1;
return(1);
}
/*ARGSUSED*/
static int null(register struct anode *p)
{
return(1);
}
static int mtime(register struct anode *p)
{
return(scomp((Now - Statb.st_mtime) / A_DAY, p->l.t, p->r.s));
}
static int atime(register struct anode *p)
{
return(scomp((Now - Statb.st_atime) / A_DAY, p->l.t, p->r.s));
}
static int ctime(register struct anode *p)
{
return(scomp((Now - Statb.st_ctime) / A_DAY, p->l.t, p->r.s));
}
static int user(register struct anode *p)
{
return(scomp(Statb.st_uid, p->l.u, p->r.s));
}
static int ino(register struct anode *p)
{
return(scomp(Statb.st_ino, p->l.u, p->r.s));
}
static int group(register struct anode *p)
{
return(p->l.u == Statb.st_gid);
}
static int nogroup(register struct anode *p)
{
return(getgroup(Statb.st_gid) == NULL);
}
static int nouser(register struct anode *p)
{
return(getuser(Statb.st_uid) == NULL);
}
static int links(register struct anode *p)
{
return(scomp(Statb.st_nlink, p->l.link, p->r.s));
}
static int size(register struct anode *p)
{
return(scomp(Statb.st_size?(Statb.st_size+511)>>9:0, p->l.sz, p->r.s));
}
static int sizec(register struct anode *p)
{
return(scomp(Statb.st_size, p->l.sz, p->r.s));
}
static int perm(register struct anode *p)
{
register int i;
i = (p->r.s=='-') ? p->l.per : 07777; /* '-' means only arg bits */
return((Statb.st_mode & i & 07777) == p->l.per);
}
static int type(register struct anode *p)
{
return((Statb.st_mode&S_IFMT)==p->l.per);
}
static int exeq(register struct anode *p)
{
if (p->r.a) {
if (Pathname) {
size_t sz = strlen(Pathname) + 1;
if (p->r.a->a_csz + sz <= p->r.a->a_msz &&
p->r.a->a_cur < p->r.a->a_maxarg-1) {
strcpy(p->r.a->a_vec[p->r.a->a_cur++] =
&p->r.a->a_spc[p->r.a->a_csz],
Pathname);
p->r.a->a_csz += sz;
return 1;
} else {
if (p->r.a->a_cur == 0) {
p->r.a->a_vec[p->r.a->a_cur++] =
Pathname;
p->r.a->a_vec[p->r.a->a_cur] = NULL;
}
else {
p->r.a->a_vec[p->r.a->a_cur] = NULL;
fflush(stdout);
doex(p->l.com, p->r.a);
return exeq(p);
}
}
} else {
if (p->r.a->a_cur == 0)
return 1;
p->r.a->a_vec[p->r.a->a_cur] = NULL;
}
}
fflush(stdout); /* to flush possible `-print' */
return(doex(p->l.com, p->r.a));
}
static int ok(struct anode *p)
{
char c; int yes;
yes = 0;
fflush(stdout); /* to flush possible `-print' */
fprintf(stderr, "< %s ... %s >? ", Argv[p->l.com], Pathname);
if (read(0, &c, 1) != 1)
exit(2);
yes = c == 'y';
if (c != '\n')
while (read(0, &c, 1) == 1 && c != '\n');
if(yes) return(doex(p->l.com, 0));
return(0);
}
static int cpio(struct anode *p)
{
if (strchr(Pathname, '\n')) {
pr("file name \"%s\" contains a newline character; "
"file not archived", Pathname);
status |= 1;
} else
fprintf(p->l.fp, "%s\n", Pathname);
return(1);
}
static int newer(register struct anode *p)
{
return Statb.st_mtime > p->l.t;
}
static int anewer(register struct anode *p)
{
return Statb.st_atime > p->l.t;
}
static int cnewer(register struct anode *p)
{
return Statb.st_ctime > p->l.t;
}
static int fstype(register struct anode *p)
{
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
return(EQ(fscur->fstype, p->l.fstype));
#elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \
|| defined (__DragonFly__) || defined (__APPLE__)
return(EQ(Statfs, p->l.fstype));
#else
return(EQ(Statb.st_fstype, p->l.fstype));
#endif
}
static int local(register struct anode *p)
{
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
return(strcmp(fscur->fstype, "nfs") && strcmp(fscur->fstype, "smbfs"));
#elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \
|| defined (__DragonFly__) || defined (__APPLE__)
return(strcmp(Statfs, "nfs") != 0);
#else
return(strcmp(Statb.st_fstype, "nfs") != 0);
#endif
}
/* support functions */
/* funny signed compare */
static int scomp(register long long a, register long long b, register char s)
{
if(s == '+')
return(a > b);
if(s == '-')
return(a < (b * -1));
return(a == b);
}
static int
doex(int com, struct aggregate *a)
{
register int np;
register char *na;
char **oargv;
int oargc;
static char **nargv;
static int narga;
static int ccode;
pid_t pid;
ccode = np = 0;
oargv = Argv;
oargc = com;
while (na=oargv[oargc++]) {
if (np >= narga-1)
nargv = srealloc(nargv, (narga+=20) * sizeof *nargv);
if(strcmp(na, ";")==0 && oargv == Argv) break;
if(strcmp(na, "{}")==0 && oargv == Argv) {
if (a) {
oargv = a->a_vec;
oargc = 0;
} else
nargv[np++] = Pathname;
}
else nargv[np++] = na;
}
if (a) {
a->a_cur = 0;
a->a_csz = 0;
}
if (np==0) return(9);
nargv[np] = 0;
if(pid = fork()) /*parent*/ while (wait(&ccode) != pid);
else { /*child*/
if (fchdir(Home) < 0) {
pr("bad starting directory");
_exit(1);
}
execvp(nargv[0], nargv);
_exit(1);
}
if (a && ccode) {
if (WIFSIGNALED(ccode))
status |= WTERMSIG(ccode) | 0200;
else if (WIFEXITED(ccode))
status |= WEXITSTATUS(ccode);
}
return(ccode && a==NULL ? 0:1);
}
static struct aggregate *mkagg(long baselen)
{
static size_t envsz;
extern char **environ;
register int i;
struct aggregate *a;
a = srealloc(NULL, sizeof *a);
if (envsz == 0)
for (i = 0; environ[i]; i++)
envsz += strlen(environ[i]) + 1;
a->a_msz = sysconf(_SC_ARG_MAX) - baselen - envsz - 2048;
a->a_spc = srealloc(NULL, a->a_msz);
a->a_maxarg = 8192;
a->a_vec = srealloc(NULL, a->a_maxarg * sizeof *a->a_vec);
a->a_csz = 0;
a->a_cur = 0;
Execplus = 1;
return a;
}
static uid_t getunum(const char *s) { /* find user name and return number */
struct passwd *pwd;
char *x;
uid_t u;
if ((pwd = getpwnam(s)) != NULL)
return pwd->pw_uid;
u = strtol(s, &x, 10);
if (*x == '\0')
return u;
er("cannot find %s name", s);
/*NOTREACHED*/
return 0;
}
static gid_t getgnum(const char *s) { /* find group name and return number */
struct group *grp;
char *x;
gid_t g;
if ((grp = getgrnam(s)) != NULL)
return grp->gr_gid;
g = strtol(s, &x, 10);
if (*x == '\0')
return g;
er("cannot find %s name", s);
/*NOTREACHED*/
return 0;
}
#define CACHESIZE 16
static const char *getuser(uid_t uid)
{
static struct {
char *name;
uid_t uid;
} cache[CACHESIZE];
static int last;
int i;
struct passwd *pwd;
const char *name;
for (i = 0; i < CACHESIZE && cache[i].name; i++)
if (cache[i].uid == uid)
goto found;
if ((pwd = getpwuid(uid)) != NULL)
name = pwd->pw_name;
else
name = "";
if (i >= CACHESIZE) {
if (last >= CACHESIZE)
last = 0;
i = last++;
}
if (cache[i].name)
free(cache[i].name);
cache[i].name = strdup(name);
cache[i].uid = uid;
found: return cache[i].name[0] ? cache[i].name : NULL;
}
static const char *getgroup(gid_t gid)
{
static struct {
char *name;
gid_t gid;
} cache[CACHESIZE];
static int last;
int i;
struct group *grp;
const char *name;
for (i = 0; i < CACHESIZE && cache[i].name; i++)
if (cache[i].gid == gid)
goto found;
if ((grp = getgrgid(gid)) != NULL)
name = grp->gr_name;
else
name = "";
if (i >= CACHESIZE) {
if (last >= CACHESIZE)
last = 0;
i = last++;
}
if (cache[i].name)
free(cache[i].name);
cache[i].name = strdup(name);
cache[i].gid = gid;
found: return cache[i].name[0] ? cache[i].name : NULL;
}
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
static void getfscur(dev_t dev)
{
int i;
for (i = 0; fstypes[i].fstype; i++)
if (fstypes[i].fsdev == dev) {
fscur = &fstypes[i];
return;
}
er("filesystem type for %s unknown", Pathname);
}
static void getfstypes(void)
{
struct stat st;
FILE *fp;
struct mntent *mp;
#ifdef __hpux
const char mtab[] = "/etc/mnttab";
#else /* __linux__, _AIX */
const char mtab[] = "/etc/mtab";
#endif /* __linux__, _AIX */
int i = 0;
if (fstypes)
return;
if ((fp = setmntent(mtab, "r")) == NULL)
er("cannot open %s: %s", mtab, strerror(errno));
while ((mp = getmntent(fp)) != NULL) {
if (EQ(mp->mnt_type, MNTTYPE_IGNORE))
continue;
if (stat(mp->mnt_dir, &st) < 0)
continue;
fstypes = srealloc(fstypes, (i+1) * sizeof *fstypes);
fstypes[i].fsdev = st.st_dev;
fstypes[i].fstype = strdup(mp->mnt_type);
i++;
}
endmntent(fp);
}
#endif /* __linux__ || _AIX || __hpux */
/*
* First part of descend, called for any file found.
*/
static int descend(char *fname, struct anode *exlist, int level)
{
struct stat ost;
register char *c1;
int i;
int rv = 0;
if(statfn(fname, &Statb)<0) {
if (statfn != lstat && lstat(fname, &Statb) == 0)
nof: c1 = "cannot follow symbolic link %s: %s";
else if (sysv3)
c1 = "stat() failed: %s: %s";
else if (errno == ENOENT || errno == ENOTDIR)
c1 = "cannot open %s: %s";
else
c1 = "stat() error %s: %s";
pr(c1, Pathname, strerror(errno));
status = 18;
return(0);
}
if (level == 0 && HLflag == 'H' && (Statb.st_mode&S_IFMT) == S_IFLNK) {
struct stat nst;
if (stat(fname, &nst) == 0)
Statb = nst;
else if (errno == ELOOP)
goto nof;
}
#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || \
defined (__DragonFly__) || defined (__APPLE__)
if (Statfs != NULL) {
static struct statfs sf;
if (statfs(fname, &sf) < 0) {
pr("statfs() error %s: %s", Pathname, strerror(errno));
status = 18;
return(0);
}
Statfs = sf.f_fstypename;
}
#endif /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */
if (Mount) {
static dev_t curdev;
if (level == 0)
curdev = Statb.st_dev;
else if (curdev != Statb.st_dev)
return(0);
}
Prune = 0;
if (!depth) {
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
if (fstypes)
getfscur(Statb.st_dev);
#endif /* __linux__ || _AIX || __hpux */
if((*exlist->F)(exlist) && Print)
puts(Pathname);
} else
ost = Statb;
if(Prune || (Statb.st_mode&S_IFMT)!=S_IFDIR)
goto reg;
if (statfn != lstat) {
for (i = 0; i < level; i++)
if (Statb.st_dev == visited[i].v_dev &&
Statb.st_ino == visited[i].v_ino) {
#ifdef SU3
pr("Symbolic link loop at %s", Pathname);
status = 18;
#endif /* SU3 */
goto reg;
}
}
if (level >= vismax) {
vismax += 20;
visited = srealloc(visited, sizeof *visited * vismax);
}
visited[level].v_dev = Statb.st_dev;
visited[level].v_ino = Statb.st_ino;
rv = descend1(fname, exlist, level);
reg:
if (depth) {
Statb = ost;
#if defined (__linux__) || defined (_AIX) || defined (__hpux)
if (fstypes)
getfscur(Statb.st_dev);
#endif /* __linux__ || _AIX || __hpux */
if ((*exlist->F)(exlist) && Print)
puts(Pathname);
}
return(rv);
}
/*
* Second part of descend, called for any directory found.
*/
static int descend1(char *fname, struct anode *exlist, int level)
{
int dir = 0; /* open directory */
register char *c1;
struct getdb *db;
register struct direc *dp;
int endofname;
int err;
int oflags = O_RDONLY;
#ifdef O_DIRECTORY
oflags |= O_DIRECTORY;
#endif
#ifdef O_NOFOLLOW
if (statfn == lstat && (HLflag != 'H' || level > 0))
oflags |= O_NOFOLLOW;
#endif
if ((dir = open(fname, oflags)) < 0 ||
fcntl(dir, F_SETFD, FD_CLOEXEC) < 0 ||
fchdir(dir) < 0) {
if (dir >= 0)
close(dir);
else if (errno == EMFILE && descend2(fname, exlist, level))
/*
* A possible performance improvement would be to
* call descend2() in the directory above, since
* the current method involves one fork() call per
* subdirectory at this level. The condition occurs
* so rarely that it seems hardly worth optimization
* though.
*/
return 0;
pr("cannot open %s: %s", Pathname, strerror(errno));
status = 18;
return 0;
}
if ((db = getdb_alloc(Pathname, dir)) == NULL) {
write(2, "no memory\n", 10);
_exit(077);
}
visited[level].v_db = db;
visited[level].v_fd = dir;
for(c1 = Pathname; *c1; ++c1);
if(*(c1-1) == '/')
--c1;
endofname = c1 - Pathname;
while ((dp = getdir(db, &err)) != NULL) {
if((dp->d_name[0]=='.' && dp->d_name[1]=='\0') ||
(dp->d_name[0]=='.' &&
dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
continue;
setpath(&Pathname[endofname], dp->d_name, 1);
Fname = &Pathname[endofname+1];
if(descend(Fname, exlist, level+1)) {
if (fchdir(dir) < 0)
er("bad directory tree");
}
}
Pathname[endofname] = '\0';
getdb_free(db);
if (err) {
pr("cannot read dir %s: %s", Pathname, strerror(errno));
status = 18;
}
close(dir);
visited[level].v_fd = -1;
return 1;
}
/*
* Third part of descend, called if the limit of open file descriptors
* is exceeded (EMFILE).
*/
static int descend2(char *fname, struct anode *exlist, int level)
{
pid_t pid;
int i;
if (Cpio || Execplus)
trailer(exlist, 0);
fflush(stdout);
switch (pid = fork()) {
case 0:
for (i = 0; i < level-1; i++) {
if (visited[i].v_fd >= 0) {
getdb_free(visited[i].v_db);
close(visited[i].v_fd);
visited[i].v_fd = -1;
}
}
status |= 0;
descend1(fname, exlist, level);
if (Cpio || Execplus)
trailer(exlist, 0);
exit(status);
/*NOTREACHED*/
default:
while (waitpid(pid, &i, 0) != pid);
if (i && WIFSIGNALED(i)) {
struct rlimit rl;
rl.rlim_cur = rl.rlim_max = 0;
setrlimit(RLIMIT_CORE, &rl);
raise(WTERMSIG(i));
pause();
}
if (i)
status |= WEXITSTATUS(i);
return 1;
case -1:
return 0;
}
}
static void setpath(char *eos, const char *fn, int slash)
{
static char *pathend;
char *opath;
for (;;) {
if (eos >= pathend) {
pathend += 14;
opath = Pathname;
Pathname = srealloc(Pathname, pathend - Pathname);
eos += Pathname - opath;
pathend += Pathname - opath;
}
if (slash) {
*eos++ = '/';
slash = 0;
} else
if ((*eos++ = *fn++) == '\0')
break;
}
}
static void pr(const char *s, ...)
{
va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, s);
vfprintf(stderr, s, ap);
va_end(ap);
fprintf(stderr, "\n");
}
static void er(const char *s, ...)
{
va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, s);
vfprintf(stderr, s, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
static void usage(void)
{
er("path-list predicate-list");
}
static void *srealloc(void *op, size_t n)
{
void *np;
if ((np = realloc(op, n)) == NULL) {
write(2, "no memory\n", 10);
_exit(077);
}
return np;
}
static void mkcpio(struct anode *p, const char *b, int ascii)
{
int fd, pd[2];
char flags[20], *cp;
p->F = cpio;
if (*b == '\0')
return;
depth = 1;
Print = 0;
Cpio = 1;
if (pipe(pd) < 0 || (p->l.fp = fdopen(pd[1], "w")) == NULL)
er("pipe() %s", strerror(errno));
if ((fd = creat(b, 0666)) < 0)
er("cannot create %s", b);
switch (p->r.pid = fork()) {
case -1:
er("can't fork");
/*NOTREACHED*/
case 0:
dup2(pd[0], 0);
close(pd[0]);
close(pd[1]);
dup2(fd, 1);
close(fd);
cp = flags;
*cp++ = '-';
*cp++ = 'o';
*cp++ = 'B';
if (ascii)
*cp++ = 'c';
if (statfn == stat)
*cp++ = 'L';
*cp = '\0';
execlp("cpio", "cpio", flags, NULL);
pr("cannot exec cpio: %s", strerror(errno));
_exit(0177);
/*NOTREACHED*/
}
close(pd[0]);
close(fd);
}
static void
trailer(register struct anode *p, int termcpio)
{
char *Opath = Pathname;
Pathname = 0;
if (p->F == or || p->F == and) {
trailer(p->l.L, termcpio);
trailer(p->r.R, termcpio);
} else if (p->F == not)
trailer(p->l.L, termcpio);
else if (p->F == cpio) {
if (termcpio) {
int s;
fclose(p->l.fp);
while (waitpid(p->r.pid, &s, 0) != p->r.pid);
if (s) {
if (WIFEXITED(s))
status |= WEXITSTATUS(s);
else if (WIFSIGNALED(s))
status |= WTERMSIG(s) | 0200;
}
} else
fflush(p->l.fp);
} else if (p->F == exeq && p->r.a)
exeq(p);
Pathname = Opath;
}
static void
mknewer(struct anode *p, const char *b, int (*f)(struct anode *))
{
if (*b && stat(b, &Statb) < 0)
er("cannot access %s", b);
p->l.t = Statb.st_mtime;
p->F = f;
}
/*
* Changes by Gunnar Ritter, Freiburg i. Br., Germany, September 2003.
*/
/* from Unix 7th Edition /usr/src/cmd/chmod.c */
/*
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* Redistributions of source code and documentation must retain the
* above copyright notice, this list of conditions and the following
* disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed or owned by Caldera
* International, Inc.
* Neither the name of Caldera International, Inc. nor the names of
* other contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define USER 05700 /* user's bits */
#define GROUP 02070 /* group's bits */
#define OTHER 00007 /* other's bits */
#define ALL 07777 /* all */
#define READ 00444 /* read permit */
#define WRITE 00222 /* write permit */
#define EXEC 00111 /* exec permit */
#define SETID 06000 /* set[ug]id */
#define STICKY 01000 /* sticky bit */
#ifndef S_ENFMT
#define S_ENFMT 02000 /* mandatory locking bit */
#endif
static mode_t absol(const char **);
static mode_t who(const char **, mode_t *);
static int what(const char **);
static mode_t where(const char **, mode_t, int *, int *, const mode_t);
static mode_t
newmode(const char *ms, const mode_t pm)
{
register mode_t o, m, b;
int lock, setsgid = 0, cleared = 0, copy = 0;
mode_t nm, om, mm;
const char *mo = ms;
nm = om = pm;
m = absol(&ms);
if (!*ms) {
nm = m;
goto out;
}
if ((lock = (nm&S_IFMT) != S_IFDIR && (nm&(S_ENFMT|S_IXGRP)) == S_ENFMT)
== 01)
nm &= ~(mode_t)S_ENFMT;
do {
m = who(&ms, &mm);
while (o = what(&ms)) {
b = where(&ms, nm, &lock, ©, pm);
switch (o) {
case '+':
nm |= b & m & ~mm;
if (b & S_ISGID)
setsgid = 1;
if (lock & 04)
lock |= 02;
break;
case '-':
nm &= ~(b & m & ~mm);
if (b & S_ISGID)
setsgid = 1;
if (lock & 04)
lock = 0;
break;
case '=':
nm &= ~m;
nm |= b & m & ~mm;
lock &= ~01;
if (lock & 04)
lock |= 02;
om = 0;
if (copy == 0)
cleared = 1;
break;
}
lock &= ~04;
}
} while (*ms++ == ',');
if (*--ms)
er("bad permissions: %s", mo);
out: if (pm & S_IFDIR) {
if ((pm & S_ISGID) && setsgid == 0)
nm |= S_ISGID;
else if ((nm & S_ISGID) && setsgid == 0)
nm &= ~(mode_t)S_ISGID;
}
return(nm);
}
static mode_t
absol(const char **ms)
{
register int c, i;
i = 0;
while ((c = *(*ms)++) >= '0' && c <= '7')
i = (i << 3) + (c - '0');
(*ms)--;
return(i);
}
static mode_t
who(const char **ms, mode_t *mp)
{
register int m;
m = 0;
*mp = 0;
for (;;) switch (*(*ms)++) {
case 'u':
m |= USER;
continue;
case 'g':
m |= GROUP;
continue;
case 'o':
m |= OTHER;
continue;
case 'a':
m |= ALL;
continue;
default:
(*ms)--;
if (m == 0) {
m = ALL;
*mp = um;
}
return m;
}
}
static int
what(const char **ms)
{
switch (**ms) {
case '+':
case '-':
case '=':
return *(*ms)++;
}
return(0);
}
static mode_t
where(const char **ms, mode_t om, int *lock, int *copy, const mode_t pm)
{
register mode_t m;
m = 0;
*copy = 0;
switch (**ms) {
case 'u':
m = (om & USER) >> 6;
goto dup;
case 'g':
m = (om & GROUP) >> 3;
goto dup;
case 'o':
m = (om & OTHER);
dup:
*copy = 1;
m &= (READ|WRITE|EXEC);
m |= (m << 3) | (m << 6);
++(*ms);
return m;
}
for (;;) switch (*(*ms)++) {
case 'r':
m |= READ;
continue;
case 'w':
m |= WRITE;
continue;
case 'x':
m |= EXEC;
continue;
case 'X':
if ((pm&S_IFMT) == S_IFDIR || (pm & EXEC))
m |= EXEC;
continue;
case 'l':
if ((pm&S_IFMT) != S_IFDIR)
*lock |= 04;
continue;
case 's':
m |= SETID;
continue;
case 't':
m |= STICKY;
continue;
default:
(*ms)--;
return m;
}
}