summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys-apps/findutils/ChangeLog6
-rw-r--r--sys-apps/findutils/Manifest14
-rw-r--r--sys-apps/findutils/files/findutils-4.3.12-selinux.diff9487
3 files changed, 17 insertions, 9490 deletions
diff --git a/sys-apps/findutils/ChangeLog b/sys-apps/findutils/ChangeLog
index d230afdecece..1c022e1be1e1 100644
--- a/sys-apps/findutils/ChangeLog
+++ b/sys-apps/findutils/ChangeLog
@@ -1,6 +1,10 @@
# ChangeLog for sys-apps/findutils
# Copyright 1999-2008 Gentoo Foundation; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/sys-apps/findutils/ChangeLog,v 1.161 2008/03/29 15:24:54 coldwind Exp $
+# $Header: /var/cvsroot/gentoo-x86/sys-apps/findutils/ChangeLog,v 1.162 2008/04/01 15:53:50 pebenito Exp $
+
+ 01 Apr 2008; Chris PeBenito <pebenito@gentoo.org>
+ files/findutils-4.3.12-selinux.diff:
+ Remove cruft from selinux patch.
29 Mar 2008; Santiago M. Mola <coldwind@gentoo.org>
findutils-4.3.13.ebuild:
diff --git a/sys-apps/findutils/Manifest b/sys-apps/findutils/Manifest
index 8e0ccf25dc2c..562c759fabfd 100644
--- a/sys-apps/findutils/Manifest
+++ b/sys-apps/findutils/Manifest
@@ -1,7 +1,10 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
AUX findutils-4.1.20-selinux.diff 10392 RMD160 8b17d8de81c8e40a6e558de7ff0e4e8174717320 SHA1 c23a941d9000e0dfce0fb65fbc97d9c2f92e6b56 SHA256 4a6c0d238b31d51cb9cbc1fd918ff5e66d314783ae6a9628c981a6368b6c44d0
AUX findutils-4.2.24-selinux.diff 14356 RMD160 d9e28cacdfd7cec1f766d2465466c472847df056 SHA1 4025e771fd39761992c7dc8503e3bed973ef26f6 SHA256 4e5df1395f80328c750d980ecb356aedd989b517e98cc52810ef03fe4f362bef
AUX findutils-4.3.11-selinux.diff 15774 RMD160 103b20347d96b33d68ae67c9295dd0c565f4aeea SHA1 27bc71ccb9031fc0c60c6e11712a2da3d37afb5e SHA256 e599d86f1787def865014d7e79a89c7fbff3c2a0492ae325d847a8adf8afdd24
-AUX findutils-4.3.12-selinux.diff 289906 RMD160 f74c1c67594d6c12109d4906c0cb29a40e7ec09d SHA1 6eae8de8ebd4180a07a793995ecc706c3e9f890a SHA256 2965c7f4f9a4fd4e0e4e7534d7407cc39a2a26163a136a867a3bf0005a0afe22
+AUX findutils-4.3.12-selinux.diff 15641 RMD160 36948882affd7e80a9a2e8052a79fe84b04ec113 SHA1 e0d37e0bfadb7dbb87a9eb136c14d816b34895dd SHA256 211686a97ebfae840f83ce4b6d21dc8c47157128adaf3fd60787dcdfe0ff5f66
AUX findutils-4.3.7-selinux.diff 15748 RMD160 1e50f8833150366213b17db3ddf53f378d582180 SHA1 704f341547b744dccc3494aa0e54293517a96821 SHA256 04ce6089ca013939560b76109378f91aa2fb616540f9cb9ed30e48f02584be02
AUX findutils-4.3.8-gnulib-stddef.patch 435 RMD160 4eb6e5e07e4124383875efff9ea8b5d47c18f81a SHA1 d85901e8eee3c2c708865a969be847421b02465f SHA256 d15754dfea82f6c8b7d1c377ffc184f759b903ef650aa856560f1fa243a9d08d
AUX findutils-4.3.8-listfile-segv.patch 1985 RMD160 f6d01e698d96d755df11da606b74d2a4e2133049 SHA1 f5d5126f28c0125bf15e642a377d01d147961334 SHA256 31eeb7c3e3ba076cbf8649942d42509eadf1cec29c9ad60ae52b4853055e35fc
@@ -24,5 +27,12 @@ EBUILD findutils-4.3.12.ebuild 1556 RMD160 9908b56f635f8558ee7fd0e6ea0d7ac4d7be5
EBUILD findutils-4.3.13.ebuild 1551 RMD160 ce9a5d08978e81bc134de7708a54e57efdc35297 SHA1 99aa22b26587555bc7720330f9922498839ab2c2 SHA256 8f4e23e3e70758d00cc10e27ede6008ca36dbc4e5db9f41afa9146516e426f34
EBUILD findutils-4.3.8-r1.ebuild 1652 RMD160 5c6dc566db50c4e6dfe7f101e3ac1b3d6bdcbb74 SHA1 a37570e5bddf36a05baf9acc13ad1d17b70bc822 SHA256 12c9647978b8011429f94fd8ab8b6f4f65c93dc3c86c1c62916cbba50b6480a2
EBUILD findutils-4.4.0.ebuild 1514 RMD160 a0c19fc70430c9c8c51730d955243ded51e863f8 SHA1 2421f458e410e56ec6da43f963b1f43c2a2f62a6 SHA256 458c5a5d225e08a1e604d799b50c0419387375f54ea0e9d23d5e99da2ed9c8d6
-MISC ChangeLog 22643 RMD160 51e3aa1a087e5e4422429b679cb045cbea6531a8 SHA1 a131aa654c29ddfefa25398af9962aa9d11ca485 SHA256 2a0feb21fbb6e348f2d76f6aaf88270ce0b4986fa118c8583a9fe630babb1b29
+MISC ChangeLog 22770 RMD160 e4c27937aa962bbc8263e83c862e7671c7ba92ed SHA1 07e65ab918b33551a4482eff02297ac2be9c1c62 SHA256 36d29451ff29a28f0fbabb74b0d1fc639b65afbef970d9f84ce5904e9ecdb36a
MISC metadata.xml 165 RMD160 84307ea324cd4f0f46d52f2fa096a1873e2fbe53 SHA1 c7b83ef947fe7e8273cc512fee9bba06586a58f9 SHA256 23a8851c12bd4cf7cf0528250ad5605be6b0780bbec0c51331d6d2c618c13f6a
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.7 (GNU/Linux)
+
+iD8DBQFH8lqX1wYvhvBeaEYRArgeAKD+oZbLuyRE/SI2GUVVbpwCQLxaGACeN01u
+GT61NNIjXcE/TnGEs/xVcIc=
+=PGPW
+-----END PGP SIGNATURE-----
diff --git a/sys-apps/findutils/files/findutils-4.3.12-selinux.diff b/sys-apps/findutils/files/findutils-4.3.12-selinux.diff
index fb772671e444..7ccde20cc014 100644
--- a/sys-apps/findutils/files/findutils-4.3.12-selinux.diff
+++ b/sys-apps/findutils/files/findutils-4.3.12-selinux.diff
@@ -111,2037 +111,6 @@ diff -purN findutils-4.3.12.orig/find/find.1 findutils-4.3.12/find/find.1
.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
@@ -2154,1547 +123,6 @@ diff -purN findutils-4.3.12.orig/find/find.c findutils-4.3.12/find/find.c
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
@@ -3906,3512 +334,6 @@ diff -purN findutils-4.3.12.orig/find/parser.c findutils-4.3.12/find/parser.c
{
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
@@ -7499,2415 +421,6 @@ diff -purN findutils-4.3.12.orig/find/pred.c findutils-4.3.12/find/pred.c
/* 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