summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris PeBenito <pebenito@gentoo.org>2008-01-30 14:02:05 +0000
committerChris PeBenito <pebenito@gentoo.org>2008-01-30 14:02:05 +0000
commit3256530c13a9979c29178971d99e57550f34de55 (patch)
tree76694e22447751424645cccd6720eb7d68a0fe94 /sys-apps/findutils/files
parentsparc stable wrt #207718, remove old (diff)
downloadgentoo-2-3256530c13a9979c29178971d99e57550f34de55.tar.gz
gentoo-2-3256530c13a9979c29178971d99e57550f34de55.tar.bz2
gentoo-2-3256530c13a9979c29178971d99e57550f34de55.zip
sys-apps/findutils: update selinux patch.
(Portage version: 2.1.4)
Diffstat (limited to 'sys-apps/findutils/files')
-rw-r--r--sys-apps/findutils/files/findutils-4.3.12-selinux.diff9951
1 files changed, 9951 insertions, 0 deletions
diff --git a/sys-apps/findutils/files/findutils-4.3.12-selinux.diff b/sys-apps/findutils/files/findutils-4.3.12-selinux.diff
new file mode 100644
index 000000000000..fb772671e444
--- /dev/null
+++ b/sys-apps/findutils/files/findutils-4.3.12-selinux.diff
@@ -0,0 +1,9951 @@
+diff -purN findutils-4.3.12.orig/find/Makefile.am findutils-4.3.12/find/Makefile.am
+--- findutils-4.3.12.orig/find/Makefile.am 2007-07-22 08:29:31.000000000 -0400
++++ findutils-4.3.12/find/Makefile.am 2008-01-30 08:46:05.750843391 -0500
+@@ -2,6 +2,7 @@ AUTOMAKE_OPTIONS = std-options
+ localedir = $(datadir)/locale
+ # noinst_PROGRAMS = regexprops
+ # regexprops_SOURCES = regexprops.c
++DEFS = @DEFS@ -I. -I$(srcdir) -I.. -DWITH_SELINUX
+
+ noinst_LIBRARIES = libfindtools.a
+ libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c tree.c util.c
+@@ -26,7 +27,7 @@ endif
+
+ EXTRA_DIST = defs.h $(man_MANS)
+ INCLUDES = -I../gnulib/lib -I$(top_srcdir)/lib -I$(top_srcdir)/gnulib/lib -I../intl -DLOCALEDIR=\"$(localedir)\"
+-LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@
++LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -lselinux
+ man_MANS = find.1
+ SUBDIRS = . testsuite
+
+diff -purN findutils-4.3.12.orig/find/Makefile.in findutils-4.3.12/find/Makefile.in
+--- findutils-4.3.12.orig/find/Makefile.in 2007-12-19 16:27:24.000000000 -0500
++++ findutils-4.3.12/find/Makefile.in 2008-01-30 08:46:05.750843391 -0500
+@@ -275,7 +275,7 @@ CPP = @CPP@
+ CPPFLAGS = @CPPFLAGS@
+ CYGPATH_W = @CYGPATH_W@
+ DEFAULT_ARG_SIZE = @DEFAULT_ARG_SIZE@
+-DEFS = @DEFS@
++DEFS = @DEFS@ -I. -I$(srcdir) -I.. -DWITH_SELINUX
+ DEPDIR = @DEPDIR@
+ DIRENT_H = @DIRENT_H@
+ ECHO_C = @ECHO_C@
+@@ -601,7 +601,7 @@ libfindtools_a_SOURCES = finddata.c fsty
+ @WITH_FTS_TRUE@oldfind_SOURCES = find.c
+ @WITH_FTS_FALSE@ftsfind_SOURCES = ftsfind.c
+ EXTRA_DIST = defs.h $(man_MANS)
+-LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@
++LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -lselinux
+ man_MANS = find.1
+ SUBDIRS = . testsuite
+ all: all-recursive
+diff -purN findutils-4.3.12.orig/find/defs.h findutils-4.3.12/find/defs.h
+--- findutils-4.3.12.orig/find/defs.h 2007-11-30 05:19:58.000000000 -0500
++++ findutils-4.3.12/find/defs.h 2008-01-30 08:46:05.754843619 -0500
+@@ -90,6 +90,9 @@ int get_statinfo PARAMS((const char *pat
+ #define MODE_RWX (S_IXUSR | S_IXGRP | S_IXOTH | MODE_RW)
+ #define MODE_ALL (S_ISUID | S_ISGID | S_ISVTX | MODE_RWX)
+
++#ifdef WITH_SELINUX
++#include <selinux/selinux.h>
++#endif /*WITH_SELINUX*/
+
+ struct predicate;
+ struct options;
+@@ -314,6 +317,9 @@ struct predicate
+ struct samefile_file_id samefileid; /* samefile */
+ mode_t type; /* type */
+ struct format_val printf_vec; /* printf fprintf fprint ls fls print0 fprint0 print */
++#ifdef WITH_SELINUX
++ security_context_t scontext; /* scontext */
++#endif /*WITH_SELINUX*/
+ } args;
+
+ /* The next predicate in the user input sequence,
+@@ -458,7 +464,9 @@ PREDICATEFUNCTION pred_used;
+ PREDICATEFUNCTION pred_user;
+ PREDICATEFUNCTION pred_writable;
+ PREDICATEFUNCTION pred_xtype;
+-
++#ifdef WITH_SELINUX
++PREDICATEFUNCTION pred_scontext;
++#endif /* WITH_SELINUX */
+
+
+ int launch PARAMS((const struct buildcmd_control *ctl,
+@@ -605,10 +613,13 @@ struct options
+ */
+ unsigned short optimisation_level;
+
+-
+ /* How should we quote filenames in error messages and so forth?
+ */
+ enum quoting_style err_quoting_style;
++
++#ifdef WITH_SELINUX
++ int (*x_getfilecon)();
++#endif /* WITH_SELINUX */
+ };
+ extern struct options options;
+
+diff -purN findutils-4.3.12.orig/find/find.1 findutils-4.3.12/find/find.1
+--- findutils-4.3.12.orig/find/find.1 2007-12-19 14:53:14.000000000 -0500
++++ findutils-4.3.12/find/find.1 2008-01-30 08:49:54.983906639 -0500
+@@ -935,6 +935,10 @@ checks the type of the file that
+ .B \-type
+ does not check.
+
++.IP "\-context \fIscontext\fR"
++.IP "\--context \fIscontext\fR"
++(SELinux only) File has the security context \fIscontext\fR.
++
+ .SS ACTIONS
+ .IP "\-delete\fR"
+ Delete files; true if removal succeeded. If the removal failed, an
+@@ -1340,6 +1344,8 @@ File's type (like in
+ U=unknown type (shouldn't happen)
+ .IP %Y
+ File's type (like %y), plus follow symlinks: L=loop, N=nonexistent
++.IP %Z
++(SELinux only) file's security context.
+ .PP
+ A `%' character followed by any other character is discarded, but the
+ other character is printed (don't rely on this, as further format
+diff -purN findutils-4.3.12.orig/find/find.1.orig findutils-4.3.12/find/find.1.orig
+--- findutils-4.3.12.orig/find/find.1.orig 1969-12-31 19:00:00.000000000 -0500
++++ findutils-4.3.12/find/find.1.orig 2007-12-19 14:53:14.000000000 -0500
+@@ -0,0 +1,2027 @@
++.TH FIND 1 \" -*- nroff -*-
++.SH NAME
++find \- search for files in a directory hierarchy
++.SH SYNOPSIS
++.B find
++[\-H] [\-L] [\-P] [\-D debugopts] [\-Olevel] [path...] [expression]
++.SH DESCRIPTION
++This manual page
++documents the GNU version of
++.BR find .
++GNU
++.B find
++searches the directory tree rooted at each given file name by
++evaluating the given expression from left to right, according to the
++rules of precedence (see section OPERATORS), until the outcome is
++known (the left hand side is false for \fIand\fR operations, true for
++\fIor\fR), at which point
++.B find
++moves on to the next file name.
++.PP
++If you are using
++.B find
++in an environment where security is important (for example if you are
++using it to seach directories that are writable by other users), you
++should read the "Security Considerations" chapter of the findutils
++documentation, which is called \fBFinding Files\fP and comes with
++findutils. That document also includes a lot more detail
++and discussion than this manual page, so you may find it a more useful
++source of information.
++.SH OPTIONS
++The
++.BR \-H ,
++.B \-L
++and
++.B \-P
++options control the treatment of symbolic
++links. Command-line arguments following these are taken to be names
++of files or directories to be examined, up to the first argument that
++begins with `\-', or the argument `(' or `!'. That argument and any
++following arguments are taken to be the expression describing what is
++to be searched for. If no paths are given, the current directory is
++used. If no expression is given, the expression
++.B \-print
++is used
++(but you should probably consider using
++.B \-print0
++instead, anyway).
++.PP
++This manual page talks about `options' within the expression list.
++These options control the behaviour of
++.B find
++but are specified immediately after the last path name. The five
++`real' options
++.BR \-H ,
++.BR \-L ,
++.BR \-P ,
++.B \-D
++and
++.B \-O
++must appear before
++the first path name, if at all. A double dash
++.B \-\-
++can also be used
++to signal that any remaining arguments are not options (though
++ensuring that all start points begin with either `./' or `/' is
++generally safer if you use wildcards in the list of start points).
++.IP \-P
++Never follow symbolic links. This is the default behaviour. When
++.B find
++examines or prints information a file, and the file is a symbolic
++link, the information used shall be taken from the properties of the
++symbolic link itself.
++
++.IP \-L
++Follow symbolic links. When
++.B find
++examines or prints information about files, the information used shall
++be taken from the properties of the file to which the link points, not
++from the link itself (unless it is a broken symbolic link or
++.B find
++is unable to examine the file to which the link points). Use of this
++option implies
++.BR \-noleaf .
++If you later use the
++.B \-P
++option,
++.B \-noleaf
++will still be in effect. If
++.B \-L
++is in effect and
++.B find
++discovers a symbolic link to a subdirectory during its search,
++the subdirectory pointed to by the symbolic link will be searched.
++.IP
++When the
++.B \-L
++option is in effect, the
++.B \-type
++predicate will always
++match against the type of the file that a symbolic link points to
++rather than the link itself (unless the symbolic link is broken).
++Using
++.B \-L
++causes the
++.B \-lname
++and
++.B \-ilname
++predicates always to return
++false.
++
++.IP \-H
++Do not follow symbolic links, except while processing the command
++line arguments. When
++.B find
++examines or prints information about files, the information used
++shall be taken from the properties of the symbolic link itself. The
++only exception to this behaviour is when a file specified on the
++command line is a symbolic link, and the link can be resolved. For
++that situation, the information used is taken from whatever the link
++points to (that is, the link is followed). The information about the
++link itself is used as a fallback if the file pointed to by the
++symbolic link cannot be examined. If
++.B \-H
++is in effect and one of the
++paths specified on the command line is a symbolic link to a directory,
++the contents of that directory will be examined (though of course
++\-maxdepth 0 would prevent this).
++.P
++If more than one of
++.BR \-H ,
++.B \-L
++and
++.B \-P
++is specified, each overrides the
++others; the last one appearing on the command line takes effect.
++Since it is the default, the
++.B \-P
++option should be considered to be in
++effect unless either
++.B \-H
++or
++.B \-L
++is specified.
++
++GNU
++.B find
++frequently stats files during the processing of the command line
++itself, before any searching has begun. These options also affect how
++those arguments are processed. Specifically, there are a number of
++tests that compare files listed on the command line against a file we
++are currently considering. In each case, the file specified on the
++command line will have been examined and some of its properties will
++have been saved. If the named file is in fact a symbolic link, and
++the
++.B \-P
++option is in effect (or if neither
++.B \-H
++nor
++.B \-L
++were specified), the information used for the comparison will be taken from
++the properties of the symbolic link. Otherwise, it will be taken from
++the properties of the file the link points to. If
++.B find
++cannot follow the link (for example because it has insufficient
++privileges or the link points to a nonexistent file) the properties of
++the link itself will be used.
++.P
++When the
++.B \-H
++or
++.B \-L options are in effect, any symbolic links listed
++as the argument of
++.B \-newer
++will be dereferenced, and the timestamp
++will be taken from the file to which the symbolic link points. The
++same consideration applies to
++.BR \-newerXY ,
++.B \-anewer
++and
++.BR \-cnewer .
++
++The
++.B \-follow
++option has a similar effect to
++.BR \-L ,
++though it takes
++effect at the point where it appears (that is, if
++.B \-L
++is not used but
++.B \-follow
++is, any symbolic links appearing after
++.B \-follow
++on the
++command line will be dereferenced, and those before it will not).
++
++.IP "\-D debugoptions"
++Print diagnostic information; this can be helpful to diagnose problems
++with why
++.B find
++is not doing what you want. The list of debug options should be comma
++separated. Compatibility of the debug options is not guaranteed
++between releases of findutils. For a complete list of valid debug
++options, see the output of
++.B find \-D
++.BR help .
++Valid debug options include
++.RS
++.IP help
++Explain the debugging options
++.IP tree
++Show the expression tree in its original and optimised form.
++.IP stat
++Print messages as files are examined with the
++.B stat
++and
++.B lstat
++system calls. The
++.B find
++program tries to minimise such calls.
++.IP opt
++Prints diagnostic information relating to the optimisation of the
++expression tree; see the \-O option.
++.IP rates
++Prints a summary indicating how often each predicate succeeded or
++failed.
++.RE
++.IP \-Olevel
++Enables query optimisation. The
++.B find
++program reorders tests to speed up execution while preserving the
++overall effect; that is, predicates with side effects are not
++reordered relative to each other. The optimisations performed at each
++optimisation level are as follows.
++.RS
++.IP 0
++Equivalent to optimisation level 1.
++.IP 1
++This is the default optimisation level and corresponds to the
++traditional behaviour. Expressions are reordered so that tests based
++only on the names of files (for example
++.B \-name
++and
++.BR \-regex )
++are performed first.
++.IP 2
++Any
++.B \-type
++or
++.B \-xtype
++tests are performed after any tests based only on the names of files,
++but before any tests that require information from the inode. On many
++modern versions of Unix, file types are returned by
++.B readdir()
++and so these predicates are faster to evaluate than predicates which
++need to stat the file first.
++.IP 3
++At this optimisation level, the full cost-based query optimiser is
++enabled. The order of tests is modified so that cheap (i.e. fast)
++tests are performed first and more expensive ones are performed later,
++if necessary. Within each cost band, predicates are evaluated earlier
++or later according to whether they are likely to succeed or not. For
++.BR \-o ,
++predicates which are likely to succeed are evaluated earlier, and for
++.BR \-a ,
++predicates which are likely to fail are evaluated earlier.
++.RE
++.IP
++The cost-based optimiser has a fixed idea of how likely any given test
++is to succeed. In some cases the probability takes account of the
++specific nature of the test (for example,
++.B \-type f
++is assumed to be more likely to succeed than
++.BR "\-type c" ).
++The cost-based optimiser is currently being evaluated. If it does
++not actually improve the performance of
++.BR find ,
++it will be removed again. Conversely, optimisations that prove to be
++reliable, robust and effective may be enabled at lower optimisation
++levels over time. However, the default behaviour (i.e. optimisation
++level 1) will not be changed in the 4.3.x release series. The
++findutils test suite runs all the tests on
++.B find
++at each optimisation level and ensures that the result is the same.
++.P
++.SH EXPRESSIONS
++The expression is made up of options (which affect overall operation
++rather than the processing of a specific file, and always return
++true), tests (which return a true or false value), and actions (which
++have side effects and return a true or false value), all separated by
++operators.
++.B \-and
++is assumed where the operator is omitted.
++
++If the expression contains no actions other than
++.BR \-prune ,
++.B \-print
++is
++performed on all files for which the expression is true.
++
++.SS OPTIONS
++.P
++All options always return true. Except for
++.BR \-daystart ,
++.B \-follow
++and
++.BR \-regextype ,
++the options affect all tests, including tests specified
++before the option. This is because the options are processed when the
++command line is parsed, while the tests don't do anything until files
++are examined. The
++.BR \-daystart ,
++.B \-follow
++and
++.B \-regextype
++options are different in this respect, and have an effect only on tests which
++appear later in the command line. Therefore, for clarity, it is best
++to place them at the beginning of the expression. A warning is issued
++if you don't do this.
++
++.IP \-d
++A synonym for \-depth, for compatibility with FreeBSD, NetBSD, MacOS X and OpenBSD.
++
++.IP \-daystart
++Measure times (for
++.BR \-amin ,
++.BR \-atime ,
++.BR \-cmin ,
++.BR \-ctime ,
++.BR \-mmin ,
++and
++.BR \-mtime )
++from the beginning of today rather than from 24 hours ago. This
++option only affects tests which appear later on the command line.
++
++.IP \-depth
++Process each directory's contents before the directory itself. The
++\-delete action also implies
++.BR \-depth .
++
++.IP \-follow
++Deprecated; use the
++.B \-L
++option instead. Dereference symbolic links.
++Implies
++.BR \-noleaf .
++The
++.B \-follow
++option affects only those tests which
++appear after it on the command line. Unless the
++.B \-H
++or
++.B \-L
++option has
++been specified, the position of the
++.B \-follow
++option changes the behaviour of the
++.B \-newer
++predicate; any files listed as the argument
++of
++.B \-newer
++will be dereferenced if they are symbolic links. The same
++consideration applies to
++.BR \-newerXY ,
++.B \-anewer
++and
++.BR \-cnewer .
++Similarly, the
++.B \-type
++predicate will always match against the type of the file
++that a symbolic link points to rather than the link itself. Using
++.B \-follow
++causes the
++.B \-lname and
++.B \-ilname
++predicates always to return false.
++
++.IP "\-help, \-\-help"
++Print a summary of the command-line usage of
++.B find
++and exit.
++
++.IP \-ignore_readdir_race
++Normally, \fBfind\fR will emit an error message when it fails to stat a file.
++If you give this option and a file is deleted between the time \fBfind\fR
++reads the name of the file from the directory and the time it tries to stat
++the file, no error message will be issued. This also applies to files
++or directories whose names are given on the command line. This option takes
++effect at the time the command line is read, which means that you cannot search
++one part of the filesystem with this option on and part of it with this option
++off (if you need to do that, you will need to issue two \fBfind\fR commands
++instead, one with the option and one without it).
++
++.IP "\-maxdepth \fIlevels\fR"
++Descend at most \fIlevels\fR (a non-negative integer) levels of
++directories below the command line arguments.
++.B \-maxdepth 0
++ means only apply the tests and actions to the command line arguments.
++
++.IP "\-mindepth \fIlevels\fR"
++Do not apply any tests or actions at levels less than \fIlevels\fR (a
++non-negative integer).
++.B \-mindepth 1
++means process all files except the command line arguments.
++
++.IP \-mount
++Don't descend directories on other filesystems. An alternate name for
++.BR \-xdev ,
++for compatibility with some other versions of
++.BR find .
++
++.IP \-noignore_readdir_race
++Turns off the effect of
++.BR \-ignore_readdir_race .
++
++.IP "\-noleaf"
++Do not optimize by assuming that directories contain 2 fewer
++subdirectories than their hard link count. This option is needed when
++searching filesystems that do not follow the Unix directory-link
++convention, such as CD-ROM or MS-DOS filesystems or AFS volume mount
++points. Each directory on a normal Unix filesystem has at least 2
++hard links: its name and its `.' entry. Additionally, its
++subdirectories (if any) each have a `..' entry linked to that
++directory. When
++.B find
++is examining a directory, after it has statted 2 fewer subdirectories
++than the directory's link count, it knows that the rest of the entries
++in the directory are non-directories (`leaf' files in the directory
++tree). If only the files' names need to be examined, there is no need
++to stat them; this gives a significant increase in search speed.
++
++.IP "\-regextype \fItype\fR"
++Changes the regular expression syntax understood by
++.B \-regex
++and
++.B \-iregex
++tests which occur later on the command line. Currently-implemented
++types are emacs (this is the default), posix-awk, posix-basic,
++posix-egrep and posix-extended.
++
++.IP "\-version, \-\-version"
++Print the \fBfind\fR version number and exit.
++
++.IP "\-warn, \-nowarn"
++Turn warning messages on or off. These warnings apply only to the
++command line usage, not to any conditions that
++.B find
++might encounter when it searches directories. The default behaviour
++corresponds to
++.B \-warn
++if standard input is a tty, and to
++.B \-nowarn
++otherwise.
++
++.IP \-xdev
++Don't descend directories on other filesystems.
++
++.SS TESTS
++Some tests, for example
++.B \-newerXY
++and
++.BR -samefile ,
++allow comparison between the file currently being examined and some
++reference file specified on the command line. When these tests are
++used, the interpretation of the reference file is determined by the
++options
++.BR \-H ,
++.B \-L
++and
++.B \-P
++and any previous
++.BR \-follow ,
++but the reference file is only examined once, at the time the command
++line is parsed. If the reference file cannot be examined (for
++example, the
++.BR stat (2)
++system call fails for it), an error message is issued, and
++.B find
++exits with a nonzero status.
++.P
++Numeric arguments can be specified as
++.IP \fI+n\fP
++for greater than
++.IR n ,
++.IP \fI\-n\fP
++for less than
++.IR n ,
++.IP \fIn\fP
++for exactly
++.IR n .
++.P
++
++.IP "\-amin \fIn\fR"
++File was last accessed \fIn\fR minutes ago.
++
++.IP "\-anewer \fIfile\fR"
++File was last accessed more recently than \fIfile\fR was modified. If
++\fIfile\fR is a symbolic link and the
++.B \-H
++option or the
++.B \-L
++option is in effect, the access time of the file it points to is
++always used.
++
++.IP "\-atime \fIn\fR"
++File was last accessed \fIn\fR*24 hours ago.
++When find figures out how many 24-hour periods ago the file
++was last accessed, any fractional part is ignored, so to match
++.B \-atime
++.BR +1 ,
++a file has to have been accessed at least
++.I two
++days ago.
++
++.IP "\-cmin \fIn\fR"
++File's status was last changed \fIn\fR minutes ago.
++
++.IP "\-cnewer \fIfile\fR"
++File's status was last changed more recently than \fIfile\fR was
++modified. If \fIfile\fR is a symbolic link and the
++.B \-H
++option or the
++.B \-L
++option is in effect, the status-change time of the file it points
++to is always used.
++
++.IP "\-ctime \fIn\fR"
++File's status was last changed \fIn\fR*24 hours ago.
++See the comments for
++.B \-atime
++to understand how rounding affects the interpretation of file status
++change times.
++
++.IP \-empty
++File is empty and is either a regular file or a directory.
++
++.IP \-executable
++Matches files which are executable and directories which are
++searchable (in a file name resolution sense). This takes into account
++access control lists and other permissions artefacts which the
++.B \-perm
++test ignores. This test makes use of the
++.BR access (2)
++system call, and so can be fooled by NFS servers which do UID
++mapping (or root-squashing), since many systems implement
++.BR access (2)
++in the client's kernel and so cannot make use of the UID mapping
++information held on the server. Because this test is based only on
++the result of the
++.BR access (2)
++system call, there is no guarantee that a file for which this test
++succeeds can actually be executed.
++
++.IP \-false
++Always false.
++
++.IP "\-fstype \fItype\fR"
++File is on a filesystem of type \fItype\fR. The valid filesystem
++types vary among different versions of Unix; an incomplete list of
++filesystem types that are accepted on some version of Unix or another
++is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use
++.B \-printf
++with the %F directive to see the types of your filesystems.
++
++.IP "\-gid \fIn\fR"
++File's numeric group ID is \fIn\fR.
++
++.IP "\-group \fIgname\fR"
++File belongs to group \fIgname\fR (numeric group ID allowed).
++
++.IP "\-ilname \fIpattern\fR"
++Like
++.BR \-lname ,
++but the match is case insensitive.
++If the
++.B \-L
++option or the
++.B \-follow
++option is in effect, this test returns false unless the symbolic link
++is broken.
++
++.IP "\-iname \fIpattern\fR"
++Like
++.BR \-name ,
++but the match is case insensitive. For example, the
++patterns `fo*' and `F??' match the file names `Foo', `FOO', `foo',
++`fOo', etc. In these patterns, unlike filename expansion by the
++shell, an initial '.' can be matched by `*'. That is,
++.B find \-name *bar
++will match the file `.foobar'. Please note that you should quote
++patterns as a matter of course, otherwise the shell will expand any
++wildcard characters in them.
++
++.IP "\-inum \fIn\fR"
++File has inode number \fIn\fR. It is normally easier to use the
++.B \-samefile
++test instead.
++
++.IP "\-ipath \fIpattern\fR"
++Behaves in the same way as
++.BR \-iwholename .
++This option is deprecated, so please do not use it.
++
++.IP "\-iregex \fIpattern\fR"
++Like
++.BR \-regex ,
++but the match is case insensitive.
++
++.IP "\-iwholename \fIpattern\fR"
++Like
++.BR \-wholename ,
++but the match is case insensitive.
++
++.IP "\-links \fIn\fR"
++File has \fIn\fR links.
++
++.IP "\-lname \fIpattern\fR"
++File is a symbolic link whose contents match shell pattern
++\fIpattern\fR. The metacharacters do not treat `/' or `.' specially.
++If the
++.B \-L
++option or the
++.B \-follow
++option is in effect, this test returns false unless the symbolic link
++is broken.
++
++.IP "\-mmin \fIn\fR"
++File's data was last modified \fIn\fR minutes ago.
++
++.IP "\-mtime \fIn\fR"
++File's data was last modified \fIn\fR*24 hours ago.
++See the comments for
++.B \-atime
++to understand how rounding affects the interpretation of file
++modification times.
++
++.IP "\-name \fIpattern\fR"
++Base of file name (the path with the leading directories removed)
++matches shell pattern \fIpattern\fR. The metacharacters (`*', `?',
++and `[]') match a `.' at the start of the base name (this is a change
++in findutils-4.2.2; see section STANDARDS CONFORMANCE below). To ignore a
++directory and the files under it, use
++.BR \-prune ;
++see an example in the
++description of
++.BR \-path .
++Braces are not recognised as being
++special, despite the fact that some shells including Bash imbue braces
++with a special meaning in shell patterns. The filename matching is
++performed with the use of the
++.BR fnmatch (3)
++library function. Don't forget to enclose the pattern in quotes
++in order to protect it from expansion by the shell.
++
++.IP "\-newer \fIfile\fR"
++File was modified more recently than \fIfile\fR. If \fIfile\fR is a
++symbolic link and the
++.B \-H
++option or the
++.B \-L
++option is in effect, the
++modification time of the file it points to is always used.
++
++.IP "\-newerXY \fIreference\fR"
++Compares the timestamp of the current file with \fIreference\fR.
++The
++.I reference
++argument is normally the name of a file (and one of its timestamps is
++used for the comparison) but it may also be a string describing an
++absolute time.
++.I X
++and
++.I Y
++are placeholders for other letters, and these letters select which
++time belonging to
++how
++.I reference
++is used for the comparison.
++.TS
++ll
++ll
++ll
++ll
++llw(2i).
++a The access time of the file \fIreference\fR
++B The birth time of the file \fIreference\fR
++c The inode status change time of \fIreference\fR
++m The modification time of the file \fIreference\fR
++t \fIreference\fR is interpreted directly as a time
++.TE
++
++Some combinations are invalid; for example, it is invalid for
++.I X
++to be
++.IR t .
++Some combinations are not implemented on all systems; for example
++.I B
++is not supported on all systems. If an invalid or unsupported
++combination of
++.I XY
++is specified, a fatal error results. Time specifications are
++interpreted as for the argument to the
++.B \-d
++option of GNU
++.BR date .
++If you try to use the birth time of a reference file, and the birth
++time cannot be determined, a fatal error message results. If you
++specify a test which refers to the birth time of files being examined,
++this test will fail for any files where the birth time is unknown.
++
++.IP \-nogroup
++No group corresponds to file's numeric group ID.
++
++.IP \-nouser
++No user corresponds to file's numeric user ID.
++
++.IP "\-path \fIpattern\fR"
++File name matches shell pattern \fIpattern\fR. The metacharacters do
++not treat `/' or `.' specially; so, for example,
++.br
++.in +1i
++find . \-path "./sr*sc"
++.br
++.in -1i
++will print an entry for a directory called `./src/misc' (if one
++exists). To ignore a whole directory tree, use
++.B \-prune
++rather than
++checking every file in the tree. For example, to skip the
++directory `src/emacs' and all files and directories under it, and
++print the names of the other files found, do something like this:
++.br
++.in +1i
++find . \-path ./src/emacs \-prune \-o \-print
++.br
++.in -1i
++Note that the pattern match test applies to the whole file name,
++starting from one of the start points named on the command line. It
++would only make sense to use an absolute path name here if the
++relevant start point is also an absolute path. This means that this
++command will never match anything:
++.br
++.in +1i
++find bar \-path /foo/bar/myfile \-print
++.br
++.in -1i
++The predicate
++.B \-path
++is also supported by HP-UX
++.B find
++and will be in a forthcoming version of the POSIX standard.
++
++.IP "\-perm \fImode\fR"
++File's permission bits are exactly \fImode\fR (octal or symbolic).
++Since an exact match is required, if you want to use this form for
++symbolic modes, you may have to specify a rather complex mode string.
++For example
++.B \-perm g=w
++will only match files which have mode 0020
++(that is, ones for which group write permission is the only permission
++set). It is more likely that you will want to use the `/' or `-'
++forms, for example
++.BR "\-perm \-g=w" ,
++which matches any file with group write permission. See the
++.B EXAMPLES
++section for some illustrative examples.
++
++.IP "\-perm \-\fImode\fR"
++All of the permission bits \fImode\fR are set for the file.
++Symbolic modes are accepted in this form, and this is usually the way
++in which would want to use them. You must specify `u', `g' or `o' if
++you use a symbolic mode. See the
++.B EXAMPLES
++section for some illustrative examples.
++
++.IP "\-perm /\fImode\fR"
++Any of the permission bits \fImode\fR are set for the file. Symbolic
++modes are accepted in this form. You must specify `u', `g' or `o' if
++you use a symbolic mode. See the
++.B EXAMPLES
++section for some illustrative examples. If no permission bits in
++.I mode
++are set, this test currently matches no files. However, it will soon
++be changed to match any file (the idea is to be more consistent with
++the behaviour of
++.B \-perm
++.BR \-000 ).
++
++.IP "\-perm +\fImode\fR"
++Deprecated, old way of searching for files with any of the permission
++bits in \fImode\fR set. You should use
++.B \-perm \fI/mode\fR
++instead. Trying to use the `+' syntax with symbolic modes will yield
++surprising results. For example, `+u+x' is a valid symbolic mode
++(equivalent to +u,+x, i.e. 0111) and will therefore not be evaluated
++as
++.B \-perm +\fImode\fR
++but instead as the exact mode specifier
++.B \-perm \fImode\fR
++and so it matches files with exact permissions 0111 instead of files with any
++execute bit set. If you found this paragraph confusing, you're not
++alone - just use
++.B \-perm /\fImode\fR.
++This form of the
++.B \-perm
++test is deprecated because the POSIX specification requires the
++interpretation of a leading `+' as being part of a symbolic mode, and
++so we switched to using `/' instead.
++
++.IP \-readable
++Matches files which are readable. This takes into account access
++control lists and other permissions artefacts which the
++.B \-perm
++test ignores. This test makes use of the
++.BR access (2)
++system call, and so can be fooled by NFS servers which do UID
++mapping (or root-squashing), since many systems implement
++.BR access (2)
++in the client's kernel and so cannot make use of the UID mapping
++information held on the server.
++
++.IP "\-regex \fIpattern\fR"
++File name matches regular expression \fIpattern\fR. This is a match
++on the whole path, not a search. For example, to match a file named
++`./fubar3', you can use the regular expression `.*bar.' or `.*b.*3',
++but not `f.*r3'. The regular expressions understood by
++.B find
++are by default Emacs Regular Expressions, but this can be
++changed with the
++.B \-regextype
++option.
++
++.IP "\-samefile \fIname\fR"
++File refers to the same inode as \fIname\fR. When
++.B \-L
++is in effect, this can include symbolic links.
++
++.IP "\-size \fIn\fR[cwbkMG]"
++File uses \fIn\fP units of space. The following suffixes
++can be used:
++.RS
++.IP `b'
++for 512-byte blocks (this is the default if no suffix is used)
++.IP `c'
++for bytes
++.IP `w'
++for two-byte words
++.IP `k'
++for Kilobytes (units of 1024 bytes)
++.IP `M'
++for Megabytes (units of 1048576 bytes)
++.IP `G'
++for Gigabytes (units of 1073741824 bytes)
++.RE
++.IP
++The size does not count indirect blocks, but it does count blocks in
++sparse files that are not actually allocated. Bear in mind that the
++`%k' and `%b' format specifiers of
++.B \-printf
++handle sparse files
++differently. The `b' suffix always denotes 512-byte blocks and never
++1 Kilobyte blocks, which is different to the behaviour of
++.BR \-ls .
++
++.IP \-true
++Always true.
++
++.IP "\-type \fIc\fR"
++File is of type \fIc\fR:
++.RS
++.IP b
++block (buffered) special
++.IP c
++character (unbuffered) special
++.IP d
++directory
++.IP p
++named pipe (FIFO)
++.IP f
++regular file
++.IP l
++symbolic link; this is never true if the
++.B \-L
++option or the
++.B \-follow
++option is in effect, unless the symbolic link is broken. If you want
++to search for symbolic links when
++.B \-L
++is in effect, use
++.BR \-xtype .
++.IP s
++socket
++.IP D
++door (Solaris)
++.RE
++.IP "\-uid \fIn\fR"
++File's numeric user ID is \fIn\fR.
++
++.IP "\-used \fIn\fR"
++File was last accessed \fIn\fR days after its status was last changed.
++
++.IP "\-user \fIuname\fR"
++File is owned by user \fIuname\fR (numeric user ID allowed).
++
++.IP "\-wholename \fIpattern\fR"
++See \-path. This alternative is less portable than
++.BR \-path .
++
++.IP "\-writable"
++Matches files which are writable. This takes into account access
++control lists and other permissions artefacts which the
++.B \-perm
++test ignores. This test makes use of the
++.BR access (2)
++system call, and so can be fooled by NFS servers which do UID
++mapping (or root-squashing), since many systems implement
++.BR access (2)
++in the client's kernel and so cannot make use of the UID mapping
++information held on the server.
++
++.IP "\-xtype \fIc\fR"
++The same as
++.B \-type
++unless the file is a symbolic link. For symbolic
++links: if the
++.B \-H
++or
++.B \-P
++option was specified, true if the file is a
++link to a file of type \fIc\fR; if the
++.B \-L
++option has been given, true
++if \fIc\fR is `l'. In other words, for symbolic links,
++.B \-xtype
++checks the type of the file that
++.B \-type
++does not check.
++
++.SS ACTIONS
++.IP "\-delete\fR"
++Delete files; true if removal succeeded. If the removal failed, an
++error message is issued.
++If
++.B \-delete
++fails,
++.BR find 's
++exit status will be nonzero
++(when it eventually exits).
++Use of
++.B \-delete
++automatically turns on the
++.B \-depth
++option.
++
++.BR Warnings :
++Don't forget that the find command line is
++evaluated as an expression, so putting
++.B \-delete
++first will make
++.B find
++try to delete everything below the starting points you specified.
++When testing a
++.B find
++command line that you later intend to use with
++.BR \-delete ,
++you should explicitly specify
++.B \-depth
++in order to avoid later surprises. Because
++.B \-delete
++implies
++.BR \-depth ,
++you cannot usefully use
++.B \-prune
++and
++.B \-delete
++together.
++
++.IP "\-exec \fIcommand\fR ;"
++Execute \fIcommand\fR; true if 0 status is returned. All following
++arguments to
++.B find
++are taken to be arguments to the command until an argument consisting
++of `;' is encountered. The string `{}' is replaced by the current
++file name being processed everywhere it occurs in the arguments to the
++command, not just in arguments where it is alone, as in some versions
++of
++.BR find .
++Both of these constructions might need to be escaped (with a `\e') or
++quoted to protect them from expansion by the shell. See the
++.B EXAMPLES
++section for examples of the use of the
++.B \-exec
++option. The specified
++command is run once for each matched file.
++The command is executed in the starting directory. There are
++unavoidable security problems surrounding use of the
++.B \-exec
++action;
++you should use the
++.B \-execdir
++option instead.
++
++.IP "\-exec \fIcommand\fR {} +"
++This variant of the
++.B \-exec
++action runs the specified command on the
++selected files, but the command line is built by appending each
++selected file name at the end; the total number of invocations of the
++command will be much less than the number of matched files. The
++command line is built in much the same way that
++.B xargs
++builds its command lines. Only one instance of `{}' is allowed within
++the command. The command is executed in the starting directory.
++
++.IP "\-execdir \fIcommand\fR ;"
++.IP "\-execdir \fIcommand\fR {} +"
++Like
++.BR \-exec ,
++but the specified command is run from the subdirectory
++containing the matched file, which is not normally the directory in
++which you started
++.BR find .
++This a much more secure method for invoking commands, as it avoids
++race conditions during resolution of the paths to the matched files.
++As with the
++.B \-exec
++action, the `+' form of
++.B \-execdir
++will build a
++command line to process more than one matched file, but any given
++invocation of
++.I command
++will only list files that exist in the same subdirectory. If you use
++this option, you must ensure that your
++.B $PATH
++environment variable does not reference `.';
++otherwise, an attacker can run any commands they like by leaving an
++appropriately-named file in a directory in which you will run
++.BR \-execdir .
++The same applies to having entries in
++.B $PATH
++which are empty or which are not absolute directory names.
++
++.IP "\-fls \fIfile\fR"
++True; like
++.B \-ls
++but write to \fIfile\fR like
++.BR \-fprint .
++The output file is always created, even if the predicate is never
++matched.
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++.IP "\-fprint \fIfile\fR"
++True; print the full file name into file \fIfile\fR. If \fIfile\fR
++does not exist when \fBfind\fR is run, it is created; if it does
++exist, it is truncated. The file names ``/dev/stdout'' and
++``/dev/stderr'' are handled specially; they refer to the standard
++output and standard error output, respectively.
++The output file is always created, even if the predicate is never matched.
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++.IP "\-fprint0 \fIfile\fR"
++True; like
++.B \-print0
++but write to \fIfile\fR like
++.BR \-fprint .
++The output file is always created, even if the predicate is never matched.
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++.IP "\-fprintf \fIfile\fR \fIformat\fR"
++True; like
++.B \-printf
++but write to \fIfile\fR like
++.BR \-fprint .
++The output file is always created, even if the predicate is never matched.
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++.IP \-ls
++True; list current file in
++.B ls \-dils
++format on standard output.
++The block counts are of 1K blocks, unless the environment variable
++POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++.IP "\-ok \fIcommand\fR ;"
++Like
++.B \-exec
++but ask the user first (on the standard input); if the
++response does not start with `y' or `Y', do not run the command, and
++return false. If the command is run, its standard input is redirected
++from
++.BR /dev/null .
++
++.IP "\-okdir \fIcommand\fR ;"
++Like
++.B \-execdir
++but ask the user first (on the standard input); if the
++response does not start with `y' or `Y', do not run the command, and
++return false. If the command is run, its standard input is redirected
++from
++.BR /dev/null .
++
++.IP \-print
++True; print the full file name on the standard output, followed by a
++newline. If you are piping the output of
++.B find
++into another program and there is the faintest possibility that the files
++which you are searching for might contain a newline, then you should
++seriously consider using the
++.B \-print0
++option instead of
++.BR \-print .
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++.IP \-print0
++True; print the full file name on the standard output, followed by a
++null character (instead of the newline character that
++.B \-print
++uses).
++This allows file names that contain newlines or other types of white
++space to be correctly interpreted by programs that process the
++\fBfind\fR output. This option corresponds to the
++.B \-0
++option of
++.BR xargs .
++
++.IP "\-printf \fIformat\fR"
++True; print \fIformat\fR on the standard output, interpreting `\e'
++escapes and `%' directives. Field widths and precisions can be
++specified as with the `printf' C function. Please note that many of
++the fields are printed as %s rather than %d, and this may mean that
++flags don't work as you might expect. This also means that the `\-'
++flag does work (it forces fields to be left-aligned). Unlike
++.BR \-print ,
++.B \-printf
++does not add a newline at the end of the string. The escapes
++and directives are:
++.RS
++.IP \ea
++Alarm bell.
++.IP \eb
++Backspace.
++.IP \ec
++Stop printing from this format immediately and flush the output.
++.IP \ef
++Form feed.
++.IP \en
++Newline.
++.IP \er
++Carriage return.
++.IP \et
++Horizontal tab.
++.IP \ev
++Vertical tab.
++.IP \e\0
++ASCII NUL.
++.IP \e\e
++A literal backslash (`\e').
++.IP \eNNN
++The character whose ASCII code is NNN (octal).
++.PP
++A `\e' character followed by any other character is treated as an
++ordinary character, so they both are printed.
++.IP %%
++A literal percent sign.
++.IP %a
++File's last access time in the format returned by the C `ctime' function.
++.IP %A\fIk\fP
++File's last access time in the format specified by \fIk\fR, which is
++either `@' or a directive for the C `strftime' function. The possible
++values for \fIk\fR are listed below; some of them might not be
++available on all systems, due to differences in `strftime' between
++systems.
++.RS
++.IP @
++seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
++.PP
++Time fields:
++.IP H
++hour (00..23)
++.IP I
++hour (01..12)
++.IP k
++hour ( 0..23)
++.IP l
++hour ( 1..12)
++.IP M
++minute (00..59)
++.IP p
++locale's AM or PM
++.IP r
++time, 12-hour (hh:mm:ss [AP]M)
++.IP S
++Second (00.00 .. 61.00). There is a fractional part.
++.IP T
++time, 24-hour (hh:mm:ss)
++.IP +
++Date and time, separated by `+', for example
++`2004\-04\-28+22:22:05.0'. This is a GNU extension. The time is
++given in the current timezone (which may be affected by setting the TZ
++environment variable). The seconds field includes a fractional part.
++.IP X
++locale's time representation (H:M:S)
++.IP Z
++time zone (e.g., EDT), or nothing if no time zone is determinable
++.PP
++Date fields:
++.IP a
++locale's abbreviated weekday name (Sun..Sat)
++.IP A
++locale's full weekday name, variable length (Sunday..Saturday)
++.IP b
++locale's abbreviated month name (Jan..Dec)
++.IP B
++locale's full month name, variable length (January..December)
++.IP c
++locale's date and time (Sat Nov 04 12:02:33 EST 1989). The format is
++the same as for
++.BR ctime (3)
++and so to preserve compatibility with that format, there is no fractional part
++in the seconds field.
++.IP d
++day of month (01..31)
++.IP D
++date (mm/dd/yy)
++.IP h
++same as b
++.IP j
++day of year (001..366)
++.IP m
++month (01..12)
++.IP U
++week number of year with Sunday as first day of week (00..53)
++.IP w
++day of week (0..6)
++.IP W
++week number of year with Monday as first day of week (00..53)
++.IP x
++locale's date representation (mm/dd/yy)
++.IP y
++last two digits of year (00..99)
++.IP Y
++year (1970...)
++.RE
++.IP %b
++The amount of disk space used for this file in 512-byte blocks. Since disk
++space is allocated in multiples of the filesystem block size this is usually
++greater than %s/512, but it can also be smaller if the file is a sparse file.
++.IP %c
++File's last status change time in the format returned by the C `ctime'
++function.
++.IP %C\fIk\fP
++File's last status change time in the format specified by \fIk\fR,
++which is the same as for %A.
++.IP %d
++File's depth in the directory tree; 0 means the file is a command line
++argument.
++.IP %D
++The device number on which the file exists (the st_dev field of struct
++stat), in decimal.
++.IP %f
++File's name with any leading directories removed (only the last element).
++.IP %F
++Type of the filesystem the file is on; this value can be used for
++\-fstype.
++.IP %g
++File's group name, or numeric group ID if the group has no name.
++.IP %G
++File's numeric group ID.
++.IP %h
++Leading directories of file's name (all but the last element).
++If the file name contains no slashes (since it is in the current
++directory) the %h specifier expands to ".".
++.IP %H
++Command line argument under which file was found.
++.IP %i
++File's inode number (in decimal).
++.IP %k
++The amount of disk space used for this file in 1K blocks. Since disk space is
++allocated in multiples of the filesystem block size this is usually greater
++than %s/1024, but it can also be smaller if the file is a sparse file.
++.IP %l
++Object of symbolic link (empty string if file is not a symbolic link).
++.IP %m
++File's permission bits (in octal). This option uses the `traditional'
++numbers which most Unix implementations use, but if your particular
++implementation uses an unusual ordering of octal permissions bits, you
++will see a difference between the actual value of the file's mode and
++the output of %m. Normally you will want to have a leading
++zero on this number, and to do this, you should use the
++.B #
++flag (as in, for example, `%#m').
++.IP %M
++File's permissions (in symbolic form, as for
++.BR ls ).
++This directive is supported in findutils 4.2.5 and later.
++.IP %n
++Number of hard links to file.
++.IP %p
++File's name.
++.IP %P
++File's name with the name of the command line argument under which
++it was found removed.
++.IP %s
++File's size in bytes.
++.IP %S
++File's sparseness. This is calculated as (BLOCKSIZE*st_blocks /
++st_size). The exact value you will get for an ordinary file of a
++certain length is system-dependent. However, normally sparse files
++will have values less than 1.0, and files which use indirect blocks
++may have a value which is greater than 1.0. The value used for
++BLOCKSIZE is system-dependent, but is usually 512 bytes. If the file
++size is zero, the value printed is undefined. On systems which lack
++support for st_blocks, a file's sparseness is assumed to be 1.0.
++.IP %t
++File's last modification time in the format returned by the C `ctime'
++function.
++.IP %T\fIk\fP
++File's last modification time in the format specified by \fIk\fR,
++which is the same as for %A.
++.IP %u
++File's user name, or numeric user ID if the user has no name.
++.IP %U
++File's numeric user ID.
++.IP %y
++File's type (like in
++.BR "ls \-l" ),
++U=unknown type (shouldn't happen)
++.IP %Y
++File's type (like %y), plus follow symlinks: L=loop, N=nonexistent
++.PP
++A `%' character followed by any other character is discarded, but the
++other character is printed (don't rely on this, as further format
++characters may be introduced). A `%' at the end of the format
++argument causes undefined behaviour since there is no following
++character. In some locales, it may hide your door keys, while in
++others it may remove the final page from the novel you are reading.
++
++The %m and %d directives support the
++.B #
++,
++.B 0
++and
++.B +
++flags, but the other directives do not, even if they
++print numbers. Numeric directives that do not support these flags
++include
++.BR G ,
++.BR U ,
++.BR b ,
++.BR D ,
++.B k
++and
++.BR n .
++The `\-' format flag is supported and changes the alignment of a field
++from right-justified (which is the default) to left-justified.
++.PP
++See the
++.B UNUSUAL FILENAMES
++section for information about how unusual characters in filenames are handled.
++
++
++.RE
++.IP \-prune
++True; if the file is a directory, do not descend into it. If
++.B \-depth
++is given, false; no effect. Because
++.B \-delete
++implies
++.BR \-depth ,
++you cannot usefully use
++.B \-prune
++and
++.B \-delete together.
++
++.IP "\-quit"
++Exit immediately. No child processes will be left running, but no more
++paths specified on the command line will be processed. For example,
++.B find /tmp/foo /tmp/bar \-print \-quit
++will print only
++.BR /tmp/foo .
++Any command lines which have been built up with
++.B \-execdir ... {} +
++will be invoked before
++.B find
++exits. The exit status may or may not be zero, depending on whether
++an error has already occurred.
++
++.SS UNUSUAL FILENAMES
++Many of the actions of
++.B find
++result in the printing of data which is under the control of other
++users. This includes file names, sizes, modification times and so
++forth. File names are a potential problem since they can contain any
++character except `\e0' and `/'. Unusual characters in file names can
++do unexpected and often undesirable things to your terminal (for
++example, changing the settings of your function keys on some
++terminals). Unusual characters are handled differently by various
++actions, as described below.
++
++.IP "\-print0, \-fprint0\"
++Always print the exact filename, unchanged, even if the output is
++going to a terminal.
++
++.IP "\-ls, \-fls"
++Unusual characters are always escaped. White space, backslash, and
++double quote characters are printed using C-style escaping (for
++example `\ef', `\e"'). Other unusual characters are printed using an
++octal escape. Other printable characters (for
++.B \-ls
++and
++.B \-fls
++these are the characters between octal 041 and 0176) are printed as-is.
++
++.IP "\-printf, \-fprintf"
++If the output is not going to a terminal, it is printed as-is.
++Otherwise, the result depends on which directive is in use. The
++directives %D, %F, %g, %G, %H, %Y, and %y expand to values which are
++not under control of files' owners, and so are printed as-is. The
++directives %a, %b, %c, %d, %i, %k, %m, %M, %n, %s, %t, %u and %U have
++values which are under the control of files' owners but which cannot
++be used to send arbitrary data to the terminal, and so these are
++printed as-is. The directives %f, %h, %l, %p and %P are quoted. This
++quoting is performed in the same way as for GNU
++.BR ls .
++This is not the same quoting mechanism as the one used for
++.B \-ls
++and
++.BR \-fls .
++If you are able to decide what format to use for the output of
++.B find
++then it is normally better to use `\e0' as a terminator
++than to use newline, as file names can contain white space and newline
++characters.
++
++.IP "\-print, \-fprint"
++Quoting is handled in the same way as for
++.B \-printf
++and
++.BR \-fprintf .
++If you are using
++.B find
++in a script or in a situation where the matched files might have
++arbitrary names, you should consider using
++.B \-print0
++instead of
++.BR \-print .
++.P
++The
++.B \-ok
++and
++.B \-okdir
++actions print the current filename as-is. This may change in a future release.
++.SS OPERATORS
++.P
++Listed in order of decreasing precedence:
++
++.IP "( \fIexpr\fR )"
++Force precedence. Since parentheses are special to the shell, you
++will normally need to quote them. Many of the examples in this manual
++page use backslashes for this purpose: `\e(...\e)' instead of `(...)'.
++
++.IP "! \fIexpr\fR"
++True if \fIexpr\fR is false. This character will also usually need
++protection from interpretation by the shell.
++
++.IP "\-not \fIexpr\fR"
++Same as ! \fIexpr\fR, but not POSIX compliant.
++
++.IP "\fIexpr1 expr2\fR"
++Two expressions in a row are taken to be joined with an
++implied "and"; \fIexpr2\fR is not evaluated if \fIexpr1\fR is false.
++
++.IP "\fIexpr1\fR \-a \fIexpr2\fR"
++Same as \fIexpr1 expr2\fR.
++
++.IP "\fIexpr1\fR \-and \fIexpr2\fR"
++Same as \fIexpr1 expr2\fR, but not POSIX compliant.
++
++.IP "\fIexpr1\fR \-o \fIexpr2\fR"
++Or; \fIexpr2\fR is not evaluated if \fIexpr1\fR is true.
++
++.IP "\fIexpr1\fR \-or \fIexpr2\fR"
++Same as \fIexpr1\fR
++.B \-o
++\fIexpr2\fR, but not POSIX compliant.
++
++.IP "\fIexpr1\fR , \fIexpr2\fR"
++List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. The
++value of \fIexpr1\fR is discarded; the value of the list is the value
++of \fIexpr2\fR. The comma operator can be useful for searching for
++several different types of thing, but traversing the filesystem
++hierarchy only once. The
++.B \-fprintf
++action can be used to list the various matched items into several
++different output files.
++
++
++.SH "STANDARDS CONFORMANCE"
++For closest compliance to the POSIX standard, you should set the
++POSIXLY_CORRECT environment variable. The following options are
++specified in the POSIX standard (IEEE Std 1003.1, 2003 Edition):
++
++.IP \fB\-H\fR
++This option is supported.
++
++.IP \fB\-L\fR
++This option is supported.
++
++.IP \fB\-name\fR
++This option is supported, but POSIX conformance depends on the
++POSIX conformance of the system's
++.BR fnmatch (3)
++library function. As of findutils-4.2.2, shell metacharacters
++(`*', `?' or `[]' for example) will match a leading `.', because
++IEEE PASC interpretation 126 requires this. This is a change from
++previous versions of findutils.
++
++.IP \fB\-type\fR
++Supported. POSIX specifies `b', `c', `d', `l', `p', `f' and `s'.
++GNU find also supports `D', representing a Door, where the OS provides these.
++
++.IP \fB\-ok\fR
++Supported. Interpretation of the response is not locale-dependent
++(see ENVIRONMENT VARIABLES).
++
++.IP \fB\-newer\fR
++Supported. If the file specified is a symbolic link, it is always
++dereferenced. This is a change from previous behaviour, which used to
++take the relevant time from the symbolic link; see the HISTORY section
++below.
++
++.IP \fB\-perm\fR
++Supported. If the POSIXLY_CORRECT environment variable is not set,
++some mode arguments (for example +a+x) which are not valid in POSIX
++are supported for backward-compatibility.
++
++.IP "Other predicates"
++The predicates
++.BR \-atime ,
++.BR \-ctime ,
++.BR \-depth ,
++.BR \-group ,
++.BR \-links ,
++.BR \-mtime ,
++.BR \-nogroup ,
++.BR \-nouser ,
++.BR \-print ,
++.BR \-prune ,
++.BR \-size ,
++.BR \-user
++and
++.B \-xdev
++are all supported.
++
++.P
++The POSIX standard specifies parentheses `(', `)', negation `!' and the
++`and' and `or' operators (
++.BR \-a ,
++.BR \-o ).
++.P
++All other options, predicates, expressions and so forth are extensions
++beyond the POSIX standard. Many of these extensions are not unique to
++GNU find, however.
++.P
++The POSIX standard requires that
++.B find
++detects loops:
++.IP
++The
++.B find
++utility shall detect infinite loops; that is, entering a
++previously visited directory that is an ancestor of the last file
++encountered. When it detects an infinite loop, find shall write a
++diagnostic message to standard error and shall either recover its
++position in the hierarchy or terminate.
++.P
++GNU
++.B find
++complies with these requirements. The link count of
++directories which contain entries which are hard links to an ancestor
++will often be lower than they otherwise should be. This can mean that
++GNU find will sometimes optimise away the visiting of a subdirectory
++which is actually a link to an ancestor. Since
++.B find
++does not actually enter such a subdirectory, it is allowed to avoid
++emitting a diagnostic message. Although this behaviour may be
++somewhat confusing, it is unlikely that anybody actually depends on
++this behaviour. If the leaf optimisation has been turned off with
++.BR \-noleaf ,
++the directory entry will always be examined and the diagnostic message
++will be issued where it is appropriate. Symbolic links cannot be used
++to create filesystem cycles as such, but if the
++.B \-L
++option or the
++.B \-follow
++option is in use, a diagnostic message is issued when
++.B find
++encounters a loop of symbolic links. As with loops containing hard
++links, the leaf optimisation will often mean that
++.B find
++knows that it doesn't need to call
++.I stat()
++or
++.I chdir()
++on the symbolic link, so this diagnostic is frequently not necessary.
++.P
++The
++.B \-d
++option is supported for compatibility with various BSD systems,
++but you should use the POSIX-compliant option
++.B \-depth
++instead.
++.P
++The POSIXLY_CORRECT environment variable does not affect the behaviour
++of the
++.B \-regex
++or
++.B \-iregex
++tests because those tests aren't specified in the POSIX standard.
++.SH "ENVIRONMENT VARIABLES"
++
++.IP LANG
++Provides a default value for the internationalization variables that
++are unset or null.
++
++.IP LC_ALL
++If set to a non-empty string value, override the values of all the
++other internationalization variables.
++
++.IP LC_COLLATE
++The POSIX standard specifies that this variable affects the pattern
++matching to be used for the
++.B \-name
++option. GNU find uses the
++.BR fnmatch (3)
++library function, and so support for `LC_COLLATE' depends on the
++system library.
++
++.IP
++POSIX also specifies that the `LC_COLLATE' environment
++variable affects the interpretation of the user's response to the
++query issued by
++.BR \-ok' ,
++but this is not the case for GNU find.
++
++.IP LC_CTYPE
++This variable affects the treatment of character classes used with
++the
++.B \-name
++test, if the system's
++.BR fnmatch (3)
++library function supports this. It has no effect on the behaviour
++of the
++.B \-ok
++expression.
++
++.IP LC_MESSAGES
++Determines the locale to be used for internationalised messages.
++
++.IP NLSPATH
++Determines the location of the internationalisation message catalogues.
++
++.IP PATH
++Affects the directories which are searched to find the executables
++invoked by
++.BR \-exec ,
++.BR \-execdir ,
++.B \-ok
++and
++.BR \-okdir .
++
++.IP POSIXLY_CORRECT
++Determines the block size used by
++.B \-ls
++and
++.BR \-fls .
++If
++.B POSIXLY_CORRECT
++is set, blocks are units of 512 bytes. Otherwise
++they are units of 1024 bytes.
++.IP
++Setting this variable also turns off
++warning messages (that is, implies
++.BR \-nowarn )
++by default, because POSIX requires that apart from
++the output for
++.BR \-ok ,
++all messages printed on stderr are diagnositcs and must result in a
++non-zero exit status.
++.IP
++When POSIXLY_CORRECT is not set,
++.B \-perm
+++zzz
++is treated just like
++.B \-perm
++/zzz
++if
+++zzz is not a valid symbolic mode. When POSIXLY_CORRECT is set, such
++constructs are treated as an error.
++
++.IP TZ
++Affects the time zone used for some of the time-related format
++directives of
++.B \-printf
++and
++.BR \-fprintf .
++.SH "EXAMPLES"
++.nf
++.B find /tmp \-name core \-type f \-print | xargs /bin/rm \-f
++
++.fi
++Find files named
++.B core
++in or below the directory
++.B /tmp
++and delete them. Note that this will work incorrectly if there are
++any filenames containing newlines, single or double quotes, or spaces.
++.P
++.B find /tmp \-name core \-type f \-print0 | xargs \-0 /bin/rm \-f
++
++.fi
++Find files named
++.B core
++in or below the directory
++.B /tmp
++and delete them, processing filenames in such a way that file or
++directory names containing single or double quotes, spaces or newlines
++are correctly handled. The
++.B \-name
++test comes before the
++.B \-type
++test in order to avoid having to call
++.B stat(2)
++on every file.
++
++.P
++.nf
++.B find . \-type f \-exec file \(aq{}\(aq \e\;
++
++.fi
++Runs `file' on every file in or below the current directory. Notice
++that the braces are enclosed in single quote marks to protect them
++from interpretation as shell script punctuation. The semicolon is
++similarly protected by the use of a backslash, though single quotes
++could have been used in that case also.
++
++.P
++.nf
++.B find / \e
++.B \e( \-perm \-4000 \-fprintf /root/suid.txt "%#m %u %p\en" \e) , \e
++.B \e( \-size +100M \-fprintf /root/big.txt "%\-10s %p\en" \e)
++
++.fi
++Traverse the filesystem just once, listing setuid files and
++directories into
++.B /root/suid.txt
++and large files into
++.BR /root/big.txt .
++
++.P
++.nf
++.B find $HOME \-mtime 0
++
++.fi
++Search for files in your home directory which have been modified in
++the last twenty-four hours. This command works this way because the
++time since each file was last modified is divided by 24 hours and any
++remainder is discarded. That means that to match
++.B \-mtime
++.BR 0 ,
++a file will have to have a modification in the past which is less than
++24 hours ago.
++
++.P
++.nf
++.B find /sbin /usr/sbin -executable \e! -readable \-print
++
++.fi
++Search for files which are executable but not readable.
++
++.P
++.nf
++.B find . \-perm 664
++
++.fi
++Search for files which have read and write permission for their owner,
++and group, but which other users can read but not write to. Files
++which meet these criteria but have other permissions bits set (for
++example if someone can execute the file) will not be matched.
++
++.P
++.nf
++.B find . \-perm \-664
++
++.fi
++Search for files which have read and write permission for their owner
++and group, and which other users can read, without regard to the
++presence of any extra permission bits (for example the executable
++bit). This will match a file which has mode 0777, for example.
++
++.P
++.nf
++.B find . \-perm /222
++
++.fi
++Search for files which are writable by somebody (their owner, or
++their group, or anybody else).
++
++.P
++.nf
++.B find . \-perm /220
++.B find . \-perm /u+w,g+w
++.B find . \-perm /u=w,g=w
++
++.fi
++All three of these commands do the same thing, but the first one uses
++the octal representation of the file mode, and the other two use the
++symbolic form. These commands all search for files which are
++writable by either their owner or their group. The files don't have
++to be writable by both the owner and group to be matched; either will
++do.
++
++.P
++.nf
++.B find . \-perm \-220
++.B find . \-perm \-g+w,u+w
++
++.fi
++Both these commands do the same thing; search for files which are
++writable by both their owner and their group.
++
++.P
++.nf
++.B find . \-perm \-444 \-perm /222 ! \-perm /111
++.B find . \-perm \-a+r \-perm /a+w ! \-perm /a+x
++
++.fi
++These two commands both search for files that are readable for
++everybody (
++.B \-perm \-444
++or
++.BR "\-perm \-a+r" ),
++have at least one write bit
++set (
++.B \-perm /222
++or
++.BR "\-perm /a+w" )
++but are not executable for anybody (
++.B ! \-perm /111
++and
++.B ! \-perm /a+x
++respectively).
++
++.P
++.nf
++.B cd /source-dir
++.B find . \-name .snapshot \-prune \-o \e( \e! \-name "*~" \-print0 \e)|
++.B cpio \-pmd0 /dest-dir
++
++.fi
++This command copies the contents of
++.B /source-dir
++to
++.BR /dest-dir ,
++but omits files and directories named
++.B .snapshot
++(and anything in them). It also omits files or directories whose name
++ends in
++.BR ~ ,
++but not their contents. The construct
++.B \-prune \-o \e( ... \-print0 \e)
++is quite common. The idea here is that the expression before
++.B \-prune
++matches things which are to be pruned. However, the
++.B \-prune
++action itself returns true, so the following
++.B \-o
++ensures that the right hand side is evaluated only for those
++directories which didn't get pruned (the contents of the pruned
++directories are not even visited, so their contents are irrelevant).
++The expression on the right hand side of the
++.B \-o
++is in parentheses only for clarity. It emphasises that the
++.B \-print0
++action takes place only for things that didn't have
++.B \-prune
++applied to them. Because the default `and' condition between tests
++binds more tightly than
++.BR \-o ,
++this is the default anyway, but the parentheses help to show
++what is going on.
++
++.SH EXIT STATUS
++.PP
++.B find
++exits with status 0 if all files are processed successfully, greater
++than 0 if errors occur. This is deliberately a very broad
++description, but if the return value is non-zero, you should not rely
++on the correctness of the results of
++.BR find .
++
++.SH "SEE ALSO"
++\fBlocate\fP(1), \fBlocatedb\fP(5), \fBupdatedb\fP(1), \fBxargs\fP(1),
++\fBchmod\fP(1), \fBfnmatch\fP(3), \fBregex\fP(7), \fBstat\fP(2),
++\fBlstat\fP(2), \fBls\fP(1), \fBprintf\fP(3), \fBstrftime\fP(3),
++\fBctime\fP(3), \fBFinding Files\fP (on-line in Info, or printed).
++.SH "HISTORY"
++As of findutils-4.2.2, shell metacharacters (`*', `?' or `[]' for
++example) used in filename patterns will match a leading `.', because
++IEEE POSIX interpretation 126 requires this.
++.P
++The syntax
++\.B \-perm +MODE
++was deprecated in findutils-4.2.21, in favour of
++\.B \-perm
++.BR /MODE .
++As of findutils-4.3.3,
++.B \-perm /000
++now matches all files instead of none.
++.P
++Nanosecond-resolution
++timestamps were implemented in findutils-4.3.3.
++.P
++As of findutils-4.3.11, the
++.B \-delete
++action sets
++.BR find 's
++exit status to a nonzero value when it fails.
++However,
++.B find
++will not exit immediately. Previously,
++.BR find 's
++exit status was unaffected by the failure of
++.BR \-delete .
++.TS
++l l l .
++Feature Added in Also occurs in
++\-newerXY 4.3.3 BSD
++\-D 4.3.1
++\-O 4.3.1
++\-readable 4.3.0
++\-writable 4.3.0
++\-executable 4.3.0
++\-regextype 4.2.24
++\-exec ... + 4.2.12 POSIX
++\-execdir 4.2.12 BSD
++\-okdir 4.2.12
++\-samefile 4.2.11
++\-H 4.2.5 POSIX
++\-L 4.2.5 POSIX
++\-P 4.2.5 BSD
++\-delete 4.2.3
++\-quit 4.2.3
++\-d 4.2.3 BSD
++\-wholename 4.2.0
++\-iwholename 4.2.0
++\-ignore_readdir_race 4.2.0
++\-fls 4.0
++\-ilname 3.8
++\-iname 3.8
++\-ipath 3.8
++\-iregex 3.8
++.TE
++.SH "NON-BUGS"
++.nf
++.B $ find . \-name *.c \-print
++find: paths must precede expression
++Usage: find [\-H] [\-L] [\-P] [\-Olevel] [\-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
++.fi
++.P
++This happens because
++.I *.c
++has been expanded by the shell
++resulting in
++.B find
++actually receiving a command line like this:
++.nf
++
++.B find . \-name bigram.c code.c frcode.c locate.c \-print
++
++.fi
++That command is of course not going to work. Instead of doing things
++this way, you should enclose the pattern in quotes or escape the wildcard:
++.nf
++.B $ find . \-name \e*.c \-print
++.fi
++
++.SH "BUGS"
++.P
++There are security problems inherent in the behaviour that the POSIX
++standard specifies for
++.BR find ,
++which therefore cannot be fixed. For example, the
++.B \-exec
++action is
++inherently insecure, and
++.B \-execdir
++should be used instead.
++Please see \fBFinding Files\fP for more information.
++.P
++The environment variable
++.B LC_COLLATE
++has no effect on the
++.B \-ok
++action.
++.P
++The best way to report a bug is to use the form at
++http://savannah.gnu.org/bugs/?group=findutils.
++The reason for this is that you will then be able to track progress in
++fixing the problem. Other comments about \fBfind\fP(1) and about
++the findutils package in general can be sent to the
++.I bug\-findutils
++mailing list. To join the list, send email to
++.IR bug\-findutils\-request@gnu.org .
+diff -purN findutils-4.3.12.orig/find/find.c findutils-4.3.12/find/find.c
+--- findutils-4.3.12.orig/find/find.c 2007-12-19 16:12:34.000000000 -0500
++++ findutils-4.3.12/find/find.c 2008-01-30 08:46:05.754843619 -0500
+@@ -1270,7 +1270,7 @@ process_path (char *pathname, char *name
+ static void
+ process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)
+ {
+- int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */
++ int subdirs_left=0; /* Number of unexamined subdirs in PATHNAME. */
+ boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */
+ unsigned int idx; /* Which entry are we on? */
+ struct stat stat_buf;
+diff -purN findutils-4.3.12.orig/find/find.c.orig findutils-4.3.12/find/find.c.orig
+--- findutils-4.3.12.orig/find/find.c.orig 1969-12-31 19:00:00.000000000 -0500
++++ findutils-4.3.12/find/find.c.orig 2007-12-19 16:12:34.000000000 -0500
+@@ -0,0 +1,1537 @@
++/* find -- search for files in a directory hierarchy
++ Copyright (C) 1990, 91, 92, 93, 94, 2000,
++ 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++*/
++/* GNU find was written by Eric Decker <cire@cisco.com>,
++ with enhancements by David MacKenzie <djm@gnu.org>,
++ Jay Plett <jay@silence.princeton.nj.us>,
++ and Tim Wood <axolotl!tim@toad.com>.
++ The idea for -print0 and xargs -0 came from
++ Dan Bernstein <brnstnd@kramden.acf.nyu.edu>.
++ Improvements have been made by James Youngman <jay@gnu.org>.
++*/
++
++
++#include <config.h>
++#include "defs.h"
++
++#define USE_SAFE_CHDIR 1
++#undef STAT_MOUNTPOINTS
++
++
++#include <errno.h>
++#include <assert.h>
++
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <openat.h>
++
++#include "xalloc.h"
++#include "human.h"
++#include "canonicalize.h"
++#include <modetype.h>
++
++#include "closein.h"
++#include "savedirinfo.h"
++#include "buildcmd.h"
++#include "dirname.h"
++#include "quote.h"
++#include "quotearg.h"
++#include "xgetcwd.h"
++#include "error.h"
++
++#ifdef HAVE_LOCALE_H
++#include <locale.h>
++#endif
++
++#if ENABLE_NLS
++# include <libintl.h>
++# define _(Text) gettext (Text)
++#else
++# define _(Text) Text
++#define textdomain(Domain)
++#define bindtextdomain(Package, Directory)
++#define ngettext(singular,plural,n) ((1==n) ? singular : plural)
++#endif
++#ifdef gettext_noop
++# define N_(String) gettext_noop (String)
++#else
++/* See locate.c for explanation as to why not use (String) */
++# define N_(String) String
++#endif
++
++#ifdef STAT_MOUNTPOINTS
++static void init_mounted_dev_list(int mandatory);
++#endif
++
++static void process_top_path PARAMS((char *pathname, mode_t mode));
++static int process_path PARAMS((char *pathname, char *name, boolean leaf, char *parent, mode_t type));
++static void process_dir PARAMS((char *pathname, char *name, int pathlen, const struct stat *statp, char *parent));
++
++
++
++/* Name this program was run with. */
++char *program_name;
++
++/* A file descriptor open to the initial working directory.
++ Doing it this way allows us to work when the i.w.d. has
++ unreadable parents. */
++int starting_desc;
++
++/* The stat buffer of the initial working directory. */
++static struct stat starting_stat_buf;
++
++enum ChdirSymlinkHandling
++ {
++ SymlinkHandleDefault, /* Normally the right choice */
++ SymlinkFollowOk /* see comment in process_top_path() */
++ };
++
++
++enum TraversalDirection
++ {
++ TraversingUp,
++ TraversingDown
++ };
++
++enum WdSanityCheckFatality
++ {
++ FATAL_IF_SANITY_CHECK_FAILS,
++ RETRY_IF_SANITY_CHECK_FAILS,
++ NON_FATAL_IF_SANITY_CHECK_FAILS
++ };
++
++
++int get_current_dirfd(void)
++{
++ return AT_FDCWD;
++}
++
++
++int
++main (int argc, char **argv)
++{
++ int i;
++ int end_of_leading_options = 0; /* First arg after any -H/-L etc. */
++ struct predicate *eval_tree;
++
++ program_name = argv[0];
++ state.exit_status = 0;
++
++ /* Set the option defaults before we do the locale
++ * initialisation as check_nofollow() needs to be executed in the
++ * POSIX locale.
++ */
++ set_option_defaults(&options);
++
++#ifdef HAVE_SETLOCALE
++ setlocale (LC_ALL, "");
++#endif
++ bindtextdomain (PACKAGE, LOCALEDIR);
++ textdomain (PACKAGE);
++ atexit (close_stdin);
++
++ /* Check for -P, -H or -L options. */
++ end_of_leading_options = process_leading_options(argc, argv);
++
++ if (options.debug_options & DebugStat)
++ options.xstat = debug_stat;
++
++#ifdef DEBUG
++ fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start));
++#endif /* DEBUG */
++
++ /* state.cwd_dir_fd has to be initialised before we call build_expression_tree()
++ * because command-line parsing may lead us to stat some files.
++ */
++ state.cwd_dir_fd = AT_FDCWD;
++
++ /* We are now processing the part of the "find" command line
++ * after the -H/-L options (if any).
++ */
++ eval_tree = build_expression_tree(argc, argv, end_of_leading_options);
++
++
++ /* safely_chdir() needs to check that it has ended up in the right place.
++ * To avoid bailing out when something gets automounted, it checks if
++ * the target directory appears to have had a directory mounted on it as
++ * we chdir()ed. The problem with this is that in order to notice that
++ * a file system was mounted, we would need to lstat() all the mount points.
++ * That strategy loses if our machine is a client of a dead NFS server.
++ *
++ * Hence if safely_chdir() and wd_sanity_check() can manage without needing
++ * to know the mounted device list, we do that.
++ */
++ if (!options.open_nofollow_available)
++ {
++#ifdef STAT_MOUNTPOINTS
++ init_mounted_dev_list(0);
++#endif
++ }
++
++
++ starting_desc = open (".", O_RDONLY
++#if defined O_LARGEFILE
++ |O_LARGEFILE
++#endif
++ );
++ if (0 <= starting_desc && fchdir (starting_desc) != 0)
++ {
++ close (starting_desc);
++ starting_desc = -1;
++ }
++
++ if (starting_desc < 0)
++ {
++ starting_dir = xgetcwd ();
++ if (! starting_dir)
++ error (1, errno, _("cannot get current directory"));
++ }
++ set_stat_placeholders(&starting_stat_buf);
++ if ((*options.xstat) (".", &starting_stat_buf) != 0)
++ error (1, errno, _("cannot stat current directory"));
++
++ /* If no paths are given, default to ".". */
++ for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++)
++ {
++ process_top_path (argv[i], 0);
++ }
++
++ /* If there were no path arguments, default to ".". */
++ if (i == end_of_leading_options)
++ {
++ /*
++ * We use a temporary variable here because some actions modify
++ * the path temporarily. Hence if we use a string constant,
++ * we get a coredump. The best example of this is if we say
++ * "find -printf %H" (note, not "find . -printf %H").
++ */
++ char defaultpath[2] = ".";
++ process_top_path (defaultpath, 0);
++ }
++
++ /* If "-exec ... {} +" has been used, there may be some
++ * partially-full command lines which have been built,
++ * but which are not yet complete. Execute those now.
++ */
++ show_success_rates(eval_tree);
++ cleanup();
++ return state.exit_status;
++}
++
++boolean is_fts_enabled(int *ftsoptions)
++{
++ /* this version of find (i.e. this main()) does not use fts. */
++ *ftsoptions = 0;
++ return false;
++}
++
++
++static char *
++specific_dirname(const char *dir)
++{
++ char dirbuf[1024];
++
++ if (0 == strcmp(".", dir))
++ {
++ /* OK, what's '.'? */
++ if (NULL != getcwd(dirbuf, sizeof(dirbuf)))
++ {
++ return strdup(dirbuf);
++ }
++ else
++ {
++ return strdup(dir);
++ }
++ }
++ else
++ {
++ char *result = canonicalize_filename_mode(dir, CAN_EXISTING);
++ if (NULL == result)
++ return strdup(dir);
++ else
++ return result;
++ }
++}
++
++
++
++/* Return non-zero if FS is the name of a file system that is likely to
++ * be automounted
++ */
++static int
++fs_likely_to_be_automounted(const char *fs)
++{
++ return ( (0==strcmp(fs, "nfs")) || (0==strcmp(fs, "autofs")) || (0==strcmp(fs, "subfs")));
++}
++
++
++
++#ifdef STAT_MOUNTPOINTS
++static dev_t *mounted_devices = NULL;
++static size_t num_mounted_devices = 0u;
++
++
++static void
++init_mounted_dev_list(int mandatory)
++{
++ assert (NULL == mounted_devices);
++ assert (0 == num_mounted_devices);
++ mounted_devices = get_mounted_devices(&num_mounted_devices);
++ if (mandatory && (NULL == mounted_devices))
++ {
++ error(1, 0, "Cannot read list of mounted devices.");
++ }
++}
++
++static void
++refresh_mounted_dev_list(void)
++{
++ if (mounted_devices)
++ {
++ free(mounted_devices);
++ mounted_devices = 0;
++ }
++ num_mounted_devices = 0u;
++ init_mounted_dev_list(1);
++}
++
++
++/* Search for device DEV in the array LIST, which is of size N. */
++static int
++dev_present(dev_t dev, const dev_t *list, size_t n)
++{
++ if (list)
++ {
++ while (n-- > 0u)
++ {
++ if ( (*list++) == dev )
++ return 1;
++ }
++ }
++ return 0;
++}
++
++enum MountPointStateChange
++ {
++ MountPointRecentlyMounted,
++ MountPointRecentlyUnmounted,
++ MountPointStateUnchanged
++ };
++
++
++
++static enum MountPointStateChange
++get_mount_state(dev_t newdev)
++{
++ int new_is_present, new_was_present;
++
++ new_was_present = dev_present(newdev, mounted_devices, num_mounted_devices);
++ refresh_mounted_dev_list();
++ new_is_present = dev_present(newdev, mounted_devices, num_mounted_devices);
++
++ if (new_was_present == new_is_present)
++ return MountPointStateUnchanged;
++ else if (new_is_present)
++ return MountPointRecentlyMounted;
++ else
++ return MountPointRecentlyUnmounted;
++}
++
++
++
++/* We stat()ed a directory, chdir()ed into it (we know this
++ * since direction is TraversingDown), stat()ed it again,
++ * and noticed that the device numbers are different. Check
++ * if the file system was recently mounted.
++ *
++ * If it was, it looks like chdir()ing into the directory
++ * caused a file system to be mounted. Maybe automount is
++ * running. Anyway, that's probably OK - but it happens
++ * only when we are moving downward.
++ *
++ * We also allow for the possibility that a similar thing
++ * has happened with the unmounting of a file system. This
++ * is much rarer, as it relies on an automounter timeout
++ * occurring at exactly the wrong moment.
++ */
++static enum WdSanityCheckFatality
++dirchange_is_fatal(const char *specific_what,
++ enum WdSanityCheckFatality isfatal,
++ int silent,
++ struct stat *newinfo)
++{
++ enum MountPointStateChange transition = get_mount_state(newinfo->st_dev);
++ switch (transition)
++ {
++ case MountPointRecentlyUnmounted:
++ isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS;
++ if (!silent)
++ {
++ error (0, 0,
++ _("Warning: file system %s has recently been unmounted."),
++ safely_quote_err_filename(0, specific_what));
++ }
++ break;
++
++ case MountPointRecentlyMounted:
++ isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS;
++ if (!silent)
++ {
++ error (0, 0,
++ _("Warning: file system %s has recently been mounted."),
++ safely_quote_err_filename(0, specific_what));
++ }
++ break;
++
++ case MountPointStateUnchanged:
++ /* leave isfatal as it is */
++ break;
++ }
++
++ return isfatal;
++}
++
++
++#endif
++
++
++
++/* Examine the results of the stat() of a directory from before we
++ * entered or left it, with the results of stat()ing it afterward. If
++ * these are different, the file system tree has been modified while we
++ * were traversing it. That might be an attempt to use a race
++ * condition to persuade find to do something it didn't intend
++ * (e.g. an attempt by an ordinary user to exploit the fact that root
++ * sometimes runs find on the whole file system). However, this can
++ * also happen if automount is running (certainly on Solaris). With
++ * automount, moving into a directory can cause a file system to be
++ * mounted there.
++ *
++ * To cope sensibly with this, we will raise an error if we see the
++ * device number change unless we are chdir()ing into a subdirectory,
++ * and the directory we moved into has been mounted or unmounted "recently".
++ * Here "recently" means since we started "find" or we last re-read
++ * the /etc/mnttab file.
++ *
++ * If the device number does not change but the inode does, that is a
++ * problem.
++ *
++ * If the device number and inode are both the same, we are happy.
++ *
++ * If a file system is (un)mounted as we chdir() into the directory, that
++ * may mean that we're now examining a section of the file system that might
++ * have been excluded from consideration (via -prune or -quit for example).
++ * Hence we print a warning message to indicate that the output of find
++ * might be inconsistent due to the change in the file system.
++ */
++static boolean
++wd_sanity_check(const char *thing_to_stat,
++ const char *progname,
++ const char *what,
++ dev_t old_dev,
++ ino_t old_ino,
++ struct stat *newinfo,
++ int parent,
++ int line_no,
++ enum TraversalDirection direction,
++ enum WdSanityCheckFatality isfatal,
++ boolean *changed) /* output parameter */
++{
++ const char *fstype;
++ char *specific_what = NULL;
++ int silent = 0;
++ const char *current_dir = ".";
++
++ *changed = false;
++
++ set_stat_placeholders(newinfo);
++ if ((*options.xstat) (current_dir, newinfo) != 0)
++ fatal_file_error(thing_to_stat);
++
++ if (old_dev != newinfo->st_dev)
++ {
++ *changed = true;
++ specific_what = specific_dirname(what);
++ fstype = filesystem_type(newinfo, current_dir);
++ silent = fs_likely_to_be_automounted(fstype);
++
++ /* This condition is rare, so once we are here it is
++ * reasonable to perform an expensive computation to
++ * determine if we should continue or fail.
++ */
++ if (TraversingDown == direction)
++ {
++#ifdef STAT_MOUNTPOINTS
++ isfatal = dirchange_is_fatal(specific_what,isfatal,silent,newinfo);
++#else
++ isfatal = RETRY_IF_SANITY_CHECK_FAILS;
++#endif
++ }
++
++ switch (isfatal)
++ {
++ case FATAL_IF_SANITY_CHECK_FAILS:
++ {
++ fstype = filesystem_type(newinfo, current_dir);
++ error (1, 0,
++ _("%1$s%2$s changed during execution of %3$s "
++ "(old device number %4$ld, new device number %5$ld, file system type is %6$s) [ref %7$ld]"),
++ safely_quote_err_filename(0, specific_what),
++ parent ? "/.." : "",
++ safely_quote_err_filename(1, progname),
++ (long) old_dev,
++ (long) newinfo->st_dev,
++ fstype,
++ (long)line_no);
++ /*NOTREACHED*/
++ return false;
++ }
++
++ case NON_FATAL_IF_SANITY_CHECK_FAILS:
++ {
++ /* Since the device has changed under us, the inode number
++ * will almost certainly also be different. However, we have
++ * already decided that this is not a problem. Hence we return
++ * without checking the inode number.
++ */
++ free(specific_what);
++ return true;
++ }
++
++ case RETRY_IF_SANITY_CHECK_FAILS:
++ return false;
++ }
++ }
++
++ /* Device number was the same, check if the inode has changed. */
++ if (old_ino != newinfo->st_ino)
++ {
++ *changed = true;
++ specific_what = specific_dirname(what);
++ fstype = filesystem_type(newinfo, current_dir);
++
++ error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0,
++ 0, /* no relevant errno value */
++ _("%1$s%2$s changed during execution of %3$s "
++ "(old inode number %4$ld, new inode number %5$ld, file system type is %5$s) [ref %7$ld]"),
++ safely_quote_err_filename(0, specific_what),
++ parent ? "/.." : "",
++ safely_quote_err_filename(1, progname),
++ (long) old_ino,
++ (long) newinfo->st_ino,
++ fstype,
++ (long)line_no);
++ free(specific_what);
++ return false;
++ }
++
++ return true;
++}
++
++enum SafeChdirStatus
++ {
++ SafeChdirOK,
++ SafeChdirFailSymlink,
++ SafeChdirFailNotDir,
++ SafeChdirFailStat,
++ SafeChdirFailWouldBeUnableToReturn,
++ SafeChdirFailChdirFailed,
++ SafeChdirFailNonexistent,
++ SafeChdirFailDestUnreadable
++ };
++
++/* Safely perform a change in directory. We do this by calling
++ * lstat() on the subdirectory, using chdir() to move into it, and
++ * then lstat()ing ".". We compare the results of the two stat calls
++ * to see if they are consistent. If not, we sound the alarm.
++ *
++ * If following_links() is true, we do follow symbolic links.
++ */
++static enum SafeChdirStatus
++safely_chdir_lstat(const char *dest,
++ enum TraversalDirection direction,
++ struct stat *statbuf_dest,
++ enum ChdirSymlinkHandling symlink_follow_option,
++ boolean *did_stat)
++{
++ struct stat statbuf_arrived;
++ int rv, dotfd=-1;
++ int saved_errno; /* specific_dirname() changes errno. */
++ boolean rv_set = false;
++ boolean statflag = false;
++ int tries = 0;
++ enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS;
++
++ saved_errno = errno = 0;
++
++ dotfd = open(".", O_RDONLY
++#if defined O_LARGEFILE
++ |O_LARGEFILE
++#endif
++ );
++
++ /* We jump back to here if wd_sanity_check()
++ * recoverably triggers an alert.
++ */
++ retry:
++ ++tries;
++
++ if (dotfd >= 0)
++ {
++ /* Stat the directory we're going to. */
++ set_stat_placeholders(statbuf_dest);
++ if (0 == options.xstat(dest, statbuf_dest))
++ {
++ statflag = true;
++
++#ifdef S_ISLNK
++ /* symlink_follow_option might be set to SymlinkFollowOk, which
++ * would allow us to chdir() into a symbolic link. This is
++ * only useful for the case where the directory we're
++ * chdir()ing into is the basename of a command line
++ * argument, for example where "foo/bar/baz" is specified on
++ * the command line. When -P is in effect (the default),
++ * baz will not be followed if it is a symlink, but if bar
++ * is a symlink, it _should_ be followed. Hence we need the
++ * ability to override the policy set by following_links().
++ */
++ if (!following_links() && S_ISLNK(statbuf_dest->st_mode))
++ {
++ /* We're not supposed to be following links, but this is
++ * a link. Check symlink_follow_option to see if we should
++ * make a special exception.
++ */
++ if (symlink_follow_option == SymlinkFollowOk)
++ {
++ /* We need to re-stat() the file so that the
++ * sanity check can pass.
++ */
++ if (0 != stat(dest, statbuf_dest))
++ {
++ rv = SafeChdirFailNonexistent;
++ rv_set = true;
++ saved_errno = errno;
++ goto fail;
++ }
++ statflag = true;
++ }
++ else
++ {
++ /* Not following symlinks, so the attempt to
++ * chdir() into a symlink should be prevented.
++ */
++ rv = SafeChdirFailSymlink;
++ rv_set = true;
++ saved_errno = 0; /* silence the error message */
++ goto fail;
++ }
++ }
++#endif
++#ifdef S_ISDIR
++ /* Although the immediately following chdir() would detect
++ * the fact that this is not a directory for us, this would
++ * result in an extra system call that fails. Anybody
++ * examining the system-call trace should ideally not be
++ * concerned that something is actually failing.
++ */
++ if (!S_ISDIR(statbuf_dest->st_mode))
++ {
++ rv = SafeChdirFailNotDir;
++ rv_set = true;
++ saved_errno = 0; /* silence the error message */
++ goto fail;
++ }
++#endif
++
++ if (options.debug_options & DebugSearch)
++ fprintf(stderr, "safely_chdir(): chdir(\"%s\")\n", dest);
++
++ if (0 == chdir(dest))
++ {
++ /* check we ended up where we wanted to go */
++ boolean changed = false;
++ if (!wd_sanity_check(".", program_name, ".",
++ statbuf_dest->st_dev,
++ statbuf_dest->st_ino,
++ &statbuf_arrived,
++ 0, __LINE__, direction,
++ isfatal,
++ &changed))
++ {
++ /* Only allow one failure. */
++ if (RETRY_IF_SANITY_CHECK_FAILS == isfatal)
++ {
++ if (0 == fchdir(dotfd))
++ {
++ isfatal = FATAL_IF_SANITY_CHECK_FAILS;
++ goto retry;
++ }
++ else
++ {
++ /* Failed to return to original directory,
++ * but we know that the current working
++ * directory is not the one that we intend
++ * to be in. Since fchdir() failed, we
++ * can't recover from this and so this error
++ * is fatal.
++ */
++ error(1, errno,
++ "failed to return to parent directory");
++ }
++ }
++ else
++ {
++ /* XXX: not sure what to use as an excuse here. */
++ rv = SafeChdirFailNonexistent;
++ rv_set = true;
++ saved_errno = 0;
++ goto fail;
++ }
++ }
++
++ close(dotfd);
++ return SafeChdirOK;
++ }
++ else
++ {
++ saved_errno = errno;
++ if (ENOENT == saved_errno)
++ {
++ rv = SafeChdirFailNonexistent;
++ rv_set = true;
++ if (options.ignore_readdir_race)
++ errno = 0; /* don't issue err msg */
++ }
++ else if (ENOTDIR == saved_errno)
++ {
++ /* This can happen if the we stat a directory,
++ * and then file system activity changes it into
++ * a non-directory.
++ */
++ saved_errno = 0; /* don't issue err msg */
++ rv = SafeChdirFailNotDir;
++ rv_set = true;
++ }
++ else
++ {
++ rv = SafeChdirFailChdirFailed;
++ rv_set = true;
++ }
++ goto fail;
++ }
++ }
++ else
++ {
++ saved_errno = errno;
++ rv = SafeChdirFailStat;
++ rv_set = true;
++
++ if ( (ENOENT == saved_errno) || (0 == state.curdepth))
++ saved_errno = 0; /* don't issue err msg */
++ goto fail;
++ }
++ }
++ else
++ {
++ /* We do not have read permissions on "." */
++ rv = SafeChdirFailWouldBeUnableToReturn;
++ rv_set = true;
++ goto fail;
++ }
++
++ /* This is the success path, so we clear errno. The caller probably
++ * won't be calling error() anyway.
++ */
++ saved_errno = 0;
++
++ /* We use the same exit path for success or failure.
++ * which has occurred is recorded in RV.
++ */
++ fail:
++ /* We do not call error() as this would result in a duplicate error
++ * message when the caller does the same thing.
++ */
++ if (saved_errno)
++ errno = saved_errno;
++
++ if (dotfd >= 0)
++ {
++ close(dotfd);
++ dotfd = -1;
++ }
++
++ *did_stat = statflag;
++ assert (rv_set);
++ return rv;
++}
++
++#if defined O_NOFOLLOW
++/* Safely change working directory to the specified subdirectory. If
++ * we are not allowed to follow symbolic links, we use open() with
++ * O_NOFOLLOW, followed by fchdir(). This ensures that we don't
++ * follow symbolic links (of course, we do follow them if the -L
++ * option is in effect).
++ */
++static enum SafeChdirStatus
++safely_chdir_nofollow(const char *dest,
++ enum TraversalDirection direction,
++ struct stat *statbuf_dest,
++ enum ChdirSymlinkHandling symlink_follow_option,
++ boolean *did_stat)
++{
++ int extraflags, fd;
++
++ (void) direction;
++ (void) statbuf_dest;
++
++ extraflags = 0;
++ *did_stat = false;
++
++ switch (symlink_follow_option)
++ {
++ case SymlinkFollowOk:
++ extraflags = 0;
++ break;
++
++ case SymlinkHandleDefault:
++ if (following_links())
++ extraflags = 0;
++ else
++ extraflags = O_NOFOLLOW;
++ break;
++ }
++
++ errno = 0;
++ fd = open(dest, O_RDONLY
++#if defined O_LARGEFILE
++ |O_LARGEFILE
++#endif
++ |extraflags);
++ if (fd < 0)
++ {
++ switch (errno)
++ {
++ case ELOOP:
++ return SafeChdirFailSymlink; /* This is why we use O_NOFOLLOW */
++ case ENOENT:
++ return SafeChdirFailNonexistent;
++ default:
++ return SafeChdirFailDestUnreadable;
++ }
++ }
++
++ errno = 0;
++ if (0 == fchdir(fd))
++ {
++ close(fd);
++ return SafeChdirOK;
++ }
++ else
++ {
++ int saved_errno = errno;
++ close(fd);
++ errno = saved_errno;
++
++ switch (errno)
++ {
++ case ENOTDIR:
++ return SafeChdirFailNotDir;
++
++ case EACCES:
++ case EBADF: /* Shouldn't happen */
++ case EINTR:
++ case EIO:
++ default:
++ return SafeChdirFailChdirFailed;
++ }
++ }
++}
++#endif
++
++static enum SafeChdirStatus
++safely_chdir(const char *dest,
++ enum TraversalDirection direction,
++ struct stat *statbuf_dest,
++ enum ChdirSymlinkHandling symlink_follow_option,
++ boolean *did_stat)
++{
++ enum SafeChdirStatus result;
++
++ /* We're about to leave a directory. If there are any -execdir
++ * argument lists which have been built but have not yet been
++ * processed, do them now because they must be done in the same
++ * directory.
++ */
++ complete_pending_execdirs(get_current_dirfd());
++
++#if !defined(O_NOFOLLOW)
++ options.open_nofollow_available = false;
++#endif
++ if (options.open_nofollow_available)
++ {
++ result = safely_chdir_nofollow(dest, direction, statbuf_dest,
++ symlink_follow_option, did_stat);
++ if (SafeChdirFailDestUnreadable != result)
++ {
++ return result;
++ }
++ else
++ {
++ /* Savannah bug #15384: fall through to use safely_chdir_lstat
++ * if the directory is not readable.
++ */
++ /* Do nothing. */
++ }
++ }
++ /* Even if O_NOFOLLOW is available, we may need to use the alternative
++ * method, since parent of the start point may be executable but not
++ * readable.
++ */
++ return safely_chdir_lstat(dest, direction, statbuf_dest,
++ symlink_follow_option, did_stat);
++}
++
++
++
++/* Safely go back to the starting directory. */
++static void
++chdir_back (void)
++{
++ struct stat stat_buf;
++ boolean dummy;
++
++ if (starting_desc < 0)
++ {
++ if (options.debug_options & DebugSearch)
++ fprintf(stderr, "chdir_back(): chdir(\"%s\")\n", starting_dir);
++
++#ifdef STAT_MOUNTPOINTS
++ /* We will need the mounted device list. Get it now if we don't
++ * already have it.
++ */
++ if (NULL == mounted_devices)
++ init_mounted_dev_list(1);
++#endif
++
++ if (chdir (starting_dir) != 0)
++ fatal_file_error(starting_dir);
++
++ wd_sanity_check(starting_dir,
++ program_name,
++ starting_dir,
++ starting_stat_buf.st_dev,
++ starting_stat_buf.st_ino,
++ &stat_buf, 0, __LINE__,
++ TraversingUp,
++ FATAL_IF_SANITY_CHECK_FAILS,
++ &dummy);
++ }
++ else
++ {
++ if (options.debug_options & DebugSearch)
++ fprintf(stderr, "chdir_back(): chdir(<starting-point>)\n");
++
++ if (fchdir (starting_desc) != 0)
++ {
++ fatal_file_error(starting_dir);
++ }
++ }
++}
++
++/* Move to the parent of a given directory and then call a function,
++ * restoring the cwd. Don't bother changing directory if the
++ * specified directory is a child of "." or is the root directory.
++ */
++static void
++at_top (char *pathname,
++ mode_t mode,
++ struct stat *pstat,
++ void (*action)(char *pathname,
++ char *basename,
++ int mode,
++ struct stat *pstat))
++{
++ int dirchange;
++ char *parent_dir = dir_name (pathname);
++ char *base = last_component (pathname);
++
++ state.curdepth = 0;
++ state.starting_path_length = strlen (pathname);
++
++ if (0 == *base
++ || 0 == strcmp(parent_dir, "."))
++ {
++ dirchange = 0;
++ base = pathname;
++ }
++ else
++ {
++ enum TraversalDirection direction;
++ enum SafeChdirStatus chdir_status;
++ struct stat st;
++ boolean did_stat = false;
++
++ dirchange = 1;
++ if (0 == strcmp(base, ".."))
++ direction = TraversingUp;
++ else
++ direction = TraversingDown;
++
++ /* We pass SymlinkFollowOk to safely_chdir(), which allows it to
++ * chdir() into a symbolic link. This is only useful for the
++ * case where the directory we're chdir()ing into is the
++ * basename of a command line argument, for example where
++ * "foo/bar/baz" is specified on the command line. When -P is
++ * in effect (the default), baz will not be followed if it is a
++ * symlink, but if bar is a symlink, it _should_ be followed.
++ * Hence we need the ability to override the policy set by
++ * following_links().
++ */
++ chdir_status = safely_chdir(parent_dir, direction, &st, SymlinkFollowOk, &did_stat);
++ if (SafeChdirOK != chdir_status)
++ {
++ const char *what = (SafeChdirFailWouldBeUnableToReturn == chdir_status) ? "." : parent_dir;
++ if (errno)
++ error (0, errno, "%s",
++ safely_quote_err_filename(0, what));
++ else
++ error (0, 0, _("Failed to safely change directory into %s"),
++ safely_quote_err_filename(0, parent_dir));
++
++ /* We can't process this command-line argument. */
++ state.exit_status = 1;
++ return;
++ }
++ }
++
++ free (parent_dir);
++ parent_dir = NULL;
++
++ action(pathname, base, mode, pstat);
++
++ if (dirchange)
++ {
++ chdir_back();
++ }
++}
++
++
++static void do_process_top_dir(char *pathname,
++ char *base,
++ int mode,
++ struct stat *pstat)
++{
++ (void) pstat;
++
++ process_path (pathname, base, false, ".", mode);
++ complete_pending_execdirs(get_current_dirfd());
++}
++
++static void do_process_predicate(char *pathname,
++ char *base,
++ int mode,
++ struct stat *pstat)
++{
++ (void) mode;
++
++ state.rel_pathname = base; /* cwd_dir_fd was already set by safely_chdir */
++ apply_predicate (pathname, pstat, get_eval_tree());
++}
++
++
++
++
++/* Descend PATHNAME, which is a command-line argument.
++
++ Actions like -execdir assume that we are in the
++ parent directory of the file we're examining,
++ and on entry to this function our working directory
++ is whatever it was when find was invoked. Therefore
++ If PATHNAME is "." we just leave things as they are.
++ Otherwise, we figure out what the parent directory is,
++ and move to that.
++*/
++static void
++process_top_path (char *pathname, mode_t mode)
++{
++ at_top(pathname, mode, NULL, do_process_top_dir);
++}
++
++
++/* Info on each directory in the current tree branch, to avoid
++ getting stuck in symbolic link loops. */
++static struct dir_id *dir_ids = NULL;
++/* Entries allocated in `dir_ids'. */
++static int dir_alloc = 0;
++/* Index in `dir_ids' of directory currently being searched.
++ This is always the last valid entry. */
++static int dir_curr = -1;
++/* (Arbitrary) number of entries to grow `dir_ids' by. */
++#define DIR_ALLOC_STEP 32
++
++
++
++/* We've detected a file system loop. This is caused by one of
++ * two things:
++ *
++ * 1. Option -L is in effect and we've hit a symbolic link that
++ * points to an ancestor. This is harmless. We won't traverse the
++ * symbolic link.
++ *
++ * 2. We have hit a real cycle in the directory hierarchy. In this
++ * case, we issue a diagnostic message (POSIX requires this) and we
++ * skip that directory entry.
++ */
++static void
++issue_loop_warning(const char *name, const char *pathname, int level)
++{
++ struct stat stbuf_link;
++ if (lstat(name, &stbuf_link) != 0)
++ stbuf_link.st_mode = S_IFREG;
++
++ if (S_ISLNK(stbuf_link.st_mode))
++ {
++ error(0, 0,
++ _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."),
++ safely_quote_err_filename(0, pathname));
++ /* XXX: POSIX appears to require that the exit status be non-zero if a
++ * diagnostic is issued.
++ */
++ }
++ else
++ {
++ int distance = 1 + (dir_curr-level);
++ /* We have found an infinite loop. POSIX requires us to
++ * issue a diagnostic. Usually we won't get to here
++ * because when the leaf optimisation is on, it will cause
++ * the subdirectory to be skipped. If /a/b/c/d is a hard
++ * link to /a/b, then the link count of /a/b/c is 2,
++ * because the ".." entry of /b/b/c/d points to /a, not
++ * to /a/b/c.
++ */
++ error(0, 0,
++ ngettext(
++ "Filesystem loop detected; %1$s has the same device number and inode as "
++ "a directory which is %2$d level higher in the file system hierarchy",
++ "Filesystem loop detected; %1$s has the same device number and inode as "
++ "a directory which is %2$d levels higher in the file system hierarchy",
++ (long)distance),
++ safely_quote_err_filename(0, pathname),
++ distance);
++ }
++}
++
++
++
++/* Recursively descend path PATHNAME, applying the predicates.
++ LEAF is true if PATHNAME is known to be in a directory that has no
++ more unexamined subdirectories, and therefore it is not a directory.
++ Knowing this allows us to avoid calling stat as long as possible for
++ leaf files.
++
++ NAME is PATHNAME relative to the current directory. We access NAME
++ but print PATHNAME.
++
++ PARENT is the path of the parent of NAME, relative to find's
++ starting directory.
++
++ Return nonzero iff PATHNAME is a directory. */
++
++static int
++process_path (char *pathname, char *name, boolean leaf, char *parent,
++ mode_t mode)
++{
++ struct stat stat_buf;
++ static dev_t root_dev; /* Device ID of current argument pathname. */
++ int i;
++ struct predicate *eval_tree;
++
++ eval_tree = get_eval_tree();
++ /* Assume it is a non-directory initially. */
++ stat_buf.st_mode = 0;
++ state.rel_pathname = name;
++ state.type = 0;
++ state.have_stat = false;
++ state.have_type = false;
++
++ if (!digest_mode(mode, pathname, name, &stat_buf, leaf))
++ return 0;
++
++ if (!S_ISDIR (state.type))
++ {
++ if (state.curdepth >= options.mindepth)
++ apply_predicate (pathname, &stat_buf, eval_tree);
++ return 0;
++ }
++
++ /* From here on, we're working on a directory. */
++
++
++ /* Now we really need to stat the directory, even if we know the
++ * type, because we need information like struct stat.st_rdev.
++ */
++ if (get_statinfo(pathname, name, &stat_buf) != 0)
++ return 0;
++
++ state.have_stat = true;
++ mode = state.type = stat_buf.st_mode; /* use full info now that we have it. */
++ state.stop_at_current_level =
++ options.maxdepth >= 0
++ && state.curdepth >= options.maxdepth;
++
++ /* If we've already seen this directory on this branch,
++ don't descend it again. */
++ for (i = 0; i <= dir_curr; i++)
++ if (stat_buf.st_ino == dir_ids[i].ino &&
++ stat_buf.st_dev == dir_ids[i].dev)
++ {
++ state.stop_at_current_level = true;
++ issue_loop_warning(name, pathname, i);
++ }
++
++ if (dir_alloc <= ++dir_curr)
++ {
++ dir_alloc += DIR_ALLOC_STEP;
++ dir_ids = (struct dir_id *)
++ xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id));
++ }
++ dir_ids[dir_curr].ino = stat_buf.st_ino;
++ dir_ids[dir_curr].dev = stat_buf.st_dev;
++
++ if (options.stay_on_filesystem)
++ {
++ if (state.curdepth == 0)
++ root_dev = stat_buf.st_dev;
++ else if (stat_buf.st_dev != root_dev)
++ state.stop_at_current_level = true;
++ }
++
++ if (options.do_dir_first && state.curdepth >= options.mindepth)
++ apply_predicate (pathname, &stat_buf, eval_tree);
++
++ if (options.debug_options & DebugSearch)
++ fprintf(stderr, "pathname = %s, stop_at_current_level = %d\n",
++ pathname, state.stop_at_current_level);
++
++ if (state.stop_at_current_level == false)
++ {
++ /* Scan directory on disk. */
++ process_dir (pathname, name, strlen (pathname), &stat_buf, parent);
++ }
++
++ if (options.do_dir_first == false && state.curdepth >= options.mindepth)
++ {
++ /* The fields in 'state' are now out of date. Correct them.
++ */
++ if (!digest_mode(mode, pathname, name, &stat_buf, leaf))
++ return 0;
++
++ if (0 == dir_curr)
++ {
++ at_top(pathname, mode, &stat_buf, do_process_predicate);
++ }
++ else
++ {
++ do_process_predicate(pathname, name, mode, &stat_buf);
++ }
++ }
++
++ dir_curr--;
++
++ return 1;
++}
++
++
++/* Scan directory PATHNAME and recurse through process_path for each entry.
++
++ PATHLEN is the length of PATHNAME.
++
++ NAME is PATHNAME relative to the current directory.
++
++ STATP is the results of *options.xstat on it.
++
++ PARENT is the path of the parent of NAME, relative to find's
++ starting directory. */
++
++static void
++process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)
++{
++ int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */
++ boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */
++ unsigned int idx; /* Which entry are we on? */
++ struct stat stat_buf;
++ size_t dircount = 0u;
++ struct savedir_dirinfo *dirinfo;
++#if 0
++ printf("process_dir: pathname=%s name=%s statp->st_nlink=%d st_ino=%d\n",
++ pathname,
++ name,
++ (int)statp->st_nlink,
++ (int)statp->st_ino);
++#endif
++ if (statp->st_nlink < 2)
++ {
++ subdirs_unreliable = true;
++ subdirs_left = 0;
++ }
++ else
++ {
++ subdirs_unreliable = false; /* not necessarily right */
++ subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */
++ }
++
++ errno = 0;
++ dirinfo = xsavedir(name, 0);
++
++
++ if (dirinfo == NULL)
++ {
++ assert (errno != 0);
++ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ }
++ else
++ {
++ register char *namep; /* Current point in `name_space'. */
++ char *cur_path; /* Full path of each file to process. */
++ char *cur_name; /* Base name of each file to process. */
++ unsigned cur_path_size; /* Bytes allocated for `cur_path'. */
++ register unsigned file_len; /* Length of each path to process. */
++ register unsigned pathname_len; /* PATHLEN plus trailing '/'. */
++ boolean did_stat = false;
++
++ if (pathname[pathlen - 1] == '/')
++ pathname_len = pathlen + 1; /* For '\0'; already have '/'. */
++ else
++ pathname_len = pathlen + 2; /* For '/' and '\0'. */
++ cur_path_size = 0;
++ cur_path = NULL;
++
++ /* We're about to leave the directory. If there are any
++ * -execdir argument lists which have been built but have not
++ * yet been processed, do them now because they must be done in
++ * the same directory.
++ */
++ complete_pending_execdirs(get_current_dirfd());
++
++ if (strcmp (name, "."))
++ {
++ enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat);
++ switch (status)
++ {
++ case SafeChdirOK:
++ /* If there had been a change but wd_sanity_check()
++ * accepted it, we need to accept that on the
++ * way back up as well, so modify our record
++ * of what we think we should see later.
++ * If there was no change, the assignments are a no-op.
++ *
++ * However, before performing the assignment, we need to
++ * check that we have the stat information. If O_NOFOLLOW
++ * is available, safely_chdir() will not have needed to use
++ * stat(), and so stat_buf will just contain random data.
++ */
++ if (!did_stat)
++ {
++ /* If there is a link we need to follow it. Hence
++ * the direct call to stat() not through (options.xstat)
++ */
++ set_stat_placeholders(&stat_buf);
++ if (0 != stat(".", &stat_buf))
++ break; /* skip the assignment. */
++ }
++ dir_ids[dir_curr].dev = stat_buf.st_dev;
++ dir_ids[dir_curr].ino = stat_buf.st_ino;
++
++ break;
++
++ case SafeChdirFailWouldBeUnableToReturn:
++ error (0, errno, ".");
++ state.exit_status = 1;
++ break;
++
++ case SafeChdirFailNonexistent:
++ case SafeChdirFailDestUnreadable:
++ case SafeChdirFailStat:
++ case SafeChdirFailNotDir:
++ case SafeChdirFailChdirFailed:
++ error (0, errno, "%s",
++ safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ return;
++
++ case SafeChdirFailSymlink:
++ error (0, 0,
++ _("warning: not following the symbolic link %s"),
++ safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ return;
++ }
++ }
++
++ for (idx=0; idx < dirinfo->size; ++idx)
++ {
++ /* savedirinfo() may return dirinfo=NULL if extended information
++ * is not available.
++ */
++ mode_t mode = (dirinfo->entries[idx].flags & SavedirHaveFileType) ?
++ dirinfo->entries[idx].type_info : 0;
++ namep = dirinfo->entries[idx].name;
++
++ /* Append this directory entry's name to the path being searched. */
++ file_len = pathname_len + strlen (namep);
++ if (file_len > cur_path_size)
++ {
++ while (file_len > cur_path_size)
++ cur_path_size += 1024;
++ if (cur_path)
++ free (cur_path);
++ cur_path = xmalloc (cur_path_size);
++ strcpy (cur_path, pathname);
++ cur_path[pathname_len - 2] = '/';
++ }
++ cur_name = cur_path + pathname_len - 1;
++ strcpy (cur_name, namep);
++
++ state.curdepth++;
++ if (!options.no_leaf_check && !subdirs_unreliable)
++ {
++ if (mode && S_ISDIR(mode) && (subdirs_left == 0))
++ {
++ /* This is a subdirectory, but the number of directories we
++ * have found now exceeds the number we would expect given
++ * the hard link count on the parent. This is likely to be
++ * a bug in the file system driver (e.g. Linux's
++ * /proc file system) or may just be a fact that the OS
++ * doesn't really handle hard links with Unix semantics.
++ * In the latter case, -noleaf should be used routinely.
++ */
++ error(0, 0, _("WARNING: Hard link count is wrong for %1$s "
++ "(saw only st_nlink=%2$d but we already saw %3$d subdirectories): "
++ "this may be a bug in your file system driver. "
++ "Automatically turning on find's -noleaf option. "
++ "Earlier results may have failed to include directories "
++ "that should have been searched."),
++ safely_quote_err_filename(0, pathname),
++ statp->st_nlink,
++ dircount);
++ state.exit_status = 1; /* We know the result is wrong, now */
++ options.no_leaf_check = true; /* Don't make same
++ mistake again */
++ subdirs_unreliable = 1;
++ subdirs_left = 1; /* band-aid for this iteration. */
++ }
++
++ /* Normal case optimization. On normal Unix
++ file systems, a directory that has no subdirectories
++ has two links: its name, and ".". Any additional
++ links are to the ".." entries of its subdirectories.
++ Once we have processed as many subdirectories as
++ there are additional links, we know that the rest of
++ the entries are non-directories -- in other words,
++ leaf files. */
++ {
++ int count;
++ count = process_path (cur_path, cur_name,
++ subdirs_left == 0, pathname,
++ mode);
++ subdirs_left -= count;
++ dircount += count;
++ }
++ }
++ else
++ {
++ /* There might be weird (e.g., CD-ROM or MS-DOS) file systems
++ mounted, which don't have Unix-like directory link counts. */
++ process_path (cur_path, cur_name, false, pathname, mode);
++ }
++
++ state.curdepth--;
++ }
++
++
++ /* We're about to leave the directory. If there are any
++ * -execdir argument lists which have been built but have not
++ * yet been processed, do them now because they must be done in
++ * the same directory.
++ */
++ complete_pending_execdirs(get_current_dirfd());
++
++ if (strcmp (name, "."))
++ {
++ enum SafeChdirStatus status;
++ struct dir_id did;
++
++ /* We could go back and do the next command-line arg
++ instead, maybe using longjmp. */
++ char const *dir;
++ boolean deref = following_links() ? true : false;
++
++ if ( (state.curdepth>0) && !deref)
++ dir = "..";
++ else
++ {
++ chdir_back ();
++ dir = parent;
++ }
++
++ did_stat = false;
++ status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat);
++ switch (status)
++ {
++ case SafeChdirOK:
++ break;
++
++ case SafeChdirFailWouldBeUnableToReturn:
++ error (1, errno, ".");
++ return;
++
++ case SafeChdirFailNonexistent:
++ case SafeChdirFailDestUnreadable:
++ case SafeChdirFailStat:
++ case SafeChdirFailSymlink:
++ case SafeChdirFailNotDir:
++ case SafeChdirFailChdirFailed:
++ error (1, errno, "%s", safely_quote_err_filename(0, pathname));
++ return;
++ }
++
++ if (dir_curr > 0)
++ {
++ did.dev = dir_ids[dir_curr-1].dev;
++ did.ino = dir_ids[dir_curr-1].ino;
++ }
++ else
++ {
++ did.dev = starting_stat_buf.st_dev;
++ did.ino = starting_stat_buf.st_ino;
++ }
++ }
++
++ if (cur_path)
++ free (cur_path);
++ free_dirinfo(dirinfo);
++ }
++
++ if (subdirs_unreliable)
++ {
++ /* Make sure we hasn't used the variable subdirs_left if we knew
++ * we shouldn't do so.
++ */
++ assert (0 == subdirs_left || options.no_leaf_check);
++ }
++}
+diff -purN findutils-4.3.12.orig/find/parser.c findutils-4.3.12/find/parser.c
+--- findutils-4.3.12.orig/find/parser.c 2007-12-19 16:12:34.000000000 -0500
++++ findutils-4.3.12/find/parser.c 2008-01-30 08:46:05.754843619 -0500
+@@ -53,6 +53,13 @@
+ #include <unistd.h>
+ #include <sys/stat.h>
+
++#ifdef WITH_SELINUX
++#include <selinux/selinux.h>
++int optionh_getfilecon(const char *name, security_context_t *p);
++int optionl_getfilecon(const char *name, security_context_t *p);
++int optionp_getfilecon(const char *name, security_context_t *p);
++#endif /*WITH_SELINUX*/
++
+ #if ENABLE_NLS
+ # include <libintl.h>
+ # define _(Text) gettext (Text)
+@@ -156,6 +163,9 @@ static boolean parse_noignore_race PARAM
+ static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
+ static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
+ static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++#ifdef WITH_SELINUX
++static boolean parse_scontext PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++#endif /*WITH_SELINUX*/
+
+ boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
+
+@@ -341,6 +351,8 @@ static struct parser_table const parse_t
+ {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
+ {ARG_TEST, "version", parse_version, NULL}, /* GNU */
+ {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
++ {ARG_TEST, "context", parse_scontext, pred_scontext}, /* SELinux */
++ {ARG_TEST, "-context", parse_scontext, pred_scontext}, /* SELinux */
+ {0, 0, 0, 0}
+ };
+
+@@ -452,10 +464,16 @@ set_follow_state(enum SymlinkOption opt)
+ case SYMLINK_ALWAYS_DEREF: /* -L */
+ options.xstat = optionl_stat;
+ options.no_leaf_check = true;
++#ifdef WITH_SELINUX
++ options.x_getfilecon = optionl_getfilecon;
++#endif /* WITH_SELINUX */
+ break;
+
+ case SYMLINK_NEVER_DEREF: /* -P (default) */
+ options.xstat = optionp_stat;
++#ifdef WITH_SELINUX
++ options.x_getfilecon = optionp_getfilecon;
++#endif /* WITH_SELINUX */
+ /* Can't turn no_leaf_check off because the user might have specified
+ * -noleaf anyway
+ */
+@@ -464,6 +482,9 @@ set_follow_state(enum SymlinkOption opt)
+ case SYMLINK_DEREF_ARGSONLY: /* -H */
+ options.xstat = optionh_stat;
+ options.no_leaf_check = true;
++#ifdef WITH_SELINUX
++ options.x_getfilecon = optionh_getfilecon;
++#endif /* WITH_SELINUX */
+ }
+ }
+ options.symlink_handling = opt;
+@@ -667,6 +688,94 @@ collect_arg_stat_info(char **argv, int *
+
+ The predicate structure is updated with the new information. */
+
++#ifdef WITH_SELINUX
++
++static int
++fallback_getfilecon(const char *name, security_context_t *p, int prev_rv)
++{
++ /* Our original getfilecon() call failed. Perhaps we can't follow a
++ * symbolic link. If that might be the problem, lgetfilecon() the link.
++ * Otherwise, admit defeat.
++ */
++ switch (errno)
++ {
++ case ENOENT:
++ case ENOTDIR:
++#ifdef DEBUG_STAT
++ fprintf(stderr, "fallback_getfilecon(): getfilecon(%s) failed; falling back on lgetfilecon()\n", name);
++#endif
++ return lgetfilecon(name, p);
++
++ case EACCES:
++ case EIO:
++ case ELOOP:
++ case ENAMETOOLONG:
++#ifdef EOVERFLOW
++ case EOVERFLOW: /* EOVERFLOW is not #defined on UNICOS. */
++#endif
++ default:
++ return prev_rv;
++ }
++}
++
++
++/* optionh_getfilecon() implements the getfilecon operation when the
++ * -H option is in effect.
++ *
++ * If the item to be examined is a command-line argument, we follow
++ * symbolic links. If the getfilecon() call fails on the command-line
++ * item, we fall back on the properties of the symbolic link.
++ *
++ * If the item to be examined is not a command-line argument, we
++ * examine the link itself.
++ */
++int
++optionh_getfilecon(const char *name, security_context_t *p)
++{
++ if (0 == state.curdepth)
++ {
++ /* This file is from the command line; deference the link (if it
++ * is a link).
++ */
++ int rv = getfilecon(name, p);
++ if (0 == rv)
++ return 0; /* success */
++ else
++ return fallback_getfilecon(name, p, rv);
++ }
++ else
++ {
++ /* Not a file on the command line; do not derefernce the link.
++ */
++ return lgetfilecon(name, p);
++ }
++}
++
++/* optionl_getfilecon() implements the getfilecon operation when the
++ * -L option is in effect. That option makes us examine the thing the
++ * symbolic link points to, not the symbolic link itself.
++ */
++int
++optionl_getfilecon(const char *name, security_context_t *p)
++{
++ int rv = getfilecon(name, p);
++ if (0 == rv)
++ return 0; /* normal case. */
++ else
++ return fallback_getfilecon(name, p, rv);
++}
++
++/* optionp_getfilecon() implements the stat operation when the -P
++ * option is in effect (this is also the default). That option makes
++ * us examine the symbolic link itself, not the thing it points to.
++ */
++int
++optionp_getfilecon(const char *name, security_context_t *p)
++{
++ return lgetfilecon(name, p);
++}
++#endif /* WITH_SELINUX */
++
+
+ static boolean
+ parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
+@@ -1124,6 +1233,10 @@ tests (N can be +N or -N or N): -amin N
+ -readable -writable -executable\n\
+ -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
+ -used N -user NAME -xtype [bcdpfls]\n"));
++#ifdef WITH_SELINUX
++ puts (_("\
++ -context CONTEXT\n"));
++#endif /*WITH_SELINUX*/
+ puts (_("\
+ actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
+ -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
+@@ -2522,6 +2635,29 @@ parse_version (const struct parser_table
+ exit (0);
+ }
+
++#ifdef WITH_SELINUX
++
++static boolean
++parse_scontext ( const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ if ( (argv == NULL) || (argv[*arg_ptr] == NULL) )
++ return( false );
++
++ our_pred = insert_primary(entry);
++ our_pred->need_stat = false;
++#ifdef DEBUG
++ our_pred->p_name = find_pred_name (pred_scontext);
++#endif /*DEBUG*/
++ our_pred->args.scontext = argv[*arg_ptr];;
++
++ (*arg_ptr)++;
++ return( true );
++}
++
++#endif /*WITH_SELINUX*/
++
+ static boolean
+ parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
+ {
+@@ -2773,7 +2909,11 @@ insert_fprintf (struct format_val *vec,
+ if (*scan2 == '.')
+ for (scan2++; ISDIGIT (*scan2); scan2++)
+ /* Do nothing. */ ;
++#ifdef WITH_SELINUX
++ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyYZ", *scan2))
++#else
+ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2))
++#endif
+ {
+ segmentp = make_segment (segmentp, format, scan2 - format,
+ KIND_FORMAT, *scan2, 0,
+diff -purN findutils-4.3.12.orig/find/parser.c.orig findutils-4.3.12/find/parser.c.orig
+--- findutils-4.3.12.orig/find/parser.c.orig 1969-12-31 19:00:00.000000000 -0500
++++ findutils-4.3.12/find/parser.c.orig 2007-12-19 16:12:34.000000000 -0500
+@@ -0,0 +1,3502 @@
++/* parser.c -- convert the command line args into an expression tree.
++ Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
++ 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <config.h>
++
++#include "defs.h"
++#include <ctype.h>
++#include <math.h>
++#include <assert.h>
++#include <pwd.h>
++#include <errno.h>
++#include <grp.h>
++#include <fnmatch.h>
++#include "modechange.h"
++#include "modetype.h"
++#include "xstrtol.h"
++#include "xalloc.h"
++#include "quote.h"
++#include "quotearg.h"
++#include "buildcmd.h"
++#include "nextelem.h"
++#include "stdio-safer.h"
++#include "regextype.h"
++#include "stat-time.h"
++#include "xstrtod.h"
++#include "fts_.h"
++#include "getdate.h"
++#include "error.h"
++#include "findutils-version.h"
++
++#include <fcntl.h>
++
++
++/* The presence of unistd.h is assumed by gnulib these days, so we
++ * might as well assume it too.
++ */
++/* We need <unistd.h> for isatty(). */
++#include <unistd.h>
++#include <sys/stat.h>
++
++#if ENABLE_NLS
++# include <libintl.h>
++# define _(Text) gettext (Text)
++#else
++# define _(Text) Text
++#endif
++#ifdef gettext_noop
++# define N_(String) gettext_noop (String)
++#else
++/* See locate.c for explanation as to why not use (String) */
++# define N_(String) String
++#endif
++
++#if !defined (isascii) || defined (STDC_HEADERS)
++#ifdef isascii
++#undef isascii
++#endif
++#define isascii(c) 1
++#endif
++
++#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
++#define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
++
++#ifndef HAVE_ENDGRENT
++#define endgrent()
++#endif
++#ifndef HAVE_ENDPWENT
++#define endpwent()
++#endif
++
++static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr));
++static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_newerXY PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++#if 0
++static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++#endif
++static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_time PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++
++boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
++
++
++static boolean insert_type PARAMS((char **argv, int *arg_ptr,
++ const struct parser_table *entry,
++ PRED_FUNC which_pred));
++static boolean insert_regex PARAMS((char *argv[], int *arg_ptr,
++ const struct parser_table *entry,
++ int regex_options));
++static boolean insert_fprintf (struct format_val *vec,
++ const struct parser_table *entry,
++ PRED_FUNC func,
++ const char *format);
++
++static struct segment **make_segment PARAMS((struct segment **segment,
++ char *format, int len,
++ int kind, char format_char,
++ char aux_format_char,
++ struct predicate *pred));
++static boolean insert_exec_ok PARAMS((const char *action,
++ const struct parser_table *entry,
++ int dirfd,
++ char *argv[],
++ int *arg_ptr));
++static boolean get_comp_type PARAMS((const char **str,
++ enum comparison_type *comp_type));
++static boolean get_relative_timestamp PARAMS((const char *str,
++ struct time_val *tval,
++ time_t origin,
++ double sec_per_unit,
++ const char *overflowmessage));
++static boolean get_num PARAMS((const char *str,
++ uintmax_t *num,
++ enum comparison_type *comp_type));
++static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr,
++ const struct parser_table *entry));
++static void open_output_file (const char *path, struct format_val *p);
++static void open_stdout (struct format_val *p);
++static boolean stream_is_tty(FILE *fp);
++static boolean parse_noop PARAMS((const struct parser_table* entry,
++ char **argv, int *arg_ptr));
++
++#define PASTE(x,y) x##y
++#define STRINGIFY(s) #s
++
++#define PARSE_OPTION(what,suffix) \
++ { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
++
++#define PARSE_POSOPT(what,suffix) \
++ { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
++
++#define PARSE_TEST(what,suffix) \
++ { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
++
++#define PARSE_TEST_NP(what,suffix) \
++ { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
++
++#define PARSE_ACTION(what,suffix) \
++ { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
++
++#define PARSE_ACTION_NP(what,suffix) \
++ { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
++
++#define PARSE_PUNCTUATION(what,suffix) \
++ { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
++
++
++/* Predicates we cannot handle in the usual way. If you add an entry
++ * to this table, double-check the switch statement in
++ * pred_sanity_check() to make sure that the new case is being
++ * correctly handled.
++ */
++static struct parser_table const parse_entry_newerXY =
++ {
++ ARG_SPECIAL_PARSE, "newerXY", parse_newerXY, pred_newerXY /* BSD */
++ };
++
++/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
++ If they are in some Unix versions of find, they are marked `Unix'. */
++
++static struct parser_table const parse_table[] =
++{
++ PARSE_PUNCTUATION("!", negate), /* POSIX */
++ PARSE_PUNCTUATION("not", negate), /* GNU */
++ PARSE_PUNCTUATION("(", openparen), /* POSIX */
++ PARSE_PUNCTUATION(")", closeparen), /* POSIX */
++ PARSE_PUNCTUATION(",", comma), /* GNU */
++ PARSE_PUNCTUATION("a", and), /* POSIX */
++ PARSE_TEST ("amin", amin), /* GNU */
++ PARSE_PUNCTUATION("and", and), /* GNU */
++ PARSE_TEST ("anewer", anewer), /* GNU */
++ {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */
++ PARSE_TEST ("cmin", cmin), /* GNU */
++ PARSE_TEST ("cnewer", cnewer), /* GNU */
++ {ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */
++ PARSE_POSOPT ("daystart", daystart), /* GNU */
++ PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
++ PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
++ PARSE_OPTION ("depth", depth), /* POSIX */
++ PARSE_TEST ("empty", empty), /* GNU */
++ {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */
++ {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */
++ PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
++ PARSE_ACTION ("fls", fls), /* GNU */
++ PARSE_POSOPT ("follow", follow), /* GNU, Unix */
++ PARSE_ACTION ("fprint", fprint), /* GNU */
++ PARSE_ACTION ("fprint0", fprint0), /* GNU */
++ {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */
++ PARSE_TEST ("fstype", fstype), /* GNU, Unix */
++ PARSE_TEST ("gid", gid), /* GNU */
++ PARSE_TEST ("group", group), /* POSIX */
++ PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
++ PARSE_TEST ("ilname", ilname), /* GNU */
++ PARSE_TEST ("iname", iname), /* GNU */
++ PARSE_TEST ("inum", inum), /* GNU, Unix */
++ PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
++ PARSE_TEST_NP ("iregex", iregex), /* GNU */
++ PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
++ PARSE_TEST ("links", links), /* POSIX */
++ PARSE_TEST ("lname", lname), /* GNU */
++ PARSE_ACTION ("ls", ls), /* GNU, Unix */
++ PARSE_OPTION ("maxdepth", maxdepth), /* GNU */
++ PARSE_OPTION ("mindepth", mindepth), /* GNU */
++ PARSE_TEST ("mmin", mmin), /* GNU */
++ PARSE_OPTION ("mount", xdev), /* Unix */
++ {ARG_TEST, "mtime", parse_time, pred_mtime}, /* POSIX */
++ PARSE_TEST ("name", name),
++#ifdef UNIMPLEMENTED_UNIX
++ PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */
++#endif
++ PARSE_TEST ("newer", newer), /* POSIX */
++ {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */
++ PARSE_OPTION ("noleaf", noleaf), /* GNU */
++ PARSE_TEST ("nogroup", nogroup), /* POSIX */
++ PARSE_TEST ("nouser", nouser), /* POSIX */
++ PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */
++ PARSE_POSOPT ("nowarn", nowarn), /* GNU */
++ PARSE_PUNCTUATION("o", or), /* POSIX */
++ PARSE_PUNCTUATION("or", or), /* GNU */
++ PARSE_ACTION ("ok", ok), /* POSIX */
++ PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */
++ PARSE_TEST ("path", path), /* GNU, HP-UX, RMS prefers wholename, but anyway soon POSIX */
++ PARSE_TEST ("perm", perm), /* POSIX */
++ PARSE_ACTION ("print", print), /* POSIX */
++ PARSE_ACTION ("print0", print0), /* GNU */
++ {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */
++ PARSE_ACTION ("prune", prune), /* POSIX */
++ PARSE_ACTION ("quit", quit), /* GNU */
++ {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */
++ PARSE_TEST ("regex", regex), /* GNU */
++ PARSE_OPTION ("regextype", regextype), /* GNU */
++ PARSE_TEST ("samefile", samefile), /* GNU */
++#if 0
++ PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */
++#endif
++ PARSE_TEST ("size", size), /* POSIX */
++ PARSE_TEST ("type", type), /* POSIX */
++ PARSE_TEST ("uid", uid), /* GNU */
++ PARSE_TEST ("used", used), /* GNU */
++ PARSE_TEST ("user", user), /* POSIX */
++ PARSE_OPTION ("warn", warn), /* GNU */
++ PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */
++ {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */
++ PARSE_OPTION ("xdev", xdev), /* POSIX */
++ PARSE_TEST ("xtype", xtype), /* GNU */
++#ifdef UNIMPLEMENTED_UNIX
++ /* It's pretty ugly for find to know about archive formats.
++ Plus what it could do with cpio archives is very limited.
++ Better to leave it out. */
++ PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */
++#endif
++ /* gnulib's stdbool.h might have made true and false into macros,
++ * so we can't leave named 'true' and 'false' tokens, so we have
++ * to expeant the relevant entries longhand.
++ */
++ {ARG_TEST, "false", parse_false, pred_false}, /* GNU */
++ {ARG_TEST, "true", parse_true, pred_true }, /* GNU */
++ {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */
++
++ /* Various other cases that don't fit neatly into our macro scheme. */
++ {ARG_TEST, "help", parse_help, NULL}, /* GNU */
++ {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
++ {ARG_TEST, "version", parse_version, NULL}, /* GNU */
++ {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
++ {0, 0, 0, 0}
++};
++
++
++static const char *first_nonoption_arg = NULL;
++static const struct parser_table *noop = NULL;
++
++
++void
++check_option_combinations(const struct predicate *p)
++{
++ enum { seen_delete=1u, seen_prune=2u };
++ unsigned int predicates = 0u;
++
++ while (p)
++ {
++ if (p->pred_func == pred_delete)
++ predicates |= seen_delete;
++ else if (p->pred_func == pred_prune)
++ predicates |= seen_prune;
++ p = p->pred_next;
++ }
++
++ if ((predicates & seen_prune) && (predicates & seen_delete))
++ {
++ /* The user specified both -delete and -prune. One might test
++ * this by first doing
++ * find dirs .... -prune ..... -print
++ * to fnd out what's going to get deleted, and then switch to
++ * find dirs .... -prune ..... -delete
++ * once we are happy. Unfortunately, the -delete action also
++ * implicitly turns on -depth, which will affect the behaviour
++ * of -prune (in fact, it makes it a no-op). In this case we
++ * would like to prevent unfortunate accidents, so we require
++ * the user to have explicitly used -depth.
++ *
++ * We only get away with this because the -delete predicate is not
++ * in POSIX. If it was, we couldn't issue a fatal error here.
++ */
++ if (!options.explicit_depth)
++ {
++ /* This fixes Savannah bug #20865. */
++ error (1, 0, _("The -delete action atomatically turns on -depth, "
++ "but -prune does nothing when -depth is in effect. "
++ "If you want to carry on anyway, just explicitly use "
++ "the -depth option."));
++ }
++ }
++}
++
++
++static const struct parser_table*
++get_noop(void)
++{
++ int i;
++ if (NULL == noop)
++ {
++ for (i = 0; parse_table[i].parser_name != 0; i++)
++ {
++ if (ARG_NOOP ==parse_table[i].type)
++ {
++ noop = &(parse_table[i]);
++ break;
++ }
++ }
++ }
++ return noop;
++}
++
++static int
++get_stat_Ytime(const struct stat *p,
++ char what,
++ struct timespec *ret)
++{
++ switch (what)
++ {
++ case 'a':
++ *ret = get_stat_atime(p);
++ return 1;
++ case 'B':
++ *ret = get_stat_birthtime(p);
++ return (ret->tv_nsec >= 0);
++ case 'c':
++ *ret = get_stat_ctime(p);
++ return 1;
++ case 'm':
++ *ret = get_stat_mtime(p);
++ return 1;
++ default:
++ assert (0);
++ abort();
++ }
++}
++
++void
++set_follow_state(enum SymlinkOption opt)
++{
++ if (options.debug_options & DebugStat)
++ {
++ /* For DebugStat, the choice is made at runtime within debug_stat()
++ * by checking the contents of the symlink_handling variable.
++ */
++ options.xstat = debug_stat;
++ }
++ else
++ {
++ switch (opt)
++ {
++ case SYMLINK_ALWAYS_DEREF: /* -L */
++ options.xstat = optionl_stat;
++ options.no_leaf_check = true;
++ break;
++
++ case SYMLINK_NEVER_DEREF: /* -P (default) */
++ options.xstat = optionp_stat;
++ /* Can't turn no_leaf_check off because the user might have specified
++ * -noleaf anyway
++ */
++ break;
++
++ case SYMLINK_DEREF_ARGSONLY: /* -H */
++ options.xstat = optionh_stat;
++ options.no_leaf_check = true;
++ }
++ }
++ options.symlink_handling = opt;
++}
++
++
++void
++parse_begin_user_args (char **args, int argno,
++ const struct predicate *last,
++ const struct predicate *predicates)
++{
++ (void) args;
++ (void) argno;
++ (void) last;
++ (void) predicates;
++ first_nonoption_arg = NULL;
++}
++
++void
++parse_end_user_args (char **args, int argno,
++ const struct predicate *last,
++ const struct predicate *predicates)
++{
++ /* does nothing */
++ (void) args;
++ (void) argno;
++ (void) last;
++ (void) predicates;
++}
++
++
++/* Check that it is legal to fid the given primary in its
++ * position and return it.
++ */
++const struct parser_table*
++found_parser(const char *original_arg, const struct parser_table *entry)
++{
++ /* If this is an option, but we have already had a
++ * non-option argument, the user may be under the
++ * impression that the behaviour of the option
++ * argument is conditional on some preceding
++ * tests. This might typically be the case with,
++ * for example, -maxdepth.
++ *
++ * The options -daystart and -follow are exempt
++ * from this treatment, since their positioning
++ * in the command line does have an effect on
++ * subsequent tests but not previous ones. That
++ * might be intentional on the part of the user.
++ */
++ if (entry->type != ARG_POSITIONAL_OPTION)
++ {
++ /* Something other than -follow/-daystart.
++ * If this is an option, check if it followed
++ * a non-option and if so, issue a warning.
++ */
++ if (entry->type == ARG_OPTION)
++ {
++ if ((first_nonoption_arg != NULL)
++ && options.warnings )
++ {
++ /* option which follows a non-option */
++ error (0, 0,
++ _("warning: you have specified the %1$s "
++ "option after a non-option argument %2$s, "
++ "but options are not positional (%3$s affects "
++ "tests specified before it as well as those "
++ "specified after it). Please specify options "
++ "before other arguments.\n"),
++ original_arg,
++ first_nonoption_arg,
++ original_arg);
++ }
++ }
++ else
++ {
++ /* Not an option or a positional option,
++ * so remember we've seen it in order to
++ * use it in a possible future warning message.
++ */
++ if (first_nonoption_arg == NULL)
++ {
++ first_nonoption_arg = original_arg;
++ }
++ }
++ }
++
++ return entry;
++}
++
++
++/* Return a pointer to the parser function to invoke for predicate
++ SEARCH_NAME.
++ Return NULL if SEARCH_NAME is not a valid predicate name. */
++
++const struct parser_table*
++find_parser (char *search_name)
++{
++ int i;
++ const char *original_arg = search_name;
++
++ /* Ugh. Special case -newerXY. */
++ if (0 == strncmp("-newer", search_name, 6)
++ && (8 == strlen(search_name)))
++ {
++ return found_parser(original_arg, &parse_entry_newerXY);
++ }
++
++ if (*search_name == '-')
++ search_name++;
++
++ for (i = 0; parse_table[i].parser_name != 0; i++)
++ {
++ if (strcmp (parse_table[i].parser_name, search_name) == 0)
++ {
++ return found_parser(original_arg, &parse_table[i]);
++ }
++ }
++ return NULL;
++}
++
++static float
++estimate_file_age_success_rate(float num_days)
++{
++ if (num_days < 0.1)
++ {
++ /* Assume 1% of files have timestamps in the future */
++ return 0.01f;
++ }
++ else if (num_days < 1)
++ {
++ /* Assume 30% of files have timestamps today */
++ return 0.3f;
++ }
++ else if (num_days > 100)
++ {
++ /* Assume 30% of files are very old */
++ return 0.3f;
++ }
++ else
++ {
++ /* Assume 39% of files are between 1 and 100 days old. */
++ return 0.39f;
++ }
++}
++
++static float
++estimate_timestamp_success_rate(time_t when)
++{
++ int num_days = (options.cur_day_start - when) / 86400;
++ return estimate_file_age_success_rate(num_days);
++}
++
++/* Collect an argument from the argument list, or
++ * return false.
++ */
++static boolean
++collect_arg(char **argv, int *arg_ptr, const char **collected_arg)
++{
++ if ((argv == NULL) || (argv[*arg_ptr] == NULL))
++ {
++ *collected_arg = NULL;
++ return false;
++ }
++ else
++ {
++ *collected_arg = argv[*arg_ptr];
++ (*arg_ptr)++;
++ return true;
++ }
++}
++
++static boolean
++collect_arg_stat_info(char **argv, int *arg_ptr, struct stat *p)
++{
++ const char *filename;
++ if (collect_arg(argv, arg_ptr, &filename))
++ {
++ if (0 == (options.xstat)(filename, p))
++ {
++ return true;
++ }
++ else
++ {
++ fatal_file_error(filename);
++ }
++ }
++ else
++ {
++ return false;
++ }
++}
++
++/* The parsers are responsible to continue scanning ARGV for
++ their arguments. Each parser knows what is and isn't
++ allowed for itself.
++
++ ARGV is the argument array.
++ *ARG_PTR is the index to start at in ARGV,
++ updated to point beyond the last element consumed.
++
++ The predicate structure is updated with the new information. */
++
++
++static boolean
++parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = get_new_pred (entry);
++ our_pred->pred_func = pred_and;
++ our_pred->p_type = BI_OP;
++ our_pred->p_prec = AND_PREC;
++ our_pred->need_stat = our_pred->need_type = false;
++ return true;
++}
++
++static boolean
++parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct stat stat_newer;
++
++ set_stat_placeholders(&stat_newer);
++ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.reftime.xval = XVAL_ATIME;
++ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
++ our_pred->args.reftime.kind = COMP_GT;
++ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
++ return true;
++ }
++ return false;
++}
++
++boolean
++parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = get_new_pred (entry);
++ our_pred->pred_func = pred_closeparen;
++ our_pred->p_type = CLOSE_PAREN;
++ our_pred->p_prec = NO_PREC;
++ our_pred->need_stat = our_pred->need_type = false;
++ return true;
++}
++
++static boolean
++parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct stat stat_newer;
++
++ set_stat_placeholders(&stat_newer);
++ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.reftime.xval = XVAL_CTIME; /* like -newercm */
++ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
++ our_pred->args.reftime.kind = COMP_GT;
++ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = get_new_pred (entry);
++ our_pred->pred_func = pred_comma;
++ our_pred->p_type = BI_OP;
++ our_pred->p_prec = COMMA_PREC;
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->est_success_rate = 1.0f;
++ return true;
++}
++
++static boolean
++parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct tm *local;
++
++ (void) entry;
++ (void) argv;
++ (void) arg_ptr;
++
++ if (options.full_days == false)
++ {
++ options.cur_day_start += DAYSECS;
++ local = localtime (&options.cur_day_start);
++ options.cur_day_start -= (local
++ ? (local->tm_sec + local->tm_min * 60
++ + local->tm_hour * 3600)
++ : options.cur_day_start % DAYSECS);
++ options.full_days = true;
++ }
++ return true;
++}
++
++static boolean
++parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
++{
++ struct predicate *our_pred;
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = insert_primary (entry);
++ our_pred->side_effects = our_pred->no_default_print = true;
++ /* -delete implies -depth */
++ options.do_dir_first = false;
++
++ /* We do not need stat information because we check for the case
++ * (errno==EISDIR) in pred_delete.
++ */
++ our_pred->need_stat = our_pred->need_type = false;
++
++ our_pred->est_success_rate = 1.0f;
++ return true;
++}
++
++static boolean
++parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ (void) entry;
++ (void) argv;
++
++ options.do_dir_first = false;
++ options.explicit_depth = true;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ if (options.warnings)
++ {
++ error (0, 0,
++ _("warning: the -d option is deprecated; please use "
++ "-depth instead, because the latter is a "
++ "POSIX-compliant feature."));
++ }
++ return parse_depth(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = insert_primary (entry);
++ our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */
++ return true;
++}
++
++static boolean
++parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_exec_ok ("-exec", entry, get_start_dirfd(), argv, arg_ptr);
++}
++
++static boolean
++parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr);
++}
++
++static boolean
++parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = insert_primary (entry);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->side_effects = our_pred->no_default_print = false;
++ our_pred->est_success_rate = 0.0f;
++ return true;
++}
++
++static boolean
++insert_fls (const struct parser_table* entry, const char *filename)
++{
++ struct predicate *our_pred = insert_primary (entry);
++ if (filename)
++ open_output_file (filename, &our_pred->args.printf_vec);
++ else
++ open_stdout (&our_pred->args.printf_vec);
++ our_pred->side_effects = our_pred->no_default_print = true;
++ our_pred->est_success_rate = 1.0f;
++ return true;
++}
++
++
++static boolean
++parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *filename;
++ return collect_arg(argv, arg_ptr, &filename)
++ && insert_fls(entry, filename);
++}
++
++static boolean
++parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ set_follow_state(SYMLINK_ALWAYS_DEREF);
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ const char *filename;
++ if (collect_arg(argv, arg_ptr, &filename))
++ {
++ our_pred = insert_primary (entry);
++ open_output_file (filename, &our_pred->args.printf_vec);
++ our_pred->side_effects = our_pred->no_default_print = true;
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->est_success_rate = 1.0f;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++static boolean
++insert_fprint(const struct parser_table* entry, const char *filename)
++{
++ struct predicate *our_pred = insert_primary (entry);
++ if (filename)
++ open_output_file (filename, &our_pred->args.printf_vec);
++ else
++ open_stdout (&our_pred->args.printf_vec);
++ our_pred->side_effects = our_pred->no_default_print = true;
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->est_success_rate = 1.0f;
++ return true;
++}
++
++
++static boolean
++parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *filename;
++ if (collect_arg(argv, arg_ptr, &filename))
++ return insert_fprint(entry, filename);
++ else
++ return false;
++}
++
++static float estimate_fstype_success_rate(const char *fsname)
++{
++ struct stat dir_stat;
++ const char *dir = "/";
++ if (0 == stat(dir, &dir_stat))
++ {
++ const char *fstype = filesystem_type(&dir_stat, dir);
++ /* Assume most files are on the same file system type as the root fs. */
++ if (0 == strcmp(fsname, fstype))
++ return 0.7f;
++ else
++ return 0.3f;
++ }
++ return 1.0f;
++}
++
++
++static boolean
++parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *typename;
++ if (collect_arg(argv, arg_ptr, &typename))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.str = typename;
++
++ /* This is an expensive operation, so although there are
++ * circumstances where it is selective, we ignore this fact
++ * because we probably don't want to promote this test to the
++ * front anyway.
++ */
++ our_pred->est_success_rate = estimate_fstype_success_rate(typename);
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++static boolean
++parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *p = insert_num (argv, arg_ptr, entry);
++ if (p)
++ {
++ p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++
++static int
++safe_atoi (const char *s)
++{
++ long lval;
++ char *end;
++
++ errno = 0;
++ lval = strtol(s, &end, 10);
++ if ( (LONG_MAX == lval) || (LONG_MIN == lval) )
++ {
++ /* max/min possible value, or an error. */
++ if (errno == ERANGE)
++ {
++ /* too big, or too small. */
++ error(1, errno, "%s", s);
++ }
++ else
++ {
++ /* not a valid number */
++ error(1, errno, "%s", s);
++ }
++ /* Otherwise, we do a range chack against INT_MAX and INT_MIN
++ * below.
++ */
++ }
++
++ if (lval > INT_MAX || lval < INT_MIN)
++ {
++ /* The number was in range for long, but not int. */
++ errno = ERANGE;
++ error(1, errno, "%s", s);
++ }
++ else if (*end)
++ {
++ error(1, errno, "Unexpected suffix %s on %s",
++ quotearg_n_style(0, options.err_quoting_style, end),
++ quotearg_n_style(1, options.err_quoting_style, s));
++ }
++ else if (end == s)
++ {
++ error(1, errno, "Expected an integer: %s",
++ quotearg_n_style(0, options.err_quoting_style, s));
++ }
++ return (int)lval;
++}
++
++
++static boolean
++parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *groupname;
++
++ if (collect_arg(argv, arg_ptr, &groupname))
++ {
++ gid_t gid;
++ struct predicate *our_pred;
++ struct group *cur_gr = getgrnam(groupname);
++ endgrent();
++ if (cur_gr)
++ {
++ gid = cur_gr->gr_gid;
++ }
++ else
++ {
++ const int gid_len = strspn (groupname, "0123456789");
++ if (gid_len)
++ {
++ if (groupname[gid_len] == 0)
++ {
++ gid = safe_atoi (groupname);
++ }
++ else
++ {
++ /* XXX: no test in test suite for this */
++ error(1, 0, _("%1$s is not the name of an existing group and"
++ " it does not look like a numeric group ID "
++ "because it has the unexpected suffix %2$s"),
++ quotearg_n_style(0, options.err_quoting_style, groupname),
++ quotearg_n_style(1, options.err_quoting_style, groupname+gid_len));
++ return false;
++ }
++ }
++ else
++ {
++ if (*groupname)
++ {
++ /* XXX: no test in test suite for this */
++ error(1, 0, _("%s is not the name of an existing group"),
++ quotearg_n_style(0, options.err_quoting_style, groupname));
++ }
++ else
++ {
++ error(1, 0, _("argument to -group is empty, but should be a group name"));
++ }
++ return false;
++ }
++ }
++ our_pred = insert_primary (entry);
++ our_pred->args.gid = gid;
++ our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2;
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ (void) entry;
++ (void) argv;
++ (void) arg_ptr;
++
++ usage(stdout, 0, NULL);
++ puts (_("\n\
++default path is the current directory; default expression is -print\n\
++expression may consist of: operators, options, tests, and actions:\n"));
++ puts (_("\
++operators (decreasing precedence; -and is implicit where no others are given):\n\
++ ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
++ EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
++ puts (_("\
++positional options (always true): -daystart -follow -regextype\n\n\
++normal options (always true, specified before other expressions):\n\
++ -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
++ --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
++ puts (_("\
++tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
++ -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
++ -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
++ -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
++ puts (_("\
++ -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
++ -readable -writable -executable\n\
++ -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
++ -used N -user NAME -xtype [bcdpfls]\n"));
++ puts (_("\
++actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
++ -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
++ -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
++ -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
++"));
++ puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
++page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
++email to <bug-findutils@gnu.org>."));
++ exit (0);
++}
++
++static float
++estimate_pattern_match_rate(const char *pattern, int is_regex)
++{
++ if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, ".")))
++ {
++ /* A wildcard; assume the pattern matches most files. */
++ return 0.8f;
++ }
++ else
++ {
++ return 0.1f;
++ }
++}
++
++static boolean
++parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *name;
++ if (collect_arg(argv, arg_ptr, &name))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.str = name;
++ /* Use the generic glob pattern estimator to figure out how many
++ * links will match, but bear in mind that most files won't be links.
++ */
++ our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0);
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++
++/* sanity check the fnmatch() function to make sure that case folding
++ * is supported (as opposed to just having the flag ignored).
++ */
++static boolean
++fnmatch_sanitycheck(void)
++{
++ static boolean checked = false;
++ if (!checked)
++ {
++ if (0 != fnmatch("foo", "foo", 0)
++ || 0 == fnmatch("Foo", "foo", 0)
++ || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
++ {
++ error (1, 0, _("sanity check of the fnmatch() library function failed."));
++ return false;
++ }
++ checked = true;
++ }
++ return checked;
++}
++
++
++static boolean
++check_name_arg(const char *pred, const char *arg)
++{
++ if (options.warnings && strchr(arg, '/'))
++ {
++ error(0, 0,_("warning: Unix filenames usually don't contain slashes "
++ "(though pathnames do). That means that '%s %s' will "
++ "probably evaluate to false all the time on this system. "
++ "You might find the '-wholename' test more useful, or "
++ "perhaps '-samefile'. Alternatively, if you are using "
++ "GNU grep, you could "
++ "use 'find ... -print0 | grep -FzZ %s'."),
++ pred,
++ safely_quote_err_filename(0, arg),
++ safely_quote_err_filename(1, arg));
++ }
++ return true; /* allow it anyway */
++}
++
++
++
++static boolean
++parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *name;
++ fnmatch_sanitycheck();
++ if (collect_arg(argv, arg_ptr, &name))
++ {
++ if (check_name_arg("-iname", name))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->args.str = name;
++ our_pred->est_success_rate = estimate_pattern_match_rate(name, 0);
++ return true;
++ }
++ }
++ return false;
++}
++
++static boolean
++parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *p = insert_num (argv, arg_ptr, entry);
++ if (p)
++ {
++ /* inode number is exact match only, so very low proportions of
++ * files match
++ */
++ p->est_success_rate = 1e-6;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++/* -ipath is deprecated (at RMS's request) in favour of
++ * -iwholename. See the node "GNU Manuals" in standards.texi
++ * for the rationale for this (basically, GNU prefers the use
++ * of the phrase "file name" to "path name"
++ */
++static boolean
++parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *name;
++
++ fnmatch_sanitycheck ();
++ if (collect_arg (argv, arg_ptr, &name))
++ {
++ struct predicate *our_pred = insert_primary_withpred (entry, pred_ipath);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->args.str = name;
++ our_pred->est_success_rate = estimate_pattern_match_rate (name, 0);
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return parse_ipath (entry, argv, arg_ptr);
++}
++
++static boolean
++parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
++}
++
++static boolean
++parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *p = insert_num (argv, arg_ptr, entry);
++ if (p)
++ {
++ if (p->args.numinfo.l_val == 1)
++ p->est_success_rate = 0.99;
++ else if (p->args.numinfo.l_val == 2)
++ p->est_success_rate = 0.01;
++ else
++ p->est_success_rate = 1e-3;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++static boolean
++parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *name;
++ fnmatch_sanitycheck();
++ if (collect_arg(argv, arg_ptr, &name))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.str = name;
++ our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0);
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ (void) &argv;
++ (void) &arg_ptr;
++ return insert_fls(entry, NULL);
++}
++
++static boolean
++insert_depthspec(const struct parser_table* entry, char **argv, int *arg_ptr,
++ int *limitptr)
++{
++ const char *depthstr;
++ int depth_len;
++ const char *predicate = argv[(*arg_ptr)-1];
++ if (collect_arg(argv, arg_ptr, &depthstr))
++ {
++ depth_len = strspn (depthstr, "0123456789");
++ if ((depth_len > 0) && (depthstr[depth_len] == 0))
++ {
++ (*limitptr) = safe_atoi (depthstr);
++ if (*limitptr >= 0)
++ {
++ return parse_noop(entry, argv, arg_ptr);
++ }
++ }
++ error(1, 0, _("Expected a positive decimal integer argument to %1$s, but got %2$s"),
++ predicate,
++ quotearg_n_style(0, options.err_quoting_style, depthstr));
++ return false;
++ }
++ /* missing argument */
++ return false;
++}
++
++
++static boolean
++parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_depthspec(entry, argv, arg_ptr, &options.maxdepth);
++}
++
++static boolean
++parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_depthspec(entry, argv, arg_ptr, &options.mindepth);
++}
++
++
++static boolean
++do_parse_xmin (const struct parser_table* entry,
++ char **argv,
++ int *arg_ptr,
++ enum xval xv)
++{
++ const char *minutes;
++
++ if (collect_arg(argv, arg_ptr, &minutes))
++ {
++ struct time_val tval;
++ tval.xval = xv;
++ if (get_relative_timestamp(minutes, &tval,
++ options.cur_day_start + DAYSECS, 60,
++ "arithmetic overflow while converting %s "
++ "minutes to a number of seconds"))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.reftime = tval;
++ our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec);
++ return true;
++ }
++ }
++ return false;
++}
++static boolean
++parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return do_parse_xmin(entry, argv, arg_ptr, XVAL_ATIME);
++}
++
++static boolean
++parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return do_parse_xmin(entry, argv, arg_ptr, XVAL_CTIME);
++}
++
++
++static boolean
++parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return do_parse_xmin(entry, argv, arg_ptr, XVAL_MTIME);
++}
++
++static boolean
++parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *name;
++ if (collect_arg(argv, arg_ptr, &name))
++ {
++ fnmatch_sanitycheck();
++ if (check_name_arg("-name", name))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->args.str = name;
++ our_pred->est_success_rate = estimate_pattern_match_rate(name, 0);
++ return true;
++ }
++ }
++ return false;
++}
++
++static boolean
++parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) &argv;
++ (void) &arg_ptr;
++
++ our_pred = get_new_pred_chk_op (entry);
++ our_pred->pred_func = pred_negate;
++ our_pred->p_type = UNI_OP;
++ our_pred->p_prec = NEGATE_PREC;
++ our_pred->need_stat = our_pred->need_type = false;
++ return true;
++}
++
++static boolean
++parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ struct stat stat_newer;
++
++ set_stat_placeholders(&stat_newer);
++ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer))
++ {
++ our_pred = insert_primary (entry);
++ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
++ our_pred->args.reftime.xval = XVAL_MTIME;
++ our_pred->args.reftime.kind = COMP_GT;
++ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
++ return true;
++ }
++ return false;
++}
++
++
++static boolean
++parse_newerXY (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ (void) argv;
++ (void) arg_ptr;
++
++ if ((argv == NULL) || (argv[*arg_ptr] == NULL))
++ {
++ return false;
++ }
++ else if (8u != strlen(argv[*arg_ptr]))
++ {
++ return false;
++ }
++ else
++ {
++ char x, y;
++ const char validchars[] = "aBcmt";
++
++ assert (0 == strncmp("-newer", argv[*arg_ptr], 6));
++ x = argv[*arg_ptr][6];
++ y = argv[*arg_ptr][7];
++
++
++#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC)
++ if ('B' == x || 'B' == y)
++ {
++ error(0, 0,
++ _("This system does not provide a way to find the birth time of a file."));
++ return 0;
++ }
++#endif
++
++ /* -newertY (for any Y) is invalid. */
++ if (x == 't'
++ || 0 == strchr(validchars, x)
++ || 0 == strchr( validchars, y))
++ {
++ return false;
++ }
++ else
++ {
++ struct predicate *our_pred;
++
++ /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr
++ * past the test name (for most other tests, this is already done)
++ */
++ (*arg_ptr)++;
++
++ our_pred = insert_primary (entry);
++
++
++ switch (x)
++ {
++ case 'a':
++ our_pred->args.reftime.xval = XVAL_ATIME;
++ break;
++ case 'B':
++ our_pred->args.reftime.xval = XVAL_BIRTHTIME;
++ break;
++ case 'c':
++ our_pred->args.reftime.xval = XVAL_CTIME;
++ break;
++ case 'm':
++ our_pred->args.reftime.xval = XVAL_MTIME;
++ break;
++ default:
++ assert (strchr(validchars, x));
++ assert (0);
++ }
++
++ if ('t' == y)
++ {
++ if (!get_date(&our_pred->args.reftime.ts,
++ argv[*arg_ptr],
++ &options.start_time))
++ {
++ error(1, 0,
++ _("I cannot figure out how to interpret %s as a date or time"),
++ quotearg_n_style(0, options.err_quoting_style, argv[*arg_ptr]));
++ }
++ }
++ else
++ {
++ struct stat stat_newer;
++
++ /* Stat the named file. */
++ set_stat_placeholders(&stat_newer);
++ if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
++ fatal_file_error(argv[*arg_ptr]);
++
++ if (!get_stat_Ytime(&stat_newer, y, &our_pred->args.reftime.ts))
++ {
++ /* We cannot extract a timestamp from the struct stat. */
++ error(1, 0, _("Cannot obtain birth time of file %s"),
++ safely_quote_err_filename(0, argv[*arg_ptr]));
++ }
++ }
++ our_pred->args.reftime.kind = COMP_GT;
++ our_pred->est_success_rate = estimate_timestamp_success_rate(our_pred->args.reftime.ts.tv_sec);
++ (*arg_ptr)++;
++
++ assert (our_pred->pred_func != NULL);
++ assert (our_pred->pred_func == pred_newerXY);
++ assert (our_pred->need_stat);
++ return true;
++ }
++ }
++}
++
++
++static boolean
++parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ options.no_leaf_check = true;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++#ifdef CACHE_IDS
++/* Arbitrary amount by which to increase size
++ of `uid_unused' and `gid_unused'. */
++#define ALLOC_STEP 2048
++
++/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
++char *uid_unused = NULL;
++
++/* Number of elements in `uid_unused'. */
++unsigned uid_allocated;
++
++/* Similar for GIDs and group entries. */
++char *gid_unused = NULL;
++unsigned gid_allocated;
++#endif
++
++static boolean
++parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) &argv;
++ (void) &arg_ptr;
++
++ our_pred = insert_primary (entry);
++ our_pred->est_success_rate = 1e-4;
++#ifdef CACHE_IDS
++ if (gid_unused == NULL)
++ {
++ struct group *gr;
++
++ gid_allocated = ALLOC_STEP;
++ gid_unused = xmalloc (gid_allocated);
++ memset (gid_unused, 1, gid_allocated);
++ setgrent ();
++ while ((gr = getgrent ()) != NULL)
++ {
++ if ((unsigned) gr->gr_gid >= gid_allocated)
++ {
++ unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
++ gid_unused = xrealloc (gid_unused, new_allocated);
++ memset (gid_unused + gid_allocated, 1,
++ new_allocated - gid_allocated);
++ gid_allocated = new_allocated;
++ }
++ gid_unused[(unsigned) gr->gr_gid] = 0;
++ }
++ endgrent ();
++ }
++#endif
++ return true;
++}
++
++static boolean
++parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ (void) argv;
++ (void) arg_ptr;
++
++
++ our_pred = insert_primary (entry);
++ our_pred->est_success_rate = 1e-3;
++#ifdef CACHE_IDS
++ if (uid_unused == NULL)
++ {
++ struct passwd *pw;
++
++ uid_allocated = ALLOC_STEP;
++ uid_unused = xmalloc (uid_allocated);
++ memset (uid_unused, 1, uid_allocated);
++ setpwent ();
++ while ((pw = getpwent ()) != NULL)
++ {
++ if ((unsigned) pw->pw_uid >= uid_allocated)
++ {
++ unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
++ uid_unused = xrealloc (uid_unused, new_allocated);
++ memset (uid_unused + uid_allocated, 1,
++ new_allocated - uid_allocated);
++ uid_allocated = new_allocated;
++ }
++ uid_unused[(unsigned) pw->pw_uid] = 0;
++ }
++ endpwent ();
++ }
++#endif
++ return true;
++}
++
++static boolean
++parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ options.warnings = false;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_exec_ok ("-ok", entry, get_start_dirfd(), argv, arg_ptr);
++}
++
++static boolean
++parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr);
++}
++
++boolean
++parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = get_new_pred_chk_op (entry);
++ our_pred->pred_func = pred_openparen;
++ our_pred->p_type = OPEN_PAREN;
++ our_pred->p_prec = NO_PREC;
++ our_pred->need_stat = our_pred->need_type = false;
++ return true;
++}
++
++static boolean
++parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = get_new_pred (entry);
++ our_pred->pred_func = pred_or;
++ our_pred->p_type = BI_OP;
++ our_pred->p_prec = OR_PREC;
++ our_pred->need_stat = our_pred->need_type = false;
++ return true;
++}
++
++/* For some time, -path was deprecated (at RMS's request) in favour of
++ * -iwholename. See the node "GNU Manuals" in standards.texi for the
++ * rationale for this (basically, GNU prefers the use of the phrase
++ * "file name" to "path name".
++ *
++ * We do not issue a warning that this usage is deprecated
++ * since
++ * (a) HPUX find supports this predicate also and
++ * (b) it will soon be in POSIX anyway.
++ */
++static boolean
++parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *name;
++ if (collect_arg(argv, arg_ptr, &name))
++ {
++ struct predicate *our_pred = insert_primary_withpred (entry, pred_path);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->args.str = name;
++ our_pred->est_success_rate = estimate_pattern_match_rate (name, 0);
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return parse_path (entry, argv, arg_ptr);
++}
++
++static void
++non_posix_mode(const char *mode)
++{
++ if (options.posixly_correct)
++ {
++ error (1, 0, _("Mode %s is not valid when POSIXLY_CORRECT is on."),
++ quotearg_n_style(0, options.err_quoting_style, mode));
++ }
++}
++
++
++static boolean
++parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ mode_t perm_val[2];
++ float rate;
++ int mode_start = 0;
++ boolean havekind = false;
++ enum permissions_type kind = PERM_EXACT;
++ struct mode_change *change = NULL;
++ struct predicate *our_pred;
++ const char *perm_expr;
++
++ if (!collect_arg(argv, arg_ptr, &perm_expr))
++ return false;
++
++ switch (perm_expr[0])
++ {
++ case '-':
++ mode_start = 1;
++ kind = PERM_AT_LEAST;
++ havekind = true;
++ rate = 0.2;
++ break;
++
++ case '+':
++ change = mode_compile (perm_expr);
++ if (NULL == change)
++ {
++ /* Most likely the caller is an old script that is still
++ * using the obsolete GNU syntax '-perm +MODE'. This old
++ * syntax was withdrawn in favor of '-perm /MODE' because
++ * it is incompatible with POSIX in some cases, but we
++ * still support uses of it that are not incompatible with
++ * POSIX.
++ *
++ * Example: POSIXLY_CORRECT=y find -perm +a+x
++ */
++ non_posix_mode(perm_expr);
++
++ /* support the previous behaviour. */
++ mode_start = 1;
++ kind = PERM_ANY;
++ rate = 0.3;
++ }
++ else
++ {
++ /* This is a POSIX-compatible usage */
++ mode_start = 0;
++ kind = PERM_EXACT;
++ rate = 0.1;
++ }
++ havekind = true;
++ break;
++
++ case '/': /* GNU extension */
++ non_posix_mode(perm_expr);
++ mode_start = 1;
++ kind = PERM_ANY;
++ havekind = true;
++ rate = 0.3;
++ break;
++
++ default:
++ /* For example, '-perm 0644', which is valid and matches
++ * only files whose mode is exactly 0644.
++ */
++ mode_start = 0;
++ kind = PERM_EXACT;
++ havekind = true;
++ rate = 0.01;
++ break;
++ }
++
++ if (NULL == change)
++ {
++ change = mode_compile (perm_expr + mode_start);
++ if (NULL == change)
++ error (1, 0, _("invalid mode %s"
++ /* TRANSLATORS: the argument is a
++ * file permission string like 'u=rw,go='
++ */),
++ quotearg_n_style(0, options.err_quoting_style, perm_expr));
++ }
++ perm_val[0] = mode_adjust (0, false, 0, change, NULL);
++ perm_val[1] = mode_adjust (0, true, 0, change, NULL);
++ free (change);
++
++ if (('/' == perm_expr[0]) && (0 == perm_val[0]) && (0 == perm_val[1]))
++ {
++ /* The meaning of -perm /000 will change in the future. It
++ * currently matches no files, but like -perm -000 it should
++ * match all files.
++ *
++ * Starting in 2005, we used to issue a warning message
++ * informing the user that the behaviour would change in the
++ * future. We have now changed the behaviour and issue a
++ * warning message that the behaviour recently changed.
++ */
++ error (0, 0,
++ _("warning: you have specified a mode pattern %s (which is "
++ "equivalent to /000). The meaning of -perm /000 has now been "
++ "changed to be consistent with -perm -000; that is, while it "
++ "used to match no files, it now matches all files."),
++ perm_expr);
++
++ kind = PERM_AT_LEAST;
++ havekind = true;
++
++ /* The "magic" number below is just the fraction of files on my
++ * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks).
++ * Actual totals are 1472 and 1073833.
++ */
++ rate = 0.9986; /* probably matches anything but a broken symlink */
++ }
++
++ our_pred = insert_primary (entry);
++ our_pred->est_success_rate = rate;
++ if (havekind)
++ {
++ our_pred->args.perm.kind = kind;
++ }
++ else
++ {
++
++ switch (perm_expr[0])
++ {
++ case '-':
++ our_pred->args.perm.kind = PERM_AT_LEAST;
++ break;
++ case '+':
++ our_pred->args.perm.kind = PERM_ANY;
++ break;
++ default:
++ our_pred->args.perm.kind = PERM_EXACT;
++ break;
++ }
++ }
++ memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val);
++ return true;
++}
++
++boolean
++parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = insert_primary (entry);
++ /* -print has the side effect of printing. This prevents us
++ from doing undesired multiple printing when the user has
++ already specified -print. */
++ our_pred->side_effects = our_pred->no_default_print = true;
++ our_pred->need_stat = our_pred->need_type = false;
++ open_stdout(&our_pred->args.printf_vec);
++ return true;
++}
++
++static boolean
++parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_fprint(entry, NULL);
++}
++
++static boolean
++parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *format;
++ if (collect_arg(argv, arg_ptr, &format))
++ {
++ struct format_val fmt;
++ open_stdout(&fmt);
++ return insert_fprintf (&fmt, entry, pred_fprintf, format);
++ }
++ return false;
++}
++
++static boolean
++parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *format, *filename;
++ if (collect_arg(argv, arg_ptr, &filename))
++ {
++ if (collect_arg(argv, arg_ptr, &format))
++ {
++ struct format_val fmt;
++ open_output_file (filename, &fmt);
++ return insert_fprintf (&fmt, entry, pred_fprintf, format);
++ }
++ }
++ return false;
++}
++
++static boolean
++parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = insert_primary (entry);
++ our_pred->need_stat = our_pred->need_type = false;
++ /* -prune has a side effect that it does not descend into
++ the current directory. */
++ our_pred->side_effects = true;
++ our_pred->no_default_print = false;
++ return true;
++}
++
++static boolean
++parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred = insert_primary (entry);
++ (void) argv;
++ (void) arg_ptr;
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->side_effects = true; /* Exiting is a side effect... */
++ our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
++ our_pred->est_success_rate = 1.0f;
++ return true;
++}
++
++
++static boolean
++parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *type_name;
++ if (collect_arg(argv, arg_ptr, &type_name))
++ {
++ /* collect the regex type name */
++ options.regex_options = get_regex_type(type_name);
++ return parse_noop(entry, argv, arg_ptr);
++ }
++ return false;
++}
++
++
++static boolean
++parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_regex (argv, arg_ptr, entry, options.regex_options);
++}
++
++static boolean
++insert_regex (char **argv,
++ int *arg_ptr,
++ const struct parser_table *entry,
++ int regex_options)
++{
++ const char *rx;
++ if (collect_arg(argv, arg_ptr, &rx))
++ {
++ struct re_pattern_buffer *re;
++ const char *error_message;
++ struct predicate *our_pred = insert_primary_withpred (entry, pred_regex);
++ our_pred->need_stat = our_pred->need_type = false;
++ re = xmalloc (sizeof (struct re_pattern_buffer));
++ our_pred->args.regex = re;
++ re->allocated = 100;
++ re->buffer = xmalloc (re->allocated);
++ re->fastmap = NULL;
++
++ re_set_syntax(regex_options);
++ re->syntax = regex_options;
++ re->translate = NULL;
++
++ error_message = re_compile_pattern (rx, strlen(rx), re);
++ if (error_message)
++ error (1, 0, "%s", error_message);
++ our_pred->est_success_rate = estimate_pattern_match_rate(rx, 1);
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ uintmax_t num;
++ char suffix;
++ enum comparison_type c_type;
++
++ int blksize = 512;
++ int len;
++
++ /* XXX: cannot (yet) convert to ue collect_arg() as this
++ * function modifies the args in-place.
++ */
++ if ((argv == NULL) || (argv[*arg_ptr] == NULL))
++ return false;
++
++ len = strlen (argv[*arg_ptr]);
++ if (len == 0)
++ error (1, 0, _("invalid null argument to -size"));
++
++ suffix = argv[*arg_ptr][len - 1];
++ switch (suffix)
++ {
++ case 'b':
++ blksize = 512;
++ argv[*arg_ptr][len - 1] = '\0';
++ break;
++
++ case 'c':
++ blksize = 1;
++ argv[*arg_ptr][len - 1] = '\0';
++ break;
++
++ case 'k':
++ blksize = 1024;
++ argv[*arg_ptr][len - 1] = '\0';
++ break;
++
++ case 'M': /* Megabytes */
++ blksize = 1024*1024;
++ argv[*arg_ptr][len - 1] = '\0';
++ break;
++
++ case 'G': /* Gigabytes */
++ blksize = 1024*1024*1024;
++ argv[*arg_ptr][len - 1] = '\0';
++ break;
++
++ case 'w':
++ blksize = 2;
++ argv[*arg_ptr][len - 1] = '\0';
++ break;
++
++ case '0':
++ case '1':
++ case '2':
++ case '3':
++ case '4':
++ case '5':
++ case '6':
++ case '7':
++ case '8':
++ case '9':
++ break;
++
++ default:
++ error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
++ }
++ /* TODO: accept fractional megabytes etc. ? */
++ if (!get_num (argv[*arg_ptr], &num, &c_type))
++ {
++ error(1, 0,
++ _("Invalid argument `%s%c' to -size"),
++ argv[*arg_ptr], (int)suffix);
++ return false;
++ }
++ our_pred = insert_primary (entry);
++ our_pred->args.size.kind = c_type;
++ our_pred->args.size.blocksize = blksize;
++ our_pred->args.size.size = num;
++ our_pred->need_stat = true;
++ our_pred->need_type = false;
++
++ if (COMP_GT == c_type)
++ our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9;
++ else if (COMP_LT == c_type)
++ our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1;
++ else
++ our_pred->est_success_rate = 0.01;
++
++ (*arg_ptr)++;
++ return true;
++}
++
++
++static boolean
++parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ /* General idea: stat the file, remember device and inode numbers.
++ * If a candidate file matches those, it's the same file.
++ */
++ struct predicate *our_pred;
++ struct stat st, fst;
++ int fd, openflags;
++
++ set_stat_placeholders(&st);
++ if (!collect_arg_stat_info(argv, arg_ptr, &st))
++ return false;
++
++ set_stat_placeholders(&fst);
++ /* POSIX systems are free to re-use the inode number of a deleted
++ * file. To ensure that we are not fooled by inode reuse, we hold
++ * the file open if we can. This would prevent the system reusing
++ * the file.
++ */
++ fd = -3; /* means, uninitialised */
++ openflags = O_RDONLY;
++
++ if (options.symlink_handling == SYMLINK_NEVER_DEREF)
++ {
++ if (options.open_nofollow_available)
++ {
++ assert (O_NOFOLLOW != 0);
++ openflags |= O_NOFOLLOW;
++ fd = -1; /* safe to open it. */
++ }
++ else
++ {
++ if (S_ISLNK(st.st_mode))
++ {
++ /* no way to ensure that a symlink will not be followed
++ * by open(2), so fall back on using lstat(). Accept
++ * the risk that the named file will be deleted and
++ * replaced with another having the same inode.
++ *
++ * Avoid opening the file.
++ */
++ fd = -2; /* Do not open it */
++ }
++ else
++ {
++ fd = -1;
++ /* Race condition here: the file might become a symlink here. */
++ }
++ }
++ }
++ else
++ {
++ /* We want to dereference the symlink anyway */
++ fd = -1; /* safe to open it without O_NOFOLLOW */
++ }
++
++ assert (fd != -3); /* check we made a decision */
++ if (fd == -1)
++ {
++ /* Race condition here. The file might become a
++ * symbolic link in between out call to stat and
++ * the call to open.
++ */
++ fd = open(argv[*arg_ptr], openflags);
++
++ if (fd >= 0)
++ {
++ /* We stat the file again here to prevent a race condition
++ * between the first stat and the call to open(2).
++ */
++ if (0 != fstat(fd, &fst))
++ {
++ fatal_file_error(argv[*arg_ptr]);
++ }
++ else
++ {
++ /* Worry about the race condition. If the file became a
++ * symlink after our first stat and before our call to
++ * open, fst may contain the stat information for the
++ * destination of the link, not the link itself.
++ */
++ if ((*options.xstat) (argv[*arg_ptr], &st))
++ fatal_file_error(argv[*arg_ptr]);
++
++ if ((options.symlink_handling == SYMLINK_NEVER_DEREF)
++ && (!options.open_nofollow_available))
++ {
++ if (S_ISLNK(st.st_mode))
++ {
++ /* We lost the race. Leave the data in st. The
++ * file descriptor points to the wrong thing.
++ */
++ close(fd);
++ fd = -1;
++ }
++ else
++ {
++ /* Several possibilities here:
++ * 1. There was no race
++ * 2. The file changed into a symlink after the stat and
++ * before the open, and then back into a non-symlink
++ * before the second stat.
++ *
++ * In case (1) there is no problem. In case (2),
++ * the stat() and fstat() calls will have returned
++ * different data. O_NOFOLLOW was not available,
++ * so the open() call may have followed a symlink
++ * even if the -P option is in effect.
++ */
++ if ((st.st_dev == fst.st_dev)
++ && (st.st_ino == fst.st_ino))
++ {
++ /* No race. No need to copy fst to st,
++ * since they should be identical (modulo
++ * differences in padding bytes).
++ */
++ }
++ else
++ {
++ /* We lost the race. Leave the data in st. The
++ * file descriptor points to the wrong thing.
++ */
++ close(fd);
++ fd = -1;
++ }
++ }
++ }
++ else
++ {
++ st = fst;
++ }
++ }
++ }
++ }
++
++ our_pred = insert_primary (entry);
++ our_pred->args.samefileid.ino = st.st_ino;
++ our_pred->args.samefileid.dev = st.st_dev;
++ our_pred->args.samefileid.fd = fd;
++ our_pred->need_type = false;
++ our_pred->need_stat = true;
++ our_pred->est_success_rate = 0.01f;
++ return true;
++}
++
++#if 0
++/* This function is commented out partly because support for it is
++ * uneven.
++ */
++static boolean
++parse_show_control_chars (const struct parser_table* entry,
++ char **argv,
++ int *arg_ptr)
++{
++ const char *arg;
++ const char *errmsg = _("The -show-control-chars option takes "
++ "a single argument which "
++ "must be 'literal' or 'safe'");
++
++ if ((argv == NULL) || (argv[*arg_ptr] == NULL))
++ {
++ error (1, errno, "%s", errmsg);
++ return false;
++ }
++ else
++ {
++ arg = argv[*arg_ptr];
++
++ if (0 == strcmp("literal", arg))
++ {
++ options.literal_control_chars = true;
++ }
++ else if (0 == strcmp("safe", arg))
++ {
++ options.literal_control_chars = false;
++ }
++ else
++ {
++ error (1, errno, "%s", errmsg);
++ return false;
++ }
++ (*arg_ptr)++; /* consume the argument. */
++ return true;
++ }
++}
++#endif
++
++
++static boolean
++parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++
++ (void) argv;
++ (void) arg_ptr;
++
++ our_pred = insert_primary (entry);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->est_success_rate = 1.0f;
++ return true;
++}
++
++static boolean
++parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ (void) entry;
++ return parse_true(get_noop(), argv, arg_ptr);
++}
++
++static boolean
++parse_accesscheck (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ (void) argv;
++ (void) arg_ptr;
++ our_pred = insert_primary (entry);
++ our_pred->need_stat = our_pred->need_type = false;
++ our_pred->side_effects = our_pred->no_default_print = false;
++ if (pred_is(our_pred, pred_executable))
++ our_pred->est_success_rate = 0.2;
++ else
++ our_pred->est_success_rate = 0.9;
++ return true;
++}
++
++static boolean
++parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_type (argv, arg_ptr, entry, pred_type);
++}
++
++static boolean
++parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *p = insert_num (argv, arg_ptr, entry);
++ if (p)
++ {
++ p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++static boolean
++parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ struct predicate *our_pred;
++ struct time_val tval;
++ const char *offset_str;
++ const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds";
++
++ if (collect_arg(argv, arg_ptr, &offset_str))
++ {
++ /* The timespec is actually a delta value, so we use an origin of 0. */
++ if (get_relative_timestamp(offset_str, &tval, 0, DAYSECS, errmsg))
++ {
++ our_pred = insert_primary (entry);
++ our_pred->args.reftime = tval;
++ our_pred->est_success_rate = estimate_file_age_success_rate(tval.ts.tv_sec / DAYSECS);
++ return true;
++ }
++ else
++ {
++ error(1, 0, _("Invalid argument %s to -used"), offset_str);
++ return false;
++ }
++ }
++ else
++ {
++ return false; /* missing argument */
++ }
++}
++
++static boolean
++parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ const char *username;
++
++ if (collect_arg(argv, arg_ptr, &username))
++ {
++ struct predicate *our_pred;
++ uid_t uid;
++ struct passwd *cur_pwd = getpwnam(username);
++ endpwent();
++ if (cur_pwd != NULL)
++ {
++ uid = cur_pwd->pw_uid;
++ }
++ else
++ {
++ int uid_len = strspn (username, "0123456789");
++ if (uid_len && (username[uid_len]==0))
++ uid = safe_atoi (username);
++ else
++ return false;
++ }
++ our_pred = insert_primary (entry);
++ our_pred->args.uid = uid;
++ our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2;
++ return true;
++ }
++ return false;
++}
++
++static boolean
++parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ int features = 0;
++ int flags;
++
++ (void) argv;
++ (void) arg_ptr;
++ (void) entry;
++
++ display_findutils_version("find");
++ printf (_("Features enabled: "));
++
++#if CACHE_IDS
++ printf("CACHE_IDS ");
++ ++features;
++#endif
++#if DEBUG
++ printf("DEBUG ");
++ ++features;
++#endif
++#if DEBUG_STAT
++ printf("DEBUG_STAT ");
++ ++features;
++#endif
++#if defined USE_STRUCT_DIRENT_D_TYPE && defined HAVE_STRUCT_DIRENT_D_TYPE
++ printf("D_TYPE ");
++ ++features;
++#endif
++#if defined O_NOFOLLOW
++ printf("O_NOFOLLOW(%s) ",
++ (options.open_nofollow_available ? "enabled" : "disabled"));
++ ++features;
++#endif
++#if defined LEAF_OPTIMISATION
++ printf("LEAF_OPTIMISATION ");
++ ++features;
++#endif
++
++ flags = 0;
++ if (is_fts_enabled(&flags))
++ {
++ int nflags = 0;
++ printf("FTS(");
++ ++features;
++
++ if (flags & FTS_CWDFD)
++ {
++ if (nflags)
++ {
++ printf(",");
++ }
++ printf("FTS_CWDFD");
++ ++nflags;
++ }
++ printf(") ");
++ }
++
++ printf("CBO(level=%d) ", (int)(options.optimisation_level));
++ ++features;
++
++ if (0 == features)
++ {
++ /* For the moment, leave this as English in case someone wants
++ to parse these strings. */
++ printf("none");
++ }
++ printf("\n");
++
++ exit (0);
++}
++
++static boolean
++parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ options.stay_on_filesystem = true;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ options.ignore_readdir_race = true;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ options.ignore_readdir_race = false;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ options.warnings = true;
++ return parse_noop(entry, argv, arg_ptr);
++}
++
++static boolean
++parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
++{
++ return insert_type (argv, arg_ptr, entry, pred_xtype);
++}
++
++static boolean
++insert_type (char **argv, int *arg_ptr,
++ const struct parser_table *entry,
++ PRED_FUNC which_pred)
++{
++ mode_t type_cell;
++ struct predicate *our_pred;
++ float rate = 0.5;
++ const char *typeletter;
++
++ if (collect_arg(argv, arg_ptr, &typeletter))
++ {
++ if (strlen(typeletter) != 1u)
++ {
++ error(1, 0, _("Arguments to -type should contain only one letter"));
++ return false;
++ }
++
++ switch (typeletter[0])
++ {
++ case 'b': /* block special */
++ type_cell = S_IFBLK;
++ rate = 0.01f;
++ break;
++ case 'c': /* character special */
++ type_cell = S_IFCHR;
++ rate = 0.01f;
++ break;
++ case 'd': /* directory */
++ type_cell = S_IFDIR;
++ rate = 0.4f;
++ break;
++ case 'f': /* regular file */
++ type_cell = S_IFREG;
++ rate = 0.95f;
++ break;
++#ifdef S_IFLNK
++ case 'l': /* symbolic link */
++ type_cell = S_IFLNK;
++ rate = 0.1f;
++ break;
++#endif
++#ifdef S_IFIFO
++ case 'p': /* pipe */
++ type_cell = S_IFIFO;
++ rate = 0.01f;
++ break;
++#endif
++#ifdef S_IFSOCK
++ case 's': /* socket */
++ type_cell = S_IFSOCK;
++ rate = 0.01f;
++ break;
++#endif
++#ifdef S_IFDOOR
++ case 'D': /* Solaris door */
++ type_cell = S_IFDOOR;
++ rate = 0.01f;
++ break;
++#endif
++ default: /* None of the above ... nuke 'em. */
++ error(1, 0, _("Unknown argument to -type: %c"), (*typeletter));
++ return false;
++ }
++ our_pred = insert_primary_withpred (entry, which_pred);
++ our_pred->est_success_rate = rate;
++
++ /* Figure out if we will need to stat the file, because if we don't
++ * need to follow symlinks, we can avoid a stat call by using
++ * struct dirent.d_type.
++ */
++ if (which_pred == pred_xtype)
++ {
++ our_pred->need_stat = true;
++ our_pred->need_type = false;
++ }
++ else
++ {
++ our_pred->need_stat = false; /* struct dirent is enough */
++ our_pred->need_type = true;
++ }
++ our_pred->args.type = type_cell;
++ return true;
++ }
++ return false;
++}
++
++
++/* Return true if the file accessed via FP is a terminal.
++ */
++static boolean
++stream_is_tty(FILE *fp)
++{
++ int fd = fileno(fp);
++ if (-1 == fd)
++ {
++ return false; /* not a valid stream */
++ }
++ else
++ {
++ return isatty(fd) ? true : false;
++ }
++
++}
++
++
++
++
++/* XXX: do we need to pass FUNC to this function? */
++static boolean
++insert_fprintf (struct format_val *vec,
++ const struct parser_table *entry, PRED_FUNC func,
++ const char *format_const)
++{
++ char *format = (char*)format_const; /* XXX: casting away constness */
++ register char *scan; /* Current address in scanning `format'. */
++ register char *scan2; /* Address inside of element being scanned. */
++ struct segment **segmentp; /* Address of current segment. */
++ struct predicate *our_pred;
++
++ our_pred = insert_primary_withpred (entry, func);
++ our_pred->side_effects = our_pred->no_default_print = true;
++ our_pred->args.printf_vec = *vec;
++ our_pred->need_type = false;
++ our_pred->need_stat = false;
++ our_pred->p_cost = NeedsNothing;
++
++ segmentp = &our_pred->args.printf_vec.segment;
++ *segmentp = NULL;
++
++ for (scan = format; *scan; scan++)
++ {
++ if (*scan == '\\')
++ {
++ scan2 = scan + 1;
++ if (*scan2 >= '0' && *scan2 <= '7')
++ {
++ register int n, i;
++
++ for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
++ i++, scan2++)
++ n = 8 * n + *scan2 - '0';
++ scan2--;
++ *scan = n;
++ }
++ else
++ {
++ switch (*scan2)
++ {
++ case 'a':
++ *scan = 7;
++ break;
++ case 'b':
++ *scan = '\b';
++ break;
++ case 'c':
++ make_segment (segmentp, format, scan - format,
++ KIND_STOP, 0, 0,
++ our_pred);
++ if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo))
++ our_pred->p_cost = NeedsStatInfo;
++ return true;
++ case 'f':
++ *scan = '\f';
++ break;
++ case 'n':
++ *scan = '\n';
++ break;
++ case 'r':
++ *scan = '\r';
++ break;
++ case 't':
++ *scan = '\t';
++ break;
++ case 'v':
++ *scan = '\v';
++ break;
++ case '\\':
++ /* *scan = '\\'; * it already is */
++ break;
++ default:
++ error (0, 0,
++ _("warning: unrecognized escape `\\%c'"), *scan2);
++ scan++;
++ continue;
++ }
++ }
++ segmentp = make_segment (segmentp, format, scan - format + 1,
++ KIND_PLAIN, 0, 0,
++ our_pred);
++ format = scan2 + 1; /* Move past the escape. */
++ scan = scan2; /* Incremented immediately by `for'. */
++ }
++ else if (*scan == '%')
++ {
++ if (scan[1] == 0)
++ {
++ /* Trailing %. We don't like those. */
++ error (1, 0, _("error: %s at end of format string"), scan);
++ }
++ else if (scan[1] == '%')
++ {
++ segmentp = make_segment (segmentp, format, scan - format + 1,
++ KIND_PLAIN, 0, 0,
++ our_pred);
++ scan++;
++ format = scan + 1;
++ continue;
++ }
++ /* Scan past flags, width and precision, to verify kind. */
++ for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
++ /* Do nothing. */ ;
++ while (ISDIGIT (*scan2))
++ scan2++;
++ if (*scan2 == '.')
++ for (scan2++; ISDIGIT (*scan2); scan2++)
++ /* Do nothing. */ ;
++ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2))
++ {
++ segmentp = make_segment (segmentp, format, scan2 - format,
++ KIND_FORMAT, *scan2, 0,
++ our_pred);
++ scan = scan2;
++ format = scan + 1;
++ }
++ else if (strchr ("ABCT", *scan2) && scan2[1])
++ {
++ segmentp = make_segment (segmentp, format, scan2 - format,
++ KIND_FORMAT, scan2[0], scan2[1],
++ our_pred);
++ scan = scan2 + 1;
++ format = scan + 1;
++ continue;
++ }
++ else
++ {
++ /* An unrecognized % escape. Print the char after the %. */
++ error (0, 0, _("warning: unrecognized format directive `%%%c'"),
++ *scan2);
++ segmentp = make_segment (segmentp, format, scan - format,
++ KIND_PLAIN, 0, 0,
++ our_pred);
++ format = scan + 1;
++ continue;
++ }
++ }
++ }
++
++ if (scan > format)
++ make_segment (segmentp, format, scan - format, KIND_PLAIN, 0, 0,
++ our_pred);
++ return true;
++}
++
++/* Create a new fprintf segment in *SEGMENT, with type KIND,
++ from the text in FORMAT, which has length LEN.
++ Return the address of the `next' pointer of the new segment. */
++
++static struct segment **
++make_segment (struct segment **segment,
++ char *format,
++ int len,
++ int kind,
++ char format_char,
++ char aux_format_char,
++ struct predicate *pred)
++{
++ enum EvaluationCost mycost = NeedsNothing;
++ char *fmt;
++
++ *segment = xmalloc (sizeof (struct segment));
++
++ (*segment)->segkind = kind;
++ (*segment)->format_char[0] = format_char;
++ (*segment)->format_char[1] = aux_format_char;
++ (*segment)->next = NULL;
++ (*segment)->text_len = len;
++
++ fmt = (*segment)->text = xmalloc (len + sizeof "d");
++ strncpy (fmt, format, len);
++ fmt += len;
++
++ switch (kind)
++ {
++ case KIND_PLAIN: /* Plain text string, no % conversion. */
++ case KIND_STOP: /* Terminate argument, no newline. */
++ assert (0 == format_char);
++ assert (0 == aux_format_char);
++ *fmt = '\0';
++ if (mycost > pred->p_cost)
++ pred->p_cost = NeedsNothing;
++ return &(*segment)->next;
++ break;
++ }
++
++ assert (kind == KIND_FORMAT);
++ switch (format_char)
++ {
++ case 'l': /* object of symlink */
++ pred->need_stat = true;
++ mycost = NeedsLinkName;
++ *fmt++ = 's';
++ break;
++
++ case 'y': /* file type */
++ pred->need_type = true;
++ mycost = NeedsType;
++ *fmt++ = 's';
++ break;
++
++ case 'a': /* atime in `ctime' format */
++ case 'A': /* atime in user-specified strftime format */
++ case 'B': /* birth time in user-specified strftime format */
++ case 'c': /* ctime in `ctime' format */
++ case 'C': /* ctime in user-specified strftime format */
++ case 'F': /* file system type */
++ case 'g': /* group name */
++ case 'i': /* inode number */
++ case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
++ case 's': /* size in bytes */
++ case 't': /* mtime in `ctime' format */
++ case 'T': /* mtime in user-specified strftime format */
++ case 'u': /* user name */
++ pred->need_stat = true;
++ mycost = NeedsStatInfo;
++ *fmt++ = 's';
++ break;
++
++ case 'S': /* sparseness */
++ pred->need_stat = true;
++ mycost = NeedsStatInfo;
++ *fmt++ = 'g';
++ break;
++
++ case 'Y': /* symlink pointed file type */
++ pred->need_stat = true;
++ mycost = NeedsType; /* true for amortised effect */
++ *fmt++ = 's';
++ break;
++
++ case 'f': /* basename of path */
++ case 'h': /* leading directories part of path */
++ case 'p': /* pathname */
++ case 'P': /* pathname with ARGV element stripped */
++ *fmt++ = 's';
++ break;
++
++ case 'H': /* ARGV element file was found under */
++ *fmt++ = 's';
++ break;
++
++ /* Numeric items that one might expect to honour
++ * #, 0, + flags but which do not.
++ */
++ case 'G': /* GID number */
++ case 'U': /* UID number */
++ case 'b': /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/
++ case 'D': /* Filesystem device on which the file exits */
++ case 'k': /* size in 1K blocks */
++ case 'n': /* number of links */
++ pred->need_stat = true;
++ mycost = NeedsStatInfo;
++ *fmt++ = 's';
++ break;
++
++ /* Numeric items that DO honour #, 0, + flags.
++ */
++ case 'd': /* depth in search tree (0 = ARGV element) */
++ *fmt++ = 'd';
++ break;
++
++ case 'm': /* mode as octal number (perms only) */
++ *fmt++ = 'o';
++ pred->need_stat = true;
++ mycost = NeedsStatInfo;
++ break;
++
++ case '{':
++ case '[':
++ case '(':
++ error (1, 0,
++ _("error: the format directive `%%%c' is reserved for future use"),
++ (int)kind);
++ /*NOTREACHED*/
++ break;
++ }
++ *fmt = '\0';
++
++ if (mycost > pred->p_cost)
++ pred->p_cost = mycost;
++ return &(*segment)->next;
++}
++
++static void
++check_path_safety(const char *action, char **argv)
++{
++ char *s;
++ const char *path = getenv("PATH");
++ if (NULL == path)
++ {
++ /* $PATH is not set. Assume the OS default is safe.
++ * That may not be true on Windows, but I'm not aware
++ * of a way to get Windows to avoid searching the
++ * current directory anyway.
++ */
++ return;
++ }
++
++ (void)argv;
++
++ s = next_element(path, 1);
++ while ((s = next_element ((char *) NULL, 1)) != NULL)
++ {
++ if (0 == strcmp(s, "."))
++ {
++ error(1, 0, _("The current directory is included in the PATH "
++ "environment variable, which is insecure in "
++ "combination with the %s action of find. "
++ "Please remove the current directory from your "
++ "$PATH (that is, remove \".\" or leading or trailing "
++ "colons)"),
++ action);
++ }
++ else if ('/' != s[0])
++ {
++ /* Relative paths are also dangerous in $PATH. */
++ error(1, 0, _("The relative path %1$s is included in the PATH "
++ "environment variable, which is insecure in "
++ "combination with the %2$s action of find. "
++ "Please remove that entry from $PATH"),
++ safely_quote_err_filename(0, s),
++ action);
++ }
++ }
++}
++
++
++/* handles both exec and ok predicate */
++static boolean
++new_insert_exec_ok (const char *action,
++ const struct parser_table *entry,
++ int dirfd,
++ char **argv,
++ int *arg_ptr)
++{
++ int start, end; /* Indexes in ARGV of start & end of cmd. */
++ int i; /* Index into cmd args */
++ int saw_braces; /* True if previous arg was '{}'. */
++ boolean allow_plus; /* True if + is a valid terminator */
++ int brace_count; /* Number of instances of {}. */
++ PRED_FUNC func = entry->pred_func;
++ enum BC_INIT_STATUS bcstatus;
++
++ struct predicate *our_pred;
++ struct exec_val *execp; /* Pointer for efficiency. */
++
++ if ((argv == NULL) || (argv[*arg_ptr] == NULL))
++ return false;
++
++ our_pred = insert_primary_withpred (entry, func);
++ our_pred->side_effects = our_pred->no_default_print = true;
++ our_pred->need_type = our_pred->need_stat = false;
++
++ execp = &our_pred->args.exec_vec;
++
++ if ((func != pred_okdir) && (func != pred_ok))
++ {
++ allow_plus = true;
++ execp->close_stdin = false;
++ }
++ else
++ {
++ allow_plus = false;
++ /* If find reads stdin (i.e. for -ok and similar), close stdin
++ * in the child to prevent some script from consiming the output
++ * intended for find.
++ */
++ execp->close_stdin = true;
++ }
++
++
++ if ((func == pred_execdir) || (func == pred_okdir))
++ {
++ options.ignore_readdir_race = false;
++ check_path_safety(action, argv);
++ execp->use_current_dir = true;
++ }
++ else
++ {
++ execp->use_current_dir = false;
++ }
++
++ our_pred->args.exec_vec.multiple = 0;
++
++ /* Count the number of args with path replacements, up until the ';'.
++ * Also figure out if the command is terminated by ";" or by "+".
++ */
++ start = *arg_ptr;
++ for (end = start, saw_braces=0, brace_count=0;
++ (argv[end] != NULL)
++ && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
++ end++)
++ {
++ /* For -exec and -execdir, "{} +" can terminate the command. */
++ if ( allow_plus
++ && argv[end][0] == '+' && argv[end][1] == 0
++ && saw_braces)
++ {
++ our_pred->args.exec_vec.multiple = 1;
++ break;
++ }
++
++ saw_braces = 0;
++ if (mbsstr (argv[end], "{}"))
++ {
++ saw_braces = 1;
++ ++brace_count;
++
++ if (0 == end && (func == pred_execdir || func == pred_okdir))
++ {
++ /* The POSIX standard says that {} replacement should
++ * occur even in the utility name. This is insecure
++ * since it means we will be executing a command whose
++ * name is chosen according to whatever find finds in
++ * the file system. That can be influenced by an
++ * attacker. Hence for -execdir and -okdir this is not
++ * allowed. We can specify this as those options are
++ * not defined by POSIX.
++ */
++ error(1, 0, _("You may not use {} within the utility name for "
++ "-execdir and -okdir, because this is a potential "
++ "security problem."));
++ }
++ }
++ }
++
++ /* Fail if no command given or no semicolon found. */
++ if ((end == start) || (argv[end] == NULL))
++ {
++ *arg_ptr = end;
++ free(our_pred);
++ return false;
++ }
++
++ if (our_pred->args.exec_vec.multiple && brace_count > 1)
++ {
++
++ const char *suffix;
++ if (func == pred_execdir)
++ suffix = "dir";
++ else
++ suffix = "";
++
++ error(1, 0,
++ _("Only one instance of {} is supported with -exec%s ... +"),
++ suffix);
++ }
++
++ /* We use a switch statement here so that the compiler warns us when
++ * we forget to handle a newly invented enum value.
++ *
++ * Like xargs, we allow 2KiB of headroom for the launched utility to
++ * export its own environment variables before calling something
++ * else.
++ */
++ bcstatus = bc_init_controlinfo(&execp->ctl, 2048u);
++ switch (bcstatus)
++ {
++ case BC_INIT_ENV_TOO_BIG:
++ case BC_INIT_CANNOT_ACCOMODATE_HEADROOM:
++ error(1, 0,
++ _("The environment is too large for exec()."));
++ break;
++ case BC_INIT_OK:
++ /* Good news. Carry on. */
++ break;
++ }
++ bc_use_sensible_arg_max(&execp->ctl);
++
++
++ execp->ctl.exec_callback = launch;
++
++ if (our_pred->args.exec_vec.multiple)
++ {
++ /* "+" terminator, so we can just append our arguments after the
++ * command and initial arguments.
++ */
++ execp->replace_vec = NULL;
++ execp->ctl.replace_pat = NULL;
++ execp->ctl.rplen = 0;
++ execp->ctl.lines_per_exec = 0; /* no limit */
++ execp->ctl.args_per_exec = 0; /* no limit */
++
++ /* remember how many arguments there are */
++ execp->ctl.initial_argc = (end-start) - 1;
++
++ /* execp->state = xmalloc(sizeof struct buildcmd_state); */
++ bc_init_state(&execp->ctl, &execp->state, execp);
++
++ /* Gather the initial arguments. Skip the {}. */
++ for (i=start; i<end-1; ++i)
++ {
++ bc_push_arg(&execp->ctl, &execp->state,
++ argv[i], strlen(argv[i])+1,
++ NULL, 0,
++ 1);
++ }
++ }
++ else
++ {
++ /* Semicolon terminator - more than one {} is supported, so we
++ * have to do brace-replacement.
++ */
++ execp->num_args = end - start;
++
++ execp->ctl.replace_pat = "{}";
++ execp->ctl.rplen = strlen(execp->ctl.replace_pat);
++ execp->ctl.lines_per_exec = 0; /* no limit */
++ execp->ctl.args_per_exec = 0; /* no limit */
++ execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
++
++
++ /* execp->state = xmalloc(sizeof(*(execp->state))); */
++ bc_init_state(&execp->ctl, &execp->state, execp);
++
++ /* Remember the (pre-replacement) arguments for later. */
++ for (i=0; i<execp->num_args; ++i)
++ {
++ execp->replace_vec[i] = argv[i+start];
++ }
++ }
++
++ if (argv[end] == NULL)
++ *arg_ptr = end;
++ else
++ *arg_ptr = end + 1;
++
++ return true;
++}
++
++
++
++static boolean
++insert_exec_ok (const char *action,
++ const struct parser_table *entry,
++ int dirfd,
++ char **argv,
++ int *arg_ptr)
++{
++ return new_insert_exec_ok(action, entry, dirfd, argv, arg_ptr);
++}
++
++
++
++/* Get a timestamp and comparison type.
++
++ STR is the ASCII representation.
++ Set *NUM_DAYS to the number of days/minutes/whatever, taken as being
++ relative to ORIGIN (usually the current moment or midnight).
++ Thus the sense of the comparison type appears to be reversed.
++ Set *COMP_TYPE to the kind of comparison that is requested.
++ Issue OVERFLOWMESSAGE if overflow occurs.
++ Return true if all okay, false if input error.
++
++ Used by -atime, -ctime and -mtime (parsers) to
++ get the appropriate information for a time predicate processor. */
++
++static boolean
++get_relative_timestamp (const char *str,
++ struct time_val *result,
++ time_t origin,
++ double sec_per_unit,
++ const char *overflowmessage)
++{
++ uintmax_t checkval;
++ double offset, seconds, f;
++
++ if (get_comp_type(&str, &result->kind))
++ {
++ /* Invert the sense of the comparison */
++ switch (result->kind)
++ {
++ case COMP_LT: result->kind = COMP_GT; break;
++ case COMP_GT: result->kind = COMP_LT; break;
++ default: break;
++ }
++
++ /* Convert the ASCII number into floating-point. */
++ if (xstrtod(str, NULL, &offset, strtod))
++ {
++ /* Separate the floating point number the user specified
++ * (which is a number of days, or minutes, etc) into an
++ * integral number of seconds (SECONDS) and a fraction (F).
++ */
++ f = modf(offset * sec_per_unit, &seconds);
++
++ result->ts.tv_sec = origin - seconds;
++ result->ts.tv_nsec = fabs(f * 1e9);
++
++ /* Check for overflow. */
++ checkval = (uintmax_t)origin - seconds;
++ if (checkval != result->ts.tv_sec)
++ {
++ /* an overflow has occurred. */
++ error (1, 0, overflowmessage, str);
++ }
++ return true;
++ }
++ else
++ {
++ /* Conversion from ASCII to double failed. */
++ return false;
++ }
++ }
++ else
++ {
++ return false;
++ }
++}
++
++/* Insert a time predicate based on the information in ENTRY.
++ ARGV is a pointer to the argument array.
++ ARG_PTR is a pointer to an index into the array, incremented if
++ all went well.
++
++ Return true if input is valid, false if not.
++
++ A new predicate node is assigned, along with an argument node
++ obtained with malloc.
++
++ Used by -atime, -ctime, and -mtime parsers. */
++
++static boolean
++parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
++{
++ struct predicate *our_pred;
++ struct time_val tval;
++ enum comparison_type comp;
++ const char *timearg, *orig_timearg;
++ const char *errmsg = "arithmetic overflow while converting %s "
++ "days to a number of seconds";
++ time_t origin;
++
++ if (!collect_arg(argv, arg_ptr, &timearg))
++ return false;
++ orig_timearg = timearg;
++
++ /* Decide the origin by previewing the comparison type. */
++ origin = options.cur_day_start;
++
++ if (get_comp_type(&timearg, &comp))
++ {
++ /* Remember, we invert the sense of the comparison, so this tests
++ * against COMP_LT instead of COMP_GT...
++ */
++ if (COMP_LT == comp)
++ {
++ uintmax_t expected = origin + (DAYSECS-1);
++ origin += (DAYSECS-1);
++ if (origin != expected)
++ {
++ error(1, 0,
++ _("arithmetic overflow when trying to calculate the end of today"));
++ }
++ }
++ }
++ /* We discard the value of comp here, as get_relative_timestamp
++ * will set tval.kind. For that to work, we have to restore
++ * timearg so that it points to the +/- prefix, if any. get_comp_type()
++ * will have advanced timearg, so we restore it.
++ */
++ timearg = orig_timearg;
++
++ if (!get_relative_timestamp(timearg, &tval, origin, DAYSECS, errmsg))
++ return false;
++
++ our_pred = insert_primary (entry);
++ our_pred->args.reftime = tval;
++ our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec);
++
++ if (options.debug_options & DebugExpressionTree)
++ {
++ time_t t;
++
++ fprintf (stderr, "inserting %s\n", our_pred->p_name);
++ fprintf (stderr, " type: %s %s ",
++ (tval.kind == COMP_GT) ? "gt" :
++ ((tval.kind == COMP_LT) ? "lt" : ((tval.kind == COMP_EQ) ? "eq" : "?")),
++ (tval.kind == COMP_GT) ? " >" :
++ ((tval.kind == COMP_LT) ? " <" : ((tval.kind == COMP_EQ) ? ">=" : " ?")));
++ t = our_pred->args.reftime.ts.tv_sec;
++ fprintf (stderr, "%ju %s",
++ (uintmax_t) our_pred->args.reftime.ts.tv_sec,
++ ctime (&t));
++ if (tval.kind == COMP_EQ)
++ {
++ t = our_pred->args.reftime.ts.tv_sec + DAYSECS;
++ fprintf (stderr, " < %ju %s",
++ (uintmax_t) t, ctime (&t));
++ }
++ }
++
++ return true;
++}
++
++/* Get the comparison type prefix (if any) from a number argument.
++ The prefix is at *STR.
++ Set *COMP_TYPE to the kind of comparison that is requested.
++ Advance *STR beyond any initial comparison prefix.
++
++ Return true if all okay, false if input error. */
++static boolean
++get_comp_type(const char **str, enum comparison_type *comp_type)
++{
++ switch (**str)
++ {
++ case '+':
++ *comp_type = COMP_GT;
++ (*str)++;
++ break;
++ case '-':
++ *comp_type = COMP_LT;
++ (*str)++;
++ break;
++ default:
++ *comp_type = COMP_EQ;
++ break;
++ }
++ return true;
++}
++
++
++
++
++
++/* Get a number with comparison information.
++ The sense of the comparison information is 'normal'; that is,
++ '+' looks for a count > than the number and '-' less than.
++
++ STR is the ASCII representation of the number.
++ Set *NUM to the number.
++ Set *COMP_TYPE to the kind of comparison that is requested.
++
++ Return true if all okay, false if input error. */
++
++static boolean
++get_num (const char *str,
++ uintmax_t *num,
++ enum comparison_type *comp_type)
++{
++ char *pend;
++
++ if (str == NULL)
++ return false;
++
++ /* Figure out the comparison type if the caller accepts one. */
++ if (comp_type)
++ {
++ if (!get_comp_type(&str, comp_type))
++ return false;
++ }
++
++ return xstrtoumax (str, &pend, 10, num, "") == LONGINT_OK;
++}
++
++/* Insert a number predicate.
++ ARGV is a pointer to the argument array.
++ *ARG_PTR is an index into ARGV, incremented if all went well.
++ *PRED is the predicate processor to insert.
++
++ Return true if input is valid, false if error.
++
++ A new predicate node is assigned, along with an argument node
++ obtained with malloc.
++
++ Used by -inum and -links parsers. */
++
++static struct predicate *
++insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
++{
++ const char *numstr;
++
++ if (collect_arg(argv, arg_ptr, &numstr))
++ {
++ uintmax_t num;
++ enum comparison_type c_type;
++
++ if (get_num (numstr, &num, &c_type))
++ {
++ struct predicate *our_pred = insert_primary (entry);
++ our_pred->args.numinfo.kind = c_type;
++ our_pred->args.numinfo.l_val = num;
++
++ if (options.debug_options & DebugExpressionTree)
++ {
++ fprintf (stderr, "inserting %s\n", our_pred->p_name);
++ fprintf (stderr, " type: %s %s ",
++ (c_type == COMP_GT) ? "gt" :
++ ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
++ (c_type == COMP_GT) ? " >" :
++ ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
++ fprintf (stderr, "%ju\n", our_pred->args.numinfo.l_val);
++ }
++ return our_pred;
++ }
++ }
++ return NULL;
++}
++
++static void
++open_output_file (const char *path, struct format_val *p)
++{
++ p->segment = NULL;
++ p->quote_opts = clone_quoting_options (NULL);
++
++ if (!strcmp (path, "/dev/stderr"))
++ {
++ p->stream = stderr;
++ p->filename = _("standard error");
++ }
++ else if (!strcmp (path, "/dev/stdout"))
++ {
++ p->stream = stdout;
++ p->filename = _("standard output");
++ }
++ else
++ {
++ p->stream = fopen_safer (path, "w");
++ p->filename = path;
++
++ if (p->stream == NULL)
++ {
++ fatal_file_error(path);
++ }
++ }
++
++ p->dest_is_tty = stream_is_tty(p->stream);
++}
++
++static void
++open_stdout (struct format_val *p)
++{
++ open_output_file("/dev/stdout", p);
++}
+diff -purN findutils-4.3.12.orig/find/pred.c findutils-4.3.12/find/pred.c
+--- findutils-4.3.12.orig/find/pred.c 2007-12-19 16:12:34.000000000 -0500
++++ findutils-4.3.12/find/pred.c 2008-01-30 08:46:05.758843847 -0500
+@@ -47,6 +47,14 @@
+ #include "error.h"
+ #include "verify.h"
+
++#ifdef WITH_SELINUX
++#include <selinux/selinux.h>
++#endif /*WITH_SELINUX*/
++
++#ifndef FNM_CASEFOLD
++#define FNM_CASEFOLD (1<<4)
++#endif /*FNM_CASEFOLD*/
++
+ #if ENABLE_NLS
+ # include <libintl.h>
+ # define _(Text) gettext (Text)
+@@ -229,6 +237,9 @@ struct pred_assoc pred_table[] =
+ {pred_user, "user "},
+ {pred_writable, "writable "},
+ {pred_xtype, "xtype "},
++#ifdef WITH_SELINUX
++ {pred_scontext, "context"},
++#endif /*WITH_SELINUX*/
+ {0, "none "}
+ };
+ #endif
+@@ -1045,6 +1056,26 @@ do_fprintf(struct format_val *dest,
+ mode_to_filetype(stat_buf->st_mode & S_IFMT));
+ }
+ break;
++#ifdef WITH_SELINUX
++ case 'Z': /* SELinux security context */
++ {
++ security_context_t scontext;
++ int rv;
++ rv = (*options.x_getfilecon)(state.rel_pathname, &scontext);
++
++ if ( rv < 0 ) {
++ fprintf(stderr, "getfileconf(%s): %s",
++ pathname, strerror(errno));
++ fflush(stderr);
++ }
++ else {
++ segment->text[segment->text_len] = 's';
++ checked_fprintf (dest, segment->text, scontext);
++ freecon(scontext);
++ }
++ }
++ break ;
++#endif /* WITH_SELINUX */
+ }
+ /* end of KIND_FORMAT case */
+ break;
+@@ -1838,6 +1869,31 @@ pred_xtype (const char *pathname, struct
+ */
+ return (pred_type (pathname, &sbuf, pred_ptr));
+ }
++
++
++#ifdef WITH_SELINUX
++
++boolean
++pred_scontext (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ int rv;
++ security_context_t scontext;
++
++ rv = (* options.x_getfilecon)(state.rel_pathname, &scontext);
++
++ if ( rv < 0 ) {
++ (void) fprintf(stderr, "getfilecon(%s): %s\n", pathname, strerror(errno));
++ (void) fflush(stderr);
++ return ( false );
++ }
++
++ rv = (fnmatch(pred_ptr->args.scontext, scontext,0)==0);
++ freecon(scontext);
++ return rv;
++}
++
++#endif /*WITH_SELINUX*/
++
+
+ /* 1) fork to get a child; parent remembers the child pid
+ 2) child execs the command requested
+diff -purN findutils-4.3.12.orig/find/pred.c.orig findutils-4.3.12/find/pred.c.orig
+--- findutils-4.3.12.orig/find/pred.c.orig 1969-12-31 19:00:00.000000000 -0500
++++ findutils-4.3.12/find/pred.c.orig 2007-12-19 16:12:34.000000000 -0500
+@@ -0,0 +1,2405 @@
++/* pred.c -- execute the expression tree.
++ Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003,
++ 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <config.h>
++#include "defs.h"
++
++#include <fnmatch.h>
++#include <signal.h>
++#include <math.h>
++#include <pwd.h>
++#include <grp.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <errno.h>
++#include <assert.h>
++#include <stdarg.h>
++#include <fcntl.h>
++#include <locale.h>
++#include <openat.h>
++#include "xalloc.h"
++#include "dirname.h"
++#include "human.h"
++#include "modetype.h"
++#include "filemode.h"
++#include "wait.h"
++#include "printquoted.h"
++#include "buildcmd.h"
++#include "yesno.h"
++#include "listfile.h"
++#include "stat-time.h"
++#include "dircallback.h"
++#include "error.h"
++#include "verify.h"
++
++#if ENABLE_NLS
++# include <libintl.h>
++# define _(Text) gettext (Text)
++#else
++# define _(Text) Text
++#endif
++#ifdef gettext_noop
++# define N_(String) gettext_noop (String)
++#else
++/* See locate.c for explanation as to why not use (String) */
++# define N_(String) String
++#endif
++
++#if !defined(SIGCHLD) && defined(SIGCLD)
++#define SIGCHLD SIGCLD
++#endif
++
++
++
++#if HAVE_DIRENT_H
++# include <dirent.h>
++# define NAMLEN(dirent) strlen((dirent)->d_name)
++#else
++# define dirent direct
++# define NAMLEN(dirent) (dirent)->d_namlen
++# if HAVE_SYS_NDIR_H
++# include <sys/ndir.h>
++# endif
++# if HAVE_SYS_DIR_H
++# include <sys/dir.h>
++# endif
++# if HAVE_NDIR_H
++# include <ndir.h>
++# endif
++#endif
++
++#ifdef CLOSEDIR_VOID
++/* Fake a return value. */
++#define CLOSEDIR(d) (closedir (d), 0)
++#else
++#define CLOSEDIR(d) closedir (d)
++#endif
++
++
++
++
++/* Get or fake the disk device blocksize.
++ Usually defined by sys/param.h (if at all). */
++#ifndef DEV_BSIZE
++# ifdef BSIZE
++# define DEV_BSIZE BSIZE
++# else /* !BSIZE */
++# define DEV_BSIZE 4096
++# endif /* !BSIZE */
++#endif /* !DEV_BSIZE */
++
++/* Extract or fake data from a `struct stat'.
++ ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
++ ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
++ ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
++#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
++# define ST_BLKSIZE(statbuf) DEV_BSIZE
++# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */
++# define ST_NBLOCKS(statbuf) \
++ (S_ISREG ((statbuf).st_mode) \
++ || S_ISDIR ((statbuf).st_mode) \
++ ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
++# else /* !_POSIX_SOURCE && BSIZE */
++# define ST_NBLOCKS(statbuf) \
++ (S_ISREG ((statbuf).st_mode) \
++ || S_ISDIR ((statbuf).st_mode) \
++ ? st_blocks ((statbuf).st_size) : 0)
++# endif /* !_POSIX_SOURCE && BSIZE */
++#else /* HAVE_STRUCT_STAT_ST_BLOCKS */
++/* Some systems, like Sequents, return st_blksize of 0 on pipes. */
++# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
++ ? (statbuf).st_blksize : DEV_BSIZE)
++# if defined hpux || defined __hpux__ || defined __hpux
++/* HP-UX counts st_blocks in 1024-byte units.
++ This loses when mixing HP-UX and BSD file systems with NFS. */
++# define ST_NBLOCKSIZE 1024
++# else /* !hpux */
++# if defined _AIX && defined _I386
++/* AIX PS/2 counts st_blocks in 4K units. */
++# define ST_NBLOCKSIZE (4 * 1024)
++# else /* not AIX PS/2 */
++# if defined _CRAY
++# define ST_NBLOCKS(statbuf) \
++ (S_ISREG ((statbuf).st_mode) \
++ || S_ISDIR ((statbuf).st_mode) \
++ ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
++# endif /* _CRAY */
++# endif /* not AIX PS/2 */
++# endif /* !hpux */
++#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
++
++#ifndef ST_NBLOCKS
++# define ST_NBLOCKS(statbuf) \
++ (S_ISREG ((statbuf).st_mode) \
++ || S_ISDIR ((statbuf).st_mode) \
++ ? (statbuf).st_blocks : 0)
++#endif
++
++#ifndef ST_NBLOCKSIZE
++# define ST_NBLOCKSIZE 512
++#endif
++
++
++#undef MAX
++#define MAX(a, b) ((a) > (b) ? (a) : (b))
++
++static boolean match_lname PARAMS((const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case));
++
++static char *format_date PARAMS((struct timespec ts, int kind));
++static char *ctime_format PARAMS((struct timespec ts));
++
++#ifdef DEBUG
++struct pred_assoc
++{
++ PRED_FUNC pred_func;
++ char *pred_name;
++};
++
++struct pred_assoc pred_table[] =
++{
++ {pred_amin, "amin "},
++ {pred_and, "and "},
++ {pred_anewer, "anewer "},
++ {pred_atime, "atime "},
++ {pred_closeparen, ") "},
++ {pred_cmin, "cmin "},
++ {pred_cnewer, "cnewer "},
++ {pred_comma, ", "},
++ {pred_ctime, "ctime "},
++ {pred_delete, "delete "},
++ {pred_empty, "empty "},
++ {pred_exec, "exec "},
++ {pred_execdir, "execdir "},
++ {pred_executable, "executable "},
++ {pred_false, "false "},
++ {pred_fprint, "fprint "},
++ {pred_fprint0, "fprint0 "},
++ {pred_fprintf, "fprintf "},
++ {pred_fstype, "fstype "},
++ {pred_gid, "gid "},
++ {pred_group, "group "},
++ {pred_ilname, "ilname "},
++ {pred_iname, "iname "},
++ {pred_inum, "inum "},
++ {pred_ipath, "ipath "},
++ {pred_links, "links "},
++ {pred_lname, "lname "},
++ {pred_ls, "ls "},
++ {pred_mmin, "mmin "},
++ {pred_mtime, "mtime "},
++ {pred_name, "name "},
++ {pred_negate, "not "},
++ {pred_newer, "newer "},
++ {pred_newerXY, "newerXY "},
++ {pred_nogroup, "nogroup "},
++ {pred_nouser, "nouser "},
++ {pred_ok, "ok "},
++ {pred_okdir, "okdir "},
++ {pred_openparen, "( "},
++ {pred_or, "or "},
++ {pred_path, "path "},
++ {pred_perm, "perm "},
++ {pred_print, "print "},
++ {pred_print0, "print0 "},
++ {pred_prune, "prune "},
++ {pred_quit, "quit "},
++ {pred_readable, "readable "},
++ {pred_regex, "regex "},
++ {pred_samefile,"samefile "},
++ {pred_size, "size "},
++ {pred_true, "true "},
++ {pred_type, "type "},
++ {pred_uid, "uid "},
++ {pred_used, "used "},
++ {pred_user, "user "},
++ {pred_writable, "writable "},
++ {pred_xtype, "xtype "},
++ {0, "none "}
++};
++#endif
++
++/* Returns ts1 - ts2 */
++static double ts_difference(struct timespec ts1,
++ struct timespec ts2)
++{
++ double d = difftime(ts1.tv_sec, ts2.tv_sec)
++ + (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec));
++ return d;
++}
++
++
++static int
++compare_ts(struct timespec ts1,
++ struct timespec ts2)
++{
++ if ((ts1.tv_sec == ts2.tv_sec) &&
++ (ts1.tv_nsec == ts2.tv_nsec))
++ {
++ return 0;
++ }
++ else
++ {
++ double diff = ts_difference(ts1, ts2);
++ return diff < 0.0 ? -1 : +1;
++ }
++}
++
++/* Predicate processing routines.
++
++ PATHNAME is the full pathname of the file being checked.
++ *STAT_BUF contains information about PATHNAME.
++ *PRED_PTR contains information for applying the predicate.
++
++ Return true if the file passes this predicate, false if not. */
++
++
++/* pred_timewindow
++ *
++ * Returns true if THE_TIME is
++ * COMP_GT: after the specified time
++ * COMP_LT: before the specified time
++ * COMP_EQ: less than WINDOW seconds after the specified time.
++ */
++static boolean
++pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window)
++{
++ switch (pred_ptr->args.reftime.kind)
++ {
++ case COMP_GT:
++ return compare_ts(ts, pred_ptr->args.reftime.ts) > 0;
++
++ case COMP_LT:
++ return compare_ts(ts, pred_ptr->args.reftime.ts) < 0;
++
++ case COMP_EQ:
++ {
++ double delta = ts_difference(ts, pred_ptr->args.reftime.ts);
++ return (delta >= 0.0 && delta < window);
++ }
++ }
++ assert (0);
++ abort ();
++}
++
++
++boolean
++pred_amin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60);
++}
++
++boolean
++pred_and (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ if (pred_ptr->pred_left == NULL
++ || apply_predicate(pathname, stat_buf, pred_ptr->pred_left))
++ {
++ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
++ }
++ else
++ return false;
++}
++
++boolean
++pred_anewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ assert (COMP_GT == pred_ptr->args.reftime.kind);
++ return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0;
++}
++
++boolean
++pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS);
++}
++
++boolean
++pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ (void) &stat_buf;
++ (void) &pred_ptr;
++
++ return true;
++}
++
++boolean
++pred_cmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60);
++}
++
++boolean
++pred_cnewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ assert (COMP_GT == pred_ptr->args.reftime.kind);
++ return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0;
++}
++
++boolean
++pred_comma (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ if (pred_ptr->pred_left != NULL)
++ {
++ apply_predicate(pathname, stat_buf,pred_ptr->pred_left);
++ }
++ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
++}
++
++boolean
++pred_ctime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS);
++}
++
++static boolean
++perform_delete(int flags)
++{
++ return 0 == unlinkat(state.cwd_dir_fd, state.rel_pathname, flags);
++}
++
++
++boolean
++pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pred_ptr;
++ (void) stat_buf;
++ if (strcmp (state.rel_pathname, "."))
++ {
++ int flags=0;
++ if (state.have_stat && S_ISDIR(stat_buf->st_mode))
++ flags |= AT_REMOVEDIR;
++ if (perform_delete(flags))
++ {
++ return true;
++ }
++ else
++ {
++ if (EISDIR == errno)
++ {
++ if ((flags & AT_REMOVEDIR) == 0)
++ {
++ /* unlink() operation failed because we should have done rmdir(). */
++ flags |= AT_REMOVEDIR;
++ if (perform_delete(flags))
++ return true;
++ }
++ }
++ }
++ error (0, errno, _("cannot delete %s"
++ /* TRANSLATORS: the argument is either a
++ * file or a directory, but we do not know which.
++ * Mail bug-findutils@gnu.org if you cannot correctly
++ * translate the string without knowing.
++ */),
++ safely_quote_err_filename(0, pathname));
++ /* Previously I had believed that having the -delete action
++ * return false provided the user with control over whether an
++ * error message is issued. While this is true, the policy of
++ * not affecting the exit status is contrary to the POSIX
++ * requirement that diagnostic messages are accompanied by a
++ * nonzero exit status. While -delete is not a POSIX option and
++ * we can therefore opt not to follow POSIX in this case, that
++ * seems somewhat arbitrary and confusing. So, as of
++ * findutils-4.3.11, we also set the exit status in this case.
++ */
++ state.exit_status = 1;
++ return false;
++ }
++ else
++ {
++ /* nothing to do. */
++ return true;
++ }
++}
++
++boolean
++pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) pred_ptr;
++
++ if (S_ISDIR (stat_buf->st_mode))
++ {
++ int fd;
++ DIR *d;
++ struct dirent *dp;
++ boolean empty = true;
++
++ errno = 0;
++ if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY
++#if defined O_LARGEFILE
++ |O_LARGEFILE
++#endif
++ )) < 0)
++ {
++ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ return false;
++ }
++ d = fdopendir (fd);
++ if (d == NULL)
++ {
++ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ return false;
++ }
++ for (dp = readdir (d); dp; dp = readdir (d))
++ {
++ if (dp->d_name[0] != '.'
++ || (dp->d_name[1] != '\0'
++ && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
++ {
++ empty = false;
++ break;
++ }
++ }
++ if (CLOSEDIR (d))
++ {
++ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ return false;
++ }
++ return (empty);
++ }
++ else if (S_ISREG (stat_buf->st_mode))
++ return (stat_buf->st_size == 0);
++ else
++ return (false);
++}
++
++static boolean
++new_impl_pred_exec (int dirfd, const char *pathname,
++ struct stat *stat_buf,
++ struct predicate *pred_ptr,
++ const char *prefix, size_t pfxlen)
++{
++ struct exec_val *execp = &pred_ptr->args.exec_vec;
++ size_t len = strlen(pathname);
++
++ (void) stat_buf;
++ execp->dirfd = dirfd;
++ if (execp->multiple)
++ {
++ /* Push the argument onto the current list.
++ * The command may or may not be run at this point,
++ * depending on the command line length limits.
++ */
++ bc_push_arg(&execp->ctl,
++ &execp->state,
++ pathname, len+1,
++ prefix, pfxlen,
++ 0);
++
++ /* remember that there are pending execdirs. */
++ state.execdirs_outstanding = true;
++
++ /* POSIX: If the primary expression is punctuated by a plus
++ * sign, the primary shall always evaluate as true
++ */
++ return true;
++ }
++ else
++ {
++ int i;
++
++ for (i=0; i<execp->num_args; ++i)
++ {
++ bc_do_insert(&execp->ctl,
++ &execp->state,
++ execp->replace_vec[i],
++ strlen(execp->replace_vec[i]),
++ prefix, pfxlen,
++ pathname, len,
++ 0);
++ }
++
++ /* Actually invoke the command. */
++ return execp->ctl.exec_callback(&execp->ctl,
++ &execp->state);
++ }
++}
++
++
++boolean
++pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ return new_impl_pred_exec(get_start_dirfd(),
++ pathname, stat_buf, pred_ptr, NULL, 0);
++}
++
++boolean
++pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./";
++ (void) &pathname;
++ return new_impl_pred_exec (get_current_dirfd(),
++ state.rel_pathname, stat_buf, pred_ptr,
++ prefix, (prefix ? 2 : 0));
++}
++
++boolean
++pred_false (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ (void) &stat_buf;
++ (void) &pred_ptr;
++
++
++ return (false);
++}
++
++boolean
++pred_fls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ FILE * stream = pred_ptr->args.printf_vec.stream;
++ list_file (pathname, state.cwd_dir_fd, state.rel_pathname, stat_buf,
++ options.start_time.tv_sec,
++ options.output_block_size,
++ pred_ptr->literal_control_chars, stream);
++ return true;
++}
++
++boolean
++pred_fprint (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ (void) &stat_buf;
++
++ print_quoted(pred_ptr->args.printf_vec.stream,
++ pred_ptr->args.printf_vec.quote_opts,
++ pred_ptr->args.printf_vec.dest_is_tty,
++ "%s\n",
++ pathname);
++ return true;
++}
++
++boolean
++pred_fprint0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ FILE * fp = pred_ptr->args.printf_vec.stream;
++
++ (void) &stat_buf;
++
++ fputs (pathname, fp);
++ putc (0, fp);
++ return true;
++}
++
++
++
++static char*
++mode_to_filetype(mode_t m)
++{
++#define HANDLE_TYPE(t,letter) if (m==t) { return letter; }
++#ifdef S_IFREG
++ HANDLE_TYPE(S_IFREG, "f"); /* regular file */
++#endif
++#ifdef S_IFDIR
++ HANDLE_TYPE(S_IFDIR, "d"); /* directory */
++#endif
++#ifdef S_IFLNK
++ HANDLE_TYPE(S_IFLNK, "l"); /* symbolic link */
++#endif
++#ifdef S_IFSOCK
++ HANDLE_TYPE(S_IFSOCK, "s"); /* Unix domain socket */
++#endif
++#ifdef S_IFBLK
++ HANDLE_TYPE(S_IFBLK, "b"); /* block device */
++#endif
++#ifdef S_IFCHR
++ HANDLE_TYPE(S_IFCHR, "c"); /* character device */
++#endif
++#ifdef S_IFIFO
++ HANDLE_TYPE(S_IFIFO, "p"); /* FIFO */
++#endif
++#ifdef S_IFDOOR
++ HANDLE_TYPE(S_IFDOOR, "D"); /* Door (e.g. on Solaris) */
++#endif
++ return "U"; /* Unknown */
++}
++
++static double
++file_sparseness(const struct stat *p)
++{
++#if defined HAVE_STRUCT_STAT_ST_BLOCKS
++ if (0 == p->st_size)
++ {
++ if (0 == p->st_blocks)
++ return 1.0;
++ else
++ return p->st_blocks < 0 ? -HUGE_VAL : HUGE_VAL;
++ }
++ else
++ {
++ double blklen = file_blocksize(p) * (double)p->st_blocks;
++ return blklen / p->st_size;
++ }
++#else
++ return 1.0;
++#endif
++}
++
++
++
++static void
++checked_fprintf(struct format_val *dest, const char *fmt, ...)
++{
++ int rv;
++ va_list ap;
++
++ va_start(ap, fmt);
++ rv = vfprintf(dest->stream, fmt, ap);
++ if (rv < 0)
++ nonfatal_file_error(dest->filename);
++}
++
++
++static void
++checked_print_quoted (struct format_val *dest,
++ const char *format, const char *s)
++{
++ int rv = print_quoted(dest->stream, dest->quote_opts, dest->dest_is_tty,
++ format, s);
++ if (rv < 0)
++ nonfatal_file_error(dest->filename);
++}
++
++
++static void
++checked_fwrite(void *p, size_t siz, size_t nmemb, struct format_val *dest)
++{
++ int items_written = fwrite(p, siz, nmemb, dest->stream);
++ if (items_written < nmemb)
++ nonfatal_file_error(dest->filename);
++}
++
++static void
++checked_fflush(struct format_val *dest)
++{
++ if (0 != fflush(dest->stream))
++ {
++ nonfatal_file_error(dest->filename);
++ }
++}
++
++static void
++do_fprintf(struct format_val *dest,
++ struct segment *segment,
++ const char *pathname,
++ const struct stat *stat_buf)
++{
++ char hbuf[LONGEST_HUMAN_READABLE + 1];
++ const char *cp;
++
++ switch (segment->segkind)
++ {
++ case KIND_PLAIN: /* Plain text string (no % conversion). */
++ /* trusted */
++ checked_fwrite(segment->text, 1, segment->text_len, dest);
++ break;
++
++ case KIND_STOP: /* Terminate argument and flush output. */
++ /* trusted */
++ checked_fwrite(segment->text, 1, segment->text_len, dest);
++ checked_fflush(dest);
++ break;
++
++ case KIND_FORMAT:
++ switch (segment->format_char[0])
++ {
++ case 'a': /* atime in `ctime' format. */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text, ctime_format (get_stat_atime(stat_buf)));
++ break;
++ case 'b': /* size in 512-byte blocks */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
++ hbuf, human_ceiling,
++ ST_NBLOCKSIZE, 512));
++ break;
++ case 'c': /* ctime in `ctime' format */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime(stat_buf)));
++ break;
++ case 'd': /* depth in search tree */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text, state.curdepth);
++ break;
++ case 'D': /* Device on which file exists (stat.st_dev) */
++ /* trusted */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) stat_buf->st_dev, hbuf,
++ human_ceiling, 1, 1));
++ break;
++ case 'f': /* base name of path */
++ /* sanitised */
++ {
++ char *base = base_name (pathname);
++ checked_print_quoted (dest, segment->text, base);
++ free (base);
++ }
++ break;
++ case 'F': /* file system type */
++ /* trusted */
++ checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname));
++ break;
++ case 'g': /* group name */
++ /* trusted */
++ /* (well, the actual group is selected by the user but
++ * its name was selected by the system administrator)
++ */
++ {
++ struct group *g;
++
++ g = getgrgid (stat_buf->st_gid);
++ if (g)
++ {
++ segment->text[segment->text_len] = 's';
++ checked_fprintf (dest, segment->text, g->gr_name);
++ break;
++ }
++ else
++ {
++ /* Do nothing. */
++ /*FALLTHROUGH*/
++ }
++ }
++ /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/
++
++ case 'G': /* GID number */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) stat_buf->st_gid, hbuf,
++ human_ceiling, 1, 1));
++ break;
++ case 'h': /* leading directories part of path */
++ /* sanitised */
++ {
++ cp = strrchr (pathname, '/');
++ if (cp == NULL) /* No leading directories. */
++ {
++ /* If there is no slash in the pathname, we still
++ * print the string because it contains characters
++ * other than just '%s'. The %h expands to ".".
++ */
++ checked_print_quoted (dest, segment->text, ".");
++ }
++ else
++ {
++ char *s = strdup(pathname);
++ s[cp - pathname] = 0;
++ checked_print_quoted (dest, segment->text, s);
++ free(s);
++ }
++ }
++ break;
++
++ case 'H': /* ARGV element file was found under */
++ /* trusted */
++ {
++ char *s = xmalloc(state.starting_path_length+1);
++ memcpy(s, pathname, state.starting_path_length);
++ s[state.starting_path_length] = 0;
++ checked_fprintf (dest, segment->text, s);
++ free(s);
++ }
++ break;
++
++ case 'i': /* inode number */
++ /* UNTRUSTED, but not exploitable I think */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) stat_buf->st_ino, hbuf,
++ human_ceiling,
++ 1, 1));
++ break;
++ case 'k': /* size in 1K blocks */
++ /* UNTRUSTED, but not exploitable I think */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
++ hbuf, human_ceiling,
++ ST_NBLOCKSIZE, 1024));
++ break;
++ case 'l': /* object of symlink */
++ /* sanitised */
++#ifdef S_ISLNK
++ {
++ char *linkname = 0;
++
++ if (S_ISLNK (stat_buf->st_mode))
++ {
++ linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname);
++ if (linkname == 0)
++ state.exit_status = 1;
++ }
++ if (linkname)
++ {
++ checked_print_quoted (dest, segment->text, linkname);
++ free (linkname);
++ }
++ else
++ {
++ /* We still need to honour the field width etc., so this is
++ * not a no-op.
++ */
++ checked_print_quoted (dest, segment->text, "");
++ }
++ }
++#endif /* S_ISLNK */
++ break;
++
++ case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */
++ /* UNTRUSTED, probably unexploitable */
++ {
++ char modestring[16] ;
++ filemodestring (stat_buf, modestring);
++ modestring[10] = '\0';
++ checked_fprintf (dest, segment->text, modestring);
++ }
++ break;
++
++ case 'm': /* mode as octal number (perms only) */
++ /* UNTRUSTED, probably unexploitable */
++ {
++ /* Output the mode portably using the traditional numbers,
++ even if the host unwisely uses some other numbering
++ scheme. But help the compiler in the common case where
++ the host uses the traditional numbering scheme. */
++ mode_t m = stat_buf->st_mode;
++ boolean traditional_numbering_scheme =
++ (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000
++ && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100
++ && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010
++ && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001);
++ checked_fprintf (dest, segment->text,
++ (traditional_numbering_scheme
++ ? m & MODE_ALL
++ : ((m & S_ISUID ? 04000 : 0)
++ | (m & S_ISGID ? 02000 : 0)
++ | (m & S_ISVTX ? 01000 : 0)
++ | (m & S_IRUSR ? 00400 : 0)
++ | (m & S_IWUSR ? 00200 : 0)
++ | (m & S_IXUSR ? 00100 : 0)
++ | (m & S_IRGRP ? 00040 : 0)
++ | (m & S_IWGRP ? 00020 : 0)
++ | (m & S_IXGRP ? 00010 : 0)
++ | (m & S_IROTH ? 00004 : 0)
++ | (m & S_IWOTH ? 00002 : 0)
++ | (m & S_IXOTH ? 00001 : 0))));
++ }
++ break;
++
++ case 'n': /* number of links */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) stat_buf->st_nlink,
++ hbuf,
++ human_ceiling,
++ 1, 1));
++ break;
++
++ case 'p': /* pathname */
++ /* sanitised */
++ checked_print_quoted (dest, segment->text, pathname);
++ break;
++
++ case 'P': /* pathname with ARGV element stripped */
++ /* sanitised */
++ if (state.curdepth > 0)
++ {
++ cp = pathname + state.starting_path_length;
++ if (*cp == '/')
++ /* Move past the slash between the ARGV element
++ and the rest of the pathname. But if the ARGV element
++ ends in a slash, we didn't add another, so we've
++ already skipped past it. */
++ cp++;
++ }
++ else
++ {
++ cp = "";
++ }
++ checked_print_quoted (dest, segment->text, cp);
++ break;
++
++ case 's': /* size in bytes */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) stat_buf->st_size,
++ hbuf, human_ceiling, 1, 1));
++ break;
++
++ case 'S': /* sparseness */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text, file_sparseness(stat_buf));;
++ break;
++
++ case 't': /* mtime in `ctime' format */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text,
++ ctime_format (get_stat_mtime(stat_buf)));
++ break;
++
++ case 'u': /* user name */
++ /* trusted */
++ /* (well, the actual user is selected by the user on systems
++ * where chown is not restricted, but the user name was
++ * selected by the system administrator)
++ */
++ {
++ struct passwd *p;
++
++ p = getpwuid (stat_buf->st_uid);
++ if (p)
++ {
++ segment->text[segment->text_len] = 's';
++ checked_fprintf (dest, segment->text, p->pw_name);
++ break;
++ }
++ /* else fallthru */
++ }
++ /* FALLTHROUGH*/ /* .. to case U */
++
++ case 'U': /* UID number */
++ /* UNTRUSTED, probably unexploitable */
++ checked_fprintf (dest, segment->text,
++ human_readable ((uintmax_t) stat_buf->st_uid, hbuf,
++ human_ceiling, 1, 1));
++ break;
++
++ /* %Y: type of file system entry like `ls -l`:
++ * (d,-,l,s,p,b,c,n) n=nonexistent(symlink)
++ */
++ case 'Y': /* in case of symlink */
++ /* trusted */
++ {
++#ifdef S_ISLNK
++ if (S_ISLNK (stat_buf->st_mode))
++ {
++ struct stat sbuf;
++ /* If we would normally follow links, do not do so.
++ * If we would normally not follow links, do so.
++ */
++ if ((following_links() ? lstat : stat)
++ (state.rel_pathname, &sbuf) != 0)
++ {
++ if ( errno == ENOENT )
++ {
++ checked_fprintf (dest, segment->text, "N");
++ break;
++ }
++ else if ( errno == ELOOP )
++ {
++ checked_fprintf (dest, segment->text, "L");
++ break;
++ }
++ else
++ {
++ checked_fprintf (dest, segment->text, "?");
++ error (0, errno, "%s",
++ safely_quote_err_filename(0, pathname));
++ /* exit_status = 1;
++ return ; */
++ break;
++ }
++ }
++ checked_fprintf (dest, segment->text,
++ mode_to_filetype(sbuf.st_mode & S_IFMT));
++ }
++#endif /* S_ISLNK */
++ else
++ {
++ checked_fprintf (dest, segment->text,
++ mode_to_filetype(stat_buf->st_mode & S_IFMT));
++ }
++ }
++ break;
++
++ case 'y':
++ /* trusted */
++ {
++ checked_fprintf (dest, segment->text,
++ mode_to_filetype(stat_buf->st_mode & S_IFMT));
++ }
++ break;
++ }
++ /* end of KIND_FORMAT case */
++ break;
++ }
++}
++
++boolean
++pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ struct format_val *dest = &pred_ptr->args.printf_vec;
++ struct segment *segment;
++
++ for (segment = dest->segment; segment; segment = segment->next)
++ {
++ if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */
++ {
++ struct timespec ts;
++ int valid = 0;
++
++ switch (segment->format_char[0])
++ {
++ case 'A':
++ ts = get_stat_atime(stat_buf);
++ valid = 1;
++ break;
++ case 'B':
++ ts = get_stat_birthtime(stat_buf);
++ if ('@' == segment->format_char[1])
++ valid = 1;
++ else
++ valid = (ts.tv_nsec >= 0);
++ break;
++ case 'C':
++ ts = get_stat_ctime(stat_buf);
++ valid = 1;
++ break;
++ case 'T':
++ ts = get_stat_mtime(stat_buf);
++ valid = 1;
++ break;
++ default:
++ assert (0);
++ abort ();
++ }
++ /* We trust the output of format_date not to contain
++ * nasty characters, though the value of the date
++ * is itself untrusted data.
++ */
++ if (valid)
++ {
++ /* trusted */
++ checked_fprintf (dest, segment->text,
++ format_date (ts, segment->format_char[1]));
++ }
++ else
++ {
++ /* The specified timestamp is not available, output
++ * nothing for the timestamp, but use the rest (so that
++ * for example find foo -printf '[%Bs] %p\n' can print
++ * "[] foo").
++ */
++ /* trusted */
++ checked_fprintf (dest, segment->text, "");
++ }
++ }
++ else
++ {
++ /* Print a segment which is not a date. */
++ do_fprintf(dest, segment, pathname, stat_buf);
++ }
++ }
++ return true;
++}
++
++boolean
++pred_fstype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ if (strcmp (filesystem_type (stat_buf, pathname), pred_ptr->args.str) == 0)
++ return true;
++ else
++ return false;
++}
++
++boolean
++pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ switch (pred_ptr->args.numinfo.kind)
++ {
++ case COMP_GT:
++ if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_LT:
++ if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_EQ:
++ if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ }
++ return (false);
++}
++
++boolean
++pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ if (pred_ptr->args.gid == stat_buf->st_gid)
++ return (true);
++ else
++ return (false);
++}
++
++boolean
++pred_ilname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ return match_lname (pathname, stat_buf, pred_ptr, true);
++}
++
++/* Common code between -name, -iname. PATHNAME is being visited, STR
++ is name to compare basename against, and FLAGS are passed to
++ fnmatch. Recall that 'find / -name /' is one of the few times where a '/'
++ in the -name must actually find something. */
++static boolean
++pred_name_common (const char *pathname, const char *str, int flags)
++{
++ char *p;
++ boolean b;
++ /* We used to use last_component() here, but that would not allow us to modify the
++ * input string, which is const. We could optimise by duplicating the string only
++ * if we need to modify it, and I'll do that if there is a measurable
++ * performance difference on a machine built after 1990...
++ */
++ char *base = base_name (pathname);
++ /* remove trailing slashes, but leave "/" or "//foo" unchanged. */
++ strip_trailing_slashes(base);
++
++ /* FNM_PERIOD is not used here because POSIX requires that it not be.
++ * See http://standards.ieee.org/reading/ieee/interp/1003-2-92_int/pasc-1003.2-126.html
++ */
++ b = fnmatch (str, base, flags) == 0;
++ free (base);
++ return b;
++}
++
++boolean
++pred_iname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) stat_buf;
++ return pred_name_common (pathname, pred_ptr->args.str, FNM_CASEFOLD);
++}
++
++boolean
++pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ switch (pred_ptr->args.numinfo.kind)
++ {
++ case COMP_GT:
++ if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_LT:
++ if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_EQ:
++ if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ }
++ return (false);
++}
++
++boolean
++pred_ipath (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) stat_buf;
++
++ if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0)
++ return (true);
++ return (false);
++}
++
++boolean
++pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ switch (pred_ptr->args.numinfo.kind)
++ {
++ case COMP_GT:
++ if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_LT:
++ if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_EQ:
++ if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ }
++ return (false);
++}
++
++boolean
++pred_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ return match_lname (pathname, stat_buf, pred_ptr, false);
++}
++
++static boolean
++match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)
++{
++ boolean ret = false;
++#ifdef S_ISLNK
++ if (S_ISLNK (stat_buf->st_mode))
++ {
++ char *linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname);
++ if (linkname)
++ {
++ if (fnmatch (pred_ptr->args.str, linkname,
++ ignore_case ? FNM_CASEFOLD : 0) == 0)
++ ret = true;
++ free (linkname);
++ }
++ }
++#endif /* S_ISLNK */
++ return ret;
++}
++
++boolean
++pred_ls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ return pred_fls(pathname, stat_buf, pred_ptr);
++}
++
++boolean
++pred_mmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) &pathname;
++ return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60);
++}
++
++boolean
++pred_mtime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS);
++}
++
++boolean
++pred_name (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) stat_buf;
++ return pred_name_common (pathname, pred_ptr->args.str, 0);
++}
++
++boolean
++pred_negate (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ return !apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
++}
++
++boolean
++pred_newer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++
++ assert (COMP_GT == pred_ptr->args.reftime.kind);
++ return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0;
++}
++
++boolean
++pred_newerXY (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ struct timespec ts;
++ boolean collected = false;
++
++ assert (COMP_GT == pred_ptr->args.reftime.kind);
++
++ switch (pred_ptr->args.reftime.xval)
++ {
++ case XVAL_TIME:
++ assert (pred_ptr->args.reftime.xval != XVAL_TIME);
++ return false;
++
++ case XVAL_ATIME:
++ ts = get_stat_atime(stat_buf);
++ collected = true;
++ break;
++
++ case XVAL_BIRTHTIME:
++ ts = get_stat_birthtime(stat_buf);
++ collected = true;
++ if (ts.tv_nsec < 0);
++ {
++ /* XXX: Cannot determine birth time. Warn once. */
++ error(0, 0, _("Warning: cannot determine birth time of file %s"),
++ safely_quote_err_filename(0, pathname));
++ return false;
++ }
++ break;
++
++ case XVAL_CTIME:
++ ts = get_stat_ctime(stat_buf);
++ collected = true;
++ break;
++
++ case XVAL_MTIME:
++ ts = get_stat_mtime(stat_buf);
++ collected = true;
++ break;
++ }
++
++ assert (collected);
++ return compare_ts(ts, pred_ptr->args.reftime.ts) > 0;
++}
++
++boolean
++pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) pred_ptr;
++
++#ifdef CACHE_IDS
++ extern char *gid_unused;
++
++ return gid_unused[(unsigned) stat_buf->st_gid];
++#else
++ return getgrgid (stat_buf->st_gid) == NULL;
++#endif
++}
++
++boolean
++pred_nouser (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++#ifdef CACHE_IDS
++ extern char *uid_unused;
++#endif
++
++ (void) pathname;
++ (void) pred_ptr;
++
++#ifdef CACHE_IDS
++ return uid_unused[(unsigned) stat_buf->st_uid];
++#else
++ return getpwuid (stat_buf->st_uid) == NULL;
++#endif
++}
++
++
++static boolean
++is_ok(const char *program, const char *arg)
++{
++ fflush (stdout);
++ /* The draft open standard requires that, in the POSIX locale,
++ the last non-blank character of this prompt be '?'.
++ The exact format is not specified.
++ This standard does not have requirements for locales other than POSIX
++ */
++ /* XXX: printing UNTRUSTED data here. */
++ fprintf (stderr, _("< %s ... %s > ? "
++ /* TRANSLATORS: we would like, if possible, the final non-blank
++ * character of this string to be '?', but it is not
++ * wholly essential. */
++ ), program, arg);
++ fflush (stderr);
++ return yesno();
++}
++
++boolean
++pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname))
++ return new_impl_pred_exec (get_start_dirfd(),
++ pathname, stat_buf, pred_ptr, NULL, 0);
++ else
++ return false;
++}
++
++boolean
++pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./";
++ if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname))
++ return new_impl_pred_exec (get_current_dirfd(),
++ state.rel_pathname, stat_buf, pred_ptr,
++ prefix, (prefix ? 2 : 0));
++ else
++ return false;
++}
++
++boolean
++pred_openparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) stat_buf;
++ (void) pred_ptr;
++ return true;
++}
++
++boolean
++pred_or (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ if (pred_ptr->pred_left == NULL
++ || !apply_predicate(pathname, stat_buf, pred_ptr->pred_left))
++ {
++ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
++ }
++ else
++ return true;
++}
++
++boolean
++pred_path (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) stat_buf;
++ if (fnmatch (pred_ptr->args.str, pathname, 0) == 0)
++ return (true);
++ return (false);
++}
++
++boolean
++pred_perm (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ mode_t mode = stat_buf->st_mode;
++ mode_t perm_val = pred_ptr->args.perm.val[S_ISDIR (mode) != 0];
++ (void) pathname;
++ switch (pred_ptr->args.perm.kind)
++ {
++ case PERM_AT_LEAST:
++ return (mode & perm_val) == perm_val;
++ break;
++
++ case PERM_ANY:
++ /* True if any of the bits set in the mask are also set in the file's mode.
++ *
++ *
++ * Otherwise, if onum is prefixed by a hyphen, the primary shall
++ * evaluate as true if at least all of the bits specified in
++ * onum that are also set in the octal mask 07777 are set.
++ *
++ * Eric Blake's interpretation is that the mode argument is zero,
++
++ */
++ if (0 == perm_val)
++ return true; /* Savannah bug 14748; we used to return false */
++ else
++ return (mode & perm_val) != 0;
++ break;
++
++ case PERM_EXACT:
++ return (mode & MODE_ALL) == perm_val;
++ break;
++
++ default:
++ abort ();
++ break;
++ }
++}
++
++
++struct access_check_args
++{
++ const char *filename;
++ int access_type;
++ int cb_errno;
++};
++
++
++static int
++access_callback(void *context)
++{
++ int rv;
++ struct access_check_args *args = context;
++ if ((rv = access(args->filename, args->access_type)) < 0)
++ args->cb_errno = errno;
++ return rv;
++}
++
++static int
++can_access(int access_type)
++{
++ struct access_check_args args;
++ args.filename = state.rel_pathname;
++ args.access_type = access_type;
++ args.cb_errno = 0;
++ return 0 == run_in_dir(state.cwd_dir_fd, access_callback, &args);
++}
++
++
++boolean
++pred_executable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) stat_buf;
++ (void) pred_ptr;
++
++ return can_access(X_OK);
++}
++
++boolean
++pred_readable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) stat_buf;
++ (void) pred_ptr;
++
++ return can_access(R_OK);
++}
++
++boolean
++pred_writable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) stat_buf;
++ (void) pred_ptr;
++
++ return can_access(W_OK);
++}
++
++boolean
++pred_print (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) stat_buf;
++ (void) pred_ptr;
++
++ print_quoted(pred_ptr->args.printf_vec.stream,
++ pred_ptr->args.printf_vec.quote_opts,
++ pred_ptr->args.printf_vec.dest_is_tty,
++ "%s\n", pathname);
++ return true;
++}
++
++boolean
++pred_print0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ return pred_fprint0(pathname, stat_buf, pred_ptr);
++}
++
++boolean
++pred_prune (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) pred_ptr;
++
++ if (options.do_dir_first == true && /* no effect with -depth */
++ stat_buf != NULL &&
++ S_ISDIR(stat_buf->st_mode))
++ state.stop_at_current_level = true;
++
++ /* findutils used to return options.do_dir_first here, so that -prune
++ * returns true only if -depth is not in effect. But POSIX requires
++ * that -prune always evaluate as true.
++ */
++ return true;
++}
++
++boolean
++pred_quit (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) stat_buf;
++ (void) pred_ptr;
++
++ /* Run any cleanups. This includes executing any command lines
++ * we have partly built but not executed.
++ */
++ cleanup();
++
++ /* Since -exec and friends don't leave child processes running in the
++ * background, there is no need to wait for them here.
++ */
++ exit(state.exit_status); /* 0 for success, etc. */
++}
++
++boolean
++pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ int len = strlen (pathname);
++(void) stat_buf;
++ if (re_match (pred_ptr->args.regex, pathname, len, 0,
++ (struct re_registers *) NULL) == len)
++ return (true);
++ return (false);
++}
++
++boolean
++pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ uintmax_t f_val;
++
++ (void) pathname;
++ f_val = ((stat_buf->st_size / pred_ptr->args.size.blocksize)
++ + (stat_buf->st_size % pred_ptr->args.size.blocksize != 0));
++ switch (pred_ptr->args.size.kind)
++ {
++ case COMP_GT:
++ if (f_val > pred_ptr->args.size.size)
++ return (true);
++ break;
++ case COMP_LT:
++ if (f_val < pred_ptr->args.size.size)
++ return (true);
++ break;
++ case COMP_EQ:
++ if (f_val == pred_ptr->args.size.size)
++ return (true);
++ break;
++ }
++ return (false);
++}
++
++boolean
++pred_samefile (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ /* Potential optimisation: because of the loop protection, we always
++ * know the device of the current directory, hence the device number
++ * of the file we're currently considering. If -L is not in effect,
++ * and the device number of the file we're looking for is not the
++ * same as the device number of the current directory, this
++ * predicate cannot return true. Hence there would be no need to
++ * stat the file we're looking at.
++ */
++ (void) pathname;
++
++ /* We will often still have an fd open on the file under consideration,
++ * but that's just to ensure inode number stability by maintaining
++ * a reference to it; we don't need the file for anything else.
++ */
++ return stat_buf->st_ino == pred_ptr->args.samefileid.ino
++ && stat_buf->st_dev == pred_ptr->args.samefileid.dev;
++}
++
++boolean
++pred_true (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ (void) stat_buf;
++ (void) pred_ptr;
++ return true;
++}
++
++boolean
++pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ mode_t mode;
++ mode_t type = pred_ptr->args.type;
++
++ assert (state.have_type);
++
++ if (0 == state.type)
++ {
++ /* This can sometimes happen with broken NFS servers.
++ * See Savannah bug #16378.
++ */
++ return false;
++ }
++
++ (void) pathname;
++
++ if (state.have_stat)
++ mode = stat_buf->st_mode;
++ else
++ mode = state.type;
++
++#ifndef S_IFMT
++ /* POSIX system; check `mode' the slow way. */
++ if ((S_ISBLK (mode) && type == S_IFBLK)
++ || (S_ISCHR (mode) && type == S_IFCHR)
++ || (S_ISDIR (mode) && type == S_IFDIR)
++ || (S_ISREG (mode) && type == S_IFREG)
++#ifdef S_IFLNK
++ || (S_ISLNK (mode) && type == S_IFLNK)
++#endif
++#ifdef S_IFIFO
++ || (S_ISFIFO (mode) && type == S_IFIFO)
++#endif
++#ifdef S_IFSOCK
++ || (S_ISSOCK (mode) && type == S_IFSOCK)
++#endif
++#ifdef S_IFDOOR
++ || (S_ISDOOR (mode) && type == S_IFDOOR)
++#endif
++ )
++#else /* S_IFMT */
++ /* Unix system; check `mode' the fast way. */
++ if ((mode & S_IFMT) == type)
++#endif /* S_IFMT */
++ return (true);
++ else
++ return (false);
++}
++
++boolean
++pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ switch (pred_ptr->args.numinfo.kind)
++ {
++ case COMP_GT:
++ if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_LT:
++ if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ case COMP_EQ:
++ if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val)
++ return (true);
++ break;
++ }
++ return (false);
++}
++
++boolean
++pred_used (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ struct timespec delta, at, ct;
++
++ (void) pathname;
++
++ /* TODO: this needs to be retested carefully (manually, if necessary) */
++ at = get_stat_atime(stat_buf);
++ ct = get_stat_ctime(stat_buf);
++ delta.tv_sec = at.tv_sec - ct.tv_sec;
++ delta.tv_nsec = at.tv_nsec - ct.tv_nsec;
++ if (delta.tv_nsec < 0)
++ {
++ delta.tv_nsec += 1000000000;
++ delta.tv_sec -= 1;
++ }
++ return pred_timewindow(delta, pred_ptr, DAYSECS);
++}
++
++boolean
++pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ (void) pathname;
++ if (pred_ptr->args.uid == stat_buf->st_uid)
++ return (true);
++ else
++ return (false);
++}
++
++boolean
++pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
++{
++ struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */
++ int (*ystat) (const char*, struct stat *p);
++
++ /* If we would normally stat the link itself, stat the target instead.
++ * If we would normally follow the link, stat the link itself instead.
++ */
++ if (following_links())
++ ystat = optionp_stat;
++ else
++ ystat = optionl_stat;
++
++ set_stat_placeholders(&sbuf);
++ if ((*ystat) (state.rel_pathname, &sbuf) != 0)
++ {
++ if (following_links() && errno == ENOENT)
++ {
++ /* If we failed to follow the symlink,
++ * fall back on looking at the symlink itself.
++ */
++ /* Mimic behavior of ls -lL. */
++ return (pred_type (pathname, stat_buf, pred_ptr));
++ }
++ else
++ {
++ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
++ state.exit_status = 1;
++ }
++ return false;
++ }
++ /* Now that we have our stat() information, query it in the same
++ * way that -type does.
++ */
++ return (pred_type (pathname, &sbuf, pred_ptr));
++}
++
++/* 1) fork to get a child; parent remembers the child pid
++ 2) child execs the command requested
++ 3) parent waits for child; checks for proper pid of child
++
++ Possible returns:
++
++ ret errno status(h) status(l)
++
++ pid x signal# 0177 stopped
++ pid x exit arg 0 term by _exit
++ pid x 0 signal # term by signal
++ -1 EINTR parent got signal
++ -1 other some other kind of error
++
++ Return true only if the pid matches, status(l) is
++ zero, and the exit arg (status high) is 0.
++ Otherwise return false, possibly printing an error message. */
++
++
++static boolean
++prep_child_for_exec (boolean close_stdin, int dirfd)
++{
++ boolean ok = true;
++ if (close_stdin)
++ {
++ const char inputfile[] = "/dev/null";
++
++ if (close(0) < 0)
++ {
++ error(0, errno, _("Cannot close standard input"));
++ ok = false;
++ }
++ else
++ {
++ if (open(inputfile, O_RDONLY
++#if defined O_LARGEFILE
++ |O_LARGEFILE
++#endif
++ ) < 0)
++ {
++ /* This is not entirely fatal, since
++ * executing the child with a closed
++ * stdin is almost as good as executing it
++ * with its stdin attached to /dev/null.
++ */
++ error (0, errno, "%s", safely_quote_err_filename(0, inputfile));
++ /* do not set ok=false, it is OK to continue anyway. */
++ }
++ }
++ }
++
++ /* Even if DebugSearch is set, don't announce our change of
++ * directory, since we're not going to emit a subsequent
++ * announcement of a call to stat() anyway, as we're about to exec
++ * something.
++ */
++ if (dirfd != AT_FDCWD)
++ {
++ assert (dirfd >= 0);
++ if (0 != fchdir(dirfd))
++ {
++ /* If we cannot execute our command in the correct directory,
++ * we should not execute it at all.
++ */
++ error(0, errno, _("Failed to change directory"));
++ ok = false;
++ }
++ }
++ return ok;
++}
++
++
++
++int
++launch (const struct buildcmd_control *ctl,
++ struct buildcmd_state *buildstate)
++{
++ int wait_status;
++ pid_t child_pid;
++ static int first_time = 1;
++ const struct exec_val *execp = buildstate->usercontext;
++
++ if (!execp->use_current_dir)
++ {
++ assert (starting_desc >= 0);
++ assert (execp->dirfd == starting_desc);
++ }
++
++
++ /* Null terminate the arg list. */
++ bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false);
++
++ /* Make sure output of command doesn't get mixed with find output. */
++ fflush (stdout);
++ fflush (stderr);
++
++ /* Make sure to listen for the kids. */
++ if (first_time)
++ {
++ first_time = 0;
++ signal (SIGCHLD, SIG_DFL);
++ }
++
++ child_pid = fork ();
++ if (child_pid == -1)
++ error (1, errno, _("cannot fork"));
++ if (child_pid == 0)
++ {
++ /* We are the child. */
++ assert (starting_desc >= 0);
++ if (!prep_child_for_exec(execp->close_stdin, execp->dirfd))
++ {
++ _exit(1);
++ }
++
++ execvp (buildstate->cmd_argv[0], buildstate->cmd_argv);
++ error (0, errno, "%s",
++ safely_quote_err_filename(0, buildstate->cmd_argv[0]));
++ _exit (1);
++ }
++
++
++ /* In parent; set up for next time. */
++ bc_clear_args(ctl, buildstate);
++
++
++ while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1)
++ {
++ if (errno != EINTR)
++ {
++ error (0, errno, _("error waiting for %s"),
++ safely_quote_err_filename(0, buildstate->cmd_argv[0]));
++ state.exit_status = 1;
++ return 0; /* FAIL */
++ }
++ }
++
++ if (WIFSIGNALED (wait_status))
++ {
++ error (0, 0, _("%1$s terminated by signal %2$d"),
++ quotearg_n_style(0, options.err_quoting_style,
++ buildstate->cmd_argv[0]),
++ WTERMSIG (wait_status));
++
++ if (execp->multiple)
++ {
++ /* -exec \; just returns false if the invoked command fails.
++ * -exec {} + returns true if the invoked command fails, but
++ * sets the program exit status.
++ */
++ state.exit_status = 1;
++ }
++
++ return 1; /* OK */
++ }
++
++ if (0 == WEXITSTATUS (wait_status))
++ {
++ return 1; /* OK */
++ }
++ else
++ {
++ if (execp->multiple)
++ {
++ /* -exec \; just returns false if the invoked command fails.
++ * -exec {} + returns true if the invoked command fails, but
++ * sets the program exit status.
++ */
++ state.exit_status = 1;
++ }
++ return 0; /* FAIL */
++ }
++
++}
++
++
++/* Return a static string formatting the time WHEN according to the
++ * strftime format character KIND.
++ *
++ * This function contains a number of assertions. These look like
++ * runtime checks of the results of computations, which would be a
++ * problem since external events should not be tested for with
++ * "assert" (instead you should use "if"). However, they are not
++ * really runtime checks. The assertions actually exist to verify
++ * that the various buffers are correctly sized.
++ */
++static char *
++format_date (struct timespec ts, int kind)
++{
++ /* In theory, we use an extra 10 characters for 9 digits of
++ * nanoseconds and 1 for the decimal point. However, the real
++ * world is more complex than that.
++ *
++ * For example, some systems return junk in the tv_nsec part of
++ * st_birthtime. An example of this is the NetBSD-4.0-RELENG kernel
++ * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE
++ * runtime and examining files on an msdos filesytem. So for that
++ * reason we set NS_BUF_LEN to 32, which is simply "long enough" as
++ * opposed to "exactly the right size". Note that the behaviour of
++ * NetBSD appears to be a result of the use of uninitialised data,
++ * as it's not 100% reproducible (more like 25%).
++ */
++ enum {
++ NS_BUF_LEN = 32,
++ DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/
++ };
++ static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS,
++ MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))];
++ char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/
++ int charsprinted, need_ns_suffix;
++ struct tm *tm;
++ char fmt[6];
++
++ /* human_readable() assumes we pass a buffer which is at least as
++ * long as LONGEST_HUMAN_READABLE. We use an assertion here to
++ * ensure that no nasty unsigned overflow happend in our calculation
++ * of the size of buf. Do the assertion here rather than in the
++ * code for %@ so that we find the problem quickly if it exists. If
++ * you want to submit a patch to move this into the if statement, go
++ * ahead, I'll apply it. But include performance timings
++ * demonstrating that the performance difference is actually
++ * measurable.
++ */
++ verify (sizeof(buf) >= LONGEST_HUMAN_READABLE);
++
++ charsprinted = 0;
++ need_ns_suffix = 0;
++
++ /* Format the main part of the time. */
++ if (kind == '+')
++ {
++ strcpy (fmt, "%F+%T");
++ need_ns_suffix = 1;
++ }
++ else
++ {
++ fmt[0] = '%';
++ fmt[1] = kind;
++ fmt[2] = '\0';
++
++ /* %a, %c, and %t are handled in ctime_format() */
++ switch (kind)
++ {
++ case 'S':
++ case 'T':
++ case 'X':
++ case '@':
++ need_ns_suffix = 1;
++ break;
++ default:
++ need_ns_suffix = 0;
++ break;
++ }
++ }
++
++ if (need_ns_suffix)
++ {
++ /* Format the nanoseconds part. Leave a trailing zero to
++ * discourage people from writing scripts which extract the
++ * fractional part of the timestamp by using column offsets.
++ * The reason for discouraging this is that in the future, the
++ * granularity may not be nanoseconds.
++ */
++ ns_buf[0] = 0;
++ charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec);
++ assert (charsprinted < NS_BUF_LEN);
++ }
++
++ if (kind != '@'
++ && (tm = localtime (&ts.tv_sec))
++ && strftime (buf, sizeof buf, fmt, tm))
++ {
++ /* For %AS, %CS, %TS, add the fractional part of the seconds
++ * information.
++ */
++ if (need_ns_suffix)
++ {
++ assert ((sizeof buf - strlen(buf)) > strlen(ns_buf));
++ strcat(buf, ns_buf);
++ }
++ return buf;
++ }
++ else
++ {
++ uintmax_t w = ts.tv_sec;
++ size_t used, len, remaining;
++
++ /* XXX: note that we are negating an unsigned type which is the
++ * widest possible unsigned type.
++ */
++ char *p = human_readable (ts.tv_sec < 0 ? -w : w, buf + 1,
++ human_ceiling, 1, 1);
++ assert (p > buf);
++ assert (p < (buf + (sizeof buf)));
++ if (ts.tv_sec < 0)
++ *--p = '-'; /* XXX: Ugh, relying on internal details of human_readable(). */
++
++ /* Add the nanoseconds part. Because we cannot enforce a
++ * particlar implementation of human_readable, we cannot assume
++ * any particular value for (p-buf). So we need to be careful
++ * that there is enough space remaining in the buffer.
++ */
++ if (need_ns_suffix)
++ {
++ len = strlen(p);
++ used = (p-buf) + len; /* Offset into buf of current end */
++ assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */
++ remaining = sizeof buf - used - 1u; /* allow space for NUL */
++
++ if (strlen(ns_buf) >= remaining)
++ {
++ error(0, 0,
++ "charsprinted=%ld but remaining=%lu: ns_buf=%s",
++ (long)charsprinted, (unsigned long)remaining, ns_buf);
++ }
++ assert (strlen(ns_buf) < remaining);
++ strcat(p, ns_buf);
++ }
++ return p;
++ }
++}
++
++static const char *weekdays[] =
++ {
++ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
++ };
++static char * months[] =
++ {
++ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
++ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
++ };
++
++
++static char *
++ctime_format (struct timespec ts)
++{
++ const struct tm * ptm;
++#define TIME_BUF_LEN 1024u
++ static char resultbuf[TIME_BUF_LEN];
++ int nout;
++
++ ptm = localtime(&ts.tv_sec);
++ if (ptm)
++ {
++ assert (ptm->tm_wday >= 0);
++ assert (ptm->tm_wday < 7);
++ assert (ptm->tm_mon >= 0);
++ assert (ptm->tm_mon < 12);
++ assert (ptm->tm_hour >= 0);
++ assert (ptm->tm_hour < 24);
++ assert (ptm->tm_min < 60);
++ assert (ptm->tm_sec <= 61); /* allows 2 leap seconds. */
++
++ /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */
++ nout = snprintf(resultbuf, TIME_BUF_LEN,
++ "%3s %3s %2d %02d:%02d:%02d.%010ld %04d",
++ weekdays[ptm->tm_wday],
++ months[ptm->tm_mon],
++ ptm->tm_mday,
++ ptm->tm_hour,
++ ptm->tm_min,
++ ptm->tm_sec,
++ (long int)ts.tv_nsec,
++ 1900 + ptm->tm_year);
++
++ assert (nout < TIME_BUF_LEN);
++ return resultbuf;
++ }
++ else
++ {
++ /* The time cannot be represented as a struct tm.
++ Output it as an integer. */
++ return format_date (ts, '@');
++ }
++}
++
++/* Copy STR into BUF and trim blanks from the end of BUF.
++ Return BUF. */
++
++static char *
++blank_rtrim (str, buf)
++ char *str;
++ char *buf;
++{
++ int i;
++
++ if (str == NULL)
++ return (NULL);
++ strcpy (buf, str);
++ i = strlen (buf) - 1;
++ while ((i >= 0) && ((buf[i] == ' ') || buf[i] == '\t'))
++ i--;
++ buf[++i] = '\0';
++ return (buf);
++}
++
++/* Print out the predicate list starting at NODE. */
++void
++print_list (FILE *fp, struct predicate *node)
++{
++ struct predicate *cur;
++ char name[256];
++
++ cur = node;
++ while (cur != NULL)
++ {
++ fprintf (fp, "[%s] ", blank_rtrim (cur->p_name, name));
++ cur = cur->pred_next;
++ }
++ fprintf (fp, "\n");
++}
++
++/* Print out the predicate list starting at NODE. */
++static void
++print_parenthesised(FILE *fp, struct predicate *node)
++{
++ int parens = 0;
++
++ if (node)
++ {
++ if ((pred_is(node, pred_or) || pred_is(node, pred_and))
++ && node->pred_left == NULL)
++ {
++ /* We print "<nothing> or X" as just "X"
++ * We print "<nothing> and X" as just "X"
++ */
++ print_parenthesised(fp, node->pred_right);
++ }
++ else
++ {
++ if (node->pred_left || node->pred_right)
++ parens = 1;
++
++ if (parens)
++ fprintf(fp, "%s", " ( ");
++ print_optlist(fp, node);
++ if (parens)
++ fprintf(fp, "%s", " ) ");
++ }
++ }
++}
++
++void
++print_optlist (FILE *fp, const struct predicate *p)
++{
++ if (p)
++ {
++ print_parenthesised(fp, p->pred_left);
++ fprintf (fp,
++ "%s%s",
++ p->need_stat ? "[call stat] " : "",
++ p->need_type ? "[need type] " : "");
++ print_predicate(fp, p);
++ fprintf(fp, " [%g] ", p->est_success_rate);
++ if (options.debug_options & DebugSuccessRates)
++ {
++ fprintf(fp, "[%ld/%ld", p->perf.successes, p->perf.visits);
++ if (p->perf.visits)
++ {
++ double real_rate = (double)p->perf.successes / (double)p->perf.visits;
++ fprintf(fp, "=%g] ", real_rate);
++ }
++ else
++ {
++ fprintf(fp, "=_] ");
++ }
++ }
++ print_parenthesised(fp, p->pred_right);
++ }
++}
++
++void show_success_rates(const struct predicate *p)
++{
++ if (options.debug_options & DebugSuccessRates)
++ {
++ fprintf(stderr, "Predicate success rates after completion:\n");
++ print_optlist(stderr, p);
++ fprintf(stderr, "\n");
++ }
++}
++
++
++
++
++#ifdef _NDEBUG
++/* If _NDEBUG is defined, the assertions will do nothing. Hence
++ * there is no point in having a function body for pred_sanity_check()
++ * if that preprocessor macro is defined.
++ */
++void
++pred_sanity_check(const struct predicate *predicates)
++{
++ /* Do nothing, since assert is a no-op with _NDEBUG set */
++ return;
++}
++#else
++void
++pred_sanity_check(const struct predicate *predicates)
++{
++ const struct predicate *p;
++
++ for (p=predicates; p != NULL; p=p->pred_next)
++ {
++ /* All predicates must do something. */
++ assert (p->pred_func != NULL);
++
++ /* All predicates must have a parser table entry. */
++ assert (p->parser_entry != NULL);
++
++ /* If the parser table tells us that just one predicate function is
++ * possible, verify that that is still the one that is in effect.
++ * If the parser has NULL for the predicate function, that means that
++ * the parse_xxx function fills it in, so we can't check it.
++ */
++ if (p->parser_entry->pred_func)
++ {
++ assert (p->parser_entry->pred_func == p->pred_func);
++ }
++
++ switch (p->parser_entry->type)
++ {
++ /* Options all take effect during parsing, so there should
++ * be no predicate entries corresponding to them. Hence we
++ * should not see any ARG_OPTION or ARG_POSITIONAL_OPTION
++ * items.
++ *
++ * This is a silly way of coding this test, but it prevents
++ * a compiler warning (i.e. otherwise it would think that
++ * there would be case statements missing).
++ */
++ case ARG_OPTION:
++ case ARG_POSITIONAL_OPTION:
++ assert (p->parser_entry->type != ARG_OPTION);
++ assert (p->parser_entry->type != ARG_POSITIONAL_OPTION);
++ break;
++
++ case ARG_ACTION:
++ assert(p->side_effects); /* actions have side effects. */
++ if (!pred_is(p, pred_prune) && !pred_is(p, pred_quit))
++ {
++ /* actions other than -prune and -quit should
++ * inhibit the default -print
++ */
++ assert (p->no_default_print);
++ }
++ break;
++
++ /* We happen to know that the only user of ARG_SPECIAL_PARSE
++ * is a test, so handle it like ARG_TEST.
++ */
++ case ARG_SPECIAL_PARSE:
++ case ARG_TEST:
++ case ARG_PUNCTUATION:
++ case ARG_NOOP:
++ /* Punctuation and tests should have no side
++ * effects and not inhibit default print.
++ */
++ assert (!p->no_default_print);
++ assert (!p->side_effects);
++ break;
++ }
++ }
++}
++#endif
+diff -purN findutils-4.3.12.orig/find/tree.c findutils-4.3.12/find/tree.c
+--- findutils-4.3.12.orig/find/tree.c 2007-12-19 16:12:34.000000000 -0500
++++ findutils-4.3.12/find/tree.c 2008-01-30 08:46:05.758843847 -0500
+@@ -1195,6 +1195,10 @@ build_expression_tree(int argc, char *ar
+ const struct parser_table *entry_close, *entry_print, *entry_open;
+ int i, oldi;
+
++#ifdef WITH_SELINUX
++ int is_selinux_enabled_flag = is_selinux_enabled()>0;
++#endif /* WITH_SELINUX */
++
+ predicates = NULL;
+
+ /* Find where in ARGV the predicates begin by skipping the list of
+@@ -1230,6 +1234,16 @@ build_expression_tree(int argc, char *ar
+ }
+
+ predicate_name = argv[i];
++
++#ifdef WITH_SELINUX
++ if (! is_selinux_enabled_flag) {
++ if ((strncmp(predicate_name,"-context",strlen("-context"))==0) ||
++ (strncmp(predicate_name,"--context",strlen("--context"))==0)) {
++ error (1, 0,_("Error: invalid predicate %s: the kernel is not selinux-enabled.\n"),predicate_name);
++ }
++ }
++#endif
++
+ parse_entry = find_parser (predicate_name);
+ if (parse_entry == NULL)
+ {
+@@ -1434,6 +1448,9 @@ get_new_pred (const struct parser_table
+ last_pred->need_stat = true;
+ last_pred->need_type = true;
+ last_pred->args.str = NULL;
++#ifdef WITH_SELINUX
++ last_pred->args.scontext = NULL;
++#endif
+ last_pred->pred_next = NULL;
+ last_pred->pred_left = NULL;
+ last_pred->pred_right = NULL;