summaryrefslogtreecommitdiffstats
path: root/shell_cmds/find
diff options
context:
space:
mode:
Diffstat (limited to 'shell_cmds/find')
-rw-r--r--shell_cmds/find/extern.h124
-rw-r--r--shell_cmds/find/find.11078
-rw-r--r--shell_cmds/find/find.c323
-rw-r--r--shell_cmds/find/find.h145
-rw-r--r--shell_cmds/find/find.plist.part30
-rw-r--r--shell_cmds/find/function.c1772
-rw-r--r--shell_cmds/find/getdate.y961
-rw-r--r--shell_cmds/find/ls.c124
-rw-r--r--shell_cmds/find/main.c166
-rw-r--r--shell_cmds/find/misc.c106
-rw-r--r--shell_cmds/find/operator.c273
-rw-r--r--shell_cmds/find/option.c201
12 files changed, 5303 insertions, 0 deletions
diff --git a/shell_cmds/find/extern.h b/shell_cmds/find/extern.h
new file mode 100644
index 0000000..f4282f7
--- /dev/null
+++ b/shell_cmds/find/extern.h
@@ -0,0 +1,124 @@
+/*-
+ * 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.
+ * 4. 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.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/16/94
+ * $FreeBSD: src/usr.bin/find/extern.h,v 1.26 2010/12/11 08:32:16 joel Exp $
+ */
+
+#include <sys/cdefs.h>
+
+void brace_subst(char *, char **, char *, int);
+PLAN *find_create(char ***);
+int find_execute(PLAN *, char **);
+PLAN *find_formplan(char **);
+PLAN *not_squish(PLAN *);
+PLAN *or_squish(PLAN *);
+PLAN *paren_squish(PLAN *);
+time_t get_date(char *);
+struct stat;
+void printlong(char *, char *, struct stat *);
+int queryuser(char **);
+OPTION *lookup_option(const char *);
+void finish_execplus(void);
+
+creat_f c_Xmin;
+creat_f c_Xtime;
+creat_f c_acl;
+creat_f c_and;
+creat_f c_delete;
+creat_f c_depth;
+creat_f c_empty;
+creat_f c_exec;
+creat_f c_flags;
+creat_f c_follow;
+creat_f c_fstype;
+creat_f c_group;
+creat_f c_inum;
+creat_f c_links;
+creat_f c_ls;
+creat_f c_mXXdepth;
+creat_f c_name;
+creat_f c_newer;
+creat_f c_nogroup;
+creat_f c_nouser;
+creat_f c_perm;
+creat_f c_print;
+creat_f c_regex;
+creat_f c_samefile;
+creat_f c_simple;
+creat_f c_size;
+creat_f c_type;
+creat_f c_user;
+creat_f c_xdev;
+
+exec_f f_Xmin;
+exec_f f_Xtime;
+exec_f f_acl;
+exec_f f_always_true;
+exec_f f_closeparen;
+exec_f f_delete;
+exec_f f_depth;
+exec_f f_empty;
+exec_f f_exec;
+exec_f f_expr;
+exec_f f_false;
+exec_f f_flags;
+exec_f f_fstype;
+exec_f f_group;
+exec_f f_inum;
+exec_f f_links;
+exec_f f_ls;
+exec_f f_name;
+exec_f f_newer;
+exec_f f_nogroup;
+exec_f f_not;
+exec_f f_nouser;
+exec_f f_openparen;
+exec_f f_or;
+exec_f f_path;
+exec_f f_perm;
+exec_f f_print;
+exec_f f_print0;
+exec_f f_prune;
+exec_f f_quit;
+exec_f f_regex;
+exec_f f_size;
+exec_f f_type;
+exec_f f_user;
+#ifdef __APPLE__
+exec_f f_xattr;
+exec_f f_xattrname;
+#endif /* __APPLE__ */
+
+extern int ftsoptions, isdeprecated, isdepth, isoutput, issort, isxargs;
+extern int mindepth, maxdepth;
+extern int regexp_flags;
+extern time_t now;
+extern int dotfd;
+extern FTS *tree;
+extern int execplus_error;
diff --git a/shell_cmds/find/find.1 b/shell_cmds/find/find.1
new file mode 100644
index 0000000..c6bb28f
--- /dev/null
+++ b/shell_cmds/find/find.1
@@ -0,0 +1,1078 @@
+.\" 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.
+.\" 4. 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.
+.\"
+.\" @(#)find.1 8.7 (Berkeley) 5/9/95
+.\" $FreeBSD: src/usr.bin/find/find.1,v 1.91 2011/09/28 18:53:36 ed Exp $
+.\"
+.Dd September 28, 2011
+.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 EXdsx
+.Op Fl f Ar path
+.Ar path ...
+.Op Ar expression
+.Nm
+.Op Fl H | Fl L | Fl P
+.Op Fl EXdsx
+.Fl f Ar path
+.Op Ar path ...
+.Op Ar expression
+.Sh DESCRIPTION
+The
+.Nm
+utility recursively descends the directory tree for each
+.Ar path
+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 indent
+.It Fl E
+Interpret regular expressions followed by
+.Ic -regex
+and
+.Ic -iregex
+primaries as extended (modern) regular expressions rather than basic
+regular expressions (BRE's).
+The
+.Xr re_format 7
+manual page fully describes both formats.
+.It Fl H
+Cause the file information and file type (see
+.Xr stat 2 )
+returned for each symbolic link specified 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
+Cause 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.
+.Pp
+This option is equivalent to the deprecated
+.Ic -follow
+primary.
+.It Fl P
+Cause the file information and file type (see
+.Xr stat 2 )
+returned for each symbolic link to be those of the link itself.
+This is the default.
+.It Fl X
+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 Li " ' "
+and double
+.Pq Dq Li " \*q "
+quotes, backslash
+.Pq Dq Li \e ,
+space, tab and newline characters.
+.Pp
+However, you may wish to consider the
+.Fl print0
+primary in conjunction with
+.Dq Nm xargs Fl 0
+as an effective alternative.
+.It Fl d
+Cause
+.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.
+.Pp
+This option is equivalent to the
+.Ic -depth
+primary of
+.St -p1003.1-2001 .
+The
+.Fl d
+option
+can be useful when
+.Nm
+is used with
+.Xr cpio 1
+to process files that are contained in directories with unusual permissions.
+It ensures that you have write permission while you are placing files in a
+directory, then sets the directory's permissions as the last thing.
+.It Fl f
+Specify a file hierarchy for
+.Nm
+to traverse.
+File hierarchies may also be specified as the operands immediately
+following the options.
+.It Fl s
+Cause
+.Nm
+to traverse the file hierarchies in lexicographical order,
+i.e., alphabetical order within each directory.
+Note:
+.Ql find -s
+and
+.Ql "find | sort"
+may give different results.
+.It Fl x
+Prevent
+.Nm
+from descending into directories that have a device number different
+than that of the file from which the descent began.
+.Pp
+This option is equivalent to the deprecated
+.Ic -xdev
+primary.
+.El
+.Sh PRIMARIES
+.Pp
+All primaries which take a numeric argument allow the number to be
+preceded by a plus sign
+.Pq Dq Li +
+or a minus sign
+.Pq Dq Li - .
+A preceding plus sign means
+.Dq more than n ,
+a preceding minus sign means
+.Dq less than n
+and neither means
+.Dq exactly n .
+.Bl -tag -width indent
+.It Ic -Bmin Ar n
+True if the difference between the time of a file's inode creation
+and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -Bnewer Ar file
+Same as
+.Ic -newerBm .
+.It Ic -Btime Ar n Ns Op Cm smhdw
+If no units are specified, this primary evaluates to
+true if the difference between the time of a file's inode creation
+and the time
+.Nm
+was started, rounded up to the next full 24-hour period, is
+.Ar n
+24-hour periods.
+.Pp
+If units are specified, this primary evaluates to
+true if the difference between the time of a file's inode creation
+and the time
+.Nm
+was started is exactly
+.Ar n
+units.
+Please refer to the
+.Ic -atime
+primary description for information on supported time units.
+.It Ic -acl
+May be used in conjunction with other primaries to locate
+files with extended ACLs.
+See
+.Xr acl 3
+for more information.
+.It Ic -amin Ar n
+True if the difference between the file last access time and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -anewer Ar file
+Same as
+.Ic -neweram .
+.It Ic -atime Ar n Ns Op Cm smhdw
+If no units are specified, this primary evaluates to
+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
+If units are specified, this primary evaluates to
+true if the difference between the file last access time and the time
+.Nm
+was started is exactly
+.Ar n
+units.
+Possible time units are as follows:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm s
+second
+.It Cm m
+minute (60 seconds)
+.It Cm h
+hour (60 minutes)
+.It Cm d
+day (24 hours)
+.It Cm w
+week (7 days)
+.El
+.Pp
+Any number of units may be combined in one
+.Ic -atime
+argument, for example,
+.Dq Li "-atime -1h30m" .
+Units are probably only useful when used in conjunction with the
+.Cm +
+or
+.Cm -
+modifier.
+.It Ic -cmin Ar n
+True if the difference between the time of last change of file status
+information and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -cnewer Ar file
+Same as
+.Ic -newercm .
+.It Ic -ctime Ar n Ns Op Cm smhdw
+If no units are specified, this primary evaluates to
+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
+If units are specified, this primary evaluates to
+true if the difference between the time of last change of file status
+information and the time
+.Nm
+was started is exactly
+.Ar n
+units.
+Please refer to the
+.Ic -atime
+primary description for information on supported time units.
+.It Ic -d
+Same as
+.Ic depth .
+GNU find implements this as a primary in mistaken emulation of
+.Fx
+.Xr find 1 .
+.It Ic -delete
+Delete found files and/or directories.
+Always returns true.
+This executes
+from the current working directory as
+.Nm
+recurses down the tree.
+It will not attempt to delete a filename with a
+.Dq Pa /
+character in its pathname relative to
+.Dq Pa \&.
+for security reasons.
+Depth-first traversal processing is implied by this option.
+Following symlinks is incompatible with this option.
+.It Ic -depth
+Always true;
+same as the
+.Fl d
+option.
+.It Ic -depth Ar n
+True if the depth of the file relative to the starting point of the traversal
+is
+.Ar n .
+.It Ic -empty
+True if the current file or directory is empty.
+.It Ic -exec Ar utility Oo Ar argument ... Oc Li \&;
+True if the program named
+.Ar utility
+returns a zero value as its exit status.
+Optional
+.Ar arguments
+may be passed to the utility.
+The expression must be terminated by a semicolon
+.Pq Dq Li \&; .
+If you invoke
+.Nm
+from a shell you may need to quote the semicolon if the shell would
+otherwise treat it as a control operator.
+If the string
+.Dq Li {}
+appears anywhere in the utility name or the
+arguments it is replaced by the pathname of the current file.
+.Ar Utility
+will be executed from the directory from which
+.Nm
+was executed.
+.Ar Utility
+and
+.Ar arguments
+are not subject to the further expansion of shell patterns
+and constructs.
+.It Ic -exec Ar utility Oo Ar argument ... Oc Li {} +
+Same as
+.Ic -exec ,
+except that
+.Dq Li {}
+is replaced with as many pathnames as possible for each invocation of
+.Ar utility .
+This behaviour is similar to that of
+.Xr xargs 1 .
+.It Ic -execdir Ar utility Oo Ar argument ... Oc Li \&;
+The
+.Ic -execdir
+primary is identical to the
+.Ic -exec
+primary with the exception that
+.Ar utility
+will be executed from the directory that holds
+the current file.
+The filename substituted for
+the string
+.Dq Li {}
+is not qualified.
+.It Ic -execdir Ar utility Oo Ar argument ... Oc Li {} +
+Same as
+.Ic -execdir ,
+except that
+.Dq Li {}
+is replaced with as many pathnames as possible for each invocation of
+.Ar utility .
+This behaviour is similar to that of
+.Xr xargs 1 .
+.It Ic -flags Oo Cm - Ns | Ns Cm + Oc Ns Ar flags , Ns Ar notflags
+The flags are specified using symbolic names (see
+.Xr chflags 1 ) .
+Those with the
+.Qq Li no
+prefix (except
+.Qq Li nodump )
+are said to be
+.Ar notflags .
+Flags in
+.Ar flags
+are checked to be set, and flags in
+.Ar notflags
+are checked to be not set.
+Note that this is different from
+.Ic -perm ,
+which only allows the user to specify mode bits that are set.
+.Pp
+If flags are preceded by a dash
+.Pq Dq Li - ,
+this primary evaluates to true
+if at least all of the bits in
+.Ar flags
+and none of the bits in
+.Ar notflags
+are set in the file's flags bits.
+If flags are preceded by a plus
+.Pq Dq Li + ,
+this primary evaluates to true
+if any of the bits in
+.Ar flags
+is set in the file's flags bits,
+or any of the bits in
+.Ar notflags
+is not set in the file's flags bits.
+Otherwise,
+this primary evaluates to true
+if the bits in
+.Ar flags
+exactly match the file's flags bits,
+and none of the
+.Ar flags
+bits match those of
+.Ar notflags .
+.It Ic -fstype Ar type
+True if the file is contained in a file system of type
+.Ar type .
+The
+.Xr lsvfs 1
+command can be used to find out the types of file systems
+that are available on the system.
+In addition, there are two pseudo-types,
+.Dq Li local
+and
+.Dq Li rdonly .
+The former matches any file system physically mounted on the system where
+the
+.Nm
+is being executed and the latter matches any file system which is
+mounted read-only.
+.It Ic -gid Ar gname
+The same thing as
+.Ar -group Ar gname
+for compatibility with GNU find.
+GNU find imposes a restriction that
+.Ar gname
+is numeric, while
+.Xr find 1
+does not.
+.It Ic -group Ar gname
+True if the file belongs to the group
+.Ar gname .
+If
+.Ar gname
+is numeric and there is no such group name, then
+.Ar gname
+is treated as a group ID.
+.It Ic -ignore_readdir_race
+This option is for GNU find compatibility and is ignored.
+.It Ic -ilname Ar pattern
+Like
+.Ic -lname ,
+but the match is case insensitive.
+This is a GNU find extension.
+.It Ic -iname Ar pattern
+Like
+.Ic -name ,
+but the match is case insensitive.
+.It Ic -inum Ar n
+True if the file has inode number
+.Ar n .
+.It Ic -ipath Ar pattern
+Like
+.Ic -path ,
+but the match is case insensitive.
+.It Ic -iregex Ar pattern
+Like
+.Ic -regex ,
+but the match is case insensitive.
+.It Ic -iwholename Ar pattern
+The same thing as
+.Ic -ipath ,
+for GNU find compatibility.
+.It Ic -links Ar n
+True if the file has
+.Ar n
+links.
+.It Ic -lname Ar pattern
+Like
+.Ic -name ,
+but the contents of the symbolic link are matched instead of the file
+name.
+Note that this only matches broken symbolic links
+if symbolic links are being followed.
+This is a GNU find extension.
+.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 device number
+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 Li -> .
+The format is identical to that produced by
+.Bk -words
+.Dq Nm ls Fl dgils .
+.Ek
+.It Ic -maxdepth Ar n
+Always true; descend at most
+.Ar n
+directory levels below the command line arguments.
+If any
+.Ic -maxdepth
+primary is specified, it applies to the entire expression even if it would
+not normally be evaluated.
+.Dq Ic -maxdepth Li 0
+limits the whole search to the command line arguments.
+.It Ic -mindepth Ar n
+Always true; do not apply any tests or actions at levels less than
+.Ar n .
+If any
+.Ic -mindepth
+primary is specified, it applies to the entire expression even if it would
+not normally be evaluated.
+.Dq Ic -mindepth Li 1
+processes all but the command line arguments.
+.It Ic -mmin Ar n
+True if the difference between the file last modification time and the time
+.Nm
+was started, rounded up to the next full minute, is
+.Ar n
+minutes.
+.It Ic -mnewer Ar file
+Same as
+.Ic -newer .
+.It Ic -mount
+The same thing as
+.Ic -xdev ,
+for GNU find compatibility.
+.It Ic -mtime Ar n Ns Op Cm smhdw
+If no units are specified, this primary evaluates to
+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
+If units are specified, this primary evaluates to
+true if the difference between the file last modification time and the time
+.Nm
+was started is exactly
+.Ar n
+units.
+Please refer to the
+.Ic -atime
+primary description for information on supported time units.
+.It Ic -name Ar pattern
+True if the last component of the pathname being examined matches
+.Ar pattern .
+Special shell pattern matching characters
+.Dq ( Li \&[ ,
+.Dq Li \&] ,
+.Dq Li * ,
+and
+.Dq Li \&? )
+may be used as part of
+.Ar pattern .
+These characters may be matched explicitly by escaping them with a
+backslash
+.Pq Dq Li \e .
+.It Ic -newer Ar file
+True if the current file has a more recent last modification time than
+.Ar file .
+.It Ic -newer Ns Ar X Ns Ar Y Ar file
+True if the current file has a more recent last access time
+.Pq Ar X Ns = Ns Cm a ,
+inode creation time
+.Pq Ar X Ns = Ns Cm B ,
+change time
+.Pq Ar X Ns = Ns Cm c ,
+or modification time
+.Pq Ar X Ns = Ns Cm m
+than the last access time
+.Pq Ar Y Ns = Ns Cm a ,
+inode creation time
+.Pq Ar Y Ns = Ns Cm B ,
+change time
+.Pq Ar Y Ns = Ns Cm c ,
+or modification time
+.Pq Ar Y Ns = Ns Cm m
+of
+.Ar file .
+In addition, if
+.Ar Y Ns = Ns Cm t ,
+then
+.Ar file
+is instead interpreted as a direct date specification of the form
+understood by
+.Xr cvs 1 .
+Note that
+.Ic -newermm
+is equivalent to
+.Ic -newer .
+.It Ic -nogroup
+True if the file belongs to an unknown group.
+.It Ic -noignore_readdir_race
+This option is for GNU find compatibility and is ignored.
+.It Ic -noleaf
+This option is for GNU find compatibility.
+In GNU find it disables an optimization not relevant to
+.Xr find 1 ,
+so it is ignored.
+.It Ic -nouser
+True if the file belongs to an unknown user.
+.It Ic -ok Ar utility Oo Ar argument ... Oc Li \&;
+The
+.Ic -ok
+primary is identical to the
+.Ic -exec
+primary with the exception that
+.Nm
+requests user affirmation for the execution of the
+.Ar utility
+by printing
+a message to the terminal and reading a response.
+If the response is not affirmative
+.Ql ( y
+in the
+.Dq Li POSIX
+locale),
+the command is not executed and the
+value of the
+.Ic -ok
+expression is false.
+.It Ic -okdir Ar utility Oo Ar argument ... Oc Li \&;
+The
+.Ic -okdir
+primary is identical to the
+.Ic -execdir
+primary with the same exception as described for the
+.Ic -ok
+primary.
+.It Ic -path Ar pattern
+True if the pathname being examined matches
+.Ar pattern .
+Special shell pattern matching characters
+.Dq ( Li \&[ ,
+.Dq Li \&] ,
+.Dq Li * ,
+and
+.Dq Li \&? )
+may be used as part of
+.Ar pattern .
+These characters may be matched explicitly by escaping them with a
+backslash
+.Pq Dq Li \e .
+Slashes
+.Pq Dq Li /
+are treated as normal characters and do not have to be
+matched explicitly.
+.It Ic -perm Oo Cm - Ns | Ns Cm + Oc Ns Ar mode
+The
+.Ar mode
+may be either symbolic (see
+.Xr chmod 1 )
+or an octal number.
+If the
+.Ar mode
+is symbolic, a starting value of zero is assumed and the
+.Ar mode
+sets or clears permissions without regard to the process' file mode
+creation mask.
+If the
+.Ar mode
+is octal, only bits 07777
+.Pq Dv S_ISUID | S_ISGID | S_ISTXT | S_IRWXU | S_IRWXG | S_IRWXO
+of the file's mode bits participate
+in the comparison.
+If the
+.Ar mode
+is preceded by a dash
+.Pq Dq Li - ,
+this primary evaluates to true
+if at least all of the bits in the
+.Ar mode
+are set in the file's mode bits.
+If the
+.Ar mode
+is preceded by a plus
+.Pq Dq Li + ,
+this primary evaluates to true
+if any of the bits in the
+.Ar mode
+are set in the file's mode bits.
+Otherwise, this primary evaluates to true if
+the bits in the
+.Ar mode
+exactly match the file's mode bits.
+Note, the first character of a symbolic mode may not be a dash
+.Pq Dq Li - .
+.It Ic -print
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output.
+If none of
+.\" 4772561
+.Ic -exec , -ls , -print , -print0 ,
+or
+.Ic -ok
+is specified, the given expression shall be effectively replaced by
+.Cm \&( Ar "given expression" Cm \&) Ic -print .
+.It Ic -print0
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output, followed by an
+.Tn ASCII
+.Dv NUL
+character (character code 0).
+.It Ic -prune
+This primary always evaluates to true.
+It causes
+.Nm
+to not descend into the current file.
+Note, the
+.Ic -prune
+primary has no effect if the
+.Fl d
+option was specified.
+.It Ic -regex Ar pattern
+True if the whole path of the file matches
+.Ar pattern
+using regular expression.
+To match a file named
+.Dq Pa ./foo/xyzzy ,
+you can use the regular expression
+.Dq Li ".*/[xyz]*"
+or
+.Dq Li ".*/foo/.*" ,
+but not
+.Dq Li xyzzy
+or
+.Dq Li /foo/ .
+.It Ic -samefile Ar name
+True if the file is a hard link to
+.Ar name .
+If the command option
+.Ic -L
+is specified, it is also true if the file is a symbolic link and
+points to
+.Ar name .
+.It Ic -size Ar n Ns Op Cm ckMGTP
+True if the file's size, rounded up, in 512-byte blocks is
+.Ar n .
+If
+.Ar n
+is followed by a
+.Cm c ,
+then the primary is true if the
+file's size is
+.Ar n
+bytes (characters).
+Similarly if
+.Ar n
+is followed by a scale indicator then the file's size is compared to
+.Ar n
+scaled as:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm k
+kilobytes (1024 bytes)
+.It Cm M
+megabytes (1024 kilobytes)
+.It Cm G
+gigabytes (1024 megabytes)
+.It Cm T
+terabytes (1024 gigabytes)
+.It Cm P
+petabytes (1024 terabytes)
+.El
+.It Ic -type Ar t
+True if the file is of the specified type.
+Possible file types are as follows:
+.Pp
+.Bl -tag -width 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
+.El
+.It Ic -uid Ar uname
+The same thing as
+.Ar -user Ar uname
+for compatibility with GNU find.
+GNU find imposes a restriction that
+.Ar uname
+is numeric, while
+.Xr find 1
+does not.
+.It Ic -user Ar uname
+True if the file belongs to the user
+.Ar uname .
+If
+.Ar uname
+is numeric and there is no such user name, then
+.Ar uname
+is treated as a user ID.
+.It Ic -wholename Ar pattern
+The same thing as
+.Ic -path ,
+for GNU find compatibility.
+.It Ic -xattr
+True if the file has any extended attributes.
+.It Ic -xattrname Ar name
+True if the file has an extended attribute with the specified
+.Ar name .
+.El
+.Sh OPERATORS
+The primaries may be combined using the following operators.
+The operators are listed in order of decreasing precedence.
+.Pp
+.Bl -tag -width indent -compact
+.It Cm \&( Ar expression Cm \&)
+This evaluates to true if the parenthesized expression evaluates to
+true.
+.Pp
+.It Cm \&! Ar expression
+.It Cm -not Ar expression
+This is the unary
+.Tn NOT
+operator.
+It evaluates to true if the expression is false.
+.Pp
+.It Cm -false
+Always false.
+.It Cm -true
+Always true.
+.Pp
+.It Ar expression Cm -and Ar expression
+.It Ar expression expression
+The
+.Cm -and
+operator is the logical
+.Tn AND
+operator.
+As it is implied by the juxtaposition of two expressions it does not
+have to be specified.
+The expression evaluates to true if both expressions are true.
+The second expression is not evaluated if the first expression is false.
+.Pp
+.It Ar expression Cm -or Ar expression
+The
+.Cm -or
+operator is the logical
+.Tn OR
+operator.
+The expression evaluates to true if either the first or the second expression
+is true.
+The second expression is not evaluated if the first expression is true.
+.El
+.Pp
+All operands and primaries must be separate arguments to
+.Nm .
+Primaries which themselves take arguments expect each argument
+to be a separate argument to
+.Nm .
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_COLLATE , LC_CTYPE , LC_MESSAGES
+and
+.Ev LC_TIME
+environment variables affect the execution of the
+.Nm
+utility as described in
+.Xr environ 7 .
+.Sh EXAMPLES
+The following examples are shown as given to the shell:
+.Bl -tag -width indent
+.It Li "find / \e! -name \*q*.c\*q -print"
+Print out a list of all the files whose names do not end in
+.Pa .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
+.Pa ttt .
+.It Li "find / \e! \e( -newer ttt -user wnj \e) -print"
+Print out a list of all the files which are not both newer than
+.Pa 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
+.Pa ttt .
+.It Li "find / -newerct '1 minute ago' -print"
+Print out a list of all the files whose inode change time is more
+recent than the current time minus one minute.
+.It Li "find / -type f -exec echo {} \e;"
+Use the
+.Xr echo 1
+command to print out a list of all the files.
+.It Li "find -L /usr/ports/packages -type l -exec rm -- {} +"
+Delete all broken symbolic links in
+.Pa /usr/ports/packages .
+.It Li "find /usr/src -name CVS -prune -o -depth +6 -print"
+Find files and directories that are at least seven levels deep
+in the working directory
+.Pa /usr/src .
+.It Li "find /usr/src -name CVS -prune -o -mindepth 7 -print"
+Is not equivalent to the previous example, since
+.Ic -prune
+is not evaluated below level seven.
+.El
+.Sh COMPATIBILITY
+The
+.Ic -follow
+primary is deprecated; the
+.Fl L
+option should be used instead.
+See the
+.Sx STANDARDS
+section below for details.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chmod 1 ,
+.Xr cvs 1 ,
+.Xr locate 1 ,
+.Xr lsvfs 1 ,
+.Xr whereis 1 ,
+.Xr which 1 ,
+.Xr xargs 1 ,
+.Xr stat 2 ,
+.Xr acl 3 ,
+.Xr fts 3 ,
+.Xr getgrent 3 ,
+.Xr getpwent 3 ,
+.Xr strmode 3 ,
+.Xr re_format 7 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+utility syntax is a superset of the syntax specified by the
+.St -p1003.1-2001
+standard.
+.Pp
+All the single character options except
+.Fl H
+and
+.Fl L
+as well as
+.Ic -amin , -anewer , -cmin , -cnewer , -delete , -empty , -fstype ,
+.Ic -iname , -inum , -iregex , -ls , -maxdepth , -mindepth , -mmin ,
+.Ic -path , -print0 , -regex
+and all of the
+.Ic -B*
+birthtime related primaries are extensions to
+.St -p1003.1-2001 .
+.Pp
+Historically, the
+.Fl d , L
+and
+.Fl x
+options were implemented using the primaries
+.Ic -depth , -follow ,
+and
+.Ic -xdev .
+These primaries always evaluated to true.
+As they were really global variables that took effect before the traversal
+began, some legal expressions could have unexpected results.
+An example is the expression
+.Ic -print Cm -o Ic -depth .
+As
+.Ic -print
+always evaluates to true, the standard order of evaluation
+implies that
+.Ic -depth
+would never be evaluated.
+This is not the case.
+.Pp
+The operator
+.Cm -or
+was implemented as
+.Cm -o ,
+and the operator
+.Cm -and
+was implemented as
+.Cm -a .
+.Pp
+Historic implementations of the
+.Ic -exec
+and
+.Ic -ok
+primaries did not replace the string
+.Dq Li {}
+in the utility name or the
+utility arguments if it had preceding or following non-whitespace characters.
+This version replaces it no matter where in the utility name or arguments
+it appears.
+.Pp
+The
+.Fl E
+option was inspired by the equivalent
+.Xr grep 1
+and
+.Xr sed 1
+options.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
+.Sh BUGS
+The special characters used by
+.Nm
+are also special characters to many shell programs.
+In particular, the characters
+.Dq Li * ,
+.Dq Li \&[ ,
+.Dq Li \&] ,
+.Dq Li \&? ,
+.Dq Li \&( ,
+.Dq Li \&) ,
+.Dq Li \&! ,
+.Dq Li \e
+and
+.Dq Li \&;
+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
+.Pa -xdev
+or
+.Pa \&! .
+These problems are handled by the
+.Fl f
+option and the
+.Xr getopt 3
+.Dq Fl Fl
+construct.
+.Pp
+The
+.Ic -delete
+primary does not interact well with other options that cause the file system
+tree traversal options to be changed.
+.Pp
+The
+.Ic -mindepth
+and
+.Ic -maxdepth
+primaries are actually global options (as documented above).
+They should
+probably be replaced by options which look like options.
diff --git a/shell_cmds/find/find.c b/shell_cmds/find/find.c
new file mode 100644
index 0000000..c59a75a
--- /dev/null
+++ b/shell_cmds/find/find.c
@@ -0,0 +1,323 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)find.c 8.5 (Berkeley) 8/5/94";
+#else
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/find.c,v 1.23 2010/12/11 08:32:16 joel Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __APPLE__
+#include <get_compat.h>
+#include <unistd.h>
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
+
+#include "find.h"
+
+#ifdef __APPLE__
+static int find_compare(const FTSENT **s1, const FTSENT **s2);
+#else /* !__APPLE__ */
+static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2);
+#endif /* __APPLE__ */
+
+/*
+ * find_compare --
+ * tell fts_open() how to order the traversal of the hierarchy.
+ * This variant gives lexicographical order, i.e., alphabetical
+ * order within each directory.
+ */
+static int
+#ifdef __APPLE__
+find_compare(const FTSENT **s1, const FTSENT **s2)
+#else /* !__APPLE__ */
+find_compare(const FTSENT * const *s1, const FTSENT * const *s2)
+#endif /* __APPLE__ */
+{
+
+ return (strcoll((*s1)->fts_name, (*s2)->fts_name));
+}
+
+/*
+ * 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 or -exec, then -print
+ * is assumed so we bracket the current expression with parens, if
+ * necessary, and add a -print node on the end.
+ */
+ if (!isoutput) {
+ OPTION *p;
+ char **argv1 = 0;
+
+ if (plan == NULL) {
+ p = lookup_option("-print");
+ new = (p->create)(p, &argv1);
+ tail = plan = new;
+ } else {
+ p = lookup_option("(");
+ new = (p->create)(p, &argv1);
+ new->next = plan;
+ plan = new;
+ p = lookup_option(")");
+ new = (p->create)(p, &argv1);
+ tail->next = new;
+ tail = new;
+ p = lookup_option("-print");
+ new = (p->create)(p, &argv1);
+ 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);
+}
+
+/* addPath - helper function used to build a list of paths that were
+ * specified on the command line that we are allowed to search.
+ */
+static char **addPath(char **array, char *newPath)
+{
+ static int pathCounter = 0;
+
+ if (newPath == NULL) { /* initialize array */
+ if ((array = malloc(sizeof(char *))) == NULL)
+ err(2, "out of memory");
+ array[0] = NULL;
+ }
+ else {
+ array = realloc(array, (++pathCounter + 1) * sizeof(char *));
+ if (array == NULL)
+ err(2, "out of memory");
+ else {
+ array[pathCounter - 1] = newPath;
+ array[pathCounter] = NULL; /* ensure array is null terminated */
+ }
+ }
+ return (array);
+}
+
+FTS *tree; /* pointer to top of FTS hierarchy */
+
+/*
+ * 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[])
+{
+ FTSENT *entry;
+ PLAN *p;
+ int rval;
+ char **myPaths;
+ int nonSearchableDirFound = 0;
+ int pathIndex;
+ struct stat statInfo;
+
+ /* special-case directories specified on command line - explicitly examine
+ * mode bits, to ensure failure if the directory cannot be searched
+ * (whether or not it's empty). UNIX conformance... <sigh>
+ */
+
+ int strict_symlinks = (ftsoptions & (FTS_COMFOLLOW|FTS_LOGICAL))
+ && COMPAT_MODE("bin/find", "unix2003");
+
+ myPaths = addPath(NULL, NULL);
+ for (pathIndex = 0; paths[pathIndex] != NULL; ++pathIndex) {
+ int stat_ret = stat(paths[pathIndex], &statInfo);
+ int stat_errno = errno;
+ if (strict_symlinks && stat_ret < 0) {
+ if (stat_errno == ELOOP) {
+ errx(1, "Symlink loop resolving %s", paths[pathIndex]);
+ }
+ }
+
+ /* retrieve mode bits, and examine "searchable" bit of
+ directories, exempt root from POSIX conformance */
+ if (COMPAT_MODE("bin/find", "unix2003") && getuid()
+ && stat_ret == 0
+ && ((statInfo.st_mode & S_IFMT) == S_IFDIR)) {
+ if (access(paths[pathIndex], X_OK) == 0) {
+ myPaths = addPath(myPaths, paths[pathIndex]);
+ } else {
+ if (stat_errno != ENAMETOOLONG) { /* if name is too long, just let existing logic handle it */
+ warnx("%s: Permission denied", paths[pathIndex]);
+ nonSearchableDirFound = 1;
+ }
+ }
+ } else {
+ /* not a directory, so add path to array */
+ myPaths = addPath(myPaths, paths[pathIndex]);
+ }
+ }
+ if (myPaths[0] == NULL) { /* were any directories searchable? */
+ free(myPaths);
+ return(nonSearchableDirFound); /* no... */
+ }
+
+ tree = fts_open(myPaths, ftsoptions, (issort ? find_compare : NULL));
+ if (tree == NULL)
+ err(1, "ftsopen");
+
+ for (rval = nonSearchableDirFound; (entry = fts_read(tree)) != NULL;) {
+ if (maxdepth != -1 && entry->fts_level >= maxdepth) {
+ if (fts_set(tree, entry, FTS_SKIP))
+ err(1, "%s", entry->fts_path);
+ }
+
+ switch (entry->fts_info) {
+ case FTS_D:
+ if (isdepth)
+ continue;
+ break;
+ case FTS_DP:
+ if (!isdepth)
+ continue;
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ (void)fflush(stdout);
+ warnx("%s: %s",
+ entry->fts_path, strerror(entry->fts_errno));
+ rval = 1;
+ continue;
+#ifdef FTS_W
+ case FTS_W:
+ continue;
+#endif /* FTS_W */
+ }
+#define BADCH " \t\n\\'\""
+ if (isxargs && strpbrk(entry->fts_path, BADCH)) {
+ (void)fflush(stdout);
+ warnx("%s: illegal path", entry->fts_path);
+ rval = 1;
+ continue;
+ }
+
+ if (mindepth != -1 && entry->fts_level < mindepth)
+ continue;
+
+ /*
+ * Call all the functions in the execution plan until one is
+ * false or all have been executed. This is where we do all
+ * the work specified by the user on the command line.
+ */
+ for (p = plan; p && (p->execute)(p, entry); p = p->next);
+ }
+ free (myPaths);
+ finish_execplus();
+ if (execplus_error) {
+ exit(execplus_error);
+ }
+ if (errno)
+ err(1, "fts_read");
+ fts_close(tree);
+ return (rval);
+}
diff --git a/shell_cmds/find/find.h b/shell_cmds/find/find.h
new file mode 100644
index 0000000..8960a0b
--- /dev/null
+++ b/shell_cmds/find/find.h
@@ -0,0 +1,145 @@
+/*-
+ * 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.
+ * 4. 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.
+ *
+ * @(#)find.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.bin/find/find.h,v 1.21 2010/12/11 08:32:16 joel Exp $
+ */
+
+#include <regex.h>
+
+/* forward declarations */
+struct _plandata;
+struct _option;
+
+/* execute function */
+typedef int exec_f(struct _plandata *, FTSENT *);
+/* create function */
+typedef struct _plandata *creat_f(struct _option *, char ***);
+
+/* function modifiers */
+#define F_NEEDOK 0x00000001 /* -ok vs. -exec */
+#define F_EXECDIR 0x00000002 /* -execdir vs. -exec */
+#define F_TIME_A 0x00000004 /* one of -atime, -anewer, -newera* */
+#define F_TIME_C 0x00000008 /* one of -ctime, -cnewer, -newerc* */
+#define F_TIME2_A 0x00000010 /* one of -newer?a */
+#define F_TIME2_C 0x00000020 /* one of -newer?c */
+#define F_TIME2_T 0x00000040 /* one of -newer?t */
+#define F_MAXDEPTH F_TIME_A /* maxdepth vs. mindepth */
+#define F_DEPTH F_TIME_A /* -depth n vs. -d */
+/* command line function modifiers */
+#define F_EQUAL 0x00000000 /* [acm]min [acm]time inum links size */
+#define F_LESSTHAN 0x00000100
+#define F_GREATER 0x00000200
+#define F_ELG_MASK 0x00000300
+#define F_ATLEAST 0x00000400 /* flags perm */
+#define F_ANY 0x00000800 /* perm */
+#define F_MTMASK 0x00003000
+#define F_MTFLAG 0x00000000 /* fstype */
+#define F_MTTYPE 0x00001000
+#define F_MTUNKNOWN 0x00002000
+#define F_IGNCASE 0x00010000 /* iname ipath iregex */
+#define F_EXACTTIME F_IGNCASE /* -[acm]time units syntax */
+#define F_EXECPLUS 0x00020000 /* -exec ... {} + */
+#define F_TIME_B 0x00040000 /* one of -Btime, -Bnewer, -newerB* */
+#define F_TIME2_B 0x00080000 /* one of -newer?B */
+#define F_LINK 0x00100000 /* lname or ilname */
+
+/* node definition */
+typedef struct _plandata {
+ struct _plandata *next; /* next node */
+ exec_f *execute; /* node evaluation function */
+ int flags; /* private flags */
+ union {
+ gid_t _g_data; /* gid */
+ ino_t _i_data; /* inode */
+ mode_t _m_data; /* mode mask */
+ struct {
+ u_long _f_flags;
+ u_long _f_notflags;
+ } fl;
+ nlink_t _l_data; /* link count */
+ short _d_data; /* level depth (-1 to N) */
+ off_t _o_data; /* file size */
+ time_t _t_data; /* time value */
+ uid_t _u_data; /* uid */
+ short _mt_data; /* mount flags */
+ struct _plandata *_p_data[2]; /* PLAN trees */
+ struct _ex {
+ char **_e_argv; /* argv array */
+ char **_e_orig; /* original strings */
+ int *_e_len; /* allocated length */
+ int _e_pbnum; /* base num. of args. used */
+ int _e_ppos; /* number of arguments used */
+ int _e_pnummax; /* max. number of arguments */
+ int _e_psize; /* number of bytes of args. */
+ int _e_pbsize; /* base num. of bytes of args */
+ int _e_psizemax; /* max num. of bytes of args */
+ struct _plandata *_e_next;/* next F_EXECPLUS in tree */
+ } ex;
+ char *_a_data[2]; /* array of char pointers */
+ char *_c_data; /* char pointer */
+ regex_t *_re_data; /* regex */
+ } p_un;
+} PLAN;
+#define a_data p_un._a_data
+#define c_data p_un._c_data
+#define d_data p_un._d_data
+#define fl_flags p_un.fl._f_flags
+#define fl_notflags p_un.fl._f_notflags
+#define g_data p_un._g_data
+#define i_data p_un._i_data
+#define l_data p_un._l_data
+#define m_data p_un._m_data
+#define mt_data p_un._mt_data
+#define o_data p_un._o_data
+#define p_data p_un._p_data
+#define t_data p_un._t_data
+#define u_data p_un._u_data
+#define re_data p_un._re_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 e_pbnum p_un.ex._e_pbnum
+#define e_ppos p_un.ex._e_ppos
+#define e_pnummax p_un.ex._e_pnummax
+#define e_psize p_un.ex._e_psize
+#define e_pbsize p_un.ex._e_pbsize
+#define e_psizemax p_un.ex._e_psizemax
+#define e_next p_un.ex._e_next
+
+typedef struct _option {
+ const char *name; /* option name */
+ creat_f *create; /* create function */
+ exec_f *execute; /* execute function */
+ int flags;
+} OPTION;
+
+#include "extern.h"
diff --git a/shell_cmds/find/find.plist.part b/shell_cmds/find/find.plist.part
new file mode 100644
index 0000000..0148f95
--- /dev/null
+++ b/shell_cmds/find/find.plist.part
@@ -0,0 +1,30 @@
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>find</string>
+ <key>OpenSourceVersion</key>
+ <string>2011-12-10</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://svnweb.freebsd.org/base/head/usr.bin/find/</string>
+ <key>OpenSourceSCM</key>
+ <string>svn co http://svn.freebsd.org/base/head/usr.bin/find/</string>
+ <key>OpenSourceImportDate</key>
+ <string>2012-01-06</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Add -xattr and -xattrname options.</string>
+ <string>execplus conformance fixes.</string>
+ <string>Man page fix (4772561).</string>
+ <string>Adjust FTS usage due to API differences.</string>
+ <string>Avoid searching unsearchable directories (3849245).</string>
+ <string>Add missing call to fts_close (4608460).</string>
+ <string>Conformance fixes related to F_EXACTTIME.</string>
+ <string>Apple-specific implementation of -acl primary.</string>
+ <string>Conformance fix for f_name (6591280).</string>
+ <string>setmode conformance fix (3936046).</string>
+ <string>Use a saner MAXLOGNAME definition.</string>
+ <string>Workaround for lack of rpmatch function.</string>
+ <string>FreeBSD revisions 260336, 260355.</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>bsd</string>
+ </dict>
diff --git a/shell_cmds/find/function.c b/shell_cmds/find/function.c
new file mode 100644
index 0000000..a673a02
--- /dev/null
+++ b/shell_cmds/find/function.c
@@ -0,0 +1,1772 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.71 2011/06/13 05:22:07 avatar Exp $");
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/acl.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 <limits.h>
+#include <pwd.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#ifdef __APPLE__
+#include <sys/sysctl.h>
+#include <sys/xattr.h>
+#include <libgen.h>
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
+
+#include "find.h"
+
+static PLAN *palloc(OPTION *);
+static long long find_parsenum(PLAN *, const char *, char *, char *);
+static long long find_parsetime(PLAN *, const char *, char *);
+static char *nextarg(OPTION *, char ***);
+
+extern char **environ;
+
+static PLAN *lastexecplus = NULL;
+int execplus_error;
+
+#define COMPARE(a, b) do { \
+ switch (plan->flags & F_ELG_MASK) { \
+ case F_EQUAL: \
+ return (a == b); \
+ case F_LESSTHAN: \
+ return (a < b); \
+ case F_GREATER: \
+ return (a > b); \
+ default: \
+ abort(); \
+ } \
+} while(0)
+
+static PLAN *
+palloc(OPTION *option)
+{
+ PLAN *new;
+
+ if ((new = malloc(sizeof(PLAN))) == NULL)
+ err(1, NULL);
+ new->execute = option->execute;
+ new->flags = option->flags;
+ new->next = NULL;
+ return new;
+}
+
+/*
+ * find_parsenum --
+ * Parse a string of the form [+-]# and return the value.
+ */
+static long long
+find_parsenum(PLAN *plan, const char *option, char *vp, char *endch)
+{
+ long long value;
+ char *endchar, *str; /* 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 strtoq(). Note, if strtoq() 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)
+ errx(1, "%s: %s: illegal trailing character", option, vp);
+ if (endch)
+ *endch = endchar[0];
+ return value;
+}
+
+/*
+ * find_parsetime --
+ * Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value.
+ */
+static long long
+find_parsetime(PLAN *plan, const char *option, char *vp)
+{
+ long long secs, value;
+ char *str, *unit; /* 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;
+ }
+
+ value = strtoq(str, &unit, 10);
+ if (value == 0 && unit == str) {
+ errx(1, "%s: %s: illegal time value", option, vp);
+ /* NOTREACHED */
+ }
+ if (*unit == '\0')
+ return value;
+
+ /* Units syntax. */
+ secs = 0;
+ for (;;) {
+ switch(*unit) {
+ case 's': /* seconds */
+ secs += value;
+ break;
+ case 'm': /* minutes */
+ secs += value * 60;
+ break;
+ case 'h': /* hours */
+ secs += value * 3600;
+ break;
+ case 'd': /* days */
+ secs += value * 86400;
+ break;
+ case 'w': /* weeks */
+ secs += value * 604800;
+ break;
+ default:
+ errx(1, "%s: %s: bad unit '%c'", option, vp, *unit);
+ /* NOTREACHED */
+ }
+ str = unit + 1;
+ if (*str == '\0') /* EOS */
+ break;
+ value = strtoq(str, &unit, 10);
+ if (value == 0 && unit == str) {
+ errx(1, "%s: %s: illegal time value", option, vp);
+ /* NOTREACHED */
+ }
+ if (*unit == '\0') {
+ errx(1, "%s: %s: missing trailing unit", option, vp);
+ /* NOTREACHED */
+ }
+ }
+ plan->flags |= F_EXACTTIME;
+ return secs;
+}
+
+/*
+ * nextarg --
+ * Check that another argument still exists, return a pointer to it,
+ * and increment the argument vector pointer.
+ */
+static char *
+nextarg(OPTION *option, char ***argvp)
+{
+ char *arg;
+
+ if ((arg = **argvp) == 0)
+ errx(1, "%s: requires additional arguments", option->name);
+ (*argvp)++;
+ return arg;
+} /* nextarg() */
+
+/*
+ * The value of n for the inode times (atime, birthtime, ctime, 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) \
+ if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \
+ ++((p)->t_data);
+
+/*
+ * -[acm]min n functions --
+ *
+ * True if the difference between the
+ * file access time (-amin)
+ * file birth time (-Bmin)
+ * last change of file status information (-cmin)
+ * file modification time (-mmin)
+ * and the current time is n min periods.
+ */
+int
+f_Xmin(PLAN *plan, FTSENT *entry)
+{
+ if (plan->flags & F_TIME_C) {
+ COMPARE((now - entry->fts_statp->st_ctime +
+ 60 - 1) / 60, plan->t_data);
+ } else if (plan->flags & F_TIME_A) {
+ COMPARE((now - entry->fts_statp->st_atime +
+ 60 - 1) / 60, plan->t_data);
+ } else if (plan->flags & F_TIME_B) {
+ COMPARE((now - entry->fts_statp->st_birthtime +
+ 60 - 1) / 60, plan->t_data);
+ } else {
+ COMPARE((now - entry->fts_statp->st_mtime +
+ 60 - 1) / 60, plan->t_data);
+ }
+}
+
+PLAN *
+c_Xmin(OPTION *option, char ***argvp)
+{
+ char *nmins;
+ PLAN *new;
+
+ nmins = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ new->t_data = find_parsenum(new, option->name, nmins, NULL);
+ TIME_CORRECT(new);
+ return new;
+}
+
+/*
+ * -[acm]time n functions --
+ *
+ * True if the difference between the
+ * file access time (-atime)
+ * file birth time (-Btime)
+ * last change of file status information (-ctime)
+ * file modification time (-mtime)
+ * and the current time is n 24 hour periods.
+ */
+
+int
+f_Xtime(PLAN *plan, FTSENT *entry)
+{
+ time_t xtime;
+
+ if (plan->flags & F_TIME_A)
+ xtime = entry->fts_statp->st_atime;
+ else if (plan->flags & F_TIME_B)
+ xtime = entry->fts_statp->st_birthtime;
+ else if (plan->flags & F_TIME_C)
+ xtime = entry->fts_statp->st_ctime;
+ else
+ xtime = entry->fts_statp->st_mtime;
+
+ if (plan->flags & F_EXACTTIME)
+ COMPARE(now - xtime, plan->t_data);
+ else
+ COMPARE((now - xtime + (COMPAT_MODE("bin/find", "unix2003") ? 0 : 86400 - 1)) / 86400, plan->t_data);
+}
+
+PLAN *
+c_Xtime(OPTION *option, char ***argvp)
+{
+ char *value;
+ PLAN *new;
+
+ value = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ new->t_data = find_parsetime(new, option->name, value);
+ if (!(new->flags & F_EXACTTIME) && !COMPAT_MODE("bin/find", "unix2003"))
+ TIME_CORRECT(new);
+ return new;
+}
+
+/*
+ * -maxdepth/-mindepth n functions --
+ *
+ * Does the same as -prune if the level of the current file is
+ * greater/less than the specified maximum/minimum depth.
+ *
+ * Note that -maxdepth and -mindepth are handled specially in
+ * find_execute() so their f_* functions are set to f_always_true().
+ */
+PLAN *
+c_mXXdepth(OPTION *option, char ***argvp)
+{
+ char *dstr;
+ PLAN *new;
+
+ dstr = nextarg(option, argvp);
+ if (dstr[0] == '-')
+ /* all other errors handled by find_parsenum() */
+ errx(1, "%s: %s: value must be positive", option->name, dstr);
+
+ new = palloc(option);
+ if (option->flags & F_MAXDEPTH)
+ maxdepth = find_parsenum(new, option->name, dstr, NULL);
+ else
+ mindepth = find_parsenum(new, option->name, dstr, NULL);
+ return new;
+}
+
+/*
+ * -acl function --
+ *
+ * Show files with EXTENDED ACL attributes.
+ */
+#ifdef __APPLE__
+int
+f_acl(PLAN *plan __unused, FTSENT *entry)
+{
+ acl_t facl;
+ int match;
+ acl_entry_t ae;
+
+ match = 0;
+ if ((facl = acl_get_link_np(entry->fts_accpath, ACL_TYPE_EXTENDED)) != NULL) {
+ if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 0) {
+ match = 1;
+ }
+ acl_free(facl);
+ }
+ return match;
+}
+#else /* !__APPLE__ */
+int
+f_acl(PLAN *plan __unused, FTSENT *entry)
+{
+ acl_t facl;
+ acl_type_t acl_type;
+ int acl_supported = 0, ret, trivial;
+
+ if (S_ISLNK(entry->fts_statp->st_mode))
+ return 0;
+ ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_NFS4;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("%s", entry->fts_accpath);
+ return (0);
+ }
+ if (acl_supported == 0) {
+ ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_ACCESS;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("%s", entry->fts_accpath);
+ return (0);
+ }
+ }
+ if (acl_supported == 0)
+ return (0);
+
+ facl = acl_get_file(entry->fts_accpath, acl_type);
+ if (facl == NULL) {
+ warn("%s", entry->fts_accpath);
+ return (0);
+ }
+ ret = acl_is_trivial_np(facl, &trivial);
+ acl_free(facl);
+ if (ret) {
+ warn("%s", entry->fts_accpath);
+ acl_free(facl);
+ return (0);
+ }
+ if (trivial)
+ return (0);
+ return (1);
+}
+#endif /* __APPLE__ */
+
+PLAN *
+c_acl(OPTION *option, char ***argvp __unused)
+{
+#ifndef __APPLE__
+ ftsoptions &= ~FTS_NOSTAT;
+#endif /* !__APPLE__ */
+ return (palloc(option));
+}
+
+#ifdef __APPLE__
+int
+f_xattr(PLAN *plan __unused, FTSENT *entry)
+{
+ ssize_t xattr;
+ int match;
+
+ match = 0;
+ xattr = listxattr(entry->fts_accpath, NULL, 0, XATTR_NOFOLLOW);
+ if (xattr > 0) {
+ match = 1;
+ }
+ return match;
+}
+
+int
+f_xattrname(PLAN *plan, FTSENT *entry)
+{
+ ssize_t xattr;
+ int match;
+
+ match = 0;
+ xattr = getxattr(entry->fts_accpath, plan->c_data, NULL, 0, 0, XATTR_NOFOLLOW);
+ if (xattr > 0) {
+ match = 1;
+ }
+ return match;
+}
+#endif /* __APPLE__ */
+
+/*
+ * -delete functions --
+ *
+ * True always. Makes its best shot and continues on regardless.
+ */
+int
+f_delete(PLAN *plan __unused, FTSENT *entry)
+{
+ /* ignore these from fts */
+ if (strcmp(entry->fts_accpath, ".") == 0 ||
+ strcmp(entry->fts_accpath, "..") == 0)
+ return 1;
+
+ /* sanity check */
+ if (isdepth == 0 || /* depth off */
+ (ftsoptions & FTS_NOSTAT)) /* not stat()ing */
+ errx(1, "-delete: insecure options got turned on");
+
+ if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */
+ (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
+ errx(1, "-delete: forbidden when symlinks are followed");
+
+ /* Potentially unsafe - do not accept relative paths whatsoever */
+ if (strchr(entry->fts_accpath, '/') != NULL)
+ errx(1, "-delete: %s: relative path potentially not safe",
+ entry->fts_accpath);
+
+ /* Turn off user immutable bits if running as root */
+ if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+ !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+ geteuid() == 0)
+ lchflags(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(OPTION *option, char ***argvp __unused)
+{
+
+ ftsoptions &= ~FTS_NOSTAT; /* no optimise */
+ isoutput = 1; /* possible output */
+ isdepth = 1; /* -depth implied */
+
+ return palloc(option);
+}
+
+
+/*
+ * always_true --
+ *
+ * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true
+ */
+int
+f_always_true(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ return 1;
+}
+
+/*
+ * -depth functions --
+ *
+ * With argument: True if the file is at level n.
+ * Without argument: 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_depth(PLAN *plan, FTSENT *entry)
+{
+ if (plan->flags & F_DEPTH)
+ COMPARE(entry->fts_level, plan->d_data);
+ else
+ return 1;
+}
+
+PLAN *
+c_depth(OPTION *option, char ***argvp)
+{
+ PLAN *new;
+ char *str;
+
+ new = palloc(option);
+
+ str = **argvp;
+ if (str && !(new->flags & F_DEPTH)) {
+ /* skip leading + or - */
+ if (*str == '+' || *str == '-')
+ str++;
+ /* skip sign */
+ if (*str == '+' || *str == '-')
+ str++;
+ if (isdigit(*str))
+ new->flags |= F_DEPTH;
+ }
+
+ if (new->flags & F_DEPTH) { /* -depth n */
+ char *ndepth;
+
+ ndepth = nextarg(option, argvp);
+ new->d_data = find_parsenum(new, option->name, ndepth, NULL);
+ } else { /* -d */
+ isdepth = 1;
+ }
+
+ return new;
+}
+
+/*
+ * -empty functions --
+ *
+ * True if the file or directory is empty
+ */
+int
+f_empty(PLAN *plan __unused, 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(OPTION *option, char ***argvp __unused)
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return palloc(option);
+}
+
+/*
+ * [-exec | -execdir | -ok] utility [arg ... ] ; functions --
+ *
+ * True if the executed utility returns a zero value as exit status.
+ * The end of the primary expression is delimited by a semicolon. If
+ * "{}" occurs anywhere, it gets replaced by the current pathname,
+ * or, in the case of -execdir, the current basename (filename
+ * without leading directory prefix). For -exec and -ok,
+ * the current directory for the execution of utility is the same as
+ * the current directory when the find utility was started, whereas
+ * for -execdir, it is the directory the file resides in.
+ *
+ * The primary -ok differs from -exec in that it requests affirmation
+ * of the user before executing the utility.
+ */
+int
+f_exec(PLAN *plan, FTSENT *entry)
+{
+ int cnt;
+ pid_t pid;
+ int status;
+ char *file;
+
+ if (entry == NULL && plan->flags & F_EXECPLUS) {
+ if (plan->e_ppos == plan->e_pbnum)
+ return (1);
+ plan->e_argv[plan->e_ppos] = NULL;
+ goto doexec;
+ }
+
+ /* XXX - if file/dir ends in '/' this will not work -- can it? */
+ if ((plan->flags & F_EXECDIR) && \
+ (file = strrchr(entry->fts_path, '/')))
+ file++;
+ else
+ file = entry->fts_path;
+
+ if (plan->flags & F_EXECPLUS) {
+ if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL)
+ err(1, NULL);
+ plan->e_len[plan->e_ppos] = strlen(file);
+ plan->e_psize += plan->e_len[plan->e_ppos];
+ if (++plan->e_ppos < plan->e_pnummax &&
+ plan->e_psize < plan->e_psizemax)
+ return (1);
+ plan->e_argv[plan->e_ppos] = NULL;
+ } else {
+ 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]);
+ }
+
+doexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv))
+ return 0;
+
+ /* make sure find output is interspersed correctly with subprocesses */
+ fflush(stdout);
+ fflush(stderr);
+
+ switch (pid = fork()) {
+ case -1:
+ err(1, "fork");
+ /* NOTREACHED */
+ case 0:
+ /* change dir back from where we started */
+ if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) {
+ warn("chdir");
+ _exit(1);
+ }
+ execvp(plan->e_argv[0], plan->e_argv);
+ warn("%s", plan->e_argv[0]);
+ _exit(1);
+ }
+ if (plan->flags & F_EXECPLUS) {
+ while (--plan->e_ppos >= plan->e_pbnum)
+ free(plan->e_argv[plan->e_ppos]);
+ plan->e_ppos = plan->e_pbnum;
+ plan->e_psize = plan->e_pbsize;
+ }
+ pid = waitpid(pid, &status, 0);
+ if (plan->flags & F_EXECPLUS && WIFEXITED(status) && WEXITSTATUS(status) && !execplus_error) {
+ /* Test 140 (8907531, 10656525) */
+ execplus_error = WEXITSTATUS(status);
+ }
+ return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
+}
+
+/*
+ * c_exec, c_execdir, c_ok --
+ * build three parallel arrays, one with pointers to the strings passed
+ * on the command line, one with (possibly duplicated) pointers to the
+ * argv array, and one with integer values that are lengths of the
+ * strings, but also flags meaning that the string has to be massaged.
+ */
+PLAN *
+c_exec(OPTION *option, char ***argvp)
+{
+ PLAN *new; /* node returned */
+ long argmax;
+ int cnt, i;
+ char **argv, **ap, **ep, *p;
+
+ /* XXX - was in c_execdir, but seems unnecessary!?
+ ftsoptions &= ~FTS_NOSTAT;
+ */
+ isoutput = 1;
+
+ /* XXX - this is a change from the previous coding */
+ new = palloc(option);
+
+ for (ap = argv = *argvp;; ++ap) {
+ if (!*ap)
+ errx(1,
+ "%s: no terminating \";\" or \"+\"", option->name);
+ if (**ap == ';')
+ break;
+ if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) {
+ new->flags |= F_EXECPLUS;
+ break;
+ }
+ }
+
+ if (ap == argv)
+ errx(1, "%s: no command specified", option->name);
+
+ cnt = ap - *argvp + 1;
+ if (new->flags & F_EXECPLUS) {
+ new->e_ppos = new->e_pbnum = cnt - 2;
+ if ((argmax = sysconf(_SC_ARG_MAX)) == -1) {
+ warn("sysconf(_SC_ARG_MAX)");
+ argmax = _POSIX_ARG_MAX;
+ }
+ argmax -= 1024;
+ for (ep = environ; *ep != NULL; ep++)
+ argmax -= strlen(*ep) + 1 + sizeof(*ep);
+ argmax -= 1 + sizeof(*ep);
+ new->e_pnummax = argmax / 16;
+ argmax -= sizeof(char *) * new->e_pnummax;
+ if (argmax <= 0)
+ errx(1, "no space for arguments");
+ new->e_psizemax = argmax;
+ new->e_pbsize = 0;
+ cnt += new->e_pnummax + 1;
+ new->e_next = lastexecplus;
+ lastexecplus = new;
+ }
+ if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL)
+ err(1, NULL);
+ if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL)
+ err(1, NULL);
+ if ((new->e_len = malloc(cnt * sizeof(int))) == NULL)
+ err(1, NULL);
+
+ for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
+ new->e_orig[cnt] = *argv;
+ if (new->flags & F_EXECPLUS)
+ new->e_pbsize += strlen(*argv) + 1;
+ for (p = *argv; *p; ++p)
+ if (!(new->flags & F_EXECPLUS) && p[0] == '{' &&
+ p[1] == '}') {
+ if ((new->e_argv[cnt] =
+ malloc(MAXPATHLEN)) == NULL)
+ err(1, NULL);
+ new->e_len[cnt] = MAXPATHLEN;
+ break;
+ }
+ if (!*p) {
+ new->e_argv[cnt] = *argv;
+ new->e_len[cnt] = 0;
+ }
+ }
+ if (new->flags & F_EXECPLUS) {
+ new->e_psize = new->e_pbsize;
+ cnt--;
+ for (i = 0; i < new->e_pnummax; i++) {
+ new->e_argv[cnt] = NULL;
+ new->e_len[cnt] = 0;
+ cnt++;
+ }
+ argv = ap;
+ goto done;
+ }
+ new->e_argv[cnt] = new->e_orig[cnt] = NULL;
+
+done: *argvp = argv + 1;
+ return new;
+}
+
+/* Finish any pending -exec ... {} + functions. */
+void
+finish_execplus(void)
+{
+ PLAN *p;
+
+ p = lastexecplus;
+ while (p != NULL) {
+ (p->execute)(p, NULL);
+ p = p->e_next;
+ }
+}
+
+int
+f_flags(PLAN *plan, FTSENT *entry)
+{
+ u_long flags;
+
+ flags = entry->fts_statp->st_flags;
+ if (plan->flags & F_ATLEAST)
+ return (flags | plan->fl_flags) == flags &&
+ !(flags & plan->fl_notflags);
+ else if (plan->flags & F_ANY)
+ return (flags & plan->fl_flags) ||
+ (flags | plan->fl_notflags) != flags;
+ else
+ return flags == plan->fl_flags &&
+ !(plan->fl_flags & plan->fl_notflags);
+}
+
+PLAN *
+c_flags(OPTION *option, char ***argvp)
+{
+ char *flags_str;
+ PLAN *new;
+ u_long flags, notflags;
+
+ flags_str = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+
+ if (*flags_str == '-') {
+ new->flags |= F_ATLEAST;
+ flags_str++;
+ } else if (*flags_str == '+') {
+ new->flags |= F_ANY;
+ flags_str++;
+ }
+ if (strtofflags(&flags_str, &flags, &notflags) == 1)
+ errx(1, "%s: %s: illegal flags string", option->name, flags_str);
+
+ new->fl_flags = flags;
+ new->fl_notflags = notflags;
+ return new;
+}
+
+/*
+ * -follow functions --
+ *
+ * Always true, causes symbolic links to be followed on a global
+ * basis.
+ */
+PLAN *
+c_follow(OPTION *option, char ***argvp __unused)
+{
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+
+ return palloc(option);
+}
+
+/*
+ * -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 statfs sb;
+ static int val_flags;
+ static char fstype[sizeof(sb.f_fstypename)];
+ char *p, save[2] = {0,0};
+
+ if ((plan->flags & F_MTMASK) == F_MTUNKNOWN)
+ return 0;
+
+ /* Only check when we cross mount point. */
+ if (first || curdev != entry->fts_statp->st_dev) {
+ curdev = entry->fts_statp->st_dev;
+
+ /*
+ * Statfs follows symlinks; find wants the link's filesystem,
+ * 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 (statfs(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_flags = sb.f_flags;
+ strlcpy(fstype, sb.f_fstypename, sizeof(fstype));
+ }
+ switch (plan->flags & F_MTMASK) {
+ case F_MTFLAG:
+ return val_flags & plan->mt_data;
+ case F_MTTYPE:
+ return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0);
+ default:
+ abort();
+ }
+}
+
+PLAN *
+c_fstype(OPTION *option, char ***argvp)
+{
+ char *fsname;
+ PLAN *new;
+
+ fsname = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ switch (*fsname) {
+ case 'l':
+ if (!strcmp(fsname, "local")) {
+ new->flags |= F_MTFLAG;
+ new->mt_data = MNT_LOCAL;
+ return new;
+ }
+ break;
+ case 'r':
+ if (!strcmp(fsname, "rdonly")) {
+ new->flags |= F_MTFLAG;
+ new->mt_data = MNT_RDONLY;
+ return new;
+ }
+ break;
+ }
+
+ new->flags |= F_MTTYPE;
+ new->c_data = fsname;
+ 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(OPTION *option, char ***argvp)
+{
+ char *gname;
+ PLAN *new;
+ struct group *g;
+ gid_t gid;
+
+ gname = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ g = getgrnam(gname);
+ if (g == NULL) {
+ char* cp = gname;
+ if (gname[0] == '-' || gname[0] == '+')
+ gname++;
+ gid = atoi(gname);
+ if (gid == 0 && gname[0] != '0')
+ errx(1, "%s: %s: no such group", option->name, gname);
+ gid = find_parsenum(new, option->name, cp, NULL);
+ } else
+ 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(OPTION *option, char ***argvp)
+{
+ char *inum_str;
+ PLAN *new;
+
+ inum_str = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ new->i_data = find_parsenum(new, option->name, inum_str, NULL);
+ return new;
+}
+
+/*
+ * -samefile FN
+ *
+ * True if the file has the same inode (eg hard link) FN
+ */
+
+/* f_samefile is just f_inum */
+PLAN *
+c_samefile(OPTION *option, char ***argvp)
+{
+ char *fn;
+ PLAN *new;
+ struct stat sb;
+
+ fn = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ if (stat(fn, &sb))
+ err(1, "%s", fn);
+ new->i_data = sb.st_ino;
+ 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(OPTION *option, char ***argvp)
+{
+ char *nlinks;
+ PLAN *new;
+
+ nlinks = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL);
+ return new;
+}
+
+/*
+ * -ls functions --
+ *
+ * Always true - prints the current entry to stdout in "ls" format.
+ */
+int
+f_ls(PLAN *plan __unused, FTSENT *entry)
+{
+ printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
+ return 1;
+}
+
+PLAN *
+c_ls(OPTION *option, char ***argvp __unused)
+{
+ ftsoptions &= ~FTS_NOSTAT;
+ isoutput = 1;
+
+ return palloc(option);
+}
+
+/*
+ * -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)
+{
+ char fn[PATH_MAX];
+ const char *name;
+ ssize_t len;
+
+ if (plan->flags & F_LINK) {
+ /*
+ * The below test both avoids obviously useless readlink()
+ * calls and ensures that symlinks with existent target do
+ * not match if symlinks are being followed.
+ * Assumption: fts will stat all symlinks that are to be
+ * followed and will return the stat information.
+ */
+ if (entry->fts_info != FTS_NSOK && entry->fts_info != FTS_SL &&
+ entry->fts_info != FTS_SLNONE)
+ return 0;
+ len = readlink(entry->fts_accpath, fn, sizeof(fn) - 1);
+ if (len == -1)
+ return 0;
+ fn[len] = '\0';
+ name = fn;
+ } else if (entry->fts_namelen == 0) {
+ name = basename(entry->fts_path);
+ } else
+ name = entry->fts_name;
+ return !fnmatch(plan->c_data, name,
+ plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
+}
+
+PLAN *
+c_name(OPTION *option, char ***argvp)
+{
+ char *pattern;
+ PLAN *new;
+
+ pattern = nextarg(option, argvp);
+ new = palloc(option);
+ new->c_data = pattern;
+ return new;
+}
+
+/*
+ * -newer file functions --
+ *
+ * True if the current file has been modified more recently
+ * then the modification time of the file named by the pathname
+ * file.
+ */
+int
+f_newer(PLAN *plan, FTSENT *entry)
+{
+ if (plan->flags & F_TIME_C)
+ return entry->fts_statp->st_ctime > plan->t_data;
+ else if (plan->flags & F_TIME_A)
+ return entry->fts_statp->st_atime > plan->t_data;
+ else if (plan->flags & F_TIME_B)
+ return entry->fts_statp->st_birthtime > plan->t_data;
+ else
+ return entry->fts_statp->st_mtime > plan->t_data;
+}
+
+PLAN *
+c_newer(OPTION *option, char ***argvp)
+{
+ char *fn_or_tspec;
+ PLAN *new;
+ struct stat sb;
+
+ fn_or_tspec = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ /* compare against what */
+ if (option->flags & F_TIME2_T) {
+ new->t_data = get_date(fn_or_tspec);
+ if (new->t_data == (time_t) -1)
+ errx(1, "Can't parse date/time: %s", fn_or_tspec);
+ } else {
+ if (stat(fn_or_tspec, &sb))
+ err(1, "%s", fn_or_tspec);
+ if (option->flags & F_TIME2_C)
+ new->t_data = sb.st_ctime;
+ else if (option->flags & F_TIME2_A)
+ new->t_data = sb.st_atime;
+ else if (option->flags & F_TIME2_B)
+ new->t_data = sb.st_birthtime;
+ else
+ new->t_data = sb.st_mtime;
+ }
+ return new;
+}
+
+/*
+ * -nogroup functions --
+ *
+ * True if file belongs to a user ID for which the equivalent
+ * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
+ */
+int
+f_nogroup(PLAN *plan __unused, FTSENT *entry)
+{
+ return group_from_gid(entry->fts_statp->st_gid, 1) == NULL;
+}
+
+PLAN *
+c_nogroup(OPTION *option, char ***argvp __unused)
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return palloc(option);
+}
+
+/*
+ * -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 __unused, FTSENT *entry)
+{
+ return user_from_uid(entry->fts_statp->st_uid, 1) == NULL;
+}
+
+PLAN *
+c_nouser(OPTION *option, char ***argvp __unused)
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return palloc(option);
+}
+
+/*
+ * -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,
+ plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
+}
+
+/* c_path is the same as c_name */
+
+/*
+ * -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 if (plan->flags & F_ANY)
+ return (mode & plan->m_data);
+ else
+ return mode == plan->m_data;
+ /* NOTREACHED */
+}
+
+PLAN *
+c_perm(OPTION *option, char ***argvp)
+{
+ char *perm;
+ PLAN *new;
+ mode_t *set;
+
+ perm = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+
+ if (*perm == '-') {
+ new->flags |= F_ATLEAST;
+ ++perm;
+ } else if (*perm == '+') {
+ if ((set = setmode(perm + 1)) != NULL) {
+ new->flags |= F_ANY;
+ ++perm;
+ free(set);
+ }
+ }
+
+ if ((set = setmode(perm)) == NULL)
+ errx(1, "%s: %s: illegal mode string", option->name, perm);
+
+ new->m_data = getmode(set, 0);
+ free(set);
+ return new;
+}
+
+/*
+ * -print functions --
+ *
+ * Always true, causes the current pathname to be written to
+ * standard output.
+ */
+int
+f_print(PLAN *plan __unused, FTSENT *entry)
+{
+ (void)puts(entry->fts_path);
+ return 1;
+}
+
+PLAN *
+c_print(OPTION *option, char ***argvp __unused)
+{
+ isoutput = 1;
+
+ return palloc(option);
+}
+
+/*
+ * -print0 functions --
+ *
+ * Always true, causes the current pathname to be written to
+ * standard output followed by a NUL character
+ */
+int
+f_print0(PLAN *plan __unused, FTSENT *entry)
+{
+ fputs(entry->fts_path, stdout);
+ fputc('\0', stdout);
+ return 1;
+}
+
+/* c_print0 is the same as c_print */
+
+/*
+ * -prune functions --
+ *
+ * Prune a portion of the hierarchy.
+ */
+int
+f_prune(PLAN *plan __unused, FTSENT *entry)
+{
+ if (fts_set(tree, entry, FTS_SKIP))
+ err(1, "%s", entry->fts_path);
+ return 1;
+}
+
+/* c_prune == c_simple */
+
+/*
+ * -regex functions --
+ *
+ * True if the whole path of the file matches pattern using
+ * regular expression.
+ */
+int
+f_regex(PLAN *plan, FTSENT *entry)
+{
+ char *str;
+ int len;
+ regex_t *pre;
+ regmatch_t pmatch;
+ int errcode;
+ char errbuf[LINE_MAX];
+ int matched;
+
+ pre = plan->re_data;
+ str = entry->fts_path;
+ len = strlen(str);
+ matched = 0;
+
+ pmatch.rm_so = 0;
+ pmatch.rm_eo = len;
+
+ errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND);
+
+ if (errcode != 0 && errcode != REG_NOMATCH) {
+ regerror(errcode, pre, errbuf, sizeof errbuf);
+ errx(1, "%s: %s",
+ plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf);
+ }
+
+ if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len)
+ matched = 1;
+
+ return matched;
+}
+
+PLAN *
+c_regex(OPTION *option, char ***argvp)
+{
+ PLAN *new;
+ char *pattern;
+ regex_t *pre;
+ int errcode;
+ char errbuf[LINE_MAX];
+
+ if ((pre = malloc(sizeof(regex_t))) == NULL)
+ err(1, NULL);
+
+ pattern = nextarg(option, argvp);
+
+ if ((errcode = regcomp(pre, pattern,
+ regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) {
+ regerror(errcode, pre, errbuf, sizeof errbuf);
+ errx(1, "%s: %s: %s",
+ option->flags & F_IGNCASE ? "-iregex" : "-regex",
+ pattern, errbuf);
+ }
+
+ new = palloc(option);
+ new->re_data = pre;
+
+ return new;
+}
+
+/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */
+
+PLAN *
+c_simple(OPTION *option, char ***argvp __unused)
+{
+ return palloc(option);
+}
+
+/*
+ * -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
+ * one of c k M G T P, the size is in bytes, kilobytes,
+ * megabytes, gigabytes, terabytes or petabytes respectively.
+ */
+#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(OPTION *option, char ***argvp)
+{
+ char *size_str;
+ PLAN *new;
+ char endch;
+ off_t scale;
+
+ size_str = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ endch = 'c';
+ new->o_data = find_parsenum(new, option->name, size_str, &endch);
+ if (endch != '\0') {
+ divsize = 0;
+
+ switch (endch) {
+ case 'c': /* characters */
+ scale = 0x1LL;
+ break;
+ case 'k': /* kilobytes 1<<10 */
+ scale = 0x400LL;
+ break;
+ case 'M': /* megabytes 1<<20 */
+ scale = 0x100000LL;
+ break;
+ case 'G': /* gigabytes 1<<30 */
+ scale = 0x40000000LL;
+ break;
+ case 'T': /* terabytes 1<<40 */
+ scale = 0x1000000000LL;
+ break;
+ case 'P': /* petabytes 1<<50 */
+ scale = 0x4000000000000LL;
+ break;
+ default:
+ errx(1, "%s: %s: illegal trailing character",
+ option->name, size_str);
+ break;
+ }
+ if (new->o_data > QUAD_MAX / scale)
+ errx(1, "%s: %s: value too large",
+ option->name, size_str);
+ new->o_data *= scale;
+ }
+ 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(OPTION *option, char ***argvp)
+{
+ char *typestring;
+ PLAN *new;
+ mode_t mask;
+
+ typestring = nextarg(option, 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 FTS_WHITEOUT
+ case 'w':
+ mask = S_IFWHT;
+ ftsoptions |= FTS_WHITEOUT;
+ break;
+#endif /* FTS_WHITEOUT */
+ default:
+ errx(1, "%s: %s: unknown type", option->name, typestring);
+ }
+
+ new = palloc(option);
+ 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(OPTION *option, char ***argvp)
+{
+ char *username;
+ PLAN *new;
+ struct passwd *p;
+ uid_t uid;
+
+ username = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ p = getpwnam(username);
+ if (p == NULL) {
+ char* cp = username;
+ if( username[0] == '-' || username[0] == '+' )
+ username++;
+ uid = atoi(username);
+ if (uid == 0 && username[0] != '0')
+ errx(1, "%s: %s: no such user", option->name, username);
+ uid = find_parsenum(new, option->name, cp, NULL);
+ } else
+ 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(OPTION *option, char ***argvp __unused)
+{
+ ftsoptions |= FTS_XDEV;
+
+ return palloc(option);
+}
+
+/*
+ * ( expression ) functions --
+ *
+ * True if expression is true.
+ */
+int
+f_expr(PLAN *plan, FTSENT *entry)
+{
+ PLAN *p;
+ int state = 0;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->execute)(p, entry)); p = p->next);
+ return state;
+}
+
+/*
+ * f_openparen and f_closeparen nodes are temporary place markers. They are
+ * eliminated during phase 2 of find_formplan() --- the '(' node is converted
+ * to a f_expr node containing the expression and the ')' node is discarded.
+ * The functions themselves are only used as constants.
+ */
+
+int
+f_openparen(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ abort();
+}
+
+int
+f_closeparen(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ abort();
+}
+
+/* c_openparen == c_simple */
+/* c_closeparen == c_simple */
+
+/*
+ * AND operator. Since AND is implicit, no node is allocated.
+ */
+PLAN *
+c_and(OPTION *option __unused, char ***argvp __unused)
+{
+ return NULL;
+}
+
+/*
+ * ! expression functions --
+ *
+ * Negation of a primary; the unary NOT operator.
+ */
+int
+f_not(PLAN *plan, FTSENT *entry)
+{
+ PLAN *p;
+ int state = 0;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->execute)(p, entry)); p = p->next);
+ return !state;
+}
+
+/* c_not == c_simple */
+
+/*
+ * 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 = 0;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->execute)(p, entry)); p = p->next);
+
+ if (state)
+ return 1;
+
+ for (p = plan->p_data[1];
+ p && (state = (p->execute)(p, entry)); p = p->next);
+ return state;
+}
+
+/* c_or == c_simple */
+
+/*
+ * -false
+ *
+ * Always false.
+ */
+int
+f_false(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ return 0;
+}
+
+/* c_false == c_simple */
+
+/*
+ * -quit
+ *
+ * Exits the program
+ */
+int
+f_quit(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ exit(0);
+}
+
+/* c_quit == c_simple */
diff --git a/shell_cmds/find/getdate.y b/shell_cmds/find/getdate.y
new file mode 100644
index 0000000..5a97c2c
--- /dev/null
+++ b/shell_cmds/find/getdate.y
@@ -0,0 +1,961 @@
+%{
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+**
+** This grammar has 10 shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
+/* SUPPRESS 288 on yyerrlab *//* Label unused */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/getdate.y,v 1.7 2010/02/09 21:24:41 ed Exp $");
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* The code at the top of get_date which figures out the offset of the
+ current time zone checks various CPP symbols to see if special
+ tricks are need, but defaults to using the gettimeofday system call.
+ Include <sys/time.h> if that will be used. */
+
+#if defined(vms)
+# include <types.h>
+#else /* defined(vms) */
+# include <sys/types.h>
+# include <sys/time.h>
+#endif /* !defined(vms) */
+
+#if defined (__STDC__) || defined (USG)
+#include <string.h>
+#endif
+
+/* Some old versions of bison generate parsers that use bcopy.
+ That loses on systems that don't provide the function, so we have
+ to redefine it here. */
+#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
+#define bcopy(from, to, len) memcpy ((to), (from), (len))
+#endif
+
+#if defined (__STDC__)
+#include <stdlib.h>
+#endif
+
+/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
+ releases):
+
+ We don't want to mess with all the portability hassles of alloca.
+ In particular, most (all?) versions of bison will use alloca in
+ their parser. If bison works on your system (e.g. it should work
+ with gcc), then go ahead and use it, but the more general solution
+ is to use byacc instead of bison, which should generate a portable
+ parser. I played with adding "#define alloca dont_use_alloca", to
+ give an error if the parser generator uses alloca (and thus detect
+ unportable getdate.c's), but that seems to cause as many problems
+ as it solves. */
+
+#include <time.h>
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+static int yyparse(void);
+static int yylex(void);
+static int yyerror(const char *);
+
+time_t get_date(char *);
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ const char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | number
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ if ($1 >= 100) {
+ yyYear = $1;
+ yyMonth = $3;
+ yyDay = $5;
+ } else {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | tUNUMBER tMONTH tSNUMBER {
+ /* e.g. 17-JUN-1992. */
+ yyDay = $1;
+ yyMonth = $2;
+ yyYear = -$3;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ yyHaveDate++;
+ yyDay= ($1)%100;
+ yyMonth= ($1/100)%100;
+ yyYear = $1/10000;
+ }
+ else {
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL, 0, 0 }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL, 0, 0 }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE const TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL, 0, 0 }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL, 0, 0 }
+};
+
+
+
+
+/* ARGSUSED */
+static int
+yyerror(const char *s __unused)
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ if (Hours == 12)
+ Hours = 0;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ if (Hours == 12)
+ Hours = 0;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+}
+
+
+/* Year is either
+ * A negative number, which means to use its absolute value (why?)
+ * A number from 0 to 99, which means a year from 1900 to 1999, or
+ * The actual year (>=100). */
+static time_t
+Convert(time_t Month, time_t Day, time_t Year,
+ time_t Hours, time_t Minutes, time_t Seconds,
+ MERIDIAN Meridian, DSTMODE DSTmode)
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 69)
+ Year += 2000;
+ else if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
+ I'm too lazy to try to check for time_t overflow in another way. */
+ if (Year < EPOCH || Year > 2038
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(time_t Start, time_t Future)
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(time_t Start, time_t RelMonth)
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(char *buff)
+{
+ char *p;
+ char *q;
+ const TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+static int
+yylex(void)
+{
+ char c;
+ char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static long
+difftm (struct tm *a, struct tm *b)
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ int days = (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay/100 - by/100)
+ + ((ay/100 >> 2) - (by/100 >> 2))
+ /* + difference in years * 365 */
+ + (long)(ay-by) * 365
+ );
+ return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec));
+}
+
+time_t
+get_date(char *p)
+{
+ struct tm *tm, *gmt_ptr, gmt;
+ int tzoff;
+ time_t Start;
+ time_t tod;
+ time_t nowtime;
+
+ bzero (&gmt, sizeof(struct tm));
+ yyInput = p;
+
+ (void)time (&nowtime);
+
+ gmt_ptr = gmtime (&nowtime);
+ if (gmt_ptr != NULL)
+ {
+ /* Make a copy, in case localtime modifies *tm (I think
+ that comment now applies to *gmt_ptr, but I am too
+ lazy to dig into how gmtime and locatime allocate the
+ structures they return pointers to). */
+ gmt = *gmt_ptr;
+ }
+
+ if (! (tm = localtime (&nowtime)))
+ return -1;
+
+ if (gmt_ptr != NULL)
+ tzoff = difftm (&gmt, tm) / 60;
+ else
+ /* We are on a system like VMS, where the system clock is
+ in local time and the system has no concept of timezones.
+ Hopefully we can fake this out (for the case in which the
+ user specifies no timezone) by just saying the timezone
+ is zero. */
+ tzoff = 0;
+
+ if(tm->tm_isdst)
+ tzoff += 60;
+
+ tm = localtime(&nowtime);
+ yyYear = tm->tm_year + 1900;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+ yyTimezone = tzoff;
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = nowtime;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+int
+main(int ac, char *av[])
+{
+ char buff[128];
+ time_t d;
+
+ (void)printf("Enter date, or blank line to exit.\n\t> ");
+ (void)fflush(stdout);
+ while (gets(buff) && buff[0]) {
+ d = get_date(buff);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("%s", ctime(&d));
+ (void)printf("\t> ");
+ (void)fflush(stdout);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/shell_cmds/find/ls.c b/shell_cmds/find/ls.c
new file mode 100644
index 0000000..f58d969
--- /dev/null
+++ b/shell_cmds/find/ls.c
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ls.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/ls.c,v 1.23 2011/09/28 18:53:36 ed Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <langinfo.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+#undef MAXLOGNAME
+#define MAXLOGNAME 17
+#endif /* __APPLE__ */
+
+#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, char *accpath, struct stat *sb)
+{
+ char modep[15];
+
+ (void)printf("%6lu %8"PRId64" ", (u_long) sb->st_ino, sb->st_blocks);
+ (void)strmode(sb->st_mode, modep);
+ (void)printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, MAXLOGNAME - 1,
+ user_from_uid(sb->st_uid, 0), MAXLOGNAME - 1,
+ group_from_gid(sb->st_gid, 0));
+
+ if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode))
+ (void)printf("%#8jx ", (uintmax_t)sb->st_rdev);
+ else
+ (void)printf("%8"PRId64" ", 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)
+{
+ char longstring[80];
+ static time_t lnow;
+ const char *format;
+ static int d_first = -1;
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ if (lnow == 0)
+ lnow = time(NULL);
+
+#define SIXMONTHS ((365 / 2) * 86400)
+ if (ftime + SIXMONTHS > lnow && ftime < lnow + SIXMONTHS)
+ /* mmm dd hh:mm || dd mmm hh:mm */
+ format = d_first ? "%e %b %R " : "%b %e %R ";
+ else
+ /* mmm dd yyyy || dd mmm yyyy */
+ format = d_first ? "%e %b %Y " : "%b %e %Y ";
+ strftime(longstring, sizeof(longstring), format, localtime(&ftime));
+ fputs(longstring, stdout);
+}
+
+static void
+printlink(char *name)
+{
+ int lnklen;
+ char path[MAXPATHLEN];
+
+ if ((lnklen = readlink(name, path, MAXPATHLEN - 1)) == -1) {
+ warn("%s", name);
+ return;
+ }
+ path[lnklen] = '\0';
+ (void)printf(" -> %s", path);
+}
diff --git a/shell_cmds/find/main.c b/shell_cmds/find/main.c
new file mode 100644
index 0000000..e95e648
--- /dev/null
+++ b/shell_cmds/find/main.c
@@ -0,0 +1,166 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/main.c,v 1.23 2011/12/10 18:11:06 ed Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <locale.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.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; /* do hierarchies in lexicographical order */
+int isxargs; /* don't permit xargs delimiting chars */
+int mindepth = -1, maxdepth = -1; /* minimum and maximum depth */
+int regexp_flags = REG_BASIC; /* use the "basic" regexp by default*/
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ char **p, **start;
+ int Hflag, Lflag, ch;
+
+ (void)setlocale(LC_ALL, "");
+
+ (void)time(&now); /* initialize the time-of-day */
+
+ p = start = argv;
+ Hflag = Lflag = 0;
+ ftsoptions = FTS_NOSTAT | FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "EHLPXdf:sx")) != -1)
+ switch (ch) {
+ case 'E':
+ regexp_flags |= REG_EXTENDED;
+ break;
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ break;
+ case 'X':
+ isxargs = 1;
+ break;
+ case 'd':
+ isdepth = 1;
+ break;
+ case 'f':
+ *p++ = optarg;
+ break;
+ case 's':
+ issort = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+ if (Lflag) {
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ }
+
+ /*
+ * Find first option to delimit the file list. The first argument
+ * that starts with a -, or is a ! or a ( must be interpreted as a
+ * part of the find expression, according to POSIX .2.
+ */
+ for (; *argv != NULL; *p++ = *argv++) {
+ if (argv[0][0] == '-')
+ break;
+ if ((argv[0][0] == '!' || argv[0][0] == '(') &&
+ argv[0][1] == '\0')
+ break;
+ }
+
+ if (p == start)
+ usage();
+ *p = NULL;
+
+ if ((dotfd = open(".", O_RDONLY, 0)) < 0)
+ err(1, ".");
+
+ exit(find_execute(find_formplan(argv), start));
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n",
+"usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]",
+" find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]");
+ exit(1);
+}
diff --git a/shell_cmds/find/misc.c b/shell_cmds/find/misc.c
new file mode 100644
index 0000000..a165c5e
--- /dev/null
+++ b/shell_cmds/find/misc.c
@@ -0,0 +1,106 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c 8.2 (Berkeley) 4/1/94";
+#else
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/misc.c,v 1.13 2010/12/11 08:32:16 joel Exp $");
+
+#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 "find.h"
+
+/*
+ * brace_subst --
+ * Replace occurrences of {} in s1 with s2 and return the result string.
+ */
+void
+brace_subst(char *orig, char **store, char *path, int len)
+{
+ int plen;
+ char ch, *p;
+
+ plen = strlen(path);
+ for (p = *store; (ch = *orig) != '\0'; ++orig)
+ if (ch == '{' && orig[1] == '}') {
+ while ((p - *store) + plen > len)
+ if (!(*store = realloc(*store, len *= 2)))
+ err(1, NULL);
+ 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 an affirmative response (according to the
+ * current locale) then 1 is returned.
+ */
+int
+queryuser(char *argv[])
+{
+ char *p, resp[256];
+
+ (void)fprintf(stderr, "\"%s", *argv);
+ while (*++argv)
+ (void)fprintf(stderr, " %s", *argv);
+ (void)fprintf(stderr, "\"? ");
+ (void)fflush(stderr);
+
+ if (fgets(resp, sizeof(resp), stdin) == NULL)
+ *resp = '\0';
+ if ((p = strchr(resp, '\n')) != NULL)
+ *p = '\0';
+ else {
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+ }
+
+ return (rpmatch(resp) == 1);
+}
diff --git a/shell_cmds/find/operator.c b/shell_cmds/find/operator.c
new file mode 100644
index 0000000..1aa851e
--- /dev/null
+++ b/shell_cmds/find/operator.c
@@ -0,0 +1,273 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)operator.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/operator.c,v 1.17 2010/12/11 08:32:16 joel Exp $");
+
+#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)
+{
+ 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 f_expr node containing a list of simple nodes.
+ */
+static PLAN *
+yankexpr(PLAN **planp)
+{
+ 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->execute == f_openparen)
+ for (tail = subplan = NULL;;) {
+ if ((next = yankexpr(planp)) == NULL)
+ errx(1, "(: missing closing ')'");
+ /*
+ * If we find a closing ')' we store the collected
+ * subplan in our '(' node and convert the node to
+ * a f_expr. The ')' we found is ignored. Otherwise,
+ * we just continue to add whatever we get to our
+ * subplan.
+ */
+ if (next->execute == f_closeparen) {
+ if (subplan == NULL)
+ errx(1, "(): empty inner expression");
+ node->p_data[0] = subplan;
+ node->execute = f_expr;
+ break;
+ } else {
+ if (subplan == NULL)
+ tail = subplan = next;
+ else {
+ tail->next = next;
+ tail = next;
+ }
+ tail->next = NULL;
+ }
+ }
+ return (node);
+}
+
+/*
+ * paren_squish --
+ * replaces "parenthesized" plans in our search plan with "expr" nodes.
+ */
+PLAN *
+paren_squish(PLAN *plan)
+{
+ 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->execute == f_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 *next; /* next node being processed */
+ PLAN *node; /* temporary node used in f_not processing */
+ PLAN *tail; /* pointer to tail of result plan */
+ PLAN *result; /* pointer to head of result plan */
+
+ tail = result = NULL;
+
+ while ((next = yanknode(&plan))) {
+ /*
+ * if we encounter a ( expression ) then look for nots in
+ * the expr subplan.
+ */
+ if (next->execute == f_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->execute == f_not) {
+ int notlevel = 1;
+
+ node = yanknode(&plan);
+ while (node != NULL && node->execute == f_not) {
+ ++notlevel;
+ node = yanknode(&plan);
+ }
+ if (node == NULL)
+ errx(1, "!: no following expression");
+ if (node->execute == f_or)
+ errx(1, "!: nothing between ! and -o");
+ /*
+ * If we encounter ! ( expr ) then look for nots in
+ * the expr subplan.
+ */
+ if (node->execute == f_expr)
+ node->p_data[0] = not_squish(node->p_data[0]);
+ 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 *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->execute == f_expr)
+ next->p_data[0] = or_squish(next->p_data[0]);
+
+ /* if we encounter a not then look for or's in the subplan */
+ if (next->execute == f_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->execute == f_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/shell_cmds/find/option.c b/shell_cmds/find/option.c
new file mode 100644
index 0000000..5771da4
--- /dev/null
+++ b/shell_cmds/find/option.c
@@ -0,0 +1,201 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)option.c 8.2 (Berkeley) 4/16/94";
+*/
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/find/option.c,v 1.32 2011/05/27 22:14:49 jilles Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fts.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "find.h"
+
+static int typecompare(const void *, const void *);
+
+/* NB: the following table must be sorted lexically. */
+/* Options listed with C++ comments are in gnu find, but not our find */
+static OPTION const options[] = {
+ { "!", c_simple, f_not, 0 },
+ { "(", c_simple, f_openparen, 0 },
+ { ")", c_simple, f_closeparen, 0 },
+ { "-Bmin", c_Xmin, f_Xmin, F_TIME_B },
+ { "-Bnewer", c_newer, f_newer, F_TIME_B },
+ { "-Btime", c_Xtime, f_Xtime, F_TIME_B },
+ { "-a", c_and, NULL, 0 },
+ { "-acl", c_acl, f_acl, 0 },
+ { "-amin", c_Xmin, f_Xmin, F_TIME_A },
+ { "-and", c_and, NULL, 0 },
+ { "-anewer", c_newer, f_newer, F_TIME_A },
+ { "-atime", c_Xtime, f_Xtime, F_TIME_A },
+ { "-cmin", c_Xmin, f_Xmin, F_TIME_C },
+ { "-cnewer", c_newer, f_newer, F_TIME_C },
+ { "-ctime", c_Xtime, f_Xtime, F_TIME_C },
+ { "-d", c_depth, f_depth, 0 },
+// -daystart
+ { "-delete", c_delete, f_delete, 0 },
+ { "-depth", c_depth, f_depth, 0 },
+ { "-empty", c_empty, f_empty, 0 },
+ { "-exec", c_exec, f_exec, 0 },
+ { "-execdir", c_exec, f_exec, F_EXECDIR },
+ { "-false", c_simple, f_false, 0 },
+ { "-flags", c_flags, f_flags, 0 },
+// -fls
+ { "-follow", c_follow, f_always_true, 0 },
+// -fprint
+// -fprint0
+// -fprintf
+ { "-fstype", c_fstype, f_fstype, 0 },
+ { "-gid", c_group, f_group, 0 },
+ { "-group", c_group, f_group, 0 },
+ { "-ignore_readdir_race",c_simple, f_always_true,0 },
+ { "-ilname", c_name, f_name, F_LINK | F_IGNCASE },
+ { "-iname", c_name, f_name, F_IGNCASE },
+ { "-inum", c_inum, f_inum, 0 },
+ { "-ipath", c_name, f_path, F_IGNCASE },
+ { "-iregex", c_regex, f_regex, F_IGNCASE },
+ { "-iwholename",c_name, f_path, F_IGNCASE },
+ { "-links", c_links, f_links, 0 },
+ { "-lname", c_name, f_name, F_LINK },
+ { "-ls", c_ls, f_ls, 0 },
+ { "-maxdepth", c_mXXdepth, f_always_true, F_MAXDEPTH },
+ { "-mindepth", c_mXXdepth, f_always_true, 0 },
+ { "-mmin", c_Xmin, f_Xmin, 0 },
+ { "-mnewer", c_newer, f_newer, 0 },
+ { "-mount", c_xdev, f_always_true, 0 },
+ { "-mtime", c_Xtime, f_Xtime, 0 },
+ { "-name", c_name, f_name, 0 },
+ { "-newer", c_newer, f_newer, 0 },
+ { "-newerBB", c_newer, f_newer, F_TIME_B | F_TIME2_B },
+ { "-newerBa", c_newer, f_newer, F_TIME_B | F_TIME2_A },
+ { "-newerBc", c_newer, f_newer, F_TIME_B | F_TIME2_C },
+ { "-newerBm", c_newer, f_newer, F_TIME_B },
+ { "-newerBt", c_newer, f_newer, F_TIME_B | F_TIME2_T },
+ { "-neweraB", c_newer, f_newer, F_TIME_A | F_TIME2_B },
+ { "-neweraa", c_newer, f_newer, F_TIME_A | F_TIME2_A },
+ { "-newerac", c_newer, f_newer, F_TIME_A | F_TIME2_C },
+ { "-neweram", c_newer, f_newer, F_TIME_A },
+ { "-newerat", c_newer, f_newer, F_TIME_A | F_TIME2_T },
+ { "-newercB", c_newer, f_newer, F_TIME_C | F_TIME2_B },
+ { "-newerca", c_newer, f_newer, F_TIME_C | F_TIME2_A },
+ { "-newercc", c_newer, f_newer, F_TIME_C | F_TIME2_C },
+ { "-newercm", c_newer, f_newer, F_TIME_C },
+ { "-newerct", c_newer, f_newer, F_TIME_C | F_TIME2_T },
+ { "-newermB", c_newer, f_newer, F_TIME2_B },
+ { "-newerma", c_newer, f_newer, F_TIME2_A },
+ { "-newermc", c_newer, f_newer, F_TIME2_C },
+ { "-newermm", c_newer, f_newer, 0 },
+ { "-newermt", c_newer, f_newer, F_TIME2_T },
+ { "-nogroup", c_nogroup, f_nogroup, 0 },
+ { "-noignore_readdir_race",c_simple, f_always_true,0 },
+ { "-noleaf", c_simple, f_always_true, 0 },
+ { "-not", c_simple, f_not, 0 },
+ { "-nouser", c_nouser, f_nouser, 0 },
+ { "-o", c_simple, f_or, 0 },
+ { "-ok", c_exec, f_exec, F_NEEDOK },
+ { "-okdir", c_exec, f_exec, F_NEEDOK | F_EXECDIR },
+ { "-or", c_simple, f_or, 0 },
+ { "-path", c_name, f_path, 0 },
+ { "-perm", c_perm, f_perm, 0 },
+ { "-print", c_print, f_print, 0 },
+ { "-print0", c_print, f_print0, 0 },
+// -printf
+ { "-prune", c_simple, f_prune, 0 },
+ { "-quit", c_simple, f_quit, 0 },
+ { "-regex", c_regex, f_regex, 0 },
+ { "-samefile", c_samefile, f_inum, 0 },
+ { "-size", c_size, f_size, 0 },
+ { "-true", c_simple, f_always_true, 0 },
+ { "-type", c_type, f_type, 0 },
+ { "-uid", c_user, f_user, 0 },
+ { "-user", c_user, f_user, 0 },
+ { "-wholename", c_name, f_path, 0 },
+#ifdef __APPLE__
+ { "-xattr", c_simple, f_xattr, 0 },
+ { "-xattrname", c_name, f_xattrname, 0 },
+#endif /* __APPLE__ */
+ { "-xdev", c_xdev, f_always_true, 0 },
+// -xtype
+};
+
+/*
+ * find_create --
+ * create a node corresponding to a command line argument.
+ *
+ * TODO:
+ * add create/process function pointers to node, so we can skip
+ * this switch stuff.
+ */
+PLAN *
+find_create(char ***argvp)
+{
+ OPTION *p;
+ PLAN *new;
+ char **argv;
+
+ argv = *argvp;
+
+ if ((p = lookup_option(*argv)) == NULL)
+ errx(1, "%s: unknown primary or operator", *argv);
+ ++argv;
+
+ new = (p->create)(p, &argv);
+ *argvp = argv;
+ return (new);
+}
+
+OPTION *
+lookup_option(const char *name)
+{
+ OPTION tmp;
+
+ tmp.name = name;
+ return ((OPTION *)bsearch(&tmp, options,
+ sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare));
+}
+
+static int
+typecompare(const void *a, const void *b)
+{
+ return (strcmp(((const OPTION *)a)->name, ((const OPTION *)b)->name));
+}