diff options
-rw-r--r-- | sys-apps/findutils/ChangeLog | 6 | ||||
-rw-r--r-- | sys-apps/findutils/Manifest | 14 | ||||
-rw-r--r-- | sys-apps/findutils/files/findutils-4.3.12-selinux.diff | 9487 |
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 |