summaryrefslogtreecommitdiff
path: root/usr.bin/find
diff options
context:
space:
mode:
authorKiyoshi Aman <kiyoshi.aman+adelie@gmail.com>2019-02-01 22:55:37 +0000
committerKiyoshi Aman <kiyoshi.aman+adelie@gmail.com>2019-02-03 18:22:05 -0600
commit5b57d28ffb6e1ef86b50f7d05d977826eae89bfe (patch)
tree154a22fe556b49e6927197336f8bf91b12eacd5e /usr.bin/find
downloaduserland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.gz
userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.bz2
userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.xz
userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.zip
initial population
Diffstat (limited to 'usr.bin/find')
-rw-r--r--usr.bin/find/extern.h102
-rw-r--r--usr.bin/find/find.1974
-rw-r--r--usr.bin/find/find.c306
-rw-r--r--usr.bin/find/find.h138
-rw-r--r--usr.bin/find/function.c2025
-rw-r--r--usr.bin/find/ls.c124
-rw-r--r--usr.bin/find/main.c164
-rw-r--r--usr.bin/find/misc.c151
-rw-r--r--usr.bin/find/operator.c272
-rw-r--r--usr.bin/find/option.c194
10 files changed, 4450 insertions, 0 deletions
diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h
new file mode 100644
index 0000000..5c94035
--- /dev/null
+++ b/usr.bin/find/extern.h
@@ -0,0 +1,102 @@
+/* $NetBSD: extern.h,v 1.29 2016/06/13 00:04:40 pgoyette 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, char *);
+PLAN *c_anewer(char ***, int, char *);
+PLAN *c_asince(char ***, int, char *);
+PLAN *c_atime(char ***, int, char *);
+PLAN *c_cmin(char ***, int, char *);
+PLAN *c_cnewer(char ***, int, char *);
+PLAN *c_csince(char ***, int, char *);
+PLAN *c_ctime(char ***, int, char *);
+PLAN *c_delete(char ***, int, char *);
+PLAN *c_depth(char ***, int, char *);
+PLAN *c_empty(char ***, int, char *);
+PLAN *c_exec(char ***, int, char *);
+PLAN *c_execdir(char ***, int, char *);
+PLAN *c_exit(char ***, int, char *);
+PLAN *c_false(char ***, int, char *);
+PLAN *c_flags(char ***, int, char *);
+PLAN *c_follow(char ***, int, char *);
+PLAN *c_fprint(char ***, int, char *);
+PLAN *c_fstype(char ***, int, char *);
+PLAN *c_group(char ***, int, char *);
+PLAN *c_iname(char ***, int, char *);
+PLAN *c_inum(char ***, int, char *);
+PLAN *c_iregex(char ***, int, char *);
+PLAN *c_links(char ***, int, char *);
+PLAN *c_ls(char ***, int, char *);
+PLAN *c_maxdepth(char ***, int, char *);
+PLAN *c_mindepth(char ***, int, char *);
+PLAN *c_mmin(char ***, int, char *);
+PLAN *c_mtime(char ***, int, char *);
+PLAN *c_name(char ***, int, char *);
+PLAN *c_newer(char ***, int, char *);
+PLAN *c_nogroup(char ***, int, char *);
+PLAN *c_nouser(char ***, int, char *);
+PLAN *c_path(char ***, int, char *);
+PLAN *c_perm(char ***, int, char *);
+PLAN *c_print(char ***, int, char *);
+PLAN *c_print0(char ***, int, char *);
+PLAN *c_printx(char ***, int, char *);
+PLAN *c_prune(char ***, int, char *);
+PLAN *c_regex(char ***, int, char *);
+PLAN *c_since(char ***, int, char *);
+PLAN *c_size(char ***, int, char *);
+PLAN *c_type(char ***, int, char *);
+PLAN *c_user(char ***, int, char *);
+PLAN *c_xdev(char ***, int, char *);
+PLAN *c_openparen(char ***, int, char *);
+PLAN *c_closeparen(char ***, int, char *);
+PLAN *c_not(char ***, int, char *);
+PLAN *c_or(char ***, int, char *);
+PLAN *c_null(char ***, int, char *);
+
+extern int ftsoptions, isdeprecated, isdepth, isoutput, issort, isxargs,
+ regcomp_flags;
diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1
new file mode 100644
index 0000000..60ce94a
--- /dev/null
+++ b/usr.bin/find/find.1
@@ -0,0 +1,974 @@
+.\" $NetBSD: find.1,v 1.89 2017/07/03 21:34:57 wiz 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 June 13, 2016
+.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:
+.Bl -tag -width Ds
+.It Fl H
+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
+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
+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
+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
+.Em not
+a breadth-first traversal.
+.It Fl E
+Causes
+.Ar regexp
+arguments to primaries to be interpreted as extended regular
+expressions (see
+.Xr re_format 7 ) .
+.It Fl f
+Specifies a file hierarchy for
+.Nm
+to traverse.
+File hierarchies may also be specified as the operands immediately
+following the options.
+.It Fl h
+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
+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
+Modifies the output 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
+.Xr xargs 1 ,
+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
+.Xr xargs 1
+can accept.
+.It Fl x
+Restricts the search to the file system containing the
+directory specified.
+Does not list mount points to other file systems.
+.El
+.Sh PRIMARIES
+All primaries which take a numeric argument of
+.Ar n
+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 Ar n ,
+a preceding minus sign means
+.Dq less than Ar n ,
+and neither means
+.Dq exactly Ar n .
+(The argument specified for the
+.Ic -user
+and
+.Ic -group
+primaries
+are similarly treated if the value is numeric and does not correspond to a
+valid user or group name.)
+.Pp
+For primaries which take a
+.Ar timestamp
+argument, the argument must be valid input to
+.Xr parsedate 3 .
+If the argument contains multiple words, enclose the argument in quotes.
+.Pp
+.Bl -tag -width Ds -compact
+.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.
+.Pp
+.It Ic -anewer Ar file
+True if the current file has a more recent last access time than
+.Ar file .
+.Pp
+.It Ic -asince Ar timestamp
+True if the file last access time is greater than the specified
+.Ar timestamp .
+.Pp
+.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.
+.Pp
+.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.
+.Pp
+.It Ic -cnewer Ar file
+True if the current file has a more recent last change time than
+.Ar file .
+.Pp
+.It Ic -csince Ar timestamp
+True if the file last status change time is greater than the specified
+.Ar timestamp .
+.Pp
+.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.
+.Pp
+.It Ic -delete
+Delete found files, symbolic links, and directories.
+Always returns true.
+This executes from the current working directory as
+.Nm
+recurses down the tree.
+To avoid deleting unexpected files, it will ignore any filenames that
+.Xr fts 3
+returns that contain a
+.Dq /
+.Xr ( fts 3
+should not return such pathnames).
+Depth-first traversal processing is implied by this option.
+This primary can also be invoked as
+.Ic -rm .
+.Pp
+.It Ic -empty
+True if the current file or directory is empty.
+.Pp
+.It Ic -exec Ar utility Oo argument ... Oc Ic \&;
+.It Ic -exec Ar utility Oo argument ... Oc Ic {} Ic \&+
+Execute the specified
+.Ar utility
+with the specified arguments.
+.Pp
+The list of arguments for
+.Ar utility
+is terminated by a lone semicolon
+.Dq Ic \&;
+or plus
+.Dq Ic \&+
+character as a separate parameter.
+The command specified by
+.Ar utility
+will be executed with its current working directory being the directory
+from which
+.Nm
+was executed.
+.Pp
+If the list of arguments is terminated by a semicolon
+.Pq Dq Ic \&; ,
+then
+.Ar utility
+is invoked once per pathname.
+If
+the string
+.Dq Ic {}
+appears one or more times in the utility name or arguments,
+then it is replaced by the pathname of the current file
+(but it need not appear, in which case the pathname
+will not be passed to
+.Ar utility ) .
+The semicolon-terminated form of the
+.Ic -exec
+primary returns true if and only if
+.Ar utility
+exits with a zero exit status.
+Note that the semicolon will have to be escaped on the shell command line
+in order to be passed as a parameter.
+.Pp
+If the list of arguments is terminated by a plus sign
+.Pq Dq Ic \&+ ,
+then 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 .
+In this case the string
+.Dq Ic {}
+must appear, and must appear as the last item in the argument list,
+just before the
+.Dq Ic \&+
+parameter, and is replaced by the pathnames of the current set of files.
+Each set is limited to no more than 5,000 pathnames,
+and is also limited such that the total number of bytes in the argument
+list does not exceed
+.Dv ARG_MAX .
+The plus-terminated form of the
+.Ic -exec
+primary always returns true.
+If the plus-terminated form of the
+.Ic -exec
+primary results in any invocation of
+.Ar utility
+exiting with non-zero exit status, then
+.Nm
+will eventually exit with non-zero status as well,
+but this does not cause
+.Nm
+to exit early.
+.Pp
+.It Ic -execdir Ar utility Oo argument ... Oc Ic \&;
+The
+.Ic -execdir
+primary is similar to the semicolon-terminated
+.Pq Dq Ic \&;
+variant of the
+.Ic -exec
+primary, with the exception that
+.Ar utility
+will be executed from the directory that holds
+the current file.
+Only the base filename is substituted for the string
+.Dq Ic {} .
+Set aggregation
+.Pq Do Ic \&+ Dc termination
+is not supported.
+.Pp
+.It Ic -exit Op Ar status
+This primary causes
+.Nm
+to stop traversing the file system and exit immediately,
+with the specified numeric exit status.
+If the
+.Ar status
+value is not specified, then
+.Nm
+will exit with status zero.
+Note that any preceding primaries will be evaluated and acted upon
+before exiting.
+.Pp
+.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).
+.Pp
+.It Ic -flags Oo Fl Oc Ns Ar flags
+If
+.Ar flags
+are preceded by a dash
+.Pq Dq Ic \- ,
+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.)
+.Pp
+.It Ic -follow
+Follow symbolic links.
+.Pp
+.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.
+.Pp
+.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 file systems
+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.
+.Pp
+.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 (and considered a numeric argument).
+.Pp
+.It Ic -iname Ar pattern
+True if the last component of the pathname being examined matches
+.Ar pattern
+in a case-insensitive manner.
+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 .
+.Pp
+.It Ic -inum Ar n
+True if the file has inode number
+.Ar n .
+.Pp
+.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.
+.Pp
+.It Ic -links Ar n
+True if the file has
+.Ar n
+links.
+.Pp
+.It Ic -rm
+This primary is an alias for
+.Ic -delete .
+.Pp
+.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 -> .
+The format is identical to that produced by
+.Dq ls -dgils .
+.Pp
+.It Ic -maxdepth Ar depth
+True if the current search depth is less than or equal to what is specified in
+.Ar depth .
+.Pp
+.It Ic -mindepth Ar depth
+True if the current search depth is at least what is specified in
+.Ar depth .
+.Pp
+.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.
+.Pp
+.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.
+.Pp
+.It Ic -ok Ar utility Oo argument ... Oc Ic \&;
+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
+.Ar 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
+.Ic -ok
+primary evaluates to false.
+Set aggregation
+.Pq Do \&+ Dc termination
+is not supported.
+.Pp
+.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 \&* ,
+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 .
+.Pp
+.It Ic -newer Ar file
+True if the current file has a more recent last modification time than
+.Ar file .
+.Pp
+.It Ic -newerXY Ar reference
+For compatibility with Gnu findutils.
+.Bl -column -offset indent ".Sy findutils" ".Sy equivalent"
+.It Sy findutils Ta Sy find
+.It Sy option Ta Sy equivalent
+.It -neweraa Ta -anewer
+.It -newerat Ta -asince
+.It -newercc Ta -cnewer
+.It -newerct Ta -csince
+.It -newermm Ta -newer
+.It -newermt Ta -since
+.El
+.Pp
+Other option variants from findutils are not implemented.
+.Pp
+.It Ic -nouser
+True if the file belongs to an unknown user.
+.Pp
+.It Ic -nogroup
+True if the file belongs to an unknown group.
+.Pp
+.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.
+.Pp
+.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 Ic \- ,
+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 Ic \- .
+.Pp
+.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 -delete ,
+.Ic -exec ,
+.Ic -execdir ,
+.Ic -exit ,
+.Ic -fprint ,
+.Ic -ls ,
+.Ic -ok ,
+.Ic -print0 ,
+.Ic -printx ,
+nor
+.Ic -rm
+is specified, the given expression shall be effectively replaced by
+.Cm \&( Ns Ar given\& expression Ns Cm \&)
+.Ic -print .
+.Pp
+.It Ic -print0
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output, followed
+by a NUL character.
+.Pp
+.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
+can safely be used as input to
+.Xr xargs 1 .
+.Pp
+.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.
+.Pp
+.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.
+.Pp
+.It Ic -since Ar timestamp
+True if the file last modification time is more recent than
+.Ar timestamp .
+.Pp
+.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 Ic c ,
+then the primary is true if the file's size is
+.Ar n
+bytes.
+.Pp
+.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 username
+True if the file belongs to the user
+.Ar username .
+If
+.Ar username
+is numeric and there is no such user on the system, then
+.Ar username
+is treated as a user id (and considered a numeric argument).
+.Pp
+.It Ic -xdev
+This primary always evaluates to true.
+It causes find not to descend past directories that have a different
+device ID
+.Va ( st_dev ,
+see
+.Xr stat 2
+S5.6.2 [POSIX.1]).
+.El
+.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.
+.It Cm \&! Ar expression
+This is the unary
+.Tn NOT
+operator.
+It evaluates to true if the expression is false.
+.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.
+.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 invocations of
+.Dq Ic -exec Ar ... Ic \&+
+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 . \-type f \-mmin \-30 \-print \-or \-mindepth 1 \-prune"
+Print out a list of all the files in the current directory that are
+newer than 30 minutes.
+.It Li "find . \-type f \-atime +10 \-mindepth 2 \-print"
+Print out a list of all the files in any sub-directories that have not
+been accessed in the past ten days.
+.It Li "find . \-mtime +90 \-exec rm \-i {} + \-or \-mindepth 1 \-prune"
+Interactively remove all of the files in the current directory that have
+not been modified in 90 days.
+.It Li "find . \-type f \-mtime +90 \-ok mv {} {}.old \e;"
+Interactively rename all of the files in the current directory and all
+sub-directories that have not been modified in 90 days.
+.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.
+.It Li "find . \-type f \-exec sh \-c 'file=\*[q]$1\*[q]; ...;' - {} \;"
+Perform an arbitrarily complex shell command for every file.
+.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 re_format 7 ,
+.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 -asince ,
+.Ic -cmin ,
+.Ic -cnewer ,
+.Ic -csince ,
+.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 ,
+.Ic -rm ,
+and
+.Ic -since
+primaries are extensions to
+.St -p1003.2 .
+.Pp
+Historically, the
+.Fl d ,
+.Fl h ,
+and
+.Fl x
+options were implemented using the primaries
+.Dq Ic -depth ,
+.Dq Ic -follow ,
+and
+.Dq Ic -xdev .
+These primaries always evaluated to true, and always
+took effect when the
+.Ar expression
+was parsed, before the file system traversal began.
+As a result, some legal expressions could be confusing.
+For example, in the expression
+.Dq Ic -print Ic -or Ic -depth ,
+.Ic -print
+always evaluates to true, so the standard meaning of
+.Ic -or
+implies that
+.Ic -depth
+would never be evaluated, but that is not what happens;
+in fact,
+.Ic -depth
+takes effect immediately, without testing whether
+.Ic -print
+returns true or false.
+.Pp
+Historically, the operator
+.Dq Ic -or
+was implemented as
+.Dq Ic -o ,
+and the operator
+.Dq Ic -and
+was implemented as
+.Dq Ic -a .
+.Pp
+Historic implementations of the
+.Dq Ic -exec
+and
+.Dq Ic -ok
+primaries did not replace the string
+.Dq Ic {}
+in the utility name or the
+utility arguments if it did not appear as a separate argument.
+This version replaces it no matter where in the utility name or arguments
+it appears.
+.Pp
+Support for
+.Dq Ic -exec Ar ... Ic \&+
+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 file system
+tree traversal options to be changed.
+.Sh HISTORY
+A much simpler
+.Nm
+command appeared in First Edition AT&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/usr.bin/find/find.c b/usr.bin/find/find.c
new file mode 100644
index 0000000..473244b
--- /dev/null
+++ b/usr.bin/find/find.c
@@ -0,0 +1,306 @@
+/* $NetBSD: find.c,v 1.30 2016/06/13 00:04:40 pgoyette 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.30 2016/06/13 00:04:40 pgoyette 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 <stdbool.h>
+#include <unistd.h>
+
+#include "find.h"
+
+static int ftscompare(const FTSENT **, const FTSENT **);
+
+/*
+ * 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, NULL);
+ tail = plan = new;
+ } else {
+ new = c_openparen(NULL, 0, NULL);
+ new->next = plan;
+ plan = new;
+ new = c_closeparen(NULL, 0, NULL);
+ tail->next = new;
+ tail = new;
+ new = c_print(NULL, 0, NULL);
+ 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 sigset_t ss;
+static bool notty;
+
+static __inline void
+sig_init(void)
+{
+ struct sigaction sa;
+ notty = !(isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) ||
+ isatty(STDERR_FILENO));
+ if (notty)
+ return;
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGINFO); /* block SIGINFO */
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = show_path;
+ (void)sigaction(SIGINFO, &sa, NULL);
+
+}
+
+static __inline void
+sig_lock(sigset_t *s)
+{
+ if (notty)
+ return;
+ sigprocmask(SIG_BLOCK, &ss, s);
+}
+
+static __inline void
+sig_unlock(const sigset_t *s)
+{
+ if (notty)
+ return;
+ 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_init();
+ sig_lock(&s);
+ for (rval = 0; cval && (g_entry = fts_read(tree)) != NULL;) {
+ 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:
+ sig_unlock(&s);
+ (void)fflush(stdout);
+ warnx("%s: %s",
+ g_entry->fts_path, strerror(g_entry->fts_errno));
+ rval = 1;
+ sig_lock(&s);
+ continue;
+ }
+#define BADCH " \t\n\\'\""
+ if (isxargs && strpbrk(g_entry->fts_path, BADCH)) {
+ sig_unlock(&s);
+ (void)fflush(stdout);
+ warnx("%s: illegal path", g_entry->fts_path);
+ rval = 1;
+ sig_lock(&s);
+ 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.
+ */
+ sig_unlock(&s);
+ for (p = plan; p && (p->eval)(p, g_entry); p = p->next)
+ if (p->type == N_EXIT) {
+ rval = p->exit_val;
+ cval = 0;
+ }
+ sig_lock(&s);
+ }
+
+ sig_unlock(&s);
+ if (g_entry == NULL && 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 *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/usr.bin/find/find.h b/usr.bin/find/find.h
new file mode 100644
index 0000000..912e60d
--- /dev/null
+++ b/usr.bin/find/find.h
@@ -0,0 +1,138 @@
+/* $NetBSD: find.h,v 1.26 2016/06/13 00:04:40 pgoyette 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>
+#include <time.h>
+
+/* node type */
+enum ntype {
+ N_AND = 1, /* must start > 0 */
+ N_AMIN, N_ANEWER, N_ASINCE, N_ATIME, N_CLOSEPAREN, N_CMIN, N_CNEWER,
+ N_CSINCE, 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_SINCE, 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 */
+ struct timespec _ts_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 ts_data p_un._ts_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, char *); /* create function */
+ int arg; /* function needs arg */
+} OPTION;
+
+#include "extern.h"
diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c
new file mode 100644
index 0000000..02540f2
--- /dev/null
+++ b/usr.bin/find/function.c
@@ -0,0 +1,2025 @@
+/* $NetBSD: function.c,v 1.77 2018/09/04 15:16:15 kre 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.77 2018/09/04 15:16:15 kre 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_asince(PLAN *, FTSENT *);
+ int f_atime(PLAN *, FTSENT *);
+ int f_cmin(PLAN *, FTSENT *);
+ int f_cnewer(PLAN *, FTSENT *);
+ int f_csince(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 *);
+/*
+ * Unimplemented Gnu findutils options
+ *
+ int f_newerBB(PLAN *, FTSENT *);
+ int f_newerBa(PLAN *, FTSENT *);
+ int f_newerBc(PLAN *, FTSENT *);
+ int f_newerBm(PLAN *, FTSENT *);
+ int f_newerBt(PLAN *, FTSENT *);
+ int f_neweraB(PLAN *, FTSENT *);
+ int f_newerac(PLAN *, FTSENT *);
+ int f_neweram(PLAN *, FTSENT *);
+ int f_newerca(PLAN *, FTSENT *);
+ int f_newercm(PLAN *, FTSENT *);
+ int f_newercB(PLAN *, FTSENT *);
+ int f_newermB(PLAN *, FTSENT *);
+ int f_newerma(PLAN *, FTSENT *);
+ int f_newermc(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_since(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);
+}
+
+/*
+ * find_parsedate --
+ *
+ * Validate the timestamp argument or report an error
+ */
+static time_t
+find_parsedate(PLAN *plan, const char *option, const char *vp)
+{
+ time_t timestamp;
+
+ errno = 0;
+ timestamp = parsedate(vp, NULL, NULL);
+ if (timestamp == -1 && errno != 0)
+ errx(1, "%s: %s: invalid timestamp value", option, vp);
+ return timestamp;
+}
+
+/*
+ * 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_AMIN, f_amin);
+ new->t_data = find_parsenum(new, opt, 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 *plan, FTSENT *entry)
+{
+
+ return timespeccmp(&entry->fts_statp->st_atim, &plan->ts_data, >);
+}
+
+PLAN *
+c_anewer(char ***argvp, int isok, char *opt)
+{
+ char *filename = **argvp;
+ PLAN *new;
+ struct stat sb;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ if (stat(filename, &sb))
+ err(1, "%s: %s", opt, filename);
+ new = palloc(N_ANEWER, f_anewer);
+ new->ts_data = sb.st_atim;
+ return (new);
+}
+
+/*
+ * -asince "timestamp" functions --
+ *
+ * True if the file access time is greater than the timestamp value
+ */
+int
+f_asince(PLAN *plan, FTSENT *entry)
+{
+ COMPARE(entry->fts_statp->st_atime, plan->t_data);
+}
+
+PLAN *
+c_asince(char ***argvp, int isok, char *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_ASINCE, f_asince);
+ new->t_data = find_parsedate(new, opt, arg);
+ new->flags = F_GREATER;
+ 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_ATIME, f_atime);
+ new->t_data = find_parsenum(new, opt, 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_CMIN, f_cmin);
+ new->t_data = find_parsenum(new, opt, 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 timespeccmp(&entry->fts_statp->st_ctim, &plan->ts_data, >);
+}
+
+PLAN *
+c_cnewer(char ***argvp, int isok, char *opt)
+{
+ char *filename = **argvp;
+ PLAN *new;
+ struct stat sb;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ if (stat(filename, &sb))
+ err(1, "%s: %s ", opt, filename);
+ new = palloc(N_CNEWER, f_cnewer);
+ new->ts_data = sb.st_ctim;
+ return (new);
+}
+
+/*
+ * -csince "timestamp" functions --
+ *
+ * True if the file status change time is greater than the timestamp value
+ */
+int
+f_csince(PLAN *plan, FTSENT *entry)
+{
+ COMPARE(entry->fts_statp->st_ctime, plan->t_data);
+}
+
+PLAN *
+c_csince(char ***argvp, int isok, char *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_CSINCE, f_csince);
+ new->t_data = find_parsedate(new, opt, arg);
+ new->flags = F_GREATER;
+ 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_CTIME, f_ctime);
+ new->t_data = find_parsenum(new, opt, arg, NULL);
+ TIME_CORRECT(new, N_CTIME);
+ return (new);
+}
+
+/*
+ * -delete functions --
+ *
+ * Always true. 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 (entry->fts_level > 0 && 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, char *opt)
+{
+
+ 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, char *opt)
+{
+ 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)
+ return (0);
+ 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, char *opt)
+{
+ 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)
+{
+ size_t cnt;
+ int 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, char *opt)
+{
+ PLAN *new; /* node returned */
+ size_t cnt;
+ int 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 exactly equal to ";", or an
+ * arg exactly equal to "+" following an arg exactly equal to
+ * "{}".
+ */
+ for (ap = argv = *argvp, brace = 0;; ++ap) {
+ if (!*ap)
+ errx(1, "%s: no terminating \";\" or \"+\"", opt);
+ lastbrace = brace;
+ brace = 0;
+ 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, "%s: terminating \"+\" not permitted.", opt);
+
+ if (new->flags & F_PLUSSET) {
+ size_t c, bufsize;
+
+ cnt = ap - *argvp - 1; /* units are words */
+ new->ep_maxargs = ARG_MAX / (sizeof (char *) + 16);
+ if (new->ep_maxargs > 5000)
+ new->ep_maxargs = 5000;
+ new->e_argv = emalloc((cnt + new->ep_maxargs)
+ * sizeof(*new->e_argv));
+
+ /* 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.
+ */
+#define MAXARG (ARG_MAX - 4 * 1024)
+ for (argv = *argvp, c = 0, cnt = 0;
+ argv < ap;
+ ++argv, ++cnt) {
+ c += strlen(*argv) + 1;
+ if (c >= MAXARG)
+ errx(1, "Arguments too long");
+ new->e_argv[cnt] = *argv;
+ }
+ if (c + new->ep_maxargs * sizeof (char *) >= MAXARG)
+ errx(1, "Arguments too long");
+ bufsize = MAXARG - c - new->ep_maxargs * sizeof (char *);
+
+ /*
+ * Allocate, and then initialize current, base, and
+ * end pointers.
+ */
+ new->ep_p = new->ep_bbp = emalloc(bufsize + 1);
+ new->ep_ebp = new->ep_bbp + bufsize - 1;
+ new->ep_rval = 0;
+ } else { /* !F_PLUSSET */
+ cnt = ap - *argvp + 1;
+ new->e_argv = emalloc(cnt * sizeof(*new->e_argv));
+ new->e_orig = emalloc(cnt * sizeof(*new->e_orig));
+ new->e_len = emalloc(cnt * sizeof(*new->e_len));
+
+ 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(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)
+{
+ size_t 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, char *opt)
+{
+ PLAN *new; /* node returned */
+ size_t 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, "%s: no terminating \";\"", opt);
+ if (**ap == ';')
+ break;
+ }
+
+ cnt = ap - *argvp + 1;
+ new->e_argv = emalloc(cnt * sizeof(*new->e_argv));
+ new->e_orig = emalloc(cnt * sizeof(*new->e_orig));
+ new->e_len = emalloc(cnt * sizeof(*new->e_len));
+
+ 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(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 *opt)
+{
+ 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, opt, 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, char *opt)
+{
+ 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 *opt)
+{
+ 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, "%s: %s: illegal flags string", opt, 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, char *opt)
+{
+ 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, char *opt)
+{
+ PLAN *new;
+
+ isoutput = 1; /* do not assume -print */
+
+ new = palloc(N_FPRINT, f_fprint);
+
+ if (NULL == (new->fprint_file = fopen(**argvp, "w")))
+ err(1, "%s: %s: cannot create file", opt, **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 *opt)
+{
+ 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)
+{
+
+ COMPARE(entry->fts_statp->st_gid, plan->g_data);
+}
+
+PLAN *
+c_group(char ***argvp, int isok, char *opt)
+{
+ char *gname = **argvp;
+ PLAN *new;
+ struct group *g;
+ gid_t gid;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_GROUP, f_group);
+ g = getgrnam(gname);
+ if (g == NULL) {
+ if (atoi(gname) == 0 && gname[0] != '0' &&
+ strcmp(gname, "+0") && strcmp(gname, "-0"))
+ errx(1, "%s: %s: no such group", opt, gname);
+ gid = find_parsenum(new, "-group", gname, NULL);
+
+ } else {
+ new->flags = F_EQUAL;
+ gid = g->gr_gid;
+ }
+
+ 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_INUM, f_inum);
+ new->i_data = find_parsenum(new, opt, 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_LINKS, f_links);
+ new->l_data = (nlink_t)find_parsenum(new, opt, 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, char *opt)
+{
+
+ 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 *opt)
+{
+ 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 *opt)
+{
+ 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_MMIN, f_mmin);
+ new->t_data = find_parsenum(new, opt, 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 *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_MTIME, f_mtime);
+ new->t_data = find_parsenum(new, opt, 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 *opt)
+{
+ 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 *opt)
+{
+ 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 timespeccmp(&entry->fts_statp->st_mtim, &plan->ts_data, >);
+}
+
+PLAN *
+c_newer(char ***argvp, int isok, char *opt)
+{
+ char *filename = **argvp;
+ PLAN *new;
+ struct stat sb;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ if (stat(filename, &sb))
+ err(1, "%s: %s", opt, filename);
+ new = palloc(N_NEWER, f_newer);
+ new->ts_data = sb.st_mtim;
+ 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, char *opt)
+{
+ 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, char *opt)
+{
+ 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 *opt)
+{
+ 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 *opt)
+{
+ 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, "%s: Cannot set file mode `%s'", opt, 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, char *opt)
+{
+
+ isoutput = 1;
+
+ return (palloc(N_PRINT, f_print));
+}
+
+PLAN *
+c_print0(char ***argvp, int isok, char *opt)
+{
+
+ isoutput = 1;
+
+ return (palloc(N_PRINT0, f_print0));
+}
+
+PLAN *
+c_printx(char ***argvp, int isok, char *opt)
+{
+
+ 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, char *opt)
+{
+
+ 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, char *opt)
+{
+
+ return (c_regex_common(argvp, isok, N_REGEX, false));
+}
+
+PLAN *
+c_iregex(char ***argvp, int isok, char *opt)
+{
+
+ return (c_regex_common(argvp, isok, N_IREGEX, true));
+}
+
+/*
+ * -since "timestamp" functions --
+ *
+ * True if the file modification time is greater than the timestamp value
+ */
+int
+f_since(PLAN *plan, FTSENT *entry)
+{
+ COMPARE(entry->fts_statp->st_mtime, plan->t_data);
+}
+
+PLAN *
+c_since(char ***argvp, int isok, char *opt)
+{
+ char *arg = **argvp;
+ PLAN *new;
+
+ (*argvp)++;
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_SINCE, f_since);
+ new->t_data = find_parsedate(new, opt, arg);
+ new->flags = F_GREATER;
+ return (new);
+}
+
+/*
+ * -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 *opt)
+{
+ 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, opt, 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 *opt)
+{
+ 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, "%s: %s: unknown type", opt, 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 *opt)
+{
+ 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, "%s: %s: no such user", opt, username);
+ uid = find_parsenum(new, opt, 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, char *opt)
+{
+ 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, char *opt)
+{
+
+ return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
+}
+
+PLAN *
+c_closeparen(char ***argvp, int isok, char *opt)
+{
+
+ 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, char *opt)
+{
+
+ 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, char *opt)
+{
+
+ return (palloc(N_OR, f_or));
+}
+
+PLAN *
+c_null(char ***argvp, int isok, char *opt)
+{
+
+ 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/usr.bin/find/ls.c b/usr.bin/find/ls.c
new file mode 100644
index 0000000..23b7cfe
--- /dev/null
+++ b/usr.bin/find/ls.c
@@ -0,0 +1,124 @@
+/* $NetBSD: ls.c,v 1.21 2011/08/31 16:24:57 plunky 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.21 2011/08/31 16:24:57 plunky 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("%3llu,%5llu ",
+ (unsigned long long)major(sb->st_rdev),
+ (unsigned long long)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(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/usr.bin/find/main.c b/usr.bin/find/main.c
new file mode 100644
index 0000000..5aab177
--- /dev/null
+++ b/usr.bin/find/main.c
@@ -0,0 +1,164 @@
+/* $NetBSD: main.c,v 1.31 2013/01/24 17:50:08 christos 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.31 2013/01/24 17:50:08 christos 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 <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 */
+
+__dead static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ char **p, **start;
+ int ch;
+
+ (void)time(&now); /* initialize the time-of-day */
+ (void)setlocale(LC_ALL, "");
+
+ /* 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 | O_CLOEXEC, 0)) == -1)
+ err(1, ".");
+
+ exit(find_execute(find_formplan(argv), start));
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "Usage: %s [-H | -L | -P] [-dEhsXx] [-f file] "
+ "file [file ...] [expression]\n", getprogname());
+ exit(1);
+}
diff --git a/usr.bin/find/misc.c b/usr.bin/find/misc.c
new file mode 100644
index 0000000..8ba84c0
--- /dev/null
+++ b/usr.bin/find/misc.c
@@ -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/usr.bin/find/operator.c b/usr.bin/find/operator.c
new file mode 100644
index 0000000..63a6aa4
--- /dev/null
+++ b/usr.bin/find/operator.c
@@ -0,0 +1,272 @@
+/* $NetBSD: operator.c,v 1.10 2014/10/18 08:33:30 snj 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.10 2014/10/18 08:33:30 snj 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 its results together.
+ */
+ while ((expr = yankexpr(&plan)) != NULL) {
+ /*
+ * if we find an unclaimed ')' it means there is a missing
+ * '(' someplace.
+ */
+ if (expr->type == N_CLOSEPAREN)
+ 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/usr.bin/find/option.c b/usr.bin/find/option.c
new file mode 100644
index 0000000..0af4447
--- /dev/null
+++ b/usr.bin/find/option.c
@@ -0,0 +1,194 @@
+/* $NetBSD: option.c,v 1.27 2016/06/13 00:04:40 pgoyette 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.27 2016/06/13 00:04:40 pgoyette 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 },
+ { "-asince", N_ASINCE, c_asince, 1 },
+ { "-atime", N_ATIME, c_atime, 1 },
+ { "-cmin", N_CMIN, c_cmin, 1 },
+ { "-cnewer", N_CNEWER, c_cnewer, 1 },
+ { "-csince", N_CSINCE, c_csince, 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 },
+
+/* Aliases for compatability with Gnu findutils */
+ { "-neweraa", N_ANEWER, c_anewer, 1 },
+ { "-newerat", N_ASINCE, c_asince, 1 },
+ { "-newercc", N_CNEWER, c_cnewer, 1 },
+ { "-newerct", N_CSINCE, c_csince, 1 },
+ { "-newermm", N_NEWER, c_newer, 1 },
+ { "-newermt", N_SINCE, c_since, 1 },
+
+/*
+ * Unimplemented Gnu findutils options
+ *
+ * If you implement any of these, be sure to re-sort the table
+ * in ascii(7) order!
+ *
+ { "-newerBB", N_UNIMPL, c_unimpl, 1 },
+ { "-newerBa", N_UNIMPL, c_unimpl, 1 },
+ { "-newerBc", N_UNIMPL, c_unimpl, 1 },
+ { "-newerBm", N_UNIMPL, c_unimpl, 1 },
+ { "-newerBt", N_UNIMPL, c_unimpl, 1 },
+ { "-neweraB", N_UNIMPL, c_unimpl, 1 },
+ { "-newerac", N_UNIMPL, c_unimpl, 1 },
+ { "-neweram", N_UNIMPL, c_unimpl, 1 },
+ { "-newerca", N_UNIMPL, c_unimpl, 1 },
+ { "-newercm", N_UNIMPL, c_unimpl, 1 },
+ { "-newercB", N_UNIMPL, c_unimpl, 1 },
+ { "-newermB", N_UNIMPL, c_unimpl, 1 },
+ { "-newerma", N_UNIMPL, c_unimpl, 1 },
+ { "-newermc", N_UNIMPL, c_unimpl, 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 },
+ { "-since", N_SINCE, c_since, 1 },
+ { "-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;
+ char *opt;
+
+ argv = *argvp;
+ opt = *argv;
+
+ if ((p = option(opt)) == NULL)
+ errx(1, "%s: unknown option", opt);
+ ++argv;
+ if (p->arg && !*argv)
+ errx(1, "%s: requires additional arguments", opt);
+
+ new = (p->create)(&argv, p->token == N_OK, opt);
+
+ *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));
+}