Gentoo Archives: gentoo-commits

From: "Chris PeBenito (pebenito)" <pebenito@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-x86 commit in sys-apps/findutils/files: findutils-4.3.12-selinux.diff
Date: Wed, 30 Jan 2008 14:04:31
Message-Id: E1JKDVt-0000Km-Vw@stork.gentoo.org
1 pebenito 08/01/30 14:02:05
2
3 Added: findutils-4.3.12-selinux.diff
4 Log:
5 sys-apps/findutils: update selinux patch.
6 (Portage version: 2.1.4)
7
8 Revision Changes Path
9 1.1 sys-apps/findutils/files/findutils-4.3.12-selinux.diff
10
11 file : http://sources.gentoo.org/viewcvs.py/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff?rev=1.1&view=markup
12 plain: http://sources.gentoo.org/viewcvs.py/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff?rev=1.1&content-type=text/plain
13
14 Index: findutils-4.3.12-selinux.diff
15 ===================================================================
16 diff -purN findutils-4.3.12.orig/find/Makefile.am findutils-4.3.12/find/Makefile.am
17 --- findutils-4.3.12.orig/find/Makefile.am 2007-07-22 08:29:31.000000000 -0400
18 +++ findutils-4.3.12/find/Makefile.am 2008-01-30 08:46:05.750843391 -0500
19 @@ -2,6 +2,7 @@ AUTOMAKE_OPTIONS = std-options
20 localedir = $(datadir)/locale
21 # noinst_PROGRAMS = regexprops
22 # regexprops_SOURCES = regexprops.c
23 +DEFS = @DEFS@ -I. -I$(srcdir) -I.. -DWITH_SELINUX
24
25 noinst_LIBRARIES = libfindtools.a
26 libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c tree.c util.c
27 @@ -26,7 +27,7 @@ endif
28
29 EXTRA_DIST = defs.h $(man_MANS)
30 INCLUDES = -I../gnulib/lib -I$(top_srcdir)/lib -I$(top_srcdir)/gnulib/lib -I../intl -DLOCALEDIR=\"$(localedir)\"
31 -LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@
32 +LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -lselinux
33 man_MANS = find.1
34 SUBDIRS = . testsuite
35
36 diff -purN findutils-4.3.12.orig/find/Makefile.in findutils-4.3.12/find/Makefile.in
37 --- findutils-4.3.12.orig/find/Makefile.in 2007-12-19 16:27:24.000000000 -0500
38 +++ findutils-4.3.12/find/Makefile.in 2008-01-30 08:46:05.750843391 -0500
39 @@ -275,7 +275,7 @@ CPP = @CPP@
40 CPPFLAGS = @CPPFLAGS@
41 CYGPATH_W = @CYGPATH_W@
42 DEFAULT_ARG_SIZE = @DEFAULT_ARG_SIZE@
43 -DEFS = @DEFS@
44 +DEFS = @DEFS@ -I. -I$(srcdir) -I.. -DWITH_SELINUX
45 DEPDIR = @DEPDIR@
46 DIRENT_H = @DIRENT_H@
47 ECHO_C = @ECHO_C@
48 @@ -601,7 +601,7 @@ libfindtools_a_SOURCES = finddata.c fsty
49 @WITH_FTS_TRUE@oldfind_SOURCES = find.c
50 @WITH_FTS_FALSE@ftsfind_SOURCES = ftsfind.c
51 EXTRA_DIST = defs.h $(man_MANS)
52 -LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@
53 +LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -lselinux
54 man_MANS = find.1
55 SUBDIRS = . testsuite
56 all: all-recursive
57 diff -purN findutils-4.3.12.orig/find/defs.h findutils-4.3.12/find/defs.h
58 --- findutils-4.3.12.orig/find/defs.h 2007-11-30 05:19:58.000000000 -0500
59 +++ findutils-4.3.12/find/defs.h 2008-01-30 08:46:05.754843619 -0500
60 @@ -90,6 +90,9 @@ int get_statinfo PARAMS((const char *pat
61 #define MODE_RWX (S_IXUSR | S_IXGRP | S_IXOTH | MODE_RW)
62 #define MODE_ALL (S_ISUID | S_ISGID | S_ISVTX | MODE_RWX)
63
64 +#ifdef WITH_SELINUX
65 +#include <selinux/selinux.h>
66 +#endif /*WITH_SELINUX*/
67
68 struct predicate;
69 struct options;
70 @@ -314,6 +317,9 @@ struct predicate
71 struct samefile_file_id samefileid; /* samefile */
72 mode_t type; /* type */
73 struct format_val printf_vec; /* printf fprintf fprint ls fls print0 fprint0 print */
74 +#ifdef WITH_SELINUX
75 + security_context_t scontext; /* scontext */
76 +#endif /*WITH_SELINUX*/
77 } args;
78
79 /* The next predicate in the user input sequence,
80 @@ -458,7 +464,9 @@ PREDICATEFUNCTION pred_used;
81 PREDICATEFUNCTION pred_user;
82 PREDICATEFUNCTION pred_writable;
83 PREDICATEFUNCTION pred_xtype;
84 -
85 +#ifdef WITH_SELINUX
86 +PREDICATEFUNCTION pred_scontext;
87 +#endif /* WITH_SELINUX */
88
89
90 int launch PARAMS((const struct buildcmd_control *ctl,
91 @@ -605,10 +613,13 @@ struct options
92 */
93 unsigned short optimisation_level;
94
95 -
96 /* How should we quote filenames in error messages and so forth?
97 */
98 enum quoting_style err_quoting_style;
99 +
100 +#ifdef WITH_SELINUX
101 + int (*x_getfilecon)();
102 +#endif /* WITH_SELINUX */
103 };
104 extern struct options options;
105
106 diff -purN findutils-4.3.12.orig/find/find.1 findutils-4.3.12/find/find.1
107 --- findutils-4.3.12.orig/find/find.1 2007-12-19 14:53:14.000000000 -0500
108 +++ findutils-4.3.12/find/find.1 2008-01-30 08:49:54.983906639 -0500
109 @@ -935,6 +935,10 @@ checks the type of the file that
110 .B \-type
111 does not check.
112
113 +.IP "\-context \fIscontext\fR"
114 +.IP "\--context \fIscontext\fR"
115 +(SELinux only) File has the security context \fIscontext\fR.
116 +
117 .SS ACTIONS
118 .IP "\-delete\fR"
119 Delete files; true if removal succeeded. If the removal failed, an
120 @@ -1340,6 +1344,8 @@ File's type (like in
121 U=unknown type (shouldn't happen)
122 .IP %Y
123 File's type (like %y), plus follow symlinks: L=loop, N=nonexistent
124 +.IP %Z
125 +(SELinux only) file's security context.
126 .PP
127 A `%' character followed by any other character is discarded, but the
128 other character is printed (don't rely on this, as further format
129 diff -purN findutils-4.3.12.orig/find/find.1.orig findutils-4.3.12/find/find.1.orig
130 --- findutils-4.3.12.orig/find/find.1.orig 1969-12-31 19:00:00.000000000 -0500
131 +++ findutils-4.3.12/find/find.1.orig 2007-12-19 14:53:14.000000000 -0500
132 @@ -0,0 +1,2027 @@
133 +.TH FIND 1 \" -*- nroff -*-
134 +.SH NAME
135 +find \- search for files in a directory hierarchy
136 +.SH SYNOPSIS
137 +.B find
138 +[\-H] [\-L] [\-P] [\-D debugopts] [\-Olevel] [path...] [expression]
139 +.SH DESCRIPTION
140 +This manual page
141 +documents the GNU version of
142 +.BR find .
143 +GNU
144 +.B find
145 +searches the directory tree rooted at each given file name by
146 +evaluating the given expression from left to right, according to the
147 +rules of precedence (see section OPERATORS), until the outcome is
148 +known (the left hand side is false for \fIand\fR operations, true for
149 +\fIor\fR), at which point
150 +.B find
151 +moves on to the next file name.
152 +.PP
153 +If you are using
154 +.B find
155 +in an environment where security is important (for example if you are
156 +using it to seach directories that are writable by other users), you
157 +should read the "Security Considerations" chapter of the findutils
158 +documentation, which is called \fBFinding Files\fP and comes with
159 +findutils. That document also includes a lot more detail
160 +and discussion than this manual page, so you may find it a more useful
161 +source of information.
162 +.SH OPTIONS
163 +The
164 +.BR \-H ,
165 +.B \-L
166 +and
167 +.B \-P
168 +options control the treatment of symbolic
169 +links. Command-line arguments following these are taken to be names
170 +of files or directories to be examined, up to the first argument that
171 +begins with `\-', or the argument `(' or `!'. That argument and any
172 +following arguments are taken to be the expression describing what is
173 +to be searched for. If no paths are given, the current directory is
174 +used. If no expression is given, the expression
175 +.B \-print
176 +is used
177 +(but you should probably consider using
178 +.B \-print0
179 +instead, anyway).
180 +.PP
181 +This manual page talks about `options' within the expression list.
182 +These options control the behaviour of
183 +.B find
184 +but are specified immediately after the last path name. The five
185 +`real' options
186 +.BR \-H ,
187 +.BR \-L ,
188 +.BR \-P ,
189 +.B \-D
190 +and
191 +.B \-O
192 +must appear before
193 +the first path name, if at all. A double dash
194 +.B \-\-
195 +can also be used
196 +to signal that any remaining arguments are not options (though
197 +ensuring that all start points begin with either `./' or `/' is
198 +generally safer if you use wildcards in the list of start points).
199 +.IP \-P
200 +Never follow symbolic links. This is the default behaviour. When
201 +.B find
202 +examines or prints information a file, and the file is a symbolic
203 +link, the information used shall be taken from the properties of the
204 +symbolic link itself.
205 +
206 +.IP \-L
207 +Follow symbolic links. When
208 +.B find
209 +examines or prints information about files, the information used shall
210 +be taken from the properties of the file to which the link points, not
211 +from the link itself (unless it is a broken symbolic link or
212 +.B find
213 +is unable to examine the file to which the link points). Use of this
214 +option implies
215 +.BR \-noleaf .
216 +If you later use the
217 +.B \-P
218 +option,
219 +.B \-noleaf
220 +will still be in effect. If
221 +.B \-L
222 +is in effect and
223 +.B find
224 +discovers a symbolic link to a subdirectory during its search,
225 +the subdirectory pointed to by the symbolic link will be searched.
226 +.IP
227 +When the
228 +.B \-L
229 +option is in effect, the
230 +.B \-type
231 +predicate will always
232 +match against the type of the file that a symbolic link points to
233 +rather than the link itself (unless the symbolic link is broken).
234 +Using
235 +.B \-L
236 +causes the
237 +.B \-lname
238 +and
239 +.B \-ilname
240 +predicates always to return
241 +false.
242 +
243 +.IP \-H
244 +Do not follow symbolic links, except while processing the command
245 +line arguments. When
246 +.B find
247 +examines or prints information about files, the information used
248 +shall be taken from the properties of the symbolic link itself. The
249 +only exception to this behaviour is when a file specified on the
250 +command line is a symbolic link, and the link can be resolved. For
251 +that situation, the information used is taken from whatever the link
252 +points to (that is, the link is followed). The information about the
253 +link itself is used as a fallback if the file pointed to by the
254 +symbolic link cannot be examined. If
255 +.B \-H
256 +is in effect and one of the
257 +paths specified on the command line is a symbolic link to a directory,
258 +the contents of that directory will be examined (though of course
259 +\-maxdepth 0 would prevent this).
260 +.P
261 +If more than one of
262 +.BR \-H ,
263 +.B \-L
264 +and
265 +.B \-P
266 +is specified, each overrides the
267 +others; the last one appearing on the command line takes effect.
268 +Since it is the default, the
269 +.B \-P
270 +option should be considered to be in
271 +effect unless either
272 +.B \-H
273 +or
274 +.B \-L
275 +is specified.
276 +
277 +GNU
278 +.B find
279 +frequently stats files during the processing of the command line
280 +itself, before any searching has begun. These options also affect how
281 +those arguments are processed. Specifically, there are a number of
282 +tests that compare files listed on the command line against a file we
283 +are currently considering. In each case, the file specified on the
284 +command line will have been examined and some of its properties will
285 +have been saved. If the named file is in fact a symbolic link, and
286 +the
287 +.B \-P
288 +option is in effect (or if neither
289 +.B \-H
290 +nor
291 +.B \-L
292 +were specified), the information used for the comparison will be taken from
293 +the properties of the symbolic link. Otherwise, it will be taken from
294 +the properties of the file the link points to. If
295 +.B find
296 +cannot follow the link (for example because it has insufficient
297 +privileges or the link points to a nonexistent file) the properties of
298 +the link itself will be used.
299 +.P
300 +When the
301 +.B \-H
302 +or
303 +.B \-L options are in effect, any symbolic links listed
304 +as the argument of
305 +.B \-newer
306 +will be dereferenced, and the timestamp
307 +will be taken from the file to which the symbolic link points. The
308 +same consideration applies to
309 +.BR \-newerXY ,
310 +.B \-anewer
311 +and
312 +.BR \-cnewer .
313 +
314 +The
315 +.B \-follow
316 +option has a similar effect to
317 +.BR \-L ,
318 +though it takes
319 +effect at the point where it appears (that is, if
320 +.B \-L
321 +is not used but
322 +.B \-follow
323 +is, any symbolic links appearing after
324 +.B \-follow
325 +on the
326 +command line will be dereferenced, and those before it will not).
327 +
328 +.IP "\-D debugoptions"
329 +Print diagnostic information; this can be helpful to diagnose problems
330 +with why
331 +.B find
332 +is not doing what you want. The list of debug options should be comma
333 +separated. Compatibility of the debug options is not guaranteed
334 +between releases of findutils. For a complete list of valid debug
335 +options, see the output of
336 +.B find \-D
337 +.BR help .
338 +Valid debug options include
339 +.RS
340 +.IP help
341 +Explain the debugging options
342 +.IP tree
343 +Show the expression tree in its original and optimised form.
344 +.IP stat
345 +Print messages as files are examined with the
346 +.B stat
347 +and
348 +.B lstat
349 +system calls. The
350 +.B find
351 +program tries to minimise such calls.
352 +.IP opt
353 +Prints diagnostic information relating to the optimisation of the
354 +expression tree; see the \-O option.
355 +.IP rates
356 +Prints a summary indicating how often each predicate succeeded or
357 +failed.
358 +.RE
359 +.IP \-Olevel
360 +Enables query optimisation. The
361 +.B find
362 +program reorders tests to speed up execution while preserving the
363 +overall effect; that is, predicates with side effects are not
364 +reordered relative to each other. The optimisations performed at each
365 +optimisation level are as follows.
366 +.RS
367 +.IP 0
368 +Equivalent to optimisation level 1.
369 +.IP 1
370 +This is the default optimisation level and corresponds to the
371 +traditional behaviour. Expressions are reordered so that tests based
372 +only on the names of files (for example
373 +.B \-name
374 +and
375 +.BR \-regex )
376 +are performed first.
377 +.IP 2
378 +Any
379 +.B \-type
380 +or
381 +.B \-xtype
382 +tests are performed after any tests based only on the names of files,
383 +but before any tests that require information from the inode. On many
384 +modern versions of Unix, file types are returned by
385 +.B readdir()
386 +and so these predicates are faster to evaluate than predicates which
387 +need to stat the file first.
388 +.IP 3
389 +At this optimisation level, the full cost-based query optimiser is
390 +enabled. The order of tests is modified so that cheap (i.e. fast)
391 +tests are performed first and more expensive ones are performed later,
392 +if necessary. Within each cost band, predicates are evaluated earlier
393 +or later according to whether they are likely to succeed or not. For
394 +.BR \-o ,
395 +predicates which are likely to succeed are evaluated earlier, and for
396 +.BR \-a ,
397 +predicates which are likely to fail are evaluated earlier.
398 +.RE
399 +.IP
400 +The cost-based optimiser has a fixed idea of how likely any given test
401 +is to succeed. In some cases the probability takes account of the
402 +specific nature of the test (for example,
403 +.B \-type f
404 +is assumed to be more likely to succeed than
405 +.BR "\-type c" ).
406 +The cost-based optimiser is currently being evaluated. If it does
407 +not actually improve the performance of
408 +.BR find ,
409 +it will be removed again. Conversely, optimisations that prove to be
410 +reliable, robust and effective may be enabled at lower optimisation
411 +levels over time. However, the default behaviour (i.e. optimisation
412 +level 1) will not be changed in the 4.3.x release series. The
413 +findutils test suite runs all the tests on
414 +.B find
415 +at each optimisation level and ensures that the result is the same.
416 +.P
417 +.SH EXPRESSIONS
418 +The expression is made up of options (which affect overall operation
419 +rather than the processing of a specific file, and always return
420 +true), tests (which return a true or false value), and actions (which
421 +have side effects and return a true or false value), all separated by
422 +operators.
423 +.B \-and
424 +is assumed where the operator is omitted.
425 +
426 +If the expression contains no actions other than
427 +.BR \-prune ,
428 +.B \-print
429 +is
430 +performed on all files for which the expression is true.
431 +
432 +.SS OPTIONS
433 +.P
434 +All options always return true. Except for
435 +.BR \-daystart ,
436 +.B \-follow
437 +and
438 +.BR \-regextype ,
439 +the options affect all tests, including tests specified
440 +before the option. This is because the options are processed when the
441 +command line is parsed, while the tests don't do anything until files
442 +are examined. The
443 +.BR \-daystart ,
444 +.B \-follow
445 +and
446 +.B \-regextype
447 +options are different in this respect, and have an effect only on tests which
448 +appear later in the command line. Therefore, for clarity, it is best
449 +to place them at the beginning of the expression. A warning is issued
450 +if you don't do this.
451 +
452 +.IP \-d
453 +A synonym for \-depth, for compatibility with FreeBSD, NetBSD, MacOS X and OpenBSD.
454 +
455 +.IP \-daystart
456 +Measure times (for
457 +.BR \-amin ,
458 +.BR \-atime ,
459 +.BR \-cmin ,
460 +.BR \-ctime ,
461 +.BR \-mmin ,
462 +and
463 +.BR \-mtime )
464 +from the beginning of today rather than from 24 hours ago. This
465 +option only affects tests which appear later on the command line.
466 +
467 +.IP \-depth
468 +Process each directory's contents before the directory itself. The
469 +\-delete action also implies
470 +.BR \-depth .
471 +
472 +.IP \-follow
473 +Deprecated; use the
474 +.B \-L
475 +option instead. Dereference symbolic links.
476 +Implies
477 +.BR \-noleaf .
478 +The
479 +.B \-follow
480 +option affects only those tests which
481 +appear after it on the command line. Unless the
482 +.B \-H
483 +or
484 +.B \-L
485 +option has
486 +been specified, the position of the
487 +.B \-follow
488 +option changes the behaviour of the
489 +.B \-newer
490 +predicate; any files listed as the argument
491 +of
492 +.B \-newer
493 +will be dereferenced if they are symbolic links. The same
494 +consideration applies to
495 +.BR \-newerXY ,
496 +.B \-anewer
497 +and
498 +.BR \-cnewer .
499 +Similarly, the
500 +.B \-type
501 +predicate will always match against the type of the file
502 +that a symbolic link points to rather than the link itself. Using
503 +.B \-follow
504 +causes the
505 +.B \-lname and
506 +.B \-ilname
507 +predicates always to return false.
508 +
509 +.IP "\-help, \-\-help"
510 +Print a summary of the command-line usage of
511 +.B find
512 +and exit.
513 +
514 +.IP \-ignore_readdir_race
515 +Normally, \fBfind\fR will emit an error message when it fails to stat a file.
516 +If you give this option and a file is deleted between the time \fBfind\fR
517 +reads the name of the file from the directory and the time it tries to stat
518 +the file, no error message will be issued. This also applies to files
519 +or directories whose names are given on the command line. This option takes
520 +effect at the time the command line is read, which means that you cannot search
521 +one part of the filesystem with this option on and part of it with this option
522 +off (if you need to do that, you will need to issue two \fBfind\fR commands
523 +instead, one with the option and one without it).
524 +
525 +.IP "\-maxdepth \fIlevels\fR"
526 +Descend at most \fIlevels\fR (a non-negative integer) levels of
527 +directories below the command line arguments.
528 +.B \-maxdepth 0
529 + means only apply the tests and actions to the command line arguments.
530 +
531 +.IP "\-mindepth \fIlevels\fR"
532 +Do not apply any tests or actions at levels less than \fIlevels\fR (a
533 +non-negative integer).
534 +.B \-mindepth 1
535 +means process all files except the command line arguments.
536 +
537 +.IP \-mount
538 +Don't descend directories on other filesystems. An alternate name for
539 +.BR \-xdev ,
540 +for compatibility with some other versions of
541 +.BR find .
542 +
543 +.IP \-noignore_readdir_race
544 +Turns off the effect of
545 +.BR \-ignore_readdir_race .
546 +
547 +.IP "\-noleaf"
548 +Do not optimize by assuming that directories contain 2 fewer
549 +subdirectories than their hard link count. This option is needed when
550 +searching filesystems that do not follow the Unix directory-link
551 +convention, such as CD-ROM or MS-DOS filesystems or AFS volume mount
552 +points. Each directory on a normal Unix filesystem has at least 2
553 +hard links: its name and its `.' entry. Additionally, its
554 +subdirectories (if any) each have a `..' entry linked to that
555 +directory. When
556 +.B find
557 +is examining a directory, after it has statted 2 fewer subdirectories
558 +than the directory's link count, it knows that the rest of the entries
559 +in the directory are non-directories (`leaf' files in the directory
560 +tree). If only the files' names need to be examined, there is no need
561 +to stat them; this gives a significant increase in search speed.
562 +
563 +.IP "\-regextype \fItype\fR"
564 +Changes the regular expression syntax understood by
565 +.B \-regex
566 +and
567 +.B \-iregex
568 +tests which occur later on the command line. Currently-implemented
569 +types are emacs (this is the default), posix-awk, posix-basic,
570 +posix-egrep and posix-extended.
571 +
572 +.IP "\-version, \-\-version"
573 +Print the \fBfind\fR version number and exit.
574 +
575 +.IP "\-warn, \-nowarn"
576 +Turn warning messages on or off. These warnings apply only to the
577 +command line usage, not to any conditions that
578 +.B find
579 +might encounter when it searches directories. The default behaviour
580 +corresponds to
581 +.B \-warn
582 +if standard input is a tty, and to
583 +.B \-nowarn
584 +otherwise.
585 +
586 +.IP \-xdev
587 +Don't descend directories on other filesystems.
588 +
589 +.SS TESTS
590 +Some tests, for example
591 +.B \-newerXY
592 +and
593 +.BR -samefile ,
594 +allow comparison between the file currently being examined and some
595 +reference file specified on the command line. When these tests are
596 +used, the interpretation of the reference file is determined by the
597 +options
598 +.BR \-H ,
599 +.B \-L
600 +and
601 +.B \-P
602 +and any previous
603 +.BR \-follow ,
604 +but the reference file is only examined once, at the time the command
605 +line is parsed. If the reference file cannot be examined (for
606 +example, the
607 +.BR stat (2)
608 +system call fails for it), an error message is issued, and
609 +.B find
610 +exits with a nonzero status.
611 +.P
612 +Numeric arguments can be specified as
613 +.IP \fI+n\fP
614 +for greater than
615 +.IR n ,
616 +.IP \fI\-n\fP
617 +for less than
618 +.IR n ,
619 +.IP \fIn\fP
620 +for exactly
621 +.IR n .
622 +.P
623 +
624 +.IP "\-amin \fIn\fR"
625 +File was last accessed \fIn\fR minutes ago.
626 +
627 +.IP "\-anewer \fIfile\fR"
628 +File was last accessed more recently than \fIfile\fR was modified. If
629 +\fIfile\fR is a symbolic link and the
630 +.B \-H
631 +option or the
632 +.B \-L
633 +option is in effect, the access time of the file it points to is
634 +always used.
635 +
636 +.IP "\-atime \fIn\fR"
637 +File was last accessed \fIn\fR*24 hours ago.
638 +When find figures out how many 24-hour periods ago the file
639 +was last accessed, any fractional part is ignored, so to match
640 +.B \-atime
641 +.BR +1 ,
642 +a file has to have been accessed at least
643 +.I two
644 +days ago.
645 +
646 +.IP "\-cmin \fIn\fR"
647 +File's status was last changed \fIn\fR minutes ago.
648 +
649 +.IP "\-cnewer \fIfile\fR"
650 +File's status was last changed more recently than \fIfile\fR was
651 +modified. If \fIfile\fR is a symbolic link and the
652 +.B \-H
653 +option or the
654 +.B \-L
655 +option is in effect, the status-change time of the file it points
656 +to is always used.
657 +
658 +.IP "\-ctime \fIn\fR"
659 +File's status was last changed \fIn\fR*24 hours ago.
660 +See the comments for
661 +.B \-atime
662 +to understand how rounding affects the interpretation of file status
663 +change times.
664 +
665 +.IP \-empty
666 +File is empty and is either a regular file or a directory.
667 +
668 +.IP \-executable
669 +Matches files which are executable and directories which are
670 +searchable (in a file name resolution sense). This takes into account
671 +access control lists and other permissions artefacts which the
672 +.B \-perm
673 +test ignores. This test makes use of the
674 +.BR access (2)
675 +system call, and so can be fooled by NFS servers which do UID
676 +mapping (or root-squashing), since many systems implement
677 +.BR access (2)
678 +in the client's kernel and so cannot make use of the UID mapping
679 +information held on the server. Because this test is based only on
680 +the result of the
681 +.BR access (2)
682 +system call, there is no guarantee that a file for which this test
683 +succeeds can actually be executed.
684 +
685 +.IP \-false
686 +Always false.
687 +
688 +.IP "\-fstype \fItype\fR"
689 +File is on a filesystem of type \fItype\fR. The valid filesystem
690 +types vary among different versions of Unix; an incomplete list of
691 +filesystem types that are accepted on some version of Unix or another
692 +is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use
693 +.B \-printf
694 +with the %F directive to see the types of your filesystems.
695 +
696 +.IP "\-gid \fIn\fR"
697 +File's numeric group ID is \fIn\fR.
698 +
699 +.IP "\-group \fIgname\fR"
700 +File belongs to group \fIgname\fR (numeric group ID allowed).
701 +
702 +.IP "\-ilname \fIpattern\fR"
703 +Like
704 +.BR \-lname ,
705 +but the match is case insensitive.
706 +If the
707 +.B \-L
708 +option or the
709 +.B \-follow
710 +option is in effect, this test returns false unless the symbolic link
711 +is broken.
712 +
713 +.IP "\-iname \fIpattern\fR"
714 +Like
715 +.BR \-name ,
716 +but the match is case insensitive. For example, the
717 +patterns `fo*' and `F??' match the file names `Foo', `FOO', `foo',
718 +`fOo', etc. In these patterns, unlike filename expansion by the
719 +shell, an initial '.' can be matched by `*'. That is,
720 +.B find \-name *bar
721 +will match the file `.foobar'. Please note that you should quote
722 +patterns as a matter of course, otherwise the shell will expand any
723 +wildcard characters in them.
724 +
725 +.IP "\-inum \fIn\fR"
726 +File has inode number \fIn\fR. It is normally easier to use the
727 +.B \-samefile
728 +test instead.
729 +
730 +.IP "\-ipath \fIpattern\fR"
731 +Behaves in the same way as
732 +.BR \-iwholename .
733 +This option is deprecated, so please do not use it.
734 +
735 +.IP "\-iregex \fIpattern\fR"
736 +Like
737 +.BR \-regex ,
738 +but the match is case insensitive.
739 +
740 +.IP "\-iwholename \fIpattern\fR"
741 +Like
742 +.BR \-wholename ,
743 +but the match is case insensitive.
744 +
745 +.IP "\-links \fIn\fR"
746 +File has \fIn\fR links.
747 +
748 +.IP "\-lname \fIpattern\fR"
749 +File is a symbolic link whose contents match shell pattern
750 +\fIpattern\fR. The metacharacters do not treat `/' or `.' specially.
751 +If the
752 +.B \-L
753 +option or the
754 +.B \-follow
755 +option is in effect, this test returns false unless the symbolic link
756 +is broken.
757 +
758 +.IP "\-mmin \fIn\fR"
759 +File's data was last modified \fIn\fR minutes ago.
760 +
761 +.IP "\-mtime \fIn\fR"
762 +File's data was last modified \fIn\fR*24 hours ago.
763 +See the comments for
764 +.B \-atime
765 +to understand how rounding affects the interpretation of file
766 +modification times.
767 +
768 +.IP "\-name \fIpattern\fR"
769 +Base of file name (the path with the leading directories removed)
770 +matches shell pattern \fIpattern\fR. The metacharacters (`*', `?',
771 +and `[]') match a `.' at the start of the base name (this is a change
772 +in findutils-4.2.2; see section STANDARDS CONFORMANCE below). To ignore a
773 +directory and the files under it, use
774 +.BR \-prune ;
775 +see an example in the
776 +description of
777 +.BR \-path .
778 +Braces are not recognised as being
779 +special, despite the fact that some shells including Bash imbue braces
780 +with a special meaning in shell patterns. The filename matching is
781 +performed with the use of the
782 +.BR fnmatch (3)
783 +library function. Don't forget to enclose the pattern in quotes
784 +in order to protect it from expansion by the shell.
785 +
786 +.IP "\-newer \fIfile\fR"
787 +File was modified more recently than \fIfile\fR. If \fIfile\fR is a
788 +symbolic link and the
789 +.B \-H
790 +option or the
791 +.B \-L
792 +option is in effect, the
793 +modification time of the file it points to is always used.
794 +
795 +.IP "\-newerXY \fIreference\fR"
796 +Compares the timestamp of the current file with \fIreference\fR.
797 +The
798 +.I reference
799 +argument is normally the name of a file (and one of its timestamps is
800 +used for the comparison) but it may also be a string describing an
801 +absolute time.
802 +.I X
803 +and
804 +.I Y
805 +are placeholders for other letters, and these letters select which
806 +time belonging to
807 +how
808 +.I reference
809 +is used for the comparison.
810 +.TS
811 +ll
812 +ll
813 +ll
814 +ll
815 +llw(2i).
816 +a The access time of the file \fIreference\fR
817 +B The birth time of the file \fIreference\fR
818 +c The inode status change time of \fIreference\fR
819 +m The modification time of the file \fIreference\fR
820 +t \fIreference\fR is interpreted directly as a time
821 +.TE
822 +
823 +Some combinations are invalid; for example, it is invalid for
824 +.I X
825 +to be
826 +.IR t .
827 +Some combinations are not implemented on all systems; for example
828 +.I B
829 +is not supported on all systems. If an invalid or unsupported
830 +combination of
831 +.I XY
832 +is specified, a fatal error results. Time specifications are
833 +interpreted as for the argument to the
834 +.B \-d
835 +option of GNU
836 +.BR date .
837 +If you try to use the birth time of a reference file, and the birth
838 +time cannot be determined, a fatal error message results. If you
839 +specify a test which refers to the birth time of files being examined,
840 +this test will fail for any files where the birth time is unknown.
841 +
842 +.IP \-nogroup
843 +No group corresponds to file's numeric group ID.
844 +
845 +.IP \-nouser
846 +No user corresponds to file's numeric user ID.
847 +
848 +.IP "\-path \fIpattern\fR"
849 +File name matches shell pattern \fIpattern\fR. The metacharacters do
850 +not treat `/' or `.' specially; so, for example,
851 +.br
852 +.in +1i
853 +find . \-path "./sr*sc"
854 +.br
855 +.in -1i
856 +will print an entry for a directory called `./src/misc' (if one
857 +exists). To ignore a whole directory tree, use
858 +.B \-prune
859 +rather than
860 +checking every file in the tree. For example, to skip the
861 +directory `src/emacs' and all files and directories under it, and
862 +print the names of the other files found, do something like this:
863 +.br
864 +.in +1i
865 +find . \-path ./src/emacs \-prune \-o \-print
866 +.br
867 +.in -1i
868 +Note that the pattern match test applies to the whole file name,
869 +starting from one of the start points named on the command line. It
870 +would only make sense to use an absolute path name here if the
871 +relevant start point is also an absolute path. This means that this
872 +command will never match anything:
873 +.br
874 +.in +1i
875 +find bar \-path /foo/bar/myfile \-print
876 +.br
877 +.in -1i
878 +The predicate
879 +.B \-path
880 +is also supported by HP-UX
881 +.B find
882 +and will be in a forthcoming version of the POSIX standard.
883 +
884 +.IP "\-perm \fImode\fR"
885 +File's permission bits are exactly \fImode\fR (octal or symbolic).
886 +Since an exact match is required, if you want to use this form for
887 +symbolic modes, you may have to specify a rather complex mode string.
888 +For example
889 +.B \-perm g=w
890 +will only match files which have mode 0020
891 +(that is, ones for which group write permission is the only permission
892 +set). It is more likely that you will want to use the `/' or `-'
893 +forms, for example
894 +.BR "\-perm \-g=w" ,
895 +which matches any file with group write permission. See the
896 +.B EXAMPLES
897 +section for some illustrative examples.
898 +
899 +.IP "\-perm \-\fImode\fR"
900 +All of the permission bits \fImode\fR are set for the file.
901 +Symbolic modes are accepted in this form, and this is usually the way
902 +in which would want to use them. You must specify `u', `g' or `o' if
903 +you use a symbolic mode. See the
904 +.B EXAMPLES
905 +section for some illustrative examples.
906 +
907 +.IP "\-perm /\fImode\fR"
908 +Any of the permission bits \fImode\fR are set for the file. Symbolic
909 +modes are accepted in this form. You must specify `u', `g' or `o' if
910 +you use a symbolic mode. See the
911 +.B EXAMPLES
912 +section for some illustrative examples. If no permission bits in
913 +.I mode
914 +are set, this test currently matches no files. However, it will soon
915 +be changed to match any file (the idea is to be more consistent with
916 +the behaviour of
917 +.B \-perm
918 +.BR \-000 ).
919 +
920 +.IP "\-perm +\fImode\fR"
921 +Deprecated, old way of searching for files with any of the permission
922 +bits in \fImode\fR set. You should use
923 +.B \-perm \fI/mode\fR
924 +instead. Trying to use the `+' syntax with symbolic modes will yield
925 +surprising results. For example, `+u+x' is a valid symbolic mode
926 +(equivalent to +u,+x, i.e. 0111) and will therefore not be evaluated
927 +as
928 +.B \-perm +\fImode\fR
929 +but instead as the exact mode specifier
930 +.B \-perm \fImode\fR
931 +and so it matches files with exact permissions 0111 instead of files with any
932 +execute bit set. If you found this paragraph confusing, you're not
933 +alone - just use
934 +.B \-perm /\fImode\fR.
935 +This form of the
936 +.B \-perm
937 +test is deprecated because the POSIX specification requires the
938 +interpretation of a leading `+' as being part of a symbolic mode, and
939 +so we switched to using `/' instead.
940 +
941 +.IP \-readable
942 +Matches files which are readable. This takes into account access
943 +control lists and other permissions artefacts which the
944 +.B \-perm
945 +test ignores. This test makes use of the
946 +.BR access (2)
947 +system call, and so can be fooled by NFS servers which do UID
948 +mapping (or root-squashing), since many systems implement
949 +.BR access (2)
950 +in the client's kernel and so cannot make use of the UID mapping
951 +information held on the server.
952 +
953 +.IP "\-regex \fIpattern\fR"
954 +File name matches regular expression \fIpattern\fR. This is a match
955 +on the whole path, not a search. For example, to match a file named
956 +`./fubar3', you can use the regular expression `.*bar.' or `.*b.*3',
957 +but not `f.*r3'. The regular expressions understood by
958 +.B find
959 +are by default Emacs Regular Expressions, but this can be
960 +changed with the
961 +.B \-regextype
962 +option.
963 +
964 +.IP "\-samefile \fIname\fR"
965 +File refers to the same inode as \fIname\fR. When
966 +.B \-L
967 +is in effect, this can include symbolic links.
968 +
969 +.IP "\-size \fIn\fR[cwbkMG]"
970 +File uses \fIn\fP units of space. The following suffixes
971 +can be used:
972 +.RS
973 +.IP `b'
974 +for 512-byte blocks (this is the default if no suffix is used)
975 +.IP `c'
976 +for bytes
977 +.IP `w'
978 +for two-byte words
979 +.IP `k'
980 +for Kilobytes (units of 1024 bytes)
981 +.IP `M'
982 +for Megabytes (units of 1048576 bytes)
983 +.IP `G'
984 +for Gigabytes (units of 1073741824 bytes)
985 +.RE
986 +.IP
987 +The size does not count indirect blocks, but it does count blocks in
988 +sparse files that are not actually allocated. Bear in mind that the
989 +`%k' and `%b' format specifiers of
990 +.B \-printf
991 +handle sparse files
992 +differently. The `b' suffix always denotes 512-byte blocks and never
993 +1 Kilobyte blocks, which is different to the behaviour of
994 +.BR \-ls .
995 +
996 +.IP \-true
997 +Always true.
998 +
999 +.IP "\-type \fIc\fR"
1000 +File is of type \fIc\fR:
1001 +.RS
1002 +.IP b
1003 +block (buffered) special
1004 +.IP c
1005 +character (unbuffered) special
1006 +.IP d
1007 +directory
1008 +.IP p
1009 +named pipe (FIFO)
1010 +.IP f
1011 +regular file
1012 +.IP l
1013 +symbolic link; this is never true if the
1014 +.B \-L
1015 +option or the
1016 +.B \-follow
1017 +option is in effect, unless the symbolic link is broken. If you want
1018 +to search for symbolic links when
1019 +.B \-L
1020 +is in effect, use
1021 +.BR \-xtype .
1022 +.IP s
1023 +socket
1024 +.IP D
1025 +door (Solaris)
1026 +.RE
1027 +.IP "\-uid \fIn\fR"
1028 +File's numeric user ID is \fIn\fR.
1029 +
1030 +.IP "\-used \fIn\fR"
1031 +File was last accessed \fIn\fR days after its status was last changed.
1032 +
1033 +.IP "\-user \fIuname\fR"
1034 +File is owned by user \fIuname\fR (numeric user ID allowed).
1035 +
1036 +.IP "\-wholename \fIpattern\fR"
1037 +See \-path. This alternative is less portable than
1038 +.BR \-path .
1039 +
1040 +.IP "\-writable"
1041 +Matches files which are writable. This takes into account access
1042 +control lists and other permissions artefacts which the
1043 +.B \-perm
1044 +test ignores. This test makes use of the
1045 +.BR access (2)
1046 +system call, and so can be fooled by NFS servers which do UID
1047 +mapping (or root-squashing), since many systems implement
1048 +.BR access (2)
1049 +in the client's kernel and so cannot make use of the UID mapping
1050 +information held on the server.
1051 +
1052 +.IP "\-xtype \fIc\fR"
1053 +The same as
1054 +.B \-type
1055 +unless the file is a symbolic link. For symbolic
1056 +links: if the
1057 +.B \-H
1058 +or
1059 +.B \-P
1060 +option was specified, true if the file is a
1061 +link to a file of type \fIc\fR; if the
1062 +.B \-L
1063 +option has been given, true
1064 +if \fIc\fR is `l'. In other words, for symbolic links,
1065 +.B \-xtype
1066 +checks the type of the file that
1067 +.B \-type
1068 +does not check.
1069 +
1070 +.SS ACTIONS
1071 +.IP "\-delete\fR"
1072 +Delete files; true if removal succeeded. If the removal failed, an
1073 +error message is issued.
1074 +If
1075 +.B \-delete
1076 +fails,
1077 +.BR find 's
1078 +exit status will be nonzero
1079 +(when it eventually exits).
1080 +Use of
1081 +.B \-delete
1082 +automatically turns on the
1083 +.B \-depth
1084 +option.
1085 +
1086 +.BR Warnings :
1087 +Don't forget that the find command line is
1088 +evaluated as an expression, so putting
1089 +.B \-delete
1090 +first will make
1091 +.B find
1092 +try to delete everything below the starting points you specified.
1093 +When testing a
1094 +.B find
1095 +command line that you later intend to use with
1096 +.BR \-delete ,
1097 +you should explicitly specify
1098 +.B \-depth
1099 +in order to avoid later surprises. Because
1100 +.B \-delete
1101 +implies
1102 +.BR \-depth ,
1103 +you cannot usefully use
1104 +.B \-prune
1105 +and
1106 +.B \-delete
1107 +together.
1108 +
1109 +.IP "\-exec \fIcommand\fR ;"
1110 +Execute \fIcommand\fR; true if 0 status is returned. All following
1111 +arguments to
1112 +.B find
1113 +are taken to be arguments to the command until an argument consisting
1114 +of `;' is encountered. The string `{}' is replaced by the current
1115 +file name being processed everywhere it occurs in the arguments to the
1116 +command, not just in arguments where it is alone, as in some versions
1117 +of
1118 +.BR find .
1119 +Both of these constructions might need to be escaped (with a `\e') or
1120 +quoted to protect them from expansion by the shell. See the
1121 +.B EXAMPLES
1122 +section for examples of the use of the
1123 +.B \-exec
1124 +option. The specified
1125 +command is run once for each matched file.
1126 +The command is executed in the starting directory. There are
1127 +unavoidable security problems surrounding use of the
1128 +.B \-exec
1129 +action;
1130 +you should use the
1131 +.B \-execdir
1132 +option instead.
1133 +
1134 +.IP "\-exec \fIcommand\fR {} +"
1135 +This variant of the
1136 +.B \-exec
1137 +action runs the specified command on the
1138 +selected files, but the command line is built by appending each
1139 +selected file name at the end; the total number of invocations of the
1140 +command will be much less than the number of matched files. The
1141 +command line is built in much the same way that
1142 +.B xargs
1143 +builds its command lines. Only one instance of `{}' is allowed within
1144 +the command. The command is executed in the starting directory.
1145 +
1146 +.IP "\-execdir \fIcommand\fR ;"
1147 +.IP "\-execdir \fIcommand\fR {} +"
1148 +Like
1149 +.BR \-exec ,
1150 +but the specified command is run from the subdirectory
1151 +containing the matched file, which is not normally the directory in
1152 +which you started
1153 +.BR find .
1154 +This a much more secure method for invoking commands, as it avoids
1155 +race conditions during resolution of the paths to the matched files.
1156 +As with the
1157 +.B \-exec
1158 +action, the `+' form of
1159 +.B \-execdir
1160 +will build a
1161 +command line to process more than one matched file, but any given
1162 +invocation of
1163 +.I command
1164 +will only list files that exist in the same subdirectory. If you use
1165 +this option, you must ensure that your
1166 +.B $PATH
1167 +environment variable does not reference `.';
1168 +otherwise, an attacker can run any commands they like by leaving an
1169 +appropriately-named file in a directory in which you will run
1170 +.BR \-execdir .
1171 +The same applies to having entries in
1172 +.B $PATH
1173 +which are empty or which are not absolute directory names.
1174 +
1175 +.IP "\-fls \fIfile\fR"
1176 +True; like
1177 +.B \-ls
1178 +but write to \fIfile\fR like
1179 +.BR \-fprint .
1180 +The output file is always created, even if the predicate is never
1181 +matched.
1182 +See the
1183 +.B UNUSUAL FILENAMES
1184 +section for information about how unusual characters in filenames are handled.
1185 +
1186 +.IP "\-fprint \fIfile\fR"
1187 +True; print the full file name into file \fIfile\fR. If \fIfile\fR
1188 +does not exist when \fBfind\fR is run, it is created; if it does
1189 +exist, it is truncated. The file names ``/dev/stdout'' and
1190 +``/dev/stderr'' are handled specially; they refer to the standard
1191 +output and standard error output, respectively.
1192 +The output file is always created, even if the predicate is never matched.
1193 +See the
1194 +.B UNUSUAL FILENAMES
1195 +section for information about how unusual characters in filenames are handled.
1196 +
1197 +.IP "\-fprint0 \fIfile\fR"
1198 +True; like
1199 +.B \-print0
1200 +but write to \fIfile\fR like
1201 +.BR \-fprint .
1202 +The output file is always created, even if the predicate is never matched.
1203 +See the
1204 +.B UNUSUAL FILENAMES
1205 +section for information about how unusual characters in filenames are handled.
1206 +
1207 +.IP "\-fprintf \fIfile\fR \fIformat\fR"
1208 +True; like
1209 +.B \-printf
1210 +but write to \fIfile\fR like
1211 +.BR \-fprint .
1212 +The output file is always created, even if the predicate is never matched.
1213 +See the
1214 +.B UNUSUAL FILENAMES
1215 +section for information about how unusual characters in filenames are handled.
1216 +
1217 +.IP \-ls
1218 +True; list current file in
1219 +.B ls \-dils
1220 +format on standard output.
1221 +The block counts are of 1K blocks, unless the environment variable
1222 +POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
1223 +See the
1224 +.B UNUSUAL FILENAMES
1225 +section for information about how unusual characters in filenames are handled.
1226 +
1227 +.IP "\-ok \fIcommand\fR ;"
1228 +Like
1229 +.B \-exec
1230 +but ask the user first (on the standard input); if the
1231 +response does not start with `y' or `Y', do not run the command, and
1232 +return false. If the command is run, its standard input is redirected
1233 +from
1234 +.BR /dev/null .
1235 +
1236 +.IP "\-okdir \fIcommand\fR ;"
1237 +Like
1238 +.B \-execdir
1239 +but ask the user first (on the standard input); if the
1240 +response does not start with `y' or `Y', do not run the command, and
1241 +return false. If the command is run, its standard input is redirected
1242 +from
1243 +.BR /dev/null .
1244 +
1245 +.IP \-print
1246 +True; print the full file name on the standard output, followed by a
1247 +newline. If you are piping the output of
1248 +.B find
1249 +into another program and there is the faintest possibility that the files
1250 +which you are searching for might contain a newline, then you should
1251 +seriously consider using the
1252 +.B \-print0
1253 +option instead of
1254 +.BR \-print .
1255 +See the
1256 +.B UNUSUAL FILENAMES
1257 +section for information about how unusual characters in filenames are handled.
1258 +
1259 +.IP \-print0
1260 +True; print the full file name on the standard output, followed by a
1261 +null character (instead of the newline character that
1262 +.B \-print
1263 +uses).
1264 +This allows file names that contain newlines or other types of white
1265 +space to be correctly interpreted by programs that process the
1266 +\fBfind\fR output. This option corresponds to the
1267 +.B \-0
1268 +option of
1269 +.BR xargs .
1270 +
1271 +.IP "\-printf \fIformat\fR"
1272 +True; print \fIformat\fR on the standard output, interpreting `\e'
1273 +escapes and `%' directives. Field widths and precisions can be
1274 +specified as with the `printf' C function. Please note that many of
1275 +the fields are printed as %s rather than %d, and this may mean that
1276 +flags don't work as you might expect. This also means that the `\-'
1277 +flag does work (it forces fields to be left-aligned). Unlike
1278 +.BR \-print ,
1279 +.B \-printf
1280 +does not add a newline at the end of the string. The escapes
1281 +and directives are:
1282 +.RS
1283 +.IP \ea
1284 +Alarm bell.
1285 +.IP \eb
1286 +Backspace.
1287 +.IP \ec
1288 +Stop printing from this format immediately and flush the output.
1289 +.IP \ef
1290 +Form feed.
1291 +.IP \en
1292 +Newline.
1293 +.IP \er
1294 +Carriage return.
1295 +.IP \et
1296 +Horizontal tab.
1297 +.IP \ev
1298 +Vertical tab.
1299 +.IP \e\0
1300 +ASCII NUL.
1301 +.IP \e\e
1302 +A literal backslash (`\e').
1303 +.IP \eNNN
1304 +The character whose ASCII code is NNN (octal).
1305 +.PP
1306 +A `\e' character followed by any other character is treated as an
1307 +ordinary character, so they both are printed.
1308 +.IP %%
1309 +A literal percent sign.
1310 +.IP %a
1311 +File's last access time in the format returned by the C `ctime' function.
1312 +.IP %A\fIk\fP
1313 +File's last access time in the format specified by \fIk\fR, which is
1314 +either `@' or a directive for the C `strftime' function. The possible
1315 +values for \fIk\fR are listed below; some of them might not be
1316 +available on all systems, due to differences in `strftime' between
1317 +systems.
1318 +.RS
1319 +.IP @
1320 +seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
1321 +.PP
1322 +Time fields:
1323 +.IP H
1324 +hour (00..23)
1325 +.IP I
1326 +hour (01..12)
1327 +.IP k
1328 +hour ( 0..23)
1329 +.IP l
1330 +hour ( 1..12)
1331 +.IP M
1332 +minute (00..59)
1333 +.IP p
1334 +locale's AM or PM
1335 +.IP r
1336 +time, 12-hour (hh:mm:ss [AP]M)
1337 +.IP S
1338 +Second (00.00 .. 61.00). There is a fractional part.
1339 +.IP T
1340 +time, 24-hour (hh:mm:ss)
1341 +.IP +
1342 +Date and time, separated by `+', for example
1343 +`2004\-04\-28+22:22:05.0'. This is a GNU extension. The time is
1344 +given in the current timezone (which may be affected by setting the TZ
1345 +environment variable). The seconds field includes a fractional part.
1346 +.IP X
1347 +locale's time representation (H:M:S)
1348 +.IP Z
1349 +time zone (e.g., EDT), or nothing if no time zone is determinable
1350 +.PP
1351 +Date fields:
1352 +.IP a
1353 +locale's abbreviated weekday name (Sun..Sat)
1354 +.IP A
1355 +locale's full weekday name, variable length (Sunday..Saturday)
1356 +.IP b
1357 +locale's abbreviated month name (Jan..Dec)
1358 +.IP B
1359 +locale's full month name, variable length (January..December)
1360 +.IP c
1361 +locale's date and time (Sat Nov 04 12:02:33 EST 1989). The format is
1362 +the same as for
1363 +.BR ctime (3)
1364 +and so to preserve compatibility with that format, there is no fractional part
1365 +in the seconds field.
1366 +.IP d
1367 +day of month (01..31)
1368 +.IP D
1369 +date (mm/dd/yy)
1370 +.IP h
1371 +same as b
1372 +.IP j
1373 +day of year (001..366)
1374 +.IP m
1375 +month (01..12)
1376 +.IP U
1377 +week number of year with Sunday as first day of week (00..53)
1378 +.IP w
1379 +day of week (0..6)
1380 +.IP W
1381 +week number of year with Monday as first day of week (00..53)
1382 +.IP x
1383 +locale's date representation (mm/dd/yy)
1384 +.IP y
1385 +last two digits of year (00..99)
1386 +.IP Y
1387 +year (1970...)
1388 +.RE
1389 +.IP %b
1390 +The amount of disk space used for this file in 512-byte blocks. Since disk
1391 +space is allocated in multiples of the filesystem block size this is usually
1392 +greater than %s/512, but it can also be smaller if the file is a sparse file.
1393 +.IP %c
1394 +File's last status change time in the format returned by the C `ctime'
1395 +function.
1396 +.IP %C\fIk\fP
1397 +File's last status change time in the format specified by \fIk\fR,
1398 +which is the same as for %A.
1399 +.IP %d
1400 +File's depth in the directory tree; 0 means the file is a command line
1401 +argument.
1402 +.IP %D
1403 +The device number on which the file exists (the st_dev field of struct
1404 +stat), in decimal.
1405 +.IP %f
1406 +File's name with any leading directories removed (only the last element).
1407 +.IP %F
1408 +Type of the filesystem the file is on; this value can be used for
1409 +\-fstype.
1410 +.IP %g
1411 +File's group name, or numeric group ID if the group has no name.
1412 +.IP %G
1413 +File's numeric group ID.
1414 +.IP %h
1415 +Leading directories of file's name (all but the last element).
1416 +If the file name contains no slashes (since it is in the current
1417 +directory) the %h specifier expands to ".".
1418 +.IP %H
1419 +Command line argument under which file was found.
1420 +.IP %i
1421 +File's inode number (in decimal).
1422 +.IP %k
1423 +The amount of disk space used for this file in 1K blocks. Since disk space is
1424 +allocated in multiples of the filesystem block size this is usually greater
1425 +than %s/1024, but it can also be smaller if the file is a sparse file.
1426 +.IP %l
1427 +Object of symbolic link (empty string if file is not a symbolic link).
1428 +.IP %m
1429 +File's permission bits (in octal). This option uses the `traditional'
1430 +numbers which most Unix implementations use, but if your particular
1431 +implementation uses an unusual ordering of octal permissions bits, you
1432 +will see a difference between the actual value of the file's mode and
1433 +the output of %m. Normally you will want to have a leading
1434 +zero on this number, and to do this, you should use the
1435 +.B #
1436 +flag (as in, for example, `%#m').
1437 +.IP %M
1438 +File's permissions (in symbolic form, as for
1439 +.BR ls ).
1440 +This directive is supported in findutils 4.2.5 and later.
1441 +.IP %n
1442 +Number of hard links to file.
1443 +.IP %p
1444 +File's name.
1445 +.IP %P
1446 +File's name with the name of the command line argument under which
1447 +it was found removed.
1448 +.IP %s
1449 +File's size in bytes.
1450 +.IP %S
1451 +File's sparseness. This is calculated as (BLOCKSIZE*st_blocks /
1452 +st_size). The exact value you will get for an ordinary file of a
1453 +certain length is system-dependent. However, normally sparse files
1454 +will have values less than 1.0, and files which use indirect blocks
1455 +may have a value which is greater than 1.0. The value used for
1456 +BLOCKSIZE is system-dependent, but is usually 512 bytes. If the file
1457 +size is zero, the value printed is undefined. On systems which lack
1458 +support for st_blocks, a file's sparseness is assumed to be 1.0.
1459 +.IP %t
1460 +File's last modification time in the format returned by the C `ctime'
1461 +function.
1462 +.IP %T\fIk\fP
1463 +File's last modification time in the format specified by \fIk\fR,
1464 +which is the same as for %A.
1465 +.IP %u
1466 +File's user name, or numeric user ID if the user has no name.
1467 +.IP %U
1468 +File's numeric user ID.
1469 +.IP %y
1470 +File's type (like in
1471 +.BR "ls \-l" ),
1472 +U=unknown type (shouldn't happen)
1473 +.IP %Y
1474 +File's type (like %y), plus follow symlinks: L=loop, N=nonexistent
1475 +.PP
1476 +A `%' character followed by any other character is discarded, but the
1477 +other character is printed (don't rely on this, as further format
1478 +characters may be introduced). A `%' at the end of the format
1479 +argument causes undefined behaviour since there is no following
1480 +character. In some locales, it may hide your door keys, while in
1481 +others it may remove the final page from the novel you are reading.
1482 +
1483 +The %m and %d directives support the
1484 +.B #
1485 +,
1486 +.B 0
1487 +and
1488 +.B +
1489 +flags, but the other directives do not, even if they
1490 +print numbers. Numeric directives that do not support these flags
1491 +include
1492 +.BR G ,
1493 +.BR U ,
1494 +.BR b ,
1495 +.BR D ,
1496 +.B k
1497 +and
1498 +.BR n .
1499 +The `\-' format flag is supported and changes the alignment of a field
1500 +from right-justified (which is the default) to left-justified.
1501 +.PP
1502 +See the
1503 +.B UNUSUAL FILENAMES
1504 +section for information about how unusual characters in filenames are handled.
1505 +
1506 +
1507 +.RE
1508 +.IP \-prune
1509 +True; if the file is a directory, do not descend into it. If
1510 +.B \-depth
1511 +is given, false; no effect. Because
1512 +.B \-delete
1513 +implies
1514 +.BR \-depth ,
1515 +you cannot usefully use
1516 +.B \-prune
1517 +and
1518 +.B \-delete together.
1519 +
1520 +.IP "\-quit"
1521 +Exit immediately. No child processes will be left running, but no more
1522 +paths specified on the command line will be processed. For example,
1523 +.B find /tmp/foo /tmp/bar \-print \-quit
1524 +will print only
1525 +.BR /tmp/foo .
1526 +Any command lines which have been built up with
1527 +.B \-execdir ... {} +
1528 +will be invoked before
1529 +.B find
1530 +exits. The exit status may or may not be zero, depending on whether
1531 +an error has already occurred.
1532 +
1533 +.SS UNUSUAL FILENAMES
1534 +Many of the actions of
1535 +.B find
1536 +result in the printing of data which is under the control of other
1537 +users. This includes file names, sizes, modification times and so
1538 +forth. File names are a potential problem since they can contain any
1539 +character except `\e0' and `/'. Unusual characters in file names can
1540 +do unexpected and often undesirable things to your terminal (for
1541 +example, changing the settings of your function keys on some
1542 +terminals). Unusual characters are handled differently by various
1543 +actions, as described below.
1544 +
1545 +.IP "\-print0, \-fprint0\"
1546 +Always print the exact filename, unchanged, even if the output is
1547 +going to a terminal.
1548 +
1549 +.IP "\-ls, \-fls"
1550 +Unusual characters are always escaped. White space, backslash, and
1551 +double quote characters are printed using C-style escaping (for
1552 +example `\ef', `\e"'). Other unusual characters are printed using an
1553 +octal escape. Other printable characters (for
1554 +.B \-ls
1555 +and
1556 +.B \-fls
1557 +these are the characters between octal 041 and 0176) are printed as-is.
1558 +
1559 +.IP "\-printf, \-fprintf"
1560 +If the output is not going to a terminal, it is printed as-is.
1561 +Otherwise, the result depends on which directive is in use. The
1562 +directives %D, %F, %g, %G, %H, %Y, and %y expand to values which are
1563 +not under control of files' owners, and so are printed as-is. The
1564 +directives %a, %b, %c, %d, %i, %k, %m, %M, %n, %s, %t, %u and %U have
1565 +values which are under the control of files' owners but which cannot
1566 +be used to send arbitrary data to the terminal, and so these are
1567 +printed as-is. The directives %f, %h, %l, %p and %P are quoted. This
1568 +quoting is performed in the same way as for GNU
1569 +.BR ls .
1570 +This is not the same quoting mechanism as the one used for
1571 +.B \-ls
1572 +and
1573 +.BR \-fls .
1574 +If you are able to decide what format to use for the output of
1575 +.B find
1576 +then it is normally better to use `\e0' as a terminator
1577 +than to use newline, as file names can contain white space and newline
1578 +characters.
1579 +
1580 +.IP "\-print, \-fprint"
1581 +Quoting is handled in the same way as for
1582 +.B \-printf
1583 +and
1584 +.BR \-fprintf .
1585 +If you are using
1586 +.B find
1587 +in a script or in a situation where the matched files might have
1588 +arbitrary names, you should consider using
1589 +.B \-print0
1590 +instead of
1591 +.BR \-print .
1592 +.P
1593 +The
1594 +.B \-ok
1595 +and
1596 +.B \-okdir
1597 +actions print the current filename as-is. This may change in a future release.
1598 +.SS OPERATORS
1599 +.P
1600 +Listed in order of decreasing precedence:
1601 +
1602 +.IP "( \fIexpr\fR )"
1603 +Force precedence. Since parentheses are special to the shell, you
1604 +will normally need to quote them. Many of the examples in this manual
1605 +page use backslashes for this purpose: `\e(...\e)' instead of `(...)'.
1606 +
1607 +.IP "! \fIexpr\fR"
1608 +True if \fIexpr\fR is false. This character will also usually need
1609 +protection from interpretation by the shell.
1610 +
1611 +.IP "\-not \fIexpr\fR"
1612 +Same as ! \fIexpr\fR, but not POSIX compliant.
1613 +
1614 +.IP "\fIexpr1 expr2\fR"
1615 +Two expressions in a row are taken to be joined with an
1616 +implied "and"; \fIexpr2\fR is not evaluated if \fIexpr1\fR is false.
1617 +
1618 +.IP "\fIexpr1\fR \-a \fIexpr2\fR"
1619 +Same as \fIexpr1 expr2\fR.
1620 +
1621 +.IP "\fIexpr1\fR \-and \fIexpr2\fR"
1622 +Same as \fIexpr1 expr2\fR, but not POSIX compliant.
1623 +
1624 +.IP "\fIexpr1\fR \-o \fIexpr2\fR"
1625 +Or; \fIexpr2\fR is not evaluated if \fIexpr1\fR is true.
1626 +
1627 +.IP "\fIexpr1\fR \-or \fIexpr2\fR"
1628 +Same as \fIexpr1\fR
1629 +.B \-o
1630 +\fIexpr2\fR, but not POSIX compliant.
1631 +
1632 +.IP "\fIexpr1\fR , \fIexpr2\fR"
1633 +List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. The
1634 +value of \fIexpr1\fR is discarded; the value of the list is the value
1635 +of \fIexpr2\fR. The comma operator can be useful for searching for
1636 +several different types of thing, but traversing the filesystem
1637 +hierarchy only once. The
1638 +.B \-fprintf
1639 +action can be used to list the various matched items into several
1640 +different output files.
1641 +
1642 +
1643 +.SH "STANDARDS CONFORMANCE"
1644 +For closest compliance to the POSIX standard, you should set the
1645 +POSIXLY_CORRECT environment variable. The following options are
1646 +specified in the POSIX standard (IEEE Std 1003.1, 2003 Edition):
1647 +
1648 +.IP \fB\-H\fR
1649 +This option is supported.
1650 +
1651 +.IP \fB\-L\fR
1652 +This option is supported.
1653 +
1654 +.IP \fB\-name\fR
1655 +This option is supported, but POSIX conformance depends on the
1656 +POSIX conformance of the system's
1657 +.BR fnmatch (3)
1658 +library function. As of findutils-4.2.2, shell metacharacters
1659 +(`*', `?' or `[]' for example) will match a leading `.', because
1660 +IEEE PASC interpretation 126 requires this. This is a change from
1661 +previous versions of findutils.
1662 +
1663 +.IP \fB\-type\fR
1664 +Supported. POSIX specifies `b', `c', `d', `l', `p', `f' and `s'.
1665 +GNU find also supports `D', representing a Door, where the OS provides these.
1666 +
1667 +.IP \fB\-ok\fR
1668 +Supported. Interpretation of the response is not locale-dependent
1669 +(see ENVIRONMENT VARIABLES).
1670 +
1671 +.IP \fB\-newer\fR
1672 +Supported. If the file specified is a symbolic link, it is always
1673 +dereferenced. This is a change from previous behaviour, which used to
1674 +take the relevant time from the symbolic link; see the HISTORY section
1675 +below.
1676 +
1677 +.IP \fB\-perm\fR
1678 +Supported. If the POSIXLY_CORRECT environment variable is not set,
1679 +some mode arguments (for example +a+x) which are not valid in POSIX
1680 +are supported for backward-compatibility.
1681 +
1682 +.IP "Other predicates"
1683 +The predicates
1684 +.BR \-atime ,
1685 +.BR \-ctime ,
1686 +.BR \-depth ,
1687 +.BR \-group ,
1688 +.BR \-links ,
1689 +.BR \-mtime ,
1690 +.BR \-nogroup ,
1691 +.BR \-nouser ,
1692 +.BR \-print ,
1693 +.BR \-prune ,
1694 +.BR \-size ,
1695 +.BR \-user
1696 +and
1697 +.B \-xdev
1698 +are all supported.
1699 +
1700 +.P
1701 +The POSIX standard specifies parentheses `(', `)', negation `!' and the
1702 +`and' and `or' operators (
1703 +.BR \-a ,
1704 +.BR \-o ).
1705 +.P
1706 +All other options, predicates, expressions and so forth are extensions
1707 +beyond the POSIX standard. Many of these extensions are not unique to
1708 +GNU find, however.
1709 +.P
1710 +The POSIX standard requires that
1711 +.B find
1712 +detects loops:
1713 +.IP
1714 +The
1715 +.B find
1716 +utility shall detect infinite loops; that is, entering a
1717 +previously visited directory that is an ancestor of the last file
1718 +encountered. When it detects an infinite loop, find shall write a
1719 +diagnostic message to standard error and shall either recover its
1720 +position in the hierarchy or terminate.
1721 +.P
1722 +GNU
1723 +.B find
1724 +complies with these requirements. The link count of
1725 +directories which contain entries which are hard links to an ancestor
1726 +will often be lower than they otherwise should be. This can mean that
1727 +GNU find will sometimes optimise away the visiting of a subdirectory
1728 +which is actually a link to an ancestor. Since
1729 +.B find
1730 +does not actually enter such a subdirectory, it is allowed to avoid
1731 +emitting a diagnostic message. Although this behaviour may be
1732 +somewhat confusing, it is unlikely that anybody actually depends on
1733 +this behaviour. If the leaf optimisation has been turned off with
1734 +.BR \-noleaf ,
1735 +the directory entry will always be examined and the diagnostic message
1736 +will be issued where it is appropriate. Symbolic links cannot be used
1737 +to create filesystem cycles as such, but if the
1738 +.B \-L
1739 +option or the
1740 +.B \-follow
1741 +option is in use, a diagnostic message is issued when
1742 +.B find
1743 +encounters a loop of symbolic links. As with loops containing hard
1744 +links, the leaf optimisation will often mean that
1745 +.B find
1746 +knows that it doesn't need to call
1747 +.I stat()
1748 +or
1749 +.I chdir()
1750 +on the symbolic link, so this diagnostic is frequently not necessary.
1751 +.P
1752 +The
1753 +.B \-d
1754 +option is supported for compatibility with various BSD systems,
1755 +but you should use the POSIX-compliant option
1756 +.B \-depth
1757 +instead.
1758 +.P
1759 +The POSIXLY_CORRECT environment variable does not affect the behaviour
1760 +of the
1761 +.B \-regex
1762 +or
1763 +.B \-iregex
1764 +tests because those tests aren't specified in the POSIX standard.
1765 +.SH "ENVIRONMENT VARIABLES"
1766 +
1767 +.IP LANG
1768 +Provides a default value for the internationalization variables that
1769 +are unset or null.
1770 +
1771 +.IP LC_ALL
1772 +If set to a non-empty string value, override the values of all the
1773 +other internationalization variables.
1774 +
1775 +.IP LC_COLLATE
1776 +The POSIX standard specifies that this variable affects the pattern
1777 +matching to be used for the
1778 +.B \-name
1779 +option. GNU find uses the
1780 +.BR fnmatch (3)
1781 +library function, and so support for `LC_COLLATE' depends on the
1782 +system library.
1783 +
1784 +.IP
1785 +POSIX also specifies that the `LC_COLLATE' environment
1786 +variable affects the interpretation of the user's response to the
1787 +query issued by
1788 +.BR \-ok' ,
1789 +but this is not the case for GNU find.
1790 +
1791 +.IP LC_CTYPE
1792 +This variable affects the treatment of character classes used with
1793 +the
1794 +.B \-name
1795 +test, if the system's
1796 +.BR fnmatch (3)
1797 +library function supports this. It has no effect on the behaviour
1798 +of the
1799 +.B \-ok
1800 +expression.
1801 +
1802 +.IP LC_MESSAGES
1803 +Determines the locale to be used for internationalised messages.
1804 +
1805 +.IP NLSPATH
1806 +Determines the location of the internationalisation message catalogues.
1807 +
1808 +.IP PATH
1809 +Affects the directories which are searched to find the executables
1810 +invoked by
1811 +.BR \-exec ,
1812 +.BR \-execdir ,
1813 +.B \-ok
1814 +and
1815 +.BR \-okdir .
1816 +
1817 +.IP POSIXLY_CORRECT
1818 +Determines the block size used by
1819 +.B \-ls
1820 +and
1821 +.BR \-fls .
1822 +If
1823 +.B POSIXLY_CORRECT
1824 +is set, blocks are units of 512 bytes. Otherwise
1825 +they are units of 1024 bytes.
1826 +.IP
1827 +Setting this variable also turns off
1828 +warning messages (that is, implies
1829 +.BR \-nowarn )
1830 +by default, because POSIX requires that apart from
1831 +the output for
1832 +.BR \-ok ,
1833 +all messages printed on stderr are diagnositcs and must result in a
1834 +non-zero exit status.
1835 +.IP
1836 +When POSIXLY_CORRECT is not set,
1837 +.B \-perm
1838 ++zzz
1839 +is treated just like
1840 +.B \-perm
1841 +/zzz
1842 +if
1843 ++zzz is not a valid symbolic mode. When POSIXLY_CORRECT is set, such
1844 +constructs are treated as an error.
1845 +
1846 +.IP TZ
1847 +Affects the time zone used for some of the time-related format
1848 +directives of
1849 +.B \-printf
1850 +and
1851 +.BR \-fprintf .
1852 +.SH "EXAMPLES"
1853 +.nf
1854 +.B find /tmp \-name core \-type f \-print | xargs /bin/rm \-f
1855 +
1856 +.fi
1857 +Find files named
1858 +.B core
1859 +in or below the directory
1860 +.B /tmp
1861 +and delete them. Note that this will work incorrectly if there are
1862 +any filenames containing newlines, single or double quotes, or spaces.
1863 +.P
1864 +.B find /tmp \-name core \-type f \-print0 | xargs \-0 /bin/rm \-f
1865 +
1866 +.fi
1867 +Find files named
1868 +.B core
1869 +in or below the directory
1870 +.B /tmp
1871 +and delete them, processing filenames in such a way that file or
1872 +directory names containing single or double quotes, spaces or newlines
1873 +are correctly handled. The
1874 +.B \-name
1875 +test comes before the
1876 +.B \-type
1877 +test in order to avoid having to call
1878 +.B stat(2)
1879 +on every file.
1880 +
1881 +.P
1882 +.nf
1883 +.B find . \-type f \-exec file \(aq{}\(aq \e\;
1884 +
1885 +.fi
1886 +Runs `file' on every file in or below the current directory. Notice
1887 +that the braces are enclosed in single quote marks to protect them
1888 +from interpretation as shell script punctuation. The semicolon is
1889 +similarly protected by the use of a backslash, though single quotes
1890 +could have been used in that case also.
1891 +
1892 +.P
1893 +.nf
1894 +.B find / \e
1895 +.B \e( \-perm \-4000 \-fprintf /root/suid.txt "%#m %u %p\en" \e) , \e
1896 +.B \e( \-size +100M \-fprintf /root/big.txt "%\-10s %p\en" \e)
1897 +
1898 +.fi
1899 +Traverse the filesystem just once, listing setuid files and
1900 +directories into
1901 +.B /root/suid.txt
1902 +and large files into
1903 +.BR /root/big.txt .
1904 +
1905 +.P
1906 +.nf
1907 +.B find $HOME \-mtime 0
1908 +
1909 +.fi
1910 +Search for files in your home directory which have been modified in
1911 +the last twenty-four hours. This command works this way because the
1912 +time since each file was last modified is divided by 24 hours and any
1913 +remainder is discarded. That means that to match
1914 +.B \-mtime
1915 +.BR 0 ,
1916 +a file will have to have a modification in the past which is less than
1917 +24 hours ago.
1918 +
1919 +.P
1920 +.nf
1921 +.B find /sbin /usr/sbin -executable \e! -readable \-print
1922 +
1923 +.fi
1924 +Search for files which are executable but not readable.
1925 +
1926 +.P
1927 +.nf
1928 +.B find . \-perm 664
1929 +
1930 +.fi
1931 +Search for files which have read and write permission for their owner,
1932 +and group, but which other users can read but not write to. Files
1933 +which meet these criteria but have other permissions bits set (for
1934 +example if someone can execute the file) will not be matched.
1935 +
1936 +.P
1937 +.nf
1938 +.B find . \-perm \-664
1939 +
1940 +.fi
1941 +Search for files which have read and write permission for their owner
1942 +and group, and which other users can read, without regard to the
1943 +presence of any extra permission bits (for example the executable
1944 +bit). This will match a file which has mode 0777, for example.
1945 +
1946 +.P
1947 +.nf
1948 +.B find . \-perm /222
1949 +
1950 +.fi
1951 +Search for files which are writable by somebody (their owner, or
1952 +their group, or anybody else).
1953 +
1954 +.P
1955 +.nf
1956 +.B find . \-perm /220
1957 +.B find . \-perm /u+w,g+w
1958 +.B find . \-perm /u=w,g=w
1959 +
1960 +.fi
1961 +All three of these commands do the same thing, but the first one uses
1962 +the octal representation of the file mode, and the other two use the
1963 +symbolic form. These commands all search for files which are
1964 +writable by either their owner or their group. The files don't have
1965 +to be writable by both the owner and group to be matched; either will
1966 +do.
1967 +
1968 +.P
1969 +.nf
1970 +.B find . \-perm \-220
1971 +.B find . \-perm \-g+w,u+w
1972 +
1973 +.fi
1974 +Both these commands do the same thing; search for files which are
1975 +writable by both their owner and their group.
1976 +
1977 +.P
1978 +.nf
1979 +.B find . \-perm \-444 \-perm /222 ! \-perm /111
1980 +.B find . \-perm \-a+r \-perm /a+w ! \-perm /a+x
1981 +
1982 +.fi
1983 +These two commands both search for files that are readable for
1984 +everybody (
1985 +.B \-perm \-444
1986 +or
1987 +.BR "\-perm \-a+r" ),
1988 +have at least one write bit
1989 +set (
1990 +.B \-perm /222
1991 +or
1992 +.BR "\-perm /a+w" )
1993 +but are not executable for anybody (
1994 +.B ! \-perm /111
1995 +and
1996 +.B ! \-perm /a+x
1997 +respectively).
1998 +
1999 +.P
2000 +.nf
2001 +.B cd /source-dir
2002 +.B find . \-name .snapshot \-prune \-o \e( \e! \-name "*~" \-print0 \e)|
2003 +.B cpio \-pmd0 /dest-dir
2004 +
2005 +.fi
2006 +This command copies the contents of
2007 +.B /source-dir
2008 +to
2009 +.BR /dest-dir ,
2010 +but omits files and directories named
2011 +.B .snapshot
2012 +(and anything in them). It also omits files or directories whose name
2013 +ends in
2014 +.BR ~ ,
2015 +but not their contents. The construct
2016 +.B \-prune \-o \e( ... \-print0 \e)
2017 +is quite common. The idea here is that the expression before
2018 +.B \-prune
2019 +matches things which are to be pruned. However, the
2020 +.B \-prune
2021 +action itself returns true, so the following
2022 +.B \-o
2023 +ensures that the right hand side is evaluated only for those
2024 +directories which didn't get pruned (the contents of the pruned
2025 +directories are not even visited, so their contents are irrelevant).
2026 +The expression on the right hand side of the
2027 +.B \-o
2028 +is in parentheses only for clarity. It emphasises that the
2029 +.B \-print0
2030 +action takes place only for things that didn't have
2031 +.B \-prune
2032 +applied to them. Because the default `and' condition between tests
2033 +binds more tightly than
2034 +.BR \-o ,
2035 +this is the default anyway, but the parentheses help to show
2036 +what is going on.
2037 +
2038 +.SH EXIT STATUS
2039 +.PP
2040 +.B find
2041 +exits with status 0 if all files are processed successfully, greater
2042 +than 0 if errors occur. This is deliberately a very broad
2043 +description, but if the return value is non-zero, you should not rely
2044 +on the correctness of the results of
2045 +.BR find .
2046 +
2047 +.SH "SEE ALSO"
2048 +\fBlocate\fP(1), \fBlocatedb\fP(5), \fBupdatedb\fP(1), \fBxargs\fP(1),
2049 +\fBchmod\fP(1), \fBfnmatch\fP(3), \fBregex\fP(7), \fBstat\fP(2),
2050 +\fBlstat\fP(2), \fBls\fP(1), \fBprintf\fP(3), \fBstrftime\fP(3),
2051 +\fBctime\fP(3), \fBFinding Files\fP (on-line in Info, or printed).
2052 +.SH "HISTORY"
2053 +As of findutils-4.2.2, shell metacharacters (`*', `?' or `[]' for
2054 +example) used in filename patterns will match a leading `.', because
2055 +IEEE POSIX interpretation 126 requires this.
2056 +.P
2057 +The syntax
2058 +\.B \-perm +MODE
2059 +was deprecated in findutils-4.2.21, in favour of
2060 +\.B \-perm
2061 +.BR /MODE .
2062 +As of findutils-4.3.3,
2063 +.B \-perm /000
2064 +now matches all files instead of none.
2065 +.P
2066 +Nanosecond-resolution
2067 +timestamps were implemented in findutils-4.3.3.
2068 +.P
2069 +As of findutils-4.3.11, the
2070 +.B \-delete
2071 +action sets
2072 +.BR find 's
2073 +exit status to a nonzero value when it fails.
2074 +However,
2075 +.B find
2076 +will not exit immediately. Previously,
2077 +.BR find 's
2078 +exit status was unaffected by the failure of
2079 +.BR \-delete .
2080 +.TS
2081 +l l l .
2082 +Feature Added in Also occurs in
2083 +\-newerXY 4.3.3 BSD
2084 +\-D 4.3.1
2085 +\-O 4.3.1
2086 +\-readable 4.3.0
2087 +\-writable 4.3.0
2088 +\-executable 4.3.0
2089 +\-regextype 4.2.24
2090 +\-exec ... + 4.2.12 POSIX
2091 +\-execdir 4.2.12 BSD
2092 +\-okdir 4.2.12
2093 +\-samefile 4.2.11
2094 +\-H 4.2.5 POSIX
2095 +\-L 4.2.5 POSIX
2096 +\-P 4.2.5 BSD
2097 +\-delete 4.2.3
2098 +\-quit 4.2.3
2099 +\-d 4.2.3 BSD
2100 +\-wholename 4.2.0
2101 +\-iwholename 4.2.0
2102 +\-ignore_readdir_race 4.2.0
2103 +\-fls 4.0
2104 +\-ilname 3.8
2105 +\-iname 3.8
2106 +\-ipath 3.8
2107 +\-iregex 3.8
2108 +.TE
2109 +.SH "NON-BUGS"
2110 +.nf
2111 +.B $ find . \-name *.c \-print
2112 +find: paths must precede expression
2113 +Usage: find [\-H] [\-L] [\-P] [\-Olevel] [\-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
2114 +.fi
2115 +.P
2116 +This happens because
2117 +.I *.c
2118 +has been expanded by the shell
2119 +resulting in
2120 +.B find
2121 +actually receiving a command line like this:
2122 +.nf
2123 +
2124 +.B find . \-name bigram.c code.c frcode.c locate.c \-print
2125 +
2126 +.fi
2127 +That command is of course not going to work. Instead of doing things
2128 +this way, you should enclose the pattern in quotes or escape the wildcard:
2129 +.nf
2130 +.B $ find . \-name \e*.c \-print
2131 +.fi
2132 +
2133 +.SH "BUGS"
2134 +.P
2135 +There are security problems inherent in the behaviour that the POSIX
2136 +standard specifies for
2137 +.BR find ,
2138 +which therefore cannot be fixed. For example, the
2139 +.B \-exec
2140 +action is
2141 +inherently insecure, and
2142 +.B \-execdir
2143 +should be used instead.
2144 +Please see \fBFinding Files\fP for more information.
2145 +.P
2146 +The environment variable
2147 +.B LC_COLLATE
2148 +has no effect on the
2149 +.B \-ok
2150 +action.
2151 +.P
2152 +The best way to report a bug is to use the form at
2153 +http://savannah.gnu.org/bugs/?group=findutils.
2154 +The reason for this is that you will then be able to track progress in
2155 +fixing the problem. Other comments about \fBfind\fP(1) and about
2156 +the findutils package in general can be sent to the
2157 +.I bug\-findutils
2158 +mailing list. To join the list, send email to
2159 +.IR bug\-findutils\-request@×××.org .
2160 diff -purN findutils-4.3.12.orig/find/find.c findutils-4.3.12/find/find.c
2161 --- findutils-4.3.12.orig/find/find.c 2007-12-19 16:12:34.000000000 -0500
2162 +++ findutils-4.3.12/find/find.c 2008-01-30 08:46:05.754843619 -0500
2163 @@ -1270,7 +1270,7 @@ process_path (char *pathname, char *name
2164 static void
2165 process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)
2166 {
2167 - int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */
2168 + int subdirs_left=0; /* Number of unexamined subdirs in PATHNAME. */
2169 boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */
2170 unsigned int idx; /* Which entry are we on? */
2171 struct stat stat_buf;
2172 diff -purN findutils-4.3.12.orig/find/find.c.orig findutils-4.3.12/find/find.c.orig
2173 --- findutils-4.3.12.orig/find/find.c.orig 1969-12-31 19:00:00.000000000 -0500
2174 +++ findutils-4.3.12/find/find.c.orig 2007-12-19 16:12:34.000000000 -0500
2175 @@ -0,0 +1,1537 @@
2176 +/* find -- search for files in a directory hierarchy
2177 + Copyright (C) 1990, 91, 92, 93, 94, 2000,
2178 + 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
2179 +
2180 + This program is free software: you can redistribute it and/or modify
2181 + it under the terms of the GNU General Public License as published by
2182 + the Free Software Foundation, either version 3 of the License, or
2183 + (at your option) any later version.
2184 +
2185 + This program is distributed in the hope that it will be useful,
2186 + but WITHOUT ANY WARRANTY; without even the implied warranty of
2187 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2188 + GNU General Public License for more details.
2189 +
2190 + You should have received a copy of the GNU General Public License
2191 + along with this program. If not, see <http://www.gnu.org/licenses/>.
2192 +*/
2193 +/* GNU find was written by Eric Decker <cire@×××××.com>,
2194 + with enhancements by David MacKenzie <djm@×××.org>,
2195 + Jay Plett <jay@××××××××××××××××××××.us>,
2196 + and Tim Wood <axolotl!tim@××××.com>.
2197 + The idea for -print0 and xargs -0 came from
2198 + Dan Bernstein <brnstnd@×××××××××××××××.edu>.
2199 + Improvements have been made by James Youngman <jay@×××.org>.
2200 +*/
2201 +
2202 +
2203 +#include <config.h>
2204 +#include "defs.h"
2205 +
2206 +#define USE_SAFE_CHDIR 1
2207 +#undef STAT_MOUNTPOINTS
2208 +
2209 +
2210 +#include <errno.h>
2211 +#include <assert.h>
2212 +
2213 +#include <sys/stat.h>
2214 +#include <fcntl.h>
2215 +#include <openat.h>
2216 +
2217 +#include "xalloc.h"
2218 +#include "human.h"
2219 +#include "canonicalize.h"
2220 +#include <modetype.h>
2221 +
2222 +#include "closein.h"
2223 +#include "savedirinfo.h"
2224 +#include "buildcmd.h"
2225 +#include "dirname.h"
2226 +#include "quote.h"
2227 +#include "quotearg.h"
2228 +#include "xgetcwd.h"
2229 +#include "error.h"
2230 +
2231 +#ifdef HAVE_LOCALE_H
2232 +#include <locale.h>
2233 +#endif
2234 +
2235 +#if ENABLE_NLS
2236 +# include <libintl.h>
2237 +# define _(Text) gettext (Text)
2238 +#else
2239 +# define _(Text) Text
2240 +#define textdomain(Domain)
2241 +#define bindtextdomain(Package, Directory)
2242 +#define ngettext(singular,plural,n) ((1==n) ? singular : plural)
2243 +#endif
2244 +#ifdef gettext_noop
2245 +# define N_(String) gettext_noop (String)
2246 +#else
2247 +/* See locate.c for explanation as to why not use (String) */
2248 +# define N_(String) String
2249 +#endif
2250 +
2251 +#ifdef STAT_MOUNTPOINTS
2252 +static void init_mounted_dev_list(int mandatory);
2253 +#endif
2254 +
2255 +static void process_top_path PARAMS((char *pathname, mode_t mode));
2256 +static int process_path PARAMS((char *pathname, char *name, boolean leaf, char *parent, mode_t type));
2257 +static void process_dir PARAMS((char *pathname, char *name, int pathlen, const struct stat *statp, char *parent));
2258 +
2259 +
2260 +
2261 +/* Name this program was run with. */
2262 +char *program_name;
2263 +
2264 +/* A file descriptor open to the initial working directory.
2265 + Doing it this way allows us to work when the i.w.d. has
2266 + unreadable parents. */
2267 +int starting_desc;
2268 +
2269 +/* The stat buffer of the initial working directory. */
2270 +static struct stat starting_stat_buf;
2271 +
2272 +enum ChdirSymlinkHandling
2273 + {
2274 + SymlinkHandleDefault, /* Normally the right choice */
2275 + SymlinkFollowOk /* see comment in process_top_path() */
2276 + };
2277 +
2278 +
2279 +enum TraversalDirection
2280 + {
2281 + TraversingUp,
2282 + TraversingDown
2283 + };
2284 +
2285 +enum WdSanityCheckFatality
2286 + {
2287 + FATAL_IF_SANITY_CHECK_FAILS,
2288 + RETRY_IF_SANITY_CHECK_FAILS,
2289 + NON_FATAL_IF_SANITY_CHECK_FAILS
2290 + };
2291 +
2292 +
2293 +int get_current_dirfd(void)
2294 +{
2295 + return AT_FDCWD;
2296 +}
2297 +
2298 +
2299 +int
2300 +main (int argc, char **argv)
2301 +{
2302 + int i;
2303 + int end_of_leading_options = 0; /* First arg after any -H/-L etc. */
2304 + struct predicate *eval_tree;
2305 +
2306 + program_name = argv[0];
2307 + state.exit_status = 0;
2308 +
2309 + /* Set the option defaults before we do the locale
2310 + * initialisation as check_nofollow() needs to be executed in the
2311 + * POSIX locale.
2312 + */
2313 + set_option_defaults(&options);
2314 +
2315 +#ifdef HAVE_SETLOCALE
2316 + setlocale (LC_ALL, "");
2317 +#endif
2318 + bindtextdomain (PACKAGE, LOCALEDIR);
2319 + textdomain (PACKAGE);
2320 + atexit (close_stdin);
2321 +
2322 + /* Check for -P, -H or -L options. */
2323 + end_of_leading_options = process_leading_options(argc, argv);
2324 +
2325 + if (options.debug_options & DebugStat)
2326 + options.xstat = debug_stat;
2327 +
2328 +#ifdef DEBUG
2329 + fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start));
2330 +#endif /* DEBUG */
2331 +
2332 + /* state.cwd_dir_fd has to be initialised before we call build_expression_tree()
2333 + * because command-line parsing may lead us to stat some files.
2334 + */
2335 + state.cwd_dir_fd = AT_FDCWD;
2336 +
2337 + /* We are now processing the part of the "find" command line
2338 + * after the -H/-L options (if any).
2339 + */
2340 + eval_tree = build_expression_tree(argc, argv, end_of_leading_options);
2341 +
2342 +
2343 + /* safely_chdir() needs to check that it has ended up in the right place.
2344 + * To avoid bailing out when something gets automounted, it checks if
2345 + * the target directory appears to have had a directory mounted on it as
2346 + * we chdir()ed. The problem with this is that in order to notice that
2347 + * a file system was mounted, we would need to lstat() all the mount points.
2348 + * That strategy loses if our machine is a client of a dead NFS server.
2349 + *
2350 + * Hence if safely_chdir() and wd_sanity_check() can manage without needing
2351 + * to know the mounted device list, we do that.
2352 + */
2353 + if (!options.open_nofollow_available)
2354 + {
2355 +#ifdef STAT_MOUNTPOINTS
2356 + init_mounted_dev_list(0);
2357 +#endif
2358 + }
2359 +
2360 +
2361 + starting_desc = open (".", O_RDONLY
2362 +#if defined O_LARGEFILE
2363 + |O_LARGEFILE
2364 +#endif
2365 + );
2366 + if (0 <= starting_desc && fchdir (starting_desc) != 0)
2367 + {
2368 + close (starting_desc);
2369 + starting_desc = -1;
2370 + }
2371 +
2372 + if (starting_desc < 0)
2373 + {
2374 + starting_dir = xgetcwd ();
2375 + if (! starting_dir)
2376 + error (1, errno, _("cannot get current directory"));
2377 + }
2378 + set_stat_placeholders(&starting_stat_buf);
2379 + if ((*options.xstat) (".", &starting_stat_buf) != 0)
2380 + error (1, errno, _("cannot stat current directory"));
2381 +
2382 + /* If no paths are given, default to ".". */
2383 + for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++)
2384 + {
2385 + process_top_path (argv[i], 0);
2386 + }
2387 +
2388 + /* If there were no path arguments, default to ".". */
2389 + if (i == end_of_leading_options)
2390 + {
2391 + /*
2392 + * We use a temporary variable here because some actions modify
2393 + * the path temporarily. Hence if we use a string constant,
2394 + * we get a coredump. The best example of this is if we say
2395 + * "find -printf %H" (note, not "find . -printf %H").
2396 + */
2397 + char defaultpath[2] = ".";
2398 + process_top_path (defaultpath, 0);
2399 + }
2400 +
2401 + /* If "-exec ... {} +" has been used, there may be some
2402 + * partially-full command lines which have been built,
2403 + * but which are not yet complete. Execute those now.
2404 + */
2405 + show_success_rates(eval_tree);
2406 + cleanup();
2407 + return state.exit_status;
2408 +}
2409 +
2410 +boolean is_fts_enabled(int *ftsoptions)
2411 +{
2412 + /* this version of find (i.e. this main()) does not use fts. */
2413 + *ftsoptions = 0;
2414 + return false;
2415 +}
2416 +
2417 +
2418 +static char *
2419 +specific_dirname(const char *dir)
2420 +{
2421 + char dirbuf[1024];
2422 +
2423 + if (0 == strcmp(".", dir))
2424 + {
2425 + /* OK, what's '.'? */
2426 + if (NULL != getcwd(dirbuf, sizeof(dirbuf)))
2427 + {
2428 + return strdup(dirbuf);
2429 + }
2430 + else
2431 + {
2432 + return strdup(dir);
2433 + }
2434 + }
2435 + else
2436 + {
2437 + char *result = canonicalize_filename_mode(dir, CAN_EXISTING);
2438 + if (NULL == result)
2439 + return strdup(dir);
2440 + else
2441 + return result;
2442 + }
2443 +}
2444 +
2445 +
2446 +
2447 +/* Return non-zero if FS is the name of a file system that is likely to
2448 + * be automounted
2449 + */
2450 +static int
2451 +fs_likely_to_be_automounted(const char *fs)
2452 +{
2453 + return ( (0==strcmp(fs, "nfs")) || (0==strcmp(fs, "autofs")) || (0==strcmp(fs, "subfs")));
2454 +}
2455 +
2456 +
2457 +
2458 +#ifdef STAT_MOUNTPOINTS
2459 +static dev_t *mounted_devices = NULL;
2460 +static size_t num_mounted_devices = 0u;
2461 +
2462 +
2463 +static void
2464 +init_mounted_dev_list(int mandatory)
2465 +{
2466 + assert (NULL == mounted_devices);
2467 + assert (0 == num_mounted_devices);
2468 + mounted_devices = get_mounted_devices(&num_mounted_devices);
2469 + if (mandatory && (NULL == mounted_devices))
2470 + {
2471 + error(1, 0, "Cannot read list of mounted devices.");
2472 + }
2473 +}
2474 +
2475 +static void
2476 +refresh_mounted_dev_list(void)
2477 +{
2478 + if (mounted_devices)
2479 + {
2480 + free(mounted_devices);
2481 + mounted_devices = 0;
2482 + }
2483 + num_mounted_devices = 0u;
2484 + init_mounted_dev_list(1);
2485 +}
2486 +
2487 +
2488 +/* Search for device DEV in the array LIST, which is of size N. */
2489 +static int
2490 +dev_present(dev_t dev, const dev_t *list, size_t n)
2491 +{
2492 + if (list)
2493 + {
2494 + while (n-- > 0u)
2495 + {
2496 + if ( (*list++) == dev )
2497 + return 1;
2498 + }
2499 + }
2500 + return 0;
2501 +}
2502 +
2503 +enum MountPointStateChange
2504 + {
2505 + MountPointRecentlyMounted,
2506 + MountPointRecentlyUnmounted,
2507 + MountPointStateUnchanged
2508 + };
2509 +
2510 +
2511 +
2512 +static enum MountPointStateChange
2513 +get_mount_state(dev_t newdev)
2514 +{
2515 + int new_is_present, new_was_present;
2516 +
2517 + new_was_present = dev_present(newdev, mounted_devices, num_mounted_devices);
2518 + refresh_mounted_dev_list();
2519 + new_is_present = dev_present(newdev, mounted_devices, num_mounted_devices);
2520 +
2521 + if (new_was_present == new_is_present)
2522 + return MountPointStateUnchanged;
2523 + else if (new_is_present)
2524 + return MountPointRecentlyMounted;
2525 + else
2526 + return MountPointRecentlyUnmounted;
2527 +}
2528 +
2529 +
2530 +
2531 +/* We stat()ed a directory, chdir()ed into it (we know this
2532 + * since direction is TraversingDown), stat()ed it again,
2533 + * and noticed that the device numbers are different. Check
2534 + * if the file system was recently mounted.
2535 + *
2536 + * If it was, it looks like chdir()ing into the directory
2537 + * caused a file system to be mounted. Maybe automount is
2538 + * running. Anyway, that's probably OK - but it happens
2539 + * only when we are moving downward.
2540 + *
2541 + * We also allow for the possibility that a similar thing
2542 + * has happened with the unmounting of a file system. This
2543 + * is much rarer, as it relies on an automounter timeout
2544 + * occurring at exactly the wrong moment.
2545 + */
2546 +static enum WdSanityCheckFatality
2547 +dirchange_is_fatal(const char *specific_what,
2548 + enum WdSanityCheckFatality isfatal,
2549 + int silent,
2550 + struct stat *newinfo)
2551 +{
2552 + enum MountPointStateChange transition = get_mount_state(newinfo->st_dev);
2553 + switch (transition)
2554 + {
2555 + case MountPointRecentlyUnmounted:
2556 + isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS;
2557 + if (!silent)
2558 + {
2559 + error (0, 0,
2560 + _("Warning: file system %s has recently been unmounted."),
2561 + safely_quote_err_filename(0, specific_what));
2562 + }
2563 + break;
2564 +
2565 + case MountPointRecentlyMounted:
2566 + isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS;
2567 + if (!silent)
2568 + {
2569 + error (0, 0,
2570 + _("Warning: file system %s has recently been mounted."),
2571 + safely_quote_err_filename(0, specific_what));
2572 + }
2573 + break;
2574 +
2575 + case MountPointStateUnchanged:
2576 + /* leave isfatal as it is */
2577 + break;
2578 + }
2579 +
2580 + return isfatal;
2581 +}
2582 +
2583 +
2584 +#endif
2585 +
2586 +
2587 +
2588 +/* Examine the results of the stat() of a directory from before we
2589 + * entered or left it, with the results of stat()ing it afterward. If
2590 + * these are different, the file system tree has been modified while we
2591 + * were traversing it. That might be an attempt to use a race
2592 + * condition to persuade find to do something it didn't intend
2593 + * (e.g. an attempt by an ordinary user to exploit the fact that root
2594 + * sometimes runs find on the whole file system). However, this can
2595 + * also happen if automount is running (certainly on Solaris). With
2596 + * automount, moving into a directory can cause a file system to be
2597 + * mounted there.
2598 + *
2599 + * To cope sensibly with this, we will raise an error if we see the
2600 + * device number change unless we are chdir()ing into a subdirectory,
2601 + * and the directory we moved into has been mounted or unmounted "recently".
2602 + * Here "recently" means since we started "find" or we last re-read
2603 + * the /etc/mnttab file.
2604 + *
2605 + * If the device number does not change but the inode does, that is a
2606 + * problem.
2607 + *
2608 + * If the device number and inode are both the same, we are happy.
2609 + *
2610 + * If a file system is (un)mounted as we chdir() into the directory, that
2611 + * may mean that we're now examining a section of the file system that might
2612 + * have been excluded from consideration (via -prune or -quit for example).
2613 + * Hence we print a warning message to indicate that the output of find
2614 + * might be inconsistent due to the change in the file system.
2615 + */
2616 +static boolean
2617 +wd_sanity_check(const char *thing_to_stat,
2618 + const char *progname,
2619 + const char *what,
2620 + dev_t old_dev,
2621 + ino_t old_ino,
2622 + struct stat *newinfo,
2623 + int parent,
2624 + int line_no,
2625 + enum TraversalDirection direction,
2626 + enum WdSanityCheckFatality isfatal,
2627 + boolean *changed) /* output parameter */
2628 +{
2629 + const char *fstype;
2630 + char *specific_what = NULL;
2631 + int silent = 0;
2632 + const char *current_dir = ".";
2633 +
2634 + *changed = false;
2635 +
2636 + set_stat_placeholders(newinfo);
2637 + if ((*options.xstat) (current_dir, newinfo) != 0)
2638 + fatal_file_error(thing_to_stat);
2639 +
2640 + if (old_dev != newinfo->st_dev)
2641 + {
2642 + *changed = true;
2643 + specific_what = specific_dirname(what);
2644 + fstype = filesystem_type(newinfo, current_dir);
2645 + silent = fs_likely_to_be_automounted(fstype);
2646 +
2647 + /* This condition is rare, so once we are here it is
2648 + * reasonable to perform an expensive computation to
2649 + * determine if we should continue or fail.
2650 + */
2651 + if (TraversingDown == direction)
2652 + {
2653 +#ifdef STAT_MOUNTPOINTS
2654 + isfatal = dirchange_is_fatal(specific_what,isfatal,silent,newinfo);
2655 +#else
2656 + isfatal = RETRY_IF_SANITY_CHECK_FAILS;
2657 +#endif
2658 + }
2659 +
2660 + switch (isfatal)
2661 + {
2662 + case FATAL_IF_SANITY_CHECK_FAILS:
2663 + {
2664 + fstype = filesystem_type(newinfo, current_dir);
2665 + error (1, 0,
2666 + _("%1$s%2$s changed during execution of %3$s "
2667 + "(old device number %4$ld, new device number %5$ld, file system type is %6$s) [ref %7$ld]"),
2668 + safely_quote_err_filename(0, specific_what),
2669 + parent ? "/.." : "",
2670 + safely_quote_err_filename(1, progname),
2671 + (long) old_dev,
2672 + (long) newinfo->st_dev,
2673 + fstype,
2674 + (long)line_no);
2675 + /*NOTREACHED*/
2676 + return false;
2677 + }
2678 +
2679 + case NON_FATAL_IF_SANITY_CHECK_FAILS:
2680 + {
2681 + /* Since the device has changed under us, the inode number
2682 + * will almost certainly also be different. However, we have
2683 + * already decided that this is not a problem. Hence we return
2684 + * without checking the inode number.
2685 + */
2686 + free(specific_what);
2687 + return true;
2688 + }
2689 +
2690 + case RETRY_IF_SANITY_CHECK_FAILS:
2691 + return false;
2692 + }
2693 + }
2694 +
2695 + /* Device number was the same, check if the inode has changed. */
2696 + if (old_ino != newinfo->st_ino)
2697 + {
2698 + *changed = true;
2699 + specific_what = specific_dirname(what);
2700 + fstype = filesystem_type(newinfo, current_dir);
2701 +
2702 + error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0,
2703 + 0, /* no relevant errno value */
2704 + _("%1$s%2$s changed during execution of %3$s "
2705 + "(old inode number %4$ld, new inode number %5$ld, file system type is %5$s) [ref %7$ld]"),
2706 + safely_quote_err_filename(0, specific_what),
2707 + parent ? "/.." : "",
2708 + safely_quote_err_filename(1, progname),
2709 + (long) old_ino,
2710 + (long) newinfo->st_ino,
2711 + fstype,
2712 + (long)line_no);
2713 + free(specific_what);
2714 + return false;
2715 + }
2716 +
2717 + return true;
2718 +}
2719 +
2720 +enum SafeChdirStatus
2721 + {
2722 + SafeChdirOK,
2723 + SafeChdirFailSymlink,
2724 + SafeChdirFailNotDir,
2725 + SafeChdirFailStat,
2726 + SafeChdirFailWouldBeUnableToReturn,
2727 + SafeChdirFailChdirFailed,
2728 + SafeChdirFailNonexistent,
2729 + SafeChdirFailDestUnreadable
2730 + };
2731 +
2732 +/* Safely perform a change in directory. We do this by calling
2733 + * lstat() on the subdirectory, using chdir() to move into it, and
2734 + * then lstat()ing ".". We compare the results of the two stat calls
2735 + * to see if they are consistent. If not, we sound the alarm.
2736 + *
2737 + * If following_links() is true, we do follow symbolic links.
2738 + */
2739 +static enum SafeChdirStatus
2740 +safely_chdir_lstat(const char *dest,
2741 + enum TraversalDirection direction,
2742 + struct stat *statbuf_dest,
2743 + enum ChdirSymlinkHandling symlink_follow_option,
2744 + boolean *did_stat)
2745 +{
2746 + struct stat statbuf_arrived;
2747 + int rv, dotfd=-1;
2748 + int saved_errno; /* specific_dirname() changes errno. */
2749 + boolean rv_set = false;
2750 + boolean statflag = false;
2751 + int tries = 0;
2752 + enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS;
2753 +
2754 + saved_errno = errno = 0;
2755 +
2756 + dotfd = open(".", O_RDONLY
2757 +#if defined O_LARGEFILE
2758 + |O_LARGEFILE
2759 +#endif
2760 + );
2761 +
2762 + /* We jump back to here if wd_sanity_check()
2763 + * recoverably triggers an alert.
2764 + */
2765 + retry:
2766 + ++tries;
2767 +
2768 + if (dotfd >= 0)
2769 + {
2770 + /* Stat the directory we're going to. */
2771 + set_stat_placeholders(statbuf_dest);
2772 + if (0 == options.xstat(dest, statbuf_dest))
2773 + {
2774 + statflag = true;
2775 +
2776 +#ifdef S_ISLNK
2777 + /* symlink_follow_option might be set to SymlinkFollowOk, which
2778 + * would allow us to chdir() into a symbolic link. This is
2779 + * only useful for the case where the directory we're
2780 + * chdir()ing into is the basename of a command line
2781 + * argument, for example where "foo/bar/baz" is specified on
2782 + * the command line. When -P is in effect (the default),
2783 + * baz will not be followed if it is a symlink, but if bar
2784 + * is a symlink, it _should_ be followed. Hence we need the
2785 + * ability to override the policy set by following_links().
2786 + */
2787 + if (!following_links() && S_ISLNK(statbuf_dest->st_mode))
2788 + {
2789 + /* We're not supposed to be following links, but this is
2790 + * a link. Check symlink_follow_option to see if we should
2791 + * make a special exception.
2792 + */
2793 + if (symlink_follow_option == SymlinkFollowOk)
2794 + {
2795 + /* We need to re-stat() the file so that the
2796 + * sanity check can pass.
2797 + */
2798 + if (0 != stat(dest, statbuf_dest))
2799 + {
2800 + rv = SafeChdirFailNonexistent;
2801 + rv_set = true;
2802 + saved_errno = errno;
2803 + goto fail;
2804 + }
2805 + statflag = true;
2806 + }
2807 + else
2808 + {
2809 + /* Not following symlinks, so the attempt to
2810 + * chdir() into a symlink should be prevented.
2811 + */
2812 + rv = SafeChdirFailSymlink;
2813 + rv_set = true;
2814 + saved_errno = 0; /* silence the error message */
2815 + goto fail;
2816 + }
2817 + }
2818 +#endif
2819 +#ifdef S_ISDIR
2820 + /* Although the immediately following chdir() would detect
2821 + * the fact that this is not a directory for us, this would
2822 + * result in an extra system call that fails. Anybody
2823 + * examining the system-call trace should ideally not be
2824 + * concerned that something is actually failing.
2825 + */
2826 + if (!S_ISDIR(statbuf_dest->st_mode))
2827 + {
2828 + rv = SafeChdirFailNotDir;
2829 + rv_set = true;
2830 + saved_errno = 0; /* silence the error message */
2831 + goto fail;
2832 + }
2833 +#endif
2834 +
2835 + if (options.debug_options & DebugSearch)
2836 + fprintf(stderr, "safely_chdir(): chdir(\"%s\")\n", dest);
2837 +
2838 + if (0 == chdir(dest))
2839 + {
2840 + /* check we ended up where we wanted to go */
2841 + boolean changed = false;
2842 + if (!wd_sanity_check(".", program_name, ".",
2843 + statbuf_dest->st_dev,
2844 + statbuf_dest->st_ino,
2845 + &statbuf_arrived,
2846 + 0, __LINE__, direction,
2847 + isfatal,
2848 + &changed))
2849 + {
2850 + /* Only allow one failure. */
2851 + if (RETRY_IF_SANITY_CHECK_FAILS == isfatal)
2852 + {
2853 + if (0 == fchdir(dotfd))
2854 + {
2855 + isfatal = FATAL_IF_SANITY_CHECK_FAILS;
2856 + goto retry;
2857 + }
2858 + else
2859 + {
2860 + /* Failed to return to original directory,
2861 + * but we know that the current working
2862 + * directory is not the one that we intend
2863 + * to be in. Since fchdir() failed, we
2864 + * can't recover from this and so this error
2865 + * is fatal.
2866 + */
2867 + error(1, errno,
2868 + "failed to return to parent directory");
2869 + }
2870 + }
2871 + else
2872 + {
2873 + /* XXX: not sure what to use as an excuse here. */
2874 + rv = SafeChdirFailNonexistent;
2875 + rv_set = true;
2876 + saved_errno = 0;
2877 + goto fail;
2878 + }
2879 + }
2880 +
2881 + close(dotfd);
2882 + return SafeChdirOK;
2883 + }
2884 + else
2885 + {
2886 + saved_errno = errno;
2887 + if (ENOENT == saved_errno)
2888 + {
2889 + rv = SafeChdirFailNonexistent;
2890 + rv_set = true;
2891 + if (options.ignore_readdir_race)
2892 + errno = 0; /* don't issue err msg */
2893 + }
2894 + else if (ENOTDIR == saved_errno)
2895 + {
2896 + /* This can happen if the we stat a directory,
2897 + * and then file system activity changes it into
2898 + * a non-directory.
2899 + */
2900 + saved_errno = 0; /* don't issue err msg */
2901 + rv = SafeChdirFailNotDir;
2902 + rv_set = true;
2903 + }
2904 + else
2905 + {
2906 + rv = SafeChdirFailChdirFailed;
2907 + rv_set = true;
2908 + }
2909 + goto fail;
2910 + }
2911 + }
2912 + else
2913 + {
2914 + saved_errno = errno;
2915 + rv = SafeChdirFailStat;
2916 + rv_set = true;
2917 +
2918 + if ( (ENOENT == saved_errno) || (0 == state.curdepth))
2919 + saved_errno = 0; /* don't issue err msg */
2920 + goto fail;
2921 + }
2922 + }
2923 + else
2924 + {
2925 + /* We do not have read permissions on "." */
2926 + rv = SafeChdirFailWouldBeUnableToReturn;
2927 + rv_set = true;
2928 + goto fail;
2929 + }
2930 +
2931 + /* This is the success path, so we clear errno. The caller probably
2932 + * won't be calling error() anyway.
2933 + */
2934 + saved_errno = 0;
2935 +
2936 + /* We use the same exit path for success or failure.
2937 + * which has occurred is recorded in RV.
2938 + */
2939 + fail:
2940 + /* We do not call error() as this would result in a duplicate error
2941 + * message when the caller does the same thing.
2942 + */
2943 + if (saved_errno)
2944 + errno = saved_errno;
2945 +
2946 + if (dotfd >= 0)
2947 + {
2948 + close(dotfd);
2949 + dotfd = -1;
2950 + }
2951 +
2952 + *did_stat = statflag;
2953 + assert (rv_set);
2954 + return rv;
2955 +}
2956 +
2957 +#if defined O_NOFOLLOW
2958 +/* Safely change working directory to the specified subdirectory. If
2959 + * we are not allowed to follow symbolic links, we use open() with
2960 + * O_NOFOLLOW, followed by fchdir(). This ensures that we don't
2961 + * follow symbolic links (of course, we do follow them if the -L
2962 + * option is in effect).
2963 + */
2964 +static enum SafeChdirStatus
2965 +safely_chdir_nofollow(const char *dest,
2966 + enum TraversalDirection direction,
2967 + struct stat *statbuf_dest,
2968 + enum ChdirSymlinkHandling symlink_follow_option,
2969 + boolean *did_stat)
2970 +{
2971 + int extraflags, fd;
2972 +
2973 + (void) direction;
2974 + (void) statbuf_dest;
2975 +
2976 + extraflags = 0;
2977 + *did_stat = false;
2978 +
2979 + switch (symlink_follow_option)
2980 + {
2981 + case SymlinkFollowOk:
2982 + extraflags = 0;
2983 + break;
2984 +
2985 + case SymlinkHandleDefault:
2986 + if (following_links())
2987 + extraflags = 0;
2988 + else
2989 + extraflags = O_NOFOLLOW;
2990 + break;
2991 + }
2992 +
2993 + errno = 0;
2994 + fd = open(dest, O_RDONLY
2995 +#if defined O_LARGEFILE
2996 + |O_LARGEFILE
2997 +#endif
2998 + |extraflags);
2999 + if (fd < 0)
3000 + {
3001 + switch (errno)
3002 + {
3003 + case ELOOP:
3004 + return SafeChdirFailSymlink; /* This is why we use O_NOFOLLOW */
3005 + case ENOENT:
3006 + return SafeChdirFailNonexistent;
3007 + default:
3008 + return SafeChdirFailDestUnreadable;
3009 + }
3010 + }
3011 +
3012 + errno = 0;
3013 + if (0 == fchdir(fd))
3014 + {
3015 + close(fd);
3016 + return SafeChdirOK;
3017 + }
3018 + else
3019 + {
3020 + int saved_errno = errno;
3021 + close(fd);
3022 + errno = saved_errno;
3023 +
3024 + switch (errno)
3025 + {
3026 + case ENOTDIR:
3027 + return SafeChdirFailNotDir;
3028 +
3029 + case EACCES:
3030 + case EBADF: /* Shouldn't happen */
3031 + case EINTR:
3032 + case EIO:
3033 + default:
3034 + return SafeChdirFailChdirFailed;
3035 + }
3036 + }
3037 +}
3038 +#endif
3039 +
3040 +static enum SafeChdirStatus
3041 +safely_chdir(const char *dest,
3042 + enum TraversalDirection direction,
3043 + struct stat *statbuf_dest,
3044 + enum ChdirSymlinkHandling symlink_follow_option,
3045 + boolean *did_stat)
3046 +{
3047 + enum SafeChdirStatus result;
3048 +
3049 + /* We're about to leave a directory. If there are any -execdir
3050 + * argument lists which have been built but have not yet been
3051 + * processed, do them now because they must be done in the same
3052 + * directory.
3053 + */
3054 + complete_pending_execdirs(get_current_dirfd());
3055 +
3056 +#if !defined(O_NOFOLLOW)
3057 + options.open_nofollow_available = false;
3058 +#endif
3059 + if (options.open_nofollow_available)
3060 + {
3061 + result = safely_chdir_nofollow(dest, direction, statbuf_dest,
3062 + symlink_follow_option, did_stat);
3063 + if (SafeChdirFailDestUnreadable != result)
3064 + {
3065 + return result;
3066 + }
3067 + else
3068 + {
3069 + /* Savannah bug #15384: fall through to use safely_chdir_lstat
3070 + * if the directory is not readable.
3071 + */
3072 + /* Do nothing. */
3073 + }
3074 + }
3075 + /* Even if O_NOFOLLOW is available, we may need to use the alternative
3076 + * method, since parent of the start point may be executable but not
3077 + * readable.
3078 + */
3079 + return safely_chdir_lstat(dest, direction, statbuf_dest,
3080 + symlink_follow_option, did_stat);
3081 +}
3082 +
3083 +
3084 +
3085 +/* Safely go back to the starting directory. */
3086 +static void
3087 +chdir_back (void)
3088 +{
3089 + struct stat stat_buf;
3090 + boolean dummy;
3091 +
3092 + if (starting_desc < 0)
3093 + {
3094 + if (options.debug_options & DebugSearch)
3095 + fprintf(stderr, "chdir_back(): chdir(\"%s\")\n", starting_dir);
3096 +
3097 +#ifdef STAT_MOUNTPOINTS
3098 + /* We will need the mounted device list. Get it now if we don't
3099 + * already have it.
3100 + */
3101 + if (NULL == mounted_devices)
3102 + init_mounted_dev_list(1);
3103 +#endif
3104 +
3105 + if (chdir (starting_dir) != 0)
3106 + fatal_file_error(starting_dir);
3107 +
3108 + wd_sanity_check(starting_dir,
3109 + program_name,
3110 + starting_dir,
3111 + starting_stat_buf.st_dev,
3112 + starting_stat_buf.st_ino,
3113 + &stat_buf, 0, __LINE__,
3114 + TraversingUp,
3115 + FATAL_IF_SANITY_CHECK_FAILS,
3116 + &dummy);
3117 + }
3118 + else
3119 + {
3120 + if (options.debug_options & DebugSearch)
3121 + fprintf(stderr, "chdir_back(): chdir(<starting-point>)\n");
3122 +
3123 + if (fchdir (starting_desc) != 0)
3124 + {
3125 + fatal_file_error(starting_dir);
3126 + }
3127 + }
3128 +}
3129 +
3130 +/* Move to the parent of a given directory and then call a function,
3131 + * restoring the cwd. Don't bother changing directory if the
3132 + * specified directory is a child of "." or is the root directory.
3133 + */
3134 +static void
3135 +at_top (char *pathname,
3136 + mode_t mode,
3137 + struct stat *pstat,
3138 + void (*action)(char *pathname,
3139 + char *basename,
3140 + int mode,
3141 + struct stat *pstat))
3142 +{
3143 + int dirchange;
3144 + char *parent_dir = dir_name (pathname);
3145 + char *base = last_component (pathname);
3146 +
3147 + state.curdepth = 0;
3148 + state.starting_path_length = strlen (pathname);
3149 +
3150 + if (0 == *base
3151 + || 0 == strcmp(parent_dir, "."))
3152 + {
3153 + dirchange = 0;
3154 + base = pathname;
3155 + }
3156 + else
3157 + {
3158 + enum TraversalDirection direction;
3159 + enum SafeChdirStatus chdir_status;
3160 + struct stat st;
3161 + boolean did_stat = false;
3162 +
3163 + dirchange = 1;
3164 + if (0 == strcmp(base, ".."))
3165 + direction = TraversingUp;
3166 + else
3167 + direction = TraversingDown;
3168 +
3169 + /* We pass SymlinkFollowOk to safely_chdir(), which allows it to
3170 + * chdir() into a symbolic link. This is only useful for the
3171 + * case where the directory we're chdir()ing into is the
3172 + * basename of a command line argument, for example where
3173 + * "foo/bar/baz" is specified on the command line. When -P is
3174 + * in effect (the default), baz will not be followed if it is a
3175 + * symlink, but if bar is a symlink, it _should_ be followed.
3176 + * Hence we need the ability to override the policy set by
3177 + * following_links().
3178 + */
3179 + chdir_status = safely_chdir(parent_dir, direction, &st, SymlinkFollowOk, &did_stat);
3180 + if (SafeChdirOK != chdir_status)
3181 + {
3182 + const char *what = (SafeChdirFailWouldBeUnableToReturn == chdir_status) ? "." : parent_dir;
3183 + if (errno)
3184 + error (0, errno, "%s",
3185 + safely_quote_err_filename(0, what));
3186 + else
3187 + error (0, 0, _("Failed to safely change directory into %s"),
3188 + safely_quote_err_filename(0, parent_dir));
3189 +
3190 + /* We can't process this command-line argument. */
3191 + state.exit_status = 1;
3192 + return;
3193 + }
3194 + }
3195 +
3196 + free (parent_dir);
3197 + parent_dir = NULL;
3198 +
3199 + action(pathname, base, mode, pstat);
3200 +
3201 + if (dirchange)
3202 + {
3203 + chdir_back();
3204 + }
3205 +}
3206 +
3207 +
3208 +static void do_process_top_dir(char *pathname,
3209 + char *base,
3210 + int mode,
3211 + struct stat *pstat)
3212 +{
3213 + (void) pstat;
3214 +
3215 + process_path (pathname, base, false, ".", mode);
3216 + complete_pending_execdirs(get_current_dirfd());
3217 +}
3218 +
3219 +static void do_process_predicate(char *pathname,
3220 + char *base,
3221 + int mode,
3222 + struct stat *pstat)
3223 +{
3224 + (void) mode;
3225 +
3226 + state.rel_pathname = base; /* cwd_dir_fd was already set by safely_chdir */
3227 + apply_predicate (pathname, pstat, get_eval_tree());
3228 +}
3229 +
3230 +
3231 +
3232 +
3233 +/* Descend PATHNAME, which is a command-line argument.
3234 +
3235 + Actions like -execdir assume that we are in the
3236 + parent directory of the file we're examining,
3237 + and on entry to this function our working directory
3238 + is whatever it was when find was invoked. Therefore
3239 + If PATHNAME is "." we just leave things as they are.
3240 + Otherwise, we figure out what the parent directory is,
3241 + and move to that.
3242 +*/
3243 +static void
3244 +process_top_path (char *pathname, mode_t mode)
3245 +{
3246 + at_top(pathname, mode, NULL, do_process_top_dir);
3247 +}
3248 +
3249 +
3250 +/* Info on each directory in the current tree branch, to avoid
3251 + getting stuck in symbolic link loops. */
3252 +static struct dir_id *dir_ids = NULL;
3253 +/* Entries allocated in `dir_ids'. */
3254 +static int dir_alloc = 0;
3255 +/* Index in `dir_ids' of directory currently being searched.
3256 + This is always the last valid entry. */
3257 +static int dir_curr = -1;
3258 +/* (Arbitrary) number of entries to grow `dir_ids' by. */
3259 +#define DIR_ALLOC_STEP 32
3260 +
3261 +
3262 +
3263 +/* We've detected a file system loop. This is caused by one of
3264 + * two things:
3265 + *
3266 + * 1. Option -L is in effect and we've hit a symbolic link that
3267 + * points to an ancestor. This is harmless. We won't traverse the
3268 + * symbolic link.
3269 + *
3270 + * 2. We have hit a real cycle in the directory hierarchy. In this
3271 + * case, we issue a diagnostic message (POSIX requires this) and we
3272 + * skip that directory entry.
3273 + */
3274 +static void
3275 +issue_loop_warning(const char *name, const char *pathname, int level)
3276 +{
3277 + struct stat stbuf_link;
3278 + if (lstat(name, &stbuf_link) != 0)
3279 + stbuf_link.st_mode = S_IFREG;
3280 +
3281 + if (S_ISLNK(stbuf_link.st_mode))
3282 + {
3283 + error(0, 0,
3284 + _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."),
3285 + safely_quote_err_filename(0, pathname));
3286 + /* XXX: POSIX appears to require that the exit status be non-zero if a
3287 + * diagnostic is issued.
3288 + */
3289 + }
3290 + else
3291 + {
3292 + int distance = 1 + (dir_curr-level);
3293 + /* We have found an infinite loop. POSIX requires us to
3294 + * issue a diagnostic. Usually we won't get to here
3295 + * because when the leaf optimisation is on, it will cause
3296 + * the subdirectory to be skipped. If /a/b/c/d is a hard
3297 + * link to /a/b, then the link count of /a/b/c is 2,
3298 + * because the ".." entry of /b/b/c/d points to /a, not
3299 + * to /a/b/c.
3300 + */
3301 + error(0, 0,
3302 + ngettext(
3303 + "Filesystem loop detected; %1$s has the same device number and inode as "
3304 + "a directory which is %2$d level higher in the file system hierarchy",
3305 + "Filesystem loop detected; %1$s has the same device number and inode as "
3306 + "a directory which is %2$d levels higher in the file system hierarchy",
3307 + (long)distance),
3308 + safely_quote_err_filename(0, pathname),
3309 + distance);
3310 + }
3311 +}
3312 +
3313 +
3314 +
3315 +/* Recursively descend path PATHNAME, applying the predicates.
3316 + LEAF is true if PATHNAME is known to be in a directory that has no
3317 + more unexamined subdirectories, and therefore it is not a directory.
3318 + Knowing this allows us to avoid calling stat as long as possible for
3319 + leaf files.
3320 +
3321 + NAME is PATHNAME relative to the current directory. We access NAME
3322 + but print PATHNAME.
3323 +
3324 + PARENT is the path of the parent of NAME, relative to find's
3325 + starting directory.
3326 +
3327 + Return nonzero iff PATHNAME is a directory. */
3328 +
3329 +static int
3330 +process_path (char *pathname, char *name, boolean leaf, char *parent,
3331 + mode_t mode)
3332 +{
3333 + struct stat stat_buf;
3334 + static dev_t root_dev; /* Device ID of current argument pathname. */
3335 + int i;
3336 + struct predicate *eval_tree;
3337 +
3338 + eval_tree = get_eval_tree();
3339 + /* Assume it is a non-directory initially. */
3340 + stat_buf.st_mode = 0;
3341 + state.rel_pathname = name;
3342 + state.type = 0;
3343 + state.have_stat = false;
3344 + state.have_type = false;
3345 +
3346 + if (!digest_mode(mode, pathname, name, &stat_buf, leaf))
3347 + return 0;
3348 +
3349 + if (!S_ISDIR (state.type))
3350 + {
3351 + if (state.curdepth >= options.mindepth)
3352 + apply_predicate (pathname, &stat_buf, eval_tree);
3353 + return 0;
3354 + }
3355 +
3356 + /* From here on, we're working on a directory. */
3357 +
3358 +
3359 + /* Now we really need to stat the directory, even if we know the
3360 + * type, because we need information like struct stat.st_rdev.
3361 + */
3362 + if (get_statinfo(pathname, name, &stat_buf) != 0)
3363 + return 0;
3364 +
3365 + state.have_stat = true;
3366 + mode = state.type = stat_buf.st_mode; /* use full info now that we have it. */
3367 + state.stop_at_current_level =
3368 + options.maxdepth >= 0
3369 + && state.curdepth >= options.maxdepth;
3370 +
3371 + /* If we've already seen this directory on this branch,
3372 + don't descend it again. */
3373 + for (i = 0; i <= dir_curr; i++)
3374 + if (stat_buf.st_ino == dir_ids[i].ino &&
3375 + stat_buf.st_dev == dir_ids[i].dev)
3376 + {
3377 + state.stop_at_current_level = true;
3378 + issue_loop_warning(name, pathname, i);
3379 + }
3380 +
3381 + if (dir_alloc <= ++dir_curr)
3382 + {
3383 + dir_alloc += DIR_ALLOC_STEP;
3384 + dir_ids = (struct dir_id *)
3385 + xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id));
3386 + }
3387 + dir_ids[dir_curr].ino = stat_buf.st_ino;
3388 + dir_ids[dir_curr].dev = stat_buf.st_dev;
3389 +
3390 + if (options.stay_on_filesystem)
3391 + {
3392 + if (state.curdepth == 0)
3393 + root_dev = stat_buf.st_dev;
3394 + else if (stat_buf.st_dev != root_dev)
3395 + state.stop_at_current_level = true;
3396 + }
3397 +
3398 + if (options.do_dir_first && state.curdepth >= options.mindepth)
3399 + apply_predicate (pathname, &stat_buf, eval_tree);
3400 +
3401 + if (options.debug_options & DebugSearch)
3402 + fprintf(stderr, "pathname = %s, stop_at_current_level = %d\n",
3403 + pathname, state.stop_at_current_level);
3404 +
3405 + if (state.stop_at_current_level == false)
3406 + {
3407 + /* Scan directory on disk. */
3408 + process_dir (pathname, name, strlen (pathname), &stat_buf, parent);
3409 + }
3410 +
3411 + if (options.do_dir_first == false && state.curdepth >= options.mindepth)
3412 + {
3413 + /* The fields in 'state' are now out of date. Correct them.
3414 + */
3415 + if (!digest_mode(mode, pathname, name, &stat_buf, leaf))
3416 + return 0;
3417 +
3418 + if (0 == dir_curr)
3419 + {
3420 + at_top(pathname, mode, &stat_buf, do_process_predicate);
3421 + }
3422 + else
3423 + {
3424 + do_process_predicate(pathname, name, mode, &stat_buf);
3425 + }
3426 + }
3427 +
3428 + dir_curr--;
3429 +
3430 + return 1;
3431 +}
3432 +
3433 +
3434 +/* Scan directory PATHNAME and recurse through process_path for each entry.
3435 +
3436 + PATHLEN is the length of PATHNAME.
3437 +
3438 + NAME is PATHNAME relative to the current directory.
3439 +
3440 + STATP is the results of *options.xstat on it.
3441 +
3442 + PARENT is the path of the parent of NAME, relative to find's
3443 + starting directory. */
3444 +
3445 +static void
3446 +process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)
3447 +{
3448 + int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */
3449 + boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */
3450 + unsigned int idx; /* Which entry are we on? */
3451 + struct stat stat_buf;
3452 + size_t dircount = 0u;
3453 + struct savedir_dirinfo *dirinfo;
3454 +#if 0
3455 + printf("process_dir: pathname=%s name=%s statp->st_nlink=%d st_ino=%d\n",
3456 + pathname,
3457 + name,
3458 + (int)statp->st_nlink,
3459 + (int)statp->st_ino);
3460 +#endif
3461 + if (statp->st_nlink < 2)
3462 + {
3463 + subdirs_unreliable = true;
3464 + subdirs_left = 0;
3465 + }
3466 + else
3467 + {
3468 + subdirs_unreliable = false; /* not necessarily right */
3469 + subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */
3470 + }
3471 +
3472 + errno = 0;
3473 + dirinfo = xsavedir(name, 0);
3474 +
3475 +
3476 + if (dirinfo == NULL)
3477 + {
3478 + assert (errno != 0);
3479 + error (0, errno, "%s", safely_quote_err_filename(0, pathname));
3480 + state.exit_status = 1;
3481 + }
3482 + else
3483 + {
3484 + register char *namep; /* Current point in `name_space'. */
3485 + char *cur_path; /* Full path of each file to process. */
3486 + char *cur_name; /* Base name of each file to process. */
3487 + unsigned cur_path_size; /* Bytes allocated for `cur_path'. */
3488 + register unsigned file_len; /* Length of each path to process. */
3489 + register unsigned pathname_len; /* PATHLEN plus trailing '/'. */
3490 + boolean did_stat = false;
3491 +
3492 + if (pathname[pathlen - 1] == '/')
3493 + pathname_len = pathlen + 1; /* For '\0'; already have '/'. */
3494 + else
3495 + pathname_len = pathlen + 2; /* For '/' and '\0'. */
3496 + cur_path_size = 0;
3497 + cur_path = NULL;
3498 +
3499 + /* We're about to leave the directory. If there are any
3500 + * -execdir argument lists which have been built but have not
3501 + * yet been processed, do them now because they must be done in
3502 + * the same directory.
3503 + */
3504 + complete_pending_execdirs(get_current_dirfd());
3505 +
3506 + if (strcmp (name, "."))
3507 + {
3508 + enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat);
3509 + switch (status)
3510 + {
3511 + case SafeChdirOK:
3512 + /* If there had been a change but wd_sanity_check()
3513 + * accepted it, we need to accept that on the
3514 + * way back up as well, so modify our record
3515 + * of what we think we should see later.
3516 + * If there was no change, the assignments are a no-op.
3517 + *
3518 + * However, before performing the assignment, we need to
3519 + * check that we have the stat information. If O_NOFOLLOW
3520 + * is available, safely_chdir() will not have needed to use
3521 + * stat(), and so stat_buf will just contain random data.
3522 + */
3523 + if (!did_stat)
3524 + {
3525 + /* If there is a link we need to follow it. Hence
3526 + * the direct call to stat() not through (options.xstat)
3527 + */
3528 + set_stat_placeholders(&stat_buf);
3529 + if (0 != stat(".", &stat_buf))
3530 + break; /* skip the assignment. */
3531 + }
3532 + dir_ids[dir_curr].dev = stat_buf.st_dev;
3533 + dir_ids[dir_curr].ino = stat_buf.st_ino;
3534 +
3535 + break;
3536 +
3537 + case SafeChdirFailWouldBeUnableToReturn:
3538 + error (0, errno, ".");
3539 + state.exit_status = 1;
3540 + break;
3541 +
3542 + case SafeChdirFailNonexistent:
3543 + case SafeChdirFailDestUnreadable:
3544 + case SafeChdirFailStat:
3545 + case SafeChdirFailNotDir:
3546 + case SafeChdirFailChdirFailed:
3547 + error (0, errno, "%s",
3548 + safely_quote_err_filename(0, pathname));
3549 + state.exit_status = 1;
3550 + return;
3551 +
3552 + case SafeChdirFailSymlink:
3553 + error (0, 0,
3554 + _("warning: not following the symbolic link %s"),
3555 + safely_quote_err_filename(0, pathname));
3556 + state.exit_status = 1;
3557 + return;
3558 + }
3559 + }
3560 +
3561 + for (idx=0; idx < dirinfo->size; ++idx)
3562 + {
3563 + /* savedirinfo() may return dirinfo=NULL if extended information
3564 + * is not available.
3565 + */
3566 + mode_t mode = (dirinfo->entries[idx].flags & SavedirHaveFileType) ?
3567 + dirinfo->entries[idx].type_info : 0;
3568 + namep = dirinfo->entries[idx].name;
3569 +
3570 + /* Append this directory entry's name to the path being searched. */
3571 + file_len = pathname_len + strlen (namep);
3572 + if (file_len > cur_path_size)
3573 + {
3574 + while (file_len > cur_path_size)
3575 + cur_path_size += 1024;
3576 + if (cur_path)
3577 + free (cur_path);
3578 + cur_path = xmalloc (cur_path_size);
3579 + strcpy (cur_path, pathname);
3580 + cur_path[pathname_len - 2] = '/';
3581 + }
3582 + cur_name = cur_path + pathname_len - 1;
3583 + strcpy (cur_name, namep);
3584 +
3585 + state.curdepth++;
3586 + if (!options.no_leaf_check && !subdirs_unreliable)
3587 + {
3588 + if (mode && S_ISDIR(mode) && (subdirs_left == 0))
3589 + {
3590 + /* This is a subdirectory, but the number of directories we
3591 + * have found now exceeds the number we would expect given
3592 + * the hard link count on the parent. This is likely to be
3593 + * a bug in the file system driver (e.g. Linux's
3594 + * /proc file system) or may just be a fact that the OS
3595 + * doesn't really handle hard links with Unix semantics.
3596 + * In the latter case, -noleaf should be used routinely.
3597 + */
3598 + error(0, 0, _("WARNING: Hard link count is wrong for %1$s "
3599 + "(saw only st_nlink=%2$d but we already saw %3$d subdirectories): "
3600 + "this may be a bug in your file system driver. "
3601 + "Automatically turning on find's -noleaf option. "
3602 + "Earlier results may have failed to include directories "
3603 + "that should have been searched."),
3604 + safely_quote_err_filename(0, pathname),
3605 + statp->st_nlink,
3606 + dircount);
3607 + state.exit_status = 1; /* We know the result is wrong, now */
3608 + options.no_leaf_check = true; /* Don't make same
3609 + mistake again */
3610 + subdirs_unreliable = 1;
3611 + subdirs_left = 1; /* band-aid for this iteration. */
3612 + }
3613 +
3614 + /* Normal case optimization. On normal Unix
3615 + file systems, a directory that has no subdirectories
3616 + has two links: its name, and ".". Any additional
3617 + links are to the ".." entries of its subdirectories.
3618 + Once we have processed as many subdirectories as
3619 + there are additional links, we know that the rest of
3620 + the entries are non-directories -- in other words,
3621 + leaf files. */
3622 + {
3623 + int count;
3624 + count = process_path (cur_path, cur_name,
3625 + subdirs_left == 0, pathname,
3626 + mode);
3627 + subdirs_left -= count;
3628 + dircount += count;
3629 + }
3630 + }
3631 + else
3632 + {
3633 + /* There might be weird (e.g., CD-ROM or MS-DOS) file systems
3634 + mounted, which don't have Unix-like directory link counts. */
3635 + process_path (cur_path, cur_name, false, pathname, mode);
3636 + }
3637 +
3638 + state.curdepth--;
3639 + }
3640 +
3641 +
3642 + /* We're about to leave the directory. If there are any
3643 + * -execdir argument lists which have been built but have not
3644 + * yet been processed, do them now because they must be done in
3645 + * the same directory.
3646 + */
3647 + complete_pending_execdirs(get_current_dirfd());
3648 +
3649 + if (strcmp (name, "."))
3650 + {
3651 + enum SafeChdirStatus status;
3652 + struct dir_id did;
3653 +
3654 + /* We could go back and do the next command-line arg
3655 + instead, maybe using longjmp. */
3656 + char const *dir;
3657 + boolean deref = following_links() ? true : false;
3658 +
3659 + if ( (state.curdepth>0) && !deref)
3660 + dir = "..";
3661 + else
3662 + {
3663 + chdir_back ();
3664 + dir = parent;
3665 + }
3666 +
3667 + did_stat = false;
3668 + status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat);
3669 + switch (status)
3670 + {
3671 + case SafeChdirOK:
3672 + break;
3673 +
3674 + case SafeChdirFailWouldBeUnableToReturn:
3675 + error (1, errno, ".");
3676 + return;
3677 +
3678 + case SafeChdirFailNonexistent:
3679 + case SafeChdirFailDestUnreadable:
3680 + case SafeChdirFailStat:
3681 + case SafeChdirFailSymlink:
3682 + case SafeChdirFailNotDir:
3683 + case SafeChdirFailChdirFailed:
3684 + error (1, errno, "%s", safely_quote_err_filename(0, pathname));
3685 + return;
3686 + }
3687 +
3688 + if (dir_curr > 0)
3689 + {
3690 + did.dev = dir_ids[dir_curr-1].dev;
3691 + did.ino = dir_ids[dir_curr-1].ino;
3692 + }
3693 + else
3694 + {
3695 + did.dev = starting_stat_buf.st_dev;
3696 + did.ino = starting_stat_buf.st_ino;
3697 + }
3698 + }
3699 +
3700 + if (cur_path)
3701 + free (cur_path);
3702 + free_dirinfo(dirinfo);
3703 + }
3704 +
3705 + if (subdirs_unreliable)
3706 + {
3707 + /* Make sure we hasn't used the variable subdirs_left if we knew
3708 + * we shouldn't do so.
3709 + */
3710 + assert (0 == subdirs_left || options.no_leaf_check);
3711 + }
3712 +}
3713 diff -purN findutils-4.3.12.orig/find/parser.c findutils-4.3.12/find/parser.c
3714 --- findutils-4.3.12.orig/find/parser.c 2007-12-19 16:12:34.000000000 -0500
3715 +++ findutils-4.3.12/find/parser.c 2008-01-30 08:46:05.754843619 -0500
3716 @@ -53,6 +53,13 @@
3717 #include <unistd.h>
3718 #include <sys/stat.h>
3719
3720 +#ifdef WITH_SELINUX
3721 +#include <selinux/selinux.h>
3722 +int optionh_getfilecon(const char *name, security_context_t *p);
3723 +int optionl_getfilecon(const char *name, security_context_t *p);
3724 +int optionp_getfilecon(const char *name, security_context_t *p);
3725 +#endif /*WITH_SELINUX*/
3726 +
3727 #if ENABLE_NLS
3728 # include <libintl.h>
3729 # define _(Text) gettext (Text)
3730 @@ -156,6 +163,9 @@ static boolean parse_noignore_race PARAM
3731 static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
3732 static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
3733 static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
3734 +#ifdef WITH_SELINUX
3735 +static boolean parse_scontext PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
3736 +#endif /*WITH_SELINUX*/
3737
3738 boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
3739
3740 @@ -341,6 +351,8 @@ static struct parser_table const parse_t
3741 {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
3742 {ARG_TEST, "version", parse_version, NULL}, /* GNU */
3743 {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
3744 + {ARG_TEST, "context", parse_scontext, pred_scontext}, /* SELinux */
3745 + {ARG_TEST, "-context", parse_scontext, pred_scontext}, /* SELinux */
3746 {0, 0, 0, 0}
3747 };
3748
3749 @@ -452,10 +464,16 @@ set_follow_state(enum SymlinkOption opt)
3750 case SYMLINK_ALWAYS_DEREF: /* -L */
3751 options.xstat = optionl_stat;
3752 options.no_leaf_check = true;
3753 +#ifdef WITH_SELINUX
3754 + options.x_getfilecon = optionl_getfilecon;
3755 +#endif /* WITH_SELINUX */
3756 break;
3757
3758 case SYMLINK_NEVER_DEREF: /* -P (default) */
3759 options.xstat = optionp_stat;
3760 +#ifdef WITH_SELINUX
3761 + options.x_getfilecon = optionp_getfilecon;
3762 +#endif /* WITH_SELINUX */
3763 /* Can't turn no_leaf_check off because the user might have specified
3764 * -noleaf anyway
3765 */
3766 @@ -464,6 +482,9 @@ set_follow_state(enum SymlinkOption opt)
3767 case SYMLINK_DEREF_ARGSONLY: /* -H */
3768 options.xstat = optionh_stat;
3769 options.no_leaf_check = true;
3770 +#ifdef WITH_SELINUX
3771 + options.x_getfilecon = optionh_getfilecon;
3772 +#endif /* WITH_SELINUX */
3773 }
3774 }
3775 options.symlink_handling = opt;
3776 @@ -667,6 +688,94 @@ collect_arg_stat_info(char **argv, int *
3777
3778 The predicate structure is updated with the new information. */
3779
3780 +#ifdef WITH_SELINUX
3781 +
3782 +static int
3783 +fallback_getfilecon(const char *name, security_context_t *p, int prev_rv)
3784 +{
3785 + /* Our original getfilecon() call failed. Perhaps we can't follow a
3786 + * symbolic link. If that might be the problem, lgetfilecon() the link.
3787 + * Otherwise, admit defeat.
3788 + */
3789 + switch (errno)
3790 + {
3791 + case ENOENT:
3792 + case ENOTDIR:
3793 +#ifdef DEBUG_STAT
3794 + fprintf(stderr, "fallback_getfilecon(): getfilecon(%s) failed; falling back on lgetfilecon()\n", name);
3795 +#endif
3796 + return lgetfilecon(name, p);
3797 +
3798 + case EACCES:
3799 + case EIO:
3800 + case ELOOP:
3801 + case ENAMETOOLONG:
3802 +#ifdef EOVERFLOW
3803 + case EOVERFLOW: /* EOVERFLOW is not #defined on UNICOS. */
3804 +#endif
3805 + default:
3806 + return prev_rv;
3807 + }
3808 +}
3809 +
3810 +
3811 +/* optionh_getfilecon() implements the getfilecon operation when the
3812 + * -H option is in effect.
3813 + *
3814 + * If the item to be examined is a command-line argument, we follow
3815 + * symbolic links. If the getfilecon() call fails on the command-line
3816 + * item, we fall back on the properties of the symbolic link.
3817 + *
3818 + * If the item to be examined is not a command-line argument, we
3819 + * examine the link itself.
3820 + */
3821 +int
3822 +optionh_getfilecon(const char *name, security_context_t *p)
3823 +{
3824 + if (0 == state.curdepth)
3825 + {
3826 + /* This file is from the command line; deference the link (if it
3827 + * is a link).
3828 + */
3829 + int rv = getfilecon(name, p);
3830 + if (0 == rv)
3831 + return 0; /* success */
3832 + else
3833 + return fallback_getfilecon(name, p, rv);
3834 + }
3835 + else
3836 + {
3837 + /* Not a file on the command line; do not derefernce the link.
3838 + */
3839 + return lgetfilecon(name, p);
3840 + }
3841 +}
3842 +
3843 +/* optionl_getfilecon() implements the getfilecon operation when the
3844 + * -L option is in effect. That option makes us examine the thing the
3845 + * symbolic link points to, not the symbolic link itself.
3846 + */
3847 +int
3848 +optionl_getfilecon(const char *name, security_context_t *p)
3849 +{
3850 + int rv = getfilecon(name, p);
3851 + if (0 == rv)
3852 + return 0; /* normal case. */
3853 + else
3854 + return fallback_getfilecon(name, p, rv);
3855 +}
3856 +
3857 +/* optionp_getfilecon() implements the stat operation when the -P
3858 + * option is in effect (this is also the default). That option makes
3859 + * us examine the symbolic link itself, not the thing it points to.
3860 + */
3861 +int
3862 +optionp_getfilecon(const char *name, security_context_t *p)
3863 +{
3864 + return lgetfilecon(name, p);
3865 +}
3866 +#endif /* WITH_SELINUX */
3867 +
3868
3869 static boolean
3870 parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
3871 @@ -1124,6 +1233,10 @@ tests (N can be +N or -N or N): -amin N
3872 -readable -writable -executable\n\
3873 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
3874 -used N -user NAME -xtype [bcdpfls]\n"));
3875 +#ifdef WITH_SELINUX
3876 + puts (_("\
3877 + -context CONTEXT\n"));
3878 +#endif /*WITH_SELINUX*/
3879 puts (_("\
3880 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
3881 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
3882 @@ -2522,6 +2635,29 @@ parse_version (const struct parser_table
3883 exit (0);
3884 }
3885
3886 +#ifdef WITH_SELINUX
3887 +
3888 +static boolean
3889 +parse_scontext ( const struct parser_table* entry, char **argv, int *arg_ptr)
3890 +{
3891 + struct predicate *our_pred;
3892 +
3893 + if ( (argv == NULL) || (argv[*arg_ptr] == NULL) )
3894 + return( false );
3895 +
3896 + our_pred = insert_primary(entry);
3897 + our_pred->need_stat = false;
3898 +#ifdef DEBUG
3899 + our_pred->p_name = find_pred_name (pred_scontext);
3900 +#endif /*DEBUG*/
3901 + our_pred->args.scontext = argv[*arg_ptr];;
3902 +
3903 + (*arg_ptr)++;
3904 + return( true );
3905 +}
3906 +
3907 +#endif /*WITH_SELINUX*/
3908 +
3909 static boolean
3910 parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
3911 {
3912 @@ -2773,7 +2909,11 @@ insert_fprintf (struct format_val *vec,
3913 if (*scan2 == '.')
3914 for (scan2++; ISDIGIT (*scan2); scan2++)
3915 /* Do nothing. */ ;
3916 +#ifdef WITH_SELINUX
3917 + if (strchr ("abcdDfFgGhHiklmMnpPsStuUyYZ", *scan2))
3918 +#else
3919 if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2))
3920 +#endif
3921 {
3922 segmentp = make_segment (segmentp, format, scan2 - format,
3923 KIND_FORMAT, *scan2, 0,
3924 diff -purN findutils-4.3.12.orig/find/parser.c.orig findutils-4.3.12/find/parser.c.orig
3925 --- findutils-4.3.12.orig/find/parser.c.orig 1969-12-31 19:00:00.000000000 -0500
3926 +++ findutils-4.3.12/find/parser.c.orig 2007-12-19 16:12:34.000000000 -0500
3927 @@ -0,0 +1,3502 @@
3928 +/* parser.c -- convert the command line args into an expression tree.
3929 + Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
3930 + 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
3931 +
3932 + This program is free software: you can redistribute it and/or modify
3933 + it under the terms of the GNU General Public License as published by
3934 + the Free Software Foundation, either version 3 of the License, or
3935 + (at your option) any later version.
3936 +
3937 + This program is distributed in the hope that it will be useful,
3938 + but WITHOUT ANY WARRANTY; without even the implied warranty of
3939 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3940 + GNU General Public License for more details.
3941 +
3942 + You should have received a copy of the GNU General Public License
3943 + along with this program. If not, see <http://www.gnu.org/licenses/>.
3944 +*/
3945 +
3946 +#include <config.h>
3947 +
3948 +#include "defs.h"
3949 +#include <ctype.h>
3950 +#include <math.h>
3951 +#include <assert.h>
3952 +#include <pwd.h>
3953 +#include <errno.h>
3954 +#include <grp.h>
3955 +#include <fnmatch.h>
3956 +#include "modechange.h"
3957 +#include "modetype.h"
3958 +#include "xstrtol.h"
3959 +#include "xalloc.h"
3960 +#include "quote.h"
3961 +#include "quotearg.h"
3962 +#include "buildcmd.h"
3963 +#include "nextelem.h"
3964 +#include "stdio-safer.h"
3965 +#include "regextype.h"
3966 +#include "stat-time.h"
3967 +#include "xstrtod.h"
3968 +#include "fts_.h"
3969 +#include "getdate.h"
3970 +#include "error.h"
3971 +#include "findutils-version.h"
3972 +
3973 +#include <fcntl.h>
3974 +
3975 +
3976 +/* The presence of unistd.h is assumed by gnulib these days, so we
3977 + * might as well assume it too.
3978 + */
3979 +/* We need <unistd.h> for isatty(). */
3980 +#include <unistd.h>
3981 +#include <sys/stat.h>
3982 +
3983 +#if ENABLE_NLS
3984 +# include <libintl.h>
3985 +# define _(Text) gettext (Text)
3986 +#else
3987 +# define _(Text) Text
3988 +#endif
3989 +#ifdef gettext_noop
3990 +# define N_(String) gettext_noop (String)
3991 +#else
3992 +/* See locate.c for explanation as to why not use (String) */
3993 +# define N_(String) String
3994 +#endif
3995 +
3996 +#if !defined (isascii) || defined (STDC_HEADERS)
3997 +#ifdef isascii
3998 +#undef isascii
3999 +#endif
4000 +#define isascii(c) 1
4001 +#endif
4002 +
4003 +#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
4004 +#define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
4005 +
4006 +#ifndef HAVE_ENDGRENT
4007 +#define endgrent()
4008 +#endif
4009 +#ifndef HAVE_ENDPWENT
4010 +#define endpwent()
4011 +#endif
4012 +
4013 +static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr));
4014 +static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4015 +static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4016 +static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4017 +static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4018 +static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4019 +static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4020 +static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4021 +static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4022 +static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4023 +static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4024 +static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4025 +static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4026 +static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4027 +static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4028 +static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4029 +static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4030 +static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4031 +static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4032 +static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4033 +static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4034 +static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4035 +static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4036 +static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4037 +static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4038 +static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4039 +static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4040 +static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4041 +static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4042 +static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4043 +static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4044 +static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4045 +static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4046 +static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4047 +static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4048 +static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4049 +static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4050 +static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4051 +static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4052 +static boolean parse_newerXY PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4053 +static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4054 +static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4055 +static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4056 +static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4057 +static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4058 +static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4059 +static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4060 +static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4061 +static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4062 +static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4063 +static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4064 +static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4065 +static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4066 +static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4067 +static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4068 +#if 0
4069 +static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4070 +#endif
4071 +static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4072 +static boolean parse_time PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4073 +static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4074 +static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4075 +static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4076 +static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4077 +static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4078 +static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4079 +static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4080 +static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4081 +static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4082 +static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4083 +static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4084 +static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4085 +static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4086 +
4087 +boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
4088 +
4089 +
4090 +static boolean insert_type PARAMS((char **argv, int *arg_ptr,
4091 + const struct parser_table *entry,
4092 + PRED_FUNC which_pred));
4093 +static boolean insert_regex PARAMS((char *argv[], int *arg_ptr,
4094 + const struct parser_table *entry,
4095 + int regex_options));
4096 +static boolean insert_fprintf (struct format_val *vec,
4097 + const struct parser_table *entry,
4098 + PRED_FUNC func,
4099 + const char *format);
4100 +
4101 +static struct segment **make_segment PARAMS((struct segment **segment,
4102 + char *format, int len,
4103 + int kind, char format_char,
4104 + char aux_format_char,
4105 + struct predicate *pred));
4106 +static boolean insert_exec_ok PARAMS((const char *action,
4107 + const struct parser_table *entry,
4108 + int dirfd,
4109 + char *argv[],
4110 + int *arg_ptr));
4111 +static boolean get_comp_type PARAMS((const char **str,
4112 + enum comparison_type *comp_type));
4113 +static boolean get_relative_timestamp PARAMS((const char *str,
4114 + struct time_val *tval,
4115 + time_t origin,
4116 + double sec_per_unit,
4117 + const char *overflowmessage));
4118 +static boolean get_num PARAMS((const char *str,
4119 + uintmax_t *num,
4120 + enum comparison_type *comp_type));
4121 +static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr,
4122 + const struct parser_table *entry));
4123 +static void open_output_file (const char *path, struct format_val *p);
4124 +static void open_stdout (struct format_val *p);
4125 +static boolean stream_is_tty(FILE *fp);
4126 +static boolean parse_noop PARAMS((const struct parser_table* entry,
4127 + char **argv, int *arg_ptr));
4128 +
4129 +#define PASTE(x,y) x##y
4130 +#define STRINGIFY(s) #s
4131 +
4132 +#define PARSE_OPTION(what,suffix) \
4133 + { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
4134 +
4135 +#define PARSE_POSOPT(what,suffix) \
4136 + { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
4137 +
4138 +#define PARSE_TEST(what,suffix) \
4139 + { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
4140 +
4141 +#define PARSE_TEST_NP(what,suffix) \
4142 + { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
4143 +
4144 +#define PARSE_ACTION(what,suffix) \
4145 + { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
4146 +
4147 +#define PARSE_ACTION_NP(what,suffix) \
4148 + { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
4149 +
4150 +#define PARSE_PUNCTUATION(what,suffix) \
4151 + { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
4152 +
4153 +
4154 +/* Predicates we cannot handle in the usual way. If you add an entry
4155 + * to this table, double-check the switch statement in
4156 + * pred_sanity_check() to make sure that the new case is being
4157 + * correctly handled.
4158 + */
4159 +static struct parser_table const parse_entry_newerXY =
4160 + {
4161 + ARG_SPECIAL_PARSE, "newerXY", parse_newerXY, pred_newerXY /* BSD */
4162 + };
4163 +
4164 +/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
4165 + If they are in some Unix versions of find, they are marked `Unix'. */
4166 +
4167 +static struct parser_table const parse_table[] =
4168 +{
4169 + PARSE_PUNCTUATION("!", negate), /* POSIX */
4170 + PARSE_PUNCTUATION("not", negate), /* GNU */
4171 + PARSE_PUNCTUATION("(", openparen), /* POSIX */
4172 + PARSE_PUNCTUATION(")", closeparen), /* POSIX */
4173 + PARSE_PUNCTUATION(",", comma), /* GNU */
4174 + PARSE_PUNCTUATION("a", and), /* POSIX */
4175 + PARSE_TEST ("amin", amin), /* GNU */
4176 + PARSE_PUNCTUATION("and", and), /* GNU */
4177 + PARSE_TEST ("anewer", anewer), /* GNU */
4178 + {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */
4179 + PARSE_TEST ("cmin", cmin), /* GNU */
4180 + PARSE_TEST ("cnewer", cnewer), /* GNU */
4181 + {ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */
4182 + PARSE_POSOPT ("daystart", daystart), /* GNU */
4183 + PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
4184 + PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
4185 + PARSE_OPTION ("depth", depth), /* POSIX */
4186 + PARSE_TEST ("empty", empty), /* GNU */
4187 + {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */
4188 + {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */
4189 + PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
4190 + PARSE_ACTION ("fls", fls), /* GNU */
4191 + PARSE_POSOPT ("follow", follow), /* GNU, Unix */
4192 + PARSE_ACTION ("fprint", fprint), /* GNU */
4193 + PARSE_ACTION ("fprint0", fprint0), /* GNU */
4194 + {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */
4195 + PARSE_TEST ("fstype", fstype), /* GNU, Unix */
4196 + PARSE_TEST ("gid", gid), /* GNU */
4197 + PARSE_TEST ("group", group), /* POSIX */
4198 + PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
4199 + PARSE_TEST ("ilname", ilname), /* GNU */
4200 + PARSE_TEST ("iname", iname), /* GNU */
4201 + PARSE_TEST ("inum", inum), /* GNU, Unix */
4202 + PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
4203 + PARSE_TEST_NP ("iregex", iregex), /* GNU */
4204 + PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
4205 + PARSE_TEST ("links", links), /* POSIX */
4206 + PARSE_TEST ("lname", lname), /* GNU */
4207 + PARSE_ACTION ("ls", ls), /* GNU, Unix */
4208 + PARSE_OPTION ("maxdepth", maxdepth), /* GNU */
4209 + PARSE_OPTION ("mindepth", mindepth), /* GNU */
4210 + PARSE_TEST ("mmin", mmin), /* GNU */
4211 + PARSE_OPTION ("mount", xdev), /* Unix */
4212 + {ARG_TEST, "mtime", parse_time, pred_mtime}, /* POSIX */
4213 + PARSE_TEST ("name", name),
4214 +#ifdef UNIMPLEMENTED_UNIX
4215 + PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */
4216 +#endif
4217 + PARSE_TEST ("newer", newer), /* POSIX */
4218 + {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */
4219 + PARSE_OPTION ("noleaf", noleaf), /* GNU */
4220 + PARSE_TEST ("nogroup", nogroup), /* POSIX */
4221 + PARSE_TEST ("nouser", nouser), /* POSIX */
4222 + PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */
4223 + PARSE_POSOPT ("nowarn", nowarn), /* GNU */
4224 + PARSE_PUNCTUATION("o", or), /* POSIX */
4225 + PARSE_PUNCTUATION("or", or), /* GNU */
4226 + PARSE_ACTION ("ok", ok), /* POSIX */
4227 + PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */
4228 + PARSE_TEST ("path", path), /* GNU, HP-UX, RMS prefers wholename, but anyway soon POSIX */
4229 + PARSE_TEST ("perm", perm), /* POSIX */
4230 + PARSE_ACTION ("print", print), /* POSIX */
4231 + PARSE_ACTION ("print0", print0), /* GNU */
4232 + {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */
4233 + PARSE_ACTION ("prune", prune), /* POSIX */
4234 + PARSE_ACTION ("quit", quit), /* GNU */
4235 + {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */
4236 + PARSE_TEST ("regex", regex), /* GNU */
4237 + PARSE_OPTION ("regextype", regextype), /* GNU */
4238 + PARSE_TEST ("samefile", samefile), /* GNU */
4239 +#if 0
4240 + PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */
4241 +#endif
4242 + PARSE_TEST ("size", size), /* POSIX */
4243 + PARSE_TEST ("type", type), /* POSIX */
4244 + PARSE_TEST ("uid", uid), /* GNU */
4245 + PARSE_TEST ("used", used), /* GNU */
4246 + PARSE_TEST ("user", user), /* POSIX */
4247 + PARSE_OPTION ("warn", warn), /* GNU */
4248 + PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */
4249 + {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */
4250 + PARSE_OPTION ("xdev", xdev), /* POSIX */
4251 + PARSE_TEST ("xtype", xtype), /* GNU */
4252 +#ifdef UNIMPLEMENTED_UNIX
4253 + /* It's pretty ugly for find to know about archive formats.
4254 + Plus what it could do with cpio archives is very limited.
4255 + Better to leave it out. */
4256 + PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */
4257 +#endif
4258 + /* gnulib's stdbool.h might have made true and false into macros,
4259 + * so we can't leave named 'true' and 'false' tokens, so we have
4260 + * to expeant the relevant entries longhand.
4261 + */
4262 + {ARG_TEST, "false", parse_false, pred_false}, /* GNU */
4263 + {ARG_TEST, "true", parse_true, pred_true }, /* GNU */
4264 + {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */
4265 +
4266 + /* Various other cases that don't fit neatly into our macro scheme. */
4267 + {ARG_TEST, "help", parse_help, NULL}, /* GNU */
4268 + {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
4269 + {ARG_TEST, "version", parse_version, NULL}, /* GNU */
4270 + {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
4271 + {0, 0, 0, 0}
4272 +};
4273 +
4274 +
4275 +static const char *first_nonoption_arg = NULL;
4276 +static const struct parser_table *noop = NULL;
4277 +
4278 +
4279 +void
4280 +check_option_combinations(const struct predicate *p)
4281 +{
4282 + enum { seen_delete=1u, seen_prune=2u };
4283 + unsigned int predicates = 0u;
4284 +
4285 + while (p)
4286 + {
4287 + if (p->pred_func == pred_delete)
4288 + predicates |= seen_delete;
4289 + else if (p->pred_func == pred_prune)
4290 + predicates |= seen_prune;
4291 + p = p->pred_next;
4292 + }
4293 +
4294 + if ((predicates & seen_prune) && (predicates & seen_delete))
4295 + {
4296 + /* The user specified both -delete and -prune. One might test
4297 + * this by first doing
4298 + * find dirs .... -prune ..... -print
4299 + * to fnd out what's going to get deleted, and then switch to
4300 + * find dirs .... -prune ..... -delete
4301 + * once we are happy. Unfortunately, the -delete action also
4302 + * implicitly turns on -depth, which will affect the behaviour
4303 + * of -prune (in fact, it makes it a no-op). In this case we
4304 + * would like to prevent unfortunate accidents, so we require
4305 + * the user to have explicitly used -depth.
4306 + *
4307 + * We only get away with this because the -delete predicate is not
4308 + * in POSIX. If it was, we couldn't issue a fatal error here.
4309 + */
4310 + if (!options.explicit_depth)
4311 + {
4312 + /* This fixes Savannah bug #20865. */
4313 + error (1, 0, _("The -delete action atomatically turns on -depth, "
4314 + "but -prune does nothing when -depth is in effect. "
4315 + "If you want to carry on anyway, just explicitly use "
4316 + "the -depth option."));
4317 + }
4318 + }
4319 +}
4320 +
4321 +
4322 +static const struct parser_table*
4323 +get_noop(void)
4324 +{
4325 + int i;
4326 + if (NULL == noop)
4327 + {
4328 + for (i = 0; parse_table[i].parser_name != 0; i++)
4329 + {
4330 + if (ARG_NOOP ==parse_table[i].type)
4331 + {
4332 + noop = &(parse_table[i]);
4333 + break;
4334 + }
4335 + }
4336 + }
4337 + return noop;
4338 +}
4339 +
4340 +static int
4341 +get_stat_Ytime(const struct stat *p,
4342 + char what,
4343 + struct timespec *ret)
4344 +{
4345 + switch (what)
4346 + {
4347 + case 'a':
4348 + *ret = get_stat_atime(p);
4349 + return 1;
4350 + case 'B':
4351 + *ret = get_stat_birthtime(p);
4352 + return (ret->tv_nsec >= 0);
4353 + case 'c':
4354 + *ret = get_stat_ctime(p);
4355 + return 1;
4356 + case 'm':
4357 + *ret = get_stat_mtime(p);
4358 + return 1;
4359 + default:
4360 + assert (0);
4361 + abort();
4362 + }
4363 +}
4364 +
4365 +void
4366 +set_follow_state(enum SymlinkOption opt)
4367 +{
4368 + if (options.debug_options & DebugStat)
4369 + {
4370 + /* For DebugStat, the choice is made at runtime within debug_stat()
4371 + * by checking the contents of the symlink_handling variable.
4372 + */
4373 + options.xstat = debug_stat;
4374 + }
4375 + else
4376 + {
4377 + switch (opt)
4378 + {
4379 + case SYMLINK_ALWAYS_DEREF: /* -L */
4380 + options.xstat = optionl_stat;
4381 + options.no_leaf_check = true;
4382 + break;
4383 +
4384 + case SYMLINK_NEVER_DEREF: /* -P (default) */
4385 + options.xstat = optionp_stat;
4386 + /* Can't turn no_leaf_check off because the user might have specified
4387 + * -noleaf anyway
4388 + */
4389 + break;
4390 +
4391 + case SYMLINK_DEREF_ARGSONLY: /* -H */
4392 + options.xstat = optionh_stat;
4393 + options.no_leaf_check = true;
4394 + }
4395 + }
4396 + options.symlink_handling = opt;
4397 +}
4398 +
4399 +
4400 +void
4401 +parse_begin_user_args (char **args, int argno,
4402 + const struct predicate *last,
4403 + const struct predicate *predicates)
4404 +{
4405 + (void) args;
4406 + (void) argno;
4407 + (void) last;
4408 + (void) predicates;
4409 + first_nonoption_arg = NULL;
4410 +}
4411 +
4412 +void
4413 +parse_end_user_args (char **args, int argno,
4414 + const struct predicate *last,
4415 + const struct predicate *predicates)
4416 +{
4417 + /* does nothing */
4418 + (void) args;
4419 + (void) argno;
4420 + (void) last;
4421 + (void) predicates;
4422 +}
4423 +
4424 +
4425 +/* Check that it is legal to fid the given primary in its
4426 + * position and return it.
4427 + */
4428 +const struct parser_table*
4429 +found_parser(const char *original_arg, const struct parser_table *entry)
4430 +{
4431 + /* If this is an option, but we have already had a
4432 + * non-option argument, the user may be under the
4433 + * impression that the behaviour of the option
4434 + * argument is conditional on some preceding
4435 + * tests. This might typically be the case with,
4436 + * for example, -maxdepth.
4437 + *
4438 + * The options -daystart and -follow are exempt
4439 + * from this treatment, since their positioning
4440 + * in the command line does have an effect on
4441 + * subsequent tests but not previous ones. That
4442 + * might be intentional on the part of the user.
4443 + */
4444 + if (entry->type != ARG_POSITIONAL_OPTION)
4445 + {
4446 + /* Something other than -follow/-daystart.
4447 + * If this is an option, check if it followed
4448 + * a non-option and if so, issue a warning.
4449 + */
4450 + if (entry->type == ARG_OPTION)
4451 + {
4452 + if ((first_nonoption_arg != NULL)
4453 + && options.warnings )
4454 + {
4455 + /* option which follows a non-option */
4456 + error (0, 0,
4457 + _("warning: you have specified the %1$s "
4458 + "option after a non-option argument %2$s, "
4459 + "but options are not positional (%3$s affects "
4460 + "tests specified before it as well as those "
4461 + "specified after it). Please specify options "
4462 + "before other arguments.\n"),
4463 + original_arg,
4464 + first_nonoption_arg,
4465 + original_arg);
4466 + }
4467 + }
4468 + else
4469 + {
4470 + /* Not an option or a positional option,
4471 + * so remember we've seen it in order to
4472 + * use it in a possible future warning message.
4473 + */
4474 + if (first_nonoption_arg == NULL)
4475 + {
4476 + first_nonoption_arg = original_arg;
4477 + }
4478 + }
4479 + }
4480 +
4481 + return entry;
4482 +}
4483 +
4484 +
4485 +/* Return a pointer to the parser function to invoke for predicate
4486 + SEARCH_NAME.
4487 + Return NULL if SEARCH_NAME is not a valid predicate name. */
4488 +
4489 +const struct parser_table*
4490 +find_parser (char *search_name)
4491 +{
4492 + int i;
4493 + const char *original_arg = search_name;
4494 +
4495 + /* Ugh. Special case -newerXY. */
4496 + if (0 == strncmp("-newer", search_name, 6)
4497 + && (8 == strlen(search_name)))
4498 + {
4499 + return found_parser(original_arg, &parse_entry_newerXY);
4500 + }
4501 +
4502 + if (*search_name == '-')
4503 + search_name++;
4504 +
4505 + for (i = 0; parse_table[i].parser_name != 0; i++)
4506 + {
4507 + if (strcmp (parse_table[i].parser_name, search_name) == 0)
4508 + {
4509 + return found_parser(original_arg, &parse_table[i]);
4510 + }
4511 + }
4512 + return NULL;
4513 +}
4514 +
4515 +static float
4516 +estimate_file_age_success_rate(float num_days)
4517 +{
4518 + if (num_days < 0.1)
4519 + {
4520 + /* Assume 1% of files have timestamps in the future */
4521 + return 0.01f;
4522 + }
4523 + else if (num_days < 1)
4524 + {
4525 + /* Assume 30% of files have timestamps today */
4526 + return 0.3f;
4527 + }
4528 + else if (num_days > 100)
4529 + {
4530 + /* Assume 30% of files are very old */
4531 + return 0.3f;
4532 + }
4533 + else
4534 + {
4535 + /* Assume 39% of files are between 1 and 100 days old. */
4536 + return 0.39f;
4537 + }
4538 +}
4539 +
4540 +static float
4541 +estimate_timestamp_success_rate(time_t when)
4542 +{
4543 + int num_days = (options.cur_day_start - when) / 86400;
4544 + return estimate_file_age_success_rate(num_days);
4545 +}
4546 +
4547 +/* Collect an argument from the argument list, or
4548 + * return false.
4549 + */
4550 +static boolean
4551 +collect_arg(char **argv, int *arg_ptr, const char **collected_arg)
4552 +{
4553 + if ((argv == NULL) || (argv[*arg_ptr] == NULL))
4554 + {
4555 + *collected_arg = NULL;
4556 + return false;
4557 + }
4558 + else
4559 + {
4560 + *collected_arg = argv[*arg_ptr];
4561 + (*arg_ptr)++;
4562 + return true;
4563 + }
4564 +}
4565 +
4566 +static boolean
4567 +collect_arg_stat_info(char **argv, int *arg_ptr, struct stat *p)
4568 +{
4569 + const char *filename;
4570 + if (collect_arg(argv, arg_ptr, &filename))
4571 + {
4572 + if (0 == (options.xstat)(filename, p))
4573 + {
4574 + return true;
4575 + }
4576 + else
4577 + {
4578 + fatal_file_error(filename);
4579 + }
4580 + }
4581 + else
4582 + {
4583 + return false;
4584 + }
4585 +}
4586 +
4587 +/* The parsers are responsible to continue scanning ARGV for
4588 + their arguments. Each parser knows what is and isn't
4589 + allowed for itself.
4590 +
4591 + ARGV is the argument array.
4592 + *ARG_PTR is the index to start at in ARGV,
4593 + updated to point beyond the last element consumed.
4594 +
4595 + The predicate structure is updated with the new information. */
4596 +
4597 +
4598 +static boolean
4599 +parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
4600 +{
4601 + struct predicate *our_pred;
4602 +
4603 + (void) argv;
4604 + (void) arg_ptr;
4605 +
4606 + our_pred = get_new_pred (entry);
4607 + our_pred->pred_func = pred_and;
4608 + our_pred->p_type = BI_OP;
4609 + our_pred->p_prec = AND_PREC;
4610 + our_pred->need_stat = our_pred->need_type = false;
4611 + return true;
4612 +}
4613 +
4614 +static boolean
4615 +parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
4616 +{
4617 + struct stat stat_newer;
4618 +
4619 + set_stat_placeholders(&stat_newer);
4620 + if (collect_arg_stat_info(argv, arg_ptr, &stat_newer))
4621 + {
4622 + struct predicate *our_pred = insert_primary (entry);
4623 + our_pred->args.reftime.xval = XVAL_ATIME;
4624 + our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
4625 + our_pred->args.reftime.kind = COMP_GT;
4626 + our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
4627 + return true;
4628 + }
4629 + return false;
4630 +}
4631 +
4632 +boolean
4633 +parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr)
4634 +{
4635 + struct predicate *our_pred;
4636 +
4637 + (void) argv;
4638 + (void) arg_ptr;
4639 +
4640 + our_pred = get_new_pred (entry);
4641 + our_pred->pred_func = pred_closeparen;
4642 + our_pred->p_type = CLOSE_PAREN;
4643 + our_pred->p_prec = NO_PREC;
4644 + our_pred->need_stat = our_pred->need_type = false;
4645 + return true;
4646 +}
4647 +
4648 +static boolean
4649 +parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
4650 +{
4651 + struct stat stat_newer;
4652 +
4653 + set_stat_placeholders(&stat_newer);
4654 + if (collect_arg_stat_info(argv, arg_ptr, &stat_newer))
4655 + {
4656 + struct predicate *our_pred = insert_primary (entry);
4657 + our_pred->args.reftime.xval = XVAL_CTIME; /* like -newercm */
4658 + our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
4659 + our_pred->args.reftime.kind = COMP_GT;
4660 + our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
4661 + return true;
4662 + }
4663 + return false;
4664 +}
4665 +
4666 +static boolean
4667 +parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
4668 +{
4669 + struct predicate *our_pred;
4670 +
4671 + (void) argv;
4672 + (void) arg_ptr;
4673 +
4674 + our_pred = get_new_pred (entry);
4675 + our_pred->pred_func = pred_comma;
4676 + our_pred->p_type = BI_OP;
4677 + our_pred->p_prec = COMMA_PREC;
4678 + our_pred->need_stat = our_pred->need_type = false;
4679 + our_pred->est_success_rate = 1.0f;
4680 + return true;
4681 +}
4682 +
4683 +static boolean
4684 +parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
4685 +{
4686 + struct tm *local;
4687 +
4688 + (void) entry;
4689 + (void) argv;
4690 + (void) arg_ptr;
4691 +
4692 + if (options.full_days == false)
4693 + {
4694 + options.cur_day_start += DAYSECS;
4695 + local = localtime (&options.cur_day_start);
4696 + options.cur_day_start -= (local
4697 + ? (local->tm_sec + local->tm_min * 60
4698 + + local->tm_hour * 3600)
4699 + : options.cur_day_start % DAYSECS);
4700 + options.full_days = true;
4701 + }
4702 + return true;
4703 +}
4704 +
4705 +static boolean
4706 +parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
4707 +{
4708 + struct predicate *our_pred;
4709 + (void) argv;
4710 + (void) arg_ptr;
4711 +
4712 + our_pred = insert_primary (entry);
4713 + our_pred->side_effects = our_pred->no_default_print = true;
4714 + /* -delete implies -depth */
4715 + options.do_dir_first = false;
4716 +
4717 + /* We do not need stat information because we check for the case
4718 + * (errno==EISDIR) in pred_delete.
4719 + */
4720 + our_pred->need_stat = our_pred->need_type = false;
4721 +
4722 + our_pred->est_success_rate = 1.0f;
4723 + return true;
4724 +}
4725 +
4726 +static boolean
4727 +parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
4728 +{
4729 + (void) entry;
4730 + (void) argv;
4731 +
4732 + options.do_dir_first = false;
4733 + options.explicit_depth = true;
4734 + return parse_noop(entry, argv, arg_ptr);
4735 +}
4736 +
4737 +static boolean
4738 +parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
4739 +{
4740 + if (options.warnings)
4741 + {
4742 + error (0, 0,
4743 + _("warning: the -d option is deprecated; please use "
4744 + "-depth instead, because the latter is a "
4745 + "POSIX-compliant feature."));
4746 + }
4747 + return parse_depth(entry, argv, arg_ptr);
4748 +}
4749 +
4750 +static boolean
4751 +parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
4752 +{
4753 + struct predicate *our_pred;
4754 + (void) argv;
4755 + (void) arg_ptr;
4756 +
4757 + our_pred = insert_primary (entry);
4758 + our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */
4759 + return true;
4760 +}
4761 +
4762 +static boolean
4763 +parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
4764 +{
4765 + return insert_exec_ok ("-exec", entry, get_start_dirfd(), argv, arg_ptr);
4766 +}
4767 +
4768 +static boolean
4769 +parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
4770 +{
4771 + return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr);
4772 +}
4773 +
4774 +static boolean
4775 +parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
4776 +{
4777 + struct predicate *our_pred;
4778 +
4779 + (void) argv;
4780 + (void) arg_ptr;
4781 +
4782 + our_pred = insert_primary (entry);
4783 + our_pred->need_stat = our_pred->need_type = false;
4784 + our_pred->side_effects = our_pred->no_default_print = false;
4785 + our_pred->est_success_rate = 0.0f;
4786 + return true;
4787 +}
4788 +
4789 +static boolean
4790 +insert_fls (const struct parser_table* entry, const char *filename)
4791 +{
4792 + struct predicate *our_pred = insert_primary (entry);
4793 + if (filename)
4794 + open_output_file (filename, &our_pred->args.printf_vec);
4795 + else
4796 + open_stdout (&our_pred->args.printf_vec);
4797 + our_pred->side_effects = our_pred->no_default_print = true;
4798 + our_pred->est_success_rate = 1.0f;
4799 + return true;
4800 +}
4801 +
4802 +
4803 +static boolean
4804 +parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
4805 +{
4806 + const char *filename;
4807 + return collect_arg(argv, arg_ptr, &filename)
4808 + && insert_fls(entry, filename);
4809 +}
4810 +
4811 +static boolean
4812 +parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
4813 +{
4814 + set_follow_state(SYMLINK_ALWAYS_DEREF);
4815 + return parse_noop(entry, argv, arg_ptr);
4816 +}
4817 +
4818 +static boolean
4819 +parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
4820 +{
4821 + struct predicate *our_pred;
4822 + const char *filename;
4823 + if (collect_arg(argv, arg_ptr, &filename))
4824 + {
4825 + our_pred = insert_primary (entry);
4826 + open_output_file (filename, &our_pred->args.printf_vec);
4827 + our_pred->side_effects = our_pred->no_default_print = true;
4828 + our_pred->need_stat = our_pred->need_type = false;
4829 + our_pred->est_success_rate = 1.0f;
4830 + return true;
4831 + }
4832 + else
4833 + {
4834 + return false;
4835 + }
4836 +}
4837 +
4838 +static boolean
4839 +insert_fprint(const struct parser_table* entry, const char *filename)
4840 +{
4841 + struct predicate *our_pred = insert_primary (entry);
4842 + if (filename)
4843 + open_output_file (filename, &our_pred->args.printf_vec);
4844 + else
4845 + open_stdout (&our_pred->args.printf_vec);
4846 + our_pred->side_effects = our_pred->no_default_print = true;
4847 + our_pred->need_stat = our_pred->need_type = false;
4848 + our_pred->est_success_rate = 1.0f;
4849 + return true;
4850 +}
4851 +
4852 +
4853 +static boolean
4854 +parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
4855 +{
4856 + const char *filename;
4857 + if (collect_arg(argv, arg_ptr, &filename))
4858 + return insert_fprint(entry, filename);
4859 + else
4860 + return false;
4861 +}
4862 +
4863 +static float estimate_fstype_success_rate(const char *fsname)
4864 +{
4865 + struct stat dir_stat;
4866 + const char *dir = "/";
4867 + if (0 == stat(dir, &dir_stat))
4868 + {
4869 + const char *fstype = filesystem_type(&dir_stat, dir);
4870 + /* Assume most files are on the same file system type as the root fs. */
4871 + if (0 == strcmp(fsname, fstype))
4872 + return 0.7f;
4873 + else
4874 + return 0.3f;
4875 + }
4876 + return 1.0f;
4877 +}
4878 +
4879 +
4880 +static boolean
4881 +parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
4882 +{
4883 + const char *typename;
4884 + if (collect_arg(argv, arg_ptr, &typename))
4885 + {
4886 + struct predicate *our_pred = insert_primary (entry);
4887 + our_pred->args.str = typename;
4888 +
4889 + /* This is an expensive operation, so although there are
4890 + * circumstances where it is selective, we ignore this fact
4891 + * because we probably don't want to promote this test to the
4892 + * front anyway.
4893 + */
4894 + our_pred->est_success_rate = estimate_fstype_success_rate(typename);
4895 + return true;
4896 + }
4897 + else
4898 + {
4899 + return false;
4900 + }
4901 +}
4902 +
4903 +static boolean
4904 +parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
4905 +{
4906 + struct predicate *p = insert_num (argv, arg_ptr, entry);
4907 + if (p)
4908 + {
4909 + p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
4910 + return true;
4911 + }
4912 + else
4913 + {
4914 + return false;
4915 + }
4916 +}
4917 +
4918 +
4919 +static int
4920 +safe_atoi (const char *s)
4921 +{
4922 + long lval;
4923 + char *end;
4924 +
4925 + errno = 0;
4926 + lval = strtol(s, &end, 10);
4927 + if ( (LONG_MAX == lval) || (LONG_MIN == lval) )
4928 + {
4929 + /* max/min possible value, or an error. */
4930 + if (errno == ERANGE)
4931 + {
4932 + /* too big, or too small. */
4933 + error(1, errno, "%s", s);
4934 + }
4935 + else
4936 + {
4937 + /* not a valid number */
4938 + error(1, errno, "%s", s);
4939 + }
4940 + /* Otherwise, we do a range chack against INT_MAX and INT_MIN
4941 + * below.
4942 + */
4943 + }
4944 +
4945 + if (lval > INT_MAX || lval < INT_MIN)
4946 + {
4947 + /* The number was in range for long, but not int. */
4948 + errno = ERANGE;
4949 + error(1, errno, "%s", s);
4950 + }
4951 + else if (*end)
4952 + {
4953 + error(1, errno, "Unexpected suffix %s on %s",
4954 + quotearg_n_style(0, options.err_quoting_style, end),
4955 + quotearg_n_style(1, options.err_quoting_style, s));
4956 + }
4957 + else if (end == s)
4958 + {
4959 + error(1, errno, "Expected an integer: %s",
4960 + quotearg_n_style(0, options.err_quoting_style, s));
4961 + }
4962 + return (int)lval;
4963 +}
4964 +
4965 +
4966 +static boolean
4967 +parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
4968 +{
4969 + const char *groupname;
4970 +
4971 + if (collect_arg(argv, arg_ptr, &groupname))
4972 + {
4973 + gid_t gid;
4974 + struct predicate *our_pred;
4975 + struct group *cur_gr = getgrnam(groupname);
4976 + endgrent();
4977 + if (cur_gr)
4978 + {
4979 + gid = cur_gr->gr_gid;
4980 + }
4981 + else
4982 + {
4983 + const int gid_len = strspn (groupname, "0123456789");
4984 + if (gid_len)
4985 + {
4986 + if (groupname[gid_len] == 0)
4987 + {
4988 + gid = safe_atoi (groupname);
4989 + }
4990 + else
4991 + {
4992 + /* XXX: no test in test suite for this */
4993 + error(1, 0, _("%1$s is not the name of an existing group and"
4994 + " it does not look like a numeric group ID "
4995 + "because it has the unexpected suffix %2$s"),
4996 + quotearg_n_style(0, options.err_quoting_style, groupname),
4997 + quotearg_n_style(1, options.err_quoting_style, groupname+gid_len));
4998 + return false;
4999 + }
5000 + }
5001 + else
5002 + {
5003 + if (*groupname)
5004 + {
5005 + /* XXX: no test in test suite for this */
5006 + error(1, 0, _("%s is not the name of an existing group"),
5007 + quotearg_n_style(0, options.err_quoting_style, groupname));
5008 + }
5009 + else
5010 + {
5011 + error(1, 0, _("argument to -group is empty, but should be a group name"));
5012 + }
5013 + return false;
5014 + }
5015 + }
5016 + our_pred = insert_primary (entry);
5017 + our_pred->args.gid = gid;
5018 + our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2;
5019 + return true;
5020 + }
5021 + return false;
5022 +}
5023 +
5024 +static boolean
5025 +parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
5026 +{
5027 + (void) entry;
5028 + (void) argv;
5029 + (void) arg_ptr;
5030 +
5031 + usage(stdout, 0, NULL);
5032 + puts (_("\n\
5033 +default path is the current directory; default expression is -print\n\
5034 +expression may consist of: operators, options, tests, and actions:\n"));
5035 + puts (_("\
5036 +operators (decreasing precedence; -and is implicit where no others are given):\n\
5037 + ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
5038 + EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
5039 + puts (_("\
5040 +positional options (always true): -daystart -follow -regextype\n\n\
5041 +normal options (always true, specified before other expressions):\n\
5042 + -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
5043 + --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
5044 + puts (_("\
5045 +tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
5046 + -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
5047 + -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
5048 + -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
5049 + puts (_("\
5050 + -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
5051 + -readable -writable -executable\n\
5052 + -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
5053 + -used N -user NAME -xtype [bcdpfls]\n"));
5054 + puts (_("\
5055 +actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
5056 + -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
5057 + -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
5058 + -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
5059 +"));
5060 + puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
5061 +page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
5062 +email to <bug-findutils@×××.org>."));
5063 + exit (0);
5064 +}
5065 +
5066 +static float
5067 +estimate_pattern_match_rate(const char *pattern, int is_regex)
5068 +{
5069 + if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, ".")))
5070 + {
5071 + /* A wildcard; assume the pattern matches most files. */
5072 + return 0.8f;
5073 + }
5074 + else
5075 + {
5076 + return 0.1f;
5077 + }
5078 +}
5079 +
5080 +static boolean
5081 +parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
5082 +{
5083 + const char *name;
5084 + if (collect_arg(argv, arg_ptr, &name))
5085 + {
5086 + struct predicate *our_pred = insert_primary (entry);
5087 + our_pred->args.str = name;
5088 + /* Use the generic glob pattern estimator to figure out how many
5089 + * links will match, but bear in mind that most files won't be links.
5090 + */
5091 + our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0);
5092 + return true;
5093 + }
5094 + else
5095 + {
5096 + return false;
5097 + }
5098 +}
5099 +
5100 +
5101 +/* sanity check the fnmatch() function to make sure that case folding
5102 + * is supported (as opposed to just having the flag ignored).
5103 + */
5104 +static boolean
5105 +fnmatch_sanitycheck(void)
5106 +{
5107 + static boolean checked = false;
5108 + if (!checked)
5109 + {
5110 + if (0 != fnmatch("foo", "foo", 0)
5111 + || 0 == fnmatch("Foo", "foo", 0)
5112 + || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
5113 + {
5114 + error (1, 0, _("sanity check of the fnmatch() library function failed."));
5115 + return false;
5116 + }
5117 + checked = true;
5118 + }
5119 + return checked;
5120 +}
5121 +
5122 +
5123 +static boolean
5124 +check_name_arg(const char *pred, const char *arg)
5125 +{
5126 + if (options.warnings && strchr(arg, '/'))
5127 + {
5128 + error(0, 0,_("warning: Unix filenames usually don't contain slashes "
5129 + "(though pathnames do). That means that '%s %s' will "
5130 + "probably evaluate to false all the time on this system. "
5131 + "You might find the '-wholename' test more useful, or "
5132 + "perhaps '-samefile'. Alternatively, if you are using "
5133 + "GNU grep, you could "
5134 + "use 'find ... -print0 | grep -FzZ %s'."),
5135 + pred,
5136 + safely_quote_err_filename(0, arg),
5137 + safely_quote_err_filename(1, arg));
5138 + }
5139 + return true; /* allow it anyway */
5140 +}
5141 +
5142 +
5143 +
5144 +static boolean
5145 +parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
5146 +{
5147 + const char *name;
5148 + fnmatch_sanitycheck();
5149 + if (collect_arg(argv, arg_ptr, &name))
5150 + {
5151 + if (check_name_arg("-iname", name))
5152 + {
5153 + struct predicate *our_pred = insert_primary (entry);
5154 + our_pred->need_stat = our_pred->need_type = false;
5155 + our_pred->args.str = name;
5156 + our_pred->est_success_rate = estimate_pattern_match_rate(name, 0);
5157 + return true;
5158 + }
5159 + }
5160 + return false;
5161 +}
5162 +
5163 +static boolean
5164 +parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
5165 +{
5166 + struct predicate *p = insert_num (argv, arg_ptr, entry);
5167 + if (p)
5168 + {
5169 + /* inode number is exact match only, so very low proportions of
5170 + * files match
5171 + */
5172 + p->est_success_rate = 1e-6;
5173 + return true;
5174 + }
5175 + else
5176 + {
5177 + return false;
5178 + }
5179 +}
5180 +
5181 +/* -ipath is deprecated (at RMS's request) in favour of
5182 + * -iwholename. See the node "GNU Manuals" in standards.texi
5183 + * for the rationale for this (basically, GNU prefers the use
5184 + * of the phrase "file name" to "path name"
5185 + */
5186 +static boolean
5187 +parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
5188 +{
5189 + const char *name;
5190 +
5191 + fnmatch_sanitycheck ();
5192 + if (collect_arg (argv, arg_ptr, &name))
5193 + {
5194 + struct predicate *our_pred = insert_primary_withpred (entry, pred_ipath);
5195 + our_pred->need_stat = our_pred->need_type = false;
5196 + our_pred->args.str = name;
5197 + our_pred->est_success_rate = estimate_pattern_match_rate (name, 0);
5198 + return true;
5199 + }
5200 + return false;
5201 +}
5202 +
5203 +static boolean
5204 +parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
5205 +{
5206 + return parse_ipath (entry, argv, arg_ptr);
5207 +}
5208 +
5209 +static boolean
5210 +parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
5211 +{
5212 + return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
5213 +}
5214 +
5215 +static boolean
5216 +parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
5217 +{
5218 + struct predicate *p = insert_num (argv, arg_ptr, entry);
5219 + if (p)
5220 + {
5221 + if (p->args.numinfo.l_val == 1)
5222 + p->est_success_rate = 0.99;
5223 + else if (p->args.numinfo.l_val == 2)
5224 + p->est_success_rate = 0.01;
5225 + else
5226 + p->est_success_rate = 1e-3;
5227 + return true;
5228 + }
5229 + else
5230 + {
5231 + return false;
5232 + }
5233 +}
5234 +
5235 +static boolean
5236 +parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
5237 +{
5238 + const char *name;
5239 + fnmatch_sanitycheck();
5240 + if (collect_arg(argv, arg_ptr, &name))
5241 + {
5242 + struct predicate *our_pred = insert_primary (entry);
5243 + our_pred->args.str = name;
5244 + our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0);
5245 + return true;
5246 + }
5247 + return false;
5248 +}
5249 +
5250 +static boolean
5251 +parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
5252 +{
5253 + (void) &argv;
5254 + (void) &arg_ptr;
5255 + return insert_fls(entry, NULL);
5256 +}
5257 +
5258 +static boolean
5259 +insert_depthspec(const struct parser_table* entry, char **argv, int *arg_ptr,
5260 + int *limitptr)
5261 +{
5262 + const char *depthstr;
5263 + int depth_len;
5264 + const char *predicate = argv[(*arg_ptr)-1];
5265 + if (collect_arg(argv, arg_ptr, &depthstr))
5266 + {
5267 + depth_len = strspn (depthstr, "0123456789");
5268 + if ((depth_len > 0) && (depthstr[depth_len] == 0))
5269 + {
5270 + (*limitptr) = safe_atoi (depthstr);
5271 + if (*limitptr >= 0)
5272 + {
5273 + return parse_noop(entry, argv, arg_ptr);
5274 + }
5275 + }
5276 + error(1, 0, _("Expected a positive decimal integer argument to %1$s, but got %2$s"),
5277 + predicate,
5278 + quotearg_n_style(0, options.err_quoting_style, depthstr));
5279 + return false;
5280 + }
5281 + /* missing argument */
5282 + return false;
5283 +}
5284 +
5285 +
5286 +static boolean
5287 +parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
5288 +{
5289 + return insert_depthspec(entry, argv, arg_ptr, &options.maxdepth);
5290 +}
5291 +
5292 +static boolean
5293 +parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
5294 +{
5295 + return insert_depthspec(entry, argv, arg_ptr, &options.mindepth);
5296 +}
5297 +
5298 +
5299 +static boolean
5300 +do_parse_xmin (const struct parser_table* entry,
5301 + char **argv,
5302 + int *arg_ptr,
5303 + enum xval xv)
5304 +{
5305 + const char *minutes;
5306 +
5307 + if (collect_arg(argv, arg_ptr, &minutes))
5308 + {
5309 + struct time_val tval;
5310 + tval.xval = xv;
5311 + if (get_relative_timestamp(minutes, &tval,
5312 + options.cur_day_start + DAYSECS, 60,
5313 + "arithmetic overflow while converting %s "
5314 + "minutes to a number of seconds"))
5315 + {
5316 + struct predicate *our_pred = insert_primary (entry);
5317 + our_pred->args.reftime = tval;
5318 + our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec);
5319 + return true;
5320 + }
5321 + }
5322 + return false;
5323 +}
5324 +static boolean
5325 +parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
5326 +{
5327 + return do_parse_xmin(entry, argv, arg_ptr, XVAL_ATIME);
5328 +}
5329 +
5330 +static boolean
5331 +parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
5332 +{
5333 + return do_parse_xmin(entry, argv, arg_ptr, XVAL_CTIME);
5334 +}
5335 +
5336 +
5337 +static boolean
5338 +parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
5339 +{
5340 + return do_parse_xmin(entry, argv, arg_ptr, XVAL_MTIME);
5341 +}
5342 +
5343 +static boolean
5344 +parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
5345 +{
5346 + const char *name;
5347 + if (collect_arg(argv, arg_ptr, &name))
5348 + {
5349 + fnmatch_sanitycheck();
5350 + if (check_name_arg("-name", name))
5351 + {
5352 + struct predicate *our_pred = insert_primary (entry);
5353 + our_pred->need_stat = our_pred->need_type = false;
5354 + our_pred->args.str = name;
5355 + our_pred->est_success_rate = estimate_pattern_match_rate(name, 0);
5356 + return true;
5357 + }
5358 + }
5359 + return false;
5360 +}
5361 +
5362 +static boolean
5363 +parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
5364 +{
5365 + struct predicate *our_pred;
5366 +
5367 + (void) &argv;
5368 + (void) &arg_ptr;
5369 +
5370 + our_pred = get_new_pred_chk_op (entry);
5371 + our_pred->pred_func = pred_negate;
5372 + our_pred->p_type = UNI_OP;
5373 + our_pred->p_prec = NEGATE_PREC;
5374 + our_pred->need_stat = our_pred->need_type = false;
5375 + return true;
5376 +}
5377 +
5378 +static boolean
5379 +parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
5380 +{
5381 + struct predicate *our_pred;
5382 + struct stat stat_newer;
5383 +
5384 + set_stat_placeholders(&stat_newer);
5385 + if (collect_arg_stat_info(argv, arg_ptr, &stat_newer))
5386 + {
5387 + our_pred = insert_primary (entry);
5388 + our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
5389 + our_pred->args.reftime.xval = XVAL_MTIME;
5390 + our_pred->args.reftime.kind = COMP_GT;
5391 + our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
5392 + return true;
5393 + }
5394 + return false;
5395 +}
5396 +
5397 +
5398 +static boolean
5399 +parse_newerXY (const struct parser_table* entry, char **argv, int *arg_ptr)
5400 +{
5401 + (void) argv;
5402 + (void) arg_ptr;
5403 +
5404 + if ((argv == NULL) || (argv[*arg_ptr] == NULL))
5405 + {
5406 + return false;
5407 + }
5408 + else if (8u != strlen(argv[*arg_ptr]))
5409 + {
5410 + return false;
5411 + }
5412 + else
5413 + {
5414 + char x, y;
5415 + const char validchars[] = "aBcmt";
5416 +
5417 + assert (0 == strncmp("-newer", argv[*arg_ptr], 6));
5418 + x = argv[*arg_ptr][6];
5419 + y = argv[*arg_ptr][7];
5420 +
5421 +
5422 +#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC)
5423 + if ('B' == x || 'B' == y)
5424 + {
5425 + error(0, 0,
5426 + _("This system does not provide a way to find the birth time of a file."));
5427 + return 0;
5428 + }
5429 +#endif
5430 +
5431 + /* -newertY (for any Y) is invalid. */
5432 + if (x == 't'
5433 + || 0 == strchr(validchars, x)
5434 + || 0 == strchr( validchars, y))
5435 + {
5436 + return false;
5437 + }
5438 + else
5439 + {
5440 + struct predicate *our_pred;
5441 +
5442 + /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr
5443 + * past the test name (for most other tests, this is already done)
5444 + */
5445 + (*arg_ptr)++;
5446 +
5447 + our_pred = insert_primary (entry);
5448 +
5449 +
5450 + switch (x)
5451 + {
5452 + case 'a':
5453 + our_pred->args.reftime.xval = XVAL_ATIME;
5454 + break;
5455 + case 'B':
5456 + our_pred->args.reftime.xval = XVAL_BIRTHTIME;
5457 + break;
5458 + case 'c':
5459 + our_pred->args.reftime.xval = XVAL_CTIME;
5460 + break;
5461 + case 'm':
5462 + our_pred->args.reftime.xval = XVAL_MTIME;
5463 + break;
5464 + default:
5465 + assert (strchr(validchars, x));
5466 + assert (0);
5467 + }
5468 +
5469 + if ('t' == y)
5470 + {
5471 + if (!get_date(&our_pred->args.reftime.ts,
5472 + argv[*arg_ptr],
5473 + &options.start_time))
5474 + {
5475 + error(1, 0,
5476 + _("I cannot figure out how to interpret %s as a date or time"),
5477 + quotearg_n_style(0, options.err_quoting_style, argv[*arg_ptr]));
5478 + }
5479 + }
5480 + else
5481 + {
5482 + struct stat stat_newer;
5483 +
5484 + /* Stat the named file. */
5485 + set_stat_placeholders(&stat_newer);
5486 + if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
5487 + fatal_file_error(argv[*arg_ptr]);
5488 +
5489 + if (!get_stat_Ytime(&stat_newer, y, &our_pred->args.reftime.ts))
5490 + {
5491 + /* We cannot extract a timestamp from the struct stat. */
5492 + error(1, 0, _("Cannot obtain birth time of file %s"),
5493 + safely_quote_err_filename(0, argv[*arg_ptr]));
5494 + }
5495 + }
5496 + our_pred->args.reftime.kind = COMP_GT;
5497 + our_pred->est_success_rate = estimate_timestamp_success_rate(our_pred->args.reftime.ts.tv_sec);
5498 + (*arg_ptr)++;
5499 +
5500 + assert (our_pred->pred_func != NULL);
5501 + assert (our_pred->pred_func == pred_newerXY);
5502 + assert (our_pred->need_stat);
5503 + return true;
5504 + }
5505 + }
5506 +}
5507 +
5508 +
5509 +static boolean
5510 +parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
5511 +{
5512 + options.no_leaf_check = true;
5513 + return parse_noop(entry, argv, arg_ptr);
5514 +}
5515 +
5516 +#ifdef CACHE_IDS
5517 +/* Arbitrary amount by which to increase size
5518 + of `uid_unused' and `gid_unused'. */
5519 +#define ALLOC_STEP 2048
5520 +
5521 +/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
5522 +char *uid_unused = NULL;
5523 +
5524 +/* Number of elements in `uid_unused'. */
5525 +unsigned uid_allocated;
5526 +
5527 +/* Similar for GIDs and group entries. */
5528 +char *gid_unused = NULL;
5529 +unsigned gid_allocated;
5530 +#endif
5531 +
5532 +static boolean
5533 +parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
5534 +{
5535 + struct predicate *our_pred;
5536 +
5537 + (void) &argv;
5538 + (void) &arg_ptr;
5539 +
5540 + our_pred = insert_primary (entry);
5541 + our_pred->est_success_rate = 1e-4;
5542 +#ifdef CACHE_IDS
5543 + if (gid_unused == NULL)
5544 + {
5545 + struct group *gr;
5546 +
5547 + gid_allocated = ALLOC_STEP;
5548 + gid_unused = xmalloc (gid_allocated);
5549 + memset (gid_unused, 1, gid_allocated);
5550 + setgrent ();
5551 + while ((gr = getgrent ()) != NULL)
5552 + {
5553 + if ((unsigned) gr->gr_gid >= gid_allocated)
5554 + {
5555 + unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
5556 + gid_unused = xrealloc (gid_unused, new_allocated);
5557 + memset (gid_unused + gid_allocated, 1,
5558 + new_allocated - gid_allocated);
5559 + gid_allocated = new_allocated;
5560 + }
5561 + gid_unused[(unsigned) gr->gr_gid] = 0;
5562 + }
5563 + endgrent ();
5564 + }
5565 +#endif
5566 + return true;
5567 +}
5568 +
5569 +static boolean
5570 +parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
5571 +{
5572 + struct predicate *our_pred;
5573 + (void) argv;
5574 + (void) arg_ptr;
5575 +
5576 +
5577 + our_pred = insert_primary (entry);
5578 + our_pred->est_success_rate = 1e-3;
5579 +#ifdef CACHE_IDS
5580 + if (uid_unused == NULL)
5581 + {
5582 + struct passwd *pw;
5583 +
5584 + uid_allocated = ALLOC_STEP;
5585 + uid_unused = xmalloc (uid_allocated);
5586 + memset (uid_unused, 1, uid_allocated);
5587 + setpwent ();
5588 + while ((pw = getpwent ()) != NULL)
5589 + {
5590 + if ((unsigned) pw->pw_uid >= uid_allocated)
5591 + {
5592 + unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
5593 + uid_unused = xrealloc (uid_unused, new_allocated);
5594 + memset (uid_unused + uid_allocated, 1,
5595 + new_allocated - uid_allocated);
5596 + uid_allocated = new_allocated;
5597 + }
5598 + uid_unused[(unsigned) pw->pw_uid] = 0;
5599 + }
5600 + endpwent ();
5601 + }
5602 +#endif
5603 + return true;
5604 +}
5605 +
5606 +static boolean
5607 +parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
5608 +{
5609 + options.warnings = false;
5610 + return parse_noop(entry, argv, arg_ptr);
5611 +}
5612 +
5613 +static boolean
5614 +parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
5615 +{
5616 + return insert_exec_ok ("-ok", entry, get_start_dirfd(), argv, arg_ptr);
5617 +}
5618 +
5619 +static boolean
5620 +parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
5621 +{
5622 + return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr);
5623 +}
5624 +
5625 +boolean
5626 +parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr)
5627 +{
5628 + struct predicate *our_pred;
5629 +
5630 + (void) argv;
5631 + (void) arg_ptr;
5632 +
5633 + our_pred = get_new_pred_chk_op (entry);
5634 + our_pred->pred_func = pred_openparen;
5635 + our_pred->p_type = OPEN_PAREN;
5636 + our_pred->p_prec = NO_PREC;
5637 + our_pred->need_stat = our_pred->need_type = false;
5638 + return true;
5639 +}
5640 +
5641 +static boolean
5642 +parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
5643 +{
5644 + struct predicate *our_pred;
5645 +
5646 + (void) argv;
5647 + (void) arg_ptr;
5648 +
5649 + our_pred = get_new_pred (entry);
5650 + our_pred->pred_func = pred_or;
5651 + our_pred->p_type = BI_OP;
5652 + our_pred->p_prec = OR_PREC;
5653 + our_pred->need_stat = our_pred->need_type = false;
5654 + return true;
5655 +}
5656 +
5657 +/* For some time, -path was deprecated (at RMS's request) in favour of
5658 + * -iwholename. See the node "GNU Manuals" in standards.texi for the
5659 + * rationale for this (basically, GNU prefers the use of the phrase
5660 + * "file name" to "path name".
5661 + *
5662 + * We do not issue a warning that this usage is deprecated
5663 + * since
5664 + * (a) HPUX find supports this predicate also and
5665 + * (b) it will soon be in POSIX anyway.
5666 + */
5667 +static boolean
5668 +parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
5669 +{
5670 + const char *name;
5671 + if (collect_arg(argv, arg_ptr, &name))
5672 + {
5673 + struct predicate *our_pred = insert_primary_withpred (entry, pred_path);
5674 + our_pred->need_stat = our_pred->need_type = false;
5675 + our_pred->args.str = name;
5676 + our_pred->est_success_rate = estimate_pattern_match_rate (name, 0);
5677 + return true;
5678 + }
5679 + return false;
5680 +}
5681 +
5682 +static boolean
5683 +parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
5684 +{
5685 + return parse_path (entry, argv, arg_ptr);
5686 +}
5687 +
5688 +static void
5689 +non_posix_mode(const char *mode)
5690 +{
5691 + if (options.posixly_correct)
5692 + {
5693 + error (1, 0, _("Mode %s is not valid when POSIXLY_CORRECT is on."),
5694 + quotearg_n_style(0, options.err_quoting_style, mode));
5695 + }
5696 +}
5697 +
5698 +
5699 +static boolean
5700 +parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
5701 +{
5702 + mode_t perm_val[2];
5703 + float rate;
5704 + int mode_start = 0;
5705 + boolean havekind = false;
5706 + enum permissions_type kind = PERM_EXACT;
5707 + struct mode_change *change = NULL;
5708 + struct predicate *our_pred;
5709 + const char *perm_expr;
5710 +
5711 + if (!collect_arg(argv, arg_ptr, &perm_expr))
5712 + return false;
5713 +
5714 + switch (perm_expr[0])
5715 + {
5716 + case '-':
5717 + mode_start = 1;
5718 + kind = PERM_AT_LEAST;
5719 + havekind = true;
5720 + rate = 0.2;
5721 + break;
5722 +
5723 + case '+':
5724 + change = mode_compile (perm_expr);
5725 + if (NULL == change)
5726 + {
5727 + /* Most likely the caller is an old script that is still
5728 + * using the obsolete GNU syntax '-perm +MODE'. This old
5729 + * syntax was withdrawn in favor of '-perm /MODE' because
5730 + * it is incompatible with POSIX in some cases, but we
5731 + * still support uses of it that are not incompatible with
5732 + * POSIX.
5733 + *
5734 + * Example: POSIXLY_CORRECT=y find -perm +a+x
5735 + */
5736 + non_posix_mode(perm_expr);
5737 +
5738 + /* support the previous behaviour. */
5739 + mode_start = 1;
5740 + kind = PERM_ANY;
5741 + rate = 0.3;
5742 + }
5743 + else
5744 + {
5745 + /* This is a POSIX-compatible usage */
5746 + mode_start = 0;
5747 + kind = PERM_EXACT;
5748 + rate = 0.1;
5749 + }
5750 + havekind = true;
5751 + break;
5752 +
5753 + case '/': /* GNU extension */
5754 + non_posix_mode(perm_expr);
5755 + mode_start = 1;
5756 + kind = PERM_ANY;
5757 + havekind = true;
5758 + rate = 0.3;
5759 + break;
5760 +
5761 + default:
5762 + /* For example, '-perm 0644', which is valid and matches
5763 + * only files whose mode is exactly 0644.
5764 + */
5765 + mode_start = 0;
5766 + kind = PERM_EXACT;
5767 + havekind = true;
5768 + rate = 0.01;
5769 + break;
5770 + }
5771 +
5772 + if (NULL == change)
5773 + {
5774 + change = mode_compile (perm_expr + mode_start);
5775 + if (NULL == change)
5776 + error (1, 0, _("invalid mode %s"
5777 + /* TRANSLATORS: the argument is a
5778 + * file permission string like 'u=rw,go='
5779 + */),
5780 + quotearg_n_style(0, options.err_quoting_style, perm_expr));
5781 + }
5782 + perm_val[0] = mode_adjust (0, false, 0, change, NULL);
5783 + perm_val[1] = mode_adjust (0, true, 0, change, NULL);
5784 + free (change);
5785 +
5786 + if (('/' == perm_expr[0]) && (0 == perm_val[0]) && (0 == perm_val[1]))
5787 + {
5788 + /* The meaning of -perm /000 will change in the future. It
5789 + * currently matches no files, but like -perm -000 it should
5790 + * match all files.
5791 + *
5792 + * Starting in 2005, we used to issue a warning message
5793 + * informing the user that the behaviour would change in the
5794 + * future. We have now changed the behaviour and issue a
5795 + * warning message that the behaviour recently changed.
5796 + */
5797 + error (0, 0,
5798 + _("warning: you have specified a mode pattern %s (which is "
5799 + "equivalent to /000). The meaning of -perm /000 has now been "
5800 + "changed to be consistent with -perm -000; that is, while it "
5801 + "used to match no files, it now matches all files."),
5802 + perm_expr);
5803 +
5804 + kind = PERM_AT_LEAST;
5805 + havekind = true;
5806 +
5807 + /* The "magic" number below is just the fraction of files on my
5808 + * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks).
5809 + * Actual totals are 1472 and 1073833.
5810 + */
5811 + rate = 0.9986; /* probably matches anything but a broken symlink */
5812 + }
5813 +
5814 + our_pred = insert_primary (entry);
5815 + our_pred->est_success_rate = rate;
5816 + if (havekind)
5817 + {
5818 + our_pred->args.perm.kind = kind;
5819 + }
5820 + else
5821 + {
5822 +
5823 + switch (perm_expr[0])
5824 + {
5825 + case '-':
5826 + our_pred->args.perm.kind = PERM_AT_LEAST;
5827 + break;
5828 + case '+':
5829 + our_pred->args.perm.kind = PERM_ANY;
5830 + break;
5831 + default:
5832 + our_pred->args.perm.kind = PERM_EXACT;
5833 + break;
5834 + }
5835 + }
5836 + memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val);
5837 + return true;
5838 +}
5839 +
5840 +boolean
5841 +parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
5842 +{
5843 + struct predicate *our_pred;
5844 +
5845 + (void) argv;
5846 + (void) arg_ptr;
5847 +
5848 + our_pred = insert_primary (entry);
5849 + /* -print has the side effect of printing. This prevents us
5850 + from doing undesired multiple printing when the user has
5851 + already specified -print. */
5852 + our_pred->side_effects = our_pred->no_default_print = true;
5853 + our_pred->need_stat = our_pred->need_type = false;
5854 + open_stdout(&our_pred->args.printf_vec);
5855 + return true;
5856 +}
5857 +
5858 +static boolean
5859 +parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
5860 +{
5861 + return insert_fprint(entry, NULL);
5862 +}
5863 +
5864 +static boolean
5865 +parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
5866 +{
5867 + const char *format;
5868 + if (collect_arg(argv, arg_ptr, &format))
5869 + {
5870 + struct format_val fmt;
5871 + open_stdout(&fmt);
5872 + return insert_fprintf (&fmt, entry, pred_fprintf, format);
5873 + }
5874 + return false;
5875 +}
5876 +
5877 +static boolean
5878 +parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
5879 +{
5880 + const char *format, *filename;
5881 + if (collect_arg(argv, arg_ptr, &filename))
5882 + {
5883 + if (collect_arg(argv, arg_ptr, &format))
5884 + {
5885 + struct format_val fmt;
5886 + open_output_file (filename, &fmt);
5887 + return insert_fprintf (&fmt, entry, pred_fprintf, format);
5888 + }
5889 + }
5890 + return false;
5891 +}
5892 +
5893 +static boolean
5894 +parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
5895 +{
5896 + struct predicate *our_pred;
5897 +
5898 + (void) argv;
5899 + (void) arg_ptr;
5900 +
5901 + our_pred = insert_primary (entry);
5902 + our_pred->need_stat = our_pred->need_type = false;
5903 + /* -prune has a side effect that it does not descend into
5904 + the current directory. */
5905 + our_pred->side_effects = true;
5906 + our_pred->no_default_print = false;
5907 + return true;
5908 +}
5909 +
5910 +static boolean
5911 +parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
5912 +{
5913 + struct predicate *our_pred = insert_primary (entry);
5914 + (void) argv;
5915 + (void) arg_ptr;
5916 + our_pred->need_stat = our_pred->need_type = false;
5917 + our_pred->side_effects = true; /* Exiting is a side effect... */
5918 + our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
5919 + our_pred->est_success_rate = 1.0f;
5920 + return true;
5921 +}
5922 +
5923 +
5924 +static boolean
5925 +parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
5926 +{
5927 + const char *type_name;
5928 + if (collect_arg(argv, arg_ptr, &type_name))
5929 + {
5930 + /* collect the regex type name */
5931 + options.regex_options = get_regex_type(type_name);
5932 + return parse_noop(entry, argv, arg_ptr);
5933 + }
5934 + return false;
5935 +}
5936 +
5937 +
5938 +static boolean
5939 +parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
5940 +{
5941 + return insert_regex (argv, arg_ptr, entry, options.regex_options);
5942 +}
5943 +
5944 +static boolean
5945 +insert_regex (char **argv,
5946 + int *arg_ptr,
5947 + const struct parser_table *entry,
5948 + int regex_options)
5949 +{
5950 + const char *rx;
5951 + if (collect_arg(argv, arg_ptr, &rx))
5952 + {
5953 + struct re_pattern_buffer *re;
5954 + const char *error_message;
5955 + struct predicate *our_pred = insert_primary_withpred (entry, pred_regex);
5956 + our_pred->need_stat = our_pred->need_type = false;
5957 + re = xmalloc (sizeof (struct re_pattern_buffer));
5958 + our_pred->args.regex = re;
5959 + re->allocated = 100;
5960 + re->buffer = xmalloc (re->allocated);
5961 + re->fastmap = NULL;
5962 +
5963 + re_set_syntax(regex_options);
5964 + re->syntax = regex_options;
5965 + re->translate = NULL;
5966 +
5967 + error_message = re_compile_pattern (rx, strlen(rx), re);
5968 + if (error_message)
5969 + error (1, 0, "%s", error_message);
5970 + our_pred->est_success_rate = estimate_pattern_match_rate(rx, 1);
5971 + return true;
5972 + }
5973 + return false;
5974 +}
5975 +
5976 +static boolean
5977 +parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
5978 +{
5979 + struct predicate *our_pred;
5980 + uintmax_t num;
5981 + char suffix;
5982 + enum comparison_type c_type;
5983 +
5984 + int blksize = 512;
5985 + int len;
5986 +
5987 + /* XXX: cannot (yet) convert to ue collect_arg() as this
5988 + * function modifies the args in-place.
5989 + */
5990 + if ((argv == NULL) || (argv[*arg_ptr] == NULL))
5991 + return false;
5992 +
5993 + len = strlen (argv[*arg_ptr]);
5994 + if (len == 0)
5995 + error (1, 0, _("invalid null argument to -size"));
5996 +
5997 + suffix = argv[*arg_ptr][len - 1];
5998 + switch (suffix)
5999 + {
6000 + case 'b':
6001 + blksize = 512;
6002 + argv[*arg_ptr][len - 1] = '\0';
6003 + break;
6004 +
6005 + case 'c':
6006 + blksize = 1;
6007 + argv[*arg_ptr][len - 1] = '\0';
6008 + break;
6009 +
6010 + case 'k':
6011 + blksize = 1024;
6012 + argv[*arg_ptr][len - 1] = '\0';
6013 + break;
6014 +
6015 + case 'M': /* Megabytes */
6016 + blksize = 1024*1024;
6017 + argv[*arg_ptr][len - 1] = '\0';
6018 + break;
6019 +
6020 + case 'G': /* Gigabytes */
6021 + blksize = 1024*1024*1024;
6022 + argv[*arg_ptr][len - 1] = '\0';
6023 + break;
6024 +
6025 + case 'w':
6026 + blksize = 2;
6027 + argv[*arg_ptr][len - 1] = '\0';
6028 + break;
6029 +
6030 + case '0':
6031 + case '1':
6032 + case '2':
6033 + case '3':
6034 + case '4':
6035 + case '5':
6036 + case '6':
6037 + case '7':
6038 + case '8':
6039 + case '9':
6040 + break;
6041 +
6042 + default:
6043 + error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
6044 + }
6045 + /* TODO: accept fractional megabytes etc. ? */
6046 + if (!get_num (argv[*arg_ptr], &num, &c_type))
6047 + {
6048 + error(1, 0,
6049 + _("Invalid argument `%s%c' to -size"),
6050 + argv[*arg_ptr], (int)suffix);
6051 + return false;
6052 + }
6053 + our_pred = insert_primary (entry);
6054 + our_pred->args.size.kind = c_type;
6055 + our_pred->args.size.blocksize = blksize;
6056 + our_pred->args.size.size = num;
6057 + our_pred->need_stat = true;
6058 + our_pred->need_type = false;
6059 +
6060 + if (COMP_GT == c_type)
6061 + our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9;
6062 + else if (COMP_LT == c_type)
6063 + our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1;
6064 + else
6065 + our_pred->est_success_rate = 0.01;
6066 +
6067 + (*arg_ptr)++;
6068 + return true;
6069 +}
6070 +
6071 +
6072 +static boolean
6073 +parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
6074 +{
6075 + /* General idea: stat the file, remember device and inode numbers.
6076 + * If a candidate file matches those, it's the same file.
6077 + */
6078 + struct predicate *our_pred;
6079 + struct stat st, fst;
6080 + int fd, openflags;
6081 +
6082 + set_stat_placeholders(&st);
6083 + if (!collect_arg_stat_info(argv, arg_ptr, &st))
6084 + return false;
6085 +
6086 + set_stat_placeholders(&fst);
6087 + /* POSIX systems are free to re-use the inode number of a deleted
6088 + * file. To ensure that we are not fooled by inode reuse, we hold
6089 + * the file open if we can. This would prevent the system reusing
6090 + * the file.
6091 + */
6092 + fd = -3; /* means, uninitialised */
6093 + openflags = O_RDONLY;
6094 +
6095 + if (options.symlink_handling == SYMLINK_NEVER_DEREF)
6096 + {
6097 + if (options.open_nofollow_available)
6098 + {
6099 + assert (O_NOFOLLOW != 0);
6100 + openflags |= O_NOFOLLOW;
6101 + fd = -1; /* safe to open it. */
6102 + }
6103 + else
6104 + {
6105 + if (S_ISLNK(st.st_mode))
6106 + {
6107 + /* no way to ensure that a symlink will not be followed
6108 + * by open(2), so fall back on using lstat(). Accept
6109 + * the risk that the named file will be deleted and
6110 + * replaced with another having the same inode.
6111 + *
6112 + * Avoid opening the file.
6113 + */
6114 + fd = -2; /* Do not open it */
6115 + }
6116 + else
6117 + {
6118 + fd = -1;
6119 + /* Race condition here: the file might become a symlink here. */
6120 + }
6121 + }
6122 + }
6123 + else
6124 + {
6125 + /* We want to dereference the symlink anyway */
6126 + fd = -1; /* safe to open it without O_NOFOLLOW */
6127 + }
6128 +
6129 + assert (fd != -3); /* check we made a decision */
6130 + if (fd == -1)
6131 + {
6132 + /* Race condition here. The file might become a
6133 + * symbolic link in between out call to stat and
6134 + * the call to open.
6135 + */
6136 + fd = open(argv[*arg_ptr], openflags);
6137 +
6138 + if (fd >= 0)
6139 + {
6140 + /* We stat the file again here to prevent a race condition
6141 + * between the first stat and the call to open(2).
6142 + */
6143 + if (0 != fstat(fd, &fst))
6144 + {
6145 + fatal_file_error(argv[*arg_ptr]);
6146 + }
6147 + else
6148 + {
6149 + /* Worry about the race condition. If the file became a
6150 + * symlink after our first stat and before our call to
6151 + * open, fst may contain the stat information for the
6152 + * destination of the link, not the link itself.
6153 + */
6154 + if ((*options.xstat) (argv[*arg_ptr], &st))
6155 + fatal_file_error(argv[*arg_ptr]);
6156 +
6157 + if ((options.symlink_handling == SYMLINK_NEVER_DEREF)
6158 + && (!options.open_nofollow_available))
6159 + {
6160 + if (S_ISLNK(st.st_mode))
6161 + {
6162 + /* We lost the race. Leave the data in st. The
6163 + * file descriptor points to the wrong thing.
6164 + */
6165 + close(fd);
6166 + fd = -1;
6167 + }
6168 + else
6169 + {
6170 + /* Several possibilities here:
6171 + * 1. There was no race
6172 + * 2. The file changed into a symlink after the stat and
6173 + * before the open, and then back into a non-symlink
6174 + * before the second stat.
6175 + *
6176 + * In case (1) there is no problem. In case (2),
6177 + * the stat() and fstat() calls will have returned
6178 + * different data. O_NOFOLLOW was not available,
6179 + * so the open() call may have followed a symlink
6180 + * even if the -P option is in effect.
6181 + */
6182 + if ((st.st_dev == fst.st_dev)
6183 + && (st.st_ino == fst.st_ino))
6184 + {
6185 + /* No race. No need to copy fst to st,
6186 + * since they should be identical (modulo
6187 + * differences in padding bytes).
6188 + */
6189 + }
6190 + else
6191 + {
6192 + /* We lost the race. Leave the data in st. The
6193 + * file descriptor points to the wrong thing.
6194 + */
6195 + close(fd);
6196 + fd = -1;
6197 + }
6198 + }
6199 + }
6200 + else
6201 + {
6202 + st = fst;
6203 + }
6204 + }
6205 + }
6206 + }
6207 +
6208 + our_pred = insert_primary (entry);
6209 + our_pred->args.samefileid.ino = st.st_ino;
6210 + our_pred->args.samefileid.dev = st.st_dev;
6211 + our_pred->args.samefileid.fd = fd;
6212 + our_pred->need_type = false;
6213 + our_pred->need_stat = true;
6214 + our_pred->est_success_rate = 0.01f;
6215 + return true;
6216 +}
6217 +
6218 +#if 0
6219 +/* This function is commented out partly because support for it is
6220 + * uneven.
6221 + */
6222 +static boolean
6223 +parse_show_control_chars (const struct parser_table* entry,
6224 + char **argv,
6225 + int *arg_ptr)
6226 +{
6227 + const char *arg;
6228 + const char *errmsg = _("The -show-control-chars option takes "
6229 + "a single argument which "
6230 + "must be 'literal' or 'safe'");
6231 +
6232 + if ((argv == NULL) || (argv[*arg_ptr] == NULL))
6233 + {
6234 + error (1, errno, "%s", errmsg);
6235 + return false;
6236 + }
6237 + else
6238 + {
6239 + arg = argv[*arg_ptr];
6240 +
6241 + if (0 == strcmp("literal", arg))
6242 + {
6243 + options.literal_control_chars = true;
6244 + }
6245 + else if (0 == strcmp("safe", arg))
6246 + {
6247 + options.literal_control_chars = false;
6248 + }
6249 + else
6250 + {
6251 + error (1, errno, "%s", errmsg);
6252 + return false;
6253 + }
6254 + (*arg_ptr)++; /* consume the argument. */
6255 + return true;
6256 + }
6257 +}
6258 +#endif
6259 +
6260 +
6261 +static boolean
6262 +parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
6263 +{
6264 + struct predicate *our_pred;
6265 +
6266 + (void) argv;
6267 + (void) arg_ptr;
6268 +
6269 + our_pred = insert_primary (entry);
6270 + our_pred->need_stat = our_pred->need_type = false;
6271 + our_pred->est_success_rate = 1.0f;
6272 + return true;
6273 +}
6274 +
6275 +static boolean
6276 +parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr)
6277 +{
6278 + (void) entry;
6279 + return parse_true(get_noop(), argv, arg_ptr);
6280 +}
6281 +
6282 +static boolean
6283 +parse_accesscheck (const struct parser_table* entry, char **argv, int *arg_ptr)
6284 +{
6285 + struct predicate *our_pred;
6286 + (void) argv;
6287 + (void) arg_ptr;
6288 + our_pred = insert_primary (entry);
6289 + our_pred->need_stat = our_pred->need_type = false;
6290 + our_pred->side_effects = our_pred->no_default_print = false;
6291 + if (pred_is(our_pred, pred_executable))
6292 + our_pred->est_success_rate = 0.2;
6293 + else
6294 + our_pred->est_success_rate = 0.9;
6295 + return true;
6296 +}
6297 +
6298 +static boolean
6299 +parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
6300 +{
6301 + return insert_type (argv, arg_ptr, entry, pred_type);
6302 +}
6303 +
6304 +static boolean
6305 +parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
6306 +{
6307 + struct predicate *p = insert_num (argv, arg_ptr, entry);
6308 + if (p)
6309 + {
6310 + p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
6311 + return true;
6312 + }
6313 + else
6314 + {
6315 + return false;
6316 + }
6317 +}
6318 +
6319 +static boolean
6320 +parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
6321 +{
6322 + struct predicate *our_pred;
6323 + struct time_val tval;
6324 + const char *offset_str;
6325 + const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds";
6326 +
6327 + if (collect_arg(argv, arg_ptr, &offset_str))
6328 + {
6329 + /* The timespec is actually a delta value, so we use an origin of 0. */
6330 + if (get_relative_timestamp(offset_str, &tval, 0, DAYSECS, errmsg))
6331 + {
6332 + our_pred = insert_primary (entry);
6333 + our_pred->args.reftime = tval;
6334 + our_pred->est_success_rate = estimate_file_age_success_rate(tval.ts.tv_sec / DAYSECS);
6335 + return true;
6336 + }
6337 + else
6338 + {
6339 + error(1, 0, _("Invalid argument %s to -used"), offset_str);
6340 + return false;
6341 + }
6342 + }
6343 + else
6344 + {
6345 + return false; /* missing argument */
6346 + }
6347 +}
6348 +
6349 +static boolean
6350 +parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
6351 +{
6352 + const char *username;
6353 +
6354 + if (collect_arg(argv, arg_ptr, &username))
6355 + {
6356 + struct predicate *our_pred;
6357 + uid_t uid;
6358 + struct passwd *cur_pwd = getpwnam(username);
6359 + endpwent();
6360 + if (cur_pwd != NULL)
6361 + {
6362 + uid = cur_pwd->pw_uid;
6363 + }
6364 + else
6365 + {
6366 + int uid_len = strspn (username, "0123456789");
6367 + if (uid_len && (username[uid_len]==0))
6368 + uid = safe_atoi (username);
6369 + else
6370 + return false;
6371 + }
6372 + our_pred = insert_primary (entry);
6373 + our_pred->args.uid = uid;
6374 + our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2;
6375 + return true;
6376 + }
6377 + return false;
6378 +}
6379 +
6380 +static boolean
6381 +parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
6382 +{
6383 + int features = 0;
6384 + int flags;
6385 +
6386 + (void) argv;
6387 + (void) arg_ptr;
6388 + (void) entry;
6389 +
6390 + display_findutils_version("find");
6391 + printf (_("Features enabled: "));
6392 +
6393 +#if CACHE_IDS
6394 + printf("CACHE_IDS ");
6395 + ++features;
6396 +#endif
6397 +#if DEBUG
6398 + printf("DEBUG ");
6399 + ++features;
6400 +#endif
6401 +#if DEBUG_STAT
6402 + printf("DEBUG_STAT ");
6403 + ++features;
6404 +#endif
6405 +#if defined USE_STRUCT_DIRENT_D_TYPE && defined HAVE_STRUCT_DIRENT_D_TYPE
6406 + printf("D_TYPE ");
6407 + ++features;
6408 +#endif
6409 +#if defined O_NOFOLLOW
6410 + printf("O_NOFOLLOW(%s) ",
6411 + (options.open_nofollow_available ? "enabled" : "disabled"));
6412 + ++features;
6413 +#endif
6414 +#if defined LEAF_OPTIMISATION
6415 + printf("LEAF_OPTIMISATION ");
6416 + ++features;
6417 +#endif
6418 +
6419 + flags = 0;
6420 + if (is_fts_enabled(&flags))
6421 + {
6422 + int nflags = 0;
6423 + printf("FTS(");
6424 + ++features;
6425 +
6426 + if (flags & FTS_CWDFD)
6427 + {
6428 + if (nflags)
6429 + {
6430 + printf(",");
6431 + }
6432 + printf("FTS_CWDFD");
6433 + ++nflags;
6434 + }
6435 + printf(") ");
6436 + }
6437 +
6438 + printf("CBO(level=%d) ", (int)(options.optimisation_level));
6439 + ++features;
6440 +
6441 + if (0 == features)
6442 + {
6443 + /* For the moment, leave this as English in case someone wants
6444 + to parse these strings. */
6445 + printf("none");
6446 + }
6447 + printf("\n");
6448 +
6449 + exit (0);
6450 +}
6451 +
6452 +static boolean
6453 +parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
6454 +{
6455 + options.stay_on_filesystem = true;
6456 + return parse_noop(entry, argv, arg_ptr);
6457 +}
6458 +
6459 +static boolean
6460 +parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
6461 +{
6462 + options.ignore_readdir_race = true;
6463 + return parse_noop(entry, argv, arg_ptr);
6464 +}
6465 +
6466 +static boolean
6467 +parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
6468 +{
6469 + options.ignore_readdir_race = false;
6470 + return parse_noop(entry, argv, arg_ptr);
6471 +}
6472 +
6473 +static boolean
6474 +parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
6475 +{
6476 + options.warnings = true;
6477 + return parse_noop(entry, argv, arg_ptr);
6478 +}
6479 +
6480 +static boolean
6481 +parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
6482 +{
6483 + return insert_type (argv, arg_ptr, entry, pred_xtype);
6484 +}
6485 +
6486 +static boolean
6487 +insert_type (char **argv, int *arg_ptr,
6488 + const struct parser_table *entry,
6489 + PRED_FUNC which_pred)
6490 +{
6491 + mode_t type_cell;
6492 + struct predicate *our_pred;
6493 + float rate = 0.5;
6494 + const char *typeletter;
6495 +
6496 + if (collect_arg(argv, arg_ptr, &typeletter))
6497 + {
6498 + if (strlen(typeletter) != 1u)
6499 + {
6500 + error(1, 0, _("Arguments to -type should contain only one letter"));
6501 + return false;
6502 + }
6503 +
6504 + switch (typeletter[0])
6505 + {
6506 + case 'b': /* block special */
6507 + type_cell = S_IFBLK;
6508 + rate = 0.01f;
6509 + break;
6510 + case 'c': /* character special */
6511 + type_cell = S_IFCHR;
6512 + rate = 0.01f;
6513 + break;
6514 + case 'd': /* directory */
6515 + type_cell = S_IFDIR;
6516 + rate = 0.4f;
6517 + break;
6518 + case 'f': /* regular file */
6519 + type_cell = S_IFREG;
6520 + rate = 0.95f;
6521 + break;
6522 +#ifdef S_IFLNK
6523 + case 'l': /* symbolic link */
6524 + type_cell = S_IFLNK;
6525 + rate = 0.1f;
6526 + break;
6527 +#endif
6528 +#ifdef S_IFIFO
6529 + case 'p': /* pipe */
6530 + type_cell = S_IFIFO;
6531 + rate = 0.01f;
6532 + break;
6533 +#endif
6534 +#ifdef S_IFSOCK
6535 + case 's': /* socket */
6536 + type_cell = S_IFSOCK;
6537 + rate = 0.01f;
6538 + break;
6539 +#endif
6540 +#ifdef S_IFDOOR
6541 + case 'D': /* Solaris door */
6542 + type_cell = S_IFDOOR;
6543 + rate = 0.01f;
6544 + break;
6545 +#endif
6546 + default: /* None of the above ... nuke 'em. */
6547 + error(1, 0, _("Unknown argument to -type: %c"), (*typeletter));
6548 + return false;
6549 + }
6550 + our_pred = insert_primary_withpred (entry, which_pred);
6551 + our_pred->est_success_rate = rate;
6552 +
6553 + /* Figure out if we will need to stat the file, because if we don't
6554 + * need to follow symlinks, we can avoid a stat call by using
6555 + * struct dirent.d_type.
6556 + */
6557 + if (which_pred == pred_xtype)
6558 + {
6559 + our_pred->need_stat = true;
6560 + our_pred->need_type = false;
6561 + }
6562 + else
6563 + {
6564 + our_pred->need_stat = false; /* struct dirent is enough */
6565 + our_pred->need_type = true;
6566 + }
6567 + our_pred->args.type = type_cell;
6568 + return true;
6569 + }
6570 + return false;
6571 +}
6572 +
6573 +
6574 +/* Return true if the file accessed via FP is a terminal.
6575 + */
6576 +static boolean
6577 +stream_is_tty(FILE *fp)
6578 +{
6579 + int fd = fileno(fp);
6580 + if (-1 == fd)
6581 + {
6582 + return false; /* not a valid stream */
6583 + }
6584 + else
6585 + {
6586 + return isatty(fd) ? true : false;
6587 + }
6588 +
6589 +}
6590 +
6591 +
6592 +
6593 +
6594 +/* XXX: do we need to pass FUNC to this function? */
6595 +static boolean
6596 +insert_fprintf (struct format_val *vec,
6597 + const struct parser_table *entry, PRED_FUNC func,
6598 + const char *format_const)
6599 +{
6600 + char *format = (char*)format_const; /* XXX: casting away constness */
6601 + register char *scan; /* Current address in scanning `format'. */
6602 + register char *scan2; /* Address inside of element being scanned. */
6603 + struct segment **segmentp; /* Address of current segment. */
6604 + struct predicate *our_pred;
6605 +
6606 + our_pred = insert_primary_withpred (entry, func);
6607 + our_pred->side_effects = our_pred->no_default_print = true;
6608 + our_pred->args.printf_vec = *vec;
6609 + our_pred->need_type = false;
6610 + our_pred->need_stat = false;
6611 + our_pred->p_cost = NeedsNothing;
6612 +
6613 + segmentp = &our_pred->args.printf_vec.segment;
6614 + *segmentp = NULL;
6615 +
6616 + for (scan = format; *scan; scan++)
6617 + {
6618 + if (*scan == '\\')
6619 + {
6620 + scan2 = scan + 1;
6621 + if (*scan2 >= '0' && *scan2 <= '7')
6622 + {
6623 + register int n, i;
6624 +
6625 + for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
6626 + i++, scan2++)
6627 + n = 8 * n + *scan2 - '0';
6628 + scan2--;
6629 + *scan = n;
6630 + }
6631 + else
6632 + {
6633 + switch (*scan2)
6634 + {
6635 + case 'a':
6636 + *scan = 7;
6637 + break;
6638 + case 'b':
6639 + *scan = '\b';
6640 + break;
6641 + case 'c':
6642 + make_segment (segmentp, format, scan - format,
6643 + KIND_STOP, 0, 0,
6644 + our_pred);
6645 + if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo))
6646 + our_pred->p_cost = NeedsStatInfo;
6647 + return true;
6648 + case 'f':
6649 + *scan = '\f';
6650 + break;
6651 + case 'n':
6652 + *scan = '\n';
6653 + break;
6654 + case 'r':
6655 + *scan = '\r';
6656 + break;
6657 + case 't':
6658 + *scan = '\t';
6659 + break;
6660 + case 'v':
6661 + *scan = '\v';
6662 + break;
6663 + case '\\':
6664 + /* *scan = '\\'; * it already is */
6665 + break;
6666 + default:
6667 + error (0, 0,
6668 + _("warning: unrecognized escape `\\%c'"), *scan2);
6669 + scan++;
6670 + continue;
6671 + }
6672 + }
6673 + segmentp = make_segment (segmentp, format, scan - format + 1,
6674 + KIND_PLAIN, 0, 0,
6675 + our_pred);
6676 + format = scan2 + 1; /* Move past the escape. */
6677 + scan = scan2; /* Incremented immediately by `for'. */
6678 + }
6679 + else if (*scan == '%')
6680 + {
6681 + if (scan[1] == 0)
6682 + {
6683 + /* Trailing %. We don't like those. */
6684 + error (1, 0, _("error: %s at end of format string"), scan);
6685 + }
6686 + else if (scan[1] == '%')
6687 + {
6688 + segmentp = make_segment (segmentp, format, scan - format + 1,
6689 + KIND_PLAIN, 0, 0,
6690 + our_pred);
6691 + scan++;
6692 + format = scan + 1;
6693 + continue;
6694 + }
6695 + /* Scan past flags, width and precision, to verify kind. */
6696 + for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
6697 + /* Do nothing. */ ;
6698 + while (ISDIGIT (*scan2))
6699 + scan2++;
6700 + if (*scan2 == '.')
6701 + for (scan2++; ISDIGIT (*scan2); scan2++)
6702 + /* Do nothing. */ ;
6703 + if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2))
6704 + {
6705 + segmentp = make_segment (segmentp, format, scan2 - format,
6706 + KIND_FORMAT, *scan2, 0,
6707 + our_pred);
6708 + scan = scan2;
6709 + format = scan + 1;
6710 + }
6711 + else if (strchr ("ABCT", *scan2) && scan2[1])
6712 + {
6713 + segmentp = make_segment (segmentp, format, scan2 - format,
6714 + KIND_FORMAT, scan2[0], scan2[1],
6715 + our_pred);
6716 + scan = scan2 + 1;
6717 + format = scan + 1;
6718 + continue;
6719 + }
6720 + else
6721 + {
6722 + /* An unrecognized % escape. Print the char after the %. */
6723 + error (0, 0, _("warning: unrecognized format directive `%%%c'"),
6724 + *scan2);
6725 + segmentp = make_segment (segmentp, format, scan - format,
6726 + KIND_PLAIN, 0, 0,
6727 + our_pred);
6728 + format = scan + 1;
6729 + continue;
6730 + }
6731 + }
6732 + }
6733 +
6734 + if (scan > format)
6735 + make_segment (segmentp, format, scan - format, KIND_PLAIN, 0, 0,
6736 + our_pred);
6737 + return true;
6738 +}
6739 +
6740 +/* Create a new fprintf segment in *SEGMENT, with type KIND,
6741 + from the text in FORMAT, which has length LEN.
6742 + Return the address of the `next' pointer of the new segment. */
6743 +
6744 +static struct segment **
6745 +make_segment (struct segment **segment,
6746 + char *format,
6747 + int len,
6748 + int kind,
6749 + char format_char,
6750 + char aux_format_char,
6751 + struct predicate *pred)
6752 +{
6753 + enum EvaluationCost mycost = NeedsNothing;
6754 + char *fmt;
6755 +
6756 + *segment = xmalloc (sizeof (struct segment));
6757 +
6758 + (*segment)->segkind = kind;
6759 + (*segment)->format_char[0] = format_char;
6760 + (*segment)->format_char[1] = aux_format_char;
6761 + (*segment)->next = NULL;
6762 + (*segment)->text_len = len;
6763 +
6764 + fmt = (*segment)->text = xmalloc (len + sizeof "d");
6765 + strncpy (fmt, format, len);
6766 + fmt += len;
6767 +
6768 + switch (kind)
6769 + {
6770 + case KIND_PLAIN: /* Plain text string, no % conversion. */
6771 + case KIND_STOP: /* Terminate argument, no newline. */
6772 + assert (0 == format_char);
6773 + assert (0 == aux_format_char);
6774 + *fmt = '\0';
6775 + if (mycost > pred->p_cost)
6776 + pred->p_cost = NeedsNothing;
6777 + return &(*segment)->next;
6778 + break;
6779 + }
6780 +
6781 + assert (kind == KIND_FORMAT);
6782 + switch (format_char)
6783 + {
6784 + case 'l': /* object of symlink */
6785 + pred->need_stat = true;
6786 + mycost = NeedsLinkName;
6787 + *fmt++ = 's';
6788 + break;
6789 +
6790 + case 'y': /* file type */
6791 + pred->need_type = true;
6792 + mycost = NeedsType;
6793 + *fmt++ = 's';
6794 + break;
6795 +
6796 + case 'a': /* atime in `ctime' format */
6797 + case 'A': /* atime in user-specified strftime format */
6798 + case 'B': /* birth time in user-specified strftime format */
6799 + case 'c': /* ctime in `ctime' format */
6800 + case 'C': /* ctime in user-specified strftime format */
6801 + case 'F': /* file system type */
6802 + case 'g': /* group name */
6803 + case 'i': /* inode number */
6804 + case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
6805 + case 's': /* size in bytes */
6806 + case 't': /* mtime in `ctime' format */
6807 + case 'T': /* mtime in user-specified strftime format */
6808 + case 'u': /* user name */
6809 + pred->need_stat = true;
6810 + mycost = NeedsStatInfo;
6811 + *fmt++ = 's';
6812 + break;
6813 +
6814 + case 'S': /* sparseness */
6815 + pred->need_stat = true;
6816 + mycost = NeedsStatInfo;
6817 + *fmt++ = 'g';
6818 + break;
6819 +
6820 + case 'Y': /* symlink pointed file type */
6821 + pred->need_stat = true;
6822 + mycost = NeedsType; /* true for amortised effect */
6823 + *fmt++ = 's';
6824 + break;
6825 +
6826 + case 'f': /* basename of path */
6827 + case 'h': /* leading directories part of path */
6828 + case 'p': /* pathname */
6829 + case 'P': /* pathname with ARGV element stripped */
6830 + *fmt++ = 's';
6831 + break;
6832 +
6833 + case 'H': /* ARGV element file was found under */
6834 + *fmt++ = 's';
6835 + break;
6836 +
6837 + /* Numeric items that one might expect to honour
6838 + * #, 0, + flags but which do not.
6839 + */
6840 + case 'G': /* GID number */
6841 + case 'U': /* UID number */
6842 + case 'b': /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/
6843 + case 'D': /* Filesystem device on which the file exits */
6844 + case 'k': /* size in 1K blocks */
6845 + case 'n': /* number of links */
6846 + pred->need_stat = true;
6847 + mycost = NeedsStatInfo;
6848 + *fmt++ = 's';
6849 + break;
6850 +
6851 + /* Numeric items that DO honour #, 0, + flags.
6852 + */
6853 + case 'd': /* depth in search tree (0 = ARGV element) */
6854 + *fmt++ = 'd';
6855 + break;
6856 +
6857 + case 'm': /* mode as octal number (perms only) */
6858 + *fmt++ = 'o';
6859 + pred->need_stat = true;
6860 + mycost = NeedsStatInfo;
6861 + break;
6862 +
6863 + case '{':
6864 + case '[':
6865 + case '(':
6866 + error (1, 0,
6867 + _("error: the format directive `%%%c' is reserved for future use"),
6868 + (int)kind);
6869 + /*NOTREACHED*/
6870 + break;
6871 + }
6872 + *fmt = '\0';
6873 +
6874 + if (mycost > pred->p_cost)
6875 + pred->p_cost = mycost;
6876 + return &(*segment)->next;
6877 +}
6878 +
6879 +static void
6880 +check_path_safety(const char *action, char **argv)
6881 +{
6882 + char *s;
6883 + const char *path = getenv("PATH");
6884 + if (NULL == path)
6885 + {
6886 + /* $PATH is not set. Assume the OS default is safe.
6887 + * That may not be true on Windows, but I'm not aware
6888 + * of a way to get Windows to avoid searching the
6889 + * current directory anyway.
6890 + */
6891 + return;
6892 + }
6893 +
6894 + (void)argv;
6895 +
6896 + s = next_element(path, 1);
6897 + while ((s = next_element ((char *) NULL, 1)) != NULL)
6898 + {
6899 + if (0 == strcmp(s, "."))
6900 + {
6901 + error(1, 0, _("The current directory is included in the PATH "
6902 + "environment variable, which is insecure in "
6903 + "combination with the %s action of find. "
6904 + "Please remove the current directory from your "
6905 + "$PATH (that is, remove \".\" or leading or trailing "
6906 + "colons)"),
6907 + action);
6908 + }
6909 + else if ('/' != s[0])
6910 + {
6911 + /* Relative paths are also dangerous in $PATH. */
6912 + error(1, 0, _("The relative path %1$s is included in the PATH "
6913 + "environment variable, which is insecure in "
6914 + "combination with the %2$s action of find. "
6915 + "Please remove that entry from $PATH"),
6916 + safely_quote_err_filename(0, s),
6917 + action);
6918 + }
6919 + }
6920 +}
6921 +
6922 +
6923 +/* handles both exec and ok predicate */
6924 +static boolean
6925 +new_insert_exec_ok (const char *action,
6926 + const struct parser_table *entry,
6927 + int dirfd,
6928 + char **argv,
6929 + int *arg_ptr)
6930 +{
6931 + int start, end; /* Indexes in ARGV of start & end of cmd. */
6932 + int i; /* Index into cmd args */
6933 + int saw_braces; /* True if previous arg was '{}'. */
6934 + boolean allow_plus; /* True if + is a valid terminator */
6935 + int brace_count; /* Number of instances of {}. */
6936 + PRED_FUNC func = entry->pred_func;
6937 + enum BC_INIT_STATUS bcstatus;
6938 +
6939 + struct predicate *our_pred;
6940 + struct exec_val *execp; /* Pointer for efficiency. */
6941 +
6942 + if ((argv == NULL) || (argv[*arg_ptr] == NULL))
6943 + return false;
6944 +
6945 + our_pred = insert_primary_withpred (entry, func);
6946 + our_pred->side_effects = our_pred->no_default_print = true;
6947 + our_pred->need_type = our_pred->need_stat = false;
6948 +
6949 + execp = &our_pred->args.exec_vec;
6950 +
6951 + if ((func != pred_okdir) && (func != pred_ok))
6952 + {
6953 + allow_plus = true;
6954 + execp->close_stdin = false;
6955 + }
6956 + else
6957 + {
6958 + allow_plus = false;
6959 + /* If find reads stdin (i.e. for -ok and similar), close stdin
6960 + * in the child to prevent some script from consiming the output
6961 + * intended for find.
6962 + */
6963 + execp->close_stdin = true;
6964 + }
6965 +
6966 +
6967 + if ((func == pred_execdir) || (func == pred_okdir))
6968 + {
6969 + options.ignore_readdir_race = false;
6970 + check_path_safety(action, argv);
6971 + execp->use_current_dir = true;
6972 + }
6973 + else
6974 + {
6975 + execp->use_current_dir = false;
6976 + }
6977 +
6978 + our_pred->args.exec_vec.multiple = 0;
6979 +
6980 + /* Count the number of args with path replacements, up until the ';'.
6981 + * Also figure out if the command is terminated by ";" or by "+".
6982 + */
6983 + start = *arg_ptr;
6984 + for (end = start, saw_braces=0, brace_count=0;
6985 + (argv[end] != NULL)
6986 + && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
6987 + end++)
6988 + {
6989 + /* For -exec and -execdir, "{} +" can terminate the command. */
6990 + if ( allow_plus
6991 + && argv[end][0] == '+' && argv[end][1] == 0
6992 + && saw_braces)
6993 + {
6994 + our_pred->args.exec_vec.multiple = 1;
6995 + break;
6996 + }
6997 +
6998 + saw_braces = 0;
6999 + if (mbsstr (argv[end], "{}"))
7000 + {
7001 + saw_braces = 1;
7002 + ++brace_count;
7003 +
7004 + if (0 == end && (func == pred_execdir || func == pred_okdir))
7005 + {
7006 + /* The POSIX standard says that {} replacement should
7007 + * occur even in the utility name. This is insecure
7008 + * since it means we will be executing a command whose
7009 + * name is chosen according to whatever find finds in
7010 + * the file system. That can be influenced by an
7011 + * attacker. Hence for -execdir and -okdir this is not
7012 + * allowed. We can specify this as those options are
7013 + * not defined by POSIX.
7014 + */
7015 + error(1, 0, _("You may not use {} within the utility name for "
7016 + "-execdir and -okdir, because this is a potential "
7017 + "security problem."));
7018 + }
7019 + }
7020 + }
7021 +
7022 + /* Fail if no command given or no semicolon found. */
7023 + if ((end == start) || (argv[end] == NULL))
7024 + {
7025 + *arg_ptr = end;
7026 + free(our_pred);
7027 + return false;
7028 + }
7029 +
7030 + if (our_pred->args.exec_vec.multiple && brace_count > 1)
7031 + {
7032 +
7033 + const char *suffix;
7034 + if (func == pred_execdir)
7035 + suffix = "dir";
7036 + else
7037 + suffix = "";
7038 +
7039 + error(1, 0,
7040 + _("Only one instance of {} is supported with -exec%s ... +"),
7041 + suffix);
7042 + }
7043 +
7044 + /* We use a switch statement here so that the compiler warns us when
7045 + * we forget to handle a newly invented enum value.
7046 + *
7047 + * Like xargs, we allow 2KiB of headroom for the launched utility to
7048 + * export its own environment variables before calling something
7049 + * else.
7050 + */
7051 + bcstatus = bc_init_controlinfo(&execp->ctl, 2048u);
7052 + switch (bcstatus)
7053 + {
7054 + case BC_INIT_ENV_TOO_BIG:
7055 + case BC_INIT_CANNOT_ACCOMODATE_HEADROOM:
7056 + error(1, 0,
7057 + _("The environment is too large for exec()."));
7058 + break;
7059 + case BC_INIT_OK:
7060 + /* Good news. Carry on. */
7061 + break;
7062 + }
7063 + bc_use_sensible_arg_max(&execp->ctl);
7064 +
7065 +
7066 + execp->ctl.exec_callback = launch;
7067 +
7068 + if (our_pred->args.exec_vec.multiple)
7069 + {
7070 + /* "+" terminator, so we can just append our arguments after the
7071 + * command and initial arguments.
7072 + */
7073 + execp->replace_vec = NULL;
7074 + execp->ctl.replace_pat = NULL;
7075 + execp->ctl.rplen = 0;
7076 + execp->ctl.lines_per_exec = 0; /* no limit */
7077 + execp->ctl.args_per_exec = 0; /* no limit */
7078 +
7079 + /* remember how many arguments there are */
7080 + execp->ctl.initial_argc = (end-start) - 1;
7081 +
7082 + /* execp->state = xmalloc(sizeof struct buildcmd_state); */
7083 + bc_init_state(&execp->ctl, &execp->state, execp);
7084 +
7085 + /* Gather the initial arguments. Skip the {}. */
7086 + for (i=start; i<end-1; ++i)
7087 + {
7088 + bc_push_arg(&execp->ctl, &execp->state,
7089 + argv[i], strlen(argv[i])+1,
7090 + NULL, 0,
7091 + 1);
7092 + }
7093 + }
7094 + else
7095 + {
7096 + /* Semicolon terminator - more than one {} is supported, so we
7097 + * have to do brace-replacement.
7098 + */
7099 + execp->num_args = end - start;
7100 +
7101 + execp->ctl.replace_pat = "{}";
7102 + execp->ctl.rplen = strlen(execp->ctl.replace_pat);
7103 + execp->ctl.lines_per_exec = 0; /* no limit */
7104 + execp->ctl.args_per_exec = 0; /* no limit */
7105 + execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
7106 +
7107 +
7108 + /* execp->state = xmalloc(sizeof(*(execp->state))); */
7109 + bc_init_state(&execp->ctl, &execp->state, execp);
7110 +
7111 + /* Remember the (pre-replacement) arguments for later. */
7112 + for (i=0; i<execp->num_args; ++i)
7113 + {
7114 + execp->replace_vec[i] = argv[i+start];
7115 + }
7116 + }
7117 +
7118 + if (argv[end] == NULL)
7119 + *arg_ptr = end;
7120 + else
7121 + *arg_ptr = end + 1;
7122 +
7123 + return true;
7124 +}
7125 +
7126 +
7127 +
7128 +static boolean
7129 +insert_exec_ok (const char *action,
7130 + const struct parser_table *entry,
7131 + int dirfd,
7132 + char **argv,
7133 + int *arg_ptr)
7134 +{
7135 + return new_insert_exec_ok(action, entry, dirfd, argv, arg_ptr);
7136 +}
7137 +
7138 +
7139 +
7140 +/* Get a timestamp and comparison type.
7141 +
7142 + STR is the ASCII representation.
7143 + Set *NUM_DAYS to the number of days/minutes/whatever, taken as being
7144 + relative to ORIGIN (usually the current moment or midnight).
7145 + Thus the sense of the comparison type appears to be reversed.
7146 + Set *COMP_TYPE to the kind of comparison that is requested.
7147 + Issue OVERFLOWMESSAGE if overflow occurs.
7148 + Return true if all okay, false if input error.
7149 +
7150 + Used by -atime, -ctime and -mtime (parsers) to
7151 + get the appropriate information for a time predicate processor. */
7152 +
7153 +static boolean
7154 +get_relative_timestamp (const char *str,
7155 + struct time_val *result,
7156 + time_t origin,
7157 + double sec_per_unit,
7158 + const char *overflowmessage)
7159 +{
7160 + uintmax_t checkval;
7161 + double offset, seconds, f;
7162 +
7163 + if (get_comp_type(&str, &result->kind))
7164 + {
7165 + /* Invert the sense of the comparison */
7166 + switch (result->kind)
7167 + {
7168 + case COMP_LT: result->kind = COMP_GT; break;
7169 + case COMP_GT: result->kind = COMP_LT; break;
7170 + default: break;
7171 + }
7172 +
7173 + /* Convert the ASCII number into floating-point. */
7174 + if (xstrtod(str, NULL, &offset, strtod))
7175 + {
7176 + /* Separate the floating point number the user specified
7177 + * (which is a number of days, or minutes, etc) into an
7178 + * integral number of seconds (SECONDS) and a fraction (F).
7179 + */
7180 + f = modf(offset * sec_per_unit, &seconds);
7181 +
7182 + result->ts.tv_sec = origin - seconds;
7183 + result->ts.tv_nsec = fabs(f * 1e9);
7184 +
7185 + /* Check for overflow. */
7186 + checkval = (uintmax_t)origin - seconds;
7187 + if (checkval != result->ts.tv_sec)
7188 + {
7189 + /* an overflow has occurred. */
7190 + error (1, 0, overflowmessage, str);
7191 + }
7192 + return true;
7193 + }
7194 + else
7195 + {
7196 + /* Conversion from ASCII to double failed. */
7197 + return false;
7198 + }
7199 + }
7200 + else
7201 + {
7202 + return false;
7203 + }
7204 +}
7205 +
7206 +/* Insert a time predicate based on the information in ENTRY.
7207 + ARGV is a pointer to the argument array.
7208 + ARG_PTR is a pointer to an index into the array, incremented if
7209 + all went well.
7210 +
7211 + Return true if input is valid, false if not.
7212 +
7213 + A new predicate node is assigned, along with an argument node
7214 + obtained with malloc.
7215 +
7216 + Used by -atime, -ctime, and -mtime parsers. */
7217 +
7218 +static boolean
7219 +parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
7220 +{
7221 + struct predicate *our_pred;
7222 + struct time_val tval;
7223 + enum comparison_type comp;
7224 + const char *timearg, *orig_timearg;
7225 + const char *errmsg = "arithmetic overflow while converting %s "
7226 + "days to a number of seconds";
7227 + time_t origin;
7228 +
7229 + if (!collect_arg(argv, arg_ptr, &timearg))
7230 + return false;
7231 + orig_timearg = timearg;
7232 +
7233 + /* Decide the origin by previewing the comparison type. */
7234 + origin = options.cur_day_start;
7235 +
7236 + if (get_comp_type(&timearg, &comp))
7237 + {
7238 + /* Remember, we invert the sense of the comparison, so this tests
7239 + * against COMP_LT instead of COMP_GT...
7240 + */
7241 + if (COMP_LT == comp)
7242 + {
7243 + uintmax_t expected = origin + (DAYSECS-1);
7244 + origin += (DAYSECS-1);
7245 + if (origin != expected)
7246 + {
7247 + error(1, 0,
7248 + _("arithmetic overflow when trying to calculate the end of today"));
7249 + }
7250 + }
7251 + }
7252 + /* We discard the value of comp here, as get_relative_timestamp
7253 + * will set tval.kind. For that to work, we have to restore
7254 + * timearg so that it points to the +/- prefix, if any. get_comp_type()
7255 + * will have advanced timearg, so we restore it.
7256 + */
7257 + timearg = orig_timearg;
7258 +
7259 + if (!get_relative_timestamp(timearg, &tval, origin, DAYSECS, errmsg))
7260 + return false;
7261 +
7262 + our_pred = insert_primary (entry);
7263 + our_pred->args.reftime = tval;
7264 + our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec);
7265 +
7266 + if (options.debug_options & DebugExpressionTree)
7267 + {
7268 + time_t t;
7269 +
7270 + fprintf (stderr, "inserting %s\n", our_pred->p_name);
7271 + fprintf (stderr, " type: %s %s ",
7272 + (tval.kind == COMP_GT) ? "gt" :
7273 + ((tval.kind == COMP_LT) ? "lt" : ((tval.kind == COMP_EQ) ? "eq" : "?")),
7274 + (tval.kind == COMP_GT) ? " >" :
7275 + ((tval.kind == COMP_LT) ? " <" : ((tval.kind == COMP_EQ) ? ">=" : " ?")));
7276 + t = our_pred->args.reftime.ts.tv_sec;
7277 + fprintf (stderr, "%ju %s",
7278 + (uintmax_t) our_pred->args.reftime.ts.tv_sec,
7279 + ctime (&t));
7280 + if (tval.kind == COMP_EQ)
7281 + {
7282 + t = our_pred->args.reftime.ts.tv_sec + DAYSECS;
7283 + fprintf (stderr, " < %ju %s",
7284 + (uintmax_t) t, ctime (&t));
7285 + }
7286 + }
7287 +
7288 + return true;
7289 +}
7290 +
7291 +/* Get the comparison type prefix (if any) from a number argument.
7292 + The prefix is at *STR.
7293 + Set *COMP_TYPE to the kind of comparison that is requested.
7294 + Advance *STR beyond any initial comparison prefix.
7295 +
7296 + Return true if all okay, false if input error. */
7297 +static boolean
7298 +get_comp_type(const char **str, enum comparison_type *comp_type)
7299 +{
7300 + switch (**str)
7301 + {
7302 + case '+':
7303 + *comp_type = COMP_GT;
7304 + (*str)++;
7305 + break;
7306 + case '-':
7307 + *comp_type = COMP_LT;
7308 + (*str)++;
7309 + break;
7310 + default:
7311 + *comp_type = COMP_EQ;
7312 + break;
7313 + }
7314 + return true;
7315 +}
7316 +
7317 +
7318 +
7319 +
7320 +
7321 +/* Get a number with comparison information.
7322 + The sense of the comparison information is 'normal'; that is,
7323 + '+' looks for a count > than the number and '-' less than.
7324 +
7325 + STR is the ASCII representation of the number.
7326 + Set *NUM to the number.
7327 + Set *COMP_TYPE to the kind of comparison that is requested.
7328 +
7329 + Return true if all okay, false if input error. */
7330 +
7331 +static boolean
7332 +get_num (const char *str,
7333 + uintmax_t *num,
7334 + enum comparison_type *comp_type)
7335 +{
7336 + char *pend;
7337 +
7338 + if (str == NULL)
7339 + return false;
7340 +
7341 + /* Figure out the comparison type if the caller accepts one. */
7342 + if (comp_type)
7343 + {
7344 + if (!get_comp_type(&str, comp_type))
7345 + return false;
7346 + }
7347 +
7348 + return xstrtoumax (str, &pend, 10, num, "") == LONGINT_OK;
7349 +}
7350 +
7351 +/* Insert a number predicate.
7352 + ARGV is a pointer to the argument array.
7353 + *ARG_PTR is an index into ARGV, incremented if all went well.
7354 + *PRED is the predicate processor to insert.
7355 +
7356 + Return true if input is valid, false if error.
7357 +
7358 + A new predicate node is assigned, along with an argument node
7359 + obtained with malloc.
7360 +
7361 + Used by -inum and -links parsers. */
7362 +
7363 +static struct predicate *
7364 +insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
7365 +{
7366 + const char *numstr;
7367 +
7368 + if (collect_arg(argv, arg_ptr, &numstr))
7369 + {
7370 + uintmax_t num;
7371 + enum comparison_type c_type;
7372 +
7373 + if (get_num (numstr, &num, &c_type))
7374 + {
7375 + struct predicate *our_pred = insert_primary (entry);
7376 + our_pred->args.numinfo.kind = c_type;
7377 + our_pred->args.numinfo.l_val = num;
7378 +
7379 + if (options.debug_options & DebugExpressionTree)
7380 + {
7381 + fprintf (stderr, "inserting %s\n", our_pred->p_name);
7382 + fprintf (stderr, " type: %s %s ",
7383 + (c_type == COMP_GT) ? "gt" :
7384 + ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
7385 + (c_type == COMP_GT) ? " >" :
7386 + ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
7387 + fprintf (stderr, "%ju\n", our_pred->args.numinfo.l_val);
7388 + }
7389 + return our_pred;
7390 + }
7391 + }
7392 + return NULL;
7393 +}
7394 +
7395 +static void
7396 +open_output_file (const char *path, struct format_val *p)
7397 +{
7398 + p->segment = NULL;
7399 + p->quote_opts = clone_quoting_options (NULL);
7400 +
7401 + if (!strcmp (path, "/dev/stderr"))
7402 + {
7403 + p->stream = stderr;
7404 + p->filename = _("standard error");
7405 + }
7406 + else if (!strcmp (path, "/dev/stdout"))
7407 + {
7408 + p->stream = stdout;
7409 + p->filename = _("standard output");
7410 + }
7411 + else
7412 + {
7413 + p->stream = fopen_safer (path, "w");
7414 + p->filename = path;
7415 +
7416 + if (p->stream == NULL)
7417 + {
7418 + fatal_file_error(path);
7419 + }
7420 + }
7421 +
7422 + p->dest_is_tty = stream_is_tty(p->stream);
7423 +}
7424 +
7425 +static void
7426 +open_stdout (struct format_val *p)
7427 +{
7428 + open_output_file("/dev/stdout", p);
7429 +}
7430 diff -purN findutils-4.3.12.orig/find/pred.c findutils-4.3.12/find/pred.c
7431 --- findutils-4.3.12.orig/find/pred.c 2007-12-19 16:12:34.000000000 -0500
7432 +++ findutils-4.3.12/find/pred.c 2008-01-30 08:46:05.758843847 -0500
7433 @@ -47,6 +47,14 @@
7434 #include "error.h"
7435 #include "verify.h"
7436
7437 +#ifdef WITH_SELINUX
7438 +#include <selinux/selinux.h>
7439 +#endif /*WITH_SELINUX*/
7440 +
7441 +#ifndef FNM_CASEFOLD
7442 +#define FNM_CASEFOLD (1<<4)
7443 +#endif /*FNM_CASEFOLD*/
7444 +
7445 #if ENABLE_NLS
7446 # include <libintl.h>
7447 # define _(Text) gettext (Text)
7448 @@ -229,6 +237,9 @@ struct pred_assoc pred_table[] =
7449 {pred_user, "user "},
7450 {pred_writable, "writable "},
7451 {pred_xtype, "xtype "},
7452 +#ifdef WITH_SELINUX
7453 + {pred_scontext, "context"},
7454 +#endif /*WITH_SELINUX*/
7455 {0, "none "}
7456 };
7457 #endif
7458 @@ -1045,6 +1056,26 @@ do_fprintf(struct format_val *dest,
7459 mode_to_filetype(stat_buf->st_mode & S_IFMT));
7460 }
7461 break;
7462 +#ifdef WITH_SELINUX
7463 + case 'Z': /* SELinux security context */
7464 + {
7465 + security_context_t scontext;
7466 + int rv;
7467 + rv = (*options.x_getfilecon)(state.rel_pathname, &scontext);
7468 +
7469 + if ( rv < 0 ) {
7470 + fprintf(stderr, "getfileconf(%s): %s",
7471 + pathname, strerror(errno));
7472 + fflush(stderr);
7473 + }
7474 + else {
7475 + segment->text[segment->text_len] = 's';
7476 + checked_fprintf (dest, segment->text, scontext);
7477 + freecon(scontext);
7478 + }
7479 + }
7480 + break ;
7481 +#endif /* WITH_SELINUX */
7482 }
7483 /* end of KIND_FORMAT case */
7484 break;
7485 @@ -1838,6 +1869,31 @@ pred_xtype (const char *pathname, struct
7486 */
7487 return (pred_type (pathname, &sbuf, pred_ptr));
7488 }
7489 +
7490 +
7491 +#ifdef WITH_SELINUX
7492 +
7493 +boolean
7494 +pred_scontext (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7495 +{
7496 + int rv;
7497 + security_context_t scontext;
7498 +
7499 + rv = (* options.x_getfilecon)(state.rel_pathname, &scontext);
7500 +
7501 + if ( rv < 0 ) {
7502 + (void) fprintf(stderr, "getfilecon(%s): %s\n", pathname, strerror(errno));
7503 + (void) fflush(stderr);
7504 + return ( false );
7505 + }
7506 +
7507 + rv = (fnmatch(pred_ptr->args.scontext, scontext,0)==0);
7508 + freecon(scontext);
7509 + return rv;
7510 +}
7511 +
7512 +#endif /*WITH_SELINUX*/
7513 +
7514
7515 /* 1) fork to get a child; parent remembers the child pid
7516 2) child execs the command requested
7517 diff -purN findutils-4.3.12.orig/find/pred.c.orig findutils-4.3.12/find/pred.c.orig
7518 --- findutils-4.3.12.orig/find/pred.c.orig 1969-12-31 19:00:00.000000000 -0500
7519 +++ findutils-4.3.12/find/pred.c.orig 2007-12-19 16:12:34.000000000 -0500
7520 @@ -0,0 +1,2405 @@
7521 +/* pred.c -- execute the expression tree.
7522 + Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003,
7523 + 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
7524 +
7525 + This program is free software: you can redistribute it and/or modify
7526 + it under the terms of the GNU General Public License as published by
7527 + the Free Software Foundation, either version 3 of the License, or
7528 + (at your option) any later version.
7529 +
7530 + This program is distributed in the hope that it will be useful,
7531 + but WITHOUT ANY WARRANTY; without even the implied warranty of
7532 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7533 + GNU General Public License for more details.
7534 +
7535 + You should have received a copy of the GNU General Public License
7536 + along with this program. If not, see <http://www.gnu.org/licenses/>.
7537 +*/
7538 +
7539 +#include <config.h>
7540 +#include "defs.h"
7541 +
7542 +#include <fnmatch.h>
7543 +#include <signal.h>
7544 +#include <math.h>
7545 +#include <pwd.h>
7546 +#include <grp.h>
7547 +#include <sys/types.h>
7548 +#include <sys/stat.h>
7549 +#include <errno.h>
7550 +#include <assert.h>
7551 +#include <stdarg.h>
7552 +#include <fcntl.h>
7553 +#include <locale.h>
7554 +#include <openat.h>
7555 +#include "xalloc.h"
7556 +#include "dirname.h"
7557 +#include "human.h"
7558 +#include "modetype.h"
7559 +#include "filemode.h"
7560 +#include "wait.h"
7561 +#include "printquoted.h"
7562 +#include "buildcmd.h"
7563 +#include "yesno.h"
7564 +#include "listfile.h"
7565 +#include "stat-time.h"
7566 +#include "dircallback.h"
7567 +#include "error.h"
7568 +#include "verify.h"
7569 +
7570 +#if ENABLE_NLS
7571 +# include <libintl.h>
7572 +# define _(Text) gettext (Text)
7573 +#else
7574 +# define _(Text) Text
7575 +#endif
7576 +#ifdef gettext_noop
7577 +# define N_(String) gettext_noop (String)
7578 +#else
7579 +/* See locate.c for explanation as to why not use (String) */
7580 +# define N_(String) String
7581 +#endif
7582 +
7583 +#if !defined(SIGCHLD) && defined(SIGCLD)
7584 +#define SIGCHLD SIGCLD
7585 +#endif
7586 +
7587 +
7588 +
7589 +#if HAVE_DIRENT_H
7590 +# include <dirent.h>
7591 +# define NAMLEN(dirent) strlen((dirent)->d_name)
7592 +#else
7593 +# define dirent direct
7594 +# define NAMLEN(dirent) (dirent)->d_namlen
7595 +# if HAVE_SYS_NDIR_H
7596 +# include <sys/ndir.h>
7597 +# endif
7598 +# if HAVE_SYS_DIR_H
7599 +# include <sys/dir.h>
7600 +# endif
7601 +# if HAVE_NDIR_H
7602 +# include <ndir.h>
7603 +# endif
7604 +#endif
7605 +
7606 +#ifdef CLOSEDIR_VOID
7607 +/* Fake a return value. */
7608 +#define CLOSEDIR(d) (closedir (d), 0)
7609 +#else
7610 +#define CLOSEDIR(d) closedir (d)
7611 +#endif
7612 +
7613 +
7614 +
7615 +
7616 +/* Get or fake the disk device blocksize.
7617 + Usually defined by sys/param.h (if at all). */
7618 +#ifndef DEV_BSIZE
7619 +# ifdef BSIZE
7620 +# define DEV_BSIZE BSIZE
7621 +# else /* !BSIZE */
7622 +# define DEV_BSIZE 4096
7623 +# endif /* !BSIZE */
7624 +#endif /* !DEV_BSIZE */
7625 +
7626 +/* Extract or fake data from a `struct stat'.
7627 + ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
7628 + ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
7629 + ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
7630 +#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
7631 +# define ST_BLKSIZE(statbuf) DEV_BSIZE
7632 +# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */
7633 +# define ST_NBLOCKS(statbuf) \
7634 + (S_ISREG ((statbuf).st_mode) \
7635 + || S_ISDIR ((statbuf).st_mode) \
7636 + ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
7637 +# else /* !_POSIX_SOURCE && BSIZE */
7638 +# define ST_NBLOCKS(statbuf) \
7639 + (S_ISREG ((statbuf).st_mode) \
7640 + || S_ISDIR ((statbuf).st_mode) \
7641 + ? st_blocks ((statbuf).st_size) : 0)
7642 +# endif /* !_POSIX_SOURCE && BSIZE */
7643 +#else /* HAVE_STRUCT_STAT_ST_BLOCKS */
7644 +/* Some systems, like Sequents, return st_blksize of 0 on pipes. */
7645 +# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
7646 + ? (statbuf).st_blksize : DEV_BSIZE)
7647 +# if defined hpux || defined __hpux__ || defined __hpux
7648 +/* HP-UX counts st_blocks in 1024-byte units.
7649 + This loses when mixing HP-UX and BSD file systems with NFS. */
7650 +# define ST_NBLOCKSIZE 1024
7651 +# else /* !hpux */
7652 +# if defined _AIX && defined _I386
7653 +/* AIX PS/2 counts st_blocks in 4K units. */
7654 +# define ST_NBLOCKSIZE (4 * 1024)
7655 +# else /* not AIX PS/2 */
7656 +# if defined _CRAY
7657 +# define ST_NBLOCKS(statbuf) \
7658 + (S_ISREG ((statbuf).st_mode) \
7659 + || S_ISDIR ((statbuf).st_mode) \
7660 + ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
7661 +# endif /* _CRAY */
7662 +# endif /* not AIX PS/2 */
7663 +# endif /* !hpux */
7664 +#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
7665 +
7666 +#ifndef ST_NBLOCKS
7667 +# define ST_NBLOCKS(statbuf) \
7668 + (S_ISREG ((statbuf).st_mode) \
7669 + || S_ISDIR ((statbuf).st_mode) \
7670 + ? (statbuf).st_blocks : 0)
7671 +#endif
7672 +
7673 +#ifndef ST_NBLOCKSIZE
7674 +# define ST_NBLOCKSIZE 512
7675 +#endif
7676 +
7677 +
7678 +#undef MAX
7679 +#define MAX(a, b) ((a) > (b) ? (a) : (b))
7680 +
7681 +static boolean match_lname PARAMS((const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case));
7682 +
7683 +static char *format_date PARAMS((struct timespec ts, int kind));
7684 +static char *ctime_format PARAMS((struct timespec ts));
7685 +
7686 +#ifdef DEBUG
7687 +struct pred_assoc
7688 +{
7689 + PRED_FUNC pred_func;
7690 + char *pred_name;
7691 +};
7692 +
7693 +struct pred_assoc pred_table[] =
7694 +{
7695 + {pred_amin, "amin "},
7696 + {pred_and, "and "},
7697 + {pred_anewer, "anewer "},
7698 + {pred_atime, "atime "},
7699 + {pred_closeparen, ") "},
7700 + {pred_cmin, "cmin "},
7701 + {pred_cnewer, "cnewer "},
7702 + {pred_comma, ", "},
7703 + {pred_ctime, "ctime "},
7704 + {pred_delete, "delete "},
7705 + {pred_empty, "empty "},
7706 + {pred_exec, "exec "},
7707 + {pred_execdir, "execdir "},
7708 + {pred_executable, "executable "},
7709 + {pred_false, "false "},
7710 + {pred_fprint, "fprint "},
7711 + {pred_fprint0, "fprint0 "},
7712 + {pred_fprintf, "fprintf "},
7713 + {pred_fstype, "fstype "},
7714 + {pred_gid, "gid "},
7715 + {pred_group, "group "},
7716 + {pred_ilname, "ilname "},
7717 + {pred_iname, "iname "},
7718 + {pred_inum, "inum "},
7719 + {pred_ipath, "ipath "},
7720 + {pred_links, "links "},
7721 + {pred_lname, "lname "},
7722 + {pred_ls, "ls "},
7723 + {pred_mmin, "mmin "},
7724 + {pred_mtime, "mtime "},
7725 + {pred_name, "name "},
7726 + {pred_negate, "not "},
7727 + {pred_newer, "newer "},
7728 + {pred_newerXY, "newerXY "},
7729 + {pred_nogroup, "nogroup "},
7730 + {pred_nouser, "nouser "},
7731 + {pred_ok, "ok "},
7732 + {pred_okdir, "okdir "},
7733 + {pred_openparen, "( "},
7734 + {pred_or, "or "},
7735 + {pred_path, "path "},
7736 + {pred_perm, "perm "},
7737 + {pred_print, "print "},
7738 + {pred_print0, "print0 "},
7739 + {pred_prune, "prune "},
7740 + {pred_quit, "quit "},
7741 + {pred_readable, "readable "},
7742 + {pred_regex, "regex "},
7743 + {pred_samefile,"samefile "},
7744 + {pred_size, "size "},
7745 + {pred_true, "true "},
7746 + {pred_type, "type "},
7747 + {pred_uid, "uid "},
7748 + {pred_used, "used "},
7749 + {pred_user, "user "},
7750 + {pred_writable, "writable "},
7751 + {pred_xtype, "xtype "},
7752 + {0, "none "}
7753 +};
7754 +#endif
7755 +
7756 +/* Returns ts1 - ts2 */
7757 +static double ts_difference(struct timespec ts1,
7758 + struct timespec ts2)
7759 +{
7760 + double d = difftime(ts1.tv_sec, ts2.tv_sec)
7761 + + (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec));
7762 + return d;
7763 +}
7764 +
7765 +
7766 +static int
7767 +compare_ts(struct timespec ts1,
7768 + struct timespec ts2)
7769 +{
7770 + if ((ts1.tv_sec == ts2.tv_sec) &&
7771 + (ts1.tv_nsec == ts2.tv_nsec))
7772 + {
7773 + return 0;
7774 + }
7775 + else
7776 + {
7777 + double diff = ts_difference(ts1, ts2);
7778 + return diff < 0.0 ? -1 : +1;
7779 + }
7780 +}
7781 +
7782 +/* Predicate processing routines.
7783 +
7784 + PATHNAME is the full pathname of the file being checked.
7785 + *STAT_BUF contains information about PATHNAME.
7786 + *PRED_PTR contains information for applying the predicate.
7787 +
7788 + Return true if the file passes this predicate, false if not. */
7789 +
7790 +
7791 +/* pred_timewindow
7792 + *
7793 + * Returns true if THE_TIME is
7794 + * COMP_GT: after the specified time
7795 + * COMP_LT: before the specified time
7796 + * COMP_EQ: less than WINDOW seconds after the specified time.
7797 + */
7798 +static boolean
7799 +pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window)
7800 +{
7801 + switch (pred_ptr->args.reftime.kind)
7802 + {
7803 + case COMP_GT:
7804 + return compare_ts(ts, pred_ptr->args.reftime.ts) > 0;
7805 +
7806 + case COMP_LT:
7807 + return compare_ts(ts, pred_ptr->args.reftime.ts) < 0;
7808 +
7809 + case COMP_EQ:
7810 + {
7811 + double delta = ts_difference(ts, pred_ptr->args.reftime.ts);
7812 + return (delta >= 0.0 && delta < window);
7813 + }
7814 + }
7815 + assert (0);
7816 + abort ();
7817 +}
7818 +
7819 +
7820 +boolean
7821 +pred_amin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7822 +{
7823 + (void) &pathname;
7824 + return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60);
7825 +}
7826 +
7827 +boolean
7828 +pred_and (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7829 +{
7830 + if (pred_ptr->pred_left == NULL
7831 + || apply_predicate(pathname, stat_buf, pred_ptr->pred_left))
7832 + {
7833 + return apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
7834 + }
7835 + else
7836 + return false;
7837 +}
7838 +
7839 +boolean
7840 +pred_anewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7841 +{
7842 + (void) &pathname;
7843 + assert (COMP_GT == pred_ptr->args.reftime.kind);
7844 + return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0;
7845 +}
7846 +
7847 +boolean
7848 +pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7849 +{
7850 + (void) &pathname;
7851 + return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS);
7852 +}
7853 +
7854 +boolean
7855 +pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7856 +{
7857 + (void) &pathname;
7858 + (void) &stat_buf;
7859 + (void) &pred_ptr;
7860 +
7861 + return true;
7862 +}
7863 +
7864 +boolean
7865 +pred_cmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7866 +{
7867 + (void) pathname;
7868 + return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60);
7869 +}
7870 +
7871 +boolean
7872 +pred_cnewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7873 +{
7874 + (void) pathname;
7875 +
7876 + assert (COMP_GT == pred_ptr->args.reftime.kind);
7877 + return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0;
7878 +}
7879 +
7880 +boolean
7881 +pred_comma (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7882 +{
7883 + if (pred_ptr->pred_left != NULL)
7884 + {
7885 + apply_predicate(pathname, stat_buf,pred_ptr->pred_left);
7886 + }
7887 + return apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
7888 +}
7889 +
7890 +boolean
7891 +pred_ctime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7892 +{
7893 + (void) &pathname;
7894 + return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS);
7895 +}
7896 +
7897 +static boolean
7898 +perform_delete(int flags)
7899 +{
7900 + return 0 == unlinkat(state.cwd_dir_fd, state.rel_pathname, flags);
7901 +}
7902 +
7903 +
7904 +boolean
7905 +pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7906 +{
7907 + (void) pred_ptr;
7908 + (void) stat_buf;
7909 + if (strcmp (state.rel_pathname, "."))
7910 + {
7911 + int flags=0;
7912 + if (state.have_stat && S_ISDIR(stat_buf->st_mode))
7913 + flags |= AT_REMOVEDIR;
7914 + if (perform_delete(flags))
7915 + {
7916 + return true;
7917 + }
7918 + else
7919 + {
7920 + if (EISDIR == errno)
7921 + {
7922 + if ((flags & AT_REMOVEDIR) == 0)
7923 + {
7924 + /* unlink() operation failed because we should have done rmdir(). */
7925 + flags |= AT_REMOVEDIR;
7926 + if (perform_delete(flags))
7927 + return true;
7928 + }
7929 + }
7930 + }
7931 + error (0, errno, _("cannot delete %s"
7932 + /* TRANSLATORS: the argument is either a
7933 + * file or a directory, but we do not know which.
7934 + * Mail bug-findutils@×××.org if you cannot correctly
7935 + * translate the string without knowing.
7936 + */),
7937 + safely_quote_err_filename(0, pathname));
7938 + /* Previously I had believed that having the -delete action
7939 + * return false provided the user with control over whether an
7940 + * error message is issued. While this is true, the policy of
7941 + * not affecting the exit status is contrary to the POSIX
7942 + * requirement that diagnostic messages are accompanied by a
7943 + * nonzero exit status. While -delete is not a POSIX option and
7944 + * we can therefore opt not to follow POSIX in this case, that
7945 + * seems somewhat arbitrary and confusing. So, as of
7946 + * findutils-4.3.11, we also set the exit status in this case.
7947 + */
7948 + state.exit_status = 1;
7949 + return false;
7950 + }
7951 + else
7952 + {
7953 + /* nothing to do. */
7954 + return true;
7955 + }
7956 +}
7957 +
7958 +boolean
7959 +pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
7960 +{
7961 + (void) pathname;
7962 + (void) pred_ptr;
7963 +
7964 + if (S_ISDIR (stat_buf->st_mode))
7965 + {
7966 + int fd;
7967 + DIR *d;
7968 + struct dirent *dp;
7969 + boolean empty = true;
7970 +
7971 + errno = 0;
7972 + if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY
7973 +#if defined O_LARGEFILE
7974 + |O_LARGEFILE
7975 +#endif
7976 + )) < 0)
7977 + {
7978 + error (0, errno, "%s", safely_quote_err_filename(0, pathname));
7979 + state.exit_status = 1;
7980 + return false;
7981 + }
7982 + d = fdopendir (fd);
7983 + if (d == NULL)
7984 + {
7985 + error (0, errno, "%s", safely_quote_err_filename(0, pathname));
7986 + state.exit_status = 1;
7987 + return false;
7988 + }
7989 + for (dp = readdir (d); dp; dp = readdir (d))
7990 + {
7991 + if (dp->d_name[0] != '.'
7992 + || (dp->d_name[1] != '\0'
7993 + && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
7994 + {
7995 + empty = false;
7996 + break;
7997 + }
7998 + }
7999 + if (CLOSEDIR (d))
8000 + {
8001 + error (0, errno, "%s", safely_quote_err_filename(0, pathname));
8002 + state.exit_status = 1;
8003 + return false;
8004 + }
8005 + return (empty);
8006 + }
8007 + else if (S_ISREG (stat_buf->st_mode))
8008 + return (stat_buf->st_size == 0);
8009 + else
8010 + return (false);
8011 +}
8012 +
8013 +static boolean
8014 +new_impl_pred_exec (int dirfd, const char *pathname,
8015 + struct stat *stat_buf,
8016 + struct predicate *pred_ptr,
8017 + const char *prefix, size_t pfxlen)
8018 +{
8019 + struct exec_val *execp = &pred_ptr->args.exec_vec;
8020 + size_t len = strlen(pathname);
8021 +
8022 + (void) stat_buf;
8023 + execp->dirfd = dirfd;
8024 + if (execp->multiple)
8025 + {
8026 + /* Push the argument onto the current list.
8027 + * The command may or may not be run at this point,
8028 + * depending on the command line length limits.
8029 + */
8030 + bc_push_arg(&execp->ctl,
8031 + &execp->state,
8032 + pathname, len+1,
8033 + prefix, pfxlen,
8034 + 0);
8035 +
8036 + /* remember that there are pending execdirs. */
8037 + state.execdirs_outstanding = true;
8038 +
8039 + /* POSIX: If the primary expression is punctuated by a plus
8040 + * sign, the primary shall always evaluate as true
8041 + */
8042 + return true;
8043 + }
8044 + else
8045 + {
8046 + int i;
8047 +
8048 + for (i=0; i<execp->num_args; ++i)
8049 + {
8050 + bc_do_insert(&execp->ctl,
8051 + &execp->state,
8052 + execp->replace_vec[i],
8053 + strlen(execp->replace_vec[i]),
8054 + prefix, pfxlen,
8055 + pathname, len,
8056 + 0);
8057 + }
8058 +
8059 + /* Actually invoke the command. */
8060 + return execp->ctl.exec_callback(&execp->ctl,
8061 + &execp->state);
8062 + }
8063 +}
8064 +
8065 +
8066 +boolean
8067 +pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8068 +{
8069 + return new_impl_pred_exec(get_start_dirfd(),
8070 + pathname, stat_buf, pred_ptr, NULL, 0);
8071 +}
8072 +
8073 +boolean
8074 +pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8075 +{
8076 + const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./";
8077 + (void) &pathname;
8078 + return new_impl_pred_exec (get_current_dirfd(),
8079 + state.rel_pathname, stat_buf, pred_ptr,
8080 + prefix, (prefix ? 2 : 0));
8081 +}
8082 +
8083 +boolean
8084 +pred_false (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8085 +{
8086 + (void) &pathname;
8087 + (void) &stat_buf;
8088 + (void) &pred_ptr;
8089 +
8090 +
8091 + return (false);
8092 +}
8093 +
8094 +boolean
8095 +pred_fls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8096 +{
8097 + FILE * stream = pred_ptr->args.printf_vec.stream;
8098 + list_file (pathname, state.cwd_dir_fd, state.rel_pathname, stat_buf,
8099 + options.start_time.tv_sec,
8100 + options.output_block_size,
8101 + pred_ptr->literal_control_chars, stream);
8102 + return true;
8103 +}
8104 +
8105 +boolean
8106 +pred_fprint (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8107 +{
8108 + (void) &pathname;
8109 + (void) &stat_buf;
8110 +
8111 + print_quoted(pred_ptr->args.printf_vec.stream,
8112 + pred_ptr->args.printf_vec.quote_opts,
8113 + pred_ptr->args.printf_vec.dest_is_tty,
8114 + "%s\n",
8115 + pathname);
8116 + return true;
8117 +}
8118 +
8119 +boolean
8120 +pred_fprint0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8121 +{
8122 + FILE * fp = pred_ptr->args.printf_vec.stream;
8123 +
8124 + (void) &stat_buf;
8125 +
8126 + fputs (pathname, fp);
8127 + putc (0, fp);
8128 + return true;
8129 +}
8130 +
8131 +
8132 +
8133 +static char*
8134 +mode_to_filetype(mode_t m)
8135 +{
8136 +#define HANDLE_TYPE(t,letter) if (m==t) { return letter; }
8137 +#ifdef S_IFREG
8138 + HANDLE_TYPE(S_IFREG, "f"); /* regular file */
8139 +#endif
8140 +#ifdef S_IFDIR
8141 + HANDLE_TYPE(S_IFDIR, "d"); /* directory */
8142 +#endif
8143 +#ifdef S_IFLNK
8144 + HANDLE_TYPE(S_IFLNK, "l"); /* symbolic link */
8145 +#endif
8146 +#ifdef S_IFSOCK
8147 + HANDLE_TYPE(S_IFSOCK, "s"); /* Unix domain socket */
8148 +#endif
8149 +#ifdef S_IFBLK
8150 + HANDLE_TYPE(S_IFBLK, "b"); /* block device */
8151 +#endif
8152 +#ifdef S_IFCHR
8153 + HANDLE_TYPE(S_IFCHR, "c"); /* character device */
8154 +#endif
8155 +#ifdef S_IFIFO
8156 + HANDLE_TYPE(S_IFIFO, "p"); /* FIFO */
8157 +#endif
8158 +#ifdef S_IFDOOR
8159 + HANDLE_TYPE(S_IFDOOR, "D"); /* Door (e.g. on Solaris) */
8160 +#endif
8161 + return "U"; /* Unknown */
8162 +}
8163 +
8164 +static double
8165 +file_sparseness(const struct stat *p)
8166 +{
8167 +#if defined HAVE_STRUCT_STAT_ST_BLOCKS
8168 + if (0 == p->st_size)
8169 + {
8170 + if (0 == p->st_blocks)
8171 + return 1.0;
8172 + else
8173 + return p->st_blocks < 0 ? -HUGE_VAL : HUGE_VAL;
8174 + }
8175 + else
8176 + {
8177 + double blklen = file_blocksize(p) * (double)p->st_blocks;
8178 + return blklen / p->st_size;
8179 + }
8180 +#else
8181 + return 1.0;
8182 +#endif
8183 +}
8184 +
8185 +
8186 +
8187 +static void
8188 +checked_fprintf(struct format_val *dest, const char *fmt, ...)
8189 +{
8190 + int rv;
8191 + va_list ap;
8192 +
8193 + va_start(ap, fmt);
8194 + rv = vfprintf(dest->stream, fmt, ap);
8195 + if (rv < 0)
8196 + nonfatal_file_error(dest->filename);
8197 +}
8198 +
8199 +
8200 +static void
8201 +checked_print_quoted (struct format_val *dest,
8202 + const char *format, const char *s)
8203 +{
8204 + int rv = print_quoted(dest->stream, dest->quote_opts, dest->dest_is_tty,
8205 + format, s);
8206 + if (rv < 0)
8207 + nonfatal_file_error(dest->filename);
8208 +}
8209 +
8210 +
8211 +static void
8212 +checked_fwrite(void *p, size_t siz, size_t nmemb, struct format_val *dest)
8213 +{
8214 + int items_written = fwrite(p, siz, nmemb, dest->stream);
8215 + if (items_written < nmemb)
8216 + nonfatal_file_error(dest->filename);
8217 +}
8218 +
8219 +static void
8220 +checked_fflush(struct format_val *dest)
8221 +{
8222 + if (0 != fflush(dest->stream))
8223 + {
8224 + nonfatal_file_error(dest->filename);
8225 + }
8226 +}
8227 +
8228 +static void
8229 +do_fprintf(struct format_val *dest,
8230 + struct segment *segment,
8231 + const char *pathname,
8232 + const struct stat *stat_buf)
8233 +{
8234 + char hbuf[LONGEST_HUMAN_READABLE + 1];
8235 + const char *cp;
8236 +
8237 + switch (segment->segkind)
8238 + {
8239 + case KIND_PLAIN: /* Plain text string (no % conversion). */
8240 + /* trusted */
8241 + checked_fwrite(segment->text, 1, segment->text_len, dest);
8242 + break;
8243 +
8244 + case KIND_STOP: /* Terminate argument and flush output. */
8245 + /* trusted */
8246 + checked_fwrite(segment->text, 1, segment->text_len, dest);
8247 + checked_fflush(dest);
8248 + break;
8249 +
8250 + case KIND_FORMAT:
8251 + switch (segment->format_char[0])
8252 + {
8253 + case 'a': /* atime in `ctime' format. */
8254 + /* UNTRUSTED, probably unexploitable */
8255 + checked_fprintf (dest, segment->text, ctime_format (get_stat_atime(stat_buf)));
8256 + break;
8257 + case 'b': /* size in 512-byte blocks */
8258 + /* UNTRUSTED, probably unexploitable */
8259 + checked_fprintf (dest, segment->text,
8260 + human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
8261 + hbuf, human_ceiling,
8262 + ST_NBLOCKSIZE, 512));
8263 + break;
8264 + case 'c': /* ctime in `ctime' format */
8265 + /* UNTRUSTED, probably unexploitable */
8266 + checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime(stat_buf)));
8267 + break;
8268 + case 'd': /* depth in search tree */
8269 + /* UNTRUSTED, probably unexploitable */
8270 + checked_fprintf (dest, segment->text, state.curdepth);
8271 + break;
8272 + case 'D': /* Device on which file exists (stat.st_dev) */
8273 + /* trusted */
8274 + checked_fprintf (dest, segment->text,
8275 + human_readable ((uintmax_t) stat_buf->st_dev, hbuf,
8276 + human_ceiling, 1, 1));
8277 + break;
8278 + case 'f': /* base name of path */
8279 + /* sanitised */
8280 + {
8281 + char *base = base_name (pathname);
8282 + checked_print_quoted (dest, segment->text, base);
8283 + free (base);
8284 + }
8285 + break;
8286 + case 'F': /* file system type */
8287 + /* trusted */
8288 + checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname));
8289 + break;
8290 + case 'g': /* group name */
8291 + /* trusted */
8292 + /* (well, the actual group is selected by the user but
8293 + * its name was selected by the system administrator)
8294 + */
8295 + {
8296 + struct group *g;
8297 +
8298 + g = getgrgid (stat_buf->st_gid);
8299 + if (g)
8300 + {
8301 + segment->text[segment->text_len] = 's';
8302 + checked_fprintf (dest, segment->text, g->gr_name);
8303 + break;
8304 + }
8305 + else
8306 + {
8307 + /* Do nothing. */
8308 + /*FALLTHROUGH*/
8309 + }
8310 + }
8311 + /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/
8312 +
8313 + case 'G': /* GID number */
8314 + /* UNTRUSTED, probably unexploitable */
8315 + checked_fprintf (dest, segment->text,
8316 + human_readable ((uintmax_t) stat_buf->st_gid, hbuf,
8317 + human_ceiling, 1, 1));
8318 + break;
8319 + case 'h': /* leading directories part of path */
8320 + /* sanitised */
8321 + {
8322 + cp = strrchr (pathname, '/');
8323 + if (cp == NULL) /* No leading directories. */
8324 + {
8325 + /* If there is no slash in the pathname, we still
8326 + * print the string because it contains characters
8327 + * other than just '%s'. The %h expands to ".".
8328 + */
8329 + checked_print_quoted (dest, segment->text, ".");
8330 + }
8331 + else
8332 + {
8333 + char *s = strdup(pathname);
8334 + s[cp - pathname] = 0;
8335 + checked_print_quoted (dest, segment->text, s);
8336 + free(s);
8337 + }
8338 + }
8339 + break;
8340 +
8341 + case 'H': /* ARGV element file was found under */
8342 + /* trusted */
8343 + {
8344 + char *s = xmalloc(state.starting_path_length+1);
8345 + memcpy(s, pathname, state.starting_path_length);
8346 + s[state.starting_path_length] = 0;
8347 + checked_fprintf (dest, segment->text, s);
8348 + free(s);
8349 + }
8350 + break;
8351 +
8352 + case 'i': /* inode number */
8353 + /* UNTRUSTED, but not exploitable I think */
8354 + checked_fprintf (dest, segment->text,
8355 + human_readable ((uintmax_t) stat_buf->st_ino, hbuf,
8356 + human_ceiling,
8357 + 1, 1));
8358 + break;
8359 + case 'k': /* size in 1K blocks */
8360 + /* UNTRUSTED, but not exploitable I think */
8361 + checked_fprintf (dest, segment->text,
8362 + human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf),
8363 + hbuf, human_ceiling,
8364 + ST_NBLOCKSIZE, 1024));
8365 + break;
8366 + case 'l': /* object of symlink */
8367 + /* sanitised */
8368 +#ifdef S_ISLNK
8369 + {
8370 + char *linkname = 0;
8371 +
8372 + if (S_ISLNK (stat_buf->st_mode))
8373 + {
8374 + linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname);
8375 + if (linkname == 0)
8376 + state.exit_status = 1;
8377 + }
8378 + if (linkname)
8379 + {
8380 + checked_print_quoted (dest, segment->text, linkname);
8381 + free (linkname);
8382 + }
8383 + else
8384 + {
8385 + /* We still need to honour the field width etc., so this is
8386 + * not a no-op.
8387 + */
8388 + checked_print_quoted (dest, segment->text, "");
8389 + }
8390 + }
8391 +#endif /* S_ISLNK */
8392 + break;
8393 +
8394 + case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */
8395 + /* UNTRUSTED, probably unexploitable */
8396 + {
8397 + char modestring[16] ;
8398 + filemodestring (stat_buf, modestring);
8399 + modestring[10] = '\0';
8400 + checked_fprintf (dest, segment->text, modestring);
8401 + }
8402 + break;
8403 +
8404 + case 'm': /* mode as octal number (perms only) */
8405 + /* UNTRUSTED, probably unexploitable */
8406 + {
8407 + /* Output the mode portably using the traditional numbers,
8408 + even if the host unwisely uses some other numbering
8409 + scheme. But help the compiler in the common case where
8410 + the host uses the traditional numbering scheme. */
8411 + mode_t m = stat_buf->st_mode;
8412 + boolean traditional_numbering_scheme =
8413 + (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000
8414 + && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100
8415 + && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010
8416 + && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001);
8417 + checked_fprintf (dest, segment->text,
8418 + (traditional_numbering_scheme
8419 + ? m & MODE_ALL
8420 + : ((m & S_ISUID ? 04000 : 0)
8421 + | (m & S_ISGID ? 02000 : 0)
8422 + | (m & S_ISVTX ? 01000 : 0)
8423 + | (m & S_IRUSR ? 00400 : 0)
8424 + | (m & S_IWUSR ? 00200 : 0)
8425 + | (m & S_IXUSR ? 00100 : 0)
8426 + | (m & S_IRGRP ? 00040 : 0)
8427 + | (m & S_IWGRP ? 00020 : 0)
8428 + | (m & S_IXGRP ? 00010 : 0)
8429 + | (m & S_IROTH ? 00004 : 0)
8430 + | (m & S_IWOTH ? 00002 : 0)
8431 + | (m & S_IXOTH ? 00001 : 0))));
8432 + }
8433 + break;
8434 +
8435 + case 'n': /* number of links */
8436 + /* UNTRUSTED, probably unexploitable */
8437 + checked_fprintf (dest, segment->text,
8438 + human_readable ((uintmax_t) stat_buf->st_nlink,
8439 + hbuf,
8440 + human_ceiling,
8441 + 1, 1));
8442 + break;
8443 +
8444 + case 'p': /* pathname */
8445 + /* sanitised */
8446 + checked_print_quoted (dest, segment->text, pathname);
8447 + break;
8448 +
8449 + case 'P': /* pathname with ARGV element stripped */
8450 + /* sanitised */
8451 + if (state.curdepth > 0)
8452 + {
8453 + cp = pathname + state.starting_path_length;
8454 + if (*cp == '/')
8455 + /* Move past the slash between the ARGV element
8456 + and the rest of the pathname. But if the ARGV element
8457 + ends in a slash, we didn't add another, so we've
8458 + already skipped past it. */
8459 + cp++;
8460 + }
8461 + else
8462 + {
8463 + cp = "";
8464 + }
8465 + checked_print_quoted (dest, segment->text, cp);
8466 + break;
8467 +
8468 + case 's': /* size in bytes */
8469 + /* UNTRUSTED, probably unexploitable */
8470 + checked_fprintf (dest, segment->text,
8471 + human_readable ((uintmax_t) stat_buf->st_size,
8472 + hbuf, human_ceiling, 1, 1));
8473 + break;
8474 +
8475 + case 'S': /* sparseness */
8476 + /* UNTRUSTED, probably unexploitable */
8477 + checked_fprintf (dest, segment->text, file_sparseness(stat_buf));;
8478 + break;
8479 +
8480 + case 't': /* mtime in `ctime' format */
8481 + /* UNTRUSTED, probably unexploitable */
8482 + checked_fprintf (dest, segment->text,
8483 + ctime_format (get_stat_mtime(stat_buf)));
8484 + break;
8485 +
8486 + case 'u': /* user name */
8487 + /* trusted */
8488 + /* (well, the actual user is selected by the user on systems
8489 + * where chown is not restricted, but the user name was
8490 + * selected by the system administrator)
8491 + */
8492 + {
8493 + struct passwd *p;
8494 +
8495 + p = getpwuid (stat_buf->st_uid);
8496 + if (p)
8497 + {
8498 + segment->text[segment->text_len] = 's';
8499 + checked_fprintf (dest, segment->text, p->pw_name);
8500 + break;
8501 + }
8502 + /* else fallthru */
8503 + }
8504 + /* FALLTHROUGH*/ /* .. to case U */
8505 +
8506 + case 'U': /* UID number */
8507 + /* UNTRUSTED, probably unexploitable */
8508 + checked_fprintf (dest, segment->text,
8509 + human_readable ((uintmax_t) stat_buf->st_uid, hbuf,
8510 + human_ceiling, 1, 1));
8511 + break;
8512 +
8513 + /* %Y: type of file system entry like `ls -l`:
8514 + * (d,-,l,s,p,b,c,n) n=nonexistent(symlink)
8515 + */
8516 + case 'Y': /* in case of symlink */
8517 + /* trusted */
8518 + {
8519 +#ifdef S_ISLNK
8520 + if (S_ISLNK (stat_buf->st_mode))
8521 + {
8522 + struct stat sbuf;
8523 + /* If we would normally follow links, do not do so.
8524 + * If we would normally not follow links, do so.
8525 + */
8526 + if ((following_links() ? lstat : stat)
8527 + (state.rel_pathname, &sbuf) != 0)
8528 + {
8529 + if ( errno == ENOENT )
8530 + {
8531 + checked_fprintf (dest, segment->text, "N");
8532 + break;
8533 + }
8534 + else if ( errno == ELOOP )
8535 + {
8536 + checked_fprintf (dest, segment->text, "L");
8537 + break;
8538 + }
8539 + else
8540 + {
8541 + checked_fprintf (dest, segment->text, "?");
8542 + error (0, errno, "%s",
8543 + safely_quote_err_filename(0, pathname));
8544 + /* exit_status = 1;
8545 + return ; */
8546 + break;
8547 + }
8548 + }
8549 + checked_fprintf (dest, segment->text,
8550 + mode_to_filetype(sbuf.st_mode & S_IFMT));
8551 + }
8552 +#endif /* S_ISLNK */
8553 + else
8554 + {
8555 + checked_fprintf (dest, segment->text,
8556 + mode_to_filetype(stat_buf->st_mode & S_IFMT));
8557 + }
8558 + }
8559 + break;
8560 +
8561 + case 'y':
8562 + /* trusted */
8563 + {
8564 + checked_fprintf (dest, segment->text,
8565 + mode_to_filetype(stat_buf->st_mode & S_IFMT));
8566 + }
8567 + break;
8568 + }
8569 + /* end of KIND_FORMAT case */
8570 + break;
8571 + }
8572 +}
8573 +
8574 +boolean
8575 +pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8576 +{
8577 + struct format_val *dest = &pred_ptr->args.printf_vec;
8578 + struct segment *segment;
8579 +
8580 + for (segment = dest->segment; segment; segment = segment->next)
8581 + {
8582 + if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */
8583 + {
8584 + struct timespec ts;
8585 + int valid = 0;
8586 +
8587 + switch (segment->format_char[0])
8588 + {
8589 + case 'A':
8590 + ts = get_stat_atime(stat_buf);
8591 + valid = 1;
8592 + break;
8593 + case 'B':
8594 + ts = get_stat_birthtime(stat_buf);
8595 + if ('@' == segment->format_char[1])
8596 + valid = 1;
8597 + else
8598 + valid = (ts.tv_nsec >= 0);
8599 + break;
8600 + case 'C':
8601 + ts = get_stat_ctime(stat_buf);
8602 + valid = 1;
8603 + break;
8604 + case 'T':
8605 + ts = get_stat_mtime(stat_buf);
8606 + valid = 1;
8607 + break;
8608 + default:
8609 + assert (0);
8610 + abort ();
8611 + }
8612 + /* We trust the output of format_date not to contain
8613 + * nasty characters, though the value of the date
8614 + * is itself untrusted data.
8615 + */
8616 + if (valid)
8617 + {
8618 + /* trusted */
8619 + checked_fprintf (dest, segment->text,
8620 + format_date (ts, segment->format_char[1]));
8621 + }
8622 + else
8623 + {
8624 + /* The specified timestamp is not available, output
8625 + * nothing for the timestamp, but use the rest (so that
8626 + * for example find foo -printf '[%Bs] %p\n' can print
8627 + * "[] foo").
8628 + */
8629 + /* trusted */
8630 + checked_fprintf (dest, segment->text, "");
8631 + }
8632 + }
8633 + else
8634 + {
8635 + /* Print a segment which is not a date. */
8636 + do_fprintf(dest, segment, pathname, stat_buf);
8637 + }
8638 + }
8639 + return true;
8640 +}
8641 +
8642 +boolean
8643 +pred_fstype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8644 +{
8645 + (void) pathname;
8646 +
8647 + if (strcmp (filesystem_type (stat_buf, pathname), pred_ptr->args.str) == 0)
8648 + return true;
8649 + else
8650 + return false;
8651 +}
8652 +
8653 +boolean
8654 +pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8655 +{
8656 + (void) pathname;
8657 +
8658 + switch (pred_ptr->args.numinfo.kind)
8659 + {
8660 + case COMP_GT:
8661 + if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val)
8662 + return (true);
8663 + break;
8664 + case COMP_LT:
8665 + if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val)
8666 + return (true);
8667 + break;
8668 + case COMP_EQ:
8669 + if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val)
8670 + return (true);
8671 + break;
8672 + }
8673 + return (false);
8674 +}
8675 +
8676 +boolean
8677 +pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8678 +{
8679 + (void) pathname;
8680 +
8681 + if (pred_ptr->args.gid == stat_buf->st_gid)
8682 + return (true);
8683 + else
8684 + return (false);
8685 +}
8686 +
8687 +boolean
8688 +pred_ilname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8689 +{
8690 + return match_lname (pathname, stat_buf, pred_ptr, true);
8691 +}
8692 +
8693 +/* Common code between -name, -iname. PATHNAME is being visited, STR
8694 + is name to compare basename against, and FLAGS are passed to
8695 + fnmatch. Recall that 'find / -name /' is one of the few times where a '/'
8696 + in the -name must actually find something. */
8697 +static boolean
8698 +pred_name_common (const char *pathname, const char *str, int flags)
8699 +{
8700 + char *p;
8701 + boolean b;
8702 + /* We used to use last_component() here, but that would not allow us to modify the
8703 + * input string, which is const. We could optimise by duplicating the string only
8704 + * if we need to modify it, and I'll do that if there is a measurable
8705 + * performance difference on a machine built after 1990...
8706 + */
8707 + char *base = base_name (pathname);
8708 + /* remove trailing slashes, but leave "/" or "//foo" unchanged. */
8709 + strip_trailing_slashes(base);
8710 +
8711 + /* FNM_PERIOD is not used here because POSIX requires that it not be.
8712 + * See http://standards.ieee.org/reading/ieee/interp/1003-2-92_int/pasc-1003.2-126.html
8713 + */
8714 + b = fnmatch (str, base, flags) == 0;
8715 + free (base);
8716 + return b;
8717 +}
8718 +
8719 +boolean
8720 +pred_iname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8721 +{
8722 + (void) stat_buf;
8723 + return pred_name_common (pathname, pred_ptr->args.str, FNM_CASEFOLD);
8724 +}
8725 +
8726 +boolean
8727 +pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8728 +{
8729 + (void) pathname;
8730 +
8731 + switch (pred_ptr->args.numinfo.kind)
8732 + {
8733 + case COMP_GT:
8734 + if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val)
8735 + return (true);
8736 + break;
8737 + case COMP_LT:
8738 + if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val)
8739 + return (true);
8740 + break;
8741 + case COMP_EQ:
8742 + if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val)
8743 + return (true);
8744 + break;
8745 + }
8746 + return (false);
8747 +}
8748 +
8749 +boolean
8750 +pred_ipath (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8751 +{
8752 + (void) stat_buf;
8753 +
8754 + if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0)
8755 + return (true);
8756 + return (false);
8757 +}
8758 +
8759 +boolean
8760 +pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8761 +{
8762 + (void) pathname;
8763 +
8764 + switch (pred_ptr->args.numinfo.kind)
8765 + {
8766 + case COMP_GT:
8767 + if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val)
8768 + return (true);
8769 + break;
8770 + case COMP_LT:
8771 + if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val)
8772 + return (true);
8773 + break;
8774 + case COMP_EQ:
8775 + if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val)
8776 + return (true);
8777 + break;
8778 + }
8779 + return (false);
8780 +}
8781 +
8782 +boolean
8783 +pred_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8784 +{
8785 + return match_lname (pathname, stat_buf, pred_ptr, false);
8786 +}
8787 +
8788 +static boolean
8789 +match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)
8790 +{
8791 + boolean ret = false;
8792 +#ifdef S_ISLNK
8793 + if (S_ISLNK (stat_buf->st_mode))
8794 + {
8795 + char *linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname);
8796 + if (linkname)
8797 + {
8798 + if (fnmatch (pred_ptr->args.str, linkname,
8799 + ignore_case ? FNM_CASEFOLD : 0) == 0)
8800 + ret = true;
8801 + free (linkname);
8802 + }
8803 + }
8804 +#endif /* S_ISLNK */
8805 + return ret;
8806 +}
8807 +
8808 +boolean
8809 +pred_ls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8810 +{
8811 + return pred_fls(pathname, stat_buf, pred_ptr);
8812 +}
8813 +
8814 +boolean
8815 +pred_mmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8816 +{
8817 + (void) &pathname;
8818 + return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60);
8819 +}
8820 +
8821 +boolean
8822 +pred_mtime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8823 +{
8824 + (void) pathname;
8825 + return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS);
8826 +}
8827 +
8828 +boolean
8829 +pred_name (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8830 +{
8831 + (void) stat_buf;
8832 + return pred_name_common (pathname, pred_ptr->args.str, 0);
8833 +}
8834 +
8835 +boolean
8836 +pred_negate (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8837 +{
8838 + return !apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
8839 +}
8840 +
8841 +boolean
8842 +pred_newer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8843 +{
8844 + (void) pathname;
8845 +
8846 + assert (COMP_GT == pred_ptr->args.reftime.kind);
8847 + return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0;
8848 +}
8849 +
8850 +boolean
8851 +pred_newerXY (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8852 +{
8853 + struct timespec ts;
8854 + boolean collected = false;
8855 +
8856 + assert (COMP_GT == pred_ptr->args.reftime.kind);
8857 +
8858 + switch (pred_ptr->args.reftime.xval)
8859 + {
8860 + case XVAL_TIME:
8861 + assert (pred_ptr->args.reftime.xval != XVAL_TIME);
8862 + return false;
8863 +
8864 + case XVAL_ATIME:
8865 + ts = get_stat_atime(stat_buf);
8866 + collected = true;
8867 + break;
8868 +
8869 + case XVAL_BIRTHTIME:
8870 + ts = get_stat_birthtime(stat_buf);
8871 + collected = true;
8872 + if (ts.tv_nsec < 0);
8873 + {
8874 + /* XXX: Cannot determine birth time. Warn once. */
8875 + error(0, 0, _("Warning: cannot determine birth time of file %s"),
8876 + safely_quote_err_filename(0, pathname));
8877 + return false;
8878 + }
8879 + break;
8880 +
8881 + case XVAL_CTIME:
8882 + ts = get_stat_ctime(stat_buf);
8883 + collected = true;
8884 + break;
8885 +
8886 + case XVAL_MTIME:
8887 + ts = get_stat_mtime(stat_buf);
8888 + collected = true;
8889 + break;
8890 + }
8891 +
8892 + assert (collected);
8893 + return compare_ts(ts, pred_ptr->args.reftime.ts) > 0;
8894 +}
8895 +
8896 +boolean
8897 +pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8898 +{
8899 + (void) pathname;
8900 + (void) pred_ptr;
8901 +
8902 +#ifdef CACHE_IDS
8903 + extern char *gid_unused;
8904 +
8905 + return gid_unused[(unsigned) stat_buf->st_gid];
8906 +#else
8907 + return getgrgid (stat_buf->st_gid) == NULL;
8908 +#endif
8909 +}
8910 +
8911 +boolean
8912 +pred_nouser (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8913 +{
8914 +#ifdef CACHE_IDS
8915 + extern char *uid_unused;
8916 +#endif
8917 +
8918 + (void) pathname;
8919 + (void) pred_ptr;
8920 +
8921 +#ifdef CACHE_IDS
8922 + return uid_unused[(unsigned) stat_buf->st_uid];
8923 +#else
8924 + return getpwuid (stat_buf->st_uid) == NULL;
8925 +#endif
8926 +}
8927 +
8928 +
8929 +static boolean
8930 +is_ok(const char *program, const char *arg)
8931 +{
8932 + fflush (stdout);
8933 + /* The draft open standard requires that, in the POSIX locale,
8934 + the last non-blank character of this prompt be '?'.
8935 + The exact format is not specified.
8936 + This standard does not have requirements for locales other than POSIX
8937 + */
8938 + /* XXX: printing UNTRUSTED data here. */
8939 + fprintf (stderr, _("< %s ... %s > ? "
8940 + /* TRANSLATORS: we would like, if possible, the final non-blank
8941 + * character of this string to be '?', but it is not
8942 + * wholly essential. */
8943 + ), program, arg);
8944 + fflush (stderr);
8945 + return yesno();
8946 +}
8947 +
8948 +boolean
8949 +pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8950 +{
8951 + if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname))
8952 + return new_impl_pred_exec (get_start_dirfd(),
8953 + pathname, stat_buf, pred_ptr, NULL, 0);
8954 + else
8955 + return false;
8956 +}
8957 +
8958 +boolean
8959 +pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8960 +{
8961 + const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./";
8962 + if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname))
8963 + return new_impl_pred_exec (get_current_dirfd(),
8964 + state.rel_pathname, stat_buf, pred_ptr,
8965 + prefix, (prefix ? 2 : 0));
8966 + else
8967 + return false;
8968 +}
8969 +
8970 +boolean
8971 +pred_openparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8972 +{
8973 + (void) pathname;
8974 + (void) stat_buf;
8975 + (void) pred_ptr;
8976 + return true;
8977 +}
8978 +
8979 +boolean
8980 +pred_or (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8981 +{
8982 + if (pred_ptr->pred_left == NULL
8983 + || !apply_predicate(pathname, stat_buf, pred_ptr->pred_left))
8984 + {
8985 + return apply_predicate(pathname, stat_buf, pred_ptr->pred_right);
8986 + }
8987 + else
8988 + return true;
8989 +}
8990 +
8991 +boolean
8992 +pred_path (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
8993 +{
8994 + (void) stat_buf;
8995 + if (fnmatch (pred_ptr->args.str, pathname, 0) == 0)
8996 + return (true);
8997 + return (false);
8998 +}
8999 +
9000 +boolean
9001 +pred_perm (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9002 +{
9003 + mode_t mode = stat_buf->st_mode;
9004 + mode_t perm_val = pred_ptr->args.perm.val[S_ISDIR (mode) != 0];
9005 + (void) pathname;
9006 + switch (pred_ptr->args.perm.kind)
9007 + {
9008 + case PERM_AT_LEAST:
9009 + return (mode & perm_val) == perm_val;
9010 + break;
9011 +
9012 + case PERM_ANY:
9013 + /* True if any of the bits set in the mask are also set in the file's mode.
9014 + *
9015 + *
9016 + * Otherwise, if onum is prefixed by a hyphen, the primary shall
9017 + * evaluate as true if at least all of the bits specified in
9018 + * onum that are also set in the octal mask 07777 are set.
9019 + *
9020 + * Eric Blake's interpretation is that the mode argument is zero,
9021 +
9022 + */
9023 + if (0 == perm_val)
9024 + return true; /* Savannah bug 14748; we used to return false */
9025 + else
9026 + return (mode & perm_val) != 0;
9027 + break;
9028 +
9029 + case PERM_EXACT:
9030 + return (mode & MODE_ALL) == perm_val;
9031 + break;
9032 +
9033 + default:
9034 + abort ();
9035 + break;
9036 + }
9037 +}
9038 +
9039 +
9040 +struct access_check_args
9041 +{
9042 + const char *filename;
9043 + int access_type;
9044 + int cb_errno;
9045 +};
9046 +
9047 +
9048 +static int
9049 +access_callback(void *context)
9050 +{
9051 + int rv;
9052 + struct access_check_args *args = context;
9053 + if ((rv = access(args->filename, args->access_type)) < 0)
9054 + args->cb_errno = errno;
9055 + return rv;
9056 +}
9057 +
9058 +static int
9059 +can_access(int access_type)
9060 +{
9061 + struct access_check_args args;
9062 + args.filename = state.rel_pathname;
9063 + args.access_type = access_type;
9064 + args.cb_errno = 0;
9065 + return 0 == run_in_dir(state.cwd_dir_fd, access_callback, &args);
9066 +}
9067 +
9068 +
9069 +boolean
9070 +pred_executable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9071 +{
9072 + (void) pathname;
9073 + (void) stat_buf;
9074 + (void) pred_ptr;
9075 +
9076 + return can_access(X_OK);
9077 +}
9078 +
9079 +boolean
9080 +pred_readable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9081 +{
9082 + (void) pathname;
9083 + (void) stat_buf;
9084 + (void) pred_ptr;
9085 +
9086 + return can_access(R_OK);
9087 +}
9088 +
9089 +boolean
9090 +pred_writable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9091 +{
9092 + (void) pathname;
9093 + (void) stat_buf;
9094 + (void) pred_ptr;
9095 +
9096 + return can_access(W_OK);
9097 +}
9098 +
9099 +boolean
9100 +pred_print (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9101 +{
9102 + (void) stat_buf;
9103 + (void) pred_ptr;
9104 +
9105 + print_quoted(pred_ptr->args.printf_vec.stream,
9106 + pred_ptr->args.printf_vec.quote_opts,
9107 + pred_ptr->args.printf_vec.dest_is_tty,
9108 + "%s\n", pathname);
9109 + return true;
9110 +}
9111 +
9112 +boolean
9113 +pred_print0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9114 +{
9115 + return pred_fprint0(pathname, stat_buf, pred_ptr);
9116 +}
9117 +
9118 +boolean
9119 +pred_prune (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9120 +{
9121 + (void) pathname;
9122 + (void) pred_ptr;
9123 +
9124 + if (options.do_dir_first == true && /* no effect with -depth */
9125 + stat_buf != NULL &&
9126 + S_ISDIR(stat_buf->st_mode))
9127 + state.stop_at_current_level = true;
9128 +
9129 + /* findutils used to return options.do_dir_first here, so that -prune
9130 + * returns true only if -depth is not in effect. But POSIX requires
9131 + * that -prune always evaluate as true.
9132 + */
9133 + return true;
9134 +}
9135 +
9136 +boolean
9137 +pred_quit (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9138 +{
9139 + (void) pathname;
9140 + (void) stat_buf;
9141 + (void) pred_ptr;
9142 +
9143 + /* Run any cleanups. This includes executing any command lines
9144 + * we have partly built but not executed.
9145 + */
9146 + cleanup();
9147 +
9148 + /* Since -exec and friends don't leave child processes running in the
9149 + * background, there is no need to wait for them here.
9150 + */
9151 + exit(state.exit_status); /* 0 for success, etc. */
9152 +}
9153 +
9154 +boolean
9155 +pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9156 +{
9157 + int len = strlen (pathname);
9158 +(void) stat_buf;
9159 + if (re_match (pred_ptr->args.regex, pathname, len, 0,
9160 + (struct re_registers *) NULL) == len)
9161 + return (true);
9162 + return (false);
9163 +}
9164 +
9165 +boolean
9166 +pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9167 +{
9168 + uintmax_t f_val;
9169 +
9170 + (void) pathname;
9171 + f_val = ((stat_buf->st_size / pred_ptr->args.size.blocksize)
9172 + + (stat_buf->st_size % pred_ptr->args.size.blocksize != 0));
9173 + switch (pred_ptr->args.size.kind)
9174 + {
9175 + case COMP_GT:
9176 + if (f_val > pred_ptr->args.size.size)
9177 + return (true);
9178 + break;
9179 + case COMP_LT:
9180 + if (f_val < pred_ptr->args.size.size)
9181 + return (true);
9182 + break;
9183 + case COMP_EQ:
9184 + if (f_val == pred_ptr->args.size.size)
9185 + return (true);
9186 + break;
9187 + }
9188 + return (false);
9189 +}
9190 +
9191 +boolean
9192 +pred_samefile (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9193 +{
9194 + /* Potential optimisation: because of the loop protection, we always
9195 + * know the device of the current directory, hence the device number
9196 + * of the file we're currently considering. If -L is not in effect,
9197 + * and the device number of the file we're looking for is not the
9198 + * same as the device number of the current directory, this
9199 + * predicate cannot return true. Hence there would be no need to
9200 + * stat the file we're looking at.
9201 + */
9202 + (void) pathname;
9203 +
9204 + /* We will often still have an fd open on the file under consideration,
9205 + * but that's just to ensure inode number stability by maintaining
9206 + * a reference to it; we don't need the file for anything else.
9207 + */
9208 + return stat_buf->st_ino == pred_ptr->args.samefileid.ino
9209 + && stat_buf->st_dev == pred_ptr->args.samefileid.dev;
9210 +}
9211 +
9212 +boolean
9213 +pred_true (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9214 +{
9215 + (void) pathname;
9216 + (void) stat_buf;
9217 + (void) pred_ptr;
9218 + return true;
9219 +}
9220 +
9221 +boolean
9222 +pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9223 +{
9224 + mode_t mode;
9225 + mode_t type = pred_ptr->args.type;
9226 +
9227 + assert (state.have_type);
9228 +
9229 + if (0 == state.type)
9230 + {
9231 + /* This can sometimes happen with broken NFS servers.
9232 + * See Savannah bug #16378.
9233 + */
9234 + return false;
9235 + }
9236 +
9237 + (void) pathname;
9238 +
9239 + if (state.have_stat)
9240 + mode = stat_buf->st_mode;
9241 + else
9242 + mode = state.type;
9243 +
9244 +#ifndef S_IFMT
9245 + /* POSIX system; check `mode' the slow way. */
9246 + if ((S_ISBLK (mode) && type == S_IFBLK)
9247 + || (S_ISCHR (mode) && type == S_IFCHR)
9248 + || (S_ISDIR (mode) && type == S_IFDIR)
9249 + || (S_ISREG (mode) && type == S_IFREG)
9250 +#ifdef S_IFLNK
9251 + || (S_ISLNK (mode) && type == S_IFLNK)
9252 +#endif
9253 +#ifdef S_IFIFO
9254 + || (S_ISFIFO (mode) && type == S_IFIFO)
9255 +#endif
9256 +#ifdef S_IFSOCK
9257 + || (S_ISSOCK (mode) && type == S_IFSOCK)
9258 +#endif
9259 +#ifdef S_IFDOOR
9260 + || (S_ISDOOR (mode) && type == S_IFDOOR)
9261 +#endif
9262 + )
9263 +#else /* S_IFMT */
9264 + /* Unix system; check `mode' the fast way. */
9265 + if ((mode & S_IFMT) == type)
9266 +#endif /* S_IFMT */
9267 + return (true);
9268 + else
9269 + return (false);
9270 +}
9271 +
9272 +boolean
9273 +pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9274 +{
9275 + (void) pathname;
9276 + switch (pred_ptr->args.numinfo.kind)
9277 + {
9278 + case COMP_GT:
9279 + if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val)
9280 + return (true);
9281 + break;
9282 + case COMP_LT:
9283 + if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val)
9284 + return (true);
9285 + break;
9286 + case COMP_EQ:
9287 + if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val)
9288 + return (true);
9289 + break;
9290 + }
9291 + return (false);
9292 +}
9293 +
9294 +boolean
9295 +pred_used (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9296 +{
9297 + struct timespec delta, at, ct;
9298 +
9299 + (void) pathname;
9300 +
9301 + /* TODO: this needs to be retested carefully (manually, if necessary) */
9302 + at = get_stat_atime(stat_buf);
9303 + ct = get_stat_ctime(stat_buf);
9304 + delta.tv_sec = at.tv_sec - ct.tv_sec;
9305 + delta.tv_nsec = at.tv_nsec - ct.tv_nsec;
9306 + if (delta.tv_nsec < 0)
9307 + {
9308 + delta.tv_nsec += 1000000000;
9309 + delta.tv_sec -= 1;
9310 + }
9311 + return pred_timewindow(delta, pred_ptr, DAYSECS);
9312 +}
9313 +
9314 +boolean
9315 +pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9316 +{
9317 + (void) pathname;
9318 + if (pred_ptr->args.uid == stat_buf->st_uid)
9319 + return (true);
9320 + else
9321 + return (false);
9322 +}
9323 +
9324 +boolean
9325 +pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
9326 +{
9327 + struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */
9328 + int (*ystat) (const char*, struct stat *p);
9329 +
9330 + /* If we would normally stat the link itself, stat the target instead.
9331 + * If we would normally follow the link, stat the link itself instead.
9332 + */
9333 + if (following_links())
9334 + ystat = optionp_stat;
9335 + else
9336 + ystat = optionl_stat;
9337 +
9338 + set_stat_placeholders(&sbuf);
9339 + if ((*ystat) (state.rel_pathname, &sbuf) != 0)
9340 + {
9341 + if (following_links() && errno == ENOENT)
9342 + {
9343 + /* If we failed to follow the symlink,
9344 + * fall back on looking at the symlink itself.
9345 + */
9346 + /* Mimic behavior of ls -lL. */
9347 + return (pred_type (pathname, stat_buf, pred_ptr));
9348 + }
9349 + else
9350 + {
9351 + error (0, errno, "%s", safely_quote_err_filename(0, pathname));
9352 + state.exit_status = 1;
9353 + }
9354 + return false;
9355 + }
9356 + /* Now that we have our stat() information, query it in the same
9357 + * way that -type does.
9358 + */
9359 + return (pred_type (pathname, &sbuf, pred_ptr));
9360 +}
9361 +
9362 +/* 1) fork to get a child; parent remembers the child pid
9363 + 2) child execs the command requested
9364 + 3) parent waits for child; checks for proper pid of child
9365 +
9366 + Possible returns:
9367 +
9368 + ret errno status(h) status(l)
9369 +
9370 + pid x signal# 0177 stopped
9371 + pid x exit arg 0 term by _exit
9372 + pid x 0 signal # term by signal
9373 + -1 EINTR parent got signal
9374 + -1 other some other kind of error
9375 +
9376 + Return true only if the pid matches, status(l) is
9377 + zero, and the exit arg (status high) is 0.
9378 + Otherwise return false, possibly printing an error message. */
9379 +
9380 +
9381 +static boolean
9382 +prep_child_for_exec (boolean close_stdin, int dirfd)
9383 +{
9384 + boolean ok = true;
9385 + if (close_stdin)
9386 + {
9387 + const char inputfile[] = "/dev/null";
9388 +
9389 + if (close(0) < 0)
9390 + {
9391 + error(0, errno, _("Cannot close standard input"));
9392 + ok = false;
9393 + }
9394 + else
9395 + {
9396 + if (open(inputfile, O_RDONLY
9397 +#if defined O_LARGEFILE
9398 + |O_LARGEFILE
9399 +#endif
9400 + ) < 0)
9401 + {
9402 + /* This is not entirely fatal, since
9403 + * executing the child with a closed
9404 + * stdin is almost as good as executing it
9405 + * with its stdin attached to /dev/null.
9406 + */
9407 + error (0, errno, "%s", safely_quote_err_filename(0, inputfile));
9408 + /* do not set ok=false, it is OK to continue anyway. */
9409 + }
9410 + }
9411 + }
9412 +
9413 + /* Even if DebugSearch is set, don't announce our change of
9414 + * directory, since we're not going to emit a subsequent
9415 + * announcement of a call to stat() anyway, as we're about to exec
9416 + * something.
9417 + */
9418 + if (dirfd != AT_FDCWD)
9419 + {
9420 + assert (dirfd >= 0);
9421 + if (0 != fchdir(dirfd))
9422 + {
9423 + /* If we cannot execute our command in the correct directory,
9424 + * we should not execute it at all.
9425 + */
9426 + error(0, errno, _("Failed to change directory"));
9427 + ok = false;
9428 + }
9429 + }
9430 + return ok;
9431 +}
9432 +
9433 +
9434 +
9435 +int
9436 +launch (const struct buildcmd_control *ctl,
9437 + struct buildcmd_state *buildstate)
9438 +{
9439 + int wait_status;
9440 + pid_t child_pid;
9441 + static int first_time = 1;
9442 + const struct exec_val *execp = buildstate->usercontext;
9443 +
9444 + if (!execp->use_current_dir)
9445 + {
9446 + assert (starting_desc >= 0);
9447 + assert (execp->dirfd == starting_desc);
9448 + }
9449 +
9450 +
9451 + /* Null terminate the arg list. */
9452 + bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false);
9453 +
9454 + /* Make sure output of command doesn't get mixed with find output. */
9455 + fflush (stdout);
9456 + fflush (stderr);
9457 +
9458 + /* Make sure to listen for the kids. */
9459 + if (first_time)
9460 + {
9461 + first_time = 0;
9462 + signal (SIGCHLD, SIG_DFL);
9463 + }
9464 +
9465 + child_pid = fork ();
9466 + if (child_pid == -1)
9467 + error (1, errno, _("cannot fork"));
9468 + if (child_pid == 0)
9469 + {
9470 + /* We are the child. */
9471 + assert (starting_desc >= 0);
9472 + if (!prep_child_for_exec(execp->close_stdin, execp->dirfd))
9473 + {
9474 + _exit(1);
9475 + }
9476 +
9477 + execvp (buildstate->cmd_argv[0], buildstate->cmd_argv);
9478 + error (0, errno, "%s",
9479 + safely_quote_err_filename(0, buildstate->cmd_argv[0]));
9480 + _exit (1);
9481 + }
9482 +
9483 +
9484 + /* In parent; set up for next time. */
9485 + bc_clear_args(ctl, buildstate);
9486 +
9487 +
9488 + while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1)
9489 + {
9490 + if (errno != EINTR)
9491 + {
9492 + error (0, errno, _("error waiting for %s"),
9493 + safely_quote_err_filename(0, buildstate->cmd_argv[0]));
9494 + state.exit_status = 1;
9495 + return 0; /* FAIL */
9496 + }
9497 + }
9498 +
9499 + if (WIFSIGNALED (wait_status))
9500 + {
9501 + error (0, 0, _("%1$s terminated by signal %2$d"),
9502 + quotearg_n_style(0, options.err_quoting_style,
9503 + buildstate->cmd_argv[0]),
9504 + WTERMSIG (wait_status));
9505 +
9506 + if (execp->multiple)
9507 + {
9508 + /* -exec \; just returns false if the invoked command fails.
9509 + * -exec {} + returns true if the invoked command fails, but
9510 + * sets the program exit status.
9511 + */
9512 + state.exit_status = 1;
9513 + }
9514 +
9515 + return 1; /* OK */
9516 + }
9517 +
9518 + if (0 == WEXITSTATUS (wait_status))
9519 + {
9520 + return 1; /* OK */
9521 + }
9522 + else
9523 + {
9524 + if (execp->multiple)
9525 + {
9526 + /* -exec \; just returns false if the invoked command fails.
9527 + * -exec {} + returns true if the invoked command fails, but
9528 + * sets the program exit status.
9529 + */
9530 + state.exit_status = 1;
9531 + }
9532 + return 0; /* FAIL */
9533 + }
9534 +
9535 +}
9536 +
9537 +
9538 +/* Return a static string formatting the time WHEN according to the
9539 + * strftime format character KIND.
9540 + *
9541 + * This function contains a number of assertions. These look like
9542 + * runtime checks of the results of computations, which would be a
9543 + * problem since external events should not be tested for with
9544 + * "assert" (instead you should use "if"). However, they are not
9545 + * really runtime checks. The assertions actually exist to verify
9546 + * that the various buffers are correctly sized.
9547 + */
9548 +static char *
9549 +format_date (struct timespec ts, int kind)
9550 +{
9551 + /* In theory, we use an extra 10 characters for 9 digits of
9552 + * nanoseconds and 1 for the decimal point. However, the real
9553 + * world is more complex than that.
9554 + *
9555 + * For example, some systems return junk in the tv_nsec part of
9556 + * st_birthtime. An example of this is the NetBSD-4.0-RELENG kernel
9557 + * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE
9558 + * runtime and examining files on an msdos filesytem. So for that
9559 + * reason we set NS_BUF_LEN to 32, which is simply "long enough" as
9560 + * opposed to "exactly the right size". Note that the behaviour of
9561 + * NetBSD appears to be a result of the use of uninitialised data,
9562 + * as it's not 100% reproducible (more like 25%).
9563 + */
9564 + enum {
9565 + NS_BUF_LEN = 32,
9566 + DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/
9567 + };
9568 + static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS,
9569 + MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))];
9570 + char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/
9571 + int charsprinted, need_ns_suffix;
9572 + struct tm *tm;
9573 + char fmt[6];
9574 +
9575 + /* human_readable() assumes we pass a buffer which is at least as
9576 + * long as LONGEST_HUMAN_READABLE. We use an assertion here to
9577 + * ensure that no nasty unsigned overflow happend in our calculation
9578 + * of the size of buf. Do the assertion here rather than in the
9579 + * code for %@ so that we find the problem quickly if it exists. If
9580 + * you want to submit a patch to move this into the if statement, go
9581 + * ahead, I'll apply it. But include performance timings
9582 + * demonstrating that the performance difference is actually
9583 + * measurable.
9584 + */
9585 + verify (sizeof(buf) >= LONGEST_HUMAN_READABLE);
9586 +
9587 + charsprinted = 0;
9588 + need_ns_suffix = 0;
9589 +
9590 + /* Format the main part of the time. */
9591 + if (kind == '+')
9592 + {
9593 + strcpy (fmt, "%F+%T");
9594 + need_ns_suffix = 1;
9595 + }
9596 + else
9597 + {
9598 + fmt[0] = '%';
9599 + fmt[1] = kind;
9600 + fmt[2] = '\0';
9601 +
9602 + /* %a, %c, and %t are handled in ctime_format() */
9603 + switch (kind)
9604 + {
9605 + case 'S':
9606 + case 'T':
9607 + case 'X':
9608 + case '@':
9609 + need_ns_suffix = 1;
9610 + break;
9611 + default:
9612 + need_ns_suffix = 0;
9613 + break;
9614 + }
9615 + }
9616 +
9617 + if (need_ns_suffix)
9618 + {
9619 + /* Format the nanoseconds part. Leave a trailing zero to
9620 + * discourage people from writing scripts which extract the
9621 + * fractional part of the timestamp by using column offsets.
9622 + * The reason for discouraging this is that in the future, the
9623 + * granularity may not be nanoseconds.
9624 + */
9625 + ns_buf[0] = 0;
9626 + charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec);
9627 + assert (charsprinted < NS_BUF_LEN);
9628 + }
9629 +
9630 + if (kind != '@'
9631 + && (tm = localtime (&ts.tv_sec))
9632 + && strftime (buf, sizeof buf, fmt, tm))
9633 + {
9634 + /* For %AS, %CS, %TS, add the fractional part of the seconds
9635 + * information.
9636 + */
9637 + if (need_ns_suffix)
9638 + {
9639 + assert ((sizeof buf - strlen(buf)) > strlen(ns_buf));
9640 + strcat(buf, ns_buf);
9641 + }
9642 + return buf;
9643 + }
9644 + else
9645 + {
9646 + uintmax_t w = ts.tv_sec;
9647 + size_t used, len, remaining;
9648 +
9649 + /* XXX: note that we are negating an unsigned type which is the
9650 + * widest possible unsigned type.
9651 + */
9652 + char *p = human_readable (ts.tv_sec < 0 ? -w : w, buf + 1,
9653 + human_ceiling, 1, 1);
9654 + assert (p > buf);
9655 + assert (p < (buf + (sizeof buf)));
9656 + if (ts.tv_sec < 0)
9657 + *--p = '-'; /* XXX: Ugh, relying on internal details of human_readable(). */
9658 +
9659 + /* Add the nanoseconds part. Because we cannot enforce a
9660 + * particlar implementation of human_readable, we cannot assume
9661 + * any particular value for (p-buf). So we need to be careful
9662 + * that there is enough space remaining in the buffer.
9663 + */
9664 + if (need_ns_suffix)
9665 + {
9666 + len = strlen(p);
9667 + used = (p-buf) + len; /* Offset into buf of current end */
9668 + assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */
9669 + remaining = sizeof buf - used - 1u; /* allow space for NUL */
9670 +
9671 + if (strlen(ns_buf) >= remaining)
9672 + {
9673 + error(0, 0,
9674 + "charsprinted=%ld but remaining=%lu: ns_buf=%s",
9675 + (long)charsprinted, (unsigned long)remaining, ns_buf);
9676 + }
9677 + assert (strlen(ns_buf) < remaining);
9678 + strcat(p, ns_buf);
9679 + }
9680 + return p;
9681 + }
9682 +}
9683 +
9684 +static const char *weekdays[] =
9685 + {
9686 + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
9687 + };
9688 +static char * months[] =
9689 + {
9690 + "Jan", "Feb", "Mar", "Apr", "May", "Jun",
9691 + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
9692 + };
9693 +
9694 +
9695 +static char *
9696 +ctime_format (struct timespec ts)
9697 +{
9698 + const struct tm * ptm;
9699 +#define TIME_BUF_LEN 1024u
9700 + static char resultbuf[TIME_BUF_LEN];
9701 + int nout;
9702 +
9703 + ptm = localtime(&ts.tv_sec);
9704 + if (ptm)
9705 + {
9706 + assert (ptm->tm_wday >= 0);
9707 + assert (ptm->tm_wday < 7);
9708 + assert (ptm->tm_mon >= 0);
9709 + assert (ptm->tm_mon < 12);
9710 + assert (ptm->tm_hour >= 0);
9711 + assert (ptm->tm_hour < 24);
9712 + assert (ptm->tm_min < 60);
9713 + assert (ptm->tm_sec <= 61); /* allows 2 leap seconds. */
9714 +
9715 + /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */
9716 + nout = snprintf(resultbuf, TIME_BUF_LEN,
9717 + "%3s %3s %2d %02d:%02d:%02d.%010ld %04d",
9718 + weekdays[ptm->tm_wday],
9719 + months[ptm->tm_mon],
9720 + ptm->tm_mday,
9721 + ptm->tm_hour,
9722 + ptm->tm_min,
9723 + ptm->tm_sec,
9724 + (long int)ts.tv_nsec,
9725 + 1900 + ptm->tm_year);
9726 +
9727 + assert (nout < TIME_BUF_LEN);
9728 + return resultbuf;
9729 + }
9730 + else
9731 + {
9732 + /* The time cannot be represented as a struct tm.
9733 + Output it as an integer. */
9734 + return format_date (ts, '@');
9735 + }
9736 +}
9737 +
9738 +/* Copy STR into BUF and trim blanks from the end of BUF.
9739 + Return BUF. */
9740 +
9741 +static char *
9742 +blank_rtrim (str, buf)
9743 + char *str;
9744 + char *buf;
9745 +{
9746 + int i;
9747 +
9748 + if (str == NULL)
9749 + return (NULL);
9750 + strcpy (buf, str);
9751 + i = strlen (buf) - 1;
9752 + while ((i >= 0) && ((buf[i] == ' ') || buf[i] == '\t'))
9753 + i--;
9754 + buf[++i] = '\0';
9755 + return (buf);
9756 +}
9757 +
9758 +/* Print out the predicate list starting at NODE. */
9759 +void
9760 +print_list (FILE *fp, struct predicate *node)
9761 +{
9762 + struct predicate *cur;
9763 + char name[256];
9764 +
9765 + cur = node;
9766 + while (cur != NULL)
9767 + {
9768 + fprintf (fp, "[%s] ", blank_rtrim (cur->p_name, name));
9769 + cur = cur->pred_next;
9770 + }
9771 + fprintf (fp, "\n");
9772 +}
9773 +
9774 +/* Print out the predicate list starting at NODE. */
9775 +static void
9776 +print_parenthesised(FILE *fp, struct predicate *node)
9777 +{
9778 + int parens = 0;
9779 +
9780 + if (node)
9781 + {
9782 + if ((pred_is(node, pred_or) || pred_is(node, pred_and))
9783 + && node->pred_left == NULL)
9784 + {
9785 + /* We print "<nothing> or X" as just "X"
9786 + * We print "<nothing> and X" as just "X"
9787 + */
9788 + print_parenthesised(fp, node->pred_right);
9789 + }
9790 + else
9791 + {
9792 + if (node->pred_left || node->pred_right)
9793 + parens = 1;
9794 +
9795 + if (parens)
9796 + fprintf(fp, "%s", " ( ");
9797 + print_optlist(fp, node);
9798 + if (parens)
9799 + fprintf(fp, "%s", " ) ");
9800 + }
9801 + }
9802 +}
9803 +
9804 +void
9805 +print_optlist (FILE *fp, const struct predicate *p)
9806 +{
9807 + if (p)
9808 + {
9809 + print_parenthesised(fp, p->pred_left);
9810 + fprintf (fp,
9811 + "%s%s",
9812 + p->need_stat ? "[call stat] " : "",
9813 + p->need_type ? "[need type] " : "");
9814 + print_predicate(fp, p);
9815 + fprintf(fp, " [%g] ", p->est_success_rate);
9816 + if (options.debug_options & DebugSuccessRates)
9817 + {
9818 + fprintf(fp, "[%ld/%ld", p->perf.successes, p->perf.visits);
9819 + if (p->perf.visits)
9820 + {
9821 + double real_rate = (double)p->perf.successes / (double)p->perf.visits;
9822 + fprintf(fp, "=%g] ", real_rate);
9823 + }
9824 + else
9825 + {
9826 + fprintf(fp, "=_] ");
9827 + }
9828 + }
9829 + print_parenthesised(fp, p->pred_right);
9830 + }
9831 +}
9832 +
9833 +void show_success_rates(const struct predicate *p)
9834 +{
9835 + if (options.debug_options & DebugSuccessRates)
9836 + {
9837 + fprintf(stderr, "Predicate success rates after completion:\n");
9838 + print_optlist(stderr, p);
9839 + fprintf(stderr, "\n");
9840 + }
9841 +}
9842 +
9843 +
9844 +
9845 +
9846 +#ifdef _NDEBUG
9847 +/* If _NDEBUG is defined, the assertions will do nothing. Hence
9848 + * there is no point in having a function body for pred_sanity_check()
9849 + * if that preprocessor macro is defined.
9850 + */
9851 +void
9852 +pred_sanity_check(const struct predicate *predicates)
9853 +{
9854 + /* Do nothing, since assert is a no-op with _NDEBUG set */
9855 + return;
9856 +}
9857 +#else
9858 +void
9859 +pred_sanity_check(const struct predicate *predicates)
9860 +{
9861 + const struct predicate *p;
9862 +
9863 + for (p=predicates; p != NULL; p=p->pred_next)
9864 + {
9865 + /* All predicates must do something. */
9866 + assert (p->pred_func != NULL);
9867 +
9868 + /* All predicates must have a parser table entry. */
9869 + assert (p->parser_entry != NULL);
9870 +
9871 + /* If the parser table tells us that just one predicate function is
9872 + * possible, verify that that is still the one that is in effect.
9873 + * If the parser has NULL for the predicate function, that means that
9874 + * the parse_xxx function fills it in, so we can't check it.
9875 + */
9876 + if (p->parser_entry->pred_func)
9877 + {
9878 + assert (p->parser_entry->pred_func == p->pred_func);
9879 + }
9880 +
9881 + switch (p->parser_entry->type)
9882 + {
9883 + /* Options all take effect during parsing, so there should
9884 + * be no predicate entries corresponding to them. Hence we
9885 + * should not see any ARG_OPTION or ARG_POSITIONAL_OPTION
9886 + * items.
9887 + *
9888 + * This is a silly way of coding this test, but it prevents
9889 + * a compiler warning (i.e. otherwise it would think that
9890 + * there would be case statements missing).
9891 + */
9892 + case ARG_OPTION:
9893 + case ARG_POSITIONAL_OPTION:
9894 + assert (p->parser_entry->type != ARG_OPTION);
9895 + assert (p->parser_entry->type != ARG_POSITIONAL_OPTION);
9896 + break;
9897 +
9898 + case ARG_ACTION:
9899 + assert(p->side_effects); /* actions have side effects. */
9900 + if (!pred_is(p, pred_prune) && !pred_is(p, pred_quit))
9901 + {
9902 + /* actions other than -prune and -quit should
9903 + * inhibit the default -print
9904 + */
9905 + assert (p->no_default_print);
9906 + }
9907 + break;
9908 +
9909 + /* We happen to know that the only user of ARG_SPECIAL_PARSE
9910 + * is a test, so handle it like ARG_TEST.
9911 + */
9912 + case ARG_SPECIAL_PARSE:
9913 + case ARG_TEST:
9914 + case ARG_PUNCTUATION:
9915 + case ARG_NOOP:
9916 + /* Punctuation and tests should have no side
9917 + * effects and not inhibit default print.
9918 + */
9919 + assert (!p->no_default_print);
9920 + assert (!p->side_effects);
9921 + break;
9922 + }
9923 + }
9924 +}
9925 +#endif
9926 diff -purN findutils-4.3.12.orig/find/tree.c findutils-4.3.12/find/tree.c
9927 --- findutils-4.3.12.orig/find/tree.c 2007-12-19 16:12:34.000000000 -0500
9928 +++ findutils-4.3.12/find/tree.c 2008-01-30 08:46:05.758843847 -0500
9929 @@ -1195,6 +1195,10 @@ build_expression_tree(int argc, char *ar
9930 const struct parser_table *entry_close, *entry_print, *entry_open;
9931 int i, oldi;
9932
9933 +#ifdef WITH_SELINUX
9934 + int is_selinux_enabled_flag = is_selinux_enabled()>0;
9935 +#endif /* WITH_SELINUX */
9936 +
9937 predicates = NULL;
9938
9939 /* Find where in ARGV the predicates begin by skipping the list of
9940 @@ -1230,6 +1234,16 @@ build_expression_tree(int argc, char *ar
9941 }
9942
9943 predicate_name = argv[i];
9944 +
9945 +#ifdef WITH_SELINUX
9946 + if (! is_selinux_enabled_flag) {
9947 + if ((strncmp(predicate_name,"-context",strlen("-context"))==0) ||
9948 + (strncmp(predicate_name,"--context",strlen("--context"))==0)) {
9949 + error (1, 0,_("Error: invalid predicate %s: the kernel is not selinux-enabled.\n"),predicate_name);
9950 + }
9951 + }
9952 +#endif
9953 +
9954 parse_entry = find_parser (predicate_name);
9955 if (parse_entry == NULL)
9956 {
9957 @@ -1434,6 +1448,9 @@ get_new_pred (const struct parser_table
9958 last_pred->need_stat = true;
9959 last_pred->need_type = true;
9960 last_pred->args.str = NULL;
9961 +#ifdef WITH_SELINUX
9962 + last_pred->args.scontext = NULL;
9963 +#endif
9964 last_pred->pred_next = NULL;
9965 last_pred->pred_left = NULL;
9966 last_pred->pred_right = NULL;
9967
9968
9969
9970 --
9971 gentoo-commits@l.g.o mailing list