diff options
author | phk <phk@FreeBSD.org> | 2001-05-03 18:05:35 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2001-05-03 18:05:35 +0000 |
commit | e51263b8a6769bca310145953de7cb2a93696e64 (patch) | |
tree | 9eec6ef5730dc41824c3e49c7f3642ecef218180 /usr.bin/find | |
parent | d7b11c99fc99c4b8a79d95f29ec61a701a8956f2 (diff) | |
download | FreeBSD-src-e51263b8a6769bca310145953de7cb2a93696e64.zip FreeBSD-src-e51263b8a6769bca310145953de7cb2a93696e64.tar.gz |
They add the following commands:
-anewer
-cnewer
-mnewer
-okdir
-newer[acm][acmt]
With it, you can form queries like
find . -newerct '1 minute ago' -print
As an extra bonus, the program is ANSI-fied - the original version
relies on some obscure features of K&R C.
(This PR was submitted in 1999, and the submittor has kept the patch
updated ever since, hats off for him guys, and how about you close a PR ??)
PR: 9374
Submitted by: Martin Birgmeier <Martin.Birgmeier@aon.at>
Diffstat (limited to 'usr.bin/find')
-rw-r--r-- | usr.bin/find/Makefile | 5 | ||||
-rw-r--r-- | usr.bin/find/extern.h | 100 | ||||
-rw-r--r-- | usr.bin/find/find.1 | 61 | ||||
-rw-r--r-- | usr.bin/find/find.c | 17 | ||||
-rw-r--r-- | usr.bin/find/find.h | 71 | ||||
-rw-r--r-- | usr.bin/find/function.c | 1296 | ||||
-rw-r--r-- | usr.bin/find/ls.c | 2 | ||||
-rw-r--r-- | usr.bin/find/operator.c | 38 | ||||
-rw-r--r-- | usr.bin/find/option.c | 130 |
9 files changed, 793 insertions, 927 deletions
diff --git a/usr.bin/find/Makefile b/usr.bin/find/Makefile index 9691657..a815525 100644 --- a/usr.bin/find/Makefile +++ b/usr.bin/find/Makefile @@ -3,6 +3,9 @@ CFLAGS+= -Wall PROG= find -SRCS= find.c function.c ls.c main.c misc.c operator.c option.c +SRCS= find.c function.c ls.c main.c misc.c operator.c option.c getdate.y +CLEANFILES+= getdate.c y.tab.h +CFLAGS+= -I${.CURDIR}/../../gnu/usr.bin/cvs/lib -DHAVE_CONFIG_H +.PATH: ${.CURDIR}/../../contrib/cvs/lib .include <bsd.prog.mk> diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h index 442a950..f200786 100644 --- a/usr.bin/find/extern.h +++ b/usr.bin/find/extern.h @@ -47,50 +47,68 @@ PLAN *paren_squish __P((PLAN *)); struct stat; void printlong __P((char *, char *, struct stat *)); int queryuser __P((char **)); +OPTION *option __P((char *)); -PLAN *c_amin __P((char *)); -PLAN *c_atime __P((char *)); -PLAN *c_cmin __P((char *)); -PLAN *c_ctime __P((char *)); -PLAN *c_delete __P((void)); -PLAN *c_depth __P((void)); -PLAN *c_empty __P((void)); -PLAN *c_exec __P((char ***, int)); -PLAN *c_flags __P((char *)); -PLAN *c_execdir __P((char ***)); -PLAN *c_follow __P((void)); +creat_f c_Xmin; +creat_f c_Xtime; +creat_f c_and; +creat_f c_delete; +creat_f c_depth; +creat_f c_empty; +creat_f c_exec; +creat_f c_flags; +creat_f c_follow; #if !defined(__NetBSD__) -PLAN *c_fstype __P((char *)); +creat_f c_fstype; #endif -PLAN *c_group __P((char *)); -PLAN *c_iname __P((char *)); -PLAN *c_inum __P((char *)); -PLAN *c_ipath __P((char *)); -PLAN *c_iregex __P((char *)); -PLAN *c_links __P((char *)); -PLAN *c_ls __P((void)); -PLAN *c_name __P((char *)); -PLAN *c_newer __P((char *)); -PLAN *c_nogroup __P((void)); -PLAN *c_nouser __P((void)); -PLAN *c_path __P((char *)); -PLAN *c_perm __P((char *)); -PLAN *c_print __P((void)); -PLAN *c_print0 __P((void)); -PLAN *c_prune __P((void)); -PLAN *c_regex __P((char *)); -PLAN *c_size __P((char *)); -PLAN *c_type __P((char *)); -PLAN *c_user __P((char *)); -PLAN *c_xdev __P((void)); -PLAN *c_openparen __P((void)); -PLAN *c_closeparen __P((void)); -PLAN *c_maxdepth __P((char *)); -PLAN *c_mindepth __P((char *)); -PLAN *c_mmin __P((char *)); -PLAN *c_mtime __P((char *)); -PLAN *c_not __P((void)); -PLAN *c_or __P((void)); +creat_f c_group; +creat_f c_inum; +creat_f c_links; +creat_f c_ls; +creat_f c_mXXdepth; +creat_f c_name; +creat_f c_newer; +creat_f c_nogroup; +creat_f c_nouser; +creat_f c_perm; +creat_f c_print; +creat_f c_regex; +creat_f c_simple; +creat_f c_size; +creat_f c_type; +creat_f c_user; +creat_f c_xdev; + +exec_f f_Xmin; +exec_f f_Xtime; +exec_f f_always_true; +exec_f f_closeparen; +exec_f f_delete; +exec_f f_empty; +exec_f f_exec; +exec_f f_expr; +exec_f f_flags; +exec_f f_fstype; +exec_f f_group; +exec_f f_inum; +exec_f f_links; +exec_f f_ls; +exec_f f_name; +exec_f f_newer; +exec_f f_nogroup; +exec_f f_not; +exec_f f_nouser; +exec_f f_openparen; +exec_f f_or; +exec_f f_path; +exec_f f_perm; +exec_f f_print; +exec_f f_print0; +exec_f f_prune; +exec_f f_regex; +exec_f f_size; +exec_f f_type; +exec_f f_user; extern int ftsoptions, isdeprecated, isdepth, isoutput, issort, isxargs; extern int mindepth, maxdepth; diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1 index 10bf481..0a8d10b 100644 --- a/usr.bin/find/find.1 +++ b/usr.bin/find/find.1 @@ -160,6 +160,9 @@ True if the difference between the file last access time and the time was started, rounded up to the next full minute, is .Ar n minutes. +.It Ic -anewer Ar file +Same as +.Ic -neweram . .It Ic -atime Ar n True if the difference between the file last access time and the time .Nm @@ -173,6 +176,9 @@ information and the time was started, rounded up to the next full minute, is .Ar n minutes. +.It Ic -cnewer Ar file +Same as +.Ic -newercm . .It Ic -ctime Ar n True if the difference between the time of last change of file status information and the time @@ -256,6 +262,9 @@ will be displayed instead of the size in bytes. If the file is a symbolic link, the pathname of the linked\-to file will be displayed preceded by ``\->''. The format is identical to that produced by ``ls \-dgils''. +.It Ic -mnewer Ar file +Same as +.Ic -newer . .It Ic -maxdepth Ar n True if the depth of the current file into the tree is less than or equal to .Ar n . @@ -287,6 +296,14 @@ If the response is other than ``y'' the command is not executed and the value of the .Ar \&ok expression is false. +.It Ic \&-okdir Ar utility Op argument ... ; +The +.Ic \&-okdir +primary is identical to the +.Ic -execdir +primary with the same exception as described for the +.Ic \&-ok +primary. .It Ic -name Ar pattern True if the last component of the pathname being examined matches .Ar pattern . @@ -312,6 +329,46 @@ but the match is case insensitive. .It Ic -newer Ar file True if the current file has a more recent last modification time than .Ar file . +.It Ic -newerXY Ar file +True if the current file has a more recent last access time ( +.Ic X += +.Ic a +), change time ( +.Ic X += +.Ic c +), or modification time ( +.Ic X += +.Ic m +) than the last access time ( +.Ic Y += +.Ic a +), change time ( +.Ic Y += +.Ic c +), or modification time ( +.Ic Y += +.Ic m +) of +.Ar file . +In addition, if +.Ic Y += +.Ic t , +then +.Ar file +is instead interpreted as a direct date specification of the form +understood by +.Xr cvs 1 . +Note that +.Ic -newermm +is equivalent to +.Ic -newer . .It Ic -nouser True if the file belongs to an unknown user. .It Ic -nogroup @@ -512,10 +569,14 @@ and owned by ``wnj''. .It Li "find / \e( -newer ttt -or -user wnj \e) -print" Print out a list of all the files that are either owned by ``wnj'' or that are newer than ``ttt''. +.It Li "find . -newerct '1 minute ago' -print" +Print out a list of all the files whose inode change time is more +recent than the current time minus one minute. .El .Sh SEE ALSO .Xr chflags 1 , .Xr chmod 1 , +.Xr cvs 1 , .Xr locate 1 , .Xr whereis 1 , .Xr which 1 , diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c index ce2595e..b10715b 100644 --- a/usr.bin/find/find.c +++ b/usr.bin/find/find.c @@ -116,17 +116,24 @@ find_formplan(argv) * necessary, and add a -print node on the end. */ if (!isoutput) { + OPTION *p; + char **argv = 0; + if (plan == NULL) { - new = c_print(); + p = option("-print"); + new = (p->create)(p, &argv); tail = plan = new; } else { - new = c_openparen(); + p = option("("); + new = (p->create)(p, &argv); new->next = plan; plan = new; - new = c_closeparen(); + p = option(")"); + new = (p->create)(p, &argv); tail->next = new; tail = new; - new = c_print(); + p = option("-print"); + new = (p->create)(p, &argv); tail->next = new; tail = new; } @@ -220,7 +227,7 @@ find_execute(plan, paths) * false or all have been executed. This is where we do all * the work specified by the user on the command line. */ - for (p = plan; p && (p->eval)(p, entry); p = p->next); + for (p = plan; p && (p->execute)(p, entry); p = p->next); if (maxdepth != -1 && entry->fts_level >= maxdepth) { if (fts_set(tree, entry, FTS_SKIP)) diff --git a/usr.bin/find/find.h b/usr.bin/find/find.h index f240fc1..ce09344 100644 --- a/usr.bin/find/find.h +++ b/usr.bin/find/find.h @@ -39,37 +39,46 @@ #include <regex.h> -/* node type */ -enum ntype { - N_AND = 1, /* must start > 0 */ - N_AMIN, N_ATIME, N_CLOSEPAREN, N_CMIN, N_CTIME, N_DEPTH, - N_EMPTY, N_EXEC, N_EXECDIR, N_EXPR, N_FLAGS, - N_FOLLOW, N_FSTYPE, N_GROUP, N_INUM, N_LINKS, N_LS, N_MMIN, - N_MTIME, N_NAME, N_INAME, N_PATH, N_IPATH, N_REGEX, N_IREGEX, - N_NEWER, N_NOGROUP, N_NOT, N_NOUSER, N_OK, N_OPENPAREN, N_OR, - N_PERM, N_PRINT, N_PRUNE, N_SIZE, N_TYPE, N_USER, N_XDEV, - N_PRINT0, N_DELETE, N_MAXDEPTH, N_MINDEPTH -}; +/* forward declarations */ +struct _plandata; +struct _option; + +/* execute function */ +typedef int exec_f __P((struct _plandata *, FTSENT *)); +/* create function */ +typedef struct _plandata *creat_f(struct _option *, char ***); + +/* function modifiers */ +#define F_NEEDOK 0x00000001 /* -ok vs. -exec */ +#define F_EXECDIR 0x00000002 /* -execdir vs. -exec */ +#define F_TIME_A 0x00000004 /* one of -atime, -anewer, -newera* */ +#define F_TIME_C 0x00000008 /* one of -ctime, -cnewer, -newerc* */ +#define F_TIME2_A 0x00000010 /* one of -newer?a */ +#define F_TIME2_C 0x00000020 /* one of -newer?c */ +#define F_TIME2_T 0x00000040 /* one of -newer?t */ +#define F_MAXDEPTH F_TIME_A /* maxdepth vs. mindepth */ +/* command line function modifiers */ +#define F_EQUAL 0x00000000 /* [acm]min [acm]time inum links size */ +#define F_LESSTHAN 0x00000100 +#define F_GREATER 0x00000200 +#define F_ELG_MASK 0x00000300 +#define F_ATLEAST 0x00000400 /* flags perm */ +#define F_ANY 0x00000800 /* perm */ +#define F_MTMASK 0x00003000 +#define F_MTFLAG 0x00000000 /* fstype */ +#define F_MTTYPE 0x00001000 +#define F_MTUNKNOWN 0x00002000 +#define F_IGNCASE 0x00010000 /* iname ipath iregex */ /* node definition */ typedef struct _plandata { - struct _plandata *next; /* next node */ - int (*eval) /* node evaluation function */ - __P((struct _plandata *, FTSENT *)); -#define F_EQUAL 1 /* [acm]time inum links size */ -#define F_LESSTHAN 2 -#define F_GREATER 3 -#define F_NEEDOK 1 /* exec ok */ -#define F_MTFLAG 1 /* fstype */ -#define F_MTTYPE 2 -#define F_ATLEAST 1 /* perm */ -#define F_ANY 2 /* perm */ - int flags; /* private flags */ - enum ntype type; /* plan node type */ + struct _plandata *next; /* next node */ + exec_f *execute; /* node evaluation function */ + int flags; /* private flags */ union { - gid_t _g_data; /* gid */ - ino_t _i_data; /* inode */ - mode_t _m_data; /* mode mask */ + gid_t _g_data; /* gid */ + ino_t _i_data; /* inode */ + mode_t _m_data; /* mode mask */ struct { u_long _f_flags; u_long _f_mask; @@ -110,12 +119,8 @@ typedef struct _plandata { typedef struct _option { char *name; /* option name */ - enum ntype token; /* token type */ - PLAN *(*create)(); /* create function: DON'T PROTOTYPE! */ -#define O_NONE 0x01 /* no call required */ -#define O_ZERO 0x02 /* pass: nothing */ -#define O_ARGV 0x04 /* pass: argv, increment argv */ -#define O_ARGVP 0x08 /* pass: *argv, N_OK || N_EXEC || N_EXECDIR */ + creat_f *create; /* create function */ + exec_f *execute; /* execute function */ int flags; } OPTION; diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index d448946..a60c4c4 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -48,6 +48,7 @@ static const char rcsid[] = #include <sys/stat.h> #include <sys/wait.h> #include <sys/mount.h> +#include <sys/timeb.h> #include <dirent.h> #include <err.h> @@ -64,8 +65,10 @@ static const char rcsid[] = #include "find.h" +time_t get_date __P((char *date, struct timeb *now)); + #define COMPARE(a, b) { \ - switch (plan->flags) { \ + switch (plan->flags & F_ELG_MASK) { \ case F_EQUAL: \ return (a == b); \ case F_LESSTHAN: \ @@ -77,9 +80,19 @@ static const char rcsid[] = } \ } -static int do_f_regex __P((PLAN *, FTSENT *, int)); -static PLAN *do_c_regex __P((char *, int)); -static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); +static PLAN * +palloc(option) + OPTION *option; +{ + PLAN *new; + + if ((new = malloc(sizeof(PLAN))) == NULL) + err(1, NULL); + new->execute = option->execute; + new->flags = option->flags; + new->next = NULL; + return new; +} /* * find_parsenum -- @@ -98,14 +111,14 @@ find_parsenum(plan, option, vp, endch) switch (*str) { case '+': ++str; - plan->flags = F_GREATER; + plan->flags |= F_GREATER; break; case '-': ++str; - plan->flags = F_LESSTHAN; + plan->flags |= F_LESSTHAN; break; default: - plan->flags = F_EQUAL; + plan->flags |= F_EQUAL; break; } @@ -121,143 +134,218 @@ find_parsenum(plan, option, vp, endch) errx(1, "%s: %s: illegal trailing character", option, vp); if (endch) *endch = endchar[0]; - return (value); + return value; } /* + * nextarg -- + * Check that another argument still exists, return a pointer to it, + * and increment the argument vector pointer. + */ +static char * +nextarg(option, argvp) + OPTION *option; + char ***argvp; +{ + char *arg; + + if ((arg = **argvp) == 0) + errx(1, "%s: requires additional arguments", option->name); + (*argvp)++; + return arg; +} /* nextarg() */ + +/* * The value of n for the inode times (atime, ctime, and mtime) is a range, * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with * -n, such that "-mtime -1" would be less than 0 days, which isn't what the * user wanted. Correct so that -1 is "less than 1". */ -#define TIME_CORRECT(p, ttype) \ - if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ +#define TIME_CORRECT(p) \ + if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ ++((p)->t_data); /* - * -amin n functions -- + * -[acm]min n functions -- * - * True if the difference between the file access time and the - * current time is n min periods. + * True if the difference between the + * file access time (-amin) + * last change of file status information (-cmin) + * file modification time (-mmin) + * and the current time is n min periods. */ int -f_amin(plan, entry) +f_Xmin(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; - COMPARE((now - entry->fts_statp->st_atime + - 60 - 1) / 60, plan->t_data); + if (plan->flags & F_TIME_C) { + COMPARE((now - entry->fts_statp->st_ctime + + 60 - 1) / 60, plan->t_data); + } else if (plan->flags & F_TIME_A) { + COMPARE((now - entry->fts_statp->st_atime + + 60 - 1) / 60, plan->t_data); + } else { + COMPARE((now - entry->fts_statp->st_mtime + + 60 - 1) / 60, plan->t_data); + } } PLAN * -c_amin(arg) - char *arg; +c_Xmin(option, argvp) + OPTION *option; + char ***argvp; { + char *nmins; PLAN *new; + nmins = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_AMIN, f_amin); - new->t_data = find_parsenum(new, "-amin", arg, NULL); - TIME_CORRECT(new, N_AMIN); - return (new); + new = palloc(option); + new->t_data = find_parsenum(new, option->name, nmins, NULL); + TIME_CORRECT(new); + return new; } - /* - * -atime n functions -- + * -[acm]time n functions -- * - * True if the difference between the file access time and the - * current time is n 24 hour periods. + * True if the difference between the + * file access time (-atime) + * last change of file status information (-ctime) + * file modification time (-mtime) + * and the current time is n 24 hour periods. */ + int -f_atime(plan, entry) +f_Xtime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; - COMPARE((now - entry->fts_statp->st_atime + - 86400 - 1) / 86400, plan->t_data); + if (plan->flags & F_TIME_C) { + COMPARE((now - entry->fts_statp->st_ctime + + 86400 - 1) / 86400, plan->t_data); + } else if (plan->flags & F_TIME_A) { + COMPARE((now - entry->fts_statp->st_atime + + 86400 - 1) / 86400, plan->t_data); + } else { + COMPARE((now - entry->fts_statp->st_mtime + + 86400 - 1) / 86400, plan->t_data); + } } PLAN * -c_atime(arg) - char *arg; +c_Xtime(option, argvp) + OPTION *option; + char ***argvp; { + char *ndays; PLAN *new; + ndays = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_ATIME, f_atime); - new->t_data = find_parsenum(new, "-atime", arg, NULL); - TIME_CORRECT(new, N_ATIME); - return (new); + new = palloc(option); + new->t_data = find_parsenum(new, option->name, ndays, NULL); + TIME_CORRECT(new); + return new; } - /* - * -cmin n functions -- + * -maxdepth/-mindepth n functions -- + * + * Does the same as -prune if the level of the current file is + * greater/less than the specified maximum/minimum depth. * - * True if the difference between the last change of file - * status information and the current time is n min periods. + * Note that -maxdepth and -mindepth are handled specially in + * find_execute() so their f_* functions are set to f_always_true(). */ -int -f_cmin(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statp->st_ctime + - 60 - 1) / 60, plan->t_data); -} - PLAN * -c_cmin(arg) - char *arg; +c_mXXdepth(option, argvp) + OPTION *option; + char ***argvp; { + char *dstr; PLAN *new; - ftsoptions &= ~FTS_NOSTAT; + dstr = nextarg(option, argvp); + if (dstr[0] == '-') + /* all other errors handled by find_parsenum() */ + errx(1, "%s: %s: value must be positive", option->name, dstr); - new = palloc(N_CMIN, f_cmin); - new->t_data = find_parsenum(new, "-cmin", arg, NULL); - TIME_CORRECT(new, N_CMIN); - return (new); + new = palloc(option); + if (option->flags & F_MAXDEPTH) + maxdepth = find_parsenum(new, option->name, dstr, NULL); + else + mindepth = find_parsenum(new, option->name, dstr, NULL); + return new; } /* - * -ctime n functions -- + * -delete functions -- * - * True if the difference between the last change of file - * status information and the current time is n 24 hour periods. + * True always. Makes its best shot and continues on regardless. */ int -f_ctime(plan, entry) +f_delete(plan, entry) PLAN *plan; FTSENT *entry; { - extern time_t now; + /* ignore these from fts */ + if (strcmp(entry->fts_accpath, ".") == 0 || + strcmp(entry->fts_accpath, "..") == 0) + return 1; + + /* sanity check */ + if (isdepth == 0 || /* depth off */ + (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ + !(ftsoptions & FTS_PHYSICAL) || /* physical off */ + (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ + errx(1, "-delete: insecure options got turned on"); + + /* Potentially unsafe - do not accept relative paths whatsoever */ + if (strchr(entry->fts_accpath, '/') != NULL) + errx(1, "-delete: %s: relative path potentially not safe", + entry->fts_accpath); - COMPARE((now - entry->fts_statp->st_ctime + - 86400 - 1) / 86400, plan->t_data); + /* Turn off user immutable bits if running as root */ + if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + geteuid() == 0) + chflags(entry->fts_accpath, + entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); + + /* rmdir directories, unlink everything else */ + if (S_ISDIR(entry->fts_statp->st_mode)) { + if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) + warn("-delete: rmdir(%s)", entry->fts_path); + } else { + if (unlink(entry->fts_accpath) < 0) + warn("-delete: unlink(%s)", entry->fts_path); + } + + /* "succeed" */ + return 1; } PLAN * -c_ctime(arg) - char *arg; +c_delete(option, argvp) + OPTION *option; + char ***argvp; { - PLAN *new; - ftsoptions &= ~FTS_NOSTAT; + ftsoptions &= ~FTS_NOSTAT; /* no optimise */ + ftsoptions |= FTS_PHYSICAL; /* disable -follow */ + ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ + isoutput = 1; /* possible output */ + isdepth = 1; /* -depth implied */ - new = palloc(N_CTIME, f_ctime); - new->t_data = find_parsenum(new, "-ctime", arg, NULL); - TIME_CORRECT(new, N_CTIME); - return (new); + return palloc(option); } @@ -273,119 +361,17 @@ f_always_true(plan, entry) PLAN *plan; FTSENT *entry; { - return (1); + return 1; } PLAN * -c_depth() -{ - isdepth = 1; - - return (palloc(N_DEPTH, f_always_true)); -} - -/* - * [-exec | -ok] utility [arg ... ] ; functions -- - * - * True if the executed utility returns a zero value as exit status. - * The end of the primary expression is delimited by a semicolon. If - * "{}" occurs anywhere, it gets replaced by the current pathname. - * The current directory for the execution of utility is the same as - * the current directory when the find utility was started. - * - * The primary -ok is different in that it requests affirmation of the - * user before executing the utility. - */ -int -f_exec(plan, entry) - register PLAN *plan; - FTSENT *entry; -{ - extern int dotfd; - register int cnt; - pid_t pid; - int status; - - for (cnt = 0; plan->e_argv[cnt]; ++cnt) - if (plan->e_len[cnt]) - brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], - entry->fts_path, plan->e_len[cnt]); - - if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) - return (0); - - /* make sure find output is interspersed correctly with subprocesses */ - fflush(stdout); - - switch (pid = fork()) { - case -1: - err(1, "fork"); - /* NOTREACHED */ - case 0: - if (fchdir(dotfd)) { - warn("chdir"); - _exit(1); - } - execvp(plan->e_argv[0], plan->e_argv); - warn("%s", plan->e_argv[0]); - _exit(1); - } - pid = waitpid(pid, &status, 0); - return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); -} - -/* - * c_exec -- - * build three parallel arrays, one with pointers to the strings passed - * on the command line, one with (possibly duplicated) pointers to the - * argv array, and one with integer values that are lengths of the - * strings, but also flags meaning that the string has to be massaged. - */ -PLAN * -c_exec(argvp, isok) +c_depth(option, argvp) + OPTION *option; char ***argvp; - int isok; { - PLAN *new; /* node returned */ - register int cnt; - register char **argv, **ap, *p; - - isoutput = 1; - - new = palloc(N_EXEC, f_exec); - if (isok) - new->flags = F_NEEDOK; - - for (ap = argv = *argvp;; ++ap) { - if (!*ap) - errx(1, - "%s: no terminating \";\"", isok ? "-ok" : "-exec"); - if (**ap == ';') - break; - } - - cnt = ap - *argvp + 1; - new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); - new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); - new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); - - for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { - new->e_orig[cnt] = *argv; - for (p = *argv; *p; ++p) - if (p[0] == '{' && p[1] == '}') { - new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); - new->e_len[cnt] = MAXPATHLEN; - break; - } - if (!*p) { - new->e_argv[cnt] = *argv; - new->e_len[cnt] = 0; - } - } - new->e_argv[cnt] = new->e_orig[cnt] = NULL; + isdepth = 1; - *argvp = argv + 1; - return (new); + return palloc(option); } /* @@ -398,8 +384,9 @@ f_empty(plan, entry) PLAN *plan; FTSENT *entry; { - if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0) - return (1); + if (S_ISREG(entry->fts_statp->st_mode) && + entry->fts_statp->st_size == 0) + return 1; if (S_ISDIR(entry->fts_statp->st_mode)) { struct dirent *dp; int empty; @@ -417,50 +404,63 @@ f_empty(plan, entry) break; } closedir(dir); - return (empty); + return empty; } - return (0); + return 0; } PLAN * -c_empty() +c_empty(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; - return (palloc(N_EMPTY, f_empty)); + return palloc(option); } /* - * -execdir utility [arg ... ] ; functions -- + * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- * * True if the executed utility returns a zero value as exit status. * The end of the primary expression is delimited by a semicolon. If - * "{}" occurs anywhere, it gets replaced by the unqualified pathname. - * The current directory for the execution of utility is the same as - * the directory where the file lives. + * "{}" occurs anywhere, it gets replaced by the current pathname, + * or, in the case of -execdir, the current basename (filename + * without leading directory prefix). For -exec and -ok, + * the current directory for the execution of utility is the same as + * the current directory when the find utility was started, whereas + * for -execdir, it is the directory the file resides in. + * + * The primary -ok differs from -exec in that it requests affirmation + * of the user before executing the utility. */ int -f_execdir(plan, entry) +f_exec(plan, entry) register PLAN *plan; FTSENT *entry; { + extern int dotfd; register int cnt; pid_t pid; int status; char *file; /* XXX - if file/dir ends in '/' this will not work -- can it? */ - if ((file = strrchr(entry->fts_path, '/'))) - file++; + if ((plan->flags & F_EXECDIR) && \ + (file = strrchr(entry->fts_path, '/'))) + file++; else - file = entry->fts_path; + file = entry->fts_path; for (cnt = 0; plan->e_argv[cnt]; ++cnt) if (plan->e_len[cnt]) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], file, plan->e_len[cnt]); - /* don't mix output of command with find output */ + if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) + return 0; + + /* make sure find output is interspersed correctly with subprocesses */ fflush(stdout); fflush(stderr); @@ -469,6 +469,11 @@ f_execdir(plan, entry) err(1, "fork"); /* NOTREACHED */ case 0: + /* change dir back from where we started */ + if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) { + warn("chdir"); + _exit(1); + } execvp(plan->e_argv[0], plan->e_argv); warn("%s", plan->e_argv[0]); _exit(1); @@ -476,31 +481,35 @@ f_execdir(plan, entry) pid = waitpid(pid, &status, 0); return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); } - + /* - * c_execdir -- + * c_exec, c_execdir, c_ok -- * build three parallel arrays, one with pointers to the strings passed * on the command line, one with (possibly duplicated) pointers to the * argv array, and one with integer values that are lengths of the * strings, but also flags meaning that the string has to be massaged. */ PLAN * -c_execdir(argvp) +c_exec(option, argvp) + OPTION *option; char ***argvp; { PLAN *new; /* node returned */ register int cnt; register char **argv, **ap, *p; + /* XXX - was in c_execdir, but seems unnecessary!? ftsoptions &= ~FTS_NOSTAT; + */ isoutput = 1; - - new = palloc(N_EXECDIR, f_execdir); + + /* XXX - this is a change from the previous coding */ + new = palloc(option); for (ap = argv = *argvp;; ++ap) { if (!*ap) errx(1, - "-execdir: no terminating \";\""); + "%s: no terminating \";\"", option->name); if (**ap == ';') break; } @@ -526,7 +535,54 @@ c_execdir(argvp) new->e_argv[cnt] = new->e_orig[cnt] = NULL; *argvp = argv + 1; - return (new); + return new; +} + +int +f_flags(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + u_long flags; + + flags = entry->fts_statp->st_flags & + (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | + SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND); + if (plan->flags & F_ATLEAST) + /* note that plan->fl_flags always is a subset of + plan->fl_mask */ + return (flags & plan->fl_mask) == plan->fl_flags; + else if (plan->flags & F_ANY) + return flags & plan->fl_mask; + else + return flags == plan->fl_flags; + /* NOTREACHED */ +} + +PLAN * +c_flags(option, argvp) + OPTION *option; + char ***argvp; +{ + char *flags_str; + PLAN *new; + u_long flags, notflags; + + flags_str = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + + if (*flags_str == '-') { + new->flags |= F_ATLEAST; + flags_str++; + } + if (strtofflags(&flags_str, &flags, ¬flags) == 1) + errx(1, "%s: %s: illegal flags string", option->name, flags_str); + + new->fl_flags = flags; + new->fl_mask = flags | notflags; + return new; } /* @@ -536,12 +592,14 @@ c_execdir(argvp) * basis. */ PLAN * -c_follow() +c_follow(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; - return (palloc(N_FOLLOW, f_always_true)); + return palloc(option); } /* @@ -560,6 +618,9 @@ f_fstype(plan, entry) static int val_type, val_flags; char *p, save[2]; + if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) + return 0; + /* Only check when we cross mount point. */ if (first || curdev != entry->fts_statp->st_dev) { curdev = entry->fts_statp->st_dev; @@ -578,7 +639,6 @@ f_fstype(plan, entry) p[0] = '.'; save[1] = p[1]; p[1] = '\0'; - } else p = NULL; @@ -599,71 +659,66 @@ f_fstype(plan, entry) val_flags = sb.f_flags; val_type = sb.f_type; } - switch (plan->flags) { + switch (plan->flags & F_MTMASK) { case F_MTFLAG: - return (val_flags & plan->mt_data) != 0; + return val_flags & plan->mt_data; case F_MTTYPE: - return (val_type == plan->mt_data); + return val_type == plan->mt_data; default: abort(); } } #if !defined(__NetBSD__) -int -f_always_false(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (0); -} - PLAN * -c_fstype(arg) - char *arg; +c_fstype(option, argvp) + OPTION *option; + char ***argvp; { + char *fsname; register PLAN *new; struct vfsconf vfc; - + + fsname = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_FSTYPE, f_fstype); + new = palloc(option); /* * Check first for a filesystem name. */ - if (getvfsbyname(arg, &vfc) == 0) { - new->flags = F_MTTYPE; + if (getvfsbyname(fsname, &vfc) == 0) { + new->flags |= F_MTTYPE; new->mt_data = vfc.vfc_typenum; - return (new); + return new; } - switch (*arg) { + switch (*fsname) { case 'l': - if (!strcmp(arg, "local")) { - new->flags = F_MTFLAG; + if (!strcmp(fsname, "local")) { + new->flags |= F_MTFLAG; new->mt_data = MNT_LOCAL; - return (new); + return new; } break; case 'r': - if (!strcmp(arg, "rdonly")) { - new->flags = F_MTFLAG; + if (!strcmp(fsname, "rdonly")) { + new->flags |= F_MTFLAG; new->mt_data = MNT_RDONLY; - return (new); + return new; } break; } + /* * We need to make filesystem checks for filesystems * that exists but aren't in the kernel work. */ - fprintf(stderr, "Warning: Unknown filesystem type %s\n", arg); - free(new); - - return (palloc(N_FSTYPE, f_always_false)); + fprintf(stderr, "Warning: Unknown filesystem type %s\n", fsname); + new->flags |= F_MTUNKNOWN; + return new; } -#endif +#endif /* __NetBSD__ */ /* * -group gname functions -- @@ -677,30 +732,33 @@ f_group(plan, entry) PLAN *plan; FTSENT *entry; { - return (entry->fts_statp->st_gid == plan->g_data); + return entry->fts_statp->st_gid == plan->g_data; } PLAN * -c_group(gname) - char *gname; +c_group(option, argvp) + OPTION *option; + char ***argvp; { + char *gname; PLAN *new; struct group *g; gid_t gid; + gname = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; g = getgrnam(gname); if (g == NULL) { gid = atoi(gname); if (gid == 0 && gname[0] != '0') - errx(1, "-group: %s: no such group", gname); + errx(1, "%s: %s: no such group", option->name, gname); } else gid = g->gr_gid; - new = palloc(N_GROUP, f_group); + new = palloc(option); new->g_data = gid; - return (new); + return new; } /* @@ -717,16 +775,19 @@ f_inum(plan, entry) } PLAN * -c_inum(arg) - char *arg; +c_inum(option, argvp) + OPTION *option; + char ***argvp; { + char *inum_str; PLAN *new; + inum_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_INUM, f_inum); - new->i_data = find_parsenum(new, "-inum", arg, NULL); - return (new); + new = palloc(option); + new->i_data = find_parsenum(new, option->name, inum_str, NULL); + return new; } /* @@ -743,16 +804,19 @@ f_links(plan, entry) } PLAN * -c_links(arg) - char *arg; +c_links(option, argvp) + OPTION *option; + char ***argvp; { + char *nlinks; PLAN *new; + nlinks = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_LINKS, f_links); - new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); - return (new); + new = palloc(option); + new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); + return new; } /* @@ -766,143 +830,21 @@ f_ls(plan, entry) FTSENT *entry; { printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); - return (1); + return 1; } PLAN * -c_ls() +c_ls(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; isoutput = 1; - return (palloc(N_LS, f_ls)); + return palloc(option); } /* - * -maxdepth n functions -- - * - * Does the same as -prune if the level of the current file is greater - * than the specified maximum depth. - * - * Note that -maxdepth and -mindepth are handled specially in - * find_execute() so their f_* functions here do nothing. - */ -int -f_maxdepth(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (1); -} - -PLAN * -c_maxdepth(arg) - char *arg; -{ - PLAN *new; - - if (*arg == '-') - /* all other errors handled by find_parsenum() */ - errx(1, "-maxdepth: %s: value must be positive", arg); - - new = palloc(N_MAXDEPTH, f_maxdepth); - maxdepth = find_parsenum(new, "-maxdepth", arg, NULL); - return (new); -} - -/* - * -mindepth n functions -- - * - * True if the current file is at or deeper than the specified minimum - * depth. - */ -int -f_mindepth(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (1); -} - -PLAN * -c_mindepth(arg) - char *arg; -{ - PLAN *new; - - if (*arg == '-') - /* all other errors handled by find_parsenum() */ - errx(1, "-maxdepth: %s: value must be positive", arg); - - new = palloc(N_MINDEPTH, f_mindepth); - mindepth = find_parsenum(new, "-mindepth", arg, NULL); - return (new); -} - -/* - * -mtime n functions -- - * - * True if the difference between the file modification time and the - * current time is n 24 hour periods. - */ -int -f_mtime(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) / - 86400, plan->t_data); -} - -PLAN * -c_mtime(arg) - char *arg; -{ - PLAN *new; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MTIME, f_mtime); - new->t_data = find_parsenum(new, "-mtime", arg, NULL); - TIME_CORRECT(new, N_MTIME); - return (new); -} - -/* - * -mmin n functions -- - * - * True if the difference between the file modification time and the - * current time is n min periods. - */ -int -f_mmin(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / - 60, plan->t_data); -} - -PLAN * -c_mmin(arg) - char *arg; -{ - PLAN *new; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MMIN, f_mmin); - new->t_data = find_parsenum(new, "-mmin", arg, NULL); - TIME_CORRECT(new, N_MMIN); - return (new); -} - - -/* * -name functions -- * * True if the basename of the filename being examined @@ -913,145 +855,22 @@ f_name(plan, entry) PLAN *plan; FTSENT *entry; { - return (!fnmatch(plan->c_data, entry->fts_name, 0)); + return !fnmatch(plan->c_data, entry->fts_name, + plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); } PLAN * -c_name(pattern) - char *pattern; -{ - PLAN *new; - - new = palloc(N_NAME, f_name); - new->c_data = pattern; - return (new); -} - - -/* - * -iname functions -- - * - * Like -iname, but the match is case insensitive. - */ -int -f_iname(plan, entry) - PLAN *plan; - FTSENT *entry; +c_name(option, argvp) + OPTION *option; + char ***argvp; { - return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD)); -} - -PLAN * -c_iname(pattern) char *pattern; -{ PLAN *new; - new = palloc(N_INAME, f_iname); + pattern = nextarg(option, argvp); + new = palloc(option); new->c_data = pattern; - return (new); -} - - -/* - * -regex functions -- - * - * True if the whole path of the file matches pattern using - * regular expression. - */ -int -f_regex(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (do_f_regex(plan, entry, 0)); -} - -PLAN * -c_regex(pattern) - char *pattern; -{ - return (do_c_regex(pattern, 0)); -} - -/* - * -iregex functions -- - * - * Like -regex, but the match is case insensitive. - */ -int -f_iregex(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (do_f_regex(plan, entry, REG_ICASE)); -} - -PLAN * -c_iregex(pattern) - char *pattern; -{ - return (do_c_regex(pattern, REG_ICASE)); -} - -static int -do_f_regex(plan, entry, icase) - PLAN *plan; - FTSENT *entry; - int icase; -{ - char *str; - size_t len; - regex_t *pre; - regmatch_t pmatch; - int errcode; - char errbuf[LINE_MAX]; - int matched; - - pre = plan->re_data; - str = entry->fts_path; - len = strlen(str); - matched = 0; - - pmatch.rm_so = 0; - pmatch.rm_eo = len; - - errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); - - if (errcode != 0 && errcode != REG_NOMATCH) { - regerror(errcode, pre, errbuf, sizeof errbuf); - errx(1, "%s: %s", - icase == 0 ? "-regex" : "-iregex", errbuf); - } - - if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) - matched = 1; - - return (matched); -} - -PLAN * -do_c_regex(pattern, icase) - char *pattern; - int icase; -{ - PLAN *new; - regex_t *pre; - int errcode; - char errbuf[LINE_MAX]; - - if ((pre = malloc(sizeof(regex_t))) == NULL) - err(1, NULL); - - if ((errcode = regcomp(pre, pattern, regexp_flags | icase)) != 0) { - regerror(errcode, pre, errbuf, sizeof errbuf); - errx(1, "%s: %s: %s", - icase == 0 ? "-regex" : "-iregex", pattern, errbuf); - } - - new = icase == 0 ? palloc(N_REGEX, f_regex) : palloc(N_IREGEX, f_iregex); - new->re_data = pre; - return (new); + return new; } /* @@ -1066,23 +885,43 @@ f_newer(plan, entry) PLAN *plan; FTSENT *entry; { - return (entry->fts_statp->st_mtime > plan->t_data); + if (plan->flags & F_TIME_C) + return entry->fts_statp->st_ctime > plan->t_data; + else if (plan->flags & F_TIME_A) + return entry->fts_statp->st_atime > plan->t_data; + else + return entry->fts_statp->st_mtime > plan->t_data; } PLAN * -c_newer(filename) - char *filename; +c_newer(option, argvp) + OPTION *option; + char ***argvp; { + char *fn_or_tspec; PLAN *new; struct stat sb; + fn_or_tspec = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - if (stat(filename, &sb)) - err(1, "%s", filename); - new = palloc(N_NEWER, f_newer); - new->t_data = sb.st_mtime; - return (new); + new = palloc(option); + /* compare against what */ + if (option->flags & F_TIME2_T) { + new->t_data = get_date(fn_or_tspec, (struct timeb *) 0); + if (new->t_data == (time_t) -1) + errx(1, "Can't parse date/time: %s", fn_or_tspec); + } else { + if (stat(fn_or_tspec, &sb)) + err(1, "%s", fn_or_tspec); + if (option->flags & F_TIME2_C) + new->t_data = sb.st_ctime; + else if (option->flags & F_TIME2_A) + new->t_data = sb.st_atime; + else + new->t_data = sb.st_mtime; + } + return new; } /* @@ -1096,15 +935,17 @@ f_nogroup(plan, entry) PLAN *plan; FTSENT *entry; { - return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); + return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; } PLAN * -c_nogroup() +c_nogroup(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; - return (palloc(N_NOGROUP, f_nogroup)); + return palloc(option); } /* @@ -1118,15 +959,17 @@ f_nouser(plan, entry) PLAN *plan; FTSENT *entry; { - return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); + return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; } PLAN * -c_nouser() +c_nouser(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; - return (palloc(N_NOUSER, f_nouser)); + return palloc(option); } /* @@ -1140,43 +983,11 @@ f_path(plan, entry) PLAN *plan; FTSENT *entry; { - return (!fnmatch(plan->c_data, entry->fts_path, 0)); -} - -PLAN * -c_path(pattern) - char *pattern; -{ - PLAN *new; - - new = palloc(N_PATH, f_path); - new->c_data = pattern; - return (new); -} - -/* - * -ipath functions -- - * - * Like -path, but the match is case insensitive. - */ -int -f_ipath(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (!fnmatch(plan->c_data, entry->fts_path, FNM_CASEFOLD)); + return !fnmatch(plan->c_data, entry->fts_path, + plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); } -PLAN * -c_ipath(pattern) - char *pattern; -{ - PLAN *new; - - new = palloc(N_IPATH, f_ipath); - new->c_data = pattern; - return (new); -} +/* c_path is the same as c_name */ /* * -perm functions -- @@ -1194,90 +1005,40 @@ f_perm(plan, entry) mode = entry->fts_statp->st_mode & (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); - if (plan->flags == F_ATLEAST) - return ((plan->m_data | mode) == mode); - else if (plan->flags == F_ANY ) - return (plan->m_data & mode); + if (plan->flags & F_ATLEAST) + return (plan->m_data | mode) == mode; else - return (mode == plan->m_data); + return mode == plan->m_data; /* NOTREACHED */ } PLAN * -c_perm(perm) - char *perm; +c_perm(option, argvp) + OPTION *option; + char ***argvp; { + char *perm; PLAN *new; mode_t *set; + perm = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_PERM, f_perm); + new = palloc(option); if (*perm == '-') { - new->flags = F_ATLEAST; + new->flags |= F_ATLEAST; ++perm; } else if (*perm == '+') { - new->flags = F_ANY; + new->flags |= F_ANY; ++perm; } if ((set = setmode(perm)) == NULL) - errx(1, "-perm: %s: illegal mode string", perm); + errx(1, "%s: %s: illegal mode string", option->name, perm); new->m_data = getmode(set, 0); free(set); - return (new); -} - -/* - * -flags functions -- - * - * The flags argument is used to represent file flags bits. - */ -int -f_flags(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - u_long flags; - - flags = entry->fts_statp->st_flags & - (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | - SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND); - if (plan->flags == F_ATLEAST) - /* note that plan->fl_flags always is a subset of - plan->fl_mask */ - return (flags & plan->fl_mask) == plan->fl_flags; - else - return flags == plan->fl_flags; - /* NOTREACHED */ -} - -PLAN * -c_flags(flags_str) - char *flags_str; -{ - PLAN *new; - u_long flags, notflags; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_FLAGS, f_flags); - - if (*flags_str == '-') { - new->flags = F_ATLEAST; - flags_str++; - } - if (strtofflags(&flags_str, &flags, ¬flags) == 1) - errx(1, "-flags: %s: illegal flags string", flags_str); - - new->fl_flags = flags; - new->fl_mask = flags | notflags; -#if 0 - printf("flags = %08x, mask = %08x (%08x, %08x)\n", - new->fl_flags, new->fl_mask, flags, notflags); -#endif return new; } @@ -1293,15 +1054,17 @@ f_print(plan, entry) FTSENT *entry; { (void)puts(entry->fts_path); - return (1); + return 1; } PLAN * -c_print() +c_print(option, argvp) + OPTION *option; + char ***argvp; { isoutput = 1; - return (palloc(N_PRINT, f_print)); + return palloc(option); } /* @@ -1317,16 +1080,10 @@ f_print0(plan, entry) { fputs(entry->fts_path, stdout); fputc('\0', stdout); - return (1); + return 1; } -PLAN * -c_print0() -{ - isoutput = 1; - - return (palloc(N_PRINT0, f_print0)); -} +/* c_print0 is the same as c_print */ /* * -prune functions -- @@ -1342,13 +1099,90 @@ f_prune(plan, entry) if (fts_set(tree, entry, FTS_SKIP)) err(1, "%s", entry->fts_path); - return (1); + return 1; } +/* c_prune == c_simple */ + +/* + * -regex functions -- + * + * True if the whole path of the file matches pattern using + * regular expression. + */ +int +f_regex(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + char *str; + size_t len; + regex_t *pre; + regmatch_t pmatch; + int errcode; + char errbuf[LINE_MAX]; + int matched; + + pre = plan->re_data; + str = entry->fts_path; + len = strlen(str); + matched = 0; + + pmatch.rm_so = 0; + pmatch.rm_eo = len; + + errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); + + if (errcode != 0 && errcode != REG_NOMATCH) { + regerror(errcode, pre, errbuf, sizeof errbuf); + errx(1, "%s: %s", + plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); + } + + if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) + matched = 1; + + return matched; +} + +PLAN * +c_regex(option, argvp) + OPTION *option; + char ***argvp; +{ + PLAN *new; + char *pattern; + regex_t *pre; + int errcode; + char errbuf[LINE_MAX]; + + if ((pre = malloc(sizeof(regex_t))) == NULL) + err(1, NULL); + + pattern = nextarg(option, argvp); + + if ((errcode = regcomp(pre, pattern, + regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { + regerror(errcode, pre, errbuf, sizeof errbuf); + errx(1, "%s: %s: %s", + option->flags & F_IGNCASE ? "-iregex" : "-regex", + pattern, errbuf); + } + + new = palloc(option); + new->re_data = pre; + + return new; +} + +/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or */ + PLAN * -c_prune() +c_simple(option, argvp) + OPTION *option; + char ***argvp; { - return (palloc(N_PRUNE, f_prune)); + return palloc(option); } /* @@ -1374,20 +1208,23 @@ f_size(plan, entry) } PLAN * -c_size(arg) - char *arg; +c_size(option, argvp) + OPTION *option; + char ***argvp; { + char *size_str; PLAN *new; char endch; + size_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_SIZE, f_size); + new = palloc(option); endch = 'c'; - new->o_data = find_parsenum(new, "-size", arg, &endch); + new->o_data = find_parsenum(new, option->name, size_str, &endch); if (endch == 'c') divsize = 0; - return (new); + return new; } /* @@ -1402,16 +1239,19 @@ f_type(plan, entry) PLAN *plan; FTSENT *entry; { - return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); + return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; } PLAN * -c_type(typestring) - char *typestring; +c_type(option, argvp) + OPTION *option; + char ***argvp; { + char *typestring; PLAN *new; mode_t mask; + typestring = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; switch (typestring[0]) { @@ -1443,72 +1283,12 @@ c_type(typestring) break; #endif /* FTS_WHITEOUT */ default: - errx(1, "-type: %s: unknown type", typestring); + errx(1, "%s: %s: unknown type", option->name, typestring); } - new = palloc(N_TYPE, f_type); + new = palloc(option); new->m_data = mask; - return (new); -} - -/* - * -delete functions -- - * - * True always. Makes it's best shot and continues on regardless. - */ -int -f_delete(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - /* ignore these from fts */ - if (strcmp(entry->fts_accpath, ".") == 0 || - strcmp(entry->fts_accpath, "..") == 0) - return (1); - - /* sanity check */ - if (isdepth == 0 || /* depth off */ - (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ - !(ftsoptions & FTS_PHYSICAL) || /* physical off */ - (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ - errx(1, "-delete: insecure options got turned on"); - - /* Potentially unsafe - do not accept relative paths whatsoever */ - if (strchr(entry->fts_accpath, '/') != NULL) - errx(1, "-delete: %s: relative path potentially not safe", - entry->fts_accpath); - - /* Turn off user immutable bits if running as root */ - if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && - !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && - geteuid() == 0) - chflags(entry->fts_accpath, - entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); - - /* rmdir directories, unlink everything else */ - if (S_ISDIR(entry->fts_statp->st_mode)) { - if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) - warn("-delete: rmdir(%s)", entry->fts_path); - } else { - if (unlink(entry->fts_accpath) < 0) - warn("-delete: unlink(%s)", entry->fts_path); - } - - /* "succeed" */ - return (1); -} - -PLAN * -c_delete() -{ - - ftsoptions &= ~FTS_NOSTAT; /* no optimise */ - ftsoptions |= FTS_PHYSICAL; /* disable -follow */ - ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ - isoutput = 1; /* possible output */ - isdepth = 1; /* -depth implied */ - - return (palloc(N_DELETE, f_delete)); + return new; } /* @@ -1523,30 +1303,33 @@ f_user(plan, entry) PLAN *plan; FTSENT *entry; { - return (entry->fts_statp->st_uid == plan->u_data); + return entry->fts_statp->st_uid == plan->u_data; } PLAN * -c_user(username) - char *username; +c_user(option, argvp) + OPTION *option; + char ***argvp; { + char *username; PLAN *new; struct passwd *p; uid_t uid; + username = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; p = getpwnam(username); if (p == NULL) { uid = atoi(username); if (uid == 0 && username[0] != '0') - errx(1, "-user: %s: no such user", username); + errx(1, "%s: %s: no such user", option->name, username); } else uid = p->pw_uid; - new = palloc(N_USER, f_user); + new = palloc(option); new->u_data = uid; - return (new); + return new; } /* @@ -1556,11 +1339,13 @@ c_user(username) * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) */ PLAN * -c_xdev() +c_xdev(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions |= FTS_XDEV; - return (palloc(N_XDEV, f_always_true)); + return palloc(option); } /* @@ -1574,29 +1359,48 @@ f_expr(plan, entry) FTSENT *entry; { register PLAN *p; - register int state; + register int state = 0; - state = 0; for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (state); + p && (state = (p->execute)(p, entry)); p = p->next); + return state; } /* - * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are + * f_openparen and f_closeparen nodes are temporary place markers. They are * eliminated during phase 2 of find_formplan() --- the '(' node is converted - * to a N_EXPR node containing the expression and the ')' node is discarded. + * to a f_expr node containing the expression and the ')' node is discarded. + * The functions themselves are only used as constants. */ -PLAN * -c_openparen() + +int +f_openparen(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + abort(); +} + +int +f_closeparen(plan, entry) + PLAN *plan; + FTSENT *entry; { - return (palloc(N_OPENPAREN, (int (*)())-1)); + abort(); } +/* c_openparen == c_simple */ +/* c_closeparen == c_simple */ + +/* + * AND operator. Since AND is implicit, no node is allocated. + */ PLAN * -c_closeparen() +c_and(option, argvp) + OPTION *option; + char ***argvp; { - return (palloc(N_CLOSEPAREN, (int (*)())-1)); + return NULL; } /* @@ -1610,19 +1414,14 @@ f_not(plan, entry) FTSENT *entry; { register PLAN *p; - register int state; + register int state = 0; - state = 0; for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (!state); + p && (state = (p->execute)(p, entry)); p = p->next); + return !state; } -PLAN * -c_not() -{ - return (palloc(N_NOT, f_not)); -} +/* c_not == c_simple */ /* * expression -o expression functions -- @@ -1636,38 +1435,17 @@ f_or(plan, entry) FTSENT *entry; { register PLAN *p; - register int state; + register int state = 0; - state = 0; for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); + p && (state = (p->execute)(p, entry)); p = p->next); if (state) - return (1); + return 1; for (p = plan->p_data[1]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (state); + p && (state = (p->execute)(p, entry)); p = p->next); + return state; } -PLAN * -c_or() -{ - return (palloc(N_OR, f_or)); -} - -static PLAN * -palloc(t, f) - enum ntype t; - int (*f) __P((PLAN *, FTSENT *)); -{ - PLAN *new; - - if ((new = malloc(sizeof(PLAN))) == NULL) - err(1, NULL); - new->type = t; - new->eval = f; - new->flags = 0; - new->next = NULL; - return (new); -} +/* c_or == c_simple */ diff --git a/usr.bin/find/ls.c b/usr.bin/find/ls.c index cd138f5..d033973 100644 --- a/usr.bin/find/ls.c +++ b/usr.bin/find/ls.c @@ -65,7 +65,7 @@ printlong(name, accpath, sb) { char modep[15], *user_from_uid(), *group_from_gid(); - (void)printf("%6lu %4qd ", (u_long)sb->st_ino, sb->st_blocks); + (void)printf("%6lu %4qd ", (u_long) sb->st_ino, sb->st_blocks); (void)strmode(sb->st_mode, modep); (void)printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, UT_NAMESIZE, user_from_uid(sb->st_uid, 0), UT_NAMESIZE, diff --git a/usr.bin/find/operator.c b/usr.bin/find/operator.c index 0cb67cc..d7ac745 100644 --- a/usr.bin/find/operator.c +++ b/usr.bin/find/operator.c @@ -72,7 +72,7 @@ yanknode(planp) * yankexpr -- * Removes one expression from the plan. This is used mainly by * paren_squish. In comments below, an expression is either a - * simple node or a N_EXPR node containing a list of simple nodes. + * simple node or a f_expr node containing a list of simple nodes. */ static PLAN * yankexpr(planp) @@ -82,7 +82,6 @@ yankexpr(planp) PLAN *node; /* pointer to returned node or expression */ PLAN *tail; /* pointer to tail of subplan */ PLAN *subplan; /* pointer to head of ( ) expression */ - int f_expr(); /* first pull the top node from the plan */ if ((node = yanknode(planp)) == NULL) @@ -94,23 +93,22 @@ yankexpr(planp) * just return it and unwind our recursion; all other nodes are * complete expressions, so just return them. */ - if (node->type == N_OPENPAREN) + if (node->execute == f_openparen) for (tail = subplan = NULL;;) { if ((next = yankexpr(planp)) == NULL) err(1, "(: missing closing ')'"); /* * If we find a closing ')' we store the collected * subplan in our '(' node and convert the node to - * a N_EXPR. The ')' we found is ignored. Otherwise, + * a f_expr. The ')' we found is ignored. Otherwise, * we just continue to add whatever we get to our * subplan. */ - if (next->type == N_CLOSEPAREN) { + if (next->execute == f_closeparen) { if (subplan == NULL) errx(1, "(): empty inner expression"); node->p_data[0] = subplan; - node->type = N_EXPR; - node->eval = f_expr; + node->execute = f_expr; break; } else { if (subplan == NULL) @@ -141,14 +139,14 @@ paren_squish(plan) /* * the basic idea is to have yankexpr do all our work and just - * collect it's results together. + * collect its results together. */ while ((expr = yankexpr(&plan)) != NULL) { /* * if we find an unclaimed ')' it means there is a missing * '(' someplace. */ - if (expr->type == N_CLOSEPAREN) + if (expr->execute == f_closeparen) errx(1, "): no beginning '('"); /* add the expression to our result plan */ @@ -172,18 +170,18 @@ not_squish(plan) PLAN *plan; /* plan to process */ { register PLAN *next; /* next node being processed */ - register PLAN *node; /* temporary node used in N_NOT processing */ + register PLAN *node; /* temporary node used in f_not processing */ register PLAN *tail; /* pointer to tail of result plan */ PLAN *result; /* pointer to head of result plan */ - tail = result = next = NULL; + tail = result = NULL; - while ((next = yanknode(&plan)) != NULL) { + while (next = yanknode(&plan)) { /* * if we encounter a ( expression ) then look for nots in * the expr subplan. */ - if (next->type == N_EXPR) + if (next->execute == f_expr) next->p_data[0] = not_squish(next->p_data[0]); /* @@ -191,23 +189,23 @@ not_squish(plan) * it in the not's subplan. As an optimization we compress * several not's to zero or one not. */ - if (next->type == N_NOT) { + if (next->execute == f_not) { int notlevel = 1; node = yanknode(&plan); - while (node != NULL && node->type == N_NOT) { + while (node != NULL && node->execute == f_not) { ++notlevel; node = yanknode(&plan); } if (node == NULL) errx(1, "!: no following expression"); - if (node->type == N_OR) + if (node->execute == f_or) errx(1, "!: nothing between ! and -o"); /* * If we encounter ! ( expr ) then look for nots in * the expr subplan. */ - if (node->type == N_EXPR) + if (node->execute == f_expr) node->p_data[0] = not_squish(node->p_data[0]); if (notlevel % 2 != 1) next = node; @@ -246,11 +244,11 @@ or_squish(plan) * if we encounter a ( expression ) then look for or's in * the expr subplan. */ - if (next->type == N_EXPR) + if (next->execute == f_expr) next->p_data[0] = or_squish(next->p_data[0]); /* if we encounter a not then look for or's in the subplan */ - if (next->type == N_NOT) + if (next->execute == f_not) next->p_data[0] = or_squish(next->p_data[0]); /* @@ -258,7 +256,7 @@ or_squish(plan) * or's first subplan and then recursively collect the * remaining stuff into the second subplan and return the or. */ - if (next->type == N_OR) { + if (next->execute == f_or) { if (result == NULL) errx(1, "-o: no expression before -o"); next->p_data[0] = result; diff --git a/usr.bin/find/option.c b/usr.bin/find/option.c index b5911f2..0a37320 100644 --- a/usr.bin/find/option.c +++ b/usr.bin/find/option.c @@ -54,62 +54,75 @@ static const char rcsid[] = #include "find.h" -static OPTION *option __P((char *)); - /* NB: the following table must be sorted lexically. */ static OPTION const options[] = { - { "!", N_NOT, c_not, O_ZERO }, - { "(", N_OPENPAREN, c_openparen, O_ZERO }, - { ")", N_CLOSEPAREN, c_closeparen, O_ZERO }, - { "-a", N_AND, NULL, O_NONE }, - { "-amin", N_AMIN, c_amin, O_ARGV }, - { "-and", N_AND, NULL, O_NONE }, - { "-atime", N_ATIME, c_atime, O_ARGV }, - { "-cmin", N_CMIN, c_cmin, O_ARGV }, - { "-ctime", N_CTIME, c_ctime, O_ARGV }, - { "-delete", N_DELETE, c_delete, O_ZERO }, - { "-depth", N_DEPTH, c_depth, O_ZERO }, - { "-empty", N_EMPTY, c_empty, O_ZERO }, - { "-exec", N_EXEC, c_exec, O_ARGVP }, - { "-execdir", N_EXECDIR, c_execdir, O_ARGVP }, - { "-flags", N_FLAGS, c_flags, O_ARGV }, - { "-follow", N_FOLLOW, c_follow, O_ZERO }, - + { "!", c_simple, f_not, 0 }, + { "(", c_simple, f_openparen, 0 }, + { ")", c_simple, f_closeparen, 0 }, + { "-a", c_and, NULL, 0 }, + { "-amin", c_Xmin, f_Xmin, F_TIME_A }, + { "-and", c_and, NULL, 0 }, + { "-anewer", c_newer, f_newer, F_TIME_A }, + { "-atime", c_Xtime, f_Xtime, F_TIME_A }, + { "-cmin", c_Xmin, f_Xmin, F_TIME_C }, + { "-cnewer", c_newer, f_newer, F_TIME_C }, + { "-ctime", c_Xtime, f_Xtime, F_TIME_C }, + { "-delete", c_delete, f_delete, 0 }, + { "-depth", c_depth, f_always_true, 0 }, + { "-empty", c_empty, f_empty, 0 }, + { "-exec", c_exec, f_exec, 0 }, + { "-execdir", c_exec, f_exec, F_EXECDIR }, + { "-flags", c_flags, f_flags, 0 }, + { "-follow", c_follow, f_always_true, 0 }, /* * NetBSD doesn't provide a getvfsbyname(), so this option * is not available if using a NetBSD kernel. */ #if !defined(__NetBSD__) - { "-fstype", N_FSTYPE, c_fstype, O_ARGV }, + { "-fstype", c_fstype, f_fstype, 0 }, #endif - { "-group", N_GROUP, c_group, O_ARGV }, - { "-iname", N_INAME, c_iname, O_ARGV }, - { "-inum", N_INUM, c_inum, O_ARGV }, - { "-ipath", N_IPATH, c_ipath, O_ARGV }, - { "-iregex", N_IREGEX, c_iregex, O_ARGV }, - { "-links", N_LINKS, c_links, O_ARGV }, - { "-ls", N_LS, c_ls, O_ZERO }, - { "-maxdepth", N_MAXDEPTH, c_maxdepth, O_ARGV }, - { "-mindepth", N_MINDEPTH, c_mindepth, O_ARGV }, - { "-mmin", N_MMIN, c_mmin, O_ARGV }, - { "-mtime", N_MTIME, c_mtime, O_ARGV }, - { "-name", N_NAME, c_name, O_ARGV }, - { "-newer", N_NEWER, c_newer, O_ARGV }, - { "-nogroup", N_NOGROUP, c_nogroup, O_ZERO }, - { "-nouser", N_NOUSER, c_nouser, O_ZERO }, - { "-o", N_OR, c_or, O_ZERO }, - { "-ok", N_OK, c_exec, O_ARGVP }, - { "-or", N_OR, c_or, O_ZERO }, - { "-path", N_PATH, c_path, O_ARGV }, - { "-perm", N_PERM, c_perm, O_ARGV }, - { "-print", N_PRINT, c_print, O_ZERO }, - { "-print0", N_PRINT0, c_print0, O_ZERO }, - { "-prune", N_PRUNE, c_prune, O_ZERO }, - { "-regex", N_REGEX, c_regex, O_ARGV }, - { "-size", N_SIZE, c_size, O_ARGV }, - { "-type", N_TYPE, c_type, O_ARGV }, - { "-user", N_USER, c_user, O_ARGV }, - { "-xdev", N_XDEV, c_xdev, O_ZERO }, + { "-group", c_group, f_group, 0 }, + { "-iname", c_name, f_name, F_IGNCASE }, + { "-inum", c_inum, f_inum, 0 }, + { "-ipath", c_name, f_path, F_IGNCASE }, + { "-iregex", c_regex, f_regex, F_IGNCASE }, + { "-links", c_links, f_links, 0 }, + { "-ls", c_ls, f_ls, 0 }, + { "-maxdepth", c_mXXdepth, f_always_true, F_MAXDEPTH }, + { "-mindepth", c_mXXdepth, f_always_true, 0 }, + { "-mmin", c_Xmin, f_Xmin, 0 }, + { "-mnewer", c_newer, f_newer, 0 }, + { "-mtime", c_Xtime, f_Xtime, 0 }, + { "-name", c_name, f_name, 0 }, + { "-newer", c_newer, f_newer, 0 }, + { "-neweraa", c_newer, f_newer, F_TIME_A | F_TIME2_A }, + { "-newerac", c_newer, f_newer, F_TIME_A | F_TIME2_C }, + { "-neweram", c_newer, f_newer, F_TIME_A }, + { "-newerat", c_newer, f_newer, F_TIME_A | F_TIME2_T }, + { "-newerca", c_newer, f_newer, F_TIME_C | F_TIME2_A }, + { "-newercc", c_newer, f_newer, F_TIME_C | F_TIME2_C }, + { "-newercm", c_newer, f_newer, F_TIME_C }, + { "-newerct", c_newer, f_newer, F_TIME_C | F_TIME2_T }, + { "-newerma", c_newer, f_newer, F_TIME2_A }, + { "-newermc", c_newer, f_newer, F_TIME2_C }, + { "-newermm", c_newer, f_newer, 0 }, + { "-newermt", c_newer, f_newer, F_TIME2_T }, + { "-nogroup", c_nogroup, f_nogroup, 0 }, + { "-nouser", c_nouser, f_nouser, 0 }, + { "-o", c_simple, f_or, 0 }, + { "-ok", c_exec, f_exec, F_NEEDOK }, + { "-okdir", c_exec, f_exec, F_NEEDOK | F_EXECDIR }, + { "-or", c_simple, f_or, 0 }, + { "-path", c_name, f_path, 0 }, + { "-perm", c_perm, f_perm, 0 }, + { "-print", c_print, f_print, 0 }, + { "-print0", c_print, f_print0, 0 }, + { "-prune", c_simple, f_prune, 0 }, + { "-regex", c_regex, f_regex, 0 }, + { "-size", c_size, f_size, 0 }, + { "-type", c_type, f_type, 0 }, + { "-user", c_user, f_user, 0 }, + { "-xdev", c_xdev, f_always_true, 0 }, }; /* @@ -133,30 +146,13 @@ find_create(argvp) if ((p = option(*argv)) == NULL) errx(1, "%s: unknown option", *argv); ++argv; - if (p->flags & (O_ARGV|O_ARGVP) && !*argv) - errx(1, "%s: requires additional arguments", *--argv); - switch(p->flags) { - case O_NONE: - new = NULL; - break; - case O_ZERO: - new = (p->create)(); - break; - case O_ARGV: - new = (p->create)(*argv++); - break; - case O_ARGVP: - new = (p->create)(&argv, p->token == N_OK); - break; - default: - abort(); - } + new = (p->create)(p, &argv); *argvp = argv; return (new); } -static OPTION * +OPTION * option(name) char *name; { |