/*#define PLUS_DEBUG*/
/* @(#)find.c 1.64 07/07/06 Copyright 2004-2007 J. Schilling */
#ifndef lint
static char sccsid[] =
"@(#)find.c 1.64 07/07/06 Copyright 2004-2007 J. Schilling";
#endif
/*
* Another find implementation...
*
* Copyright (c) 2004-2007 J. Schilling
*/
/*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* See the file CDDL.Schily.txt in this distribution for details.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file CDDL.Schily.txt from this distribution.
*/
#ifdef __FIND__
#define FIND_MAIN
#endif
#include <schily/mconfig.h>
#include <stdio.h>
#include <schily/unistd.h>
#include <schily/stdlib.h>
#ifdef HAVE_FCHDIR
#include <schily/fcntl.h>
#else
#include <schily/maxpath.h>
#endif
#include <schily/stat.h>
#include <schily/time.h>
#include <schily/wait.h>
#include <schily/string.h>
#include <schily/utypes.h> /* incl. limits.h (_POSIX_ARG_MAX/ARG_MAX) */
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h> /* #defines NCARGS on old systems */
#endif
#ifndef DEV_BSIZE
#define DEV_BSIZE 512
#endif
#include <schily/btorder.h>
#include <schily/getcwd.h>
#include <schily/patmatch.h>
#if defined(HAVE_FNMATCH_H) & defined(HAVE_FNMATCH)
#include <fnmatch.h>
#endif
#include <schily/standard.h>
#include <schily/jmpdefs.h>
#include <schily/schily.h>
#include <pwd.h>
#include <grp.h>
#include <schily/nlsdefs.h>
#ifdef __FIND__
char strvers[] = "1.3"; /* The pure version string */
#endif
typedef struct {
char *left;
char *right;
char *this;
int op;
union {
int i;
long l;
dev_t dev;
ino_t ino;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
size_t size;
time_t time;
} val, val2;
} findn_t;
#include "walk.h"
#define FIND_NODE
#include "find.h"
#include "find_list.h"
#include "find_misc.h"
LOCAL char *tokennames[] = {
#define OPEN 0
"(",
#define CLOSE 1
")",
#define LNOT 2
"!",
#define AND 3
"a",
#define LOR 4
"o",
#define ATIME 5
"atime",
#define CTIME 6
"ctime",
#define DEPTH 7
"depth",
#define EXEC 8
"exec",
#define FOLLOW 9 /* POSIX Extension */
"follow",
#define FSTYPE 10 /* POSIX Extension */
"fstype",
#define GROUP 11
"group",
#define INUM 12 /* POSIX Extension */
"inum",
#define LINKS 13
"links",
#define LOCL 14 /* POSIX Extension */
"local",
#define LS 15 /* POSIX Extension */
"ls",
#define MODE 16 /* POSIX Extension */
"mode",
#define MOUNT 17 /* POSIX Extension */
"mount",
#define MTIME 18
"mtime",
#define NAME 19
"name",
#define NEWER 20
"newer",
#define NOGRP 21
"nogroup",
#define NOUSER 22
"nouser",
#define OK_EXEC 23
"ok",
#define PERM 24
"perm",
#define PRINT 25
"print",
#define PRINTNNL 26 /* POSIX Extension */
"printnnl",
#define PRUNE 27
"prune",
#define SIZE 28
"size",
#define TIME 29 /* POSIX Extension */
"time",
#define TYPE 30
"type",
#define USER 31
"user",
#define XDEV 32
"xdev",
#define PATH 33 /* POSIX Extension */
"path",
#define LNAME 34 /* POSIX Extension */
"lname",
#define PAT 35 /* POSIX Extension */
"pat",
#define PPAT 36 /* POSIX Extension */
"ppat",
#define LPAT 37 /* POSIX Extension */
"lpat",
#define PACL 38 /* POSIX Extension */
"acl",
#define XATTR 39 /* POSIX Extension */
"xattr",
#define LINKEDTO 40 /* POSIX Extension */
"linkedto",
#define NEWERAA 41 /* POSIX Extension */
"neweraa",
#define NEWERAC 42 /* POSIX Extension */
"newerac",
#define NEWERAM 43 /* POSIX Extension */
"neweram",
#define NEWERCA 44 /* POSIX Extension */
"newerca",
#define NEWERCC 45 /* POSIX Extension */
"newercc",
#define NEWERCM 46 /* POSIX Extension */
"newercm",
#define NEWERMA 47 /* POSIX Extension */
"newerma",
#define NEWERMC 48 /* POSIX Extension */
"newermc",
#define NEWERMM 49 /* POSIX Extension */
"newermm",
#define SPARSE 50 /* POSIX Extension */
"sparse",
#define LTRUE 51 /* POSIX Extension */
"true",
#define LFALSE 52 /* POSIX Extension */
"false",
#define MAXDEPTH 53 /* POSIX Extension */
"maxdepth",
#define MINDEPTH 54 /* POSIX Extension */
"mindepth",
#define HELP 55 /* POSIX Extension */
"help",
#define CHOWN 56 /* POSIX Extension */
"chown",
#define CHGRP 57 /* POSIX Extension */
"chgrp",
#define CHMOD 58 /* POSIX Extension */
"chmod",
#define DOSTAT 59 /* POSIX Extension */
"dostat",
#define ENDPRIM 60
0,
#define EXECPLUS 61
"exec",
0
};
#define NTOK ((sizeof (tokennames) / sizeof (tokennames[0])) - 1)
/*
* The struct plusargs and the adjacent space that holds the
* arg vector and the string table. The struct plusargs member "av"
* is also part of the ARG_MAX sized space that follows.
*
* ---------------------------------
* | Other struct plusargs fields | Don't count against ARG_MAX
* ---------------------------------
* ---------------------------------
* | New Arg vector[0] | Space for ARG_MAX starts here
* ---------------------------------
* | . |
* | . | Arg space grows upwards
* | V |
* ---------------------------------
* | Arg vector end | "nextargp" points here
* ---------------------------------
* ---------------------------------
* | Space for first arg string |
* --------------------------------- "laststr" points here
* | ^ |
* | . | String space "grows" downwards
* | . |
* ---------------------------------
* | Space for first arg string | Space for ARG_MAX ends here
* --------------------------------- "endp" points here
*/
struct plusargs {
struct plusargs *next; /* Next in list for flushing */
char *endp; /* Points to end of data block */
char **nextargp; /* Points to next av[] entry */
char *laststr; /* points to last used string */
int ac; /* The argc for our command */
char *av[1]; /* The argv for our command */
};
#ifdef PLUS_DEBUG /* We are no longer reentrant */
LOCAL struct plusargs *plusp; /* Avoid PLUS_DEBUG if possible */
#endif
#define MINSECS (60)
#define HOURSECS (60 * MINSECS)
#define DAYSECS (24 * HOURSECS)
#define YEARSECS (365 * DAYSECS)
extern time_t find_sixmonth; /* 6 months before limit (ls) */
extern time_t find_now; /* now limit (ls) */
LOCAL findn_t Printnode = { 0, 0, 0, PRINT };
#ifndef __GNUC__
#define inline
#endif
EXPORT void find_argsinit __PR((finda_t *fap));
EXPORT void find_timeinit __PR((time_t now));
EXPORT findn_t *find_printnode __PR((void));
EXPORT findn_t *find_addprint __PR((findn_t *np, finda_t *fap));
LOCAL findn_t *allocnode __PR((finda_t *fap));
EXPORT void find_free __PR((findn_t *t, finda_t *fap));
LOCAL void find_freenode __PR((findn_t *t));
LOCAL void nexttoken __PR((finda_t *fap));
LOCAL BOOL _nexttoken __PR((finda_t *fap));
LOCAL void errjmp __PR((finda_t *fap, int err));
EXPORT int find_token __PR((char *word));
EXPORT char *find_tname __PR((int op));
LOCAL char *nextarg __PR((finda_t *fap, findn_t *t));
EXPORT findn_t *find_parse __PR((finda_t *fap));
LOCAL findn_t *parse __PR((finda_t *fap));
LOCAL findn_t *parseand __PR((finda_t *fap));
LOCAL findn_t *parseprim __PR((finda_t *fap));
LOCAL int getperm __PR((FILE *f, findn_t *t, BOOL findperm, int smode, int isX));
LOCAL char *getsperm __PR((FILE *f, char *p, mode_t *mp, int smode, int isX));
LOCAL mode_t iswho __PR((int c));
LOCAL int isop __PR((int c));
LOCAL mode_t isperm __PR((int c, int isX));
EXPORT void find_firstprim __PR((int *pac, char *const **pav));
EXPORT BOOL find_primary __PR((findn_t *t, int op));
EXPORT BOOL find_pname __PR((findn_t *t, char *word));
#ifdef FIND_MAIN
LOCAL int walkfunc __PR((char *nm, struct stat *fs, int type, struct WALK *state));
#endif
#ifdef __FIND__
LOCAL inline BOOL find_expr __PR((char *f, char *ff, struct stat *fs, struct WALK *state, findn_t *t));
#else
EXPORT BOOL find_expr __PR((char *f, char *ff, struct stat *fs, struct WALK *state, findn_t *t));
#endif
LOCAL BOOL doexec __PR((char *f, int ac, char **av, struct WALK *state));
LOCAL int argsize __PR((void));
LOCAL BOOL pluscreate __PR((FILE *f, int ac, char **av, finda_t *fap));
LOCAL BOOL plusexec __PR((char *f, findn_t *t, struct WALK *state));
EXPORT int find_plusflush __PR((void *p, struct WALK *state));
EXPORT void find_usage __PR((FILE *f));
#ifdef FIND_MAIN
LOCAL int getflg __PR((char *optstr, long *argp));
EXPORT int main __PR((int ac, char **av));
#endif
EXPORT void
find_argsinit(fap)
finda_t *fap;
{
fap->Argc = 0;
fap->Argv = (char **)NULL;
fap->std[0] = stdin;
fap->std[1] = stdout;
fap->std[2] = stderr;
fap->primtype = 0;
fap->found_action = FALSE;
fap->patlen = 0;
fap->walkflags = 0;
fap->maxdepth = -1;
fap->mindepth = -1;
fap->plusp = (struct plusargs *)NULL;
fap->jmp = NULL;
fap->error = 0;
}
EXPORT void
find_timeinit(now)
time_t now;
{
find_now = now + 60;
find_sixmonth = now - 6L*30L*24L*60L*60L;
}
EXPORT findn_t *
find_printnode()
{
return (&Printnode);
}
/*
* Add a -print node to the parsed tree if there is no action already.
*/
EXPORT findn_t *
find_addprint(np, fap)
findn_t *np;
finda_t *fap;
{
findn_t *n;
n = allocnode(fap);
if (n == NULL) {
find_freenode(np);
return ((void *)NULL);
}
n->op = AND;
n->left = (char *)np;
n->right = (char *)&Printnode;
return (n);
}
/*
* allocnode is currently called by:
* find_addprint(), parse(), parseand(), parseprim()
*/
LOCAL findn_t *
allocnode(fap)
finda_t *fap;
{
findn_t *n;
n = __fjmalloc(fap->std[2], sizeof (findn_t), "allocnode", JM_RETURN);
if (n == NULL)
return (n);
n->left = 0;
n->right = 0;
n->this = 0;
n->op = 0;
n->val.l = 0;
n->val2.l = 0;
return (n);
}
EXPORT void
find_free(t, fap)
findn_t *t;
finda_t *fap;
{
if (fap != NULL) {
struct plusargs *p;
struct plusargs *np = NULL;
for (p = fap->plusp; p != NULL; p = np) {
np = p->next;
free(p);
}
}
find_freenode(t);
}
LOCAL void
find_freenode(t)
findn_t *t;
{
if (t == (findn_t *)NULL || t == &Printnode)
return;
switch (t->op) {
case OPEN:
case LNOT:
find_freenode((findn_t *)t->this);
break;
case AND:
case LOR:
find_freenode((findn_t *)t->left);
find_freenode((findn_t *)t->right);
break;
case PAT:
case PPAT:
case LPAT:
if (t->right != NULL)
free(t->right); /* aux array for patcompile() */
break;
default:
;
}
free(t);
}
LOCAL void
nexttoken(fap)
register finda_t *fap;
{
if (!_nexttoken(fap)) {
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
}
/*
* No errjmp() variant of nexttoken(), returns FALSE on error.
*/
LOCAL BOOL
_nexttoken(fap)
register finda_t *fap;
{
register char *word;
register char *tail;
if (fap->Argc <= 0) {
fap->primtype = FIND_ENDARGS;
return (TRUE);
}
word = *fap->Argv;
if ((tail = strchr(word, '=')) != NULL) {
#ifdef XXX
if (*tail == '\0') {
fap->Argv++; fap->Argc--;
} else
#endif
*fap->Argv = ++tail;
} else {
fap->Argv++; fap->Argc--;
}
if ((fap->primtype = find_token(word)) >= 0)
return (TRUE);
ferrmsgno(fap->std[2], EX_BAD, gettext("Bad Option: '%s'.\n"), word);
find_usage(fap->std[2]);
fap->primtype = FIND_ERRARG; /* Mark as "parse aborted" */
return (FALSE);
}
LOCAL void
errjmp(fap, err)
register finda_t *fap;
int err;
{
fap->primtype = FIND_ERRARG; /* Mark as "parse aborted" */
fap->error = err; /* Set error return */
siglongjmp(((sigjmps_t *)fap->jmp)->jb, 1);
/* NOTREACHED */
}
EXPORT int
find_token(word)
register char *word;
{
char **tp;
char *equalp;
int type;
if ((equalp = strchr(word, '=')) != NULL)
*equalp = '\0';
if (*word == '-') {
/*
* Do not allow -(, -), -!
*/
if (word[1] == '\0' || !strchr("()!", word[1]))
word++;
} else if (!strchr("()!", word[0]) && (!equalp || equalp[1] == '\0')) {
goto bad;
}
for (type = 0, tp = tokennames; *tp; tp++, type++) {
if (streql(*tp, word)) {
if (equalp)
*equalp = '=';
return (type);
}
}
bad:
if (equalp)
*equalp = '=';
return (-1);
}
EXPORT char *
find_tname(op)
int op;
{
if (op >= 0 && op < NTOK)
return (tokennames[op]);
return ("unknown");
}
LOCAL char *
nextarg(fap, t)
finda_t *fap;
findn_t *t;
{
if (fap->Argc-- <= 0) {
char *prim = NULL;
int pt = t->op;
if (pt >= 0 && pt < NTOK)
prim = tokennames[pt];
if (prim) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Missing arg for '%s%s'.\n"),
pt > LNOT ? "-":"", prim);
} else {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Missing arg.\n"));
}
errjmp(fap, EX_BAD);
/* NOTREACHED */
return ((char *)0);
} else {
return (*fap->Argv++);
}
}
EXPORT findn_t *
find_parse(fap)
finda_t *fap;
{
findn_t *ret;
if (!_nexttoken(fap))
return ((findn_t *)NULL); /* Immediate parse error */
if (fap->primtype == FIND_ENDARGS)
return ((findn_t *)NULL); /* Empty command */
ret = parse(fap);
if (ret)
return (ret);
if (fap->primtype == HELP) {
fap->primtype = FIND_ERRARG;
} else if (fap->error == 0) {
fap->primtype = FIND_ERRARG;
fap->error = geterrno();
if (fap->error == 0)
fap->error = EX_BAD;
}
return (ret);
}
LOCAL findn_t *
parse(fap)
finda_t *fap;
{
findn_t *n;
n = parseand(fap);
if (n == NULL)
return (n);
if (fap->primtype == LOR) {
findn_t *l = allocnode(fap);
if (l == NULL)
goto err;
l->left = (char *)n;
l->op = fap->primtype;
if (_nexttoken(fap))
l->right = (char *)parse(fap);
if (l->right == NULL) {
find_freenode(l);
n = NULL; /* Do not free twice */
goto err;
}
return (l);
}
return (n);
err:
find_freenode(n);
fap->primtype = FIND_ERRARG; /* Mark as "parse aborted" */
return ((findn_t *)NULL);
}
LOCAL findn_t *
parseand(fap)
finda_t *fap;
{
findn_t *n;
n = parseprim(fap);
if (n == NULL)
return (n);
if ((fap->primtype == AND) ||
(fap->primtype != LOR && fap->primtype != CLOSE &&
fap->primtype != FIND_ENDARGS)) {
findn_t *l = allocnode(fap);
BOOL ok = TRUE;
if (l == NULL)
goto err;
l->left = (char *)n;
l->op = AND; /* If no Operator, default to AND -a */
if (fap->primtype == AND) /* Fetch Operator for next node */
ok = _nexttoken(fap);
if (ok)
l->right = (char *)parseand(fap);
if (l->right == NULL) {
find_freenode(l);
n = NULL; /* Do not free twice */
goto err;
}
return (l);
}
return (n);
err:
find_freenode(n);
fap->primtype = FIND_ERRARG; /* Mark as "parse aborted" */
return ((findn_t *)NULL);
}
LOCAL findn_t *
parseprim(fap)
finda_t *fap;
{
sigjmps_t jmp;
sigjmps_t *ojmp = fap->jmp;
register findn_t *n;
register char *p;
Llong ll;
n = allocnode(fap);
if (n == (findn_t *)NULL) {
fap->primtype = FIND_ERRARG; /* Mark as "parse aborted" */
return ((findn_t *)NULL);
}
fap->jmp = &jmp;
if (sigsetjmp(jmp.jb, 1) != 0) {
/*
* We come here from siglongjmp()
*/
find_freenode(n);
fap->jmp = ojmp; /* Restore old jump target */
return ((findn_t *)NULL);
}
switch (n->op = fap->primtype) {
/*
* Use simple to old (historic) shell globbing.
*/
case NAME:
case PATH:
case LNAME:
#if defined(HAVE_FNMATCH_H) & defined(HAVE_FNMATCH)
n->this = nextarg(fap, n);
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
#endif
/* FALLTHROUGH */
/* Implement "fallback" to patmatch() if we have no fnmatch() */
/*
* Use patmatch() which is a regular expression matcher that implements
* extensions that are compatible to old (historic) shell globbing.
*/
case PAT:
case PPAT:
case LPAT: {
int plen;
plen = strlen(n->this = nextarg(fap, n));
if (plen > fap->patlen)
fap->patlen = plen;
n->right = __fjmalloc(fap->std[2], sizeof (int)*plen,
"space for pattern", fap->jmp);
if ((n->val.i = patcompile((Uchar *)n->this, plen, (int *)n->right)) == 0) {
ferrmsgno(fap->std[2],
EX_BAD, gettext("Bad pattern in '-%s %s'.\n"),
tokennames[n->op], n->this);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
case SIZE:
fap->walkflags &= ~WALK_NOSTAT;
p = n->left = nextarg(fap, n);
if (p[0] == '-' || p[0] == '+')
p++;
p = astoll(p, &ll);
if (p[0] == 'c' && p[1] == '\0')
n->this = p;
else if (*p) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Non numeric character '%c' in '-size %s'.\n"),
*p, n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->val.size = ll;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case LINKS:
fap->walkflags &= ~WALK_NOSTAT;
p = n->left = nextarg(fap, n);
if (p[0] == '-' || p[0] == '+')
p++;
p = astoll(p, &ll);
if (*p) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Non numeric character '%c' in '-links %s'.\n"),
*p, n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->val.nlink = ll;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case INUM:
fap->walkflags &= ~WALK_NOSTAT;
p = n->left = nextarg(fap, n);
if (p[0] == '-' || p[0] == '+')
p++;
p = astoll(p, &ll);
if (*p) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Non numeric character '%c' in '-inum %s'.\n"),
*p, n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->val.ino = ll;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case LINKEDTO: {
struct stat ns;
fap->walkflags &= ~WALK_NOSTAT;
if (stat(n->left = nextarg(fap, n), &ns) < 0) {
ferrmsg(fap->std[2],
gettext("Cannot stat '%s'.\n"), n->left);
errjmp(fap, geterrno());
/* NOTREACHED */
}
n->val.ino = ns.st_ino;
n->val2.dev = ns.st_dev;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
case TIME:
case ATIME:
case CTIME:
case MTIME: {
int len;
fap->walkflags &= ~WALK_NOSTAT;
p = n->left = nextarg(fap, n);
if (p[0] == '-' || p[0] == '+')
p++;
if (gettnum(p, &n->val.time) != 1) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Bad timespec in '-%s %s'.\n"),
tokennames[n->op], n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
len = strlen(p);
if (len > 0) {
len = (Uchar)p[len-1];
if (!(len >= '0' && len <= '9'))
n->val2.i = 1;
}
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
case NEWERAA:
case NEWERCA:
case NEWERMA: {
struct stat ns;
fap->walkflags &= ~WALK_NOSTAT;
if (stat(n->left = nextarg(fap, n), &ns) < 0) {
ferrmsg(fap->std[2],
gettext("Cannot stat '%s'.\n"), n->left);
errjmp(fap, geterrno());
/* NOTREACHED */
}
n->val.time = ns.st_atime;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
case NEWERAC:
case NEWERCC:
case NEWERMC: {
struct stat ns;
fap->walkflags &= ~WALK_NOSTAT;
if (stat(n->left = nextarg(fap, n), &ns) < 0) {
ferrmsg(fap->std[2],
gettext("Cannot stat '%s'.\n"), n->left);
errjmp(fap, geterrno());
/* NOTREACHED */
}
n->val.time = ns.st_ctime;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
case NEWERAM:
case NEWERCM:
case NEWERMM:
case NEWER: {
struct stat ns;
fap->walkflags &= ~WALK_NOSTAT;
if (stat(n->left = nextarg(fap, n), &ns) < 0) {
ferrmsg(fap->std[2],
gettext("Cannot stat '%s'.\n"), n->left);
errjmp(fap, geterrno());
/* NOTREACHED */
}
n->val.time = ns.st_mtime;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
case TYPE:
fap->walkflags &= ~WALK_NOSTAT;
n->this = (char *)nextarg(fap, n);
switch (*(n->this)) {
case 'b': case 'c': case 'd': case 'D':
case 'e': case 'f': case 'l': case 'p':
case 's':
if ((n->this)[1] == '\0') {
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
}
ferrmsgno(fap->std[2], EX_BAD,
gettext("Bad type '%c' in '-type %s'.\n"),
*n->this, n->this);
errjmp(fap, EX_BAD);
/* NOTREACHED */
break;
case FSTYPE:
fap->walkflags &= ~WALK_NOSTAT;
#ifdef HAVE_ST_FSTYPE
n->this = (char *)nextarg(fap, n);
#else
ferrmsgno(fap->std[2], EX_BAD,
gettext("-fstype not supported by this OS.\n"));
errjmp(fap, EX_BAD);
/* NOTREACHED */
#endif
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case LOCL:
fap->walkflags &= ~WALK_NOSTAT;
#ifndef HAVE_ST_FSTYPE
ferrmsgno(fap->std[2], EX_BAD,
gettext("-local not supported by this OS.\n"));
errjmp(fap, EX_BAD);
/* NOTREACHED */
#endif
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
#ifdef CHOWN
case CHOWN:
#endif
case USER: {
struct passwd *pw;
char *u;
fap->walkflags &= ~WALK_NOSTAT;
u = n->left = nextarg(fap, n);
if (u[0] == '-' || u[0] == '+')
u++;
if ((pw = getpwnam(u)) != NULL) {
n->val.uid = pw->pw_uid;
} else {
if (*astoll(n->left, &ll)) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("User '%s' not in passwd database.\n"),
n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->val.uid = ll;
}
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
#ifdef CHGRP
case CHGRP:
#endif
case GROUP: {
struct group *gr;
char *g;
fap->walkflags &= ~WALK_NOSTAT;
g = n->left = nextarg(fap, n);
if (g[0] == '-' || g[0] == '+')
g++;
if ((gr = getgrnam(g)) != NULL) {
n->val.gid = gr->gr_gid;
} else {
if (*astoll(n->left, &ll)) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Group '%s' not in group database.\n"),
n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->val.gid = ll;
}
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
#ifdef CHMOD
case CHMOD:
#endif
case PERM:
fap->walkflags &= ~WALK_NOSTAT;
n->left = nextarg(fap, n);
if (getperm(fap->std[2], n, n->op == PERM, 0, n->op == PERM ? -1:0) < 0) {
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case MODE:
fap->walkflags &= ~WALK_NOSTAT;
ferrmsgno(fap->std[2], EX_BAD,
gettext("-mode not yet implemented.\n"));
errjmp(fap, EX_BAD);
/* NOTREACHED */
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case XDEV:
case MOUNT:
fap->walkflags &= ~WALK_NOSTAT;
fap->walkflags |= WALK_MOUNT;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case DEPTH:
fap->walkflags |= WALK_DEPTH;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case FOLLOW:
fap->walkflags &= ~WALK_PHYS;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case MAXDEPTH:
case MINDEPTH:
p = n->left = nextarg(fap, n);
p = astoll(p, &ll);
if (*p) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Non numeric character '%c' in '-%s %s'.\n"),
*p, tokennames[n->op], n->left);
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->val.l = ll;
if (n->op == MAXDEPTH)
fap->maxdepth = ll;
else
fap->mindepth = ll;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case NOUSER:
case NOGRP:
case PACL:
case XATTR:
case SPARSE:
case DOSTAT:
fap->walkflags &= ~WALK_NOSTAT;
case PRUNE:
case LTRUE:
case LFALSE:
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case OK_EXEC:
case EXEC: {
int i = 1;
n->this = (char *)fap->Argv; /* Cheat: Pointer is pointer */
nextarg(fap, n); /* Eat up cmd name */
while ((p = nextarg(fap, n)) != NULL) {
if (streql(p, ";"))
break;
else if (streql(p, "+") && streql(fap->Argv[-2], "{}")) {
n->op = fap->primtype = EXECPLUS;
if (!pluscreate(fap->std[2], --i, (char **)n->this, fap)) {
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
n->this = (char *)fap->plusp;
break;
}
i++;
}
n->val.i = i;
#ifdef PLUS_DEBUG
if (0) {
char **pp = (char **)n->this;
for (i = 0; i < n->val.i; i++, pp++)
printf("ARG %d '%s'\n", i, *pp);
}
#endif
}
/* FALLTHRU */
case LS:
fap->walkflags &= ~WALK_NOSTAT;
case PRINT:
case PRINTNNL:
fap->found_action = TRUE;
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case FIND_ENDARGS:
#ifdef DEBUG
ferrmsgno(fap->std[2], EX_BAD,
gettext("ENDARGS in parseprim()\n"));
#endif
ferrmsgno(fap->std[2], EX_BAD,
gettext("Incomplete expression.\n"));
find_freenode(n);
fap->jmp = ojmp; /* Restore old jump target */
return ((findn_t *)NULL);
case OPEN:
nexttoken(fap);
n->this = (char *)parse(fap);
if (fap->primtype != CLOSE) {
ferrmsgno(fap->std[2], EX_BAD,
gettext("Found '%s', but ')' expected.\n"),
fap->Argv[-1]);
errjmp(fap, EX_BAD);
/* NOTREACHED */
} else {
nexttoken(fap);
fap->jmp = ojmp; /* Restore old jump target */
return (n);
}
break;
case CLOSE:
ferrmsgno(fap->std[2], EX_BAD, gettext("Missing '('.\n"));
errjmp(fap, EX_BAD);
/* NOTREACHED */
case LNOT:
nexttoken(fap);
n->this = (char *)parseprim(fap);
if (n->this == NULL) {
find_freenode(n);
return ((findn_t *)NULL);
}
fap->jmp = ojmp; /* Restore old jump target */
return (n);
case AND:
case LOR:
ferrmsgno(fap->std[2], EX_BAD,
gettext("Invalid expression with -%s.\n"), tokennames[n->op]);
errjmp(fap, EX_BAD);
/* NOTREACHED */
case HELP:
find_usage(fap->std[2]);
find_freenode(n);
return ((findn_t *)NULL);
default:
ferrmsgno(fap->std[2], EX_BAD,
gettext("Internal malfunction.\n"));
errjmp(fap, EX_BAD);
/* NOTREACHED */
}
fap->jmp = ojmp; /* Restore old jump target */
fap->primtype = FIND_ERRARG; /* Mark as "parse aborted" */
return (0);
}
/*
* This is the mode bit translation code stolen from star...
*/
#define TSUID 04000 /* Set UID on execution */
#define TSGID 02000 /* Set GID on execution */
#define TSVTX 01000 /* On directories, restricted deletion flag */
#define TUREAD 00400 /* Read by owner */
#define TUWRITE 00200 /* Write by owner special */
#define TUEXEC 00100 /* Execute/search by owner */
#define TGREAD 00040 /* Read by group */
#define TGWRITE 00020 /* Write by group */
#define TGEXEC 00010 /* Execute/search by group */
#define TOREAD 00004 /* Read by other */
#define TOWRITE 00002 /* Write by other */
#define TOEXEC 00001 /* Execute/search by other */
#define TALLMODES 07777 /* The low 12 bits mentioned in the standard */
#define S_ALLPERM (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_ALLFLAGS (S_ISUID|S_ISGID|S_ISVTX)
#define S_ALLMODES (S_ALLFLAGS | S_ALLPERM)
#if S_ISUID == TSUID && S_ISGID == TSGID && S_ISVTX == TSVTX && \
S_IRUSR == TUREAD && S_IWUSR == TUWRITE && S_IXUSR == TUEXEC && \
S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC && \
S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
#define HAVE_POSIX_MODE_BITS /* st_mode bits are equal to TAR mode bits */
#endif
#ifdef HAVE_POSIX_MODE_BITS /* st_mode bits are equal to TAR mode bits */
#define OSMODE(xmode) (xmode)
#else
#define OSMODE(xmode) ((xmode & TSUID ? S_ISUID : 0) \
| (xmode & TSGID ? S_ISGID : 0) \
| (xmode & TSVTX ? S_ISVTX : 0) \
| (xmode & TUREAD ? S_IRUSR : 0) \
| (xmode & TUWRITE ? S_IWUSR : 0) \
| (xmode & TUEXEC ? S_IXUSR : 0) \
| (xmode & TGREAD ? S_IRGRP : 0) \
| (xmode & TGWRITE ? S_IWGRP : 0) \
| (xmode & TGEXEC ? S_IXGRP : 0) \
| (xmode & TOREAD ? S_IROTH : 0) \
| (xmode & TOWRITE ? S_IWOTH : 0) \
| (xmode & TOEXEC ? S_IXOTH : 0))
#endif
LOCAL int
getperm(f, t, findperm, smode, isX)
FILE *f; /* FILE * to print error messages to */
findn_t *t;
BOOL findperm; /* TRUE if we implement find -perm */
int smode; /* The start mode for the computation */
int isX; /* -1: Ignore X, 0: no dir/X 1: X OK */
{
char *p;
Llong ll;
mode_t mm;
p = t->left;
if (findperm && *p == '-')
p++;
if (*p >= '0' && *p <= '7') {
p = astollb(p, &ll, 8);
if (*p) {
ferrmsgno(f, EX_BAD,
gettext("Non octal character '%c' in '-%s %s'.\n"),
*p, tokennames[t->op], t->left);
return (-1);
}
mm = ll & TALLMODES;
t->val.mode = OSMODE(mm);
return (0);
}
p = getsperm(f, p, &t->val.mode, smode, isX);
if (p && *p != '\0') {
ferrmsgno(f, EX_BAD,
gettext("Bad perm character '%c' found in '-%s %s'.\n"),
*p, tokennames[t->op], t->left);
return (-1);
}
#ifdef PERM_DEBUG
error("GETPERM (%c) %4.4lo\n", *t->left, (long)t->val.mode);
#endif
return (0);
}
LOCAL char *
getsperm(f, p, mp, smode, isX)
FILE *f;
char *p; /* The perm input string */
mode_t *mp; /* To set t->val.mode */
int smode; /* The start mode for the computation */
int isX; /* -1: Ignore X, 0: no dir/X 1: X OK */
{
#ifdef OLD
mode_t permval = 0; /* POSIX start value for "find" */
#else
mode_t permval = smode; /* POSIX start value for "find" */
#endif
mode_t who;
mode_t m;
int op;
mode_t perms;
mode_t pm;
nextclause:
#ifdef PERM_DEBUG
error("getsperm(%s)\n", p);
#endif
who = 0;
while ((m = iswho(*p)) != 0) {
p++;
who |= m;
}
if (who == 0) {
mode_t mask = umask(0);
umask(mask);
who = ~mask;
who &= (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
}
#ifdef PERM_DEBUG
error("WHO %4.4lo\n", (long)who);
error("getsperm(--->%s)\n", p);
#endif
nextop:
if ((op = isop(*p)) != '\0')
p++;
#ifdef PERM_DEBUG
error("op '%c'\n", op);
error("getsperm(--->%s)\n", p);
#endif
if (op == 0) {
ferrmsgno(f, EX_BAD, gettext("Missing -perm op.\n"));
return (p);
}
perms = 0;
while ((pm = isperm(*p, isX)) != (mode_t)-1) {
p++;
perms |= pm;
}
#ifdef PERM_DEBUG
error("PERM %4.4lo\n", (long)perms);
error("START PERMVAL %4.4lo\n", (long)permval);
#endif
if ((perms == 0) && (*p == 'u' || *p == 'g' || *p == 'o')) {
mode_t what = 0;
/*
* First select the bit triple we like to copy.
*/
switch (*p) {
case 'u':
what = permval & S_IRWXU;
break;
case 'g':
what = permval & S_IRWXG;
break;
case 'o':
what = permval & S_IRWXO;
break;
}
/*
* Now copy over bit by bit without relying on shifts
* that would make implicit assumptions on values.
*/
if (what & (S_IRUSR|S_IRGRP|S_IROTH))
perms |= (who & (S_IRUSR|S_IRGRP|S_IROTH));
if (what & (S_IWUSR|S_IWGRP|S_IWOTH))
perms |= (who & (S_IWUSR|S_IWGRP|S_IWOTH));
if (what & (S_IXUSR|S_IXGRP|S_IXOTH))
perms |= (who & (S_IXUSR|S_IXGRP|S_IXOTH));
p++;
}
#ifdef PERM_DEBUG
error("getsperm(--->%s)\n", p);
#endif
switch (op) {
case '=':
permval &= ~who;
/* FALLTHRU */
case '+':
permval |= (who & perms);
break;
case '-':
permval &= ~(who & perms);
break;
}
#ifdef PERM_DEBUG
error("END PERMVAL %4.4lo\n", (long)permval);
#endif
if (isop(*p))
goto nextop;
if (*p == ',') {
p++;
goto nextclause;
}
*mp = permval;
return (p);
}
LOCAL mode_t
iswho(c)
int c;
{
switch (c) {
case 'u': /* user */
return (S_ISUID|S_ISVTX|S_IRWXU);
case 'g': /* group */
return (S_ISGID|S_ISVTX|S_IRWXG);
case 'o': /* other */
return (S_ISVTX|S_IRWXO);
case 'a': /* all */
return (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
default:
return (0);
}
}
LOCAL int
isop(c)
int c; /* The current perm character to parse */
{
switch (c) {
case '+':
case '-':
case '=':
return (c);
default:
return (0);
}
}
LOCAL mode_t
isperm(c, isX)
int c; /* The current perm character to parse */
int isX; /* -1: Ignore X, 0: no dir/X 1: X OK */
{
switch (c) {
case 'r':
return (S_IRUSR|S_IRGRP|S_IROTH);
case 'w':
return (S_IWUSR|S_IWGRP|S_IWOTH);
case 'X':
if (isX < 0)
return ((mode_t)-1); /* Singnal parse error */
if (isX == 0)
return ((mode_t)0); /* No 'X' handling here */
/* FALLTROUGH */
case 'x':
return (S_IXUSR|S_IXGRP|S_IXOTH);
case 's':
return (S_ISUID|S_ISGID);
case 'l':
return (S_ISGID);
case 't':
return (S_ISVTX);
default:
return ((mode_t)-1);
}
}
EXPORT void
find_firstprim(pac, pav)
int *pac;
char *const *pav[];
{
register int cac = *pac;
register char *const *cav = *pav;
register char c;
while (cac > 0 &&
(c = **cav) != '-' && c != '(' && c != ')' && c != '!') {
cav++;
cac--;
}
*pac = cac;
*pav = cav;
}
EXPORT BOOL
find_primary(t, op)
findn_t *t;
int op;
{
BOOL ret = FALSE;
if (t->op == op) {
return (TRUE);
}
switch (t->op) {
case OPEN:
ret = find_primary((findn_t *)t->this, op);
break;
case LNOT:
ret = find_primary((findn_t *)t->this, op);
break;
case AND:
ret = find_primary((findn_t *)t->left, op);
if (ret)
return (ret);
ret = find_primary((findn_t *)t->right, op);
break;
case LOR:
ret = find_primary((findn_t *)t->left, op);
if (ret)
return (ret);
ret = find_primary((findn_t *)t->right, op);
break;
default:
;
}
return (ret);
}
EXPORT BOOL
find_pname(t, word)
findn_t *t;
char *word;
{
if (streql(word, "-exec+"))
return (find_primary(t, EXECPLUS));
return (find_primary(t, find_token(word)));
}
#ifdef FIND_MAIN
LOCAL int
walkfunc(nm, fs, type, state)
char *nm;
struct stat *fs;
int type;
struct WALK *state;
{
if (type == WALK_NS) {
ferrmsg(state->std[2], gettext("Cannot stat '%s'.\n"), nm);
state->err = 1;
return (0);
} else if (type == WALK_SLN && (state->walkflags & WALK_PHYS) == 0) {
ferrmsg(state->std[2],
gettext("Cannot follow symlink '%s'.\n"), nm);
state->err = 1;
return (0);
} else if (type == WALK_DNR) {
if (state->flags & WALK_WF_NOCHDIR) {
ferrmsg(state->std[2],
gettext("Cannot chdir to '%s'.\n"), nm);
} else {
ferrmsg(state->std[2],
gettext("Cannot read '%s'.\n"), nm);
}
state->err = 1;
return (0);
}
if (state->maxdepth >= 0 && state->level >= state->maxdepth)
state->flags |= WALK_WF_PRUNE;
if (state->mindepth >= 0 && state->level < state->mindepth)
return (0);
find_expr(nm, nm + state->base, fs, state, state->tree);
return (0);
}
#endif
#ifdef __FIND__
LOCAL inline BOOL
#else
EXPORT BOOL
#endif
find_expr(f, ff, fs, state, t)
char *f; /* path name */
char *ff; /* file name */
struct stat *fs;
struct WALK *state;
findn_t *t;
{
time_t xtime;
char *p;
char lname[8192];
switch (t->op) {
case LNAME: {
int lsize;
if (!S_ISLNK(fs->st_mode))
return (FALSE);
if (state->lname != NULL) {
p = state->lname;
goto nmatch;
}
lname[0] = '\0';
lsize = readlink(state->level ? ff : f, lname, sizeof (lname));
if (lsize < 0) {
ferrmsg(state->std[2],
gettext("Cannot read link '%s'.\n"), ff);
return (FALSE);
}
lname[sizeof (lname)-1] = '\0';
if (lsize >= 0)
lname[lsize] = '\0';
p = lname;
goto nmatch;
}
case PATH:
p = f;
goto nmatch;
case NAME:
p = ff;
nmatch:
#if defined(HAVE_FNMATCH_H) & defined(HAVE_FNMATCH)
return (!fnmatch(t->this, p, 0));
#else
goto pattern; /* Use patmatch() as "fallback" */
#endif
case LPAT: {
int lsize;
if (!S_ISLNK(fs->st_mode))
return (FALSE);
if (state->lname != NULL) {
p = state->lname;
goto pattern;
}
lname[0] = '\0';
lsize = readlink(state->level ? ff : f, lname, sizeof (lname));
if (lsize < 0) {
ferrmsg(state->std[2],
gettext("Cannot read link '%s'.\n"), ff);
return (FALSE);
}
lname[sizeof (lname)-1] = '\0';
if (lsize >= 0)
lname[lsize] = '\0';
p = lname;
goto pattern;
}
case PPAT:
p = f;
goto pattern;
case PAT:
p = ff;
pattern: {
Uchar *pr; /* patmatch() return */
pr = patmatch((Uchar *)t->this, (int *)t->right,
(Uchar *)p, 0, strlen(p), t->val.i, state->patstate);
return (*p && pr && (*pr == '\0'));
}
case SIZE:
switch (*(t->left)) {
case '+':
if (t->this)
return (fs->st_size > t->val.size);
return ((fs->st_size+511)/512 > t->val.size);
case '-':
if (t->this)
return (fs->st_size < t->val.size);
return ((fs->st_size+511)/512 < t->val.size);
default:
if (t->this)
return (fs->st_size == t->val.size);
return ((fs->st_size+511)/512 == t->val.size);
}
case LINKS:
switch (*(t->left)) {
case '+':
return (fs->st_nlink > t->val.nlink);
case '-':
return (fs->st_nlink < t->val.nlink);
default:
return (fs->st_nlink == t->val.nlink);
}
case INUM:
switch (*(t->left)) {
case '+':
return (fs->st_ino > t->val.ino);
case '-':
return (fs->st_ino < t->val.ino);
default:
return (fs->st_ino == t->val.ino);
}
case LINKEDTO:
return ((fs->st_ino == t->val.ino) &&
(fs->st_dev == t->val2.dev));
case ATIME:
xtime = fs->st_atime;
goto times;
case CTIME:
xtime = fs->st_ctime;
goto times;
case MTIME:
case TIME:
xtime = fs->st_mtime;
times:
if (t->val2.i != 0)
goto timex;
switch (*(t->left)) {
case '+':
return ((find_now-xtime)/DAYSECS > t->val.time);
case '-':
return ((find_now-xtime)/DAYSECS < t->val.time);
default:
return ((find_now-xtime)/DAYSECS == t->val.time);
}
timex:
switch (*(t->left)) {
case '+':
return ((find_now-xtime) > t->val.time);
case '-':
return ((find_now-xtime) < t->val.time);
default:
return ((find_now-xtime) == t->val.time);
}
case NEWERAA:
case NEWERAC:
case NEWERAM:
return (t->val.time < fs->st_atime);
case NEWERCA:
case NEWERCC:
case NEWERCM:
return (t->val.time < fs->st_ctime);
case NEWER:
case NEWERMA:
case NEWERMC:
case NEWERMM:
return (t->val.time < fs->st_mtime);
case TYPE:
switch (*(t->this)) {
case 'b':
return (S_ISBLK(fs->st_mode));
case 'c':
return (S_ISCHR(fs->st_mode));
case 'd':
return (S_ISDIR(fs->st_mode));
case 'D':
return (S_ISDOOR(fs->st_mode));
case 'e':
return (S_ISEVC(fs->st_mode));
case 'f':
return (S_ISREG(fs->st_mode));
case 'l':
return (S_ISLNK(fs->st_mode));
case 'p':
return (S_ISFIFO(fs->st_mode));
case 'P':
return (S_ISPORT(fs->st_mode));
case 's':
return (S_ISSOCK(fs->st_mode));
default:
return (FALSE);
}
case FSTYPE:
#ifdef HAVE_ST_FSTYPE
return (streql(t->this, fs->st_fstype));
#else
return (TRUE);
#endif
case LOCL:
#ifdef HAVE_ST_FSTYPE
if (streql("nfs", fs->st_fstype) ||
streql("autofs", fs->st_fstype) ||
streql("cachefs", fs->st_fstype))
return (FALSE);
#endif
return (TRUE);
#ifdef CHOWN
case CHOWN:
fs->st_uid = t->val.uid;
return (TRUE);
#endif
case USER:
switch (*(t->left)) {
case '+':
return (fs->st_uid > t->val.uid);
case '-':
return (fs->st_uid < t->val.uid);
default:
return (fs->st_uid == t->val.uid);
}
#ifdef CHGRP
case CHGRP:
fs->st_gid = t->val.gid;
return (TRUE);
#endif
case GROUP:
switch (*(t->left)) {
case '+':
return (fs->st_gid > t->val.gid);
case '-':
return (fs->st_gid < t->val.gid);
default:
return (fs->st_gid == t->val.gid);
}
#ifdef CHMOD
case CHMOD:
getperm(state->std[2], t, FALSE, fs->st_mode & S_ALLMODES,
S_ISDIR(fs->st_mode) ||
(fs->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) != 0);
fs->st_mode &= ~S_ALLMODES;
fs->st_mode |= t->val.mode;
return (TRUE);
#endif
case PERM:
if (t->left[0] == '-')
return ((fs->st_mode & t->val.mode) == t->val.mode);
else
return ((fs->st_mode & S_ALLMODES) == t->val.mode);
case MODE:
return (TRUE);
case XDEV:
case MOUNT:
case DEPTH:
case FOLLOW:
case DOSTAT:
return (TRUE);
case NOUSER:
/* return (nameuid(NULL, 32, fs->st_uid));*/
return (getpwuid(fs->st_uid) == NULL);
case NOGRP:
/* return (namegid(NULL, 32, fs->st_gid));*/
return (getgrgid(fs->st_gid) == NULL);
case PRUNE:
state->flags |= WALK_WF_PRUNE;
return (TRUE);
case MAXDEPTH:
case MINDEPTH:
return (TRUE);
case PACL:
if (state->pflags & PF_ACL) {
return ((state->pflags & PF_HAS_ACL) != 0);
}
return (has_acl(state->std[2], f, ff, fs));
case XATTR:
if (state->pflags & PF_XATTR) {
return ((state->pflags & PF_HAS_XATTR) != 0);
}
return (has_xattr(state->std[2], ff));
case SPARSE:
if (!S_ISREG(fs->st_mode))
return (FALSE);
#ifdef HAVE_ST_BLOCKS
return (fs->st_size > (fs->st_blocks * DEV_BSIZE + DEV_BSIZE));
#else
return (FALSE);
#endif
case OK_EXEC: {
char qbuf[32];
fflush(state->std[1]);
fprintf(state->std[2], "< %s ... %s > ? ", ((char **)t->this)[0], f);
fflush(state->std[2]);
fgetline(state->std[0], qbuf, sizeof (qbuf) - 1);
switch (qbuf[0]) {
case 'y':
if (qbuf[1] == '\0' || streql(qbuf, "yes")) break;
default:
return (FALSE);
}
}
/* FALLTHRU */
case EXEC:
return (doexec(f, t->val.i, (char **)t->this, state));
case EXECPLUS:
return (plusexec(f, t, state));
case PRINT:
filewrite(state->std[1], f, strlen(f));
putc('\n', state->std[1]);
return (TRUE);
case PRINTNNL:
filewrite(state->std[1], f, strlen(f));
putc(' ', state->std[1]);
return (TRUE);
case LS:
/*
* The third parameter is the file name used for readlink()
* (inside find_list()) relatively to the current working
* directory. For file names from the command line, we did not
* perform a chdir() before, so we need to use the full path
* name.
*/
find_list(state->std, fs, f, state->level ? ff : f, state);
return (TRUE);
case LTRUE:
return (TRUE);
case LFALSE:
return (FALSE);
case OPEN:
return (find_expr(f, ff, fs, state, (findn_t *)t->this));
case LNOT:
return (!find_expr(f, ff, fs, state, (findn_t *)t->this));
case AND:
return (find_expr(f, ff, fs, state, (findn_t *)t->left) ?
find_expr(f, ff, fs, state, (findn_t *)t->right) : 0);
case LOR:
return (find_expr(f, ff, fs, state, (findn_t *)t->left) ?
1 : find_expr(f, ff, fs, state, (findn_t *)t->right));
}
return (FALSE); /* Unknown operator ??? */
}
LOCAL BOOL
doexec(f, ac, av, state)
char *f;
int ac;
char **av;
struct WALK *state;
{
pid_t pid;
int retval;
if ((pid = fork()) < 0) {
ferrmsg(state->std[2], gettext("Cannot fork child.\n"));
return (FALSE);
}
if (pid) {
while (wait(&retval) != pid)
/* LINTED */
;
return (retval == 0);
} else {
register int i;
register char **pp = av;
/*
* This is the forked process and for this reason, we may
* call fcomerr() here without problems.
*/
if (walkhome(state) < 0) {
fcomerr(state->std[2],
gettext("Cannot chdir to '.'.\n"));
}
#ifndef F_SETFD
walkclose(state);
#endif
#define iscurlypair(p) ((p)[0] == '{' && (p)[1] == '}' && (p)[2] == '\0')
if (f) {
for (i = 0; i < ac; i++, pp++) {
register char *p = *pp;
if (iscurlypair(p)) /* streql(p, "{}") */
*pp = f;
}
}
#ifdef PLUS_DEBUG
error("argsize %d\n",
(plusp->endp - (char *)&plusp->nextargp[0]) -
(plusp->laststr - (char *)&plusp->nextargp[1]));
#endif
fexecv(av[0], state->std[0], state->std[1], state->std[2], ac, av);
#ifdef PLUS_DEBUG
error("argsize %d\n",
(plusp->endp - (char *)&plusp->nextargp[0]) -
(plusp->laststr - (char *)&plusp->nextargp[1]));
#endif
/*
* This is the forked process and for this reason, we may
* call fcomerr() here without problems.
*/
fcomerr(state->std[2],
gettext("Cannot execute '%s'.\n"), av[0]);
/* NOTREACHED */
return (-1);
}
}
#ifndef LINE_MAX
#define LINE_MAX 1024
#endif
#if defined(IS_MACOS_X) && defined(HAVE_CRT_EXTERNS_H)
/*
* The MAC OS X linker does not grok "common" varaibles.
* We need to fetch the address of "environ" using a hack.
*/
#include <crt_externs.h>
#define environ *_NSGetEnviron()
#else
extern char **environ;
#endif
/*
* Return ARG_MAX - LINE_MAX - size of current environment.
*
* The return value is reduced by LINE_MAX to allow the called
* program to do own exec(2) calls with slightly increased arg size.
*/
LOCAL int
argsize()
{
static int ret = 0;
if (ret == 0) {
register int evs = 0;
register char **ep;
for (ep = environ; *ep; ep++) {
evs += strlen(*ep) + 1 + sizeof (ep);
}
evs += sizeof (char **); /* The environ NULL ptr at the end */
#ifdef _SC_ARG_MAX
ret = sysconf(_SC_ARG_MAX);
if (ret < 0)
ret = _POSIX_ARG_MAX;
#else
#ifdef ARG_MAX
ret = ARG_MAX;
#else
#ifdef NCARGS
ret = NCARGS;
#endif
#endif
#endif
#ifdef PLUS_DEBUG
ret = 3000;
#define LINE_MAX 100
error("evs %d\n", evs);
#endif
if (ret <= 0)
ret = 10000; /* Just a guess */
ret -= evs;
if ((ret - LINE_MAX) > 0)
ret -= LINE_MAX;
else
ret -= 100;
}
return (ret);
}
LOCAL BOOL
pluscreate(f, ac, av, fap)
FILE *f;
int ac;
char **av;
finda_t *fap;
{
struct plusargs *pp;
register char **ap = av;
register int i;
#ifdef PLUS_DEBUG
printf("Argc %d\n", ac);
ap = av;
for (i = 0; i < ac; i++, ap++)
printf("ARG %d '%s'\n", i, *ap);
#endif
pp = __fjmalloc(fap->std[2], argsize() + sizeof (struct plusargs),
"-exec args", fap->jmp);
pp->laststr = pp->endp = (char *)(&pp->av[0]) + argsize();
pp->ac = 0;
pp->nextargp = &pp->av[0];
#ifdef PLUS_DEBUG
printf("pp %d\n", pp);
printf("pp->laststr %d\n", pp->laststr);
printf("argsize() %d\n", argsize());
#endif
/*
* Copy args from command line.
*/
ap = av;
for (i = 0; i < ac; i++, ap++) {
#ifdef PLUS_DEBUG
printf("ARG %d '%s'\n", i, *ap);
#endif
*(pp->nextargp++) = *ap;
pp->laststr -= strlen(*ap) + 1;
pp->ac++;
if (pp->laststr <= (char *)pp->nextargp) {
ferrmsgno(f, EX_BAD,
gettext("No space to copy -exec args.\n"));
free(pp); /* The exec plusargs struct */
return (FALSE);
}
}
#ifdef PLUS_DEBUG
error("lastr %d endp %d diff %d\n",
pp->laststr, pp->endp, pp->endp - pp->laststr);
#endif
pp->endp = pp->laststr; /* Reduce endp by the size of cmdline args */
#ifdef PLUS_DEBUG
ap = &pp->av[0];
for (i = 0; i < pp->ac; i++, ap++) {
printf("ARG %d '%s'\n", i, *ap);
}
#endif
#ifdef PLUS_DEBUG
printf("pp %d\n", pp);
printf("pp->laststr %d\n", pp->laststr);
#endif
pp->next = fap->plusp;
fap->plusp = pp;
#ifdef PLUS_DEBUG
plusp = fap->plusp; /* Makes libfind not MT safe */
#endif
return (TRUE);
}
LOCAL BOOL
plusexec(f, t, state)
char *f;
findn_t *t;
struct WALK *state;
{
register struct plusargs *pp = (struct plusargs *)t->this;
#ifdef PLUS_DEBUG
register char **ap;
register int i;
#endif
int size;
int slen = strlen(f) + 1;
char *cp;
int ret = TRUE;
size = pp->laststr - (char *)&pp->nextargp[2];
if (slen > size) {
pp->nextargp[0] = NULL;
ret = doexec(NULL, pp->ac, pp->av, state);
pp->laststr = pp->endp;
pp->ac = t->val.i;
pp->nextargp = &pp->av[t->val.i];
size = pp->laststr - (char *)&pp->nextargp[2];
}
if (slen > size) {
ferrmsgno(state->std[2], EX_BAD,
gettext("No space for arg '%s'.\n"), f);
return (FALSE);
}
cp = pp->laststr - slen;
strcpy(cp, f);
pp->nextargp[0] = cp;
pp->ac++;
pp->nextargp++;
pp->laststr -= slen;
#ifdef PLUS_DEBUG
ap = &plusp->av[0];
for (i = 0; i < plusp->ac; i++, ap++) {
printf("ARG %d '%s'\n", i, *ap);
}
error("EXECPLUS '%s'\n", f);
#endif
return (ret);
}
EXPORT int
find_plusflush(p, state)
void *p;
struct WALK *state;
{
struct plusargs *plusp = p;
BOOL ret = TRUE;
/*
* Execute all unflushed '-exec .... {} +' expressions.
*/
while (plusp) {
#ifdef PLUS_DEBUG
error("lastr %p endp %p\n", plusp->laststr, plusp->endp);
#endif
if (plusp->laststr != plusp->endp) {
plusp->nextargp[0] = NULL;
if (!doexec(NULL, plusp->ac, plusp->av, state))
ret = FALSE;
}
plusp = plusp->next;
}
return (ret);
}
EXPORT void
find_usage(f)
FILE *f;
{
fprintf(f, gettext("Usage: %s [options] [path_1 ... path_n] [expression]\n"), get_progname());
fprintf(f, gettext("Options:\n"));
fprintf(f, gettext(" -H follow symbolic links encountered on command line\n"));
fprintf(f, gettext(" -L follow all symbolic links\n"));
fprintf(f, gettext("* -P do not follow symbolic links (default)\n"));
fprintf(f, gettext("* -help Print this help.\n"));
fprintf(f, gettext("* -version Print version number.\n"));
fprintf(f, gettext("Operators in decreasing precedence:\n"));
fprintf(f, gettext(" ( ) group an expression\n"));
fprintf(f, gettext(" !, -a, -o negate a primary (unary NOT), logical AND, logical OR\n"));
fprintf(f, gettext("Primaries:\n"));
fprintf(f, gettext("* -acl TRUE if the file has additional ACLs defined\n"));
fprintf(f, gettext(" -atime # TRUE if st_atime is in specified range\n"));
#ifdef CHGRP
fprintf(f, gettext("* -chgrp gname/gid always TRUE, sets st_gid to gname/gid\n"));
#endif
#ifdef CHMOD
fprintf(f, gettext("* -chmod mode/onum always TRUE, sets permissions to mode/onum\n"));
#endif
#ifdef CHOWN
fprintf(f, gettext("* -chown uname/uid always TRUE, sets st_uid to uname/uid\n"));
#endif
fprintf(f, gettext(" -ctime # TRUE if st_ctime is in specified range\n"));
fprintf(f, gettext(" -depth evaluate directory content before directory (always TRUE)\n"));
fprintf(f, gettext("* -dostat Do not do stat optimization (always TRUE)\n"));
fprintf(f, gettext(" -exec program [argument ...] \\;\n"));
fprintf(f, gettext(" -exec program [argument ...] {} +\n"));
fprintf(f, gettext("* -false always FALSE\n"));
fprintf(f, gettext("* -follow outdated: follow all symbolic links (always TRUE)\n"));
fprintf(f, gettext("* -fstype type TRUE if st_fstype matches type\n"));
fprintf(f, gettext(" -group gname/gid TRUE if st_gid matches gname/gid\n"));
fprintf(f, gettext("* -inum # TRUE if st_ino is in specified range\n"));
fprintf(f, gettext("* -linkedto path TRUE if the file is linked to path\n"));
fprintf(f, gettext(" -links # TRUE if st_nlink is in specified range\n"));
fprintf(f, gettext("* -lname glob TRUE if symlink name matches shell glob\n"));
fprintf(f, gettext("* -local TRUE if st_fstype does not match remote fs types\n"));
fprintf(f, gettext("* -lpat pattern TRUE if symlink name matches pattern\n"));
fprintf(f, gettext("* -ls list files similar to 'ls -ilds' (always TRUE)\n"));
fprintf(f, gettext("* -maxdepth # descend at most # directory levels (always TRUE)\n"));
fprintf(f, gettext("* -mindepth # start tests at directory level # (always TRUE)\n"));
fprintf(f, gettext(" -mtime # TRUE if st_mtime is in specified range\n"));
fprintf(f, gettext(" -name glob TRUE if path component matches shell glob\n"));
fprintf(f, gettext(" -newer file TRUE if st_mtime newer then mtime of file\n"));
fprintf(f, gettext(" -newerXY file TRUE if [acm]time (X) newer then [acm]time (Y) of file\n"));
fprintf(f, gettext(" -nogroup TRUE if not in group database\n"));
fprintf(f, gettext(" -nouser TRUE if not in user database\n"));
fprintf(f, gettext(" -ok program [argument ...] \\;\n"));
fprintf(f, gettext("* -pat pattern TRUE if path component matches pattern\n"));
fprintf(f, gettext("* -path glob TRUE if full path matches shell glob\n"));
fprintf(f, gettext(" -perm mode/onum TRUE if symbolic/octal permission matches\n"));
fprintf(f, gettext("* -ppat pattern TRUE if full path matches pattern\n"));
fprintf(f, gettext(" -print print file names line separated to stdout (always TRUE)\n"));
fprintf(f, gettext("* -printnnl print file names space separated to stdout (always TRUE)\n"));
fprintf(f, gettext(" -prune do not descent current directory (always TRUE)\n"));
fprintf(f, gettext(" -size # TRUE if st_size is in specified range\n"));
fprintf(f, gettext("* -sparse TRUE if file appears to be sparse\n"));
fprintf(f, gettext("* -true always TRUE\n"));
fprintf(f, gettext(" -type c TRUE if file type matches, c is from (b c d D e f l p P s)\n"));
fprintf(f, gettext(" -user uname/uid TRUE if st_uid matches uname/uid\n"));
fprintf(f, gettext("* -xattr TRUE if the file has extended attributes\n"));
fprintf(f, gettext(" -xdev, -mount restrict search to current filesystem (always TRUE)\n"));
fprintf(f, gettext("Primaries marked with '*' are POSIX extensions, avoid them in portable scripts.\n"));
fprintf(f, gettext("If path is omitted, '.' is used. If expression is omitted, -print is used.\n"));
}
#ifdef FIND_MAIN
/* ARGSUSED */
LOCAL int
getflg(optstr, argp)
char *optstr;
long *argp;
{
/* error("optstr: '%s'\n", optstr);*/
if (optstr[1] != '\0')
return (-1);
switch (*optstr) {
case 'H':
*(int *)argp |= WALK_ARGFOLLOW;
return (TRUE);
case 'L':
*(int *)argp |= WALK_ALLFOLLOW;
return (TRUE);
case 'P':
*(int *)argp &= ~(WALK_ARGFOLLOW | WALK_ALLFOLLOW);
return (TRUE);
default:
return (-1);
}
}
EXPORT int
main(ac, av)
int ac;
char **av;
{
int cac = ac;
char * *cav = av;
char * *firstpath;
char * *firstprim;
BOOL help = FALSE;
BOOL prversion = FALSE;
finda_t fa;
findn_t *Tree;
struct WALK walkstate;
save_args(ac, av);
#ifdef USE_NLS
setlocale(LC_ALL, "");
bindtextdomain("SCHILY_FIND", "/opt/schily/lib/locale");
textdomain("SCHILY_FIND");
#endif
find_argsinit(&fa);
fa.walkflags = WALK_CHDIR | WALK_PHYS;
fa.walkflags |= WALK_NOSTAT;
fa.walkflags |= WALK_NOEXIT;
/*
* Do not check the return code for getargs() as we may get an error
* code from e.g. "find -print" and we do not like to handle this here.
*/
cac--, cav++;
getargs(&cac, (char * const **)&cav, "help,version,&",
&help, &prversion,
getflg, (long *)&fa.walkflags);
if (help) {
find_usage(stderr);
return (0);
}
if (prversion) {
printf("sfind release %s (%s-%s-%s) Copyright (C) 2004-2007 Jörg Schilling\n",
strvers,
HOST_CPU, HOST_VENDOR, HOST_OS);
return (0);
}
firstpath = cav; /* Remember first file type arg */
find_firstprim(&cac, (char *const **)&cav);
firstprim = cav; /* Remember first Primary type arg */
fa.Argv = cav;
fa.Argc = cac;
if (cac) {
Tree = find_parse(&fa);
if (fa.primtype == FIND_ERRARG) {
find_free(Tree, &fa);
return (fa.error);
}
if (fa.primtype != FIND_ENDARGS) {
ferrmsgno(stderr, EX_BAD,
gettext("Incomplete expression.\n"));
find_free(Tree, &fa);
return (EX_BAD);
}
if (find_pname(Tree, "-chown") || find_pname(Tree, "-chgrp") ||
find_pname(Tree, "-chmod")) {
ferrmsgno(stderr, EX_BAD,
gettext("Unsupported primary -chown/-chgrp/-chmod.\n"));
find_free(Tree, &fa);
return (EX_BAD);
}
} else {
Tree = 0;
}
if (Tree == 0) {
Tree = find_printnode();
} else if (!fa.found_action) {
Tree = find_addprint(Tree, &fa);
if (Tree == (findn_t *)NULL)
return (geterrno());
}
walkinitstate(&walkstate);
if (fa.patlen > 0) {
walkstate.patstate = __jmalloc(sizeof (int) * fa.patlen,
"space for pattern state", JM_RETURN);
if (walkstate.patstate == NULL)
return (geterrno());
}
find_timeinit(time(0));
walkstate.walkflags = fa.walkflags;
walkstate.maxdepth = fa.maxdepth;
walkstate.mindepth = fa.mindepth;
walkstate.lname = NULL;
walkstate.tree = Tree;
walkstate.err = 0;
walkstate.pflags = 0;
if (firstpath == firstprim) {
treewalk(".", walkfunc, &walkstate);
} else {
for (cav = firstpath; cav != firstprim; cav++) {
treewalk(*cav, walkfunc, &walkstate);
/*
* XXX hier break wenn treewalk() Fehler gemeldet
*/
}
}
/*
* Execute all unflushed '-exec .... {} +' expressions.
*/
find_plusflush(fa.plusp, &walkstate);
find_free(Tree, &fa);
if (walkstate.patstate != NULL)
free(walkstate.patstate);
return (walkstate.err);
}
#endif /* FIND_MAIN */
syntax highlighted by Code2HTML, v. 0.9.1