]> Zhao Yanbai Git Server - minix.git/commitdiff
commands/find: original netbsd find
authorBen Gras <ben@minix3.org>
Wed, 9 Jun 2010 12:19:38 +0000 (12:19 +0000)
committerBen Gras <ben@minix3.org>
Wed, 9 Jun 2010 12:19:38 +0000 (12:19 +0000)
commands/find/Makefile [new file with mode: 0644]
commands/find/extern.h [new file with mode: 0644]
commands/find/find.1 [new file with mode: 0644]
commands/find/find.c [new file with mode: 0644]
commands/find/find.h [new file with mode: 0644]
commands/find/function.c [new file with mode: 0644]
commands/find/ls.c [new file with mode: 0644]
commands/find/main.c [new file with mode: 0644]
commands/find/misc.c [new file with mode: 0644]
commands/find/operator.c [new file with mode: 0644]
commands/find/option.c [new file with mode: 0644]

diff --git a/commands/find/Makefile b/commands/find/Makefile
new file mode 100644 (file)
index 0000000..0eee921
--- /dev/null
@@ -0,0 +1,13 @@
+#      $NetBSD: Makefile,v 1.12 2006/12/14 20:55:56 he Exp $
+#      from: @(#)Makefile      8.1 (Berkeley) 6/6/93
+
+.include <bsd.own.mk>
+
+PROG=  find
+SRCS=  find.c function.c ls.c main.c misc.c operator.c option.c
+WARNS= 4
+
+LDADD+=-lutil
+DPADD+=${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/commands/find/extern.h b/commands/find/extern.h
new file mode 100644 (file)
index 0000000..7c8c4ad
--- /dev/null
@@ -0,0 +1,99 @@
+/*     $NetBSD: extern.h,v 1.28 2007/07/19 07:49:30 daniel Exp $       */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     from: @(#)extern.h      8.3 (Berkeley) 4/16/94
+ */
+
+#include <sys/cdefs.h>
+
+void    brace_subst(char *, char **, char *, int *);
+PLAN   *find_create(char ***);
+int     find_execute(PLAN *, char **);
+PLAN   *find_formplan(char **);
+int     find_traverse(PLAN *, int (*)(PLAN *, void *), void *);
+int     f_expr(PLAN *, FTSENT *);
+PLAN   *not_squish(PLAN *);
+PLAN   *or_squish(PLAN *);
+PLAN   *paren_squish(PLAN *);
+int     plan_cleanup(PLAN *, void *);
+void    printlong(char *, char *, struct stat *);
+int     queryuser(char **);
+void    show_path(int);
+
+PLAN   *c_amin(char ***, int);
+PLAN   *c_anewer(char ***, int);
+PLAN   *c_atime(char ***, int);
+PLAN   *c_cmin(char ***, int);
+PLAN   *c_cnewer(char ***, int);
+PLAN   *c_ctime(char ***, int);
+PLAN   *c_delete(char ***, int);
+PLAN   *c_depth(char ***, int);
+PLAN   *c_empty(char ***, int);
+PLAN   *c_exec(char ***, int);
+PLAN   *c_execdir(char ***, int);
+PLAN   *c_exit(char ***, int);
+PLAN   *c_false(char ***, int);
+PLAN   *c_flags(char ***, int);
+PLAN   *c_follow(char ***, int);
+PLAN   *c_fprint(char ***, int);
+PLAN   *c_fstype(char ***, int);
+PLAN   *c_group(char ***, int);
+PLAN   *c_iname(char ***, int);
+PLAN   *c_inum(char ***, int);
+PLAN   *c_iregex(char ***, int);
+PLAN   *c_links(char ***, int);
+PLAN   *c_ls(char ***, int);
+PLAN   *c_maxdepth(char ***, int);
+PLAN   *c_mindepth(char ***, int);
+PLAN   *c_mmin(char ***, int);
+PLAN   *c_mtime(char ***, int);
+PLAN   *c_name(char ***, int);
+PLAN   *c_newer(char ***, int);
+PLAN   *c_nogroup(char ***, int);
+PLAN   *c_nouser(char ***, int);
+PLAN   *c_path(char ***, int);
+PLAN   *c_perm(char ***, int);
+PLAN   *c_print(char ***, int);
+PLAN   *c_print0(char ***, int);
+PLAN   *c_printx(char ***, int);
+PLAN   *c_prune(char ***, int);
+PLAN   *c_regex(char ***, int);
+PLAN   *c_size(char ***, int);
+PLAN   *c_type(char ***, int);
+PLAN   *c_user(char ***, int);
+PLAN   *c_xdev(char ***, int);
+PLAN   *c_openparen(char ***, int);
+PLAN   *c_closeparen(char ***, int);
+PLAN   *c_not(char ***, int);
+PLAN   *c_or(char ***, int);
+PLAN   *c_null(char ***, int);
+
+extern int ftsoptions, isdeprecated, isdepth, isoutput, issort, isxargs,
+       regcomp_flags;
diff --git a/commands/find/find.1 b/commands/find/find.1
new file mode 100644 (file)
index 0000000..ab0be0a
--- /dev/null
@@ -0,0 +1,839 @@
+.\"    $NetBSD: find.1,v 1.66 2007/07/19 07:49:30 daniel Exp $
+.\"
+.\" Copyright (c) 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    from: @(#)find.1        8.7 (Berkeley) 5/9/95
+.\"
+.Dd July 19, 2007
+.Dt FIND 1
+.Os
+.Sh NAME
+.Nm find
+.Nd walk a file hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl H | Fl L | Fl P
+.Op Fl dEhsXx
+.Ar file
+.Op Ar file ...
+.Op Ar expression
+.Nm
+.Op Fl H | Fl L | Fl P
+.Op Fl dEhsXx
+.Fl f Ar file
+.Op Ar file ...
+.Op Ar expression
+.Sh DESCRIPTION
+.Nm
+recursively descends the directory tree for each
+.Ar file
+listed, evaluating an
+.Ar expression
+(composed of the
+.Dq primaries
+and
+.Dq operands
+listed below) in terms
+of each file in the tree.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width Ds
+.It Fl H
+The
+.Fl H
+option causes the file information and file type (see
+.Xr stat 2 ) ,
+returned for each symbolic link encountered on the command line to be
+those of the file referenced by the link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself.
+File information of all symbolic links not on the command line is that
+of the link itself.
+.It Fl L
+The
+.Fl L
+option causes the file information and file type (see
+.Xr stat 2 )
+returned for each symbolic link to be those of the file referenced by the
+link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself.
+.It Fl P
+The
+.Fl P
+option causes the file information and file type (see
+.Xr stat 2 )
+returned for each symbolic link to be those of the link itself.
+.It Fl d
+The
+.Fl d
+option causes
+.Nm
+to perform a depth-first traversal, i.e., directories
+are visited in post-order and all entries in a directory will be acted
+on before the directory itself.
+By default,
+.Nm
+visits directories in pre-order, i.e., before their contents.
+Note, the default is
+.Ar not
+a breadth-first traversal.
+.It Fl E
+The
+.Fl E
+option causes
+.Ar regexp
+arguments to primaries to be interpreted as extended regular
+expressions (see
+.Xr re_format 7 ) .
+.It Fl f
+The
+.Fl f
+option specifies a file hierarchy for
+.Nm
+to traverse.
+File hierarchies may also be specified as the operands immediately
+following the options.
+.It Fl h
+The
+.Fl h
+option causes the file information and file type (see
+.Xr stat  2  ) ,
+returned for each symbolic link to be those of the file referenced by the
+link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself.
+.It Fl s
+The
+.Fl s
+option causes the entries of each directory to be sorted in
+lexicographical order.
+Note that the sorting is done only inside of each directory;
+files in different directories are not sorted.
+Therefore,
+.Sq Li a/b
+appears before
+.Sq Li a.b ,
+which is different from
+.Dq Li "find ... \&| sort"
+order.
+.It Fl X
+The
+.Fl X
+option is a modification to permit
+.Nm
+to be safely used in conjunction with
+.Xr xargs 1 .
+If a file name contains any of the delimiting characters used by
+.Nm xargs ,
+a diagnostic message is displayed on standard error, and the file
+is skipped.
+The delimiting characters include single
+.Pq Dq \&'
+and double
+.Pq Dq \&"
+quotes, backslash
+.Pq Dq \e ,
+space, tab and newline characters.
+Alternatively, the
+.Ic -print0
+or
+.Ic -printx
+primaries can be used to format the output in a way that
+.Nm xargs
+can accept.
+.It Fl x
+The
+.Fl x
+option restricts the search to the file system containing the
+directory specified.
+Does not list mount points to other file systems.
+.El
+.Sh PRIMARIES
+.Bl -tag -width Ds
+.It Ic -amin Ar n
+True if the difference between the file last access time and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -anewer Ar file
+True if the current file has a more recent last access time than
+.Ar file  .
+.It Ic -atime Ar n
+True if the difference between the file last access time and the time
+.Nm
+was started, rounded up to the next full 24-hour period, is
+.Ar n
+24-hour periods.
+.It Ic -cmin Ar n
+True if the difference between the time of last change of file status
+information and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -cnewer Ar file
+True if the current file has a more recent last change time than
+.Ar file  .
+.It Ic -ctime Ar n
+True if the difference between the time of last change of file status
+information and the time
+.Nm
+was started, rounded up to the next full 24-hour period, is
+.Ar n
+24-hour periods.
+.It Ic -delete
+Delete found files and/or directories.
+Always returns True.
+This executes from the current working directory as
+.Nm
+recurses down the tree.
+It will not attempt to delete a filename with a
+.Dq \/
+character in its pathname relative to
+.Dq \.
+for security reasons.
+Depth-first traversal processing is implied by this option.
+This can also be invoked as
+.Ic -rm .
+.It Ic -empty
+True if the current file or directory is empty.
+.\" The ".sp" below is probably not the right way to get the desired effect.
+.It Ic -exec Ar utility Oo argument ... Oc No ;
+.sp -1l
+.It Ic -exec Ar utility Oo argument ... Oc No {} +
+Execute the specified
+.Ar utility
+with the specified arguments.
+The list of arguments is terminated by
+.Dq Li \&;
+or
+.Dq Li \&+ .
+.Ar utility
+will be executed from the directory from which
+.Nm
+was executed.
+.Pp
+If terminated by a semicolon
+.Pq Dq \&; ,
+the
+.Ar utility
+is invoked once per path.
+If the string
+.Dq {}
+appears anywhere in the utility name or the arguments,
+it is replaced by the pathname of the current file.
+.Pp
+If terminated by a plus sign
+.Pq Dq \&+ ,
+the pathnames for which the
+primary is evaluated are aggregated into sets, and
+.Ar utility
+will be invoked once per set, similar to
+.Xr xargs 1 .
+If any invocation exits with non-zero exit status, then
+.Nm
+will eventually do so as well, but this does not cause
+.Nm
+to exit early.
+The string
+.Dq {}
+must appear, and must appear last.
+Each set is limitted to no more than 5,000 pathnames,
+and is also limitted such that the invokation of
+.Ar utility
+does not exceed
+.Dv ARG_MAX .
+.It Ic -execdir Ar utility Oo argument ... Oc No ;
+The
+.Ic -execdir
+primary is similar to the semicolon-terminated
+.Pq Dq \&;
+variant of the
+.Ic -exec
+primary, with the exception that
+.Ar utility
+will be executed from the directory that holds
+the current file.
+The filename substituted for the string
+.Dq {}
+is not qualified.
+Set aggregation
+.Pq Do \&+ Dc termination
+is not supported.
+.It Ic -exit Op Ar n
+This primary causes
+.Nm
+to stop traversing the filesystem and exit immediately if a
+previous condition was met.
+If no value is specified, the exit value will be 0, else
+.Ar n .
+Note that other primaries will be evaluated and acted upon before exiting.
+.It Ic -false
+This primary always evaluates to false.
+This can be used following a primary that caused the
+expression to be true to make the expression to be false.
+This can be useful after using a
+.Ic -fprint
+primary so it can continue to the next expression (using an
+.Cm -or
+operator, for example).
+.It Ic -flags Oo Fl Oc Ns Ar flags
+If
+.Ar flags
+are preceded by a dash
+.Pq Dq - ,
+this primary evaluates to true
+if at least all of the bits in
+.Ar flags
+are set in the file's flags bits.
+If
+.Ar flags
+are not preceded by a dash, this primary evaluates to true if
+the bits in
+.Ar flags
+exactly match the file's flags bits.
+If
+.Ar flags
+is
+.Dq none ,
+files with no flags bits set are matched.
+(See
+.Xr chflags 1
+for more information about file flags.)
+.It Ic -follow
+Follow symbolic links.
+.It Ic -fprint Ar filename
+This primary always evaluates to true.
+This creates
+.Ar filename
+or overwrites the file if it already exists.
+The file is created at startup.
+It writes the pathname of the current file to this file, followed
+by a newline character.
+The file will be empty if no files are matched.
+.It Ic -fstype Ar type
+True if the file is contained in a file system of type
+.Ar type .
+The
+.Xr sysctl 8
+command can be used to find out the types of filesystems
+that are available on the system:
+.Bd -literal -offset indent
+sysctl vfs.generic.fstypes
+.Ed
+.Pp
+In addition, there are two pseudo-types,
+.Dq local
+and
+.Dq rdonly .
+The former matches any file system physically mounted on the system where
+the
+.Nm
+is being executed, and the latter matches any file system which is
+mounted read-only.
+.It Ic -group Ar gname
+True if the file belongs to the group
+.Ar gname  .
+If
+.Ar gname
+is numeric and there is no such group name, then
+.Ar gname
+is treated as a group id.
+.It Ic -iname Ar pattern
+True if the last component of the pathname being examined
+matches
+.Ar pattern .
+Case insensitive.
+.It Ic -inum Ar n
+True if the file has inode number
+.Ar n  .
+.It Ic -iregex Ar regexp
+True if the path name of the current file matches the case-insensitive
+basic regular expression
+.Pq see Xr re_format 7
+.Ar regexp .
+This is a match on the whole path, not a search for the regular expression
+within the path.
+.It Ic -links Ar n
+True if the file has
+.Ar n
+links.
+.It Ic -rm
+This is an alias for
+.Ic -delete .
+.It Ic -ls
+This primary always evaluates to true.
+The following information for the current file is written to standard output:
+its inode number, size in 512-byte blocks, file permissions, number of hard
+links, owner, group, size in bytes, last modification time, and pathname.
+If the file is a block or character special file, the major and minor numbers
+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
+.Dq -\*[Gt] .
+The format is identical to that produced by
+.Dq ls -dgils .
+.It Ic -maxdepth Ar n
+True if the current search depth is less than or equal to what is specified in
+.Ar n .
+.It Ic -mindepth Ar n
+True if the current search depth is at least what is specified in
+.Ar n .
+.It Ic -mmin Ar n
+True if the difference between the file last modification time and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -mtime Ar n
+True if the difference between the file last modification time and the time
+.Nm
+was started, rounded up to the next full 24-hour period, is
+.Ar n
+24-hour periods.
+.It Ic -ok Ar utility Oo argument ... Oc No ;
+The
+.Ic -ok
+primary is similar to the semicolon-terminated
+.Pq Dq \&;
+variant of the
+.Ic -exec
+primary, with the exception that
+.Nm
+requests user affirmation for the execution of the utility by printing
+a message to the terminal and reading a response.
+If the response is other than
+.Dq y ,
+the command is not executed and the
+.Ar -ok
+primary evaluates to false.
+Set aggregation
+.Pq Do \&+ Dc termination
+is not supported.
+.It Ic -name Ar pattern
+True if the last component of the pathname being examined matches
+.Ar pattern  .
+Special shell pattern matching characters
+.Po
+.Dq \&[ ,
+.Dq \&] ,
+.Dq \&* ,
+.Dq \&?
+.Pc
+may be used as part of
+.Ar pattern  .
+These characters may be matched explicitly by escaping them with a
+backslash
+.Pq Dq \e .
+.It Ic -newer Ar file
+True if the current file has a more recent last modification time than
+.Ar file  .
+.It Ic -nouser
+True if the file belongs to an unknown user.
+.It Ic -nogroup
+True if the file belongs to an unknown group.
+.It Ic -path Ar pattern
+True if the pathname being examined matches
+.Ar pattern  .
+Special shell pattern matching characters
+.Po
+.Dq \&[ ,
+.Dq \&] ,
+.Dq \&* ,
+and
+.Dq \&?
+.Pc
+may be used as part of
+.Ar pattern  .
+These characters may be matched explicitly by escaping them with a
+backslash
+.Pq Dq \e .
+Slashes
+.Pq Dq /
+are treated as normal characters and do not have to be
+matched explicitly.
+.It Ic -perm Oo Fl Oc Ns Ar mode
+The
+.Ar mode
+may be either symbolic (see
+.Xr chmod  1  )
+or an octal number.
+If the mode is symbolic, a starting value of zero is assumed and the
+mode sets or clears permissions without regard to the process' file mode
+creation mask.
+If the mode is octal, only bits 07777
+.Pf ( Dv S_ISUID
+|
+.Dv S_ISGID
+|
+.Dv S_ISTXT
+|
+.Dv S_IRWXU
+|
+.Dv S_IRWXG
+|
+.Dv S_IRWXO )
+of the file's mode bits participate
+in the comparison.
+If the mode is preceded by a dash
+.Pq Dq - ,
+this primary evaluates to true
+if at least all of the bits in the mode are set in the file's mode bits.
+If the mode is not preceded by a dash, this primary evaluates to true if
+the bits in the mode exactly match the file's mode bits.
+Note, the first character of a symbolic mode may not be a dash
+.Pq Dq - .
+.It Ic -print
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output, followed
+by a newline character.
+If none of
+.Ic -exec ,
+.Ic -exit ,
+.Ic -fprint ,
+.Ic -ls ,
+.Ic -ok ,
+.Ic -print0 ,
+nor
+.Ic -printx
+is specified, the given expression shall be effectively replaced by
+.Cm \&( Ns Ar given\& expression Ns Cm \&)
+.Ic -print .
+.It Ic -print0
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output, followed
+by a null character.
+.It Ic -printx
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output,
+with each space, tab, newline, backslash, dollar sign, and single,
+double, or back quotation mark prefixed by a backslash, so the output of
+.Nm find
+can safely be used as input to
+.Nm xargs .
+.It Ic -prune
+This primary always evaluates to true.
+It causes
+.Nm
+to not descend into the current file.
+Note, the
+.Ic -prune
+primary has no effect if the
+.Fl d
+option was specified.
+.It Ic -regex Ar regexp
+True if the path name of the current file matches the case-sensitive
+basic regular expression
+.Pq see Xr re_format 7
+.Ar regexp .
+This is a match on the whole path, not a search for the regular expression
+within the path.
+.It Ic -size Ar n Ns Op Cm c
+True if the file's size, rounded up, in 512-byte blocks is
+.Ar n  .
+If
+.Ar n
+is followed by a
+.Dq c ,
+then the primary is true if the file's size is
+.Ar n
+bytes.
+.It Ic -type Ar t
+True if the file is of the specified type.
+Possible file types are as follows:
+.Pp
+.Bl -tag -width flag -offset indent -compact
+.It Cm b
+block special
+.It Cm c
+character special
+.It Cm d
+directory
+.It Cm f
+regular file
+.It Cm l
+symbolic link
+.It Cm p
+FIFO
+.It Cm s
+socket
+.It Cm W
+whiteout
+.It Cm w
+whiteout
+.El
+.Pp
+.It Ic -user Ar uname
+True if the file belongs to the user
+.Ar uname  .
+If
+.Ar uname
+is numeric and there is no such user name, then
+.Ar uname
+is treated as a user id (and considered a numeric argument).
+.It Ic -xdev
+This primary always evaluates to true.
+It causes find not to descend past directories that have a different
+device ID (st_dev, see
+.Xr stat 2
+S5.6.2 [POSIX.1]).
+.El
+.Pp
+All primaries which take a numeric argument allow the number to be
+preceded by a plus sign
+.Pq Dq +
+or a minus sign
+.Pq Dq \- .
+A preceding plus sign means
+.Dq more than n ,
+a preceding minus sign means
+.Dq less than n ,
+and neither means
+.Dq exactly n .
+.Sh OPERATORS
+The primaries may be combined using the following operators.
+The operators are listed in order of decreasing precedence.
+.Bl -tag -width (expression)
+.It Cm \&( Ar expression Cm \&)
+This evaluates to true if the parenthesized expression evaluates to
+true.
+.Pp
+.It Cm \&! Ar expression
+This is the unary
+.Tn NOT
+operator.
+It evaluates to true if the expression is false.
+.Pp
+.It Ar expression Cm -and Ar expression
+.It Ar expression expression
+The
+.Cm -and
+operator is the logical
+.Tn AND
+operator.
+As it is implied by the juxtaposition of two expressions it does not
+have to be specified.
+The expression evaluates to true if both expressions are true.
+The second expression is not evaluated if the first expression is false.
+.Pp
+.It Ar expression Cm -or Ar expression
+The
+.Cm -or
+operator is the logical
+.Tn OR
+operator.
+The expression evaluates to true if either the first or the second expression
+is true.
+The second expression is not evaluated if the first expression is true.
+.El
+.Pp
+All operands and primaries must be separate arguments to
+.Nm  .
+Primaries which themselves take arguments expect each argument
+to be a separate argument to
+.Nm  .
+.Sh EXIT STATUS
+The
+.Nm
+utility normally exits 0 on success, and exits with 1 under certain
+internal error conditions.
+If any invokations of
+.Dq Ic -exec Ar ... No +
+primaries return non-zero exit-status, then
+.Nm
+will do so as well.
+.Sh EXAMPLES
+The following examples are shown as given to the shell:
+.Bl -tag -width findx
+.It Li "find  /  \e!  -name  \*q*.c\*q  -print"
+Print out a list of all the files whose names do not end in
+.Dq \&.c .
+.It Li "find  /  -newer  ttt  -user  wnj  -print"
+Print out a list of all the files owned by user
+.Dq wnj
+that are newer than the file
+.Dq ttt .
+.It Li "find  /  \e!  \e(  -newer  ttt  -user  wnj  \e)  -print"
+Print out a list of all the files which are not both newer than
+.Dq ttt
+and owned by
+.Dq 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
+.Dq wnj
+or that are newer than
+.Dq ttt .
+.It Li "find  /  \e(  -newer  ttt  -or  -user wnj  \e)  -exit 1"
+Return immediately with a value of 1 if any files are found that are either
+owned by
+.Dq wnj
+or that are newer than
+.Dq ttt ,
+but do not print them.
+.It Li "find  /  \e(  -newer  ttt  -or  -user wnj  \e)  -ls -exit 1"
+Same as above, but list the first file matching the criteria before exiting
+with a value of 1.
+.El
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chmod 1 ,
+.Xr locate 1 ,
+.Xr xargs 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr getgrent 3 ,
+.Xr getpwent 3 ,
+.Xr strmode 3 ,
+.Xr symlink 7 ,
+.Xr sysctl 8
+.Sh STANDARDS
+The
+.Nm
+utility syntax is a superset of the syntax specified by the
+.St -p1003.2
+standard.
+.Pp
+The options and the
+.Ic -amin ,
+.Ic -anewer ,
+.Ic -cmin ,
+.Ic -cnewer ,
+.Ic -delete ,
+.Ic -empty ,
+.Ic -execdir ,
+.Ic -follow ,
+.Ic -fstype ,
+.Ic -iname ,
+.Ic -inum ,
+.Ic -iregex ,
+.Ic -links ,
+.Ic -ls ,
+.Ic -maxdepth ,
+.Ic -mindepth ,
+.Ic -mmin ,
+.Ic -path ,
+.Ic -print0 ,
+.Ic -printx ,
+.Ic -regex ,
+and
+.Ic -rm
+primaries are extensions to
+.St -p1003.2 .
+.Pp
+Historically, the
+.Fl d ,
+.Fl h ,
+and
+.Fl x
+options were implemented using the primaries
+.Dq -depth ,
+.Dq -follow ,
+and
+.Dq -xdev .
+These primaries always evaluated to true.
+As they were really global variables that took effect before the traversal
+began, some legal expressions could have unexpected results.
+An example is the expression
+.Dq -print -o -depth .
+As -print always evaluates to true, the standard order of evaluation
+implies that -depth would never be evaluated.
+This is not the case.
+.Pp
+The operator
+.Dq -or
+was implemented as
+.Dq -o ,
+and the operator
+.Dq -and
+was implemented as
+.Dq -a .
+.Pp
+Historic implementations of the
+.Ic -exec
+and
+.Ic -ok
+primaries did not replace the string
+.Dq {}
+in the utility name or the
+utility arguments if it had preceding or following non-whitespace characters.
+This version replaces it no matter where in the utility name or arguments
+it appears.
+.Pp
+Support for
+.Dq Ic -exec Ar ... No +
+is consistent with
+.Em IEEE PASC Interpretation 1003.2 #210 ,
+though the feature originated in
+.Tn SVR4 .
+.Pp
+The
+.Ic -delete
+primary does not interact well with other options that cause the filesystem
+tree traversal options to be changed.
+.Sh HISTORY
+A much simpler
+.Nm find
+command appeared in First Edition AT\*[Am]T Unix.
+The syntax had become similar to the present version by
+the time of the Fifth Edition.
+.Sh BUGS
+The special characters used by
+.Nm
+are also special characters to many shell programs.
+In particular, the characters
+.Dq \&* ,
+.Dq \&[ ,
+.Dq \&] ,
+.Dq \&? ,
+.Dq \&( ,
+.Dq \&) ,
+.Dq \&! ,
+.Dq \e ,
+and
+.Dq \&;
+may have to be escaped from the shell.
+.Pp
+As there is no delimiter separating options and file names or file
+names and the
+.Ar expression ,
+it is difficult to specify files named
+.Dq -xdev
+or
+.Dq \&! .
+These problems are handled by the
+.Fl f
+option and the
+.Xr getopt 3
+.Dq --
+construct.
diff --git a/commands/find/find.c b/commands/find/find.c
new file mode 100644 (file)
index 0000000..67d5ca5
--- /dev/null
@@ -0,0 +1,284 @@
+/*     $NetBSD: find.c,v 1.25 2007/09/25 04:10:12 lukem Exp $  */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)find.c       8.5 (Berkeley) 8/5/94";
+#else
+__RCSID("$NetBSD: find.c,v 1.25 2007/09/25 04:10:12 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "find.h"
+
+static int ftscompare(const FTSENT **, const FTSENT **);
+
+static void sig_lock(sigset_t *);
+static void sig_unlock(const sigset_t *);
+
+/*
+ * find_formplan --
+ *     process the command line and create a "plan" corresponding to the
+ *     command arguments.
+ */
+PLAN *
+find_formplan(char **argv)
+{
+       PLAN *plan, *tail, *new;
+
+       /*
+        * for each argument in the command line, determine what kind of node
+        * it is, create the appropriate node type and add the new plan node
+        * to the end of the existing plan.  The resulting plan is a linked
+        * list of plan nodes.  For example, the string:
+        *
+        *      % find . -name foo -newer bar -print
+        *
+        * results in the plan:
+        *
+        *      [-name foo]--> [-newer bar]--> [-print]
+        *
+        * in this diagram, `[-name foo]' represents the plan node generated
+        * by c_name() with an argument of foo and `-->' represents the
+        * plan->next pointer.
+        */
+       for (plan = tail = NULL; *argv;) {
+               if (!(new = find_create(&argv)))
+                       continue;
+               if (plan == NULL)
+                       tail = plan = new;
+               else {
+                       tail->next = new;
+                       tail = new;
+               }
+       }
+
+       /*
+        * if the user didn't specify one of -print, -ok, -fprint, -exec, or
+        * -exit, then -print is assumed so we bracket the current expression
+        * with parens, if necessary, and add a -print node on the end.
+        */
+       if (!isoutput) {
+               if (plan == NULL) {
+                       new = c_print(NULL, 0);
+                       tail = plan = new;
+               } else {
+                       new = c_openparen(NULL, 0);
+                       new->next = plan;
+                       plan = new;
+                       new = c_closeparen(NULL, 0);
+                       tail->next = new;
+                       tail = new;
+                       new = c_print(NULL, 0);
+                       tail->next = new;
+                       tail = new;
+               }
+       }
+
+       /*
+        * the command line has been completely processed into a search plan
+        * except for the (, ), !, and -o operators.  Rearrange the plan so
+        * that the portions of the plan which are affected by the operators
+        * are moved into operator nodes themselves.  For example:
+        *
+        *      [!]--> [-name foo]--> [-print]
+        *
+        * becomes
+        *
+        *      [! [-name foo] ]--> [-print]
+        *
+        * and
+        *
+        *      [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
+        *
+        * becomes
+        *
+        *      [expr [-depth]-->[-name foo] ]--> [-print]
+        *
+        * operators are handled in order of precedence.
+        */
+
+       plan = paren_squish(plan);              /* ()'s */
+       plan = not_squish(plan);                /* !'s */
+       plan = or_squish(plan);                 /* -o's */
+       return (plan);
+}
+
+static int
+ftscompare(const FTSENT **e1, const FTSENT **e2)
+{
+
+       return (strcoll((*e1)->fts_name, (*e2)->fts_name));
+}
+
+static void
+sig_lock(sigset_t *s)
+{
+       sigset_t new;
+
+       sigemptyset(&new);
+       sigaddset(&new, SIGINFO); /* block SIGINFO */
+       sigprocmask(SIG_BLOCK, &new, s);
+}
+
+static void
+sig_unlock(const sigset_t *s)
+{
+
+       sigprocmask(SIG_SETMASK, s, NULL);
+}
+
+FTS *tree;                     /* pointer to top of FTS hierarchy */
+FTSENT *g_entry;               /* shared with SIGINFO handler */
+
+/*
+ * find_execute --
+ *     take a search plan and an array of search paths and executes the plan
+ *     over all FTSENT's returned for the given search paths.
+ */
+int
+find_execute(PLAN *plan, char **paths)
+{
+       PLAN *p;
+       int r, rval, cval;
+       sigset_t s;
+
+       cval = 1;
+
+       if (!(tree = fts_open(paths, ftsoptions, issort ? ftscompare : NULL)))
+               err(1, "ftsopen");
+
+       sig_lock(&s);
+       for (rval = 0; cval && (g_entry = fts_read(tree)) != NULL; sig_lock(&s)) {
+               sig_unlock(&s);
+               switch (g_entry->fts_info) {
+               case FTS_D:
+                       if (isdepth)
+                               continue;
+                       break;
+               case FTS_DP:
+                       if (!isdepth)
+                               continue;
+                       break;
+               case FTS_DNR:
+               case FTS_ERR:
+               case FTS_NS:
+                       (void)fflush(stdout);
+                       warnx("%s: %s",
+                           g_entry->fts_path, strerror(g_entry->fts_errno));
+                       rval = 1;
+                       continue;
+               }
+#define        BADCH   " \t\n\\'\""
+               if (isxargs && strpbrk(g_entry->fts_path, BADCH)) {
+                       (void)fflush(stdout);
+                       warnx("%s: illegal path", g_entry->fts_path);
+                       rval = 1;
+                       continue;
+               }
+
+               /*
+                * Call all the functions in the execution plan until one is
+                * 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, g_entry); p = p->next)
+                       if (p->type == N_EXIT) {
+                               rval = p->exit_val;
+                               cval = 0;
+                       }
+       }
+
+       sig_unlock(&s);
+       if (errno)
+               err(1, "fts_read");
+       (void)fts_close(tree);
+
+       /*
+        * Cleanup any plans with leftover state.
+        * Keep the last non-zero return value.
+        */
+       if ((r = find_traverse(plan, plan_cleanup, NULL)) != 0)
+               rval = r;
+
+       return (rval);
+}
+
+/*
+ * find_traverse --
+ *     traverse the plan tree and execute func() on all plans.  This
+ *     does not evaluate each plan's eval() function; it is intended
+ *     for operations that must run on all plans, such as state
+ *     cleanup.
+ *
+ *     If any func() returns non-zero, then so will find_traverse().
+ */
+int
+find_traverse(plan, func, arg)
+       PLAN *plan;
+       int (*func)(PLAN *, void *);
+       void *arg;
+{
+       PLAN *p;
+       int r, rval;
+
+       rval = 0;
+       for (p = plan; p; p = p->next) {
+               if ((r = func(p, arg)) != 0)
+                       rval = r;
+               if (p->type == N_EXPR || p->type == N_OR) {
+                       if (p->p_data[0])
+                               if ((r = find_traverse(p->p_data[0],
+                                           func, arg)) != 0)
+                                       rval = r;
+                       if (p->p_data[1])
+                               if ((r = find_traverse(p->p_data[1],
+                                           func, arg)) != 0)
+                                       rval = r;
+               }
+       }
+       return rval;
+}
diff --git a/commands/find/find.h b/commands/find/find.h
new file mode 100644 (file)
index 0000000..d14b0cb
--- /dev/null
@@ -0,0 +1,135 @@
+/*     $NetBSD: find.h,v 1.24 2007/02/06 13:25:01 elad Exp $   */
+
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     from: @(#)find.h        8.1 (Berkeley) 6/6/93
+ */
+
+#include <regex.h>
+
+/* node type */
+enum ntype {
+       N_AND = 1,                              /* must start > 0 */
+       N_AMIN, N_ANEWER, N_ATIME, N_CLOSEPAREN, N_CMIN, N_CNEWER, N_CTIME,
+       N_DEPTH, N_EMPTY, N_EXEC, N_EXECDIR, N_EXIT, N_EXPR, N_FALSE, N_FLAGS,
+       N_FOLLOW, N_FPRINT, N_FSTYPE, N_GROUP,
+       N_INAME, N_INUM, N_IREGEX, N_LINKS, N_LS, N_MINDEPTH, N_MAXDEPTH,
+       N_MMIN, N_MTIME, N_NAME, N_NEWER, N_NOGROUP, N_NOT, N_NOUSER, N_OK,
+       N_OPENPAREN, N_OR, N_PATH, N_PERM, N_PRINT, N_PRINT0, N_PRINTX,
+       N_PRUNE, N_REGEX, N_SIZE, N_TYPE, N_USER, N_XDEV, N_DELETE
+};
+
+/* node definition */
+typedef struct _plandata {
+       struct _plandata *next;                 /* next node */
+       int (*eval)(struct _plandata *, FTSENT *);
+                                               /* node evaluation function */
+#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_PLUSSET       2                       /* -exec ... {} + */
+#define        F_MTFLAG        1                       /* fstype */
+#define        F_MTTYPE        2
+#define        F_ATLEAST       1                       /* perm */
+       int flags;                              /* private flags */
+       enum ntype type;                        /* plan node type */
+       union {
+               u_int32_t _f_data;              /* flags */
+               gid_t _g_data;                  /* gid */
+               ino_t _i_data;                  /* inode */
+               mode_t _m_data;                 /* mode mask */
+               nlink_t _l_data;                /* link count */
+               off_t _o_data;                  /* file size */
+               time_t _t_data;                 /* time value */
+               uid_t _u_data;                  /* uid */
+               short _mt_data;                 /* mount flags */
+               struct _plandata *_p_data[2];   /* PLAN trees */
+               struct _ex {
+                       char **_e_argv;         /* argv array */
+                       char **_e_orig;         /* original strings */
+                       int *_e_len;            /* allocated length */
+                       char **_ep_bxp;         /* ptr to 1st addt'l arg */
+                       char *_ep_p;            /* current buffer pointer */
+                       char *_ep_bbp;          /* begin buffer pointer */
+                       char *_ep_ebp;          /* end buffer pointer */
+                       int _ep_maxargs;        /* max #args */
+                       int _ep_narg;           /* # addt'l args */
+                       int _ep_rval;           /* return value */
+               } ex;
+               char *_a_data[2];               /* array of char pointers */
+               char *_c_data;                  /* char pointer */
+               int _exit_val;                  /* exit value */
+               int _max_data;                  /* tree depth */
+               int _min_data;                  /* tree depth */
+               regex_t _regexp_data;           /* compiled regexp */
+               FILE *_fprint_file;             /* file stream for -fprint */
+       } p_un;
+} PLAN;
+#define        a_data          p_un._a_data
+#define        c_data          p_un._c_data
+#define        i_data          p_un._i_data
+#define        f_data          p_un._f_data
+#define        g_data          p_un._g_data
+#define        l_data          p_un._l_data
+#define        m_data          p_un._m_data
+#define        mt_data         p_un._mt_data
+#define        o_data          p_un._o_data
+#define        p_data          p_un._p_data
+#define        t_data          p_un._t_data
+#define        u_data          p_un._u_data
+#define        e_argv          p_un.ex._e_argv
+#define        e_orig          p_un.ex._e_orig
+#define        e_len           p_un.ex._e_len
+#define        ep_p            p_un.ex._ep_p
+#define        ep_bbp          p_un.ex._ep_bbp
+#define        ep_ebp          p_un.ex._ep_ebp
+#define        ep_bxp          p_un.ex._ep_bxp
+#define        ep_cnt          p_un.ex._ep_cnt
+#define        ep_maxargs      p_un.ex._ep_maxargs
+#define        ep_nline        p_un.ex._ep_nline
+#define        ep_narg         p_un.ex._ep_narg
+#define        ep_rval         p_un.ex._ep_rval
+#define        exit_val        p_un._exit_val
+#define        max_data        p_un._max_data
+#define        min_data        p_un._min_data
+#define        regexp_data     p_un._regexp_data
+#define        fprint_file     p_un._fprint_file
+
+typedef struct _option {
+       const char *name;               /* option name */
+       enum ntype token;               /* token type */
+       PLAN *(*create)(char ***, int); /* create function */
+       int arg;                        /* function needs arg */
+} OPTION;
+
+#include "extern.h"
diff --git a/commands/find/function.c b/commands/find/function.c
new file mode 100644 (file)
index 0000000..c8b8613
--- /dev/null
@@ -0,0 +1,1896 @@
+/*     $NetBSD: function.c,v 1.64 2007/07/19 07:49:30 daniel Exp $     */
+
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)function.c   8.10 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: function.c,v 1.64 2007/07/19 07:49:30 daniel Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "find.h"
+
+#define        COMPARE(a, b) {                                                 \
+       switch (plan->flags) {                                          \
+       case F_EQUAL:                                                   \
+               return (a == b);                                        \
+       case F_LESSTHAN:                                                \
+               return (a < b);                                         \
+       case F_GREATER:                                                 \
+               return (a > b);                                         \
+       default:                                                        \
+               abort();                                                \
+       }                                                               \
+}
+
+static int64_t find_parsenum(PLAN *, const char *, const char *, char *);
+static void    run_f_exec(PLAN *);
+       int     f_always_true(PLAN *, FTSENT *);
+       int     f_amin(PLAN *, FTSENT *);
+       int     f_anewer(PLAN *, FTSENT *);
+       int     f_atime(PLAN *, FTSENT *);
+       int     f_cmin(PLAN *, FTSENT *);
+       int     f_cnewer(PLAN *, FTSENT *);
+       int     f_ctime(PLAN *, FTSENT *);
+       int     f_delete(PLAN *, FTSENT *);
+       int     f_empty(PLAN *, FTSENT *);
+       int     f_exec(PLAN *, FTSENT *);
+       int     f_execdir(PLAN *, FTSENT *);
+       int     f_false(PLAN *, FTSENT *);
+       int     f_flags(PLAN *, FTSENT *);
+       int     f_fprint(PLAN *, FTSENT *);
+       int     f_fstype(PLAN *, FTSENT *);
+       int     f_group(PLAN *, FTSENT *);
+       int     f_iname(PLAN *, FTSENT *);
+       int     f_inum(PLAN *, FTSENT *);
+       int     f_links(PLAN *, FTSENT *);
+       int     f_ls(PLAN *, FTSENT *);
+       int     f_mindepth(PLAN *, FTSENT *);
+       int     f_maxdepth(PLAN *, FTSENT *);
+       int     f_mmin(PLAN *, FTSENT *);
+       int     f_mtime(PLAN *, FTSENT *);
+       int     f_name(PLAN *, FTSENT *);
+       int     f_newer(PLAN *, FTSENT *);
+       int     f_nogroup(PLAN *, FTSENT *);
+       int     f_nouser(PLAN *, FTSENT *);
+       int     f_path(PLAN *, FTSENT *);
+       int     f_perm(PLAN *, FTSENT *);
+       int     f_print(PLAN *, FTSENT *);
+       int     f_print0(PLAN *, FTSENT *);
+       int     f_printx(PLAN *, FTSENT *);
+       int     f_prune(PLAN *, FTSENT *);
+       int     f_regex(PLAN *, FTSENT *);
+       int     f_size(PLAN *, FTSENT *);
+       int     f_type(PLAN *, FTSENT *);
+       int     f_user(PLAN *, FTSENT *);
+       int     f_not(PLAN *, FTSENT *);
+       int     f_or(PLAN *, FTSENT *);
+static PLAN   *c_regex_common(char ***, int, enum ntype, bool);
+static PLAN   *palloc(enum ntype, int (*)(PLAN *, FTSENT *));
+
+extern int dotfd;
+extern FTS *tree;
+extern time_t now;
+
+/*
+ * find_parsenum --
+ *     Parse a string of the form [+-]# and return the value.
+ */
+static int64_t
+find_parsenum(PLAN *plan, const char *option, const char *vp, char *endch)
+{
+       int64_t value;
+       const char *str;
+       char *endchar; /* Pointer to character ending conversion. */
+
+       /* Determine comparison from leading + or -. */
+       str = vp;
+       switch (*str) {
+       case '+':
+               ++str;
+               plan->flags = F_GREATER;
+               break;
+       case '-':
+               ++str;
+               plan->flags = F_LESSTHAN;
+               break;
+       default:
+               plan->flags = F_EQUAL;
+               break;
+       }
+
+       /*
+        * Convert the string with strtol().  Note, if strtol() returns zero
+        * and endchar points to the beginning of the string we know we have
+        * a syntax error.
+        */
+       value = strtoq(str, &endchar, 10);
+       if (value == 0 && endchar == str)
+               errx(1, "%s: %s: illegal numeric value", option, vp);
+       if (endchar[0] && (endch == NULL || endchar[0] != *endch))
+               errx(1, "%s: %s: illegal trailing character", option, vp);
+       if (endch)
+               *endch = endchar[0];
+       return (value);
+}
+
+/*
+ * 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)             \
+               ++((p)->t_data);
+
+/*
+ * -amin n functions --
+ *
+ *     True if the difference between the file access time and the
+ *     current time is n 1 minute periods.
+ */
+int
+f_amin(PLAN *plan, FTSENT *entry)
+{
+       COMPARE((now - entry->fts_statp->st_atime +
+           SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
+}
+
+PLAN *
+c_amin(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*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);
+}
+
+/*
+ * -anewer file functions --
+ *
+ *     True if the current file has been accessed more recently
+ *     than the access time of the file named by the pathname
+ *     file.
+ */
+int
+f_anewer(plan, entry)
+       PLAN *plan;
+       FTSENT *entry;
+{
+
+       return (entry->fts_statp->st_atime > plan->t_data);
+}
+
+PLAN *
+c_anewer(char ***argvp, int isok)
+{
+       char *filename = **argvp;
+       PLAN *new;
+       struct stat sb;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       if (stat(filename, &sb))
+               err(1, "%s", filename);
+       new = palloc(N_ANEWER, f_anewer);
+       new->t_data = sb.st_atime;
+       return (new);
+}
+
+/*
+ * -atime n functions --
+ *
+ *     True if the difference between the file access time and the
+ *     current time is n 24 hour periods.
+ */
+int
+f_atime(PLAN *plan, FTSENT *entry)
+{
+       COMPARE((now - entry->fts_statp->st_atime +
+           SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
+}
+
+PLAN *
+c_atime(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*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);
+}
+/*
+ * -cmin n functions --
+ *
+ *     True if the difference between the last change of file
+ *     status information and the current time is n 24 hour periods.
+ */
+int
+f_cmin(PLAN *plan, FTSENT *entry)
+{
+       COMPARE((now - entry->fts_statp->st_ctime +
+           SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
+}
+
+PLAN *
+c_cmin(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_CMIN, f_cmin);
+       new->t_data = find_parsenum(new, "-cmin", arg, NULL);
+       TIME_CORRECT(new, N_CMIN);
+       return (new);
+}
+
+/*
+ * -cnewer file functions --
+ *
+ *     True if the current file has been changed more recently
+ *     than the changed time of the file named by the pathname
+ *     file.
+ */
+int
+f_cnewer(PLAN *plan, FTSENT *entry)
+{
+
+       return (entry->fts_statp->st_ctime > plan->t_data);
+}
+
+PLAN *
+c_cnewer(char ***argvp, int isok)
+{
+       char *filename = **argvp;
+       PLAN *new;
+       struct stat sb;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       if (stat(filename, &sb))
+               err(1, "%s", filename);
+       new = palloc(N_CNEWER, f_cnewer);
+       new->t_data = sb.st_ctime;
+       return (new);
+}
+
+/*
+ * -ctime n functions --
+ *
+ *     True if the difference between the last change of file
+ *     status information and the current time is n 24 hour periods.
+ */
+int
+f_ctime(PLAN *plan, FTSENT *entry)
+{
+       COMPARE((now - entry->fts_statp->st_ctime +
+           SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
+}
+
+PLAN *
+c_ctime(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_CTIME, f_ctime);
+       new->t_data = find_parsenum(new, "-ctime", arg, NULL);
+       TIME_CORRECT(new, N_CTIME);
+       return (new);
+}
+
+/*
+ * -delete functions --
+ *
+ *     True always.  Makes its best shot and continues on regardless.
+ */
+int
+f_delete(PLAN *plan __unused, 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(char ***argvp __unused, int isok)
+{
+
+       ftsoptions &= ~FTS_NOSTAT;      /* no optimize */
+       ftsoptions |= FTS_PHYSICAL;     /* disable -follow */
+       ftsoptions &= ~FTS_LOGICAL;     /* disable -follow */
+       isoutput = 1;                   /* possible output */
+       isdepth = 1;                    /* -depth implied */
+
+       return palloc(N_DELETE, f_delete);
+}
+
+/*
+ * -depth functions --
+ *
+ *     Always true, causes descent of the directory hierarchy to be done
+ *     so that all entries in a directory are acted on before the directory
+ *     itself.
+ */
+int
+f_always_true(PLAN *plan, FTSENT *entry)
+{
+
+       return (1);
+}
+
+PLAN *
+c_depth(char ***argvp, int isok)
+{
+       isdepth = 1;
+
+       return (palloc(N_DEPTH, f_always_true));
+}
+
+/*
+ * -empty functions --
+ *
+ *     True if the file or directory is empty
+ */
+int
+f_empty(PLAN *plan, FTSENT *entry)
+{
+       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;
+               DIR *dir;
+
+               empty = 1;
+               dir = opendir(entry->fts_accpath);
+               if (dir == NULL)
+                       err(1, "%s", entry->fts_accpath);
+               for (dp = readdir(dir); dp; dp = readdir(dir))
+                       if (dp->d_name[0] != '.' ||
+                           (dp->d_name[1] != '\0' &&
+                               (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
+                               empty = 0;
+                               break;
+                       }
+               closedir(dir);
+               return (empty);
+       }
+       return (0);
+}
+
+PLAN *
+c_empty(char ***argvp, int isok)
+{
+       ftsoptions &= ~FTS_NOSTAT;
+
+       return (palloc(N_EMPTY, f_empty));
+}
+
+/*
+ * [-exec | -ok] utility [arg ... ] ; functions --
+ * [-exec | -ok] utility [arg ... ] {} + functions --
+ *
+ *     If the end of the primary expression is delimited by a
+ *     semicolon: true if the executed utility returns a zero value
+ *     as exit status.  If "{}" occurs anywhere, it gets replaced by
+ *     the current pathname.
+ *
+ *     If the end of the primary expression is delimited by a plus
+ *     sign: always true. Pathnames for which the primary is
+ *     evaluated shall be aggregated into sets. The utility will be
+ *     executed once per set, with "{}" replaced by the entire set of
+ *     pathnames (as if xargs). "{}" must appear last.
+ *
+ *     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 *plan, FTSENT *entry)
+{
+       int cnt, l;
+       pid_t pid;
+       int status;
+
+       if (plan->flags & F_PLUSSET) {
+               /*
+                * Confirm sufficient buffer space, then copy the path
+                * to the buffer.
+                */
+               l = strlen(entry->fts_path);
+               if (plan->ep_p + l < plan->ep_ebp) {
+                       plan->ep_bxp[plan->ep_narg++] =
+                           strcpy(plan->ep_p, entry->fts_path);
+                       plan->ep_p += l + 1;
+
+                       if (plan->ep_narg == plan->ep_maxargs)
+                               run_f_exec(plan);
+               } else {
+                       /*
+                        * Without sufficient space to copy in the next
+                        * argument, run the command to empty out the
+                        * buffer before re-attepting the copy.
+                        */
+                       run_f_exec(plan);
+                       if ((plan->ep_p + l < plan->ep_ebp)) {
+                               plan->ep_bxp[plan->ep_narg++]
+                                   = strcpy(plan->ep_p, entry->fts_path);
+                               plan->ep_p += l + 1;
+                       } else
+                               errx(1, "insufficient space for argument");
+               }
+               return (1);
+       } else {
+               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);
+
+               /* Don't mix output of command with find output. */
+               fflush(stdout);
+               fflush(stderr);
+
+               switch (pid = vfork()) {
+               case -1:
+                       err(1, "vfork");
+                       /* 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));
+       }
+}
+
+static void
+run_f_exec(PLAN *plan)
+{
+       pid_t pid;
+       int rval, status;
+
+       /* Ensure arg list is null terminated. */
+       plan->ep_bxp[plan->ep_narg] = NULL;
+
+       /* Don't mix output of command with find output. */
+       fflush(stdout);
+       fflush(stderr);
+
+       switch (pid = vfork()) {
+       case -1:
+               err(1, "vfork");
+               /* 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);
+       }
+
+       /* Clear out the argument list. */
+       plan->ep_narg = 0;
+       plan->ep_bxp[plan->ep_narg] = NULL;
+       /* As well as the argument buffer. */
+       plan->ep_p = plan->ep_bbp;
+       *plan->ep_p = '\0';
+
+       pid = waitpid(pid, &status, 0);
+       if (WIFEXITED(status))
+               rval = WEXITSTATUS(status);
+       else
+               rval = -1;
+
+       /*
+        * If we have a non-zero exit status, preserve it so find(1) can
+        * later exit with it.
+        */
+       if (rval)
+               plan->ep_rval = rval;
+}
+
+/*
+ * 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.
+ *
+ *     If -exec ... {} +, use only the first array, but make it large
+ *     enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a
+ *     discussion), and then allocate ARG_MAX - 4K of space for args.
+ */
+PLAN *
+c_exec(char ***argvp, int isok)
+{
+       PLAN *new;                      /* node returned */
+       int cnt, brace, lastbrace;
+       char **argv, **ap, *p;
+
+       isoutput = 1;
+
+       new = palloc(N_EXEC, f_exec);
+       if (isok)
+               new->flags |= F_NEEDOK;
+
+       /*
+        * Terminate if we encounter an arg exacty equal to ";", or an
+        * arg exacty equal to "+" following an arg exacty equal to
+        * "{}".
+        */
+       for (ap = argv = *argvp, brace = 0;; ++ap) {
+               if (!*ap)
+                       errx(1, "%s: no terminating \";\" or \"+\"",
+                           isok ? "-ok" : "-exec");
+               lastbrace = brace;
+               if (strcmp(*ap, "{}") == 0)
+                       brace = 1;
+               if (strcmp(*ap, ";") == 0)
+                       break;
+               if (strcmp(*ap, "+") == 0 && lastbrace) {
+                       new->flags |= F_PLUSSET;
+                       break;
+               }
+       }
+
+       /*
+        * POSIX says -ok ... {} + "need not be supported," and it does
+        * not make much sense anyway.
+        */
+       if (new->flags & F_NEEDOK && new->flags & F_PLUSSET)
+               errx(1, "-ok: terminating \"+\" not permitted.");
+
+       if (new->flags & F_PLUSSET) {
+               u_int c, bufsize;
+
+               cnt = ap - *argvp - 1;                  /* units are words */
+               new->ep_maxargs = 5000;
+               new->e_argv = (char **)emalloc((u_int)(cnt + new->ep_maxargs)
+                                               * sizeof(char **));
+
+               /* We start stuffing arguments after the user's last one. */
+               new->ep_bxp = &new->e_argv[cnt];
+               new->ep_narg = 0;
+
+               /*
+                * Count up the space of the user's arguments, and
+                * subtract that from what we allocate.
+                */
+               for (argv = *argvp, c = 0, cnt = 0;
+                    argv < ap;
+                    ++argv, ++cnt) {
+                       c += strlen(*argv) + 1;
+                       new->e_argv[cnt] = *argv;
+               }
+               bufsize = ARG_MAX - 4 * 1024 - c;
+
+
+               /*
+                * Allocate, and then initialize current, base, and
+                * end pointers.
+                */
+               new->ep_p = new->ep_bbp = malloc(bufsize + 1);
+               new->ep_ebp = new->ep_bbp + bufsize - 1;
+               new->ep_rval = 0;
+       } else { /* !F_PLUSSET */
+               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_orig[cnt] = NULL;
+       }
+
+       new->e_argv[cnt] = NULL;
+       *argvp = argv + 1;
+       return (new);
+}
+
+/*
+ * -execdir 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.
+ */
+int
+f_execdir(PLAN *plan, FTSENT *entry)
+{
+       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++;
+       else
+               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 */
+       fflush(stdout);
+       fflush(stderr);
+
+       switch (pid = vfork()) {
+       case -1:
+               err(1, "fork");
+               /* NOTREACHED */
+       case 0:
+               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_execdir --
+ *     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(char ***argvp, int isok)
+{
+       PLAN *new;                      /* node returned */
+       int cnt;
+       char **argv, **ap, *p;
+
+       ftsoptions &= ~FTS_NOSTAT;
+       isoutput = 1;
+
+       new = palloc(N_EXECDIR, f_execdir);
+
+       for (ap = argv = *argvp;; ++ap) {
+               if (!*ap)
+                       errx(1,
+                           "-execdir: no terminating \";\"");
+               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;
+
+       *argvp = argv + 1;
+       return (new);
+}
+
+PLAN *
+c_exit(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       /* not technically true, but otherwise '-print' is implied */
+       isoutput = 1;
+
+       new = palloc(N_EXIT, f_always_true);
+
+       if (arg) {
+               (*argvp)++;
+               new->exit_val = find_parsenum(new, "-exit", arg, NULL);
+       } else
+               new->exit_val = 0;
+
+       return (new);
+}
+
+
+/*
+ * -false function
+ */
+int
+f_false(PLAN *plan, FTSENT *entry)
+{
+
+       return (0);
+}
+
+PLAN *
+c_false(char ***argvp, int isok)
+{
+       return (palloc(N_FALSE, f_false));
+}
+
+
+/*
+ * -flags [-]flags functions --
+ */
+int
+f_flags(PLAN *plan, FTSENT *entry)
+{
+       u_int32_t flags;
+
+       flags = entry->fts_statp->st_flags;
+       if (plan->flags == F_ATLEAST)
+               return ((plan->f_data | flags) == flags);
+       else
+               return (flags == plan->f_data);
+       /* NOTREACHED */
+}
+
+PLAN *
+c_flags(char ***argvp, int isok)
+{
+       char *flags = **argvp;
+       PLAN *new;
+       u_long flagset;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_FLAGS, f_flags);
+
+       if (*flags == '-') {
+               new->flags = F_ATLEAST;
+               ++flags;
+       }
+
+       flagset = 0;
+       if ((strcmp(flags, "none") != 0) &&
+           (string_to_flags(&flags, &flagset, NULL) != 0))
+               errx(1, "-flags: %s: illegal flags string", flags);
+       new->f_data = flagset;
+       return (new);
+}
+
+/*
+ * -follow functions --
+ *
+ *     Always true, causes symbolic links to be followed on a global
+ *     basis.
+ */
+PLAN *
+c_follow(char ***argvp, int isok)
+{
+       ftsoptions &= ~FTS_PHYSICAL;
+       ftsoptions |= FTS_LOGICAL;
+
+       return (palloc(N_FOLLOW, f_always_true));
+}
+
+/* -fprint functions --
+ *
+ *     Causes the current pathame to be written to the defined output file.
+ */
+int
+f_fprint(PLAN *plan, FTSENT *entry)
+{
+
+       if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path))
+               warn("fprintf");
+
+       return(1);
+
+       /* no descriptors are closed; they will be closed by
+          operating system when this find command exits.  */
+}
+
+PLAN *
+c_fprint(char ***argvp, int isok)
+{
+       PLAN *new;
+
+       isoutput = 1; /* do not assume -print */
+
+       new = palloc(N_FPRINT, f_fprint);
+
+       if (NULL == (new->fprint_file = fopen(**argvp, "w")))
+               err(1, "-fprint: %s: cannot create file", **argvp);
+
+       (*argvp)++;
+       return (new);
+}
+
+/*
+ * -fstype functions --
+ *
+ *     True if the file is of a certain type.
+ */
+int
+f_fstype(PLAN *plan, FTSENT *entry)
+{
+       static dev_t curdev;    /* need a guaranteed illegal dev value */
+       static int first = 1;
+       struct statvfs sb;
+       static short val;
+       static char fstype[sizeof(sb.f_fstypename)];
+       char *p, save[2];
+
+       memset(&save, 0, sizeof save);  /* XXX gcc */
+
+       /* Only check when we cross mount point. */
+       if (first || curdev != entry->fts_statp->st_dev) {
+               curdev = entry->fts_statp->st_dev;
+
+               /*
+                * Statfs follows symlinks; find wants the link's file system,
+                * not where it points.
+                */
+               if (entry->fts_info == FTS_SL ||
+                   entry->fts_info == FTS_SLNONE) {
+                       if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
+                               ++p;
+                       else
+                               p = entry->fts_accpath;
+                       save[0] = p[0];
+                       p[0] = '.';
+                       save[1] = p[1];
+                       p[1] = '\0';
+
+               } else
+                       p = NULL;
+
+               if (statvfs(entry->fts_accpath, &sb))
+                       err(1, "%s", entry->fts_accpath);
+
+               if (p) {
+                       p[0] = save[0];
+                       p[1] = save[1];
+               }
+
+               first = 0;
+
+               /*
+                * Further tests may need both of these values, so
+                * always copy both of them.
+                */
+               val = sb.f_flag;
+               strlcpy(fstype, sb.f_fstypename, sizeof(fstype));
+       }
+       switch (plan->flags) {
+       case F_MTFLAG:
+               return (val & plan->mt_data);
+       case F_MTTYPE:
+               return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0);
+       default:
+               abort();
+       }
+}
+
+PLAN *
+c_fstype(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_FSTYPE, f_fstype);
+
+       switch (*arg) {
+       case 'l':
+               if (!strcmp(arg, "local")) {
+                       new->flags = F_MTFLAG;
+                       new->mt_data = MNT_LOCAL;
+                       return (new);
+               }
+               break;
+       case 'r':
+               if (!strcmp(arg, "rdonly")) {
+                       new->flags = F_MTFLAG;
+                       new->mt_data = MNT_RDONLY;
+                       return (new);
+               }
+               break;
+       }
+
+       new->flags = F_MTTYPE;
+       new->c_data = arg;
+       return (new);
+}
+
+/*
+ * -group gname functions --
+ *
+ *     True if the file belongs to the group gname.  If gname is numeric and
+ *     an equivalent of the getgrnam() function does not return a valid group
+ *     name, gname is taken as a group ID.
+ */
+int
+f_group(PLAN *plan, FTSENT *entry)
+{
+
+       return (entry->fts_statp->st_gid == plan->g_data);
+}
+
+PLAN *
+c_group(char ***argvp, int isok)
+{
+       char *gname = **argvp;
+       PLAN *new;
+       struct group *g;
+       gid_t gid;
+
+       (*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);
+       } else
+               gid = g->gr_gid;
+
+       new = palloc(N_GROUP, f_group);
+       new->g_data = gid;
+       return (new);
+}
+
+/*
+ * -inum n functions --
+ *
+ *     True if the file has inode # n.
+ */
+int
+f_inum(PLAN *plan, FTSENT *entry)
+{
+
+       COMPARE(entry->fts_statp->st_ino, plan->i_data);
+}
+
+PLAN *
+c_inum(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_INUM, f_inum);
+       new->i_data = find_parsenum(new, "-inum", arg, NULL);
+       return (new);
+}
+
+/*
+ * -links n functions --
+ *
+ *     True if the file has n links.
+ */
+int
+f_links(PLAN *plan, FTSENT *entry)
+{
+
+       COMPARE(entry->fts_statp->st_nlink, plan->l_data);
+}
+
+PLAN *
+c_links(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_LINKS, f_links);
+       new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
+       return (new);
+}
+
+/*
+ * -ls functions --
+ *
+ *     Always true - prints the current entry to stdout in "ls" format.
+ */
+int
+f_ls(PLAN *plan, FTSENT *entry)
+{
+
+       printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
+       return (1);
+}
+
+PLAN *
+c_ls(char ***argvp, int isok)
+{
+
+       ftsoptions &= ~FTS_NOSTAT;
+       isoutput = 1;
+
+       return (palloc(N_LS, f_ls));
+}
+
+/*
+ * - maxdepth n functions --
+ *
+ *     True if the current search depth is less than or equal to the
+ *     maximum depth specified
+ */
+int
+f_maxdepth(PLAN *plan, FTSENT *entry)
+{
+       extern FTS *tree;
+
+       if (entry->fts_level >= plan->max_data)
+               fts_set(tree, entry, FTS_SKIP);
+       return (entry->fts_level <= plan->max_data);
+}
+
+PLAN *
+c_maxdepth(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       new = palloc(N_MAXDEPTH, f_maxdepth);
+       new->max_data = atoi(arg);
+       return (new);
+}
+
+/*
+ * - mindepth n functions --
+ *
+ *     True if the current search depth is greater than or equal to the
+ *     minimum depth specified
+ */
+int
+f_mindepth(PLAN *plan, FTSENT *entry)
+{
+       return (entry->fts_level >= plan->min_data);
+}
+
+PLAN *
+c_mindepth(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       new = palloc(N_MINDEPTH, f_mindepth);
+       new->min_data = atoi(arg);
+       return (new);
+}
+/*
+ * -mmin n functions --
+ *
+ *     True if the difference between the file modification time and the
+ *     current time is n 24 hour periods.
+ */
+int
+f_mmin(PLAN *plan, FTSENT *entry)
+{
+       COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) /
+           SECSPERMIN, plan->t_data);
+}
+
+PLAN *
+c_mmin(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       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);
+}
+/*
+ * -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 *plan, FTSENT *entry)
+{
+       COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
+           SECSPERDAY, plan->t_data);
+}
+
+PLAN *
+c_mtime(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       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);
+}
+
+/*
+ * -name functions --
+ *
+ *     True if the basename of the filename being examined
+ *     matches pattern using Pattern Matching Notation S3.14
+ */
+int
+f_name(PLAN *plan, FTSENT *entry)
+{
+
+       return (!fnmatch(plan->c_data, entry->fts_name, 0));
+}
+
+PLAN *
+c_name(char ***argvp, int isok)
+{
+       char *pattern = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       new = palloc(N_NAME, f_name);
+       new->c_data = pattern;
+       return (new);
+}
+
+/*
+ * -iname functions --
+ *
+ *     Similar to -name, but does case insensitive matching
+ *
+ */
+int
+f_iname(PLAN *plan, FTSENT *entry)
+{
+       return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
+}
+
+PLAN *
+c_iname(char ***argvp, int isok)
+{
+       char *pattern = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       new = palloc(N_INAME, f_iname);
+       new->c_data = pattern;
+       return (new);
+}
+
+/*
+ * -newer file functions --
+ *
+ *     True if the current file has been modified more recently
+ *     than the modification time of the file named by the pathname
+ *     file.
+ */
+int
+f_newer(PLAN *plan, FTSENT *entry)
+{
+
+       return (entry->fts_statp->st_mtime > plan->t_data);
+}
+
+PLAN *
+c_newer(char ***argvp, int isok)
+{
+       char *filename = **argvp;
+       PLAN *new;
+       struct stat sb;
+
+       (*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);
+}
+
+/*
+ * -nogroup functions --
+ *
+ *     True if file belongs to a user ID for which the equivalent
+ *     of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
+ */
+int
+f_nogroup(PLAN *plan, FTSENT *entry)
+{
+
+       return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
+}
+
+PLAN *
+c_nogroup(char ***argvp, int isok)
+{
+       ftsoptions &= ~FTS_NOSTAT;
+
+       return (palloc(N_NOGROUP, f_nogroup));
+}
+
+/*
+ * -nouser functions --
+ *
+ *     True if file belongs to a user ID for which the equivalent
+ *     of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
+ */
+int
+f_nouser(PLAN *plan, FTSENT *entry)
+{
+
+       return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
+}
+
+PLAN *
+c_nouser(char ***argvp, int isok)
+{
+       ftsoptions &= ~FTS_NOSTAT;
+
+       return (palloc(N_NOUSER, f_nouser));
+}
+
+/*
+ * -path functions --
+ *
+ *     True if the path of the filename being examined
+ *     matches pattern using Pattern Matching Notation S3.14
+ */
+int
+f_path(PLAN *plan, FTSENT *entry)
+{
+
+       return (!fnmatch(plan->c_data, entry->fts_path, 0));
+}
+
+PLAN *
+c_path(char ***argvp, int isok)
+{
+       char *pattern = **argvp;
+       PLAN *new;
+
+       (*argvp)++;
+       new = palloc(N_NAME, f_path);
+       new->c_data = pattern;
+       return (new);
+}
+
+/*
+ * -perm functions --
+ *
+ *     The mode argument is used to represent file mode bits.  If it starts
+ *     with a leading digit, it's treated as an octal mode, otherwise as a
+ *     symbolic mode.
+ */
+int
+f_perm(PLAN *plan, FTSENT *entry)
+{
+       mode_t mode;
+
+       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
+               return (mode == plan->m_data);
+       /* NOTREACHED */
+}
+
+PLAN *
+c_perm(char ***argvp, int isok)
+{
+       char *perm = **argvp;
+       PLAN *new;
+       mode_t *set;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_PERM, f_perm);
+
+       if (*perm == '-') {
+               new->flags = F_ATLEAST;
+               ++perm;
+       }
+
+       if ((set = setmode(perm)) == NULL)
+               err(1, "-perm: Cannot set file mode `%s'", perm);
+
+       new->m_data = getmode(set, 0);
+       free(set);
+       return (new);
+}
+
+/*
+ * -print functions --
+ *
+ *     Always true, causes the current pathame to be written to
+ *     standard output.
+ */
+int
+f_print(PLAN *plan, FTSENT *entry)
+{
+
+       (void)printf("%s\n", entry->fts_path);
+       return (1);
+}
+
+int
+f_print0(PLAN *plan, FTSENT *entry)
+{
+
+       (void)fputs(entry->fts_path, stdout);
+       (void)fputc('\0', stdout);
+       return (1);
+}
+
+int
+f_printx(PLAN *plan, FTSENT *entry)
+{
+       char *cp;
+
+       for (cp = entry->fts_path; *cp; cp++) {
+               if (*cp == '\'' || *cp == '\"' || *cp == ' ' ||
+                   *cp == '$'  || *cp == '`'  ||
+                   *cp == '\t' || *cp == '\n' || *cp == '\\')
+                       fputc('\\', stdout);
+
+               fputc(*cp, stdout);
+       }
+
+       fputc('\n', stdout);
+       return (1);
+}
+
+PLAN *
+c_print(char ***argvp, int isok)
+{
+
+       isoutput = 1;
+
+       return (palloc(N_PRINT, f_print));
+}
+
+PLAN *
+c_print0(char ***argvp, int isok)
+{
+
+       isoutput = 1;
+
+       return (palloc(N_PRINT0, f_print0));
+}
+
+PLAN *
+c_printx(char ***argvp, int isok)
+{
+
+       isoutput = 1;
+
+       return (palloc(N_PRINTX, f_printx));
+}
+
+/*
+ * -prune functions --
+ *
+ *     Prune a portion of the hierarchy.
+ */
+int
+f_prune(PLAN *plan, FTSENT *entry)
+{
+       if (fts_set(tree, entry, FTS_SKIP))
+               err(1, "%s", entry->fts_path);
+       return (1);
+}
+
+PLAN *
+c_prune(char ***argvp, int isok)
+{
+
+       return (palloc(N_PRUNE, f_prune));
+}
+
+/*
+ * -regex regexp (and related) functions --
+ *
+ *     True if the complete file path matches the regular expression regexp.
+ *     For -regex, regexp is a case-sensitive (basic) regular expression.
+ *     For -iregex, regexp is a case-insensitive (basic) regular expression.
+ */
+int
+f_regex(PLAN *plan, FTSENT *entry)
+{
+
+       return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0);
+}
+
+static PLAN *
+c_regex_common(char ***argvp, int isok, enum ntype type, bool icase)
+{
+       char errbuf[LINE_MAX];
+       regex_t reg;
+       char *regexp = **argvp;
+       char *lineregexp;
+       PLAN *new;
+       int rv;
+       size_t len;
+
+       (*argvp)++;
+
+       len = strlen(regexp) + 1 + 6;
+       lineregexp = malloc(len);       /* max needed */
+       if (lineregexp == NULL)
+               err(1, NULL);
+       snprintf(lineregexp, len, "^%s(%s%s)$",
+           (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp,
+           (regcomp_flags & REG_EXTENDED) ? "" : "\\");
+       rv = regcomp(&reg, lineregexp, REG_NOSUB|regcomp_flags|
+           (icase ? REG_ICASE : 0));
+       free(lineregexp);
+       if (rv != 0) {
+               regerror(rv, &reg, errbuf, sizeof errbuf);
+               errx(1, "regexp %s: %s", regexp, errbuf);
+       }
+
+       new = palloc(type, f_regex);
+       new->regexp_data = reg;
+       return (new);
+}
+
+PLAN *
+c_regex(char ***argvp, int isok)
+{
+
+       return (c_regex_common(argvp, isok, N_REGEX, false));
+}
+
+PLAN *
+c_iregex(char ***argvp, int isok)
+{
+
+       return (c_regex_common(argvp, isok, N_IREGEX, true));
+}
+
+/*
+ * -size n[c] functions --
+ *
+ *     True if the file size in bytes, divided by an implementation defined
+ *     value and rounded up to the next integer, is n.  If n is followed by
+ *     a c, the size is in bytes.
+ */
+#define        FIND_SIZE       512
+static int divsize = 1;
+
+int
+f_size(PLAN *plan, FTSENT *entry)
+{
+       off_t size;
+
+       size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
+           FIND_SIZE : entry->fts_statp->st_size;
+       COMPARE(size, plan->o_data);
+}
+
+PLAN *
+c_size(char ***argvp, int isok)
+{
+       char *arg = **argvp;
+       PLAN *new;
+       char endch;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_SIZE, f_size);
+       endch = 'c';
+       new->o_data = find_parsenum(new, "-size", arg, &endch);
+       if (endch == 'c')
+               divsize = 0;
+       return (new);
+}
+
+/*
+ * -type c functions --
+ *
+ *     True if the type of the file is c, where c is b, c, d, p, f or w
+ *     for block special file, character special file, directory, FIFO,
+ *     regular file or whiteout respectively.
+ */
+int
+f_type(PLAN *plan, FTSENT *entry)
+{
+
+       return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
+}
+
+PLAN *
+c_type(char ***argvp, int isok)
+{
+       char *typestring = **argvp;
+       PLAN *new;
+       mode_t  mask = (mode_t)0;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       switch (typestring[0]) {
+       case 'b':
+               mask = S_IFBLK;
+               break;
+       case 'c':
+               mask = S_IFCHR;
+               break;
+       case 'd':
+               mask = S_IFDIR;
+               break;
+       case 'f':
+               mask = S_IFREG;
+               break;
+       case 'l':
+               mask = S_IFLNK;
+               break;
+       case 'p':
+               mask = S_IFIFO;
+               break;
+       case 's':
+               mask = S_IFSOCK;
+               break;
+#ifdef S_IFWHT
+       case 'W':
+       case 'w':
+               mask = S_IFWHT;
+#ifdef FTS_WHITEOUT
+               ftsoptions |= FTS_WHITEOUT;
+#endif
+               break;
+#endif /* S_IFWHT */
+       default:
+               errx(1, "-type: %s: unknown type", typestring);
+       }
+
+       new = palloc(N_TYPE, f_type);
+       new->m_data = mask;
+       return (new);
+}
+
+/*
+ * -user uname functions --
+ *
+ *     True if the file belongs to the user uname.  If uname is numeric and
+ *     an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
+ *     return a valid user name, uname is taken as a user ID.
+ */
+int
+f_user(PLAN *plan, FTSENT *entry)
+{
+
+       COMPARE(entry->fts_statp->st_uid, plan->u_data);
+}
+
+PLAN *
+c_user(char ***argvp, int isok)
+{
+       char *username = **argvp;
+       PLAN *new;
+       struct passwd *p;
+       uid_t uid;
+
+       (*argvp)++;
+       ftsoptions &= ~FTS_NOSTAT;
+
+       new = palloc(N_USER, f_user);
+       p = getpwnam(username);
+       if (p == NULL) {
+               if (atoi(username) == 0 && username[0] != '0' &&
+                   strcmp(username, "+0") && strcmp(username, "-0"))
+                       errx(1, "-user: %s: no such user", username);
+               uid = find_parsenum(new, "-user", username, NULL);
+
+       } else {
+               new->flags = F_EQUAL;
+               uid = p->pw_uid;
+       }
+
+       new->u_data = uid;
+       return (new);
+}
+
+/*
+ * -xdev functions --
+ *
+ *     Always true, causes find not to descend past directories that have a
+ *     different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
+ */
+PLAN *
+c_xdev(char ***argvp, int isok)
+{
+       ftsoptions |= FTS_XDEV;
+
+       return (palloc(N_XDEV, f_always_true));
+}
+
+/*
+ * ( expression ) functions --
+ *
+ *     True if expression is true.
+ */
+int
+f_expr(PLAN *plan, FTSENT *entry)
+{
+       PLAN *p;
+       int state;
+
+       state = 0;
+       for (p = plan->p_data[0];
+           p && (state = (p->eval)(p, entry)); p = p->next);
+       return (state);
+}
+
+/*
+ * N_OPENPAREN and N_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.
+ */
+PLAN *
+c_openparen(char ***argvp, int isok)
+{
+
+       return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
+}
+
+PLAN *
+c_closeparen(char ***argvp, int isok)
+{
+
+       return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
+}
+
+/*
+ * ! expression functions --
+ *
+ *     Negation of a primary; the unary NOT operator.
+ */
+int
+f_not(PLAN *plan, FTSENT *entry)
+{
+       PLAN *p;
+       int state;
+
+       state = 0;
+       for (p = plan->p_data[0];
+           p && (state = (p->eval)(p, entry)); p = p->next);
+       return (!state);
+}
+
+PLAN *
+c_not(char ***argvp, int isok)
+{
+
+       return (palloc(N_NOT, f_not));
+}
+
+/*
+ * expression -o expression functions --
+ *
+ *     Alternation of primaries; the OR operator.  The second expression is
+ * not evaluated if the first expression is true.
+ */
+int
+f_or(PLAN *plan, FTSENT *entry)
+{
+       PLAN *p;
+       int state;
+
+       state = 0;
+       for (p = plan->p_data[0];
+           p && (state = (p->eval)(p, entry)); p = p->next);
+
+       if (state)
+               return (1);
+
+       for (p = plan->p_data[1];
+           p && (state = (p->eval)(p, entry)); p = p->next);
+       return (state);
+}
+
+PLAN *
+c_or(char ***argvp, int isok)
+{
+
+       return (palloc(N_OR, f_or));
+}
+
+PLAN *
+c_null(char ***argvp, int isok)
+{
+
+       return (NULL);
+}
+
+
+/*
+ * plan_cleanup --
+ *     Check and see if the specified plan has any residual state,
+ *     and if so, clean it up as appropriate.
+ *
+ *     At the moment, only N_EXEC has state. Two kinds: 1)
+ *     lists of files to feed to subprocesses 2) State on exit
+ *     statusses of past subprocesses.
+ */
+/* ARGSUSED1 */
+int
+plan_cleanup(PLAN *plan, void *arg)
+{
+       if (plan->type==N_EXEC && plan->ep_narg)
+               run_f_exec(plan);
+
+       return plan->ep_rval;           /* Passed save exit-status up chain */
+}
+
+static PLAN *
+palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
+{
+       PLAN *new;
+
+       if ((new = malloc(sizeof(PLAN))) == NULL)
+               err(1, NULL);
+       memset(new, 0, sizeof(PLAN));
+       new->type = t;
+       new->eval = f;
+       return (new);
+}
diff --git a/commands/find/ls.c b/commands/find/ls.c
new file mode 100644 (file)
index 0000000..71fe959
--- /dev/null
@@ -0,0 +1,123 @@
+/*     $NetBSD: ls.c,v 1.19 2006/10/11 19:51:10 apb Exp $      */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)ls.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: ls.c,v 1.19 2006/10/11 19:51:10 apb Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "find.h"
+
+/* Derived from the print routines in the ls(1) source code. */
+
+static void printlink(char *);
+static void printtime(time_t);
+
+void
+printlong(char *name,                  /* filename to print */
+       char *accpath,                  /* current valid path to filename */
+       struct stat *sb)                /* stat buffer */
+{
+       char modep[15];
+
+       (void)printf("%7lu %6lld ", (u_long)sb->st_ino,
+           (long long)sb->st_blocks);
+       (void)strmode(sb->st_mode, modep);
+       (void)printf("%s %3lu %-*s %-*s ", modep, (unsigned long)sb->st_nlink,
+           LOGIN_NAME_MAX, user_from_uid(sb->st_uid, 0), LOGIN_NAME_MAX,
+           group_from_gid(sb->st_gid, 0));
+
+       if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode))
+               (void)printf("%3d,%5d ", major(sb->st_rdev),
+                   minor(sb->st_rdev));
+       else
+               (void)printf("%9lld ", (long long)sb->st_size);
+       printtime(sb->st_mtime);
+       (void)printf("%s", name);
+       if (S_ISLNK(sb->st_mode))
+               printlink(accpath);
+       (void)putchar('\n');
+}
+
+static void
+printtime(time_t ftime)
+{
+       int i;
+       char *longstring;
+
+       longstring = ctime(&ftime);
+       for (i = 4; i < 11; ++i)
+               (void)putchar(longstring[i]);
+
+#define        SIXMONTHS       ((DAYSPERNYEAR / 2) * SECSPERDAY)
+       if (ftime + SIXMONTHS > time((time_t *)NULL))
+               for (i = 11; i < 16; ++i)
+                       (void)putchar(longstring[i]);
+       else {
+               (void)putchar(' ');
+               for (i = 20; i < 24; ++i)
+                       (void)putchar(longstring[i]);
+       }
+       (void)putchar(' ');
+}
+
+static void
+printlink(char *name)
+{
+       int lnklen;
+       char path[MAXPATHLEN + 1];
+
+       if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
+               warn("%s", name);
+               return;
+       }
+       path[lnklen] = '\0';
+       (void)printf(" -> %s", path);
+}
diff --git a/commands/find/main.c b/commands/find/main.c
new file mode 100644 (file)
index 0000000..587db06
--- /dev/null
@@ -0,0 +1,173 @@
+/*     $NetBSD: main.c,v 1.28 2008/07/21 14:19:22 lukem Exp $  */
+
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c     8.4 (Berkeley) 5/4/95";
+#else
+__COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
+ The Regents of the University of California.  All rights reserved.");
+__RCSID("$NetBSD: main.c,v 1.28 2008/07/21 14:19:22 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <signal.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "find.h"
+
+time_t now;                    /* time find was run */
+int dotfd;                     /* starting directory */
+int ftsoptions;                        /* options for the ftsopen(3) call */
+int isdeprecated;              /* using deprecated syntax */
+int isdepth;                   /* do directories on post-order visit */
+int isoutput;                  /* user specified output operator */
+int issort;                    /* sort directory entries */
+int isxargs;                   /* don't permit xargs delimiting chars */
+int regcomp_flags = REG_BASIC; /* regex compilation flags */
+
+int main(int, char **);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       char **p, **start;
+       int ch;
+
+       (void)time(&now);       /* initialize the time-of-day */
+       (void)setlocale(LC_ALL, "");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_RESTART;
+       sa.sa_handler = show_path;
+       sigaction(SIGINFO, &sa, NULL);
+
+       /* array to hold dir list.  at most (argc - 1) elements. */
+       p = start = malloc(argc * sizeof (char *));
+       if (p == NULL)
+               err(1, NULL);
+
+       ftsoptions = FTS_NOSTAT | FTS_PHYSICAL;
+       while ((ch = getopt(argc, argv, "HLPdEf:hsXx")) != -1)
+               switch (ch) {
+               case 'H':
+                       ftsoptions &= ~FTS_LOGICAL;
+                       ftsoptions |= FTS_PHYSICAL|FTS_COMFOLLOW;
+                       break;
+               case 'L':
+                       ftsoptions &= ~(FTS_COMFOLLOW|FTS_PHYSICAL);
+                       ftsoptions |= FTS_LOGICAL;
+                       break;
+               case 'P':
+                       ftsoptions &= ~(FTS_COMFOLLOW|FTS_LOGICAL);
+                       ftsoptions |= FTS_PHYSICAL;
+                       break;
+               case 'd':
+                       isdepth = 1;
+                       break;
+               case 'E':
+                       regcomp_flags = REG_EXTENDED;
+                       break;
+               case 'f':
+                       *p++ = optarg;
+                       break;
+               case 'h':
+                       ftsoptions &= ~FTS_PHYSICAL;
+                       ftsoptions |= FTS_LOGICAL;
+                       break;
+               case 's':
+                       issort = 1;
+                       break;
+               case 'X':
+                       isxargs = 1;
+                       break;
+               case 'x':
+                       ftsoptions |= FTS_XDEV;
+                       break;
+               case '?':
+               default:
+                       break;
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       /*
+        * Find first option to delimit the file list.  The first argument
+        * that starts with a -, or is a ! or a ( must be interpreted as a
+        * part of the find expression, according to POSIX .2.
+        */
+       for (; *argv != NULL; *p++ = *argv++) {
+               if (argv[0][0] == '-')
+                       break;
+               if ((argv[0][0] == '!' || argv[0][0] == '(') &&
+                   argv[0][1] == '\0')
+                       break;
+       }
+
+       if (p == start)
+               usage();
+
+       *p = NULL;
+
+       if ((dotfd = open(".", O_RDONLY, 0)) == -1 ||
+           fcntl(dotfd, F_SETFD, FD_CLOEXEC) == -1)
+               err(1, ".");
+
+       exit(find_execute(find_formplan(argv), start));
+}
+
+static void
+usage(void)
+{
+
+       (void)fprintf(stderr,
+"usage: find [-H | -L | -P] [-dEhsXx] [-f file] file [file ...] [expression]\n");
+       exit(1);
+}
diff --git a/commands/find/misc.c b/commands/find/misc.c
new file mode 100644 (file)
index 0000000..8ba84c0
--- /dev/null
@@ -0,0 +1,151 @@
+/*     $NetBSD: misc.c,v 1.14 2006/10/11 19:51:10 apb Exp $    */
+
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)misc.c       8.2 (Berkeley) 4/1/94";
+#else
+__RCSID("$NetBSD: misc.c,v 1.14 2006/10/11 19:51:10 apb Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "find.h"
+
+/*
+ * brace_subst --
+ *     Replace occurrences of {} in orig with path, and place it in a malloced
+ *      area of memory set in store.
+ */
+void
+brace_subst(char *orig, char **store, char *path, int *len)
+{
+       int nlen, plen, rest;
+       char ch, *p, *ostore;
+
+       plen = strlen(path);
+       for (p = *store; (ch = *orig) != '\0'; ++orig)
+               if (ch == '{' && orig[1] == '}') {
+                       /* Length of string after the {}. */
+                       rest = strlen(&orig[2]);
+
+                       nlen = *len;
+                       while ((p - *store) + plen + rest + 1 > nlen)
+                               nlen *= 2;
+
+                       if (nlen > *len) {
+                               ostore = *store;
+                               if ((*store = realloc(ostore, nlen)) == NULL)
+                                       err(1, "realloc");
+                               *len = nlen;
+                               p += *store - ostore;   /* Relocate. */
+                       }
+                       memmove(p, path, plen);
+                       p += plen;
+                       ++orig;
+               } else
+                       *p++ = ch;
+       *p = '\0';
+}
+
+/*
+ * queryuser --
+ *     print a message to standard error and then read input from standard
+ *     input. If the input is 'y' then 1 is returned.
+ */
+int
+queryuser(char **argv)
+{
+       int ch, first, nl;
+
+       (void)fprintf(stderr, "\"%s", *argv);
+       while (*++argv)
+               (void)fprintf(stderr, " %s", *argv);
+       (void)fprintf(stderr, "\"? ");
+       (void)fflush(stderr);
+
+       first = ch = getchar();
+       for (nl = 0;;) {
+               if (ch == '\n') {
+                       nl = 1;
+                       break;
+               }
+               if (ch == EOF)
+                       break;
+               ch = getchar();
+       }
+
+       if (!nl) {
+               (void)fprintf(stderr, "\n");
+               (void)fflush(stderr);
+       }
+        return (first == 'y');
+}
+
+/*
+ * show_path --
+ *     called on SIGINFO
+ */
+/* ARGSUSED */
+void
+show_path(int sig)
+{
+       extern FTSENT *g_entry;
+       int errno_bak;
+
+       if (g_entry == NULL) {
+               /*
+                * not initialized yet.
+                * assumption: pointer assignment is atomic.
+                */
+               return;
+       }
+
+       errno_bak = errno;
+       write(STDERR_FILENO, "find path: ", 11);
+       write(STDERR_FILENO, g_entry->fts_path, g_entry->fts_pathlen);
+       write(STDERR_FILENO, "\n", 1);
+       errno = errno_bak;
+}
diff --git a/commands/find/operator.c b/commands/find/operator.c
new file mode 100644 (file)
index 0000000..19e8cb9
--- /dev/null
@@ -0,0 +1,272 @@
+/*     $NetBSD: operator.c,v 1.9 2006/10/11 19:51:10 apb Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)operator.c   8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: operator.c,v 1.9 2006/10/11 19:51:10 apb Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+
+#include "find.h"
+
+static PLAN *yanknode(PLAN **);
+static PLAN *yankexpr(PLAN **);
+
+/*
+ * yanknode --
+ *     destructively removes the top from the plan
+ */
+static PLAN *
+yanknode(PLAN **planp)         /* pointer to top of plan (modified) */
+{
+       PLAN *node;             /* top node removed from the plan */
+
+       if ((node = (*planp)) == NULL)
+               return (NULL);
+       (*planp) = (*planp)->next;
+       node->next = NULL;
+       return (node);
+}
+
+/*
+ * 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.
+ */
+static PLAN *
+yankexpr(PLAN **planp)         /* pointer to top of plan (modified) */
+{
+       PLAN *next;             /* temp node holding subexpression results */
+       PLAN *node;             /* pointer to returned node or expression */
+       PLAN *tail;             /* pointer to tail of subplan */
+       PLAN *subplan;          /* pointer to head of ( ) expression */
+
+       /* first pull the top node from the plan */
+       if ((node = yanknode(planp)) == NULL)
+               return (NULL);
+
+       /*
+        * If the node is an '(' then we recursively slurp up expressions
+        * until we find its associated ')'.  If it's a closing paren we
+        * just return it and unwind our recursion; all other nodes are
+        * complete expressions, so just return them.
+        */
+       if (node->type == N_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,
+                        * we just continue to add whatever we get to our
+                        * subplan.
+                        */
+                       if (next->type == N_CLOSEPAREN) {
+                               if (subplan == NULL)
+                                       errx(1, "(): empty inner expression");
+                               node->p_data[0] = subplan;
+                               node->type = N_EXPR;
+                               node->eval = f_expr;
+                               break;
+                       } else {
+                               if (subplan == NULL)
+                                       tail = subplan = next;
+                               else {
+                                       tail->next = next;
+                                       tail = next;
+                               }
+                               tail->next = NULL;
+                       }
+               }
+       return (node);
+}
+
+/*
+ * paren_squish --
+ *     replaces "parentheisized" plans in our search plan with "expr" nodes.
+ */
+PLAN *
+paren_squish(PLAN *plan)       /* plan with ( ) nodes */
+{
+       PLAN *expr;             /* pointer to next expression */
+       PLAN *tail;             /* pointer to tail of result plan */
+       PLAN *result;           /* pointer to head of result plan */
+
+       result = tail = NULL;
+
+       /*
+        * the basic idea is to have yankexpr do all our work and just
+        * collect it's results together.
+        */
+       while ((expr = yankexpr(&plan)) != NULL) {
+               /*
+                * if we find an unclaimed ')' it means there is a missing
+                * '(' someplace.
+                */
+               if (expr->type == N_CLOSEPAREN)
+                       errx(1, "): no beginning '('");
+
+               /* add the expression to our result plan */
+               if (result == NULL)
+                       tail = result = expr;
+               else {
+                       tail->next = expr;
+                       tail = expr;
+               }
+               tail->next = NULL;
+       }
+       return (result);
+}
+
+/*
+ * not_squish --
+ *     compresses "!" expressions in our search plan.
+ */
+PLAN *
+not_squish(PLAN *plan)         /* plan to process */
+{
+       PLAN *next;             /* next node being processed */
+       PLAN *node;             /* temporary node used in N_NOT processing */
+       PLAN *tail;             /* pointer to tail of result plan */
+       PLAN *result;           /* pointer to head of result plan */
+
+       tail = result = next = NULL;
+
+       while ((next = yanknode(&plan)) != NULL) {
+               /*
+                * if we encounter a ( expression ) then look for nots in
+                * the expr subplan.
+                */
+               if (next->type == N_EXPR)
+                       next->p_data[0] = not_squish(next->p_data[0]);
+
+               /*
+                * if we encounter a not, then snag the next node and place
+                * it in the not's subplan.  As an optimization we compress
+                * several not's to zero or one not.
+                */
+               if (next->type == N_NOT) {
+                       int notlevel = 1;
+
+                       node = yanknode(&plan);
+                       while (node != NULL && node->type == N_NOT) {
+                               ++notlevel;
+                               node = yanknode(&plan);
+                       }
+                       if (node == NULL)
+                               errx(1, "!: no following expression");
+                       if (node->type == N_OR)
+                               errx(1, "!: nothing between ! and -o");
+                       if (node->type == N_EXPR)
+                               node = not_squish(node);
+                       if (notlevel % 2 != 1)
+                               next = node;
+                       else
+                               next->p_data[0] = node;
+               }
+
+               /* add the node to our result plan */
+               if (result == NULL)
+                       tail = result = next;
+               else {
+                       tail->next = next;
+                       tail = next;
+               }
+               tail->next = NULL;
+       }
+       return (result);
+}
+
+/*
+ * or_squish --
+ *     compresses -o expressions in our search plan.
+ */
+PLAN *
+or_squish(PLAN *plan)          /* plan with ors to be squished */
+{
+       PLAN *next;             /* next node being processed */
+       PLAN *tail;             /* pointer to tail of result plan */
+       PLAN *result;           /* pointer to head of result plan */
+
+       tail = result = next = NULL;
+
+       while ((next = yanknode(&plan)) != NULL) {
+               /*
+                * if we encounter a ( expression ) then look for or's in
+                * the expr subplan.
+                */
+               if (next->type == N_EXPR)
+                       next->p_data[0] = or_squish(next->p_data[0]);
+
+               /* if we encounter a not then look for not's in the subplan */
+               if (next->type == N_NOT)
+                       next->p_data[0] = or_squish(next->p_data[0]);
+
+               /*
+                * if we encounter an or, then place our collected plan in the
+                * 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 (result == NULL)
+                               errx(1, "-o: no expression before -o");
+                       next->p_data[0] = result;
+                       next->p_data[1] = or_squish(plan);
+                       if (next->p_data[1] == NULL)
+                               errx(1, "-o: no expression after -o");
+                       return (next);
+               }
+
+               /* add the node to our result plan */
+               if (result == NULL)
+                       tail = result = next;
+               else {
+                       tail->next = next;
+                       tail = next;
+               }
+               tail->next = NULL;
+       }
+       return (result);
+}
diff --git a/commands/find/option.c b/commands/find/option.c
new file mode 100644 (file)
index 0000000..b827ab2
--- /dev/null
@@ -0,0 +1,157 @@
+/*     $NetBSD: option.c,v 1.26 2007/02/06 15:33:22 perry Exp $        */
+
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)option.c     8.2 (Berkeley) 4/16/94";
+#else
+__RCSID("$NetBSD: option.c,v 1.26 2007/02/06 15:33:22 perry Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "find.h"
+
+int typecompare(const void *, const void *);
+static OPTION *option(char *);
+
+/* NB: the following table must be sorted lexically. */
+static OPTION const options[] = {
+       { "!",          N_NOT,          c_not,          0 },
+       { "(",          N_OPENPAREN,    c_openparen,    0 },
+       { ")",          N_CLOSEPAREN,   c_closeparen,   0 },
+       { "-a",         N_AND,          c_null,         0 },
+       { "-amin",      N_AMIN,         c_amin,         1 },
+       { "-and",       N_AND,          c_null,         0 },
+       { "-anewer",    N_ANEWER,       c_anewer,       1 },
+       { "-atime",     N_ATIME,        c_atime,        1 },
+       { "-cmin",      N_CMIN,         c_cmin,         1 },
+       { "-cnewer",    N_CNEWER,       c_cnewer,       1 },
+       { "-ctime",     N_CTIME,        c_ctime,        1 },
+       { "-delete",    N_DELETE,       c_delete,       0 },
+       { "-depth",     N_DEPTH,        c_depth,        0 },
+       { "-empty",     N_EMPTY,        c_empty,        0 },
+       { "-exec",      N_EXEC,         c_exec,         1 },
+       { "-execdir",   N_EXECDIR,      c_execdir,      1 },
+       { "-exit",      N_EXIT,         c_exit,         0 },
+       { "-false",     N_FALSE,        c_false,        0 },
+       { "-flags",     N_FLAGS,        c_flags,        1 },
+       { "-follow",    N_FOLLOW,       c_follow,       0 },
+       { "-fprint",    N_FPRINT,       c_fprint,       1 },
+       { "-fstype",    N_FSTYPE,       c_fstype,       1 },
+       { "-group",     N_GROUP,        c_group,        1 },
+       { "-iname",     N_INAME,        c_iname,        1 },
+       { "-inum",      N_INUM,         c_inum,         1 },
+       { "-iregex",    N_IREGEX,       c_iregex,       1 },
+       { "-links",     N_LINKS,        c_links,        1 },
+       { "-ls",        N_LS,           c_ls,           0 },
+       { "-maxdepth",  N_MAXDEPTH,     c_maxdepth,     1 },
+       { "-mindepth",  N_MINDEPTH,     c_mindepth,     1 },
+       { "-mmin",      N_MMIN,         c_mmin,         1 },
+       { "-mtime",     N_MTIME,        c_mtime,        1 },
+       { "-name",      N_NAME,         c_name,         1 },
+       { "-newer",     N_NEWER,        c_newer,        1 },
+       { "-nogroup",   N_NOGROUP,      c_nogroup,      0 },
+       { "-nouser",    N_NOUSER,       c_nouser,       0 },
+       { "-o",         N_OR,           c_or,           0 },
+       { "-ok",        N_OK,           c_exec,         1 },
+       { "-or",        N_OR,           c_or,           0 },
+       { "-path",      N_PATH,         c_path,         1 },
+       { "-perm",      N_PERM,         c_perm,         1 },
+       { "-print",     N_PRINT,        c_print,        0 },
+       { "-print0",    N_PRINT0,       c_print0,       0 },
+       { "-printx",    N_PRINTX,       c_printx,       0 },
+       { "-prune",     N_PRUNE,        c_prune,        0 },
+       { "-regex",     N_REGEX,        c_regex,        1 },
+       { "-rm",        N_DELETE,       c_delete,       0 },
+       { "-size",      N_SIZE,         c_size,         1 },
+       { "-type",      N_TYPE,         c_type,         1 },
+       { "-user",      N_USER,         c_user,         1 },
+       { "-xdev",      N_XDEV,         c_xdev,         0 }
+};
+
+/*
+ * find_create --
+ *     create a node corresponding to a command line argument.
+ *
+ * TODO:
+ *     add create/process function pointers to node, so we can skip
+ *     this switch stuff.
+ */
+PLAN *
+find_create(char ***argvp)
+{
+       OPTION *p;
+       PLAN *new;
+       char **argv;
+
+       argv = *argvp;
+
+       if ((p = option(*argv)) == NULL)
+               errx(1, "%s: unknown option", *argv);
+       ++argv;
+       if (p->arg && !*argv)
+               errx(1, "%s: requires additional arguments", *--argv);
+
+       new = (p->create)(&argv, p->token == N_OK);
+
+       *argvp = argv;
+       return (new);
+}
+
+static OPTION *
+option(char *name)
+{
+       OPTION tmp;
+
+       tmp.name = name;
+       return ((OPTION *)bsearch(&tmp, options,
+           sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare));
+}
+
+int
+typecompare(const void *a, const void *b)
+{
+
+       return (strcmp(((const OPTION *)a)->name, ((const OPTION *)b)->name));
+}