1 |
pebenito 08/04/01 15:53:51 |
2 |
|
3 |
Modified: findutils-4.3.12-selinux.diff |
4 |
Log: |
5 |
remove cruft from findutils selinux patch. |
6 |
(Portage version: 2.1.4.4) |
7 |
|
8 |
Revision Changes Path |
9 |
1.2 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.2&view=markup |
12 |
plain: http://sources.gentoo.org/viewcvs.py/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff?rev=1.2&content-type=text/plain |
13 |
diff : http://sources.gentoo.org/viewcvs.py/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff?r1=1.1&r2=1.2 |
14 |
|
15 |
Index: findutils-4.3.12-selinux.diff |
16 |
=================================================================== |
17 |
RCS file: /var/cvsroot/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff,v |
18 |
retrieving revision 1.1 |
19 |
retrieving revision 1.2 |
20 |
diff -u -r1.1 -r1.2 |
21 |
--- findutils-4.3.12-selinux.diff 30 Jan 2008 14:02:05 -0000 1.1 |
22 |
+++ findutils-4.3.12-selinux.diff 1 Apr 2008 15:53:50 -0000 1.2 |
23 |
@@ -111,2037 +111,6 @@ |
24 |
.PP |
25 |
A `%' character followed by any other character is discarded, but the |
26 |
other character is printed (don't rely on this, as further format |
27 |
-diff -purN findutils-4.3.12.orig/find/find.1.orig findutils-4.3.12/find/find.1.orig |
28 |
---- findutils-4.3.12.orig/find/find.1.orig 1969-12-31 19:00:00.000000000 -0500 |
29 |
-+++ findutils-4.3.12/find/find.1.orig 2007-12-19 14:53:14.000000000 -0500 |
30 |
-@@ -0,0 +1,2027 @@ |
31 |
-+.TH FIND 1 \" -*- nroff -*- |
32 |
-+.SH NAME |
33 |
-+find \- search for files in a directory hierarchy |
34 |
-+.SH SYNOPSIS |
35 |
-+.B find |
36 |
-+[\-H] [\-L] [\-P] [\-D debugopts] [\-Olevel] [path...] [expression] |
37 |
-+.SH DESCRIPTION |
38 |
-+This manual page |
39 |
-+documents the GNU version of |
40 |
-+.BR find . |
41 |
-+GNU |
42 |
-+.B find |
43 |
-+searches the directory tree rooted at each given file name by |
44 |
-+evaluating the given expression from left to right, according to the |
45 |
-+rules of precedence (see section OPERATORS), until the outcome is |
46 |
-+known (the left hand side is false for \fIand\fR operations, true for |
47 |
-+\fIor\fR), at which point |
48 |
-+.B find |
49 |
-+moves on to the next file name. |
50 |
-+.PP |
51 |
-+If you are using |
52 |
-+.B find |
53 |
-+in an environment where security is important (for example if you are |
54 |
-+using it to seach directories that are writable by other users), you |
55 |
-+should read the "Security Considerations" chapter of the findutils |
56 |
-+documentation, which is called \fBFinding Files\fP and comes with |
57 |
-+findutils. That document also includes a lot more detail |
58 |
-+and discussion than this manual page, so you may find it a more useful |
59 |
-+source of information. |
60 |
-+.SH OPTIONS |
61 |
-+The |
62 |
-+.BR \-H , |
63 |
-+.B \-L |
64 |
-+and |
65 |
-+.B \-P |
66 |
-+options control the treatment of symbolic |
67 |
-+links. Command-line arguments following these are taken to be names |
68 |
-+of files or directories to be examined, up to the first argument that |
69 |
-+begins with `\-', or the argument `(' or `!'. That argument and any |
70 |
-+following arguments are taken to be the expression describing what is |
71 |
-+to be searched for. If no paths are given, the current directory is |
72 |
-+used. If no expression is given, the expression |
73 |
-+.B \-print |
74 |
-+is used |
75 |
-+(but you should probably consider using |
76 |
-+.B \-print0 |
77 |
-+instead, anyway). |
78 |
-+.PP |
79 |
-+This manual page talks about `options' within the expression list. |
80 |
-+These options control the behaviour of |
81 |
-+.B find |
82 |
-+but are specified immediately after the last path name. The five |
83 |
-+`real' options |
84 |
-+.BR \-H , |
85 |
-+.BR \-L , |
86 |
-+.BR \-P , |
87 |
-+.B \-D |
88 |
-+and |
89 |
-+.B \-O |
90 |
-+must appear before |
91 |
-+the first path name, if at all. A double dash |
92 |
-+.B \-\- |
93 |
-+can also be used |
94 |
-+to signal that any remaining arguments are not options (though |
95 |
-+ensuring that all start points begin with either `./' or `/' is |
96 |
-+generally safer if you use wildcards in the list of start points). |
97 |
-+.IP \-P |
98 |
-+Never follow symbolic links. This is the default behaviour. When |
99 |
-+.B find |
100 |
-+examines or prints information a file, and the file is a symbolic |
101 |
-+link, the information used shall be taken from the properties of the |
102 |
-+symbolic link itself. |
103 |
-+ |
104 |
-+.IP \-L |
105 |
-+Follow symbolic links. When |
106 |
-+.B find |
107 |
-+examines or prints information about files, the information used shall |
108 |
-+be taken from the properties of the file to which the link points, not |
109 |
-+from the link itself (unless it is a broken symbolic link or |
110 |
-+.B find |
111 |
-+is unable to examine the file to which the link points). Use of this |
112 |
-+option implies |
113 |
-+.BR \-noleaf . |
114 |
-+If you later use the |
115 |
-+.B \-P |
116 |
-+option, |
117 |
-+.B \-noleaf |
118 |
-+will still be in effect. If |
119 |
-+.B \-L |
120 |
-+is in effect and |
121 |
-+.B find |
122 |
-+discovers a symbolic link to a subdirectory during its search, |
123 |
-+the subdirectory pointed to by the symbolic link will be searched. |
124 |
-+.IP |
125 |
-+When the |
126 |
-+.B \-L |
127 |
-+option is in effect, the |
128 |
-+.B \-type |
129 |
-+predicate will always |
130 |
-+match against the type of the file that a symbolic link points to |
131 |
-+rather than the link itself (unless the symbolic link is broken). |
132 |
-+Using |
133 |
-+.B \-L |
134 |
-+causes the |
135 |
-+.B \-lname |
136 |
-+and |
137 |
-+.B \-ilname |
138 |
-+predicates always to return |
139 |
-+false. |
140 |
-+ |
141 |
-+.IP \-H |
142 |
-+Do not follow symbolic links, except while processing the command |
143 |
-+line arguments. When |
144 |
-+.B find |
145 |
-+examines or prints information about files, the information used |
146 |
-+shall be taken from the properties of the symbolic link itself. The |
147 |
-+only exception to this behaviour is when a file specified on the |
148 |
-+command line is a symbolic link, and the link can be resolved. For |
149 |
-+that situation, the information used is taken from whatever the link |
150 |
-+points to (that is, the link is followed). The information about the |
151 |
-+link itself is used as a fallback if the file pointed to by the |
152 |
-+symbolic link cannot be examined. If |
153 |
-+.B \-H |
154 |
-+is in effect and one of the |
155 |
-+paths specified on the command line is a symbolic link to a directory, |
156 |
-+the contents of that directory will be examined (though of course |
157 |
-+\-maxdepth 0 would prevent this). |
158 |
-+.P |
159 |
-+If more than one of |
160 |
-+.BR \-H , |
161 |
-+.B \-L |
162 |
-+and |
163 |
-+.B \-P |
164 |
-+is specified, each overrides the |
165 |
-+others; the last one appearing on the command line takes effect. |
166 |
-+Since it is the default, the |
167 |
-+.B \-P |
168 |
-+option should be considered to be in |
169 |
-+effect unless either |
170 |
-+.B \-H |
171 |
-+or |
172 |
-+.B \-L |
173 |
-+is specified. |
174 |
-+ |
175 |
-+GNU |
176 |
-+.B find |
177 |
-+frequently stats files during the processing of the command line |
178 |
-+itself, before any searching has begun. These options also affect how |
179 |
-+those arguments are processed. Specifically, there are a number of |
180 |
-+tests that compare files listed on the command line against a file we |
181 |
-+are currently considering. In each case, the file specified on the |
182 |
-+command line will have been examined and some of its properties will |
183 |
-+have been saved. If the named file is in fact a symbolic link, and |
184 |
-+the |
185 |
-+.B \-P |
186 |
-+option is in effect (or if neither |
187 |
-+.B \-H |
188 |
-+nor |
189 |
-+.B \-L |
190 |
-+were specified), the information used for the comparison will be taken from |
191 |
-+the properties of the symbolic link. Otherwise, it will be taken from |
192 |
-+the properties of the file the link points to. If |
193 |
-+.B find |
194 |
-+cannot follow the link (for example because it has insufficient |
195 |
-+privileges or the link points to a nonexistent file) the properties of |
196 |
-+the link itself will be used. |
197 |
-+.P |
198 |
-+When the |
199 |
-+.B \-H |
200 |
-+or |
201 |
-+.B \-L options are in effect, any symbolic links listed |
202 |
-+as the argument of |
203 |
-+.B \-newer |
204 |
-+will be dereferenced, and the timestamp |
205 |
-+will be taken from the file to which the symbolic link points. The |
206 |
-+same consideration applies to |
207 |
-+.BR \-newerXY , |
208 |
-+.B \-anewer |
209 |
-+and |
210 |
-+.BR \-cnewer . |
211 |
-+ |
212 |
-+The |
213 |
-+.B \-follow |
214 |
-+option has a similar effect to |
215 |
-+.BR \-L , |
216 |
-+though it takes |
217 |
-+effect at the point where it appears (that is, if |
218 |
-+.B \-L |
219 |
-+is not used but |
220 |
-+.B \-follow |
221 |
-+is, any symbolic links appearing after |
222 |
-+.B \-follow |
223 |
-+on the |
224 |
-+command line will be dereferenced, and those before it will not). |
225 |
-+ |
226 |
-+.IP "\-D debugoptions" |
227 |
-+Print diagnostic information; this can be helpful to diagnose problems |
228 |
-+with why |
229 |
-+.B find |
230 |
-+is not doing what you want. The list of debug options should be comma |
231 |
-+separated. Compatibility of the debug options is not guaranteed |
232 |
-+between releases of findutils. For a complete list of valid debug |
233 |
-+options, see the output of |
234 |
-+.B find \-D |
235 |
-+.BR help . |
236 |
-+Valid debug options include |
237 |
-+.RS |
238 |
-+.IP help |
239 |
-+Explain the debugging options |
240 |
-+.IP tree |
241 |
-+Show the expression tree in its original and optimised form. |
242 |
-+.IP stat |
243 |
-+Print messages as files are examined with the |
244 |
-+.B stat |
245 |
-+and |
246 |
-+.B lstat |
247 |
-+system calls. The |
248 |
-+.B find |
249 |
-+program tries to minimise such calls. |
250 |
-+.IP opt |
251 |
-+Prints diagnostic information relating to the optimisation of the |
252 |
-+expression tree; see the \-O option. |
253 |
-+.IP rates |
254 |
-+Prints a summary indicating how often each predicate succeeded or |
255 |
-+failed. |
256 |
-+.RE |
257 |
-+.IP \-Olevel |
258 |
-+Enables query optimisation. The |
259 |
-+.B find |
260 |
-+program reorders tests to speed up execution while preserving the |
261 |
-+overall effect; that is, predicates with side effects are not |
262 |
-+reordered relative to each other. The optimisations performed at each |
263 |
-+optimisation level are as follows. |
264 |
-+.RS |
265 |
-+.IP 0 |
266 |
-+Equivalent to optimisation level 1. |
267 |
-+.IP 1 |
268 |
-+This is the default optimisation level and corresponds to the |
269 |
-+traditional behaviour. Expressions are reordered so that tests based |
270 |
-+only on the names of files (for example |
271 |
-+.B \-name |
272 |
-+and |
273 |
-+.BR \-regex ) |
274 |
-+are performed first. |
275 |
-+.IP 2 |
276 |
-+Any |
277 |
-+.B \-type |
278 |
-+or |
279 |
-+.B \-xtype |
280 |
-+tests are performed after any tests based only on the names of files, |
281 |
-+but before any tests that require information from the inode. On many |
282 |
-+modern versions of Unix, file types are returned by |
283 |
-+.B readdir() |
284 |
-+and so these predicates are faster to evaluate than predicates which |
285 |
-+need to stat the file first. |
286 |
-+.IP 3 |
287 |
-+At this optimisation level, the full cost-based query optimiser is |
288 |
-+enabled. The order of tests is modified so that cheap (i.e. fast) |
289 |
-+tests are performed first and more expensive ones are performed later, |
290 |
-+if necessary. Within each cost band, predicates are evaluated earlier |
291 |
-+or later according to whether they are likely to succeed or not. For |
292 |
-+.BR \-o , |
293 |
-+predicates which are likely to succeed are evaluated earlier, and for |
294 |
-+.BR \-a , |
295 |
-+predicates which are likely to fail are evaluated earlier. |
296 |
-+.RE |
297 |
-+.IP |
298 |
-+The cost-based optimiser has a fixed idea of how likely any given test |
299 |
-+is to succeed. In some cases the probability takes account of the |
300 |
-+specific nature of the test (for example, |
301 |
-+.B \-type f |
302 |
-+is assumed to be more likely to succeed than |
303 |
-+.BR "\-type c" ). |
304 |
-+The cost-based optimiser is currently being evaluated. If it does |
305 |
-+not actually improve the performance of |
306 |
-+.BR find , |
307 |
-+it will be removed again. Conversely, optimisations that prove to be |
308 |
-+reliable, robust and effective may be enabled at lower optimisation |
309 |
-+levels over time. However, the default behaviour (i.e. optimisation |
310 |
-+level 1) will not be changed in the 4.3.x release series. The |
311 |
-+findutils test suite runs all the tests on |
312 |
-+.B find |
313 |
-+at each optimisation level and ensures that the result is the same. |
314 |
-+.P |
315 |
-+.SH EXPRESSIONS |
316 |
-+The expression is made up of options (which affect overall operation |
317 |
-+rather than the processing of a specific file, and always return |
318 |
-+true), tests (which return a true or false value), and actions (which |
319 |
-+have side effects and return a true or false value), all separated by |
320 |
-+operators. |
321 |
-+.B \-and |
322 |
-+is assumed where the operator is omitted. |
323 |
-+ |
324 |
-+If the expression contains no actions other than |
325 |
-+.BR \-prune , |
326 |
-+.B \-print |
327 |
-+is |
328 |
-+performed on all files for which the expression is true. |
329 |
-+ |
330 |
-+.SS OPTIONS |
331 |
-+.P |
332 |
-+All options always return true. Except for |
333 |
-+.BR \-daystart , |
334 |
-+.B \-follow |
335 |
-+and |
336 |
-+.BR \-regextype , |
337 |
-+the options affect all tests, including tests specified |
338 |
-+before the option. This is because the options are processed when the |
339 |
-+command line is parsed, while the tests don't do anything until files |
340 |
-+are examined. The |
341 |
-+.BR \-daystart , |
342 |
-+.B \-follow |
343 |
-+and |
344 |
-+.B \-regextype |
345 |
-+options are different in this respect, and have an effect only on tests which |
346 |
-+appear later in the command line. Therefore, for clarity, it is best |
347 |
-+to place them at the beginning of the expression. A warning is issued |
348 |
-+if you don't do this. |
349 |
-+ |
350 |
-+.IP \-d |
351 |
-+A synonym for \-depth, for compatibility with FreeBSD, NetBSD, MacOS X and OpenBSD. |
352 |
-+ |
353 |
-+.IP \-daystart |
354 |
-+Measure times (for |
355 |
-+.BR \-amin , |
356 |
-+.BR \-atime , |
357 |
-+.BR \-cmin , |
358 |
-+.BR \-ctime , |
359 |
-+.BR \-mmin , |
360 |
-+and |
361 |
-+.BR \-mtime ) |
362 |
-+from the beginning of today rather than from 24 hours ago. This |
363 |
-+option only affects tests which appear later on the command line. |
364 |
-+ |
365 |
-+.IP \-depth |
366 |
-+Process each directory's contents before the directory itself. The |
367 |
-+\-delete action also implies |
368 |
-+.BR \-depth . |
369 |
-+ |
370 |
-+.IP \-follow |
371 |
-+Deprecated; use the |
372 |
-+.B \-L |
373 |
-+option instead. Dereference symbolic links. |
374 |
-+Implies |
375 |
-+.BR \-noleaf . |
376 |
-+The |
377 |
-+.B \-follow |
378 |
-+option affects only those tests which |
379 |
-+appear after it on the command line. Unless the |
380 |
-+.B \-H |
381 |
-+or |
382 |
-+.B \-L |
383 |
-+option has |
384 |
-+been specified, the position of the |
385 |
-+.B \-follow |
386 |
-+option changes the behaviour of the |
387 |
-+.B \-newer |
388 |
-+predicate; any files listed as the argument |
389 |
-+of |
390 |
-+.B \-newer |
391 |
-+will be dereferenced if they are symbolic links. The same |
392 |
-+consideration applies to |
393 |
-+.BR \-newerXY , |
394 |
-+.B \-anewer |
395 |
-+and |
396 |
-+.BR \-cnewer . |
397 |
-+Similarly, the |
398 |
-+.B \-type |
399 |
-+predicate will always match against the type of the file |
400 |
-+that a symbolic link points to rather than the link itself. Using |
401 |
-+.B \-follow |
402 |
-+causes the |
403 |
-+.B \-lname and |
404 |
-+.B \-ilname |
405 |
-+predicates always to return false. |
406 |
-+ |
407 |
-+.IP "\-help, \-\-help" |
408 |
-+Print a summary of the command-line usage of |
409 |
-+.B find |
410 |
-+and exit. |
411 |
-+ |
412 |
-+.IP \-ignore_readdir_race |
413 |
-+Normally, \fBfind\fR will emit an error message when it fails to stat a file. |
414 |
-+If you give this option and a file is deleted between the time \fBfind\fR |
415 |
-+reads the name of the file from the directory and the time it tries to stat |
416 |
-+the file, no error message will be issued. This also applies to files |
417 |
-+or directories whose names are given on the command line. This option takes |
418 |
-+effect at the time the command line is read, which means that you cannot search |
419 |
-+one part of the filesystem with this option on and part of it with this option |
420 |
-+off (if you need to do that, you will need to issue two \fBfind\fR commands |
421 |
-+instead, one with the option and one without it). |
422 |
-+ |
423 |
-+.IP "\-maxdepth \fIlevels\fR" |
424 |
-+Descend at most \fIlevels\fR (a non-negative integer) levels of |
425 |
-+directories below the command line arguments. |
426 |
-+.B \-maxdepth 0 |
427 |
-+ means only apply the tests and actions to the command line arguments. |
428 |
-+ |
429 |
-+.IP "\-mindepth \fIlevels\fR" |
430 |
-+Do not apply any tests or actions at levels less than \fIlevels\fR (a |
431 |
-+non-negative integer). |
432 |
-+.B \-mindepth 1 |
433 |
-+means process all files except the command line arguments. |
434 |
-+ |
435 |
-+.IP \-mount |
436 |
-+Don't descend directories on other filesystems. An alternate name for |
437 |
-+.BR \-xdev , |
438 |
-+for compatibility with some other versions of |
439 |
-+.BR find . |
440 |
-+ |
441 |
-+.IP \-noignore_readdir_race |
442 |
-+Turns off the effect of |
443 |
-+.BR \-ignore_readdir_race . |
444 |
-+ |
445 |
-+.IP "\-noleaf" |
446 |
-+Do not optimize by assuming that directories contain 2 fewer |
447 |
-+subdirectories than their hard link count. This option is needed when |
448 |
-+searching filesystems that do not follow the Unix directory-link |
449 |
-+convention, such as CD-ROM or MS-DOS filesystems or AFS volume mount |
450 |
-+points. Each directory on a normal Unix filesystem has at least 2 |
451 |
-+hard links: its name and its `.' entry. Additionally, its |
452 |
-+subdirectories (if any) each have a `..' entry linked to that |
453 |
-+directory. When |
454 |
-+.B find |
455 |
-+is examining a directory, after it has statted 2 fewer subdirectories |
456 |
-+than the directory's link count, it knows that the rest of the entries |
457 |
-+in the directory are non-directories (`leaf' files in the directory |
458 |
-+tree). If only the files' names need to be examined, there is no need |
459 |
-+to stat them; this gives a significant increase in search speed. |
460 |
-+ |
461 |
-+.IP "\-regextype \fItype\fR" |
462 |
-+Changes the regular expression syntax understood by |
463 |
-+.B \-regex |
464 |
-+and |
465 |
-+.B \-iregex |
466 |
-+tests which occur later on the command line. Currently-implemented |
467 |
-+types are emacs (this is the default), posix-awk, posix-basic, |
468 |
-+posix-egrep and posix-extended. |
469 |
-+ |
470 |
-+.IP "\-version, \-\-version" |
471 |
-+Print the \fBfind\fR version number and exit. |
472 |
-+ |
473 |
-+.IP "\-warn, \-nowarn" |
474 |
-+Turn warning messages on or off. These warnings apply only to the |
475 |
-+command line usage, not to any conditions that |
476 |
-+.B find |
477 |
-+might encounter when it searches directories. The default behaviour |
478 |
-+corresponds to |
479 |
-+.B \-warn |
480 |
-+if standard input is a tty, and to |
481 |
-+.B \-nowarn |
482 |
-+otherwise. |
483 |
-+ |
484 |
-+.IP \-xdev |
485 |
-+Don't descend directories on other filesystems. |
486 |
-+ |
487 |
-+.SS TESTS |
488 |
-+Some tests, for example |
489 |
-+.B \-newerXY |
490 |
-+and |
491 |
-+.BR -samefile , |
492 |
-+allow comparison between the file currently being examined and some |
493 |
-+reference file specified on the command line. When these tests are |
494 |
-+used, the interpretation of the reference file is determined by the |
495 |
-+options |
496 |
-+.BR \-H , |
497 |
-+.B \-L |
498 |
-+and |
499 |
-+.B \-P |
500 |
-+and any previous |
501 |
-+.BR \-follow , |
502 |
-+but the reference file is only examined once, at the time the command |
503 |
-+line is parsed. If the reference file cannot be examined (for |
504 |
-+example, the |
505 |
-+.BR stat (2) |
506 |
-+system call fails for it), an error message is issued, and |
507 |
-+.B find |
508 |
-+exits with a nonzero status. |
509 |
-+.P |
510 |
-+Numeric arguments can be specified as |
511 |
-+.IP \fI+n\fP |
512 |
-+for greater than |
513 |
-+.IR n , |
514 |
-+.IP \fI\-n\fP |
515 |
-+for less than |
516 |
-+.IR n , |
517 |
-+.IP \fIn\fP |
518 |
-+for exactly |
519 |
-+.IR n . |
520 |
-+.P |
521 |
-+ |
522 |
-+.IP "\-amin \fIn\fR" |
523 |
-+File was last accessed \fIn\fR minutes ago. |
524 |
-+ |
525 |
-+.IP "\-anewer \fIfile\fR" |
526 |
-+File was last accessed more recently than \fIfile\fR was modified. If |
527 |
-+\fIfile\fR is a symbolic link and the |
528 |
-+.B \-H |
529 |
-+option or the |
530 |
-+.B \-L |
531 |
-+option is in effect, the access time of the file it points to is |
532 |
-+always used. |
533 |
-+ |
534 |
-+.IP "\-atime \fIn\fR" |
535 |
-+File was last accessed \fIn\fR*24 hours ago. |
536 |
-+When find figures out how many 24-hour periods ago the file |
537 |
-+was last accessed, any fractional part is ignored, so to match |
538 |
-+.B \-atime |
539 |
-+.BR +1 , |
540 |
-+a file has to have been accessed at least |
541 |
-+.I two |
542 |
-+days ago. |
543 |
-+ |
544 |
-+.IP "\-cmin \fIn\fR" |
545 |
-+File's status was last changed \fIn\fR minutes ago. |
546 |
-+ |
547 |
-+.IP "\-cnewer \fIfile\fR" |
548 |
-+File's status was last changed more recently than \fIfile\fR was |
549 |
-+modified. If \fIfile\fR is a symbolic link and the |
550 |
-+.B \-H |
551 |
-+option or the |
552 |
-+.B \-L |
553 |
-+option is in effect, the status-change time of the file it points |
554 |
-+to is always used. |
555 |
-+ |
556 |
-+.IP "\-ctime \fIn\fR" |
557 |
-+File's status was last changed \fIn\fR*24 hours ago. |
558 |
-+See the comments for |
559 |
-+.B \-atime |
560 |
-+to understand how rounding affects the interpretation of file status |
561 |
-+change times. |
562 |
-+ |
563 |
-+.IP \-empty |
564 |
-+File is empty and is either a regular file or a directory. |
565 |
-+ |
566 |
-+.IP \-executable |
567 |
-+Matches files which are executable and directories which are |
568 |
-+searchable (in a file name resolution sense). This takes into account |
569 |
-+access control lists and other permissions artefacts which the |
570 |
-+.B \-perm |
571 |
-+test ignores. This test makes use of the |
572 |
-+.BR access (2) |
573 |
-+system call, and so can be fooled by NFS servers which do UID |
574 |
-+mapping (or root-squashing), since many systems implement |
575 |
-+.BR access (2) |
576 |
-+in the client's kernel and so cannot make use of the UID mapping |
577 |
-+information held on the server. Because this test is based only on |
578 |
-+the result of the |
579 |
-+.BR access (2) |
580 |
-+system call, there is no guarantee that a file for which this test |
581 |
-+succeeds can actually be executed. |
582 |
-+ |
583 |
-+.IP \-false |
584 |
-+Always false. |
585 |
-+ |
586 |
-+.IP "\-fstype \fItype\fR" |
587 |
-+File is on a filesystem of type \fItype\fR. The valid filesystem |
588 |
-+types vary among different versions of Unix; an incomplete list of |
589 |
-+filesystem types that are accepted on some version of Unix or another |
590 |
-+is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use |
591 |
-+.B \-printf |
592 |
-+with the %F directive to see the types of your filesystems. |
593 |
-+ |
594 |
-+.IP "\-gid \fIn\fR" |
595 |
-+File's numeric group ID is \fIn\fR. |
596 |
-+ |
597 |
-+.IP "\-group \fIgname\fR" |
598 |
-+File belongs to group \fIgname\fR (numeric group ID allowed). |
599 |
-+ |
600 |
-+.IP "\-ilname \fIpattern\fR" |
601 |
-+Like |
602 |
-+.BR \-lname , |
603 |
-+but the match is case insensitive. |
604 |
-+If the |
605 |
-+.B \-L |
606 |
-+option or the |
607 |
-+.B \-follow |
608 |
-+option is in effect, this test returns false unless the symbolic link |
609 |
-+is broken. |
610 |
-+ |
611 |
-+.IP "\-iname \fIpattern\fR" |
612 |
-+Like |
613 |
-+.BR \-name , |
614 |
-+but the match is case insensitive. For example, the |
615 |
-+patterns `fo*' and `F??' match the file names `Foo', `FOO', `foo', |
616 |
-+`fOo', etc. In these patterns, unlike filename expansion by the |
617 |
-+shell, an initial '.' can be matched by `*'. That is, |
618 |
-+.B find \-name *bar |
619 |
-+will match the file `.foobar'. Please note that you should quote |
620 |
-+patterns as a matter of course, otherwise the shell will expand any |
621 |
-+wildcard characters in them. |
622 |
-+ |
623 |
-+.IP "\-inum \fIn\fR" |
624 |
-+File has inode number \fIn\fR. It is normally easier to use the |
625 |
-+.B \-samefile |
626 |
-+test instead. |
627 |
-+ |
628 |
-+.IP "\-ipath \fIpattern\fR" |
629 |
-+Behaves in the same way as |
630 |
-+.BR \-iwholename . |
631 |
-+This option is deprecated, so please do not use it. |
632 |
-+ |
633 |
-+.IP "\-iregex \fIpattern\fR" |
634 |
-+Like |
635 |
-+.BR \-regex , |
636 |
-+but the match is case insensitive. |
637 |
-+ |
638 |
-+.IP "\-iwholename \fIpattern\fR" |
639 |
-+Like |
640 |
-+.BR \-wholename , |
641 |
-+but the match is case insensitive. |
642 |
-+ |
643 |
-+.IP "\-links \fIn\fR" |
644 |
-+File has \fIn\fR links. |
645 |
-+ |
646 |
-+.IP "\-lname \fIpattern\fR" |
647 |
-+File is a symbolic link whose contents match shell pattern |
648 |
-+\fIpattern\fR. The metacharacters do not treat `/' or `.' specially. |
649 |
-+If the |
650 |
-+.B \-L |
651 |
-+option or the |
652 |
-+.B \-follow |
653 |
-+option is in effect, this test returns false unless the symbolic link |
654 |
-+is broken. |
655 |
-+ |
656 |
-+.IP "\-mmin \fIn\fR" |
657 |
-+File's data was last modified \fIn\fR minutes ago. |
658 |
-+ |
659 |
-+.IP "\-mtime \fIn\fR" |
660 |
-+File's data was last modified \fIn\fR*24 hours ago. |
661 |
-+See the comments for |
662 |
-+.B \-atime |
663 |
-+to understand how rounding affects the interpretation of file |
664 |
-+modification times. |
665 |
-+ |
666 |
-+.IP "\-name \fIpattern\fR" |
667 |
-+Base of file name (the path with the leading directories removed) |
668 |
-+matches shell pattern \fIpattern\fR. The metacharacters (`*', `?', |
669 |
-+and `[]') match a `.' at the start of the base name (this is a change |
670 |
-+in findutils-4.2.2; see section STANDARDS CONFORMANCE below). To ignore a |
671 |
-+directory and the files under it, use |
672 |
-+.BR \-prune ; |
673 |
-+see an example in the |
674 |
-+description of |
675 |
-+.BR \-path . |
676 |
-+Braces are not recognised as being |
677 |
-+special, despite the fact that some shells including Bash imbue braces |
678 |
-+with a special meaning in shell patterns. The filename matching is |
679 |
-+performed with the use of the |
680 |
-+.BR fnmatch (3) |
681 |
-+library function. Don't forget to enclose the pattern in quotes |
682 |
-+in order to protect it from expansion by the shell. |
683 |
-+ |
684 |
-+.IP "\-newer \fIfile\fR" |
685 |
-+File was modified more recently than \fIfile\fR. If \fIfile\fR is a |
686 |
-+symbolic link and the |
687 |
-+.B \-H |
688 |
-+option or the |
689 |
-+.B \-L |
690 |
-+option is in effect, the |
691 |
-+modification time of the file it points to is always used. |
692 |
-+ |
693 |
-+.IP "\-newerXY \fIreference\fR" |
694 |
-+Compares the timestamp of the current file with \fIreference\fR. |
695 |
-+The |
696 |
-+.I reference |
697 |
-+argument is normally the name of a file (and one of its timestamps is |
698 |
-+used for the comparison) but it may also be a string describing an |
699 |
-+absolute time. |
700 |
-+.I X |
701 |
-+and |
702 |
-+.I Y |
703 |
-+are placeholders for other letters, and these letters select which |
704 |
-+time belonging to |
705 |
-+how |
706 |
-+.I reference |
707 |
-+is used for the comparison. |
708 |
-+.TS |
709 |
-+ll |
710 |
-+ll |
711 |
-+ll |
712 |
-+ll |
713 |
-+llw(2i). |
714 |
-+a The access time of the file \fIreference\fR |
715 |
-+B The birth time of the file \fIreference\fR |
716 |
-+c The inode status change time of \fIreference\fR |
717 |
-+m The modification time of the file \fIreference\fR |
718 |
-+t \fIreference\fR is interpreted directly as a time |
719 |
-+.TE |
720 |
-+ |
721 |
-+Some combinations are invalid; for example, it is invalid for |
722 |
-+.I X |
723 |
-+to be |
724 |
-+.IR t . |
725 |
-+Some combinations are not implemented on all systems; for example |
726 |
-+.I B |
727 |
-+is not supported on all systems. If an invalid or unsupported |
728 |
-+combination of |
729 |
-+.I XY |
730 |
-+is specified, a fatal error results. Time specifications are |
731 |
-+interpreted as for the argument to the |
732 |
-+.B \-d |
733 |
-+option of GNU |
734 |
-+.BR date . |
735 |
-+If you try to use the birth time of a reference file, and the birth |
736 |
-+time cannot be determined, a fatal error message results. If you |
737 |
-+specify a test which refers to the birth time of files being examined, |
738 |
-+this test will fail for any files where the birth time is unknown. |
739 |
-+ |
740 |
-+.IP \-nogroup |
741 |
-+No group corresponds to file's numeric group ID. |
742 |
-+ |
743 |
-+.IP \-nouser |
744 |
-+No user corresponds to file's numeric user ID. |
745 |
-+ |
746 |
-+.IP "\-path \fIpattern\fR" |
747 |
-+File name matches shell pattern \fIpattern\fR. The metacharacters do |
748 |
-+not treat `/' or `.' specially; so, for example, |
749 |
-+.br |
750 |
-+.in +1i |
751 |
-+find . \-path "./sr*sc" |
752 |
-+.br |
753 |
-+.in -1i |
754 |
-+will print an entry for a directory called `./src/misc' (if one |
755 |
-+exists). To ignore a whole directory tree, use |
756 |
-+.B \-prune |
757 |
-+rather than |
758 |
-+checking every file in the tree. For example, to skip the |
759 |
-+directory `src/emacs' and all files and directories under it, and |
760 |
-+print the names of the other files found, do something like this: |
761 |
-+.br |
762 |
-+.in +1i |
763 |
-+find . \-path ./src/emacs \-prune \-o \-print |
764 |
-+.br |
765 |
-+.in -1i |
766 |
-+Note that the pattern match test applies to the whole file name, |
767 |
-+starting from one of the start points named on the command line. It |
768 |
-+would only make sense to use an absolute path name here if the |
769 |
-+relevant start point is also an absolute path. This means that this |
770 |
-+command will never match anything: |
771 |
-+.br |
772 |
-+.in +1i |
773 |
-+find bar \-path /foo/bar/myfile \-print |
774 |
-+.br |
775 |
-+.in -1i |
776 |
-+The predicate |
777 |
-+.B \-path |
778 |
-+is also supported by HP-UX |
779 |
-+.B find |
780 |
-+and will be in a forthcoming version of the POSIX standard. |
781 |
-+ |
782 |
-+.IP "\-perm \fImode\fR" |
783 |
-+File's permission bits are exactly \fImode\fR (octal or symbolic). |
784 |
-+Since an exact match is required, if you want to use this form for |
785 |
-+symbolic modes, you may have to specify a rather complex mode string. |
786 |
-+For example |
787 |
-+.B \-perm g=w |
788 |
-+will only match files which have mode 0020 |
789 |
-+(that is, ones for which group write permission is the only permission |
790 |
-+set). It is more likely that you will want to use the `/' or `-' |
791 |
-+forms, for example |
792 |
-+.BR "\-perm \-g=w" , |
793 |
-+which matches any file with group write permission. See the |
794 |
-+.B EXAMPLES |
795 |
-+section for some illustrative examples. |
796 |
-+ |
797 |
-+.IP "\-perm \-\fImode\fR" |
798 |
-+All of the permission bits \fImode\fR are set for the file. |
799 |
-+Symbolic modes are accepted in this form, and this is usually the way |
800 |
-+in which would want to use them. You must specify `u', `g' or `o' if |
801 |
-+you use a symbolic mode. See the |
802 |
-+.B EXAMPLES |
803 |
-+section for some illustrative examples. |
804 |
-+ |
805 |
-+.IP "\-perm /\fImode\fR" |
806 |
-+Any of the permission bits \fImode\fR are set for the file. Symbolic |
807 |
-+modes are accepted in this form. You must specify `u', `g' or `o' if |
808 |
-+you use a symbolic mode. See the |
809 |
-+.B EXAMPLES |
810 |
-+section for some illustrative examples. If no permission bits in |
811 |
-+.I mode |
812 |
-+are set, this test currently matches no files. However, it will soon |
813 |
-+be changed to match any file (the idea is to be more consistent with |
814 |
-+the behaviour of |
815 |
-+.B \-perm |
816 |
-+.BR \-000 ). |
817 |
-+ |
818 |
-+.IP "\-perm +\fImode\fR" |
819 |
-+Deprecated, old way of searching for files with any of the permission |
820 |
-+bits in \fImode\fR set. You should use |
821 |
-+.B \-perm \fI/mode\fR |
822 |
-+instead. Trying to use the `+' syntax with symbolic modes will yield |
823 |
-+surprising results. For example, `+u+x' is a valid symbolic mode |
824 |
-+(equivalent to +u,+x, i.e. 0111) and will therefore not be evaluated |
825 |
-+as |
826 |
-+.B \-perm +\fImode\fR |
827 |
-+but instead as the exact mode specifier |
828 |
-+.B \-perm \fImode\fR |
829 |
-+and so it matches files with exact permissions 0111 instead of files with any |
830 |
-+execute bit set. If you found this paragraph confusing, you're not |
831 |
-+alone - just use |
832 |
-+.B \-perm /\fImode\fR. |
833 |
-+This form of the |
834 |
-+.B \-perm |
835 |
-+test is deprecated because the POSIX specification requires the |
836 |
-+interpretation of a leading `+' as being part of a symbolic mode, and |
837 |
-+so we switched to using `/' instead. |
838 |
-+ |
839 |
-+.IP \-readable |
840 |
-+Matches files which are readable. This takes into account access |
841 |
-+control lists and other permissions artefacts which the |
842 |
-+.B \-perm |
843 |
-+test ignores. This test makes use of the |
844 |
-+.BR access (2) |
845 |
-+system call, and so can be fooled by NFS servers which do UID |
846 |
-+mapping (or root-squashing), since many systems implement |
847 |
-+.BR access (2) |
848 |
-+in the client's kernel and so cannot make use of the UID mapping |
849 |
-+information held on the server. |
850 |
-+ |
851 |
-+.IP "\-regex \fIpattern\fR" |
852 |
-+File name matches regular expression \fIpattern\fR. This is a match |
853 |
-+on the whole path, not a search. For example, to match a file named |
854 |
-+`./fubar3', you can use the regular expression `.*bar.' or `.*b.*3', |
855 |
-+but not `f.*r3'. The regular expressions understood by |
856 |
-+.B find |
857 |
-+are by default Emacs Regular Expressions, but this can be |
858 |
-+changed with the |
859 |
-+.B \-regextype |
860 |
-+option. |
861 |
-+ |
862 |
-+.IP "\-samefile \fIname\fR" |
863 |
-+File refers to the same inode as \fIname\fR. When |
864 |
-+.B \-L |
865 |
-+is in effect, this can include symbolic links. |
866 |
-+ |
867 |
-+.IP "\-size \fIn\fR[cwbkMG]" |
868 |
-+File uses \fIn\fP units of space. The following suffixes |
869 |
-+can be used: |
870 |
-+.RS |
871 |
-+.IP `b' |
872 |
-+for 512-byte blocks (this is the default if no suffix is used) |
873 |
-+.IP `c' |
874 |
-+for bytes |
875 |
-+.IP `w' |
876 |
-+for two-byte words |
877 |
-+.IP `k' |
878 |
-+for Kilobytes (units of 1024 bytes) |
879 |
-+.IP `M' |
880 |
-+for Megabytes (units of 1048576 bytes) |
881 |
-+.IP `G' |
882 |
-+for Gigabytes (units of 1073741824 bytes) |
883 |
-+.RE |
884 |
-+.IP |
885 |
-+The size does not count indirect blocks, but it does count blocks in |
886 |
-+sparse files that are not actually allocated. Bear in mind that the |
887 |
-+`%k' and `%b' format specifiers of |
888 |
-+.B \-printf |
889 |
-+handle sparse files |
890 |
-+differently. The `b' suffix always denotes 512-byte blocks and never |
891 |
-+1 Kilobyte blocks, which is different to the behaviour of |
892 |
-+.BR \-ls . |
893 |
-+ |
894 |
-+.IP \-true |
895 |
-+Always true. |
896 |
-+ |
897 |
-+.IP "\-type \fIc\fR" |
898 |
-+File is of type \fIc\fR: |
899 |
-+.RS |
900 |
-+.IP b |
901 |
-+block (buffered) special |
902 |
-+.IP c |
903 |
-+character (unbuffered) special |
904 |
-+.IP d |
905 |
-+directory |
906 |
-+.IP p |
907 |
-+named pipe (FIFO) |
908 |
-+.IP f |
909 |
-+regular file |
910 |
-+.IP l |
911 |
-+symbolic link; this is never true if the |
912 |
-+.B \-L |
913 |
-+option or the |
914 |
-+.B \-follow |
915 |
-+option is in effect, unless the symbolic link is broken. If you want |
916 |
-+to search for symbolic links when |
917 |
-+.B \-L |
918 |
-+is in effect, use |
919 |
-+.BR \-xtype . |
920 |
-+.IP s |
921 |
-+socket |
922 |
-+.IP D |
923 |
-+door (Solaris) |
924 |
-+.RE |
925 |
-+.IP "\-uid \fIn\fR" |
926 |
-+File's numeric user ID is \fIn\fR. |
927 |
-+ |
928 |
-+.IP "\-used \fIn\fR" |
929 |
-+File was last accessed \fIn\fR days after its status was last changed. |
930 |
-+ |
931 |
-+.IP "\-user \fIuname\fR" |
932 |
-+File is owned by user \fIuname\fR (numeric user ID allowed). |
933 |
-+ |
934 |
-+.IP "\-wholename \fIpattern\fR" |
935 |
-+See \-path. This alternative is less portable than |
936 |
-+.BR \-path . |
937 |
-+ |
938 |
-+.IP "\-writable" |
939 |
-+Matches files which are writable. This takes into account access |
940 |
-+control lists and other permissions artefacts which the |
941 |
-+.B \-perm |
942 |
-+test ignores. This test makes use of the |
943 |
-+.BR access (2) |
944 |
-+system call, and so can be fooled by NFS servers which do UID |
945 |
-+mapping (or root-squashing), since many systems implement |
946 |
-+.BR access (2) |
947 |
-+in the client's kernel and so cannot make use of the UID mapping |
948 |
-+information held on the server. |
949 |
-+ |
950 |
-+.IP "\-xtype \fIc\fR" |
951 |
-+The same as |
952 |
-+.B \-type |
953 |
-+unless the file is a symbolic link. For symbolic |
954 |
-+links: if the |
955 |
-+.B \-H |
956 |
-+or |
957 |
-+.B \-P |
958 |
-+option was specified, true if the file is a |
959 |
-+link to a file of type \fIc\fR; if the |
960 |
-+.B \-L |
961 |
-+option has been given, true |
962 |
-+if \fIc\fR is `l'. In other words, for symbolic links, |
963 |
-+.B \-xtype |
964 |
-+checks the type of the file that |
965 |
-+.B \-type |
966 |
-+does not check. |
967 |
-+ |
968 |
-+.SS ACTIONS |
969 |
-+.IP "\-delete\fR" |
970 |
-+Delete files; true if removal succeeded. If the removal failed, an |
971 |
-+error message is issued. |
972 |
-+If |
973 |
-+.B \-delete |
974 |
-+fails, |
975 |
-+.BR find 's |
976 |
-+exit status will be nonzero |
977 |
-+(when it eventually exits). |
978 |
-+Use of |
979 |
-+.B \-delete |
980 |
-+automatically turns on the |
981 |
-+.B \-depth |
982 |
-+option. |
983 |
-+ |
984 |
-+.BR Warnings : |
985 |
-+Don't forget that the find command line is |
986 |
-+evaluated as an expression, so putting |
987 |
-+.B \-delete |
988 |
-+first will make |
989 |
-+.B find |
990 |
-+try to delete everything below the starting points you specified. |
991 |
-+When testing a |
992 |
-+.B find |
993 |
-+command line that you later intend to use with |
994 |
-+.BR \-delete , |
995 |
-+you should explicitly specify |
996 |
-+.B \-depth |
997 |
-+in order to avoid later surprises. Because |
998 |
-+.B \-delete |
999 |
-+implies |
1000 |
-+.BR \-depth , |
1001 |
-+you cannot usefully use |
1002 |
-+.B \-prune |
1003 |
-+and |
1004 |
-+.B \-delete |
1005 |
-+together. |
1006 |
-+ |
1007 |
-+.IP "\-exec \fIcommand\fR ;" |
1008 |
-+Execute \fIcommand\fR; true if 0 status is returned. All following |
1009 |
-+arguments to |
1010 |
-+.B find |
1011 |
-+are taken to be arguments to the command until an argument consisting |
1012 |
-+of `;' is encountered. The string `{}' is replaced by the current |
1013 |
-+file name being processed everywhere it occurs in the arguments to the |
1014 |
-+command, not just in arguments where it is alone, as in some versions |
1015 |
-+of |
1016 |
-+.BR find . |
1017 |
-+Both of these constructions might need to be escaped (with a `\e') or |
1018 |
-+quoted to protect them from expansion by the shell. See the |
1019 |
-+.B EXAMPLES |
1020 |
-+section for examples of the use of the |
1021 |
-+.B \-exec |
1022 |
-+option. The specified |
1023 |
-+command is run once for each matched file. |
1024 |
-+The command is executed in the starting directory. There are |
1025 |
-+unavoidable security problems surrounding use of the |
1026 |
-+.B \-exec |
1027 |
-+action; |
1028 |
-+you should use the |
1029 |
-+.B \-execdir |
1030 |
-+option instead. |
1031 |
-+ |
1032 |
-+.IP "\-exec \fIcommand\fR {} +" |
1033 |
-+This variant of the |
1034 |
-+.B \-exec |
1035 |
-+action runs the specified command on the |
1036 |
-+selected files, but the command line is built by appending each |
1037 |
-+selected file name at the end; the total number of invocations of the |
1038 |
-+command will be much less than the number of matched files. The |
1039 |
-+command line is built in much the same way that |
1040 |
-+.B xargs |
1041 |
-+builds its command lines. Only one instance of `{}' is allowed within |
1042 |
-+the command. The command is executed in the starting directory. |
1043 |
-+ |
1044 |
-+.IP "\-execdir \fIcommand\fR ;" |
1045 |
-+.IP "\-execdir \fIcommand\fR {} +" |
1046 |
-+Like |
1047 |
-+.BR \-exec , |
1048 |
-+but the specified command is run from the subdirectory |
1049 |
-+containing the matched file, which is not normally the directory in |
1050 |
-+which you started |
1051 |
-+.BR find . |
1052 |
-+This a much more secure method for invoking commands, as it avoids |
1053 |
-+race conditions during resolution of the paths to the matched files. |
1054 |
-+As with the |
1055 |
-+.B \-exec |
1056 |
-+action, the `+' form of |
1057 |
-+.B \-execdir |
1058 |
-+will build a |
1059 |
-+command line to process more than one matched file, but any given |
1060 |
-+invocation of |
1061 |
-+.I command |
1062 |
-+will only list files that exist in the same subdirectory. If you use |
1063 |
-+this option, you must ensure that your |
1064 |
-+.B $PATH |
1065 |
-+environment variable does not reference `.'; |
1066 |
-+otherwise, an attacker can run any commands they like by leaving an |
1067 |
-+appropriately-named file in a directory in which you will run |
1068 |
-+.BR \-execdir . |
1069 |
-+The same applies to having entries in |
1070 |
-+.B $PATH |
1071 |
-+which are empty or which are not absolute directory names. |
1072 |
-+ |
1073 |
-+.IP "\-fls \fIfile\fR" |
1074 |
-+True; like |
1075 |
-+.B \-ls |
1076 |
-+but write to \fIfile\fR like |
1077 |
-+.BR \-fprint . |
1078 |
-+The output file is always created, even if the predicate is never |
1079 |
-+matched. |
1080 |
-+See the |
1081 |
-+.B UNUSUAL FILENAMES |
1082 |
-+section for information about how unusual characters in filenames are handled. |
1083 |
-+ |
1084 |
-+.IP "\-fprint \fIfile\fR" |
1085 |
-+True; print the full file name into file \fIfile\fR. If \fIfile\fR |
1086 |
-+does not exist when \fBfind\fR is run, it is created; if it does |
1087 |
-+exist, it is truncated. The file names ``/dev/stdout'' and |
1088 |
-+``/dev/stderr'' are handled specially; they refer to the standard |
1089 |
-+output and standard error output, respectively. |
1090 |
-+The output file is always created, even if the predicate is never matched. |
1091 |
-+See the |
1092 |
-+.B UNUSUAL FILENAMES |
1093 |
-+section for information about how unusual characters in filenames are handled. |
1094 |
-+ |
1095 |
-+.IP "\-fprint0 \fIfile\fR" |
1096 |
-+True; like |
1097 |
-+.B \-print0 |
1098 |
-+but write to \fIfile\fR like |
1099 |
-+.BR \-fprint . |
1100 |
-+The output file is always created, even if the predicate is never matched. |
1101 |
-+See the |
1102 |
-+.B UNUSUAL FILENAMES |
1103 |
-+section for information about how unusual characters in filenames are handled. |
1104 |
-+ |
1105 |
-+.IP "\-fprintf \fIfile\fR \fIformat\fR" |
1106 |
-+True; like |
1107 |
-+.B \-printf |
1108 |
-+but write to \fIfile\fR like |
1109 |
-+.BR \-fprint . |
1110 |
-+The output file is always created, even if the predicate is never matched. |
1111 |
-+See the |
1112 |
-+.B UNUSUAL FILENAMES |
1113 |
-+section for information about how unusual characters in filenames are handled. |
1114 |
-+ |
1115 |
-+.IP \-ls |
1116 |
-+True; list current file in |
1117 |
-+.B ls \-dils |
1118 |
-+format on standard output. |
1119 |
-+The block counts are of 1K blocks, unless the environment variable |
1120 |
-+POSIXLY_CORRECT is set, in which case 512-byte blocks are used. |
1121 |
-+See the |
1122 |
-+.B UNUSUAL FILENAMES |
1123 |
-+section for information about how unusual characters in filenames are handled. |
1124 |
-+ |
1125 |
-+.IP "\-ok \fIcommand\fR ;" |
1126 |
-+Like |
1127 |
-+.B \-exec |
1128 |
-+but ask the user first (on the standard input); if the |
1129 |
-+response does not start with `y' or `Y', do not run the command, and |
1130 |
-+return false. If the command is run, its standard input is redirected |
1131 |
-+from |
1132 |
-+.BR /dev/null . |
1133 |
-+ |
1134 |
-+.IP "\-okdir \fIcommand\fR ;" |
1135 |
-+Like |
1136 |
-+.B \-execdir |
1137 |
-+but ask the user first (on the standard input); if the |
1138 |
-+response does not start with `y' or `Y', do not run the command, and |
1139 |
-+return false. If the command is run, its standard input is redirected |
1140 |
-+from |
1141 |
-+.BR /dev/null . |
1142 |
-+ |
1143 |
-+.IP \-print |
1144 |
-+True; print the full file name on the standard output, followed by a |
1145 |
-+newline. If you are piping the output of |
1146 |
-+.B find |
1147 |
-+into another program and there is the faintest possibility that the files |
1148 |
-+which you are searching for might contain a newline, then you should |
1149 |
-+seriously consider using the |
1150 |
-+.B \-print0 |
1151 |
-+option instead of |
1152 |
-+.BR \-print . |
1153 |
-+See the |
1154 |
-+.B UNUSUAL FILENAMES |
1155 |
-+section for information about how unusual characters in filenames are handled. |
1156 |
-+ |
1157 |
-+.IP \-print0 |
1158 |
-+True; print the full file name on the standard output, followed by a |
1159 |
-+null character (instead of the newline character that |
1160 |
-+.B \-print |
1161 |
-+uses). |
1162 |
-+This allows file names that contain newlines or other types of white |
1163 |
-+space to be correctly interpreted by programs that process the |
1164 |
-+\fBfind\fR output. This option corresponds to the |
1165 |
-+.B \-0 |
1166 |
-+option of |
1167 |
-+.BR xargs . |
1168 |
-+ |
1169 |
-+.IP "\-printf \fIformat\fR" |
1170 |
-+True; print \fIformat\fR on the standard output, interpreting `\e' |
1171 |
-+escapes and `%' directives. Field widths and precisions can be |
1172 |
-+specified as with the `printf' C function. Please note that many of |
1173 |
-+the fields are printed as %s rather than %d, and this may mean that |
1174 |
-+flags don't work as you might expect. This also means that the `\-' |
1175 |
-+flag does work (it forces fields to be left-aligned). Unlike |
1176 |
-+.BR \-print , |
1177 |
-+.B \-printf |
1178 |
-+does not add a newline at the end of the string. The escapes |
1179 |
-+and directives are: |
1180 |
-+.RS |
1181 |
-+.IP \ea |
1182 |
-+Alarm bell. |
1183 |
-+.IP \eb |
1184 |
-+Backspace. |
1185 |
-+.IP \ec |
1186 |
-+Stop printing from this format immediately and flush the output. |
1187 |
-+.IP \ef |
1188 |
-+Form feed. |
1189 |
-+.IP \en |
1190 |
-+Newline. |
1191 |
-+.IP \er |
1192 |
-+Carriage return. |
1193 |
-+.IP \et |
1194 |
-+Horizontal tab. |
1195 |
-+.IP \ev |
1196 |
-+Vertical tab. |
1197 |
-+.IP \e\0 |
1198 |
-+ASCII NUL. |
1199 |
-+.IP \e\e |
1200 |
-+A literal backslash (`\e'). |
1201 |
-+.IP \eNNN |
1202 |
-+The character whose ASCII code is NNN (octal). |
1203 |
-+.PP |
1204 |
-+A `\e' character followed by any other character is treated as an |
1205 |
-+ordinary character, so they both are printed. |
1206 |
-+.IP %% |
1207 |
-+A literal percent sign. |
1208 |
-+.IP %a |
1209 |
-+File's last access time in the format returned by the C `ctime' function. |
1210 |
-+.IP %A\fIk\fP |
1211 |
-+File's last access time in the format specified by \fIk\fR, which is |
1212 |
-+either `@' or a directive for the C `strftime' function. The possible |
1213 |
-+values for \fIk\fR are listed below; some of them might not be |
1214 |
-+available on all systems, due to differences in `strftime' between |
1215 |
-+systems. |
1216 |
-+.RS |
1217 |
-+.IP @ |
1218 |
-+seconds since Jan. 1, 1970, 00:00 GMT, with fractional part. |
1219 |
-+.PP |
1220 |
-+Time fields: |
1221 |
-+.IP H |
1222 |
-+hour (00..23) |
1223 |
-+.IP I |
1224 |
-+hour (01..12) |
1225 |
-+.IP k |
1226 |
-+hour ( 0..23) |
1227 |
-+.IP l |
1228 |
-+hour ( 1..12) |
1229 |
-+.IP M |
1230 |
-+minute (00..59) |
1231 |
-+.IP p |
1232 |
-+locale's AM or PM |
1233 |
-+.IP r |
1234 |
-+time, 12-hour (hh:mm:ss [AP]M) |
1235 |
-+.IP S |
1236 |
-+Second (00.00 .. 61.00). There is a fractional part. |
1237 |
-+.IP T |
1238 |
-+time, 24-hour (hh:mm:ss) |
1239 |
-+.IP + |
1240 |
-+Date and time, separated by `+', for example |
1241 |
-+`2004\-04\-28+22:22:05.0'. This is a GNU extension. The time is |
1242 |
-+given in the current timezone (which may be affected by setting the TZ |
1243 |
-+environment variable). The seconds field includes a fractional part. |
1244 |
-+.IP X |
1245 |
-+locale's time representation (H:M:S) |
1246 |
-+.IP Z |
1247 |
-+time zone (e.g., EDT), or nothing if no time zone is determinable |
1248 |
-+.PP |
1249 |
-+Date fields: |
1250 |
-+.IP a |
1251 |
-+locale's abbreviated weekday name (Sun..Sat) |
1252 |
-+.IP A |
1253 |
-+locale's full weekday name, variable length (Sunday..Saturday) |
1254 |
-+.IP b |
1255 |
-+locale's abbreviated month name (Jan..Dec) |
1256 |
-+.IP B |
1257 |
-+locale's full month name, variable length (January..December) |
1258 |
-+.IP c |
1259 |
-+locale's date and time (Sat Nov 04 12:02:33 EST 1989). The format is |
1260 |
-+the same as for |
1261 |
-+.BR ctime (3) |
1262 |
-+and so to preserve compatibility with that format, there is no fractional part |
1263 |
-+in the seconds field. |
1264 |
-+.IP d |
1265 |
-+day of month (01..31) |
1266 |
-+.IP D |
1267 |
-+date (mm/dd/yy) |
1268 |
-+.IP h |
1269 |
-+same as b |
1270 |
-+.IP j |
1271 |
-+day of year (001..366) |
1272 |
-+.IP m |
1273 |
-+month (01..12) |
1274 |
-+.IP U |
1275 |
-+week number of year with Sunday as first day of week (00..53) |
1276 |
-+.IP w |
1277 |
-+day of week (0..6) |
1278 |
-+.IP W |
1279 |
-+week number of year with Monday as first day of week (00..53) |
1280 |
-+.IP x |
1281 |
-+locale's date representation (mm/dd/yy) |
1282 |
-+.IP y |
1283 |
-+last two digits of year (00..99) |
1284 |
-+.IP Y |
1285 |
-+year (1970...) |
1286 |
-+.RE |
1287 |
-+.IP %b |
1288 |
-+The amount of disk space used for this file in 512-byte blocks. Since disk |
1289 |
-+space is allocated in multiples of the filesystem block size this is usually |
1290 |
-+greater than %s/512, but it can also be smaller if the file is a sparse file. |
1291 |
-+.IP %c |
1292 |
-+File's last status change time in the format returned by the C `ctime' |
1293 |
-+function. |
1294 |
-+.IP %C\fIk\fP |
1295 |
-+File's last status change time in the format specified by \fIk\fR, |
1296 |
-+which is the same as for %A. |
1297 |
-+.IP %d |
1298 |
-+File's depth in the directory tree; 0 means the file is a command line |
1299 |
-+argument. |
1300 |
-+.IP %D |
1301 |
-+The device number on which the file exists (the st_dev field of struct |
1302 |
-+stat), in decimal. |
1303 |
-+.IP %f |
1304 |
-+File's name with any leading directories removed (only the last element). |
1305 |
-+.IP %F |
1306 |
-+Type of the filesystem the file is on; this value can be used for |
1307 |
-+\-fstype. |
1308 |
-+.IP %g |
1309 |
-+File's group name, or numeric group ID if the group has no name. |
1310 |
-+.IP %G |
1311 |
-+File's numeric group ID. |
1312 |
-+.IP %h |
1313 |
-+Leading directories of file's name (all but the last element). |
1314 |
-+If the file name contains no slashes (since it is in the current |
1315 |
-+directory) the %h specifier expands to ".". |
1316 |
-+.IP %H |
1317 |
-+Command line argument under which file was found. |
1318 |
-+.IP %i |
1319 |
-+File's inode number (in decimal). |
1320 |
-+.IP %k |
1321 |
-+The amount of disk space used for this file in 1K blocks. Since disk space is |
1322 |
-+allocated in multiples of the filesystem block size this is usually greater |
1323 |
-+than %s/1024, but it can also be smaller if the file is a sparse file. |
1324 |
-+.IP %l |
1325 |
-+Object of symbolic link (empty string if file is not a symbolic link). |
1326 |
-+.IP %m |
1327 |
-+File's permission bits (in octal). This option uses the `traditional' |
1328 |
-+numbers which most Unix implementations use, but if your particular |
1329 |
-+implementation uses an unusual ordering of octal permissions bits, you |
1330 |
-+will see a difference between the actual value of the file's mode and |
1331 |
-+the output of %m. Normally you will want to have a leading |
1332 |
-+zero on this number, and to do this, you should use the |
1333 |
-+.B # |
1334 |
-+flag (as in, for example, `%#m'). |
1335 |
-+.IP %M |
1336 |
-+File's permissions (in symbolic form, as for |
1337 |
-+.BR ls ). |
1338 |
-+This directive is supported in findutils 4.2.5 and later. |
1339 |
-+.IP %n |
1340 |
-+Number of hard links to file. |
1341 |
-+.IP %p |
1342 |
-+File's name. |
1343 |
-+.IP %P |
1344 |
-+File's name with the name of the command line argument under which |
1345 |
-+it was found removed. |
1346 |
-+.IP %s |
1347 |
-+File's size in bytes. |
1348 |
-+.IP %S |
1349 |
-+File's sparseness. This is calculated as (BLOCKSIZE*st_blocks / |
1350 |
-+st_size). The exact value you will get for an ordinary file of a |
1351 |
-+certain length is system-dependent. However, normally sparse files |
1352 |
-+will have values less than 1.0, and files which use indirect blocks |
1353 |
-+may have a value which is greater than 1.0. The value used for |
1354 |
-+BLOCKSIZE is system-dependent, but is usually 512 bytes. If the file |
1355 |
-+size is zero, the value printed is undefined. On systems which lack |
1356 |
-+support for st_blocks, a file's sparseness is assumed to be 1.0. |
1357 |
-+.IP %t |
1358 |
-+File's last modification time in the format returned by the C `ctime' |
1359 |
-+function. |
1360 |
-+.IP %T\fIk\fP |
1361 |
-+File's last modification time in the format specified by \fIk\fR, |
1362 |
-+which is the same as for %A. |
1363 |
-+.IP %u |
1364 |
-+File's user name, or numeric user ID if the user has no name. |
1365 |
-+.IP %U |
1366 |
-+File's numeric user ID. |
1367 |
-+.IP %y |
1368 |
-+File's type (like in |
1369 |
-+.BR "ls \-l" ), |
1370 |
-+U=unknown type (shouldn't happen) |
1371 |
-+.IP %Y |
1372 |
-+File's type (like %y), plus follow symlinks: L=loop, N=nonexistent |
1373 |
-+.PP |
1374 |
-+A `%' character followed by any other character is discarded, but the |
1375 |
-+other character is printed (don't rely on this, as further format |
1376 |
-+characters may be introduced). A `%' at the end of the format |
1377 |
-+argument causes undefined behaviour since there is no following |
1378 |
-+character. In some locales, it may hide your door keys, while in |
1379 |
-+others it may remove the final page from the novel you are reading. |
1380 |
-+ |
1381 |
-+The %m and %d directives support the |
1382 |
-+.B # |
1383 |
-+, |
1384 |
-+.B 0 |
1385 |
-+and |
1386 |
-+.B + |
1387 |
-+flags, but the other directives do not, even if they |
1388 |
-+print numbers. Numeric directives that do not support these flags |
1389 |
-+include |
1390 |
-+.BR G , |
1391 |
-+.BR U , |
1392 |
-+.BR b , |
1393 |
-+.BR D , |
1394 |
-+.B k |
1395 |
-+and |
1396 |
-+.BR n . |
1397 |
-+The `\-' format flag is supported and changes the alignment of a field |
1398 |
-+from right-justified (which is the default) to left-justified. |
1399 |
-+.PP |
1400 |
-+See the |
1401 |
-+.B UNUSUAL FILENAMES |
1402 |
-+section for information about how unusual characters in filenames are handled. |
1403 |
-+ |
1404 |
-+ |
1405 |
-+.RE |
1406 |
-+.IP \-prune |
1407 |
-+True; if the file is a directory, do not descend into it. If |
1408 |
-+.B \-depth |
1409 |
-+is given, false; no effect. Because |
1410 |
-+.B \-delete |
1411 |
-+implies |
1412 |
-+.BR \-depth , |
1413 |
-+you cannot usefully use |
1414 |
-+.B \-prune |
1415 |
-+and |
1416 |
-+.B \-delete together. |
1417 |
-+ |
1418 |
-+.IP "\-quit" |
1419 |
-+Exit immediately. No child processes will be left running, but no more |
1420 |
-+paths specified on the command line will be processed. For example, |
1421 |
-+.B find /tmp/foo /tmp/bar \-print \-quit |
1422 |
-+will print only |
1423 |
-+.BR /tmp/foo . |
1424 |
-+Any command lines which have been built up with |
1425 |
-+.B \-execdir ... {} + |
1426 |
-+will be invoked before |
1427 |
-+.B find |
1428 |
-+exits. The exit status may or may not be zero, depending on whether |
1429 |
-+an error has already occurred. |
1430 |
-+ |
1431 |
-+.SS UNUSUAL FILENAMES |
1432 |
-+Many of the actions of |
1433 |
-+.B find |
1434 |
-+result in the printing of data which is under the control of other |
1435 |
-+users. This includes file names, sizes, modification times and so |
1436 |
-+forth. File names are a potential problem since they can contain any |
1437 |
-+character except `\e0' and `/'. Unusual characters in file names can |
1438 |
-+do unexpected and often undesirable things to your terminal (for |
1439 |
-+example, changing the settings of your function keys on some |
1440 |
-+terminals). Unusual characters are handled differently by various |
1441 |
-+actions, as described below. |
1442 |
-+ |
1443 |
-+.IP "\-print0, \-fprint0\" |
1444 |
-+Always print the exact filename, unchanged, even if the output is |
1445 |
-+going to a terminal. |
1446 |
-+ |
1447 |
-+.IP "\-ls, \-fls" |
1448 |
-+Unusual characters are always escaped. White space, backslash, and |
1449 |
-+double quote characters are printed using C-style escaping (for |
1450 |
-+example `\ef', `\e"'). Other unusual characters are printed using an |
1451 |
-+octal escape. Other printable characters (for |
1452 |
-+.B \-ls |
1453 |
-+and |
1454 |
-+.B \-fls |
1455 |
-+these are the characters between octal 041 and 0176) are printed as-is. |
1456 |
-+ |
1457 |
-+.IP "\-printf, \-fprintf" |
1458 |
-+If the output is not going to a terminal, it is printed as-is. |
1459 |
-+Otherwise, the result depends on which directive is in use. The |
1460 |
-+directives %D, %F, %g, %G, %H, %Y, and %y expand to values which are |
1461 |
-+not under control of files' owners, and so are printed as-is. The |
1462 |
-+directives %a, %b, %c, %d, %i, %k, %m, %M, %n, %s, %t, %u and %U have |
1463 |
-+values which are under the control of files' owners but which cannot |
1464 |
-+be used to send arbitrary data to the terminal, and so these are |
1465 |
-+printed as-is. The directives %f, %h, %l, %p and %P are quoted. This |
1466 |
-+quoting is performed in the same way as for GNU |
1467 |
-+.BR ls . |
1468 |
-+This is not the same quoting mechanism as the one used for |
1469 |
-+.B \-ls |
1470 |
-+and |
1471 |
-+.BR \-fls . |
1472 |
-+If you are able to decide what format to use for the output of |
1473 |
-+.B find |
1474 |
-+then it is normally better to use `\e0' as a terminator |
1475 |
-+than to use newline, as file names can contain white space and newline |
1476 |
-+characters. |
1477 |
-+ |
1478 |
-+.IP "\-print, \-fprint" |
1479 |
-+Quoting is handled in the same way as for |
1480 |
-+.B \-printf |
1481 |
-+and |
1482 |
-+.BR \-fprintf . |
1483 |
-+If you are using |
1484 |
-+.B find |
1485 |
-+in a script or in a situation where the matched files might have |
1486 |
-+arbitrary names, you should consider using |
1487 |
-+.B \-print0 |
1488 |
-+instead of |
1489 |
-+.BR \-print . |
1490 |
-+.P |
1491 |
-+The |
1492 |
-+.B \-ok |
1493 |
-+and |
1494 |
-+.B \-okdir |
1495 |
-+actions print the current filename as-is. This may change in a future release. |
1496 |
-+.SS OPERATORS |
1497 |
-+.P |
1498 |
-+Listed in order of decreasing precedence: |
1499 |
-+ |
1500 |
-+.IP "( \fIexpr\fR )" |
1501 |
-+Force precedence. Since parentheses are special to the shell, you |
1502 |
-+will normally need to quote them. Many of the examples in this manual |
1503 |
-+page use backslashes for this purpose: `\e(...\e)' instead of `(...)'. |
1504 |
-+ |
1505 |
-+.IP "! \fIexpr\fR" |
1506 |
-+True if \fIexpr\fR is false. This character will also usually need |
1507 |
-+protection from interpretation by the shell. |
1508 |
-+ |
1509 |
-+.IP "\-not \fIexpr\fR" |
1510 |
-+Same as ! \fIexpr\fR, but not POSIX compliant. |
1511 |
-+ |
1512 |
-+.IP "\fIexpr1 expr2\fR" |
1513 |
-+Two expressions in a row are taken to be joined with an |
1514 |
-+implied "and"; \fIexpr2\fR is not evaluated if \fIexpr1\fR is false. |
1515 |
-+ |
1516 |
-+.IP "\fIexpr1\fR \-a \fIexpr2\fR" |
1517 |
-+Same as \fIexpr1 expr2\fR. |
1518 |
-+ |
1519 |
-+.IP "\fIexpr1\fR \-and \fIexpr2\fR" |
1520 |
-+Same as \fIexpr1 expr2\fR, but not POSIX compliant. |
1521 |
-+ |
1522 |
-+.IP "\fIexpr1\fR \-o \fIexpr2\fR" |
1523 |
-+Or; \fIexpr2\fR is not evaluated if \fIexpr1\fR is true. |
1524 |
-+ |
1525 |
-+.IP "\fIexpr1\fR \-or \fIexpr2\fR" |
1526 |
-+Same as \fIexpr1\fR |
1527 |
-+.B \-o |
1528 |
-+\fIexpr2\fR, but not POSIX compliant. |
1529 |
-+ |
1530 |
-+.IP "\fIexpr1\fR , \fIexpr2\fR" |
1531 |
-+List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. The |
1532 |
-+value of \fIexpr1\fR is discarded; the value of the list is the value |
1533 |
-+of \fIexpr2\fR. The comma operator can be useful for searching for |
1534 |
-+several different types of thing, but traversing the filesystem |
1535 |
-+hierarchy only once. The |
1536 |
-+.B \-fprintf |
1537 |
-+action can be used to list the various matched items into several |
1538 |
-+different output files. |
1539 |
-+ |
1540 |
-+ |
1541 |
-+.SH "STANDARDS CONFORMANCE" |
1542 |
-+For closest compliance to the POSIX standard, you should set the |
1543 |
-+POSIXLY_CORRECT environment variable. The following options are |
1544 |
-+specified in the POSIX standard (IEEE Std 1003.1, 2003 Edition): |
1545 |
-+ |
1546 |
-+.IP \fB\-H\fR |
1547 |
-+This option is supported. |
1548 |
-+ |
1549 |
-+.IP \fB\-L\fR |
1550 |
-+This option is supported. |
1551 |
-+ |
1552 |
-+.IP \fB\-name\fR |
1553 |
-+This option is supported, but POSIX conformance depends on the |
1554 |
-+POSIX conformance of the system's |
1555 |
-+.BR fnmatch (3) |
1556 |
-+library function. As of findutils-4.2.2, shell metacharacters |
1557 |
-+(`*', `?' or `[]' for example) will match a leading `.', because |
1558 |
-+IEEE PASC interpretation 126 requires this. This is a change from |
1559 |
-+previous versions of findutils. |
1560 |
-+ |
1561 |
-+.IP \fB\-type\fR |
1562 |
-+Supported. POSIX specifies `b', `c', `d', `l', `p', `f' and `s'. |
1563 |
-+GNU find also supports `D', representing a Door, where the OS provides these. |
1564 |
-+ |
1565 |
-+.IP \fB\-ok\fR |
1566 |
-+Supported. Interpretation of the response is not locale-dependent |
1567 |
-+(see ENVIRONMENT VARIABLES). |
1568 |
-+ |
1569 |
-+.IP \fB\-newer\fR |
1570 |
-+Supported. If the file specified is a symbolic link, it is always |
1571 |
-+dereferenced. This is a change from previous behaviour, which used to |
1572 |
-+take the relevant time from the symbolic link; see the HISTORY section |
1573 |
-+below. |
1574 |
-+ |
1575 |
-+.IP \fB\-perm\fR |
1576 |
-+Supported. If the POSIXLY_CORRECT environment variable is not set, |
1577 |
-+some mode arguments (for example +a+x) which are not valid in POSIX |
1578 |
-+are supported for backward-compatibility. |
1579 |
-+ |
1580 |
-+.IP "Other predicates" |
1581 |
-+The predicates |
1582 |
-+.BR \-atime , |
1583 |
-+.BR \-ctime , |
1584 |
-+.BR \-depth , |
1585 |
-+.BR \-group , |
1586 |
-+.BR \-links , |
1587 |
-+.BR \-mtime , |
1588 |
-+.BR \-nogroup , |
1589 |
-+.BR \-nouser , |
1590 |
-+.BR \-print , |
1591 |
-+.BR \-prune , |
1592 |
-+.BR \-size , |
1593 |
-+.BR \-user |
1594 |
-+and |
1595 |
-+.B \-xdev |
1596 |
-+are all supported. |
1597 |
-+ |
1598 |
-+.P |
1599 |
-+The POSIX standard specifies parentheses `(', `)', negation `!' and the |
1600 |
-+`and' and `or' operators ( |
1601 |
-+.BR \-a , |
1602 |
-+.BR \-o ). |
1603 |
-+.P |
1604 |
-+All other options, predicates, expressions and so forth are extensions |
1605 |
-+beyond the POSIX standard. Many of these extensions are not unique to |
1606 |
-+GNU find, however. |
1607 |
-+.P |
1608 |
-+The POSIX standard requires that |
1609 |
-+.B find |
1610 |
-+detects loops: |
1611 |
-+.IP |
1612 |
-+The |
1613 |
-+.B find |
1614 |
-+utility shall detect infinite loops; that is, entering a |
1615 |
-+previously visited directory that is an ancestor of the last file |
1616 |
-+encountered. When it detects an infinite loop, find shall write a |
1617 |
-+diagnostic message to standard error and shall either recover its |
1618 |
-+position in the hierarchy or terminate. |
1619 |
-+.P |
1620 |
-+GNU |
1621 |
-+.B find |
1622 |
-+complies with these requirements. The link count of |
1623 |
-+directories which contain entries which are hard links to an ancestor |
1624 |
-+will often be lower than they otherwise should be. This can mean that |
1625 |
-+GNU find will sometimes optimise away the visiting of a subdirectory |
1626 |
-+which is actually a link to an ancestor. Since |
1627 |
-+.B find |
1628 |
-+does not actually enter such a subdirectory, it is allowed to avoid |
1629 |
-+emitting a diagnostic message. Although this behaviour may be |
1630 |
-+somewhat confusing, it is unlikely that anybody actually depends on |
1631 |
-+this behaviour. If the leaf optimisation has been turned off with |
1632 |
-+.BR \-noleaf , |
1633 |
-+the directory entry will always be examined and the diagnostic message |
1634 |
-+will be issued where it is appropriate. Symbolic links cannot be used |
1635 |
-+to create filesystem cycles as such, but if the |
1636 |
-+.B \-L |
1637 |
-+option or the |
1638 |
-+.B \-follow |
1639 |
-+option is in use, a diagnostic message is issued when |
1640 |
-+.B find |
1641 |
-+encounters a loop of symbolic links. As with loops containing hard |
1642 |
-+links, the leaf optimisation will often mean that |
1643 |
-+.B find |
1644 |
-+knows that it doesn't need to call |
1645 |
-+.I stat() |
1646 |
-+or |
1647 |
-+.I chdir() |
1648 |
-+on the symbolic link, so this diagnostic is frequently not necessary. |
1649 |
-+.P |
1650 |
-+The |
1651 |
-+.B \-d |
1652 |
-+option is supported for compatibility with various BSD systems, |
1653 |
-+but you should use the POSIX-compliant option |
1654 |
-+.B \-depth |
1655 |
-+instead. |
1656 |
-+.P |
1657 |
-+The POSIXLY_CORRECT environment variable does not affect the behaviour |
1658 |
-+of the |
1659 |
-+.B \-regex |
1660 |
-+or |
1661 |
-+.B \-iregex |
1662 |
-+tests because those tests aren't specified in the POSIX standard. |
1663 |
-+.SH "ENVIRONMENT VARIABLES" |
1664 |
-+ |
1665 |
-+.IP LANG |
1666 |
-+Provides a default value for the internationalization variables that |
1667 |
-+are unset or null. |
1668 |
-+ |
1669 |
-+.IP LC_ALL |
1670 |
-+If set to a non-empty string value, override the values of all the |
1671 |
-+other internationalization variables. |
1672 |
-+ |
1673 |
-+.IP LC_COLLATE |
1674 |
-+The POSIX standard specifies that this variable affects the pattern |
1675 |
-+matching to be used for the |
1676 |
-+.B \-name |
1677 |
-+option. GNU find uses the |
1678 |
-+.BR fnmatch (3) |
1679 |
-+library function, and so support for `LC_COLLATE' depends on the |
1680 |
-+system library. |
1681 |
-+ |
1682 |
-+.IP |
1683 |
-+POSIX also specifies that the `LC_COLLATE' environment |
1684 |
-+variable affects the interpretation of the user's response to the |
1685 |
-+query issued by |
1686 |
-+.BR \-ok' , |
1687 |
-+but this is not the case for GNU find. |
1688 |
-+ |
1689 |
-+.IP LC_CTYPE |
1690 |
-+This variable affects the treatment of character classes used with |
1691 |
-+the |
1692 |
-+.B \-name |
1693 |
-+test, if the system's |
1694 |
-+.BR fnmatch (3) |
1695 |
-+library function supports this. It has no effect on the behaviour |
1696 |
-+of the |
1697 |
-+.B \-ok |
1698 |
-+expression. |
1699 |
-+ |
1700 |
-+.IP LC_MESSAGES |
1701 |
-+Determines the locale to be used for internationalised messages. |
1702 |
-+ |
1703 |
-+.IP NLSPATH |
1704 |
-+Determines the location of the internationalisation message catalogues. |
1705 |
-+ |
1706 |
-+.IP PATH |
1707 |
-+Affects the directories which are searched to find the executables |
1708 |
-+invoked by |
1709 |
-+.BR \-exec , |
1710 |
-+.BR \-execdir , |
1711 |
-+.B \-ok |
1712 |
-+and |
1713 |
-+.BR \-okdir . |
1714 |
-+ |
1715 |
-+.IP POSIXLY_CORRECT |
1716 |
-+Determines the block size used by |
1717 |
-+.B \-ls |
1718 |
-+and |
1719 |
-+.BR \-fls . |
1720 |
-+If |
1721 |
-+.B POSIXLY_CORRECT |
1722 |
-+is set, blocks are units of 512 bytes. Otherwise |
1723 |
-+they are units of 1024 bytes. |
1724 |
-+.IP |
1725 |
-+Setting this variable also turns off |
1726 |
-+warning messages (that is, implies |
1727 |
-+.BR \-nowarn ) |
1728 |
-+by default, because POSIX requires that apart from |
1729 |
-+the output for |
1730 |
-+.BR \-ok , |
1731 |
-+all messages printed on stderr are diagnositcs and must result in a |
1732 |
-+non-zero exit status. |
1733 |
-+.IP |
1734 |
-+When POSIXLY_CORRECT is not set, |
1735 |
-+.B \-perm |
1736 |
-++zzz |
1737 |
-+is treated just like |
1738 |
-+.B \-perm |
1739 |
-+/zzz |
1740 |
-+if |
1741 |
-++zzz is not a valid symbolic mode. When POSIXLY_CORRECT is set, such |
1742 |
-+constructs are treated as an error. |
1743 |
-+ |
1744 |
-+.IP TZ |
1745 |
-+Affects the time zone used for some of the time-related format |
1746 |
-+directives of |
1747 |
-+.B \-printf |
1748 |
-+and |
1749 |
-+.BR \-fprintf . |
1750 |
-+.SH "EXAMPLES" |
1751 |
-+.nf |
1752 |
-+.B find /tmp \-name core \-type f \-print | xargs /bin/rm \-f |
1753 |
-+ |
1754 |
-+.fi |
1755 |
-+Find files named |
1756 |
-+.B core |
1757 |
-+in or below the directory |
1758 |
-+.B /tmp |
1759 |
-+and delete them. Note that this will work incorrectly if there are |
1760 |
-+any filenames containing newlines, single or double quotes, or spaces. |
1761 |
-+.P |
1762 |
-+.B find /tmp \-name core \-type f \-print0 | xargs \-0 /bin/rm \-f |
1763 |
-+ |
1764 |
-+.fi |
1765 |
-+Find files named |
1766 |
-+.B core |
1767 |
-+in or below the directory |
1768 |
-+.B /tmp |
1769 |
-+and delete them, processing filenames in such a way that file or |
1770 |
-+directory names containing single or double quotes, spaces or newlines |
1771 |
-+are correctly handled. The |
1772 |
-+.B \-name |
1773 |
-+test comes before the |
1774 |
-+.B \-type |
1775 |
-+test in order to avoid having to call |
1776 |
-+.B stat(2) |
1777 |
-+on every file. |
1778 |
-+ |
1779 |
-+.P |
1780 |
-+.nf |
1781 |
-+.B find . \-type f \-exec file \(aq{}\(aq \e\; |
1782 |
-+ |
1783 |
-+.fi |
1784 |
-+Runs `file' on every file in or below the current directory. Notice |
1785 |
-+that the braces are enclosed in single quote marks to protect them |
1786 |
-+from interpretation as shell script punctuation. The semicolon is |
1787 |
-+similarly protected by the use of a backslash, though single quotes |
1788 |
-+could have been used in that case also. |
1789 |
-+ |
1790 |
-+.P |
1791 |
-+.nf |
1792 |
-+.B find / \e |
1793 |
-+.B \e( \-perm \-4000 \-fprintf /root/suid.txt "%#m %u %p\en" \e) , \e |
1794 |
-+.B \e( \-size +100M \-fprintf /root/big.txt "%\-10s %p\en" \e) |
1795 |
-+ |
1796 |
-+.fi |
1797 |
-+Traverse the filesystem just once, listing setuid files and |
1798 |
-+directories into |
1799 |
-+.B /root/suid.txt |
1800 |
-+and large files into |
1801 |
-+.BR /root/big.txt . |
1802 |
-+ |
1803 |
-+.P |
1804 |
-+.nf |
1805 |
-+.B find $HOME \-mtime 0 |
1806 |
-+ |
1807 |
-+.fi |
1808 |
-+Search for files in your home directory which have been modified in |
1809 |
-+the last twenty-four hours. This command works this way because the |
1810 |
-+time since each file was last modified is divided by 24 hours and any |
1811 |
-+remainder is discarded. That means that to match |
1812 |
-+.B \-mtime |
1813 |
-+.BR 0 , |
1814 |
-+a file will have to have a modification in the past which is less than |
1815 |
-+24 hours ago. |
1816 |
-+ |
1817 |
-+.P |
1818 |
-+.nf |
1819 |
-+.B find /sbin /usr/sbin -executable \e! -readable \-print |
1820 |
-+ |
1821 |
-+.fi |
1822 |
-+Search for files which are executable but not readable. |
1823 |
-+ |
1824 |
-+.P |
1825 |
-+.nf |
1826 |
-+.B find . \-perm 664 |
1827 |
-+ |
1828 |
-+.fi |
1829 |
-+Search for files which have read and write permission for their owner, |
1830 |
-+and group, but which other users can read but not write to. Files |
1831 |
-+which meet these criteria but have other permissions bits set (for |
1832 |
-+example if someone can execute the file) will not be matched. |
1833 |
-+ |
1834 |
-+.P |
1835 |
-+.nf |
1836 |
-+.B find . \-perm \-664 |
1837 |
-+ |
1838 |
-+.fi |
1839 |
-+Search for files which have read and write permission for their owner |
1840 |
-+and group, and which other users can read, without regard to the |
1841 |
-+presence of any extra permission bits (for example the executable |
1842 |
-+bit). This will match a file which has mode 0777, for example. |
1843 |
-+ |
1844 |
-+.P |
1845 |
-+.nf |
1846 |
-+.B find . \-perm /222 |
1847 |
-+ |
1848 |
-+.fi |
1849 |
-+Search for files which are writable by somebody (their owner, or |
1850 |
-+their group, or anybody else). |
1851 |
-+ |
1852 |
-+.P |
1853 |
-+.nf |
1854 |
-+.B find . \-perm /220 |
1855 |
-+.B find . \-perm /u+w,g+w |
1856 |
-+.B find . \-perm /u=w,g=w |
1857 |
-+ |
1858 |
-+.fi |
1859 |
-+All three of these commands do the same thing, but the first one uses |
1860 |
-+the octal representation of the file mode, and the other two use the |
1861 |
-+symbolic form. These commands all search for files which are |
1862 |
-+writable by either their owner or their group. The files don't have |
1863 |
-+to be writable by both the owner and group to be matched; either will |
1864 |
-+do. |
1865 |
-+ |
1866 |
-+.P |
1867 |
-+.nf |
1868 |
-+.B find . \-perm \-220 |
1869 |
-+.B find . \-perm \-g+w,u+w |
1870 |
-+ |
1871 |
-+.fi |
1872 |
-+Both these commands do the same thing; search for files which are |
1873 |
-+writable by both their owner and their group. |
1874 |
-+ |
1875 |
-+.P |
1876 |
-+.nf |
1877 |
-+.B find . \-perm \-444 \-perm /222 ! \-perm /111 |
1878 |
-+.B find . \-perm \-a+r \-perm /a+w ! \-perm /a+x |
1879 |
-+ |
1880 |
-+.fi |
1881 |
-+These two commands both search for files that are readable for |
1882 |
-+everybody ( |
1883 |
-+.B \-perm \-444 |
1884 |
-+or |
1885 |
-+.BR "\-perm \-a+r" ), |
1886 |
-+have at least one write bit |
1887 |
-+set ( |
1888 |
-+.B \-perm /222 |
1889 |
-+or |
1890 |
-+.BR "\-perm /a+w" ) |
1891 |
-+but are not executable for anybody ( |
1892 |
-+.B ! \-perm /111 |
1893 |
-+and |
1894 |
-+.B ! \-perm /a+x |
1895 |
-+respectively). |
1896 |
-+ |
1897 |
-+.P |
1898 |
-+.nf |
1899 |
-+.B cd /source-dir |
1900 |
-+.B find . \-name .snapshot \-prune \-o \e( \e! \-name "*~" \-print0 \e)| |
1901 |
-+.B cpio \-pmd0 /dest-dir |
1902 |
-+ |
1903 |
-+.fi |
1904 |
-+This command copies the contents of |
1905 |
-+.B /source-dir |
1906 |
-+to |
1907 |
-+.BR /dest-dir , |
1908 |
-+but omits files and directories named |
1909 |
-+.B .snapshot |
1910 |
-+(and anything in them). It also omits files or directories whose name |
1911 |
-+ends in |
1912 |
-+.BR ~ , |
1913 |
-+but not their contents. The construct |
1914 |
-+.B \-prune \-o \e( ... \-print0 \e) |
1915 |
-+is quite common. The idea here is that the expression before |
1916 |
-+.B \-prune |
1917 |
-+matches things which are to be pruned. However, the |
1918 |
-+.B \-prune |
1919 |
-+action itself returns true, so the following |
1920 |
-+.B \-o |
1921 |
-+ensures that the right hand side is evaluated only for those |
1922 |
-+directories which didn't get pruned (the contents of the pruned |
1923 |
-+directories are not even visited, so their contents are irrelevant). |
1924 |
-+The expression on the right hand side of the |
1925 |
-+.B \-o |
1926 |
-+is in parentheses only for clarity. It emphasises that the |
1927 |
-+.B \-print0 |
1928 |
-+action takes place only for things that didn't have |
1929 |
-+.B \-prune |
1930 |
-+applied to them. Because the default `and' condition between tests |
1931 |
-+binds more tightly than |
1932 |
-+.BR \-o , |
1933 |
-+this is the default anyway, but the parentheses help to show |
1934 |
-+what is going on. |
1935 |
-+ |
1936 |
-+.SH EXIT STATUS |
1937 |
-+.PP |
1938 |
-+.B find |
1939 |
-+exits with status 0 if all files are processed successfully, greater |
1940 |
-+than 0 if errors occur. This is deliberately a very broad |
1941 |
-+description, but if the return value is non-zero, you should not rely |
1942 |
-+on the correctness of the results of |
1943 |
-+.BR find . |
1944 |
-+ |
1945 |
-+.SH "SEE ALSO" |
1946 |
-+\fBlocate\fP(1), \fBlocatedb\fP(5), \fBupdatedb\fP(1), \fBxargs\fP(1), |
1947 |
-+\fBchmod\fP(1), \fBfnmatch\fP(3), \fBregex\fP(7), \fBstat\fP(2), |
1948 |
-+\fBlstat\fP(2), \fBls\fP(1), \fBprintf\fP(3), \fBstrftime\fP(3), |
1949 |
-+\fBctime\fP(3), \fBFinding Files\fP (on-line in Info, or printed). |
1950 |
-+.SH "HISTORY" |
1951 |
-+As of findutils-4.2.2, shell metacharacters (`*', `?' or `[]' for |
1952 |
-+example) used in filename patterns will match a leading `.', because |
1953 |
-+IEEE POSIX interpretation 126 requires this. |
1954 |
-+.P |
1955 |
-+The syntax |
1956 |
-+\.B \-perm +MODE |
1957 |
-+was deprecated in findutils-4.2.21, in favour of |
1958 |
-+\.B \-perm |
1959 |
-+.BR /MODE . |
1960 |
-+As of findutils-4.3.3, |
1961 |
-+.B \-perm /000 |
1962 |
-+now matches all files instead of none. |
1963 |
-+.P |
1964 |
-+Nanosecond-resolution |
1965 |
-+timestamps were implemented in findutils-4.3.3. |
1966 |
-+.P |
1967 |
-+As of findutils-4.3.11, the |
1968 |
-+.B \-delete |
1969 |
-+action sets |
1970 |
-+.BR find 's |
1971 |
-+exit status to a nonzero value when it fails. |
1972 |
-+However, |
1973 |
-+.B find |
1974 |
-+will not exit immediately. Previously, |
1975 |
-+.BR find 's |
1976 |
-+exit status was unaffected by the failure of |
1977 |
-+.BR \-delete . |
1978 |
-+.TS |
1979 |
-+l l l . |
1980 |
-+Feature Added in Also occurs in |
1981 |
-+\-newerXY 4.3.3 BSD |
1982 |
-+\-D 4.3.1 |
1983 |
-+\-O 4.3.1 |
1984 |
-+\-readable 4.3.0 |
1985 |
-+\-writable 4.3.0 |
1986 |
-+\-executable 4.3.0 |
1987 |
-+\-regextype 4.2.24 |
1988 |
-+\-exec ... + 4.2.12 POSIX |
1989 |
-+\-execdir 4.2.12 BSD |
1990 |
-+\-okdir 4.2.12 |
1991 |
-+\-samefile 4.2.11 |
1992 |
-+\-H 4.2.5 POSIX |
1993 |
-+\-L 4.2.5 POSIX |
1994 |
-+\-P 4.2.5 BSD |
1995 |
-+\-delete 4.2.3 |
1996 |
-+\-quit 4.2.3 |
1997 |
-+\-d 4.2.3 BSD |
1998 |
-+\-wholename 4.2.0 |
1999 |
-+\-iwholename 4.2.0 |
2000 |
-+\-ignore_readdir_race 4.2.0 |
2001 |
-+\-fls 4.0 |
2002 |
-+\-ilname 3.8 |
2003 |
-+\-iname 3.8 |
2004 |
-+\-ipath 3.8 |
2005 |
-+\-iregex 3.8 |
2006 |
-+.TE |
2007 |
-+.SH "NON-BUGS" |
2008 |
-+.nf |
2009 |
-+.B $ find . \-name *.c \-print |
2010 |
-+find: paths must precede expression |
2011 |
-+Usage: find [\-H] [\-L] [\-P] [\-Olevel] [\-D help|tree|search|stat|rates|opt|exec] [path...] [expression] |
2012 |
-+.fi |
2013 |
-+.P |
2014 |
-+This happens because |
2015 |
-+.I *.c |
2016 |
-+has been expanded by the shell |
2017 |
-+resulting in |
2018 |
-+.B find |
2019 |
-+actually receiving a command line like this: |
2020 |
-+.nf |
2021 |
-+ |
2022 |
-+.B find . \-name bigram.c code.c frcode.c locate.c \-print |
2023 |
-+ |
2024 |
-+.fi |
2025 |
-+That command is of course not going to work. Instead of doing things |
2026 |
-+this way, you should enclose the pattern in quotes or escape the wildcard: |
2027 |
-+.nf |
2028 |
-+.B $ find . \-name \e*.c \-print |
2029 |
-+.fi |
2030 |
-+ |
2031 |
-+.SH "BUGS" |
2032 |
-+.P |
2033 |
-+There are security problems inherent in the behaviour that the POSIX |
2034 |
-+standard specifies for |
2035 |
-+.BR find , |
2036 |
-+which therefore cannot be fixed. For example, the |
2037 |
-+.B \-exec |
2038 |
-+action is |
2039 |
-+inherently insecure, and |
2040 |
-+.B \-execdir |
2041 |
-+should be used instead. |
2042 |
-+Please see \fBFinding Files\fP for more information. |
2043 |
-+.P |
2044 |
-+The environment variable |
2045 |
-+.B LC_COLLATE |
2046 |
-+has no effect on the |
2047 |
-+.B \-ok |
2048 |
-+action. |
2049 |
-+.P |
2050 |
-+The best way to report a bug is to use the form at |
2051 |
-+http://savannah.gnu.org/bugs/?group=findutils. |
2052 |
-+The reason for this is that you will then be able to track progress in |
2053 |
-+fixing the problem. Other comments about \fBfind\fP(1) and about |
2054 |
-+the findutils package in general can be sent to the |
2055 |
-+.I bug\-findutils |
2056 |
-+mailing list. To join the list, send email to |
2057 |
-+.IR bug\-findutils\-request@×××.org . |
2058 |
diff -purN findutils-4.3.12.orig/find/find.c findutils-4.3.12/find/find.c |
2059 |
--- findutils-4.3.12.orig/find/find.c 2007-12-19 16:12:34.000000000 -0500 |
2060 |
+++ findutils-4.3.12/find/find.c 2008-01-30 08:46:05.754843619 -0500 |
2061 |
@@ -2154,7760 +123,304 @@ |
2062 |
boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */ |
2063 |
unsigned int idx; /* Which entry are we on? */ |
2064 |
struct stat stat_buf; |
2065 |
-diff -purN findutils-4.3.12.orig/find/find.c.orig findutils-4.3.12/find/find.c.orig |
2066 |
---- findutils-4.3.12.orig/find/find.c.orig 1969-12-31 19:00:00.000000000 -0500 |
2067 |
-+++ findutils-4.3.12/find/find.c.orig 2007-12-19 16:12:34.000000000 -0500 |
2068 |
-@@ -0,0 +1,1537 @@ |
2069 |
-+/* find -- search for files in a directory hierarchy |
2070 |
-+ Copyright (C) 1990, 91, 92, 93, 94, 2000, |
2071 |
-+ 2003, 2004, 2005, 2007 Free Software Foundation, Inc. |
2072 |
-+ |
2073 |
-+ This program is free software: you can redistribute it and/or modify |
2074 |
-+ it under the terms of the GNU General Public License as published by |
2075 |
-+ the Free Software Foundation, either version 3 of the License, or |
2076 |
-+ (at your option) any later version. |
2077 |
-+ |
2078 |
-+ This program is distributed in the hope that it will be useful, |
2079 |
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
2080 |
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2081 |
-+ GNU General Public License for more details. |
2082 |
-+ |
2083 |
-+ You should have received a copy of the GNU General Public License |
2084 |
-+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
2085 |
-+*/ |
2086 |
-+/* GNU find was written by Eric Decker <cire@×××××.com>, |
2087 |
-+ with enhancements by David MacKenzie <djm@×××.org>, |
2088 |
-+ Jay Plett <jay@××××××××××××××××××××.us>, |
2089 |
-+ and Tim Wood <axolotl!tim@××××.com>. |
2090 |
-+ The idea for -print0 and xargs -0 came from |
2091 |
-+ Dan Bernstein <brnstnd@×××××××××××××××.edu>. |
2092 |
-+ Improvements have been made by James Youngman <jay@×××.org>. |
2093 |
-+*/ |
2094 |
-+ |
2095 |
-+ |
2096 |
-+#include <config.h> |
2097 |
-+#include "defs.h" |
2098 |
-+ |
2099 |
-+#define USE_SAFE_CHDIR 1 |
2100 |
-+#undef STAT_MOUNTPOINTS |
2101 |
-+ |
2102 |
-+ |
2103 |
-+#include <errno.h> |
2104 |
-+#include <assert.h> |
2105 |
-+ |
2106 |
-+#include <sys/stat.h> |
2107 |
-+#include <fcntl.h> |
2108 |
-+#include <openat.h> |
2109 |
-+ |
2110 |
-+#include "xalloc.h" |
2111 |
-+#include "human.h" |
2112 |
-+#include "canonicalize.h" |
2113 |
-+#include <modetype.h> |
2114 |
-+ |
2115 |
-+#include "closein.h" |
2116 |
-+#include "savedirinfo.h" |
2117 |
-+#include "buildcmd.h" |
2118 |
-+#include "dirname.h" |
2119 |
-+#include "quote.h" |
2120 |
-+#include "quotearg.h" |
2121 |
-+#include "xgetcwd.h" |
2122 |
-+#include "error.h" |
2123 |
-+ |
2124 |
-+#ifdef HAVE_LOCALE_H |
2125 |
-+#include <locale.h> |
2126 |
-+#endif |
2127 |
-+ |
2128 |
-+#if ENABLE_NLS |
2129 |
-+# include <libintl.h> |
2130 |
-+# define _(Text) gettext (Text) |
2131 |
-+#else |
2132 |
-+# define _(Text) Text |
2133 |
-+#define textdomain(Domain) |
2134 |
-+#define bindtextdomain(Package, Directory) |
2135 |
-+#define ngettext(singular,plural,n) ((1==n) ? singular : plural) |
2136 |
-+#endif |
2137 |
-+#ifdef gettext_noop |
2138 |
-+# define N_(String) gettext_noop (String) |
2139 |
-+#else |
2140 |
-+/* See locate.c for explanation as to why not use (String) */ |
2141 |
-+# define N_(String) String |
2142 |
-+#endif |
2143 |
-+ |
2144 |
-+#ifdef STAT_MOUNTPOINTS |
2145 |
-+static void init_mounted_dev_list(int mandatory); |
2146 |
-+#endif |
2147 |
-+ |
2148 |
-+static void process_top_path PARAMS((char *pathname, mode_t mode)); |
2149 |
-+static int process_path PARAMS((char *pathname, char *name, boolean leaf, char *parent, mode_t type)); |
2150 |
-+static void process_dir PARAMS((char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)); |
2151 |
-+ |
2152 |
-+ |
2153 |
-+ |
2154 |
-+/* Name this program was run with. */ |
2155 |
-+char *program_name; |
2156 |
-+ |
2157 |
-+/* A file descriptor open to the initial working directory. |
2158 |
-+ Doing it this way allows us to work when the i.w.d. has |
2159 |
-+ unreadable parents. */ |
2160 |
-+int starting_desc; |
2161 |
-+ |
2162 |
-+/* The stat buffer of the initial working directory. */ |
2163 |
-+static struct stat starting_stat_buf; |
2164 |
-+ |
2165 |
-+enum ChdirSymlinkHandling |
2166 |
-+ { |
2167 |
-+ SymlinkHandleDefault, /* Normally the right choice */ |
2168 |
-+ SymlinkFollowOk /* see comment in process_top_path() */ |
2169 |
-+ }; |
2170 |
-+ |
2171 |
-+ |
2172 |
-+enum TraversalDirection |
2173 |
-+ { |
2174 |
-+ TraversingUp, |
2175 |
-+ TraversingDown |
2176 |
-+ }; |
2177 |
-+ |
2178 |
-+enum WdSanityCheckFatality |
2179 |
-+ { |
2180 |
-+ FATAL_IF_SANITY_CHECK_FAILS, |
2181 |
-+ RETRY_IF_SANITY_CHECK_FAILS, |
2182 |
-+ NON_FATAL_IF_SANITY_CHECK_FAILS |
2183 |
-+ }; |
2184 |
-+ |
2185 |
-+ |
2186 |
-+int get_current_dirfd(void) |
2187 |
-+{ |
2188 |
-+ return AT_FDCWD; |
2189 |
-+} |
2190 |
+diff -purN findutils-4.3.12.orig/find/parser.c findutils-4.3.12/find/parser.c |
2191 |
+--- findutils-4.3.12.orig/find/parser.c 2007-12-19 16:12:34.000000000 -0500 |
2192 |
++++ findutils-4.3.12/find/parser.c 2008-01-30 08:46:05.754843619 -0500 |
2193 |
+@@ -53,6 +53,13 @@ |
2194 |
+ #include <unistd.h> |
2195 |
+ #include <sys/stat.h> |
2196 |
+ |
2197 |
++#ifdef WITH_SELINUX |
2198 |
++#include <selinux/selinux.h> |
2199 |
++int optionh_getfilecon(const char *name, security_context_t *p); |
2200 |
++int optionl_getfilecon(const char *name, security_context_t *p); |
2201 |
++int optionp_getfilecon(const char *name, security_context_t *p); |
2202 |
++#endif /*WITH_SELINUX*/ |
2203 |
+ |
2204 |
+ #if ENABLE_NLS |
2205 |
+ # include <libintl.h> |
2206 |
+ # define _(Text) gettext (Text) |
2207 |
+@@ -156,6 +163,9 @@ static boolean parse_noignore_race PARAM |
2208 |
+ static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
2209 |
+ static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
2210 |
+ static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
2211 |
++#ifdef WITH_SELINUX |
2212 |
++static boolean parse_scontext PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
2213 |
++#endif /*WITH_SELINUX*/ |
2214 |
+ |
2215 |
+ boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
2216 |
+ |
2217 |
+@@ -341,6 +351,8 @@ static struct parser_table const parse_t |
2218 |
+ {ARG_TEST, "-help", parse_help, NULL}, /* GNU */ |
2219 |
+ {ARG_TEST, "version", parse_version, NULL}, /* GNU */ |
2220 |
+ {ARG_TEST, "-version", parse_version, NULL}, /* GNU */ |
2221 |
++ {ARG_TEST, "context", parse_scontext, pred_scontext}, /* SELinux */ |
2222 |
++ {ARG_TEST, "-context", parse_scontext, pred_scontext}, /* SELinux */ |
2223 |
+ {0, 0, 0, 0} |
2224 |
+ }; |
2225 |
+ |
2226 |
+@@ -452,10 +464,16 @@ set_follow_state(enum SymlinkOption opt) |
2227 |
+ case SYMLINK_ALWAYS_DEREF: /* -L */ |
2228 |
+ options.xstat = optionl_stat; |
2229 |
+ options.no_leaf_check = true; |
2230 |
++#ifdef WITH_SELINUX |
2231 |
++ options.x_getfilecon = optionl_getfilecon; |
2232 |
++#endif /* WITH_SELINUX */ |
2233 |
+ break; |
2234 |
+ |
2235 |
+ case SYMLINK_NEVER_DEREF: /* -P (default) */ |
2236 |
+ options.xstat = optionp_stat; |
2237 |
++#ifdef WITH_SELINUX |
2238 |
++ options.x_getfilecon = optionp_getfilecon; |
2239 |
++#endif /* WITH_SELINUX */ |
2240 |
+ /* Can't turn no_leaf_check off because the user might have specified |
2241 |
+ * -noleaf anyway |
2242 |
+ */ |
2243 |
+@@ -464,6 +482,9 @@ set_follow_state(enum SymlinkOption opt) |
2244 |
+ case SYMLINK_DEREF_ARGSONLY: /* -H */ |
2245 |
+ options.xstat = optionh_stat; |
2246 |
+ options.no_leaf_check = true; |
2247 |
++#ifdef WITH_SELINUX |
2248 |
++ options.x_getfilecon = optionh_getfilecon; |
2249 |
++#endif /* WITH_SELINUX */ |
2250 |
+ } |
2251 |
+ } |
2252 |
+ options.symlink_handling = opt; |
2253 |
+@@ -667,6 +688,94 @@ collect_arg_stat_info(char **argv, int * |
2254 |
+ |
2255 |
+ The predicate structure is updated with the new information. */ |
2256 |
+ |
2257 |
++#ifdef WITH_SELINUX |
2258 |
+ |
2259 |
-+int |
2260 |
-+main (int argc, char **argv) |
2261 |
++static int |
2262 |
++fallback_getfilecon(const char *name, security_context_t *p, int prev_rv) |
2263 |
+{ |
2264 |
-+ int i; |
2265 |
-+ int end_of_leading_options = 0; /* First arg after any -H/-L etc. */ |
2266 |
-+ struct predicate *eval_tree; |
2267 |
-+ |
2268 |
-+ program_name = argv[0]; |
2269 |
-+ state.exit_status = 0; |
2270 |
-+ |
2271 |
-+ /* Set the option defaults before we do the locale |
2272 |
-+ * initialisation as check_nofollow() needs to be executed in the |
2273 |
-+ * POSIX locale. |
2274 |
++ /* Our original getfilecon() call failed. Perhaps we can't follow a |
2275 |
++ * symbolic link. If that might be the problem, lgetfilecon() the link. |
2276 |
++ * Otherwise, admit defeat. |
2277 |
+ */ |
2278 |
-+ set_option_defaults(&options); |
2279 |
-+ |
2280 |
-+#ifdef HAVE_SETLOCALE |
2281 |
-+ setlocale (LC_ALL, ""); |
2282 |
-+#endif |
2283 |
-+ bindtextdomain (PACKAGE, LOCALEDIR); |
2284 |
-+ textdomain (PACKAGE); |
2285 |
-+ atexit (close_stdin); |
2286 |
-+ |
2287 |
-+ /* Check for -P, -H or -L options. */ |
2288 |
-+ end_of_leading_options = process_leading_options(argc, argv); |
2289 |
-+ |
2290 |
-+ if (options.debug_options & DebugStat) |
2291 |
-+ options.xstat = debug_stat; |
2292 |
-+ |
2293 |
-+#ifdef DEBUG |
2294 |
-+ fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start)); |
2295 |
-+#endif /* DEBUG */ |
2296 |
-+ |
2297 |
-+ /* state.cwd_dir_fd has to be initialised before we call build_expression_tree() |
2298 |
-+ * because command-line parsing may lead us to stat some files. |
2299 |
-+ */ |
2300 |
-+ state.cwd_dir_fd = AT_FDCWD; |
2301 |
-+ |
2302 |
-+ /* We are now processing the part of the "find" command line |
2303 |
-+ * after the -H/-L options (if any). |
2304 |
-+ */ |
2305 |
-+ eval_tree = build_expression_tree(argc, argv, end_of_leading_options); |
2306 |
-+ |
2307 |
-+ |
2308 |
-+ /* safely_chdir() needs to check that it has ended up in the right place. |
2309 |
-+ * To avoid bailing out when something gets automounted, it checks if |
2310 |
-+ * the target directory appears to have had a directory mounted on it as |
2311 |
-+ * we chdir()ed. The problem with this is that in order to notice that |
2312 |
-+ * a file system was mounted, we would need to lstat() all the mount points. |
2313 |
-+ * That strategy loses if our machine is a client of a dead NFS server. |
2314 |
-+ * |
2315 |
-+ * Hence if safely_chdir() and wd_sanity_check() can manage without needing |
2316 |
-+ * to know the mounted device list, we do that. |
2317 |
-+ */ |
2318 |
-+ if (!options.open_nofollow_available) |
2319 |
-+ { |
2320 |
-+#ifdef STAT_MOUNTPOINTS |
2321 |
-+ init_mounted_dev_list(0); |
2322 |
-+#endif |
2323 |
-+ } |
2324 |
-+ |
2325 |
-+ |
2326 |
-+ starting_desc = open (".", O_RDONLY |
2327 |
-+#if defined O_LARGEFILE |
2328 |
-+ |O_LARGEFILE |
2329 |
-+#endif |
2330 |
-+ ); |
2331 |
-+ if (0 <= starting_desc && fchdir (starting_desc) != 0) |
2332 |
-+ { |
2333 |
-+ close (starting_desc); |
2334 |
-+ starting_desc = -1; |
2335 |
-+ } |
2336 |
-+ |
2337 |
-+ if (starting_desc < 0) |
2338 |
-+ { |
2339 |
-+ starting_dir = xgetcwd (); |
2340 |
-+ if (! starting_dir) |
2341 |
-+ error (1, errno, _("cannot get current directory")); |
2342 |
-+ } |
2343 |
-+ set_stat_placeholders(&starting_stat_buf); |
2344 |
-+ if ((*options.xstat) (".", &starting_stat_buf) != 0) |
2345 |
-+ error (1, errno, _("cannot stat current directory")); |
2346 |
-+ |
2347 |
-+ /* If no paths are given, default to ".". */ |
2348 |
-+ for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++) |
2349 |
-+ { |
2350 |
-+ process_top_path (argv[i], 0); |
2351 |
-+ } |
2352 |
-+ |
2353 |
-+ /* If there were no path arguments, default to ".". */ |
2354 |
-+ if (i == end_of_leading_options) |
2355 |
-+ { |
2356 |
-+ /* |
2357 |
-+ * We use a temporary variable here because some actions modify |
2358 |
-+ * the path temporarily. Hence if we use a string constant, |
2359 |
-+ * we get a coredump. The best example of this is if we say |
2360 |
-+ * "find -printf %H" (note, not "find . -printf %H"). |
2361 |
-+ */ |
2362 |
-+ char defaultpath[2] = "."; |
2363 |
-+ process_top_path (defaultpath, 0); |
2364 |
-+ } |
2365 |
-+ |
2366 |
-+ /* If "-exec ... {} +" has been used, there may be some |
2367 |
-+ * partially-full command lines which have been built, |
2368 |
-+ * but which are not yet complete. Execute those now. |
2369 |
-+ */ |
2370 |
-+ show_success_rates(eval_tree); |
2371 |
-+ cleanup(); |
2372 |
-+ return state.exit_status; |
2373 |
-+} |
2374 |
-+ |
2375 |
-+boolean is_fts_enabled(int *ftsoptions) |
2376 |
-+{ |
2377 |
-+ /* this version of find (i.e. this main()) does not use fts. */ |
2378 |
-+ *ftsoptions = 0; |
2379 |
-+ return false; |
2380 |
-+} |
2381 |
-+ |
2382 |
-+ |
2383 |
-+static char * |
2384 |
-+specific_dirname(const char *dir) |
2385 |
-+{ |
2386 |
-+ char dirbuf[1024]; |
2387 |
-+ |
2388 |
-+ if (0 == strcmp(".", dir)) |
2389 |
-+ { |
2390 |
-+ /* OK, what's '.'? */ |
2391 |
-+ if (NULL != getcwd(dirbuf, sizeof(dirbuf))) |
2392 |
-+ { |
2393 |
-+ return strdup(dirbuf); |
2394 |
-+ } |
2395 |
-+ else |
2396 |
-+ { |
2397 |
-+ return strdup(dir); |
2398 |
-+ } |
2399 |
-+ } |
2400 |
-+ else |
2401 |
-+ { |
2402 |
-+ char *result = canonicalize_filename_mode(dir, CAN_EXISTING); |
2403 |
-+ if (NULL == result) |
2404 |
-+ return strdup(dir); |
2405 |
-+ else |
2406 |
-+ return result; |
2407 |
-+ } |
2408 |
-+} |
2409 |
-+ |
2410 |
-+ |
2411 |
-+ |
2412 |
-+/* Return non-zero if FS is the name of a file system that is likely to |
2413 |
-+ * be automounted |
2414 |
-+ */ |
2415 |
-+static int |
2416 |
-+fs_likely_to_be_automounted(const char *fs) |
2417 |
-+{ |
2418 |
-+ return ( (0==strcmp(fs, "nfs")) || (0==strcmp(fs, "autofs")) || (0==strcmp(fs, "subfs"))); |
2419 |
-+} |
2420 |
-+ |
2421 |
-+ |
2422 |
-+ |
2423 |
-+#ifdef STAT_MOUNTPOINTS |
2424 |
-+static dev_t *mounted_devices = NULL; |
2425 |
-+static size_t num_mounted_devices = 0u; |
2426 |
-+ |
2427 |
-+ |
2428 |
-+static void |
2429 |
-+init_mounted_dev_list(int mandatory) |
2430 |
-+{ |
2431 |
-+ assert (NULL == mounted_devices); |
2432 |
-+ assert (0 == num_mounted_devices); |
2433 |
-+ mounted_devices = get_mounted_devices(&num_mounted_devices); |
2434 |
-+ if (mandatory && (NULL == mounted_devices)) |
2435 |
-+ { |
2436 |
-+ error(1, 0, "Cannot read list of mounted devices."); |
2437 |
-+ } |
2438 |
-+} |
2439 |
-+ |
2440 |
-+static void |
2441 |
-+refresh_mounted_dev_list(void) |
2442 |
-+{ |
2443 |
-+ if (mounted_devices) |
2444 |
-+ { |
2445 |
-+ free(mounted_devices); |
2446 |
-+ mounted_devices = 0; |
2447 |
-+ } |
2448 |
-+ num_mounted_devices = 0u; |
2449 |
-+ init_mounted_dev_list(1); |
2450 |
-+} |
2451 |
-+ |
2452 |
-+ |
2453 |
-+/* Search for device DEV in the array LIST, which is of size N. */ |
2454 |
-+static int |
2455 |
-+dev_present(dev_t dev, const dev_t *list, size_t n) |
2456 |
-+{ |
2457 |
-+ if (list) |
2458 |
-+ { |
2459 |
-+ while (n-- > 0u) |
2460 |
-+ { |
2461 |
-+ if ( (*list++) == dev ) |
2462 |
-+ return 1; |
2463 |
-+ } |
2464 |
-+ } |
2465 |
-+ return 0; |
2466 |
-+} |
2467 |
-+ |
2468 |
-+enum MountPointStateChange |
2469 |
-+ { |
2470 |
-+ MountPointRecentlyMounted, |
2471 |
-+ MountPointRecentlyUnmounted, |
2472 |
-+ MountPointStateUnchanged |
2473 |
-+ }; |
2474 |
-+ |
2475 |
-+ |
2476 |
-+ |
2477 |
-+static enum MountPointStateChange |
2478 |
-+get_mount_state(dev_t newdev) |
2479 |
-+{ |
2480 |
-+ int new_is_present, new_was_present; |
2481 |
-+ |
2482 |
-+ new_was_present = dev_present(newdev, mounted_devices, num_mounted_devices); |
2483 |
-+ refresh_mounted_dev_list(); |
2484 |
-+ new_is_present = dev_present(newdev, mounted_devices, num_mounted_devices); |
2485 |
-+ |
2486 |
-+ if (new_was_present == new_is_present) |
2487 |
-+ return MountPointStateUnchanged; |
2488 |
-+ else if (new_is_present) |
2489 |
-+ return MountPointRecentlyMounted; |
2490 |
-+ else |
2491 |
-+ return MountPointRecentlyUnmounted; |
2492 |
-+} |
2493 |
-+ |
2494 |
-+ |
2495 |
-+ |
2496 |
-+/* We stat()ed a directory, chdir()ed into it (we know this |
2497 |
-+ * since direction is TraversingDown), stat()ed it again, |
2498 |
-+ * and noticed that the device numbers are different. Check |
2499 |
-+ * if the file system was recently mounted. |
2500 |
-+ * |
2501 |
-+ * If it was, it looks like chdir()ing into the directory |
2502 |
-+ * caused a file system to be mounted. Maybe automount is |
2503 |
-+ * running. Anyway, that's probably OK - but it happens |
2504 |
-+ * only when we are moving downward. |
2505 |
-+ * |
2506 |
-+ * We also allow for the possibility that a similar thing |
2507 |
-+ * has happened with the unmounting of a file system. This |
2508 |
-+ * is much rarer, as it relies on an automounter timeout |
2509 |
-+ * occurring at exactly the wrong moment. |
2510 |
-+ */ |
2511 |
-+static enum WdSanityCheckFatality |
2512 |
-+dirchange_is_fatal(const char *specific_what, |
2513 |
-+ enum WdSanityCheckFatality isfatal, |
2514 |
-+ int silent, |
2515 |
-+ struct stat *newinfo) |
2516 |
-+{ |
2517 |
-+ enum MountPointStateChange transition = get_mount_state(newinfo->st_dev); |
2518 |
-+ switch (transition) |
2519 |
-+ { |
2520 |
-+ case MountPointRecentlyUnmounted: |
2521 |
-+ isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS; |
2522 |
-+ if (!silent) |
2523 |
-+ { |
2524 |
-+ error (0, 0, |
2525 |
-+ _("Warning: file system %s has recently been unmounted."), |
2526 |
-+ safely_quote_err_filename(0, specific_what)); |
2527 |
-+ } |
2528 |
-+ break; |
2529 |
-+ |
2530 |
-+ case MountPointRecentlyMounted: |
2531 |
-+ isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS; |
2532 |
-+ if (!silent) |
2533 |
-+ { |
2534 |
-+ error (0, 0, |
2535 |
-+ _("Warning: file system %s has recently been mounted."), |
2536 |
-+ safely_quote_err_filename(0, specific_what)); |
2537 |
-+ } |
2538 |
-+ break; |
2539 |
-+ |
2540 |
-+ case MountPointStateUnchanged: |
2541 |
-+ /* leave isfatal as it is */ |
2542 |
-+ break; |
2543 |
-+ } |
2544 |
-+ |
2545 |
-+ return isfatal; |
2546 |
-+} |
2547 |
-+ |
2548 |
-+ |
2549 |
-+#endif |
2550 |
-+ |
2551 |
-+ |
2552 |
-+ |
2553 |
-+/* Examine the results of the stat() of a directory from before we |
2554 |
-+ * entered or left it, with the results of stat()ing it afterward. If |
2555 |
-+ * these are different, the file system tree has been modified while we |
2556 |
-+ * were traversing it. That might be an attempt to use a race |
2557 |
-+ * condition to persuade find to do something it didn't intend |
2558 |
-+ * (e.g. an attempt by an ordinary user to exploit the fact that root |
2559 |
-+ * sometimes runs find on the whole file system). However, this can |
2560 |
-+ * also happen if automount is running (certainly on Solaris). With |
2561 |
-+ * automount, moving into a directory can cause a file system to be |
2562 |
-+ * mounted there. |
2563 |
-+ * |
2564 |
-+ * To cope sensibly with this, we will raise an error if we see the |
2565 |
-+ * device number change unless we are chdir()ing into a subdirectory, |
2566 |
-+ * and the directory we moved into has been mounted or unmounted "recently". |
2567 |
-+ * Here "recently" means since we started "find" or we last re-read |
2568 |
-+ * the /etc/mnttab file. |
2569 |
-+ * |
2570 |
-+ * If the device number does not change but the inode does, that is a |
2571 |
-+ * problem. |
2572 |
-+ * |
2573 |
-+ * If the device number and inode are both the same, we are happy. |
2574 |
-+ * |
2575 |
-+ * If a file system is (un)mounted as we chdir() into the directory, that |
2576 |
-+ * may mean that we're now examining a section of the file system that might |
2577 |
-+ * have been excluded from consideration (via -prune or -quit for example). |
2578 |
-+ * Hence we print a warning message to indicate that the output of find |
2579 |
-+ * might be inconsistent due to the change in the file system. |
2580 |
-+ */ |
2581 |
-+static boolean |
2582 |
-+wd_sanity_check(const char *thing_to_stat, |
2583 |
-+ const char *progname, |
2584 |
-+ const char *what, |
2585 |
-+ dev_t old_dev, |
2586 |
-+ ino_t old_ino, |
2587 |
-+ struct stat *newinfo, |
2588 |
-+ int parent, |
2589 |
-+ int line_no, |
2590 |
-+ enum TraversalDirection direction, |
2591 |
-+ enum WdSanityCheckFatality isfatal, |
2592 |
-+ boolean *changed) /* output parameter */ |
2593 |
-+{ |
2594 |
-+ const char *fstype; |
2595 |
-+ char *specific_what = NULL; |
2596 |
-+ int silent = 0; |
2597 |
-+ const char *current_dir = "."; |
2598 |
-+ |
2599 |
-+ *changed = false; |
2600 |
-+ |
2601 |
-+ set_stat_placeholders(newinfo); |
2602 |
-+ if ((*options.xstat) (current_dir, newinfo) != 0) |
2603 |
-+ fatal_file_error(thing_to_stat); |
2604 |
-+ |
2605 |
-+ if (old_dev != newinfo->st_dev) |
2606 |
-+ { |
2607 |
-+ *changed = true; |
2608 |
-+ specific_what = specific_dirname(what); |
2609 |
-+ fstype = filesystem_type(newinfo, current_dir); |
2610 |
-+ silent = fs_likely_to_be_automounted(fstype); |
2611 |
-+ |
2612 |
-+ /* This condition is rare, so once we are here it is |
2613 |
-+ * reasonable to perform an expensive computation to |
2614 |
-+ * determine if we should continue or fail. |
2615 |
-+ */ |
2616 |
-+ if (TraversingDown == direction) |
2617 |
-+ { |
2618 |
-+#ifdef STAT_MOUNTPOINTS |
2619 |
-+ isfatal = dirchange_is_fatal(specific_what,isfatal,silent,newinfo); |
2620 |
-+#else |
2621 |
-+ isfatal = RETRY_IF_SANITY_CHECK_FAILS; |
2622 |
-+#endif |
2623 |
-+ } |
2624 |
-+ |
2625 |
-+ switch (isfatal) |
2626 |
-+ { |
2627 |
-+ case FATAL_IF_SANITY_CHECK_FAILS: |
2628 |
-+ { |
2629 |
-+ fstype = filesystem_type(newinfo, current_dir); |
2630 |
-+ error (1, 0, |
2631 |
-+ _("%1$s%2$s changed during execution of %3$s " |
2632 |
-+ "(old device number %4$ld, new device number %5$ld, file system type is %6$s) [ref %7$ld]"), |
2633 |
-+ safely_quote_err_filename(0, specific_what), |
2634 |
-+ parent ? "/.." : "", |
2635 |
-+ safely_quote_err_filename(1, progname), |
2636 |
-+ (long) old_dev, |
2637 |
-+ (long) newinfo->st_dev, |
2638 |
-+ fstype, |
2639 |
-+ (long)line_no); |
2640 |
-+ /*NOTREACHED*/ |
2641 |
-+ return false; |
2642 |
-+ } |
2643 |
-+ |
2644 |
-+ case NON_FATAL_IF_SANITY_CHECK_FAILS: |
2645 |
-+ { |
2646 |
-+ /* Since the device has changed under us, the inode number |
2647 |
-+ * will almost certainly also be different. However, we have |
2648 |
-+ * already decided that this is not a problem. Hence we return |
2649 |
-+ * without checking the inode number. |
2650 |
-+ */ |
2651 |
-+ free(specific_what); |
2652 |
-+ return true; |
2653 |
-+ } |
2654 |
-+ |
2655 |
-+ case RETRY_IF_SANITY_CHECK_FAILS: |
2656 |
-+ return false; |
2657 |
-+ } |
2658 |
-+ } |
2659 |
-+ |
2660 |
-+ /* Device number was the same, check if the inode has changed. */ |
2661 |
-+ if (old_ino != newinfo->st_ino) |
2662 |
-+ { |
2663 |
-+ *changed = true; |
2664 |
-+ specific_what = specific_dirname(what); |
2665 |
-+ fstype = filesystem_type(newinfo, current_dir); |
2666 |
-+ |
2667 |
-+ error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0, |
2668 |
-+ 0, /* no relevant errno value */ |
2669 |
-+ _("%1$s%2$s changed during execution of %3$s " |
2670 |
-+ "(old inode number %4$ld, new inode number %5$ld, file system type is %5$s) [ref %7$ld]"), |
2671 |
-+ safely_quote_err_filename(0, specific_what), |
2672 |
-+ parent ? "/.." : "", |
2673 |
-+ safely_quote_err_filename(1, progname), |
2674 |
-+ (long) old_ino, |
2675 |
-+ (long) newinfo->st_ino, |
2676 |
-+ fstype, |
2677 |
-+ (long)line_no); |
2678 |
-+ free(specific_what); |
2679 |
-+ return false; |
2680 |
-+ } |
2681 |
-+ |
2682 |
-+ return true; |
2683 |
-+} |
2684 |
-+ |
2685 |
-+enum SafeChdirStatus |
2686 |
-+ { |
2687 |
-+ SafeChdirOK, |
2688 |
-+ SafeChdirFailSymlink, |
2689 |
-+ SafeChdirFailNotDir, |
2690 |
-+ SafeChdirFailStat, |
2691 |
-+ SafeChdirFailWouldBeUnableToReturn, |
2692 |
-+ SafeChdirFailChdirFailed, |
2693 |
-+ SafeChdirFailNonexistent, |
2694 |
-+ SafeChdirFailDestUnreadable |
2695 |
-+ }; |
2696 |
-+ |
2697 |
-+/* Safely perform a change in directory. We do this by calling |
2698 |
-+ * lstat() on the subdirectory, using chdir() to move into it, and |
2699 |
-+ * then lstat()ing ".". We compare the results of the two stat calls |
2700 |
-+ * to see if they are consistent. If not, we sound the alarm. |
2701 |
-+ * |
2702 |
-+ * If following_links() is true, we do follow symbolic links. |
2703 |
-+ */ |
2704 |
-+static enum SafeChdirStatus |
2705 |
-+safely_chdir_lstat(const char *dest, |
2706 |
-+ enum TraversalDirection direction, |
2707 |
-+ struct stat *statbuf_dest, |
2708 |
-+ enum ChdirSymlinkHandling symlink_follow_option, |
2709 |
-+ boolean *did_stat) |
2710 |
-+{ |
2711 |
-+ struct stat statbuf_arrived; |
2712 |
-+ int rv, dotfd=-1; |
2713 |
-+ int saved_errno; /* specific_dirname() changes errno. */ |
2714 |
-+ boolean rv_set = false; |
2715 |
-+ boolean statflag = false; |
2716 |
-+ int tries = 0; |
2717 |
-+ enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS; |
2718 |
-+ |
2719 |
-+ saved_errno = errno = 0; |
2720 |
-+ |
2721 |
-+ dotfd = open(".", O_RDONLY |
2722 |
-+#if defined O_LARGEFILE |
2723 |
-+ |O_LARGEFILE |
2724 |
-+#endif |
2725 |
-+ ); |
2726 |
-+ |
2727 |
-+ /* We jump back to here if wd_sanity_check() |
2728 |
-+ * recoverably triggers an alert. |
2729 |
-+ */ |
2730 |
-+ retry: |
2731 |
-+ ++tries; |
2732 |
-+ |
2733 |
-+ if (dotfd >= 0) |
2734 |
-+ { |
2735 |
-+ /* Stat the directory we're going to. */ |
2736 |
-+ set_stat_placeholders(statbuf_dest); |
2737 |
-+ if (0 == options.xstat(dest, statbuf_dest)) |
2738 |
-+ { |
2739 |
-+ statflag = true; |
2740 |
-+ |
2741 |
-+#ifdef S_ISLNK |
2742 |
-+ /* symlink_follow_option might be set to SymlinkFollowOk, which |
2743 |
-+ * would allow us to chdir() into a symbolic link. This is |
2744 |
-+ * only useful for the case where the directory we're |
2745 |
-+ * chdir()ing into is the basename of a command line |
2746 |
-+ * argument, for example where "foo/bar/baz" is specified on |
2747 |
-+ * the command line. When -P is in effect (the default), |
2748 |
-+ * baz will not be followed if it is a symlink, but if bar |
2749 |
-+ * is a symlink, it _should_ be followed. Hence we need the |
2750 |
-+ * ability to override the policy set by following_links(). |
2751 |
-+ */ |
2752 |
-+ if (!following_links() && S_ISLNK(statbuf_dest->st_mode)) |
2753 |
-+ { |
2754 |
-+ /* We're not supposed to be following links, but this is |
2755 |
-+ * a link. Check symlink_follow_option to see if we should |
2756 |
-+ * make a special exception. |
2757 |
-+ */ |
2758 |
-+ if (symlink_follow_option == SymlinkFollowOk) |
2759 |
-+ { |
2760 |
-+ /* We need to re-stat() the file so that the |
2761 |
-+ * sanity check can pass. |
2762 |
-+ */ |
2763 |
-+ if (0 != stat(dest, statbuf_dest)) |
2764 |
-+ { |
2765 |
-+ rv = SafeChdirFailNonexistent; |
2766 |
-+ rv_set = true; |
2767 |
-+ saved_errno = errno; |
2768 |
-+ goto fail; |
2769 |
-+ } |
2770 |
-+ statflag = true; |
2771 |
-+ } |
2772 |
-+ else |
2773 |
-+ { |
2774 |
-+ /* Not following symlinks, so the attempt to |
2775 |
-+ * chdir() into a symlink should be prevented. |
2776 |
-+ */ |
2777 |
-+ rv = SafeChdirFailSymlink; |
2778 |
-+ rv_set = true; |
2779 |
-+ saved_errno = 0; /* silence the error message */ |
2780 |
-+ goto fail; |
2781 |
-+ } |
2782 |
-+ } |
2783 |
-+#endif |
2784 |
-+#ifdef S_ISDIR |
2785 |
-+ /* Although the immediately following chdir() would detect |
2786 |
-+ * the fact that this is not a directory for us, this would |
2787 |
-+ * result in an extra system call that fails. Anybody |
2788 |
-+ * examining the system-call trace should ideally not be |
2789 |
-+ * concerned that something is actually failing. |
2790 |
-+ */ |
2791 |
-+ if (!S_ISDIR(statbuf_dest->st_mode)) |
2792 |
-+ { |
2793 |
-+ rv = SafeChdirFailNotDir; |
2794 |
-+ rv_set = true; |
2795 |
-+ saved_errno = 0; /* silence the error message */ |
2796 |
-+ goto fail; |
2797 |
-+ } |
2798 |
-+#endif |
2799 |
-+ |
2800 |
-+ if (options.debug_options & DebugSearch) |
2801 |
-+ fprintf(stderr, "safely_chdir(): chdir(\"%s\")\n", dest); |
2802 |
-+ |
2803 |
-+ if (0 == chdir(dest)) |
2804 |
-+ { |
2805 |
-+ /* check we ended up where we wanted to go */ |
2806 |
-+ boolean changed = false; |
2807 |
-+ if (!wd_sanity_check(".", program_name, ".", |
2808 |
-+ statbuf_dest->st_dev, |
2809 |
-+ statbuf_dest->st_ino, |
2810 |
-+ &statbuf_arrived, |
2811 |
-+ 0, __LINE__, direction, |
2812 |
-+ isfatal, |
2813 |
-+ &changed)) |
2814 |
-+ { |
2815 |
-+ /* Only allow one failure. */ |
2816 |
-+ if (RETRY_IF_SANITY_CHECK_FAILS == isfatal) |
2817 |
-+ { |
2818 |
-+ if (0 == fchdir(dotfd)) |
2819 |
-+ { |
2820 |
-+ isfatal = FATAL_IF_SANITY_CHECK_FAILS; |
2821 |
-+ goto retry; |
2822 |
-+ } |
2823 |
-+ else |
2824 |
-+ { |
2825 |
-+ /* Failed to return to original directory, |
2826 |
-+ * but we know that the current working |
2827 |
-+ * directory is not the one that we intend |
2828 |
-+ * to be in. Since fchdir() failed, we |
2829 |
-+ * can't recover from this and so this error |
2830 |
-+ * is fatal. |
2831 |
-+ */ |
2832 |
-+ error(1, errno, |
2833 |
-+ "failed to return to parent directory"); |
2834 |
-+ } |
2835 |
-+ } |
2836 |
-+ else |
2837 |
-+ { |
2838 |
-+ /* XXX: not sure what to use as an excuse here. */ |
2839 |
-+ rv = SafeChdirFailNonexistent; |
2840 |
-+ rv_set = true; |
2841 |
-+ saved_errno = 0; |
2842 |
-+ goto fail; |
2843 |
-+ } |
2844 |
-+ } |
2845 |
-+ |
2846 |
-+ close(dotfd); |
2847 |
-+ return SafeChdirOK; |
2848 |
-+ } |
2849 |
-+ else |
2850 |
-+ { |
2851 |
-+ saved_errno = errno; |
2852 |
-+ if (ENOENT == saved_errno) |
2853 |
-+ { |
2854 |
-+ rv = SafeChdirFailNonexistent; |
2855 |
-+ rv_set = true; |
2856 |
-+ if (options.ignore_readdir_race) |
2857 |
-+ errno = 0; /* don't issue err msg */ |
2858 |
-+ } |
2859 |
-+ else if (ENOTDIR == saved_errno) |
2860 |
-+ { |
2861 |
-+ /* This can happen if the we stat a directory, |
2862 |
-+ * and then file system activity changes it into |
2863 |
-+ * a non-directory. |
2864 |
-+ */ |
2865 |
-+ saved_errno = 0; /* don't issue err msg */ |
2866 |
-+ rv = SafeChdirFailNotDir; |
2867 |
-+ rv_set = true; |
2868 |
-+ } |
2869 |
-+ else |
2870 |
-+ { |
2871 |
-+ rv = SafeChdirFailChdirFailed; |
2872 |
-+ rv_set = true; |
2873 |
-+ } |
2874 |
-+ goto fail; |
2875 |
-+ } |
2876 |
-+ } |
2877 |
-+ else |
2878 |
-+ { |
2879 |
-+ saved_errno = errno; |
2880 |
-+ rv = SafeChdirFailStat; |
2881 |
-+ rv_set = true; |
2882 |
-+ |
2883 |
-+ if ( (ENOENT == saved_errno) || (0 == state.curdepth)) |
2884 |
-+ saved_errno = 0; /* don't issue err msg */ |
2885 |
-+ goto fail; |
2886 |
-+ } |
2887 |
-+ } |
2888 |
-+ else |
2889 |
-+ { |
2890 |
-+ /* We do not have read permissions on "." */ |
2891 |
-+ rv = SafeChdirFailWouldBeUnableToReturn; |
2892 |
-+ rv_set = true; |
2893 |
-+ goto fail; |
2894 |
-+ } |
2895 |
-+ |
2896 |
-+ /* This is the success path, so we clear errno. The caller probably |
2897 |
-+ * won't be calling error() anyway. |
2898 |
-+ */ |
2899 |
-+ saved_errno = 0; |
2900 |
-+ |
2901 |
-+ /* We use the same exit path for success or failure. |
2902 |
-+ * which has occurred is recorded in RV. |
2903 |
-+ */ |
2904 |
-+ fail: |
2905 |
-+ /* We do not call error() as this would result in a duplicate error |
2906 |
-+ * message when the caller does the same thing. |
2907 |
-+ */ |
2908 |
-+ if (saved_errno) |
2909 |
-+ errno = saved_errno; |
2910 |
-+ |
2911 |
-+ if (dotfd >= 0) |
2912 |
-+ { |
2913 |
-+ close(dotfd); |
2914 |
-+ dotfd = -1; |
2915 |
-+ } |
2916 |
-+ |
2917 |
-+ *did_stat = statflag; |
2918 |
-+ assert (rv_set); |
2919 |
-+ return rv; |
2920 |
-+} |
2921 |
-+ |
2922 |
-+#if defined O_NOFOLLOW |
2923 |
-+/* Safely change working directory to the specified subdirectory. If |
2924 |
-+ * we are not allowed to follow symbolic links, we use open() with |
2925 |
-+ * O_NOFOLLOW, followed by fchdir(). This ensures that we don't |
2926 |
-+ * follow symbolic links (of course, we do follow them if the -L |
2927 |
-+ * option is in effect). |
2928 |
-+ */ |
2929 |
-+static enum SafeChdirStatus |
2930 |
-+safely_chdir_nofollow(const char *dest, |
2931 |
-+ enum TraversalDirection direction, |
2932 |
-+ struct stat *statbuf_dest, |
2933 |
-+ enum ChdirSymlinkHandling symlink_follow_option, |
2934 |
-+ boolean *did_stat) |
2935 |
-+{ |
2936 |
-+ int extraflags, fd; |
2937 |
-+ |
2938 |
-+ (void) direction; |
2939 |
-+ (void) statbuf_dest; |
2940 |
-+ |
2941 |
-+ extraflags = 0; |
2942 |
-+ *did_stat = false; |
2943 |
-+ |
2944 |
-+ switch (symlink_follow_option) |
2945 |
-+ { |
2946 |
-+ case SymlinkFollowOk: |
2947 |
-+ extraflags = 0; |
2948 |
-+ break; |
2949 |
-+ |
2950 |
-+ case SymlinkHandleDefault: |
2951 |
-+ if (following_links()) |
2952 |
-+ extraflags = 0; |
2953 |
-+ else |
2954 |
-+ extraflags = O_NOFOLLOW; |
2955 |
-+ break; |
2956 |
-+ } |
2957 |
-+ |
2958 |
-+ errno = 0; |
2959 |
-+ fd = open(dest, O_RDONLY |
2960 |
-+#if defined O_LARGEFILE |
2961 |
-+ |O_LARGEFILE |
2962 |
-+#endif |
2963 |
-+ |extraflags); |
2964 |
-+ if (fd < 0) |
2965 |
-+ { |
2966 |
-+ switch (errno) |
2967 |
-+ { |
2968 |
-+ case ELOOP: |
2969 |
-+ return SafeChdirFailSymlink; /* This is why we use O_NOFOLLOW */ |
2970 |
-+ case ENOENT: |
2971 |
-+ return SafeChdirFailNonexistent; |
2972 |
-+ default: |
2973 |
-+ return SafeChdirFailDestUnreadable; |
2974 |
-+ } |
2975 |
-+ } |
2976 |
-+ |
2977 |
-+ errno = 0; |
2978 |
-+ if (0 == fchdir(fd)) |
2979 |
-+ { |
2980 |
-+ close(fd); |
2981 |
-+ return SafeChdirOK; |
2982 |
-+ } |
2983 |
-+ else |
2984 |
-+ { |
2985 |
-+ int saved_errno = errno; |
2986 |
-+ close(fd); |
2987 |
-+ errno = saved_errno; |
2988 |
-+ |
2989 |
-+ switch (errno) |
2990 |
-+ { |
2991 |
-+ case ENOTDIR: |
2992 |
-+ return SafeChdirFailNotDir; |
2993 |
-+ |
2994 |
-+ case EACCES: |
2995 |
-+ case EBADF: /* Shouldn't happen */ |
2996 |
-+ case EINTR: |
2997 |
-+ case EIO: |
2998 |
-+ default: |
2999 |
-+ return SafeChdirFailChdirFailed; |
3000 |
-+ } |
3001 |
-+ } |
3002 |
-+} |
3003 |
-+#endif |
3004 |
-+ |
3005 |
-+static enum SafeChdirStatus |
3006 |
-+safely_chdir(const char *dest, |
3007 |
-+ enum TraversalDirection direction, |
3008 |
-+ struct stat *statbuf_dest, |
3009 |
-+ enum ChdirSymlinkHandling symlink_follow_option, |
3010 |
-+ boolean *did_stat) |
3011 |
-+{ |
3012 |
-+ enum SafeChdirStatus result; |
3013 |
-+ |
3014 |
-+ /* We're about to leave a directory. If there are any -execdir |
3015 |
-+ * argument lists which have been built but have not yet been |
3016 |
-+ * processed, do them now because they must be done in the same |
3017 |
-+ * directory. |
3018 |
-+ */ |
3019 |
-+ complete_pending_execdirs(get_current_dirfd()); |
3020 |
-+ |
3021 |
-+#if !defined(O_NOFOLLOW) |
3022 |
-+ options.open_nofollow_available = false; |
3023 |
-+#endif |
3024 |
-+ if (options.open_nofollow_available) |
3025 |
-+ { |
3026 |
-+ result = safely_chdir_nofollow(dest, direction, statbuf_dest, |
3027 |
-+ symlink_follow_option, did_stat); |
3028 |
-+ if (SafeChdirFailDestUnreadable != result) |
3029 |
-+ { |
3030 |
-+ return result; |
3031 |
-+ } |
3032 |
-+ else |
3033 |
-+ { |
3034 |
-+ /* Savannah bug #15384: fall through to use safely_chdir_lstat |
3035 |
-+ * if the directory is not readable. |
3036 |
-+ */ |
3037 |
-+ /* Do nothing. */ |
3038 |
-+ } |
3039 |
-+ } |
3040 |
-+ /* Even if O_NOFOLLOW is available, we may need to use the alternative |
3041 |
-+ * method, since parent of the start point may be executable but not |
3042 |
-+ * readable. |
3043 |
-+ */ |
3044 |
-+ return safely_chdir_lstat(dest, direction, statbuf_dest, |
3045 |
-+ symlink_follow_option, did_stat); |
3046 |
-+} |
3047 |
-+ |
3048 |
-+ |
3049 |
-+ |
3050 |
-+/* Safely go back to the starting directory. */ |
3051 |
-+static void |
3052 |
-+chdir_back (void) |
3053 |
-+{ |
3054 |
-+ struct stat stat_buf; |
3055 |
-+ boolean dummy; |
3056 |
-+ |
3057 |
-+ if (starting_desc < 0) |
3058 |
-+ { |
3059 |
-+ if (options.debug_options & DebugSearch) |
3060 |
-+ fprintf(stderr, "chdir_back(): chdir(\"%s\")\n", starting_dir); |
3061 |
-+ |
3062 |
-+#ifdef STAT_MOUNTPOINTS |
3063 |
-+ /* We will need the mounted device list. Get it now if we don't |
3064 |
-+ * already have it. |
3065 |
-+ */ |
3066 |
-+ if (NULL == mounted_devices) |
3067 |
-+ init_mounted_dev_list(1); |
3068 |
-+#endif |
3069 |
-+ |
3070 |
-+ if (chdir (starting_dir) != 0) |
3071 |
-+ fatal_file_error(starting_dir); |
3072 |
-+ |
3073 |
-+ wd_sanity_check(starting_dir, |
3074 |
-+ program_name, |
3075 |
-+ starting_dir, |
3076 |
-+ starting_stat_buf.st_dev, |
3077 |
-+ starting_stat_buf.st_ino, |
3078 |
-+ &stat_buf, 0, __LINE__, |
3079 |
-+ TraversingUp, |
3080 |
-+ FATAL_IF_SANITY_CHECK_FAILS, |
3081 |
-+ &dummy); |
3082 |
-+ } |
3083 |
-+ else |
3084 |
-+ { |
3085 |
-+ if (options.debug_options & DebugSearch) |
3086 |
-+ fprintf(stderr, "chdir_back(): chdir(<starting-point>)\n"); |
3087 |
-+ |
3088 |
-+ if (fchdir (starting_desc) != 0) |
3089 |
-+ { |
3090 |
-+ fatal_file_error(starting_dir); |
3091 |
-+ } |
3092 |
-+ } |
3093 |
-+} |
3094 |
-+ |
3095 |
-+/* Move to the parent of a given directory and then call a function, |
3096 |
-+ * restoring the cwd. Don't bother changing directory if the |
3097 |
-+ * specified directory is a child of "." or is the root directory. |
3098 |
-+ */ |
3099 |
-+static void |
3100 |
-+at_top (char *pathname, |
3101 |
-+ mode_t mode, |
3102 |
-+ struct stat *pstat, |
3103 |
-+ void (*action)(char *pathname, |
3104 |
-+ char *basename, |
3105 |
-+ int mode, |
3106 |
-+ struct stat *pstat)) |
3107 |
-+{ |
3108 |
-+ int dirchange; |
3109 |
-+ char *parent_dir = dir_name (pathname); |
3110 |
-+ char *base = last_component (pathname); |
3111 |
-+ |
3112 |
-+ state.curdepth = 0; |
3113 |
-+ state.starting_path_length = strlen (pathname); |
3114 |
-+ |
3115 |
-+ if (0 == *base |
3116 |
-+ || 0 == strcmp(parent_dir, ".")) |
3117 |
-+ { |
3118 |
-+ dirchange = 0; |
3119 |
-+ base = pathname; |
3120 |
-+ } |
3121 |
-+ else |
3122 |
-+ { |
3123 |
-+ enum TraversalDirection direction; |
3124 |
-+ enum SafeChdirStatus chdir_status; |
3125 |
-+ struct stat st; |
3126 |
-+ boolean did_stat = false; |
3127 |
-+ |
3128 |
-+ dirchange = 1; |
3129 |
-+ if (0 == strcmp(base, "..")) |
3130 |
-+ direction = TraversingUp; |
3131 |
-+ else |
3132 |
-+ direction = TraversingDown; |
3133 |
-+ |
3134 |
-+ /* We pass SymlinkFollowOk to safely_chdir(), which allows it to |
3135 |
-+ * chdir() into a symbolic link. This is only useful for the |
3136 |
-+ * case where the directory we're chdir()ing into is the |
3137 |
-+ * basename of a command line argument, for example where |
3138 |
-+ * "foo/bar/baz" is specified on the command line. When -P is |
3139 |
-+ * in effect (the default), baz will not be followed if it is a |
3140 |
-+ * symlink, but if bar is a symlink, it _should_ be followed. |
3141 |
-+ * Hence we need the ability to override the policy set by |
3142 |
-+ * following_links(). |
3143 |
-+ */ |
3144 |
-+ chdir_status = safely_chdir(parent_dir, direction, &st, SymlinkFollowOk, &did_stat); |
3145 |
-+ if (SafeChdirOK != chdir_status) |
3146 |
-+ { |
3147 |
-+ const char *what = (SafeChdirFailWouldBeUnableToReturn == chdir_status) ? "." : parent_dir; |
3148 |
-+ if (errno) |
3149 |
-+ error (0, errno, "%s", |
3150 |
-+ safely_quote_err_filename(0, what)); |
3151 |
-+ else |
3152 |
-+ error (0, 0, _("Failed to safely change directory into %s"), |
3153 |
-+ safely_quote_err_filename(0, parent_dir)); |
3154 |
-+ |
3155 |
-+ /* We can't process this command-line argument. */ |
3156 |
-+ state.exit_status = 1; |
3157 |
-+ return; |
3158 |
-+ } |
3159 |
-+ } |
3160 |
-+ |
3161 |
-+ free (parent_dir); |
3162 |
-+ parent_dir = NULL; |
3163 |
-+ |
3164 |
-+ action(pathname, base, mode, pstat); |
3165 |
-+ |
3166 |
-+ if (dirchange) |
3167 |
-+ { |
3168 |
-+ chdir_back(); |
3169 |
-+ } |
3170 |
-+} |
3171 |
-+ |
3172 |
-+ |
3173 |
-+static void do_process_top_dir(char *pathname, |
3174 |
-+ char *base, |
3175 |
-+ int mode, |
3176 |
-+ struct stat *pstat) |
3177 |
-+{ |
3178 |
-+ (void) pstat; |
3179 |
-+ |
3180 |
-+ process_path (pathname, base, false, ".", mode); |
3181 |
-+ complete_pending_execdirs(get_current_dirfd()); |
3182 |
-+} |
3183 |
-+ |
3184 |
-+static void do_process_predicate(char *pathname, |
3185 |
-+ char *base, |
3186 |
-+ int mode, |
3187 |
-+ struct stat *pstat) |
3188 |
-+{ |
3189 |
-+ (void) mode; |
3190 |
-+ |
3191 |
-+ state.rel_pathname = base; /* cwd_dir_fd was already set by safely_chdir */ |
3192 |
-+ apply_predicate (pathname, pstat, get_eval_tree()); |
3193 |
-+} |
3194 |
-+ |
3195 |
-+ |
3196 |
-+ |
3197 |
-+ |
3198 |
-+/* Descend PATHNAME, which is a command-line argument. |
3199 |
-+ |
3200 |
-+ Actions like -execdir assume that we are in the |
3201 |
-+ parent directory of the file we're examining, |
3202 |
-+ and on entry to this function our working directory |
3203 |
-+ is whatever it was when find was invoked. Therefore |
3204 |
-+ If PATHNAME is "." we just leave things as they are. |
3205 |
-+ Otherwise, we figure out what the parent directory is, |
3206 |
-+ and move to that. |
3207 |
-+*/ |
3208 |
-+static void |
3209 |
-+process_top_path (char *pathname, mode_t mode) |
3210 |
-+{ |
3211 |
-+ at_top(pathname, mode, NULL, do_process_top_dir); |
3212 |
-+} |
3213 |
-+ |
3214 |
-+ |
3215 |
-+/* Info on each directory in the current tree branch, to avoid |
3216 |
-+ getting stuck in symbolic link loops. */ |
3217 |
-+static struct dir_id *dir_ids = NULL; |
3218 |
-+/* Entries allocated in `dir_ids'. */ |
3219 |
-+static int dir_alloc = 0; |
3220 |
-+/* Index in `dir_ids' of directory currently being searched. |
3221 |
-+ This is always the last valid entry. */ |
3222 |
-+static int dir_curr = -1; |
3223 |
-+/* (Arbitrary) number of entries to grow `dir_ids' by. */ |
3224 |
-+#define DIR_ALLOC_STEP 32 |
3225 |
-+ |
3226 |
-+ |
3227 |
-+ |
3228 |
-+/* We've detected a file system loop. This is caused by one of |
3229 |
-+ * two things: |
3230 |
-+ * |
3231 |
-+ * 1. Option -L is in effect and we've hit a symbolic link that |
3232 |
-+ * points to an ancestor. This is harmless. We won't traverse the |
3233 |
-+ * symbolic link. |
3234 |
-+ * |
3235 |
-+ * 2. We have hit a real cycle in the directory hierarchy. In this |
3236 |
-+ * case, we issue a diagnostic message (POSIX requires this) and we |
3237 |
-+ * skip that directory entry. |
3238 |
-+ */ |
3239 |
-+static void |
3240 |
-+issue_loop_warning(const char *name, const char *pathname, int level) |
3241 |
-+{ |
3242 |
-+ struct stat stbuf_link; |
3243 |
-+ if (lstat(name, &stbuf_link) != 0) |
3244 |
-+ stbuf_link.st_mode = S_IFREG; |
3245 |
-+ |
3246 |
-+ if (S_ISLNK(stbuf_link.st_mode)) |
3247 |
-+ { |
3248 |
-+ error(0, 0, |
3249 |
-+ _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."), |
3250 |
-+ safely_quote_err_filename(0, pathname)); |
3251 |
-+ /* XXX: POSIX appears to require that the exit status be non-zero if a |
3252 |
-+ * diagnostic is issued. |
3253 |
-+ */ |
3254 |
-+ } |
3255 |
-+ else |
3256 |
-+ { |
3257 |
-+ int distance = 1 + (dir_curr-level); |
3258 |
-+ /* We have found an infinite loop. POSIX requires us to |
3259 |
-+ * issue a diagnostic. Usually we won't get to here |
3260 |
-+ * because when the leaf optimisation is on, it will cause |
3261 |
-+ * the subdirectory to be skipped. If /a/b/c/d is a hard |
3262 |
-+ * link to /a/b, then the link count of /a/b/c is 2, |
3263 |
-+ * because the ".." entry of /b/b/c/d points to /a, not |
3264 |
-+ * to /a/b/c. |
3265 |
-+ */ |
3266 |
-+ error(0, 0, |
3267 |
-+ ngettext( |
3268 |
-+ "Filesystem loop detected; %1$s has the same device number and inode as " |
3269 |
-+ "a directory which is %2$d level higher in the file system hierarchy", |
3270 |
-+ "Filesystem loop detected; %1$s has the same device number and inode as " |
3271 |
-+ "a directory which is %2$d levels higher in the file system hierarchy", |
3272 |
-+ (long)distance), |
3273 |
-+ safely_quote_err_filename(0, pathname), |
3274 |
-+ distance); |
3275 |
-+ } |
3276 |
-+} |
3277 |
-+ |
3278 |
-+ |
3279 |
-+ |
3280 |
-+/* Recursively descend path PATHNAME, applying the predicates. |
3281 |
-+ LEAF is true if PATHNAME is known to be in a directory that has no |
3282 |
-+ more unexamined subdirectories, and therefore it is not a directory. |
3283 |
-+ Knowing this allows us to avoid calling stat as long as possible for |
3284 |
-+ leaf files. |
3285 |
-+ |
3286 |
-+ NAME is PATHNAME relative to the current directory. We access NAME |
3287 |
-+ but print PATHNAME. |
3288 |
-+ |
3289 |
-+ PARENT is the path of the parent of NAME, relative to find's |
3290 |
-+ starting directory. |
3291 |
-+ |
3292 |
-+ Return nonzero iff PATHNAME is a directory. */ |
3293 |
-+ |
3294 |
-+static int |
3295 |
-+process_path (char *pathname, char *name, boolean leaf, char *parent, |
3296 |
-+ mode_t mode) |
3297 |
-+{ |
3298 |
-+ struct stat stat_buf; |
3299 |
-+ static dev_t root_dev; /* Device ID of current argument pathname. */ |
3300 |
-+ int i; |
3301 |
-+ struct predicate *eval_tree; |
3302 |
-+ |
3303 |
-+ eval_tree = get_eval_tree(); |
3304 |
-+ /* Assume it is a non-directory initially. */ |
3305 |
-+ stat_buf.st_mode = 0; |
3306 |
-+ state.rel_pathname = name; |
3307 |
-+ state.type = 0; |
3308 |
-+ state.have_stat = false; |
3309 |
-+ state.have_type = false; |
3310 |
-+ |
3311 |
-+ if (!digest_mode(mode, pathname, name, &stat_buf, leaf)) |
3312 |
-+ return 0; |
3313 |
-+ |
3314 |
-+ if (!S_ISDIR (state.type)) |
3315 |
-+ { |
3316 |
-+ if (state.curdepth >= options.mindepth) |
3317 |
-+ apply_predicate (pathname, &stat_buf, eval_tree); |
3318 |
-+ return 0; |
3319 |
-+ } |
3320 |
-+ |
3321 |
-+ /* From here on, we're working on a directory. */ |
3322 |
-+ |
3323 |
-+ |
3324 |
-+ /* Now we really need to stat the directory, even if we know the |
3325 |
-+ * type, because we need information like struct stat.st_rdev. |
3326 |
-+ */ |
3327 |
-+ if (get_statinfo(pathname, name, &stat_buf) != 0) |
3328 |
-+ return 0; |
3329 |
-+ |
3330 |
-+ state.have_stat = true; |
3331 |
-+ mode = state.type = stat_buf.st_mode; /* use full info now that we have it. */ |
3332 |
-+ state.stop_at_current_level = |
3333 |
-+ options.maxdepth >= 0 |
3334 |
-+ && state.curdepth >= options.maxdepth; |
3335 |
-+ |
3336 |
-+ /* If we've already seen this directory on this branch, |
3337 |
-+ don't descend it again. */ |
3338 |
-+ for (i = 0; i <= dir_curr; i++) |
3339 |
-+ if (stat_buf.st_ino == dir_ids[i].ino && |
3340 |
-+ stat_buf.st_dev == dir_ids[i].dev) |
3341 |
-+ { |
3342 |
-+ state.stop_at_current_level = true; |
3343 |
-+ issue_loop_warning(name, pathname, i); |
3344 |
-+ } |
3345 |
-+ |
3346 |
-+ if (dir_alloc <= ++dir_curr) |
3347 |
-+ { |
3348 |
-+ dir_alloc += DIR_ALLOC_STEP; |
3349 |
-+ dir_ids = (struct dir_id *) |
3350 |
-+ xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id)); |
3351 |
-+ } |
3352 |
-+ dir_ids[dir_curr].ino = stat_buf.st_ino; |
3353 |
-+ dir_ids[dir_curr].dev = stat_buf.st_dev; |
3354 |
-+ |
3355 |
-+ if (options.stay_on_filesystem) |
3356 |
-+ { |
3357 |
-+ if (state.curdepth == 0) |
3358 |
-+ root_dev = stat_buf.st_dev; |
3359 |
-+ else if (stat_buf.st_dev != root_dev) |
3360 |
-+ state.stop_at_current_level = true; |
3361 |
-+ } |
3362 |
-+ |
3363 |
-+ if (options.do_dir_first && state.curdepth >= options.mindepth) |
3364 |
-+ apply_predicate (pathname, &stat_buf, eval_tree); |
3365 |
-+ |
3366 |
-+ if (options.debug_options & DebugSearch) |
3367 |
-+ fprintf(stderr, "pathname = %s, stop_at_current_level = %d\n", |
3368 |
-+ pathname, state.stop_at_current_level); |
3369 |
-+ |
3370 |
-+ if (state.stop_at_current_level == false) |
3371 |
-+ { |
3372 |
-+ /* Scan directory on disk. */ |
3373 |
-+ process_dir (pathname, name, strlen (pathname), &stat_buf, parent); |
3374 |
-+ } |
3375 |
-+ |
3376 |
-+ if (options.do_dir_first == false && state.curdepth >= options.mindepth) |
3377 |
-+ { |
3378 |
-+ /* The fields in 'state' are now out of date. Correct them. |
3379 |
-+ */ |
3380 |
-+ if (!digest_mode(mode, pathname, name, &stat_buf, leaf)) |
3381 |
-+ return 0; |
3382 |
-+ |
3383 |
-+ if (0 == dir_curr) |
3384 |
-+ { |
3385 |
-+ at_top(pathname, mode, &stat_buf, do_process_predicate); |
3386 |
-+ } |
3387 |
-+ else |
3388 |
-+ { |
3389 |
-+ do_process_predicate(pathname, name, mode, &stat_buf); |
3390 |
-+ } |
3391 |
-+ } |
3392 |
-+ |
3393 |
-+ dir_curr--; |
3394 |
-+ |
3395 |
-+ return 1; |
3396 |
-+} |
3397 |
-+ |
3398 |
-+ |
3399 |
-+/* Scan directory PATHNAME and recurse through process_path for each entry. |
3400 |
-+ |
3401 |
-+ PATHLEN is the length of PATHNAME. |
3402 |
-+ |
3403 |
-+ NAME is PATHNAME relative to the current directory. |
3404 |
-+ |
3405 |
-+ STATP is the results of *options.xstat on it. |
3406 |
-+ |
3407 |
-+ PARENT is the path of the parent of NAME, relative to find's |
3408 |
-+ starting directory. */ |
3409 |
-+ |
3410 |
-+static void |
3411 |
-+process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent) |
3412 |
-+{ |
3413 |
-+ int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */ |
3414 |
-+ boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */ |
3415 |
-+ unsigned int idx; /* Which entry are we on? */ |
3416 |
-+ struct stat stat_buf; |
3417 |
-+ size_t dircount = 0u; |
3418 |
-+ struct savedir_dirinfo *dirinfo; |
3419 |
-+#if 0 |
3420 |
-+ printf("process_dir: pathname=%s name=%s statp->st_nlink=%d st_ino=%d\n", |
3421 |
-+ pathname, |
3422 |
-+ name, |
3423 |
-+ (int)statp->st_nlink, |
3424 |
-+ (int)statp->st_ino); |
3425 |
-+#endif |
3426 |
-+ if (statp->st_nlink < 2) |
3427 |
-+ { |
3428 |
-+ subdirs_unreliable = true; |
3429 |
-+ subdirs_left = 0; |
3430 |
-+ } |
3431 |
-+ else |
3432 |
-+ { |
3433 |
-+ subdirs_unreliable = false; /* not necessarily right */ |
3434 |
-+ subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */ |
3435 |
-+ } |
3436 |
-+ |
3437 |
-+ errno = 0; |
3438 |
-+ dirinfo = xsavedir(name, 0); |
3439 |
-+ |
3440 |
-+ |
3441 |
-+ if (dirinfo == NULL) |
3442 |
-+ { |
3443 |
-+ assert (errno != 0); |
3444 |
-+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
3445 |
-+ state.exit_status = 1; |
3446 |
-+ } |
3447 |
-+ else |
3448 |
-+ { |
3449 |
-+ register char *namep; /* Current point in `name_space'. */ |
3450 |
-+ char *cur_path; /* Full path of each file to process. */ |
3451 |
-+ char *cur_name; /* Base name of each file to process. */ |
3452 |
-+ unsigned cur_path_size; /* Bytes allocated for `cur_path'. */ |
3453 |
-+ register unsigned file_len; /* Length of each path to process. */ |
3454 |
-+ register unsigned pathname_len; /* PATHLEN plus trailing '/'. */ |
3455 |
-+ boolean did_stat = false; |
3456 |
-+ |
3457 |
-+ if (pathname[pathlen - 1] == '/') |
3458 |
-+ pathname_len = pathlen + 1; /* For '\0'; already have '/'. */ |
3459 |
-+ else |
3460 |
-+ pathname_len = pathlen + 2; /* For '/' and '\0'. */ |
3461 |
-+ cur_path_size = 0; |
3462 |
-+ cur_path = NULL; |
3463 |
-+ |
3464 |
-+ /* We're about to leave the directory. If there are any |
3465 |
-+ * -execdir argument lists which have been built but have not |
3466 |
-+ * yet been processed, do them now because they must be done in |
3467 |
-+ * the same directory. |
3468 |
-+ */ |
3469 |
-+ complete_pending_execdirs(get_current_dirfd()); |
3470 |
-+ |
3471 |
-+ if (strcmp (name, ".")) |
3472 |
-+ { |
3473 |
-+ enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat); |
3474 |
-+ switch (status) |
3475 |
-+ { |
3476 |
-+ case SafeChdirOK: |
3477 |
-+ /* If there had been a change but wd_sanity_check() |
3478 |
-+ * accepted it, we need to accept that on the |
3479 |
-+ * way back up as well, so modify our record |
3480 |
-+ * of what we think we should see later. |
3481 |
-+ * If there was no change, the assignments are a no-op. |
3482 |
-+ * |
3483 |
-+ * However, before performing the assignment, we need to |
3484 |
-+ * check that we have the stat information. If O_NOFOLLOW |
3485 |
-+ * is available, safely_chdir() will not have needed to use |
3486 |
-+ * stat(), and so stat_buf will just contain random data. |
3487 |
-+ */ |
3488 |
-+ if (!did_stat) |
3489 |
-+ { |
3490 |
-+ /* If there is a link we need to follow it. Hence |
3491 |
-+ * the direct call to stat() not through (options.xstat) |
3492 |
-+ */ |
3493 |
-+ set_stat_placeholders(&stat_buf); |
3494 |
-+ if (0 != stat(".", &stat_buf)) |
3495 |
-+ break; /* skip the assignment. */ |
3496 |
-+ } |
3497 |
-+ dir_ids[dir_curr].dev = stat_buf.st_dev; |
3498 |
-+ dir_ids[dir_curr].ino = stat_buf.st_ino; |
3499 |
-+ |
3500 |
-+ break; |
3501 |
-+ |
3502 |
-+ case SafeChdirFailWouldBeUnableToReturn: |
3503 |
-+ error (0, errno, "."); |
3504 |
-+ state.exit_status = 1; |
3505 |
-+ break; |
3506 |
-+ |
3507 |
-+ case SafeChdirFailNonexistent: |
3508 |
-+ case SafeChdirFailDestUnreadable: |
3509 |
-+ case SafeChdirFailStat: |
3510 |
-+ case SafeChdirFailNotDir: |
3511 |
-+ case SafeChdirFailChdirFailed: |
3512 |
-+ error (0, errno, "%s", |
3513 |
-+ safely_quote_err_filename(0, pathname)); |
3514 |
-+ state.exit_status = 1; |
3515 |
-+ return; |
3516 |
-+ |
3517 |
-+ case SafeChdirFailSymlink: |
3518 |
-+ error (0, 0, |
3519 |
-+ _("warning: not following the symbolic link %s"), |
3520 |
-+ safely_quote_err_filename(0, pathname)); |
3521 |
-+ state.exit_status = 1; |
3522 |
-+ return; |
3523 |
-+ } |
3524 |
-+ } |
3525 |
-+ |
3526 |
-+ for (idx=0; idx < dirinfo->size; ++idx) |
3527 |
-+ { |
3528 |
-+ /* savedirinfo() may return dirinfo=NULL if extended information |
3529 |
-+ * is not available. |
3530 |
-+ */ |
3531 |
-+ mode_t mode = (dirinfo->entries[idx].flags & SavedirHaveFileType) ? |
3532 |
-+ dirinfo->entries[idx].type_info : 0; |
3533 |
-+ namep = dirinfo->entries[idx].name; |
3534 |
-+ |
3535 |
-+ /* Append this directory entry's name to the path being searched. */ |
3536 |
-+ file_len = pathname_len + strlen (namep); |
3537 |
-+ if (file_len > cur_path_size) |
3538 |
-+ { |
3539 |
-+ while (file_len > cur_path_size) |
3540 |
-+ cur_path_size += 1024; |
3541 |
-+ if (cur_path) |
3542 |
-+ free (cur_path); |
3543 |
-+ cur_path = xmalloc (cur_path_size); |
3544 |
-+ strcpy (cur_path, pathname); |
3545 |
-+ cur_path[pathname_len - 2] = '/'; |
3546 |
-+ } |
3547 |
-+ cur_name = cur_path + pathname_len - 1; |
3548 |
-+ strcpy (cur_name, namep); |
3549 |
-+ |
3550 |
-+ state.curdepth++; |
3551 |
-+ if (!options.no_leaf_check && !subdirs_unreliable) |
3552 |
-+ { |
3553 |
-+ if (mode && S_ISDIR(mode) && (subdirs_left == 0)) |
3554 |
-+ { |
3555 |
-+ /* This is a subdirectory, but the number of directories we |
3556 |
-+ * have found now exceeds the number we would expect given |
3557 |
-+ * the hard link count on the parent. This is likely to be |
3558 |
-+ * a bug in the file system driver (e.g. Linux's |
3559 |
-+ * /proc file system) or may just be a fact that the OS |
3560 |
-+ * doesn't really handle hard links with Unix semantics. |
3561 |
-+ * In the latter case, -noleaf should be used routinely. |
3562 |
-+ */ |
3563 |
-+ error(0, 0, _("WARNING: Hard link count is wrong for %1$s " |
3564 |
-+ "(saw only st_nlink=%2$d but we already saw %3$d subdirectories): " |
3565 |
-+ "this may be a bug in your file system driver. " |
3566 |
-+ "Automatically turning on find's -noleaf option. " |
3567 |
-+ "Earlier results may have failed to include directories " |
3568 |
-+ "that should have been searched."), |
3569 |
-+ safely_quote_err_filename(0, pathname), |
3570 |
-+ statp->st_nlink, |
3571 |
-+ dircount); |
3572 |
-+ state.exit_status = 1; /* We know the result is wrong, now */ |
3573 |
-+ options.no_leaf_check = true; /* Don't make same |
3574 |
-+ mistake again */ |
3575 |
-+ subdirs_unreliable = 1; |
3576 |
-+ subdirs_left = 1; /* band-aid for this iteration. */ |
3577 |
-+ } |
3578 |
-+ |
3579 |
-+ /* Normal case optimization. On normal Unix |
3580 |
-+ file systems, a directory that has no subdirectories |
3581 |
-+ has two links: its name, and ".". Any additional |
3582 |
-+ links are to the ".." entries of its subdirectories. |
3583 |
-+ Once we have processed as many subdirectories as |
3584 |
-+ there are additional links, we know that the rest of |
3585 |
-+ the entries are non-directories -- in other words, |
3586 |
-+ leaf files. */ |
3587 |
-+ { |
3588 |
-+ int count; |
3589 |
-+ count = process_path (cur_path, cur_name, |
3590 |
-+ subdirs_left == 0, pathname, |
3591 |
-+ mode); |
3592 |
-+ subdirs_left -= count; |
3593 |
-+ dircount += count; |
3594 |
-+ } |
3595 |
-+ } |
3596 |
-+ else |
3597 |
-+ { |
3598 |
-+ /* There might be weird (e.g., CD-ROM or MS-DOS) file systems |
3599 |
-+ mounted, which don't have Unix-like directory link counts. */ |
3600 |
-+ process_path (cur_path, cur_name, false, pathname, mode); |
3601 |
-+ } |
3602 |
-+ |
3603 |
-+ state.curdepth--; |
3604 |
-+ } |
3605 |
-+ |
3606 |
-+ |
3607 |
-+ /* We're about to leave the directory. If there are any |
3608 |
-+ * -execdir argument lists which have been built but have not |
3609 |
-+ * yet been processed, do them now because they must be done in |
3610 |
-+ * the same directory. |
3611 |
-+ */ |
3612 |
-+ complete_pending_execdirs(get_current_dirfd()); |
3613 |
-+ |
3614 |
-+ if (strcmp (name, ".")) |
3615 |
-+ { |
3616 |
-+ enum SafeChdirStatus status; |
3617 |
-+ struct dir_id did; |
3618 |
-+ |
3619 |
-+ /* We could go back and do the next command-line arg |
3620 |
-+ instead, maybe using longjmp. */ |
3621 |
-+ char const *dir; |
3622 |
-+ boolean deref = following_links() ? true : false; |
3623 |
-+ |
3624 |
-+ if ( (state.curdepth>0) && !deref) |
3625 |
-+ dir = ".."; |
3626 |
-+ else |
3627 |
-+ { |
3628 |
-+ chdir_back (); |
3629 |
-+ dir = parent; |
3630 |
-+ } |
3631 |
-+ |
3632 |
-+ did_stat = false; |
3633 |
-+ status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat); |
3634 |
-+ switch (status) |
3635 |
-+ { |
3636 |
-+ case SafeChdirOK: |
3637 |
-+ break; |
3638 |
-+ |
3639 |
-+ case SafeChdirFailWouldBeUnableToReturn: |
3640 |
-+ error (1, errno, "."); |
3641 |
-+ return; |
3642 |
-+ |
3643 |
-+ case SafeChdirFailNonexistent: |
3644 |
-+ case SafeChdirFailDestUnreadable: |
3645 |
-+ case SafeChdirFailStat: |
3646 |
-+ case SafeChdirFailSymlink: |
3647 |
-+ case SafeChdirFailNotDir: |
3648 |
-+ case SafeChdirFailChdirFailed: |
3649 |
-+ error (1, errno, "%s", safely_quote_err_filename(0, pathname)); |
3650 |
-+ return; |
3651 |
-+ } |
3652 |
-+ |
3653 |
-+ if (dir_curr > 0) |
3654 |
-+ { |
3655 |
-+ did.dev = dir_ids[dir_curr-1].dev; |
3656 |
-+ did.ino = dir_ids[dir_curr-1].ino; |
3657 |
-+ } |
3658 |
-+ else |
3659 |
-+ { |
3660 |
-+ did.dev = starting_stat_buf.st_dev; |
3661 |
-+ did.ino = starting_stat_buf.st_ino; |
3662 |
-+ } |
3663 |
-+ } |
3664 |
-+ |
3665 |
-+ if (cur_path) |
3666 |
-+ free (cur_path); |
3667 |
-+ free_dirinfo(dirinfo); |
3668 |
-+ } |
3669 |
-+ |
3670 |
-+ if (subdirs_unreliable) |
3671 |
-+ { |
3672 |
-+ /* Make sure we hasn't used the variable subdirs_left if we knew |
3673 |
-+ * we shouldn't do so. |
3674 |
-+ */ |
3675 |
-+ assert (0 == subdirs_left || options.no_leaf_check); |
3676 |
-+ } |
3677 |
-+} |
3678 |
-diff -purN findutils-4.3.12.orig/find/parser.c findutils-4.3.12/find/parser.c |
3679 |
---- findutils-4.3.12.orig/find/parser.c 2007-12-19 16:12:34.000000000 -0500 |
3680 |
-+++ findutils-4.3.12/find/parser.c 2008-01-30 08:46:05.754843619 -0500 |
3681 |
-@@ -53,6 +53,13 @@ |
3682 |
- #include <unistd.h> |
3683 |
- #include <sys/stat.h> |
3684 |
- |
3685 |
-+#ifdef WITH_SELINUX |
3686 |
-+#include <selinux/selinux.h> |
3687 |
-+int optionh_getfilecon(const char *name, security_context_t *p); |
3688 |
-+int optionl_getfilecon(const char *name, security_context_t *p); |
3689 |
-+int optionp_getfilecon(const char *name, security_context_t *p); |
3690 |
-+#endif /*WITH_SELINUX*/ |
3691 |
-+ |
3692 |
- #if ENABLE_NLS |
3693 |
- # include <libintl.h> |
3694 |
- # define _(Text) gettext (Text) |
3695 |
-@@ -156,6 +163,9 @@ static boolean parse_noignore_race PARAM |
3696 |
- static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3697 |
- static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3698 |
- static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3699 |
-+#ifdef WITH_SELINUX |
3700 |
-+static boolean parse_scontext PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3701 |
-+#endif /*WITH_SELINUX*/ |
3702 |
- |
3703 |
- boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3704 |
- |
3705 |
-@@ -341,6 +351,8 @@ static struct parser_table const parse_t |
3706 |
- {ARG_TEST, "-help", parse_help, NULL}, /* GNU */ |
3707 |
- {ARG_TEST, "version", parse_version, NULL}, /* GNU */ |
3708 |
- {ARG_TEST, "-version", parse_version, NULL}, /* GNU */ |
3709 |
-+ {ARG_TEST, "context", parse_scontext, pred_scontext}, /* SELinux */ |
3710 |
-+ {ARG_TEST, "-context", parse_scontext, pred_scontext}, /* SELinux */ |
3711 |
- {0, 0, 0, 0} |
3712 |
- }; |
3713 |
- |
3714 |
-@@ -452,10 +464,16 @@ set_follow_state(enum SymlinkOption opt) |
3715 |
- case SYMLINK_ALWAYS_DEREF: /* -L */ |
3716 |
- options.xstat = optionl_stat; |
3717 |
- options.no_leaf_check = true; |
3718 |
-+#ifdef WITH_SELINUX |
3719 |
-+ options.x_getfilecon = optionl_getfilecon; |
3720 |
-+#endif /* WITH_SELINUX */ |
3721 |
- break; |
3722 |
- |
3723 |
- case SYMLINK_NEVER_DEREF: /* -P (default) */ |
3724 |
- options.xstat = optionp_stat; |
3725 |
-+#ifdef WITH_SELINUX |
3726 |
-+ options.x_getfilecon = optionp_getfilecon; |
3727 |
-+#endif /* WITH_SELINUX */ |
3728 |
- /* Can't turn no_leaf_check off because the user might have specified |
3729 |
- * -noleaf anyway |
3730 |
- */ |
3731 |
-@@ -464,6 +482,9 @@ set_follow_state(enum SymlinkOption opt) |
3732 |
- case SYMLINK_DEREF_ARGSONLY: /* -H */ |
3733 |
- options.xstat = optionh_stat; |
3734 |
- options.no_leaf_check = true; |
3735 |
-+#ifdef WITH_SELINUX |
3736 |
-+ options.x_getfilecon = optionh_getfilecon; |
3737 |
-+#endif /* WITH_SELINUX */ |
3738 |
- } |
3739 |
- } |
3740 |
- options.symlink_handling = opt; |
3741 |
-@@ -667,6 +688,94 @@ collect_arg_stat_info(char **argv, int * |
3742 |
- |
3743 |
- The predicate structure is updated with the new information. */ |
3744 |
- |
3745 |
-+#ifdef WITH_SELINUX |
3746 |
-+ |
3747 |
-+static int |
3748 |
-+fallback_getfilecon(const char *name, security_context_t *p, int prev_rv) |
3749 |
-+{ |
3750 |
-+ /* Our original getfilecon() call failed. Perhaps we can't follow a |
3751 |
-+ * symbolic link. If that might be the problem, lgetfilecon() the link. |
3752 |
-+ * Otherwise, admit defeat. |
3753 |
-+ */ |
3754 |
-+ switch (errno) |
3755 |
-+ { |
3756 |
-+ case ENOENT: |
3757 |
-+ case ENOTDIR: |
3758 |
-+#ifdef DEBUG_STAT |
3759 |
-+ fprintf(stderr, "fallback_getfilecon(): getfilecon(%s) failed; falling back on lgetfilecon()\n", name); |
3760 |
-+#endif |
3761 |
-+ return lgetfilecon(name, p); |
3762 |
-+ |
3763 |
-+ case EACCES: |
3764 |
-+ case EIO: |
3765 |
-+ case ELOOP: |
3766 |
-+ case ENAMETOOLONG: |
3767 |
-+#ifdef EOVERFLOW |
3768 |
-+ case EOVERFLOW: /* EOVERFLOW is not #defined on UNICOS. */ |
3769 |
-+#endif |
3770 |
-+ default: |
3771 |
-+ return prev_rv; |
3772 |
-+ } |
3773 |
-+} |
3774 |
-+ |
3775 |
-+ |
3776 |
-+/* optionh_getfilecon() implements the getfilecon operation when the |
3777 |
-+ * -H option is in effect. |
3778 |
-+ * |
3779 |
-+ * If the item to be examined is a command-line argument, we follow |
3780 |
-+ * symbolic links. If the getfilecon() call fails on the command-line |
3781 |
-+ * item, we fall back on the properties of the symbolic link. |
3782 |
-+ * |
3783 |
-+ * If the item to be examined is not a command-line argument, we |
3784 |
-+ * examine the link itself. |
3785 |
-+ */ |
3786 |
-+int |
3787 |
-+optionh_getfilecon(const char *name, security_context_t *p) |
3788 |
-+{ |
3789 |
-+ if (0 == state.curdepth) |
3790 |
-+ { |
3791 |
-+ /* This file is from the command line; deference the link (if it |
3792 |
-+ * is a link). |
3793 |
-+ */ |
3794 |
-+ int rv = getfilecon(name, p); |
3795 |
-+ if (0 == rv) |
3796 |
-+ return 0; /* success */ |
3797 |
-+ else |
3798 |
-+ return fallback_getfilecon(name, p, rv); |
3799 |
-+ } |
3800 |
-+ else |
3801 |
-+ { |
3802 |
-+ /* Not a file on the command line; do not derefernce the link. |
3803 |
-+ */ |
3804 |
-+ return lgetfilecon(name, p); |
3805 |
-+ } |
3806 |
-+} |
3807 |
-+ |
3808 |
-+/* optionl_getfilecon() implements the getfilecon operation when the |
3809 |
-+ * -L option is in effect. That option makes us examine the thing the |
3810 |
-+ * symbolic link points to, not the symbolic link itself. |
3811 |
-+ */ |
3812 |
-+int |
3813 |
-+optionl_getfilecon(const char *name, security_context_t *p) |
3814 |
-+{ |
3815 |
-+ int rv = getfilecon(name, p); |
3816 |
-+ if (0 == rv) |
3817 |
-+ return 0; /* normal case. */ |
3818 |
-+ else |
3819 |
-+ return fallback_getfilecon(name, p, rv); |
3820 |
-+} |
3821 |
-+ |
3822 |
-+/* optionp_getfilecon() implements the stat operation when the -P |
3823 |
-+ * option is in effect (this is also the default). That option makes |
3824 |
-+ * us examine the symbolic link itself, not the thing it points to. |
3825 |
-+ */ |
3826 |
-+int |
3827 |
-+optionp_getfilecon(const char *name, security_context_t *p) |
3828 |
-+{ |
3829 |
-+ return lgetfilecon(name, p); |
3830 |
-+} |
3831 |
-+#endif /* WITH_SELINUX */ |
3832 |
-+ |
3833 |
- |
3834 |
- static boolean |
3835 |
- parse_and (const struct parser_table* entry, char **argv, int *arg_ptr) |
3836 |
-@@ -1124,6 +1233,10 @@ tests (N can be +N or -N or N): -amin N |
3837 |
- -readable -writable -executable\n\ |
3838 |
- -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\ |
3839 |
- -used N -user NAME -xtype [bcdpfls]\n")); |
3840 |
-+#ifdef WITH_SELINUX |
3841 |
-+ puts (_("\ |
3842 |
-+ -context CONTEXT\n")); |
3843 |
-+#endif /*WITH_SELINUX*/ |
3844 |
- puts (_("\ |
3845 |
- actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\ |
3846 |
- -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\ |
3847 |
-@@ -2522,6 +2635,29 @@ parse_version (const struct parser_table |
3848 |
- exit (0); |
3849 |
- } |
3850 |
- |
3851 |
-+#ifdef WITH_SELINUX |
3852 |
-+ |
3853 |
-+static boolean |
3854 |
-+parse_scontext ( const struct parser_table* entry, char **argv, int *arg_ptr) |
3855 |
-+{ |
3856 |
-+ struct predicate *our_pred; |
3857 |
-+ |
3858 |
-+ if ( (argv == NULL) || (argv[*arg_ptr] == NULL) ) |
3859 |
-+ return( false ); |
3860 |
-+ |
3861 |
-+ our_pred = insert_primary(entry); |
3862 |
-+ our_pred->need_stat = false; |
3863 |
-+#ifdef DEBUG |
3864 |
-+ our_pred->p_name = find_pred_name (pred_scontext); |
3865 |
-+#endif /*DEBUG*/ |
3866 |
-+ our_pred->args.scontext = argv[*arg_ptr];; |
3867 |
-+ |
3868 |
-+ (*arg_ptr)++; |
3869 |
-+ return( true ); |
3870 |
-+} |
3871 |
-+ |
3872 |
-+#endif /*WITH_SELINUX*/ |
3873 |
-+ |
3874 |
- static boolean |
3875 |
- parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr) |
3876 |
- { |
3877 |
-@@ -2773,7 +2909,11 @@ insert_fprintf (struct format_val *vec, |
3878 |
- if (*scan2 == '.') |
3879 |
- for (scan2++; ISDIGIT (*scan2); scan2++) |
3880 |
- /* Do nothing. */ ; |
3881 |
-+#ifdef WITH_SELINUX |
3882 |
-+ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyYZ", *scan2)) |
3883 |
-+#else |
3884 |
- if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2)) |
3885 |
-+#endif |
3886 |
- { |
3887 |
- segmentp = make_segment (segmentp, format, scan2 - format, |
3888 |
- KIND_FORMAT, *scan2, 0, |
3889 |
-diff -purN findutils-4.3.12.orig/find/parser.c.orig findutils-4.3.12/find/parser.c.orig |
3890 |
---- findutils-4.3.12.orig/find/parser.c.orig 1969-12-31 19:00:00.000000000 -0500 |
3891 |
-+++ findutils-4.3.12/find/parser.c.orig 2007-12-19 16:12:34.000000000 -0500 |
3892 |
-@@ -0,0 +1,3502 @@ |
3893 |
-+/* parser.c -- convert the command line args into an expression tree. |
3894 |
-+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003, |
3895 |
-+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc. |
3896 |
-+ |
3897 |
-+ This program is free software: you can redistribute it and/or modify |
3898 |
-+ it under the terms of the GNU General Public License as published by |
3899 |
-+ the Free Software Foundation, either version 3 of the License, or |
3900 |
-+ (at your option) any later version. |
3901 |
-+ |
3902 |
-+ This program is distributed in the hope that it will be useful, |
3903 |
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
3904 |
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3905 |
-+ GNU General Public License for more details. |
3906 |
-+ |
3907 |
-+ You should have received a copy of the GNU General Public License |
3908 |
-+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
3909 |
-+*/ |
3910 |
-+ |
3911 |
-+#include <config.h> |
3912 |
-+ |
3913 |
-+#include "defs.h" |
3914 |
-+#include <ctype.h> |
3915 |
-+#include <math.h> |
3916 |
-+#include <assert.h> |
3917 |
-+#include <pwd.h> |
3918 |
-+#include <errno.h> |
3919 |
-+#include <grp.h> |
3920 |
-+#include <fnmatch.h> |
3921 |
-+#include "modechange.h" |
3922 |
-+#include "modetype.h" |
3923 |
-+#include "xstrtol.h" |
3924 |
-+#include "xalloc.h" |
3925 |
-+#include "quote.h" |
3926 |
-+#include "quotearg.h" |
3927 |
-+#include "buildcmd.h" |
3928 |
-+#include "nextelem.h" |
3929 |
-+#include "stdio-safer.h" |
3930 |
-+#include "regextype.h" |
3931 |
-+#include "stat-time.h" |
3932 |
-+#include "xstrtod.h" |
3933 |
-+#include "fts_.h" |
3934 |
-+#include "getdate.h" |
3935 |
-+#include "error.h" |
3936 |
-+#include "findutils-version.h" |
3937 |
-+ |
3938 |
-+#include <fcntl.h> |
3939 |
-+ |
3940 |
-+ |
3941 |
-+/* The presence of unistd.h is assumed by gnulib these days, so we |
3942 |
-+ * might as well assume it too. |
3943 |
-+ */ |
3944 |
-+/* We need <unistd.h> for isatty(). */ |
3945 |
-+#include <unistd.h> |
3946 |
-+#include <sys/stat.h> |
3947 |
-+ |
3948 |
-+#if ENABLE_NLS |
3949 |
-+# include <libintl.h> |
3950 |
-+# define _(Text) gettext (Text) |
3951 |
-+#else |
3952 |
-+# define _(Text) Text |
3953 |
-+#endif |
3954 |
-+#ifdef gettext_noop |
3955 |
-+# define N_(String) gettext_noop (String) |
3956 |
-+#else |
3957 |
-+/* See locate.c for explanation as to why not use (String) */ |
3958 |
-+# define N_(String) String |
3959 |
-+#endif |
3960 |
-+ |
3961 |
-+#if !defined (isascii) || defined (STDC_HEADERS) |
3962 |
-+#ifdef isascii |
3963 |
-+#undef isascii |
3964 |
-+#endif |
3965 |
-+#define isascii(c) 1 |
3966 |
-+#endif |
3967 |
-+ |
3968 |
-+#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c)) |
3969 |
-+#define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c)) |
3970 |
-+ |
3971 |
-+#ifndef HAVE_ENDGRENT |
3972 |
-+#define endgrent() |
3973 |
-+#endif |
3974 |
-+#ifndef HAVE_ENDPWENT |
3975 |
-+#define endpwent() |
3976 |
-+#endif |
3977 |
-+ |
3978 |
-+static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr)); |
3979 |
-+static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3980 |
-+static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3981 |
-+static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3982 |
-+static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3983 |
-+static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3984 |
-+static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3985 |
-+static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3986 |
-+static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3987 |
-+static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3988 |
-+static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3989 |
-+static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3990 |
-+static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3991 |
-+static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3992 |
-+static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3993 |
-+static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3994 |
-+static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3995 |
-+static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3996 |
-+static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3997 |
-+static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3998 |
-+static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3999 |
-+static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4000 |
-+static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4001 |
-+static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4002 |
-+static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4003 |
-+static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4004 |
-+static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4005 |
-+static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4006 |
-+static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4007 |
-+static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4008 |
-+static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4009 |
-+static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4010 |
-+static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4011 |
-+static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4012 |
-+static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4013 |
-+static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4014 |
-+static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4015 |
-+static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4016 |
-+static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4017 |
-+static boolean parse_newerXY PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4018 |
-+static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4019 |
-+static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4020 |
-+static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4021 |
-+static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4022 |
-+static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4023 |
-+static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4024 |
-+static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4025 |
-+static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4026 |
-+static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4027 |
-+static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4028 |
-+static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4029 |
-+static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4030 |
-+static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4031 |
-+static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4032 |
-+static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4033 |
-+#if 0 |
4034 |
-+static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4035 |
-+#endif |
4036 |
-+static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4037 |
-+static boolean parse_time PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4038 |
-+static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4039 |
-+static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4040 |
-+static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4041 |
-+static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4042 |
-+static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4043 |
-+static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4044 |
-+static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4045 |
-+static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4046 |
-+static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4047 |
-+static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4048 |
-+static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4049 |
-+static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4050 |
-+static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4051 |
-+ |
4052 |
-+boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4053 |
-+ |
4054 |
-+ |
4055 |
-+static boolean insert_type PARAMS((char **argv, int *arg_ptr, |
4056 |
-+ const struct parser_table *entry, |
4057 |
-+ PRED_FUNC which_pred)); |
4058 |
-+static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, |
4059 |
-+ const struct parser_table *entry, |
4060 |
-+ int regex_options)); |
4061 |
-+static boolean insert_fprintf (struct format_val *vec, |
4062 |
-+ const struct parser_table *entry, |
4063 |
-+ PRED_FUNC func, |
4064 |
-+ const char *format); |
4065 |
-+ |
4066 |
-+static struct segment **make_segment PARAMS((struct segment **segment, |
4067 |
-+ char *format, int len, |
4068 |
-+ int kind, char format_char, |
4069 |
-+ char aux_format_char, |
4070 |
-+ struct predicate *pred)); |
4071 |
-+static boolean insert_exec_ok PARAMS((const char *action, |
4072 |
-+ const struct parser_table *entry, |
4073 |
-+ int dirfd, |
4074 |
-+ char *argv[], |
4075 |
-+ int *arg_ptr)); |
4076 |
-+static boolean get_comp_type PARAMS((const char **str, |
4077 |
-+ enum comparison_type *comp_type)); |
4078 |
-+static boolean get_relative_timestamp PARAMS((const char *str, |
4079 |
-+ struct time_val *tval, |
4080 |
-+ time_t origin, |
4081 |
-+ double sec_per_unit, |
4082 |
-+ const char *overflowmessage)); |
4083 |
-+static boolean get_num PARAMS((const char *str, |
4084 |
-+ uintmax_t *num, |
4085 |
-+ enum comparison_type *comp_type)); |
4086 |
-+static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr, |
4087 |
-+ const struct parser_table *entry)); |
4088 |
-+static void open_output_file (const char *path, struct format_val *p); |
4089 |
-+static void open_stdout (struct format_val *p); |
4090 |
-+static boolean stream_is_tty(FILE *fp); |
4091 |
-+static boolean parse_noop PARAMS((const struct parser_table* entry, |
4092 |
-+ char **argv, int *arg_ptr)); |
4093 |
-+ |
4094 |
-+#define PASTE(x,y) x##y |
4095 |
-+#define STRINGIFY(s) #s |
4096 |
-+ |
4097 |
-+#define PARSE_OPTION(what,suffix) \ |
4098 |
-+ { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL } |
4099 |
-+ |
4100 |
-+#define PARSE_POSOPT(what,suffix) \ |
4101 |
-+ { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL } |
4102 |
-+ |
4103 |
-+#define PARSE_TEST(what,suffix) \ |
4104 |
-+ { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) } |
4105 |
-+ |
4106 |
-+#define PARSE_TEST_NP(what,suffix) \ |
4107 |
-+ { (ARG_TEST), (what), PASTE(parse_,suffix), NULL } |
4108 |
-+ |
4109 |
-+#define PARSE_ACTION(what,suffix) \ |
4110 |
-+ { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) } |
4111 |
-+ |
4112 |
-+#define PARSE_ACTION_NP(what,suffix) \ |
4113 |
-+ { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL } |
4114 |
-+ |
4115 |
-+#define PARSE_PUNCTUATION(what,suffix) \ |
4116 |
-+ { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) } |
4117 |
-+ |
4118 |
-+ |
4119 |
-+/* Predicates we cannot handle in the usual way. If you add an entry |
4120 |
-+ * to this table, double-check the switch statement in |
4121 |
-+ * pred_sanity_check() to make sure that the new case is being |
4122 |
-+ * correctly handled. |
4123 |
-+ */ |
4124 |
-+static struct parser_table const parse_entry_newerXY = |
4125 |
-+ { |
4126 |
-+ ARG_SPECIAL_PARSE, "newerXY", parse_newerXY, pred_newerXY /* BSD */ |
4127 |
-+ }; |
4128 |
-+ |
4129 |
-+/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'. |
4130 |
-+ If they are in some Unix versions of find, they are marked `Unix'. */ |
4131 |
-+ |
4132 |
-+static struct parser_table const parse_table[] = |
4133 |
-+{ |
4134 |
-+ PARSE_PUNCTUATION("!", negate), /* POSIX */ |
4135 |
-+ PARSE_PUNCTUATION("not", negate), /* GNU */ |
4136 |
-+ PARSE_PUNCTUATION("(", openparen), /* POSIX */ |
4137 |
-+ PARSE_PUNCTUATION(")", closeparen), /* POSIX */ |
4138 |
-+ PARSE_PUNCTUATION(",", comma), /* GNU */ |
4139 |
-+ PARSE_PUNCTUATION("a", and), /* POSIX */ |
4140 |
-+ PARSE_TEST ("amin", amin), /* GNU */ |
4141 |
-+ PARSE_PUNCTUATION("and", and), /* GNU */ |
4142 |
-+ PARSE_TEST ("anewer", anewer), /* GNU */ |
4143 |
-+ {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */ |
4144 |
-+ PARSE_TEST ("cmin", cmin), /* GNU */ |
4145 |
-+ PARSE_TEST ("cnewer", cnewer), /* GNU */ |
4146 |
-+ {ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */ |
4147 |
-+ PARSE_POSOPT ("daystart", daystart), /* GNU */ |
4148 |
-+ PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */ |
4149 |
-+ PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */ |
4150 |
-+ PARSE_OPTION ("depth", depth), /* POSIX */ |
4151 |
-+ PARSE_TEST ("empty", empty), /* GNU */ |
4152 |
-+ {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */ |
4153 |
-+ {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */ |
4154 |
-+ PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */ |
4155 |
-+ PARSE_ACTION ("fls", fls), /* GNU */ |
4156 |
-+ PARSE_POSOPT ("follow", follow), /* GNU, Unix */ |
4157 |
-+ PARSE_ACTION ("fprint", fprint), /* GNU */ |
4158 |
-+ PARSE_ACTION ("fprint0", fprint0), /* GNU */ |
4159 |
-+ {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */ |
4160 |
-+ PARSE_TEST ("fstype", fstype), /* GNU, Unix */ |
4161 |
-+ PARSE_TEST ("gid", gid), /* GNU */ |
4162 |
-+ PARSE_TEST ("group", group), /* POSIX */ |
4163 |
-+ PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */ |
4164 |
-+ PARSE_TEST ("ilname", ilname), /* GNU */ |
4165 |
-+ PARSE_TEST ("iname", iname), /* GNU */ |
4166 |
-+ PARSE_TEST ("inum", inum), /* GNU, Unix */ |
4167 |
-+ PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */ |
4168 |
-+ PARSE_TEST_NP ("iregex", iregex), /* GNU */ |
4169 |
-+ PARSE_TEST_NP ("iwholename", iwholename), /* GNU */ |
4170 |
-+ PARSE_TEST ("links", links), /* POSIX */ |
4171 |
-+ PARSE_TEST ("lname", lname), /* GNU */ |
4172 |
-+ PARSE_ACTION ("ls", ls), /* GNU, Unix */ |
4173 |
-+ PARSE_OPTION ("maxdepth", maxdepth), /* GNU */ |
4174 |
-+ PARSE_OPTION ("mindepth", mindepth), /* GNU */ |
4175 |
-+ PARSE_TEST ("mmin", mmin), /* GNU */ |
4176 |
-+ PARSE_OPTION ("mount", xdev), /* Unix */ |
4177 |
-+ {ARG_TEST, "mtime", parse_time, pred_mtime}, /* POSIX */ |
4178 |
-+ PARSE_TEST ("name", name), |
4179 |
-+#ifdef UNIMPLEMENTED_UNIX |
4180 |
-+ PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */ |
4181 |
-+#endif |
4182 |
-+ PARSE_TEST ("newer", newer), /* POSIX */ |
4183 |
-+ {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */ |
4184 |
-+ PARSE_OPTION ("noleaf", noleaf), /* GNU */ |
4185 |
-+ PARSE_TEST ("nogroup", nogroup), /* POSIX */ |
4186 |
-+ PARSE_TEST ("nouser", nouser), /* POSIX */ |
4187 |
-+ PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */ |
4188 |
-+ PARSE_POSOPT ("nowarn", nowarn), /* GNU */ |
4189 |
-+ PARSE_PUNCTUATION("o", or), /* POSIX */ |
4190 |
-+ PARSE_PUNCTUATION("or", or), /* GNU */ |
4191 |
-+ PARSE_ACTION ("ok", ok), /* POSIX */ |
4192 |
-+ PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */ |
4193 |
-+ PARSE_TEST ("path", path), /* GNU, HP-UX, RMS prefers wholename, but anyway soon POSIX */ |
4194 |
-+ PARSE_TEST ("perm", perm), /* POSIX */ |
4195 |
-+ PARSE_ACTION ("print", print), /* POSIX */ |
4196 |
-+ PARSE_ACTION ("print0", print0), /* GNU */ |
4197 |
-+ {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */ |
4198 |
-+ PARSE_ACTION ("prune", prune), /* POSIX */ |
4199 |
-+ PARSE_ACTION ("quit", quit), /* GNU */ |
4200 |
-+ {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */ |
4201 |
-+ PARSE_TEST ("regex", regex), /* GNU */ |
4202 |
-+ PARSE_OPTION ("regextype", regextype), /* GNU */ |
4203 |
-+ PARSE_TEST ("samefile", samefile), /* GNU */ |
4204 |
-+#if 0 |
4205 |
-+ PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */ |
4206 |
-+#endif |
4207 |
-+ PARSE_TEST ("size", size), /* POSIX */ |
4208 |
-+ PARSE_TEST ("type", type), /* POSIX */ |
4209 |
-+ PARSE_TEST ("uid", uid), /* GNU */ |
4210 |
-+ PARSE_TEST ("used", used), /* GNU */ |
4211 |
-+ PARSE_TEST ("user", user), /* POSIX */ |
4212 |
-+ PARSE_OPTION ("warn", warn), /* GNU */ |
4213 |
-+ PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */ |
4214 |
-+ {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */ |
4215 |
-+ PARSE_OPTION ("xdev", xdev), /* POSIX */ |
4216 |
-+ PARSE_TEST ("xtype", xtype), /* GNU */ |
4217 |
-+#ifdef UNIMPLEMENTED_UNIX |
4218 |
-+ /* It's pretty ugly for find to know about archive formats. |
4219 |
-+ Plus what it could do with cpio archives is very limited. |
4220 |
-+ Better to leave it out. */ |
4221 |
-+ PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */ |
4222 |
-+#endif |
4223 |
-+ /* gnulib's stdbool.h might have made true and false into macros, |
4224 |
-+ * so we can't leave named 'true' and 'false' tokens, so we have |
4225 |
-+ * to expeant the relevant entries longhand. |
4226 |
-+ */ |
4227 |
-+ {ARG_TEST, "false", parse_false, pred_false}, /* GNU */ |
4228 |
-+ {ARG_TEST, "true", parse_true, pred_true }, /* GNU */ |
4229 |
-+ {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */ |
4230 |
-+ |
4231 |
-+ /* Various other cases that don't fit neatly into our macro scheme. */ |
4232 |
-+ {ARG_TEST, "help", parse_help, NULL}, /* GNU */ |
4233 |
-+ {ARG_TEST, "-help", parse_help, NULL}, /* GNU */ |
4234 |
-+ {ARG_TEST, "version", parse_version, NULL}, /* GNU */ |
4235 |
-+ {ARG_TEST, "-version", parse_version, NULL}, /* GNU */ |
4236 |
-+ {0, 0, 0, 0} |
4237 |
-+}; |
4238 |
-+ |
4239 |
-+ |
4240 |
-+static const char *first_nonoption_arg = NULL; |
4241 |
-+static const struct parser_table *noop = NULL; |
4242 |
-+ |
4243 |
-+ |
4244 |
-+void |
4245 |
-+check_option_combinations(const struct predicate *p) |
4246 |
-+{ |
4247 |
-+ enum { seen_delete=1u, seen_prune=2u }; |
4248 |
-+ unsigned int predicates = 0u; |
4249 |
-+ |
4250 |
-+ while (p) |
4251 |
-+ { |
4252 |
-+ if (p->pred_func == pred_delete) |
4253 |
-+ predicates |= seen_delete; |
4254 |
-+ else if (p->pred_func == pred_prune) |
4255 |
-+ predicates |= seen_prune; |
4256 |
-+ p = p->pred_next; |
4257 |
-+ } |
4258 |
-+ |
4259 |
-+ if ((predicates & seen_prune) && (predicates & seen_delete)) |
4260 |
-+ { |
4261 |
-+ /* The user specified both -delete and -prune. One might test |
4262 |
-+ * this by first doing |
4263 |
-+ * find dirs .... -prune ..... -print |
4264 |
-+ * to fnd out what's going to get deleted, and then switch to |
4265 |
-+ * find dirs .... -prune ..... -delete |
4266 |
-+ * once we are happy. Unfortunately, the -delete action also |
4267 |
-+ * implicitly turns on -depth, which will affect the behaviour |
4268 |
-+ * of -prune (in fact, it makes it a no-op). In this case we |
4269 |
-+ * would like to prevent unfortunate accidents, so we require |
4270 |
-+ * the user to have explicitly used -depth. |
4271 |
-+ * |
4272 |
-+ * We only get away with this because the -delete predicate is not |
4273 |
-+ * in POSIX. If it was, we couldn't issue a fatal error here. |
4274 |
-+ */ |
4275 |
-+ if (!options.explicit_depth) |
4276 |
-+ { |
4277 |
-+ /* This fixes Savannah bug #20865. */ |
4278 |
-+ error (1, 0, _("The -delete action atomatically turns on -depth, " |
4279 |
-+ "but -prune does nothing when -depth is in effect. " |
4280 |
-+ "If you want to carry on anyway, just explicitly use " |
4281 |
-+ "the -depth option.")); |
4282 |
-+ } |
4283 |
-+ } |
4284 |
-+} |
4285 |
-+ |
4286 |
-+ |
4287 |
-+static const struct parser_table* |
4288 |
-+get_noop(void) |
4289 |
-+{ |
4290 |
-+ int i; |
4291 |
-+ if (NULL == noop) |
4292 |
-+ { |
4293 |
-+ for (i = 0; parse_table[i].parser_name != 0; i++) |
4294 |
-+ { |
4295 |
-+ if (ARG_NOOP ==parse_table[i].type) |
4296 |
-+ { |
4297 |
-+ noop = &(parse_table[i]); |
4298 |
-+ break; |
4299 |
-+ } |
4300 |
-+ } |
4301 |
-+ } |
4302 |
-+ return noop; |
4303 |
-+} |
4304 |
-+ |
4305 |
-+static int |
4306 |
-+get_stat_Ytime(const struct stat *p, |
4307 |
-+ char what, |
4308 |
-+ struct timespec *ret) |
4309 |
-+{ |
4310 |
-+ switch (what) |
4311 |
-+ { |
4312 |
-+ case 'a': |
4313 |
-+ *ret = get_stat_atime(p); |
4314 |
-+ return 1; |
4315 |
-+ case 'B': |
4316 |
-+ *ret = get_stat_birthtime(p); |
4317 |
-+ return (ret->tv_nsec >= 0); |
4318 |
-+ case 'c': |
4319 |
-+ *ret = get_stat_ctime(p); |
4320 |
-+ return 1; |
4321 |
-+ case 'm': |
4322 |
-+ *ret = get_stat_mtime(p); |
4323 |
-+ return 1; |
4324 |
-+ default: |
4325 |
-+ assert (0); |
4326 |
-+ abort(); |
4327 |
-+ } |
4328 |
-+} |
4329 |
-+ |
4330 |
-+void |
4331 |
-+set_follow_state(enum SymlinkOption opt) |
4332 |
-+{ |
4333 |
-+ if (options.debug_options & DebugStat) |
4334 |
-+ { |
4335 |
-+ /* For DebugStat, the choice is made at runtime within debug_stat() |
4336 |
-+ * by checking the contents of the symlink_handling variable. |
4337 |
-+ */ |
4338 |
-+ options.xstat = debug_stat; |
4339 |
-+ } |
4340 |
-+ else |
4341 |
-+ { |
4342 |
-+ switch (opt) |
4343 |
-+ { |
4344 |
-+ case SYMLINK_ALWAYS_DEREF: /* -L */ |
4345 |
-+ options.xstat = optionl_stat; |
4346 |
-+ options.no_leaf_check = true; |
4347 |
-+ break; |
4348 |
-+ |
4349 |
-+ case SYMLINK_NEVER_DEREF: /* -P (default) */ |
4350 |
-+ options.xstat = optionp_stat; |
4351 |
-+ /* Can't turn no_leaf_check off because the user might have specified |
4352 |
-+ * -noleaf anyway |
4353 |
-+ */ |
4354 |
-+ break; |
4355 |
-+ |
4356 |
-+ case SYMLINK_DEREF_ARGSONLY: /* -H */ |
4357 |
-+ options.xstat = optionh_stat; |
4358 |
-+ options.no_leaf_check = true; |
4359 |
-+ } |
4360 |
-+ } |
4361 |
-+ options.symlink_handling = opt; |
4362 |
-+} |
4363 |
-+ |
4364 |
-+ |
4365 |
-+void |
4366 |
-+parse_begin_user_args (char **args, int argno, |
4367 |
-+ const struct predicate *last, |
4368 |
-+ const struct predicate *predicates) |
4369 |
-+{ |
4370 |
-+ (void) args; |
4371 |
-+ (void) argno; |
4372 |
-+ (void) last; |
4373 |
-+ (void) predicates; |
4374 |
-+ first_nonoption_arg = NULL; |
4375 |
-+} |
4376 |
-+ |
4377 |
-+void |
4378 |
-+parse_end_user_args (char **args, int argno, |
4379 |
-+ const struct predicate *last, |
4380 |
-+ const struct predicate *predicates) |
4381 |
-+{ |
4382 |
-+ /* does nothing */ |
4383 |
-+ (void) args; |
4384 |
-+ (void) argno; |
4385 |
-+ (void) last; |
4386 |
-+ (void) predicates; |
4387 |
-+} |
4388 |
-+ |
4389 |
-+ |
4390 |
-+/* Check that it is legal to fid the given primary in its |
4391 |
-+ * position and return it. |
4392 |
-+ */ |
4393 |
-+const struct parser_table* |
4394 |
-+found_parser(const char *original_arg, const struct parser_table *entry) |
4395 |
-+{ |
4396 |
-+ /* If this is an option, but we have already had a |
4397 |
-+ * non-option argument, the user may be under the |
4398 |
-+ * impression that the behaviour of the option |
4399 |
-+ * argument is conditional on some preceding |
4400 |
-+ * tests. This might typically be the case with, |
4401 |
-+ * for example, -maxdepth. |
4402 |
-+ * |
4403 |
-+ * The options -daystart and -follow are exempt |
4404 |
-+ * from this treatment, since their positioning |
4405 |
-+ * in the command line does have an effect on |
4406 |
-+ * subsequent tests but not previous ones. That |
4407 |
-+ * might be intentional on the part of the user. |
4408 |
-+ */ |
4409 |
-+ if (entry->type != ARG_POSITIONAL_OPTION) |
4410 |
-+ { |
4411 |
-+ /* Something other than -follow/-daystart. |
4412 |
-+ * If this is an option, check if it followed |
4413 |
-+ * a non-option and if so, issue a warning. |
4414 |
-+ */ |
4415 |
-+ if (entry->type == ARG_OPTION) |
4416 |
-+ { |
4417 |
-+ if ((first_nonoption_arg != NULL) |
4418 |
-+ && options.warnings ) |
4419 |
-+ { |
4420 |
-+ /* option which follows a non-option */ |
4421 |
-+ error (0, 0, |
4422 |
-+ _("warning: you have specified the %1$s " |
4423 |
-+ "option after a non-option argument %2$s, " |
4424 |
-+ "but options are not positional (%3$s affects " |
4425 |
-+ "tests specified before it as well as those " |
4426 |
-+ "specified after it). Please specify options " |
4427 |
-+ "before other arguments.\n"), |
4428 |
-+ original_arg, |
4429 |
-+ first_nonoption_arg, |
4430 |
-+ original_arg); |
4431 |
-+ } |
4432 |
-+ } |
4433 |
-+ else |
4434 |
-+ { |
4435 |
-+ /* Not an option or a positional option, |
4436 |
-+ * so remember we've seen it in order to |
4437 |
-+ * use it in a possible future warning message. |
4438 |
-+ */ |
4439 |
-+ if (first_nonoption_arg == NULL) |
4440 |
-+ { |
4441 |
-+ first_nonoption_arg = original_arg; |
4442 |
-+ } |
4443 |
-+ } |
4444 |
-+ } |
4445 |
-+ |
4446 |
-+ return entry; |
4447 |
-+} |
4448 |
-+ |
4449 |
-+ |
4450 |
-+/* Return a pointer to the parser function to invoke for predicate |
4451 |
-+ SEARCH_NAME. |
4452 |
-+ Return NULL if SEARCH_NAME is not a valid predicate name. */ |
4453 |
-+ |
4454 |
-+const struct parser_table* |
4455 |
-+find_parser (char *search_name) |
4456 |
-+{ |
4457 |
-+ int i; |
4458 |
-+ const char *original_arg = search_name; |
4459 |
-+ |
4460 |
-+ /* Ugh. Special case -newerXY. */ |
4461 |
-+ if (0 == strncmp("-newer", search_name, 6) |
4462 |
-+ && (8 == strlen(search_name))) |
4463 |
-+ { |
4464 |
-+ return found_parser(original_arg, &parse_entry_newerXY); |
4465 |
-+ } |
4466 |
-+ |
4467 |
-+ if (*search_name == '-') |
4468 |
-+ search_name++; |
4469 |
-+ |
4470 |
-+ for (i = 0; parse_table[i].parser_name != 0; i++) |
4471 |
-+ { |
4472 |
-+ if (strcmp (parse_table[i].parser_name, search_name) == 0) |
4473 |
-+ { |
4474 |
-+ return found_parser(original_arg, &parse_table[i]); |
4475 |
-+ } |
4476 |
-+ } |
4477 |
-+ return NULL; |
4478 |
-+} |
4479 |
-+ |
4480 |
-+static float |
4481 |
-+estimate_file_age_success_rate(float num_days) |
4482 |
-+{ |
4483 |
-+ if (num_days < 0.1) |
4484 |
-+ { |
4485 |
-+ /* Assume 1% of files have timestamps in the future */ |
4486 |
-+ return 0.01f; |
4487 |
-+ } |
4488 |
-+ else if (num_days < 1) |
4489 |
-+ { |
4490 |
-+ /* Assume 30% of files have timestamps today */ |
4491 |
-+ return 0.3f; |
4492 |
-+ } |
4493 |
-+ else if (num_days > 100) |
4494 |
-+ { |
4495 |
-+ /* Assume 30% of files are very old */ |
4496 |
-+ return 0.3f; |
4497 |
-+ } |
4498 |
-+ else |
4499 |
-+ { |
4500 |
-+ /* Assume 39% of files are between 1 and 100 days old. */ |
4501 |
-+ return 0.39f; |
4502 |
-+ } |
4503 |
-+} |
4504 |
-+ |
4505 |
-+static float |
4506 |
-+estimate_timestamp_success_rate(time_t when) |
4507 |
-+{ |
4508 |
-+ int num_days = (options.cur_day_start - when) / 86400; |
4509 |
-+ return estimate_file_age_success_rate(num_days); |
4510 |
-+} |
4511 |
-+ |
4512 |
-+/* Collect an argument from the argument list, or |
4513 |
-+ * return false. |
4514 |
-+ */ |
4515 |
-+static boolean |
4516 |
-+collect_arg(char **argv, int *arg_ptr, const char **collected_arg) |
4517 |
-+{ |
4518 |
-+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
4519 |
-+ { |
4520 |
-+ *collected_arg = NULL; |
4521 |
-+ return false; |
4522 |
-+ } |
4523 |
-+ else |
4524 |
-+ { |
4525 |
-+ *collected_arg = argv[*arg_ptr]; |
4526 |
-+ (*arg_ptr)++; |
4527 |
-+ return true; |
4528 |
-+ } |
4529 |
-+} |
4530 |
-+ |
4531 |
-+static boolean |
4532 |
-+collect_arg_stat_info(char **argv, int *arg_ptr, struct stat *p) |
4533 |
-+{ |
4534 |
-+ const char *filename; |
4535 |
-+ if (collect_arg(argv, arg_ptr, &filename)) |
4536 |
-+ { |
4537 |
-+ if (0 == (options.xstat)(filename, p)) |
4538 |
-+ { |
4539 |
-+ return true; |
4540 |
-+ } |
4541 |
-+ else |
4542 |
-+ { |
4543 |
-+ fatal_file_error(filename); |
4544 |
-+ } |
4545 |
-+ } |
4546 |
-+ else |
4547 |
-+ { |
4548 |
-+ return false; |
4549 |
-+ } |
4550 |
-+} |
4551 |
-+ |
4552 |
-+/* The parsers are responsible to continue scanning ARGV for |
4553 |
-+ their arguments. Each parser knows what is and isn't |
4554 |
-+ allowed for itself. |
4555 |
-+ |
4556 |
-+ ARGV is the argument array. |
4557 |
-+ *ARG_PTR is the index to start at in ARGV, |
4558 |
-+ updated to point beyond the last element consumed. |
4559 |
-+ |
4560 |
-+ The predicate structure is updated with the new information. */ |
4561 |
-+ |
4562 |
-+ |
4563 |
-+static boolean |
4564 |
-+parse_and (const struct parser_table* entry, char **argv, int *arg_ptr) |
4565 |
-+{ |
4566 |
-+ struct predicate *our_pred; |
4567 |
-+ |
4568 |
-+ (void) argv; |
4569 |
-+ (void) arg_ptr; |
4570 |
-+ |
4571 |
-+ our_pred = get_new_pred (entry); |
4572 |
-+ our_pred->pred_func = pred_and; |
4573 |
-+ our_pred->p_type = BI_OP; |
4574 |
-+ our_pred->p_prec = AND_PREC; |
4575 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4576 |
-+ return true; |
4577 |
-+} |
4578 |
-+ |
4579 |
-+static boolean |
4580 |
-+parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr) |
4581 |
-+{ |
4582 |
-+ struct stat stat_newer; |
4583 |
-+ |
4584 |
-+ set_stat_placeholders(&stat_newer); |
4585 |
-+ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) |
4586 |
-+ { |
4587 |
-+ struct predicate *our_pred = insert_primary (entry); |
4588 |
-+ our_pred->args.reftime.xval = XVAL_ATIME; |
4589 |
-+ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); |
4590 |
-+ our_pred->args.reftime.kind = COMP_GT; |
4591 |
-+ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); |
4592 |
-+ return true; |
4593 |
-+ } |
4594 |
-+ return false; |
4595 |
-+} |
4596 |
-+ |
4597 |
-+boolean |
4598 |
-+parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr) |
4599 |
-+{ |
4600 |
-+ struct predicate *our_pred; |
4601 |
-+ |
4602 |
-+ (void) argv; |
4603 |
-+ (void) arg_ptr; |
4604 |
-+ |
4605 |
-+ our_pred = get_new_pred (entry); |
4606 |
-+ our_pred->pred_func = pred_closeparen; |
4607 |
-+ our_pred->p_type = CLOSE_PAREN; |
4608 |
-+ our_pred->p_prec = NO_PREC; |
4609 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4610 |
-+ return true; |
4611 |
-+} |
4612 |
-+ |
4613 |
-+static boolean |
4614 |
-+parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr) |
4615 |
-+{ |
4616 |
-+ struct stat stat_newer; |
4617 |
-+ |
4618 |
-+ set_stat_placeholders(&stat_newer); |
4619 |
-+ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) |
4620 |
-+ { |
4621 |
-+ struct predicate *our_pred = insert_primary (entry); |
4622 |
-+ our_pred->args.reftime.xval = XVAL_CTIME; /* like -newercm */ |
4623 |
-+ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); |
4624 |
-+ our_pred->args.reftime.kind = COMP_GT; |
4625 |
-+ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); |
4626 |
-+ return true; |
4627 |
-+ } |
4628 |
-+ return false; |
4629 |
-+} |
4630 |
-+ |
4631 |
-+static boolean |
4632 |
-+parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr) |
4633 |
-+{ |
4634 |
-+ struct predicate *our_pred; |
4635 |
-+ |
4636 |
-+ (void) argv; |
4637 |
-+ (void) arg_ptr; |
4638 |
-+ |
4639 |
-+ our_pred = get_new_pred (entry); |
4640 |
-+ our_pred->pred_func = pred_comma; |
4641 |
-+ our_pred->p_type = BI_OP; |
4642 |
-+ our_pred->p_prec = COMMA_PREC; |
4643 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4644 |
-+ our_pred->est_success_rate = 1.0f; |
4645 |
-+ return true; |
4646 |
-+} |
4647 |
-+ |
4648 |
-+static boolean |
4649 |
-+parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr) |
4650 |
-+{ |
4651 |
-+ struct tm *local; |
4652 |
-+ |
4653 |
-+ (void) entry; |
4654 |
-+ (void) argv; |
4655 |
-+ (void) arg_ptr; |
4656 |
-+ |
4657 |
-+ if (options.full_days == false) |
4658 |
-+ { |
4659 |
-+ options.cur_day_start += DAYSECS; |
4660 |
-+ local = localtime (&options.cur_day_start); |
4661 |
-+ options.cur_day_start -= (local |
4662 |
-+ ? (local->tm_sec + local->tm_min * 60 |
4663 |
-+ + local->tm_hour * 3600) |
4664 |
-+ : options.cur_day_start % DAYSECS); |
4665 |
-+ options.full_days = true; |
4666 |
-+ } |
4667 |
-+ return true; |
4668 |
-+} |
4669 |
-+ |
4670 |
-+static boolean |
4671 |
-+parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr) |
4672 |
-+{ |
4673 |
-+ struct predicate *our_pred; |
4674 |
-+ (void) argv; |
4675 |
-+ (void) arg_ptr; |
4676 |
-+ |
4677 |
-+ our_pred = insert_primary (entry); |
4678 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
4679 |
-+ /* -delete implies -depth */ |
4680 |
-+ options.do_dir_first = false; |
4681 |
-+ |
4682 |
-+ /* We do not need stat information because we check for the case |
4683 |
-+ * (errno==EISDIR) in pred_delete. |
4684 |
-+ */ |
4685 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4686 |
-+ |
4687 |
-+ our_pred->est_success_rate = 1.0f; |
4688 |
-+ return true; |
4689 |
-+} |
4690 |
-+ |
4691 |
-+static boolean |
4692 |
-+parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr) |
4693 |
-+{ |
4694 |
-+ (void) entry; |
4695 |
-+ (void) argv; |
4696 |
-+ |
4697 |
-+ options.do_dir_first = false; |
4698 |
-+ options.explicit_depth = true; |
4699 |
-+ return parse_noop(entry, argv, arg_ptr); |
4700 |
-+} |
4701 |
-+ |
4702 |
-+static boolean |
4703 |
-+parse_d (const struct parser_table* entry, char **argv, int *arg_ptr) |
4704 |
-+{ |
4705 |
-+ if (options.warnings) |
4706 |
-+ { |
4707 |
-+ error (0, 0, |
4708 |
-+ _("warning: the -d option is deprecated; please use " |
4709 |
-+ "-depth instead, because the latter is a " |
4710 |
-+ "POSIX-compliant feature.")); |
4711 |
-+ } |
4712 |
-+ return parse_depth(entry, argv, arg_ptr); |
4713 |
-+} |
4714 |
-+ |
4715 |
-+static boolean |
4716 |
-+parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr) |
4717 |
-+{ |
4718 |
-+ struct predicate *our_pred; |
4719 |
-+ (void) argv; |
4720 |
-+ (void) arg_ptr; |
4721 |
-+ |
4722 |
-+ our_pred = insert_primary (entry); |
4723 |
-+ our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */ |
4724 |
-+ return true; |
4725 |
-+} |
4726 |
-+ |
4727 |
-+static boolean |
4728 |
-+parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr) |
4729 |
-+{ |
4730 |
-+ return insert_exec_ok ("-exec", entry, get_start_dirfd(), argv, arg_ptr); |
4731 |
-+} |
4732 |
-+ |
4733 |
-+static boolean |
4734 |
-+parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr) |
4735 |
-+{ |
4736 |
-+ return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr); |
4737 |
-+} |
4738 |
-+ |
4739 |
-+static boolean |
4740 |
-+parse_false (const struct parser_table* entry, char **argv, int *arg_ptr) |
4741 |
-+{ |
4742 |
-+ struct predicate *our_pred; |
4743 |
-+ |
4744 |
-+ (void) argv; |
4745 |
-+ (void) arg_ptr; |
4746 |
-+ |
4747 |
-+ our_pred = insert_primary (entry); |
4748 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4749 |
-+ our_pred->side_effects = our_pred->no_default_print = false; |
4750 |
-+ our_pred->est_success_rate = 0.0f; |
4751 |
-+ return true; |
4752 |
-+} |
4753 |
-+ |
4754 |
-+static boolean |
4755 |
-+insert_fls (const struct parser_table* entry, const char *filename) |
4756 |
-+{ |
4757 |
-+ struct predicate *our_pred = insert_primary (entry); |
4758 |
-+ if (filename) |
4759 |
-+ open_output_file (filename, &our_pred->args.printf_vec); |
4760 |
-+ else |
4761 |
-+ open_stdout (&our_pred->args.printf_vec); |
4762 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
4763 |
-+ our_pred->est_success_rate = 1.0f; |
4764 |
-+ return true; |
4765 |
-+} |
4766 |
-+ |
4767 |
-+ |
4768 |
-+static boolean |
4769 |
-+parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr) |
4770 |
-+{ |
4771 |
-+ const char *filename; |
4772 |
-+ return collect_arg(argv, arg_ptr, &filename) |
4773 |
-+ && insert_fls(entry, filename); |
4774 |
-+} |
4775 |
-+ |
4776 |
-+static boolean |
4777 |
-+parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr) |
4778 |
-+{ |
4779 |
-+ set_follow_state(SYMLINK_ALWAYS_DEREF); |
4780 |
-+ return parse_noop(entry, argv, arg_ptr); |
4781 |
-+} |
4782 |
-+ |
4783 |
-+static boolean |
4784 |
-+parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr) |
4785 |
-+{ |
4786 |
-+ struct predicate *our_pred; |
4787 |
-+ const char *filename; |
4788 |
-+ if (collect_arg(argv, arg_ptr, &filename)) |
4789 |
-+ { |
4790 |
-+ our_pred = insert_primary (entry); |
4791 |
-+ open_output_file (filename, &our_pred->args.printf_vec); |
4792 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
4793 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4794 |
-+ our_pred->est_success_rate = 1.0f; |
4795 |
-+ return true; |
4796 |
-+ } |
4797 |
-+ else |
4798 |
-+ { |
4799 |
-+ return false; |
4800 |
-+ } |
4801 |
-+} |
4802 |
-+ |
4803 |
-+static boolean |
4804 |
-+insert_fprint(const struct parser_table* entry, const char *filename) |
4805 |
-+{ |
4806 |
-+ struct predicate *our_pred = insert_primary (entry); |
4807 |
-+ if (filename) |
4808 |
-+ open_output_file (filename, &our_pred->args.printf_vec); |
4809 |
-+ else |
4810 |
-+ open_stdout (&our_pred->args.printf_vec); |
4811 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
4812 |
-+ our_pred->need_stat = our_pred->need_type = false; |
4813 |
-+ our_pred->est_success_rate = 1.0f; |
4814 |
-+ return true; |
4815 |
-+} |
4816 |
-+ |
4817 |
-+ |
4818 |
-+static boolean |
4819 |
-+parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr) |
4820 |
-+{ |
4821 |
-+ const char *filename; |
4822 |
-+ if (collect_arg(argv, arg_ptr, &filename)) |
4823 |
-+ return insert_fprint(entry, filename); |
4824 |
-+ else |
4825 |
-+ return false; |
4826 |
-+} |
4827 |
-+ |
4828 |
-+static float estimate_fstype_success_rate(const char *fsname) |
4829 |
-+{ |
4830 |
-+ struct stat dir_stat; |
4831 |
-+ const char *dir = "/"; |
4832 |
-+ if (0 == stat(dir, &dir_stat)) |
4833 |
-+ { |
4834 |
-+ const char *fstype = filesystem_type(&dir_stat, dir); |
4835 |
-+ /* Assume most files are on the same file system type as the root fs. */ |
4836 |
-+ if (0 == strcmp(fsname, fstype)) |
4837 |
-+ return 0.7f; |
4838 |
-+ else |
4839 |
-+ return 0.3f; |
4840 |
-+ } |
4841 |
-+ return 1.0f; |
4842 |
-+} |
4843 |
-+ |
4844 |
-+ |
4845 |
-+static boolean |
4846 |
-+parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr) |
4847 |
-+{ |
4848 |
-+ const char *typename; |
4849 |
-+ if (collect_arg(argv, arg_ptr, &typename)) |
4850 |
-+ { |
4851 |
-+ struct predicate *our_pred = insert_primary (entry); |
4852 |
-+ our_pred->args.str = typename; |
4853 |
-+ |
4854 |
-+ /* This is an expensive operation, so although there are |
4855 |
-+ * circumstances where it is selective, we ignore this fact |
4856 |
-+ * because we probably don't want to promote this test to the |
4857 |
-+ * front anyway. |
4858 |
-+ */ |
4859 |
-+ our_pred->est_success_rate = estimate_fstype_success_rate(typename); |
4860 |
-+ return true; |
4861 |
-+ } |
4862 |
-+ else |
4863 |
-+ { |
4864 |
-+ return false; |
4865 |
-+ } |
4866 |
-+} |
4867 |
-+ |
4868 |
-+static boolean |
4869 |
-+parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr) |
4870 |
-+{ |
4871 |
-+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
4872 |
-+ if (p) |
4873 |
-+ { |
4874 |
-+ p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2; |
4875 |
-+ return true; |
4876 |
-+ } |
4877 |
-+ else |
4878 |
-+ { |
4879 |
-+ return false; |
4880 |
-+ } |
4881 |
-+} |
4882 |
-+ |
4883 |
-+ |
4884 |
-+static int |
4885 |
-+safe_atoi (const char *s) |
4886 |
-+{ |
4887 |
-+ long lval; |
4888 |
-+ char *end; |
4889 |
-+ |
4890 |
-+ errno = 0; |
4891 |
-+ lval = strtol(s, &end, 10); |
4892 |
-+ if ( (LONG_MAX == lval) || (LONG_MIN == lval) ) |
4893 |
-+ { |
4894 |
-+ /* max/min possible value, or an error. */ |
4895 |
-+ if (errno == ERANGE) |
4896 |
-+ { |
4897 |
-+ /* too big, or too small. */ |
4898 |
-+ error(1, errno, "%s", s); |
4899 |
-+ } |
4900 |
-+ else |
4901 |
-+ { |
4902 |
-+ /* not a valid number */ |
4903 |
-+ error(1, errno, "%s", s); |
4904 |
-+ } |
4905 |
-+ /* Otherwise, we do a range chack against INT_MAX and INT_MIN |
4906 |
-+ * below. |
4907 |
-+ */ |
4908 |
-+ } |
4909 |
-+ |
4910 |
-+ if (lval > INT_MAX || lval < INT_MIN) |
4911 |
-+ { |
4912 |
-+ /* The number was in range for long, but not int. */ |
4913 |
-+ errno = ERANGE; |
4914 |
-+ error(1, errno, "%s", s); |
4915 |
-+ } |
4916 |
-+ else if (*end) |
4917 |
-+ { |
4918 |
-+ error(1, errno, "Unexpected suffix %s on %s", |
4919 |
-+ quotearg_n_style(0, options.err_quoting_style, end), |
4920 |
-+ quotearg_n_style(1, options.err_quoting_style, s)); |
4921 |
-+ } |
4922 |
-+ else if (end == s) |
4923 |
-+ { |
4924 |
-+ error(1, errno, "Expected an integer: %s", |
4925 |
-+ quotearg_n_style(0, options.err_quoting_style, s)); |
4926 |
-+ } |
4927 |
-+ return (int)lval; |
4928 |
-+} |
4929 |
-+ |
4930 |
-+ |
4931 |
-+static boolean |
4932 |
-+parse_group (const struct parser_table* entry, char **argv, int *arg_ptr) |
4933 |
-+{ |
4934 |
-+ const char *groupname; |
4935 |
-+ |
4936 |
-+ if (collect_arg(argv, arg_ptr, &groupname)) |
4937 |
-+ { |
4938 |
-+ gid_t gid; |
4939 |
-+ struct predicate *our_pred; |
4940 |
-+ struct group *cur_gr = getgrnam(groupname); |
4941 |
-+ endgrent(); |
4942 |
-+ if (cur_gr) |
4943 |
-+ { |
4944 |
-+ gid = cur_gr->gr_gid; |
4945 |
-+ } |
4946 |
-+ else |
4947 |
-+ { |
4948 |
-+ const int gid_len = strspn (groupname, "0123456789"); |
4949 |
-+ if (gid_len) |
4950 |
-+ { |
4951 |
-+ if (groupname[gid_len] == 0) |
4952 |
-+ { |
4953 |
-+ gid = safe_atoi (groupname); |
4954 |
-+ } |
4955 |
-+ else |
4956 |
-+ { |
4957 |
-+ /* XXX: no test in test suite for this */ |
4958 |
-+ error(1, 0, _("%1$s is not the name of an existing group and" |
4959 |
-+ " it does not look like a numeric group ID " |
4960 |
-+ "because it has the unexpected suffix %2$s"), |
4961 |
-+ quotearg_n_style(0, options.err_quoting_style, groupname), |
4962 |
-+ quotearg_n_style(1, options.err_quoting_style, groupname+gid_len)); |
4963 |
-+ return false; |
4964 |
-+ } |
4965 |
-+ } |
4966 |
-+ else |
4967 |
-+ { |
4968 |
-+ if (*groupname) |
4969 |
-+ { |
4970 |
-+ /* XXX: no test in test suite for this */ |
4971 |
-+ error(1, 0, _("%s is not the name of an existing group"), |
4972 |
-+ quotearg_n_style(0, options.err_quoting_style, groupname)); |
4973 |
-+ } |
4974 |
-+ else |
4975 |
-+ { |
4976 |
-+ error(1, 0, _("argument to -group is empty, but should be a group name")); |
4977 |
-+ } |
4978 |
-+ return false; |
4979 |
-+ } |
4980 |
-+ } |
4981 |
-+ our_pred = insert_primary (entry); |
4982 |
-+ our_pred->args.gid = gid; |
4983 |
-+ our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2; |
4984 |
-+ return true; |
4985 |
-+ } |
4986 |
-+ return false; |
4987 |
-+} |
4988 |
-+ |
4989 |
-+static boolean |
4990 |
-+parse_help (const struct parser_table* entry, char **argv, int *arg_ptr) |
4991 |
-+{ |
4992 |
-+ (void) entry; |
4993 |
-+ (void) argv; |
4994 |
-+ (void) arg_ptr; |
4995 |
-+ |
4996 |
-+ usage(stdout, 0, NULL); |
4997 |
-+ puts (_("\n\ |
4998 |
-+default path is the current directory; default expression is -print\n\ |
4999 |
-+expression may consist of: operators, options, tests, and actions:\n")); |
5000 |
-+ puts (_("\ |
5001 |
-+operators (decreasing precedence; -and is implicit where no others are given):\n\ |
5002 |
-+ ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\ |
5003 |
-+ EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n")); |
5004 |
-+ puts (_("\ |
5005 |
-+positional options (always true): -daystart -follow -regextype\n\n\ |
5006 |
-+normal options (always true, specified before other expressions):\n\ |
5007 |
-+ -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\ |
5008 |
-+ --version -xdev -ignore_readdir_race -noignore_readdir_race\n")); |
5009 |
-+ puts (_("\ |
5010 |
-+tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\ |
5011 |
-+ -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\ |
5012 |
-+ -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\ |
5013 |
-+ -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE")); |
5014 |
-+ puts (_("\ |
5015 |
-+ -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\ |
5016 |
-+ -readable -writable -executable\n\ |
5017 |
-+ -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\ |
5018 |
-+ -used N -user NAME -xtype [bcdpfls]\n")); |
5019 |
-+ puts (_("\ |
5020 |
-+actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\ |
5021 |
-+ -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\ |
5022 |
-+ -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\ |
5023 |
-+ -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\ |
5024 |
-+")); |
5025 |
-+ puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\ |
5026 |
-+page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\ |
5027 |
-+email to <bug-findutils@×××.org>.")); |
5028 |
-+ exit (0); |
5029 |
-+} |
5030 |
-+ |
5031 |
-+static float |
5032 |
-+estimate_pattern_match_rate(const char *pattern, int is_regex) |
5033 |
-+{ |
5034 |
-+ if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, "."))) |
5035 |
-+ { |
5036 |
-+ /* A wildcard; assume the pattern matches most files. */ |
5037 |
-+ return 0.8f; |
5038 |
-+ } |
5039 |
-+ else |
5040 |
-+ { |
5041 |
-+ return 0.1f; |
5042 |
-+ } |
5043 |
-+} |
5044 |
-+ |
5045 |
-+static boolean |
5046 |
-+parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr) |
5047 |
-+{ |
5048 |
-+ const char *name; |
5049 |
-+ if (collect_arg(argv, arg_ptr, &name)) |
5050 |
-+ { |
5051 |
-+ struct predicate *our_pred = insert_primary (entry); |
5052 |
-+ our_pred->args.str = name; |
5053 |
-+ /* Use the generic glob pattern estimator to figure out how many |
5054 |
-+ * links will match, but bear in mind that most files won't be links. |
5055 |
-+ */ |
5056 |
-+ our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0); |
5057 |
-+ return true; |
5058 |
-+ } |
5059 |
-+ else |
5060 |
-+ { |
5061 |
-+ return false; |
5062 |
-+ } |
5063 |
-+} |
5064 |
-+ |
5065 |
-+ |
5066 |
-+/* sanity check the fnmatch() function to make sure that case folding |
5067 |
-+ * is supported (as opposed to just having the flag ignored). |
5068 |
-+ */ |
5069 |
-+static boolean |
5070 |
-+fnmatch_sanitycheck(void) |
5071 |
-+{ |
5072 |
-+ static boolean checked = false; |
5073 |
-+ if (!checked) |
5074 |
-+ { |
5075 |
-+ if (0 != fnmatch("foo", "foo", 0) |
5076 |
-+ || 0 == fnmatch("Foo", "foo", 0) |
5077 |
-+ || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD)) |
5078 |
-+ { |
5079 |
-+ error (1, 0, _("sanity check of the fnmatch() library function failed.")); |
5080 |
-+ return false; |
5081 |
-+ } |
5082 |
-+ checked = true; |
5083 |
-+ } |
5084 |
-+ return checked; |
5085 |
-+} |
5086 |
-+ |
5087 |
-+ |
5088 |
-+static boolean |
5089 |
-+check_name_arg(const char *pred, const char *arg) |
5090 |
-+{ |
5091 |
-+ if (options.warnings && strchr(arg, '/')) |
5092 |
-+ { |
5093 |
-+ error(0, 0,_("warning: Unix filenames usually don't contain slashes " |
5094 |
-+ "(though pathnames do). That means that '%s %s' will " |
5095 |
-+ "probably evaluate to false all the time on this system. " |
5096 |
-+ "You might find the '-wholename' test more useful, or " |
5097 |
-+ "perhaps '-samefile'. Alternatively, if you are using " |
5098 |
-+ "GNU grep, you could " |
5099 |
-+ "use 'find ... -print0 | grep -FzZ %s'."), |
5100 |
-+ pred, |
5101 |
-+ safely_quote_err_filename(0, arg), |
5102 |
-+ safely_quote_err_filename(1, arg)); |
5103 |
-+ } |
5104 |
-+ return true; /* allow it anyway */ |
5105 |
-+} |
5106 |
-+ |
5107 |
-+ |
5108 |
-+ |
5109 |
-+static boolean |
5110 |
-+parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr) |
5111 |
-+{ |
5112 |
-+ const char *name; |
5113 |
-+ fnmatch_sanitycheck(); |
5114 |
-+ if (collect_arg(argv, arg_ptr, &name)) |
5115 |
-+ { |
5116 |
-+ if (check_name_arg("-iname", name)) |
5117 |
-+ { |
5118 |
-+ struct predicate *our_pred = insert_primary (entry); |
5119 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5120 |
-+ our_pred->args.str = name; |
5121 |
-+ our_pred->est_success_rate = estimate_pattern_match_rate(name, 0); |
5122 |
-+ return true; |
5123 |
-+ } |
5124 |
-+ } |
5125 |
-+ return false; |
5126 |
-+} |
5127 |
-+ |
5128 |
-+static boolean |
5129 |
-+parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr) |
5130 |
-+{ |
5131 |
-+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
5132 |
-+ if (p) |
5133 |
-+ { |
5134 |
-+ /* inode number is exact match only, so very low proportions of |
5135 |
-+ * files match |
5136 |
-+ */ |
5137 |
-+ p->est_success_rate = 1e-6; |
5138 |
-+ return true; |
5139 |
-+ } |
5140 |
-+ else |
5141 |
-+ { |
5142 |
-+ return false; |
5143 |
-+ } |
5144 |
-+} |
5145 |
-+ |
5146 |
-+/* -ipath is deprecated (at RMS's request) in favour of |
5147 |
-+ * -iwholename. See the node "GNU Manuals" in standards.texi |
5148 |
-+ * for the rationale for this (basically, GNU prefers the use |
5149 |
-+ * of the phrase "file name" to "path name" |
5150 |
-+ */ |
5151 |
-+static boolean |
5152 |
-+parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr) |
5153 |
-+{ |
5154 |
-+ const char *name; |
5155 |
-+ |
5156 |
-+ fnmatch_sanitycheck (); |
5157 |
-+ if (collect_arg (argv, arg_ptr, &name)) |
5158 |
-+ { |
5159 |
-+ struct predicate *our_pred = insert_primary_withpred (entry, pred_ipath); |
5160 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5161 |
-+ our_pred->args.str = name; |
5162 |
-+ our_pred->est_success_rate = estimate_pattern_match_rate (name, 0); |
5163 |
-+ return true; |
5164 |
-+ } |
5165 |
-+ return false; |
5166 |
-+} |
5167 |
-+ |
5168 |
-+static boolean |
5169 |
-+parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr) |
5170 |
-+{ |
5171 |
-+ return parse_ipath (entry, argv, arg_ptr); |
5172 |
-+} |
5173 |
-+ |
5174 |
-+static boolean |
5175 |
-+parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr) |
5176 |
-+{ |
5177 |
-+ return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options); |
5178 |
-+} |
5179 |
-+ |
5180 |
-+static boolean |
5181 |
-+parse_links (const struct parser_table* entry, char **argv, int *arg_ptr) |
5182 |
-+{ |
5183 |
-+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
5184 |
-+ if (p) |
5185 |
-+ { |
5186 |
-+ if (p->args.numinfo.l_val == 1) |
5187 |
-+ p->est_success_rate = 0.99; |
5188 |
-+ else if (p->args.numinfo.l_val == 2) |
5189 |
-+ p->est_success_rate = 0.01; |
5190 |
-+ else |
5191 |
-+ p->est_success_rate = 1e-3; |
5192 |
-+ return true; |
5193 |
-+ } |
5194 |
-+ else |
5195 |
-+ { |
5196 |
-+ return false; |
5197 |
-+ } |
5198 |
-+} |
5199 |
-+ |
5200 |
-+static boolean |
5201 |
-+parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr) |
5202 |
-+{ |
5203 |
-+ const char *name; |
5204 |
-+ fnmatch_sanitycheck(); |
5205 |
-+ if (collect_arg(argv, arg_ptr, &name)) |
5206 |
-+ { |
5207 |
-+ struct predicate *our_pred = insert_primary (entry); |
5208 |
-+ our_pred->args.str = name; |
5209 |
-+ our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0); |
5210 |
-+ return true; |
5211 |
-+ } |
5212 |
-+ return false; |
5213 |
-+} |
5214 |
-+ |
5215 |
-+static boolean |
5216 |
-+parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr) |
5217 |
-+{ |
5218 |
-+ (void) &argv; |
5219 |
-+ (void) &arg_ptr; |
5220 |
-+ return insert_fls(entry, NULL); |
5221 |
-+} |
5222 |
-+ |
5223 |
-+static boolean |
5224 |
-+insert_depthspec(const struct parser_table* entry, char **argv, int *arg_ptr, |
5225 |
-+ int *limitptr) |
5226 |
-+{ |
5227 |
-+ const char *depthstr; |
5228 |
-+ int depth_len; |
5229 |
-+ const char *predicate = argv[(*arg_ptr)-1]; |
5230 |
-+ if (collect_arg(argv, arg_ptr, &depthstr)) |
5231 |
-+ { |
5232 |
-+ depth_len = strspn (depthstr, "0123456789"); |
5233 |
-+ if ((depth_len > 0) && (depthstr[depth_len] == 0)) |
5234 |
-+ { |
5235 |
-+ (*limitptr) = safe_atoi (depthstr); |
5236 |
-+ if (*limitptr >= 0) |
5237 |
-+ { |
5238 |
-+ return parse_noop(entry, argv, arg_ptr); |
5239 |
-+ } |
5240 |
-+ } |
5241 |
-+ error(1, 0, _("Expected a positive decimal integer argument to %1$s, but got %2$s"), |
5242 |
-+ predicate, |
5243 |
-+ quotearg_n_style(0, options.err_quoting_style, depthstr)); |
5244 |
-+ return false; |
5245 |
-+ } |
5246 |
-+ /* missing argument */ |
5247 |
-+ return false; |
5248 |
-+} |
5249 |
-+ |
5250 |
-+ |
5251 |
-+static boolean |
5252 |
-+parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr) |
5253 |
-+{ |
5254 |
-+ return insert_depthspec(entry, argv, arg_ptr, &options.maxdepth); |
5255 |
-+} |
5256 |
-+ |
5257 |
-+static boolean |
5258 |
-+parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr) |
5259 |
-+{ |
5260 |
-+ return insert_depthspec(entry, argv, arg_ptr, &options.mindepth); |
5261 |
-+} |
5262 |
-+ |
5263 |
-+ |
5264 |
-+static boolean |
5265 |
-+do_parse_xmin (const struct parser_table* entry, |
5266 |
-+ char **argv, |
5267 |
-+ int *arg_ptr, |
5268 |
-+ enum xval xv) |
5269 |
-+{ |
5270 |
-+ const char *minutes; |
5271 |
-+ |
5272 |
-+ if (collect_arg(argv, arg_ptr, &minutes)) |
5273 |
-+ { |
5274 |
-+ struct time_val tval; |
5275 |
-+ tval.xval = xv; |
5276 |
-+ if (get_relative_timestamp(minutes, &tval, |
5277 |
-+ options.cur_day_start + DAYSECS, 60, |
5278 |
-+ "arithmetic overflow while converting %s " |
5279 |
-+ "minutes to a number of seconds")) |
5280 |
-+ { |
5281 |
-+ struct predicate *our_pred = insert_primary (entry); |
5282 |
-+ our_pred->args.reftime = tval; |
5283 |
-+ our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec); |
5284 |
-+ return true; |
5285 |
-+ } |
5286 |
-+ } |
5287 |
-+ return false; |
5288 |
-+} |
5289 |
-+static boolean |
5290 |
-+parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr) |
5291 |
-+{ |
5292 |
-+ return do_parse_xmin(entry, argv, arg_ptr, XVAL_ATIME); |
5293 |
-+} |
5294 |
-+ |
5295 |
-+static boolean |
5296 |
-+parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr) |
5297 |
-+{ |
5298 |
-+ return do_parse_xmin(entry, argv, arg_ptr, XVAL_CTIME); |
5299 |
-+} |
5300 |
-+ |
5301 |
-+ |
5302 |
-+static boolean |
5303 |
-+parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr) |
5304 |
-+{ |
5305 |
-+ return do_parse_xmin(entry, argv, arg_ptr, XVAL_MTIME); |
5306 |
-+} |
5307 |
-+ |
5308 |
-+static boolean |
5309 |
-+parse_name (const struct parser_table* entry, char **argv, int *arg_ptr) |
5310 |
-+{ |
5311 |
-+ const char *name; |
5312 |
-+ if (collect_arg(argv, arg_ptr, &name)) |
5313 |
-+ { |
5314 |
-+ fnmatch_sanitycheck(); |
5315 |
-+ if (check_name_arg("-name", name)) |
5316 |
-+ { |
5317 |
-+ struct predicate *our_pred = insert_primary (entry); |
5318 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5319 |
-+ our_pred->args.str = name; |
5320 |
-+ our_pred->est_success_rate = estimate_pattern_match_rate(name, 0); |
5321 |
-+ return true; |
5322 |
-+ } |
5323 |
-+ } |
5324 |
-+ return false; |
5325 |
-+} |
5326 |
-+ |
5327 |
-+static boolean |
5328 |
-+parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr) |
5329 |
-+{ |
5330 |
-+ struct predicate *our_pred; |
5331 |
-+ |
5332 |
-+ (void) &argv; |
5333 |
-+ (void) &arg_ptr; |
5334 |
-+ |
5335 |
-+ our_pred = get_new_pred_chk_op (entry); |
5336 |
-+ our_pred->pred_func = pred_negate; |
5337 |
-+ our_pred->p_type = UNI_OP; |
5338 |
-+ our_pred->p_prec = NEGATE_PREC; |
5339 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5340 |
-+ return true; |
5341 |
-+} |
5342 |
-+ |
5343 |
-+static boolean |
5344 |
-+parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr) |
5345 |
-+{ |
5346 |
-+ struct predicate *our_pred; |
5347 |
-+ struct stat stat_newer; |
5348 |
-+ |
5349 |
-+ set_stat_placeholders(&stat_newer); |
5350 |
-+ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) |
5351 |
-+ { |
5352 |
-+ our_pred = insert_primary (entry); |
5353 |
-+ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); |
5354 |
-+ our_pred->args.reftime.xval = XVAL_MTIME; |
5355 |
-+ our_pred->args.reftime.kind = COMP_GT; |
5356 |
-+ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); |
5357 |
-+ return true; |
5358 |
-+ } |
5359 |
-+ return false; |
5360 |
-+} |
5361 |
-+ |
5362 |
-+ |
5363 |
-+static boolean |
5364 |
-+parse_newerXY (const struct parser_table* entry, char **argv, int *arg_ptr) |
5365 |
-+{ |
5366 |
-+ (void) argv; |
5367 |
-+ (void) arg_ptr; |
5368 |
-+ |
5369 |
-+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
5370 |
-+ { |
5371 |
-+ return false; |
5372 |
-+ } |
5373 |
-+ else if (8u != strlen(argv[*arg_ptr])) |
5374 |
-+ { |
5375 |
-+ return false; |
5376 |
-+ } |
5377 |
-+ else |
5378 |
-+ { |
5379 |
-+ char x, y; |
5380 |
-+ const char validchars[] = "aBcmt"; |
5381 |
-+ |
5382 |
-+ assert (0 == strncmp("-newer", argv[*arg_ptr], 6)); |
5383 |
-+ x = argv[*arg_ptr][6]; |
5384 |
-+ y = argv[*arg_ptr][7]; |
5385 |
-+ |
5386 |
-+ |
5387 |
-+#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC) |
5388 |
-+ if ('B' == x || 'B' == y) |
5389 |
-+ { |
5390 |
-+ error(0, 0, |
5391 |
-+ _("This system does not provide a way to find the birth time of a file.")); |
5392 |
-+ return 0; |
5393 |
-+ } |
5394 |
-+#endif |
5395 |
-+ |
5396 |
-+ /* -newertY (for any Y) is invalid. */ |
5397 |
-+ if (x == 't' |
5398 |
-+ || 0 == strchr(validchars, x) |
5399 |
-+ || 0 == strchr( validchars, y)) |
5400 |
-+ { |
5401 |
-+ return false; |
5402 |
-+ } |
5403 |
-+ else |
5404 |
-+ { |
5405 |
-+ struct predicate *our_pred; |
5406 |
-+ |
5407 |
-+ /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr |
5408 |
-+ * past the test name (for most other tests, this is already done) |
5409 |
-+ */ |
5410 |
-+ (*arg_ptr)++; |
5411 |
-+ |
5412 |
-+ our_pred = insert_primary (entry); |
5413 |
-+ |
5414 |
-+ |
5415 |
-+ switch (x) |
5416 |
-+ { |
5417 |
-+ case 'a': |
5418 |
-+ our_pred->args.reftime.xval = XVAL_ATIME; |
5419 |
-+ break; |
5420 |
-+ case 'B': |
5421 |
-+ our_pred->args.reftime.xval = XVAL_BIRTHTIME; |
5422 |
-+ break; |
5423 |
-+ case 'c': |
5424 |
-+ our_pred->args.reftime.xval = XVAL_CTIME; |
5425 |
-+ break; |
5426 |
-+ case 'm': |
5427 |
-+ our_pred->args.reftime.xval = XVAL_MTIME; |
5428 |
-+ break; |
5429 |
-+ default: |
5430 |
-+ assert (strchr(validchars, x)); |
5431 |
-+ assert (0); |
5432 |
-+ } |
5433 |
-+ |
5434 |
-+ if ('t' == y) |
5435 |
-+ { |
5436 |
-+ if (!get_date(&our_pred->args.reftime.ts, |
5437 |
-+ argv[*arg_ptr], |
5438 |
-+ &options.start_time)) |
5439 |
-+ { |
5440 |
-+ error(1, 0, |
5441 |
-+ _("I cannot figure out how to interpret %s as a date or time"), |
5442 |
-+ quotearg_n_style(0, options.err_quoting_style, argv[*arg_ptr])); |
5443 |
-+ } |
5444 |
-+ } |
5445 |
-+ else |
5446 |
-+ { |
5447 |
-+ struct stat stat_newer; |
5448 |
-+ |
5449 |
-+ /* Stat the named file. */ |
5450 |
-+ set_stat_placeholders(&stat_newer); |
5451 |
-+ if ((*options.xstat) (argv[*arg_ptr], &stat_newer)) |
5452 |
-+ fatal_file_error(argv[*arg_ptr]); |
5453 |
-+ |
5454 |
-+ if (!get_stat_Ytime(&stat_newer, y, &our_pred->args.reftime.ts)) |
5455 |
-+ { |
5456 |
-+ /* We cannot extract a timestamp from the struct stat. */ |
5457 |
-+ error(1, 0, _("Cannot obtain birth time of file %s"), |
5458 |
-+ safely_quote_err_filename(0, argv[*arg_ptr])); |
5459 |
-+ } |
5460 |
-+ } |
5461 |
-+ our_pred->args.reftime.kind = COMP_GT; |
5462 |
-+ our_pred->est_success_rate = estimate_timestamp_success_rate(our_pred->args.reftime.ts.tv_sec); |
5463 |
-+ (*arg_ptr)++; |
5464 |
-+ |
5465 |
-+ assert (our_pred->pred_func != NULL); |
5466 |
-+ assert (our_pred->pred_func == pred_newerXY); |
5467 |
-+ assert (our_pred->need_stat); |
5468 |
-+ return true; |
5469 |
-+ } |
5470 |
-+ } |
5471 |
-+} |
5472 |
-+ |
5473 |
-+ |
5474 |
-+static boolean |
5475 |
-+parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr) |
5476 |
-+{ |
5477 |
-+ options.no_leaf_check = true; |
5478 |
-+ return parse_noop(entry, argv, arg_ptr); |
5479 |
-+} |
5480 |
-+ |
5481 |
-+#ifdef CACHE_IDS |
5482 |
-+/* Arbitrary amount by which to increase size |
5483 |
-+ of `uid_unused' and `gid_unused'. */ |
5484 |
-+#define ALLOC_STEP 2048 |
5485 |
-+ |
5486 |
-+/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */ |
5487 |
-+char *uid_unused = NULL; |
5488 |
-+ |
5489 |
-+/* Number of elements in `uid_unused'. */ |
5490 |
-+unsigned uid_allocated; |
5491 |
-+ |
5492 |
-+/* Similar for GIDs and group entries. */ |
5493 |
-+char *gid_unused = NULL; |
5494 |
-+unsigned gid_allocated; |
5495 |
-+#endif |
5496 |
-+ |
5497 |
-+static boolean |
5498 |
-+parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr) |
5499 |
-+{ |
5500 |
-+ struct predicate *our_pred; |
5501 |
-+ |
5502 |
-+ (void) &argv; |
5503 |
-+ (void) &arg_ptr; |
5504 |
-+ |
5505 |
-+ our_pred = insert_primary (entry); |
5506 |
-+ our_pred->est_success_rate = 1e-4; |
5507 |
-+#ifdef CACHE_IDS |
5508 |
-+ if (gid_unused == NULL) |
5509 |
-+ { |
5510 |
-+ struct group *gr; |
5511 |
-+ |
5512 |
-+ gid_allocated = ALLOC_STEP; |
5513 |
-+ gid_unused = xmalloc (gid_allocated); |
5514 |
-+ memset (gid_unused, 1, gid_allocated); |
5515 |
-+ setgrent (); |
5516 |
-+ while ((gr = getgrent ()) != NULL) |
5517 |
-+ { |
5518 |
-+ if ((unsigned) gr->gr_gid >= gid_allocated) |
5519 |
-+ { |
5520 |
-+ unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP; |
5521 |
-+ gid_unused = xrealloc (gid_unused, new_allocated); |
5522 |
-+ memset (gid_unused + gid_allocated, 1, |
5523 |
-+ new_allocated - gid_allocated); |
5524 |
-+ gid_allocated = new_allocated; |
5525 |
-+ } |
5526 |
-+ gid_unused[(unsigned) gr->gr_gid] = 0; |
5527 |
-+ } |
5528 |
-+ endgrent (); |
5529 |
-+ } |
5530 |
-+#endif |
5531 |
-+ return true; |
5532 |
-+} |
5533 |
-+ |
5534 |
-+static boolean |
5535 |
-+parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr) |
5536 |
-+{ |
5537 |
-+ struct predicate *our_pred; |
5538 |
-+ (void) argv; |
5539 |
-+ (void) arg_ptr; |
5540 |
-+ |
5541 |
-+ |
5542 |
-+ our_pred = insert_primary (entry); |
5543 |
-+ our_pred->est_success_rate = 1e-3; |
5544 |
-+#ifdef CACHE_IDS |
5545 |
-+ if (uid_unused == NULL) |
5546 |
-+ { |
5547 |
-+ struct passwd *pw; |
5548 |
-+ |
5549 |
-+ uid_allocated = ALLOC_STEP; |
5550 |
-+ uid_unused = xmalloc (uid_allocated); |
5551 |
-+ memset (uid_unused, 1, uid_allocated); |
5552 |
-+ setpwent (); |
5553 |
-+ while ((pw = getpwent ()) != NULL) |
5554 |
-+ { |
5555 |
-+ if ((unsigned) pw->pw_uid >= uid_allocated) |
5556 |
-+ { |
5557 |
-+ unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP; |
5558 |
-+ uid_unused = xrealloc (uid_unused, new_allocated); |
5559 |
-+ memset (uid_unused + uid_allocated, 1, |
5560 |
-+ new_allocated - uid_allocated); |
5561 |
-+ uid_allocated = new_allocated; |
5562 |
-+ } |
5563 |
-+ uid_unused[(unsigned) pw->pw_uid] = 0; |
5564 |
-+ } |
5565 |
-+ endpwent (); |
5566 |
-+ } |
5567 |
-+#endif |
5568 |
-+ return true; |
5569 |
-+} |
5570 |
-+ |
5571 |
-+static boolean |
5572 |
-+parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr) |
5573 |
-+{ |
5574 |
-+ options.warnings = false; |
5575 |
-+ return parse_noop(entry, argv, arg_ptr); |
5576 |
-+} |
5577 |
-+ |
5578 |
-+static boolean |
5579 |
-+parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr) |
5580 |
-+{ |
5581 |
-+ return insert_exec_ok ("-ok", entry, get_start_dirfd(), argv, arg_ptr); |
5582 |
-+} |
5583 |
-+ |
5584 |
-+static boolean |
5585 |
-+parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr) |
5586 |
-+{ |
5587 |
-+ return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr); |
5588 |
-+} |
5589 |
-+ |
5590 |
-+boolean |
5591 |
-+parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr) |
5592 |
-+{ |
5593 |
-+ struct predicate *our_pred; |
5594 |
-+ |
5595 |
-+ (void) argv; |
5596 |
-+ (void) arg_ptr; |
5597 |
-+ |
5598 |
-+ our_pred = get_new_pred_chk_op (entry); |
5599 |
-+ our_pred->pred_func = pred_openparen; |
5600 |
-+ our_pred->p_type = OPEN_PAREN; |
5601 |
-+ our_pred->p_prec = NO_PREC; |
5602 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5603 |
-+ return true; |
5604 |
-+} |
5605 |
-+ |
5606 |
-+static boolean |
5607 |
-+parse_or (const struct parser_table* entry, char **argv, int *arg_ptr) |
5608 |
-+{ |
5609 |
-+ struct predicate *our_pred; |
5610 |
-+ |
5611 |
-+ (void) argv; |
5612 |
-+ (void) arg_ptr; |
5613 |
-+ |
5614 |
-+ our_pred = get_new_pred (entry); |
5615 |
-+ our_pred->pred_func = pred_or; |
5616 |
-+ our_pred->p_type = BI_OP; |
5617 |
-+ our_pred->p_prec = OR_PREC; |
5618 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5619 |
-+ return true; |
5620 |
-+} |
5621 |
-+ |
5622 |
-+/* For some time, -path was deprecated (at RMS's request) in favour of |
5623 |
-+ * -iwholename. See the node "GNU Manuals" in standards.texi for the |
5624 |
-+ * rationale for this (basically, GNU prefers the use of the phrase |
5625 |
-+ * "file name" to "path name". |
5626 |
-+ * |
5627 |
-+ * We do not issue a warning that this usage is deprecated |
5628 |
-+ * since |
5629 |
-+ * (a) HPUX find supports this predicate also and |
5630 |
-+ * (b) it will soon be in POSIX anyway. |
5631 |
-+ */ |
5632 |
-+static boolean |
5633 |
-+parse_path (const struct parser_table* entry, char **argv, int *arg_ptr) |
5634 |
-+{ |
5635 |
-+ const char *name; |
5636 |
-+ if (collect_arg(argv, arg_ptr, &name)) |
5637 |
-+ { |
5638 |
-+ struct predicate *our_pred = insert_primary_withpred (entry, pred_path); |
5639 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5640 |
-+ our_pred->args.str = name; |
5641 |
-+ our_pred->est_success_rate = estimate_pattern_match_rate (name, 0); |
5642 |
-+ return true; |
5643 |
-+ } |
5644 |
-+ return false; |
5645 |
-+} |
5646 |
-+ |
5647 |
-+static boolean |
5648 |
-+parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr) |
5649 |
-+{ |
5650 |
-+ return parse_path (entry, argv, arg_ptr); |
5651 |
-+} |
5652 |
-+ |
5653 |
-+static void |
5654 |
-+non_posix_mode(const char *mode) |
5655 |
-+{ |
5656 |
-+ if (options.posixly_correct) |
5657 |
-+ { |
5658 |
-+ error (1, 0, _("Mode %s is not valid when POSIXLY_CORRECT is on."), |
5659 |
-+ quotearg_n_style(0, options.err_quoting_style, mode)); |
5660 |
-+ } |
5661 |
-+} |
5662 |
-+ |
5663 |
-+ |
5664 |
-+static boolean |
5665 |
-+parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr) |
5666 |
-+{ |
5667 |
-+ mode_t perm_val[2]; |
5668 |
-+ float rate; |
5669 |
-+ int mode_start = 0; |
5670 |
-+ boolean havekind = false; |
5671 |
-+ enum permissions_type kind = PERM_EXACT; |
5672 |
-+ struct mode_change *change = NULL; |
5673 |
-+ struct predicate *our_pred; |
5674 |
-+ const char *perm_expr; |
5675 |
-+ |
5676 |
-+ if (!collect_arg(argv, arg_ptr, &perm_expr)) |
5677 |
-+ return false; |
5678 |
-+ |
5679 |
-+ switch (perm_expr[0]) |
5680 |
-+ { |
5681 |
-+ case '-': |
5682 |
-+ mode_start = 1; |
5683 |
-+ kind = PERM_AT_LEAST; |
5684 |
-+ havekind = true; |
5685 |
-+ rate = 0.2; |
5686 |
-+ break; |
5687 |
-+ |
5688 |
-+ case '+': |
5689 |
-+ change = mode_compile (perm_expr); |
5690 |
-+ if (NULL == change) |
5691 |
-+ { |
5692 |
-+ /* Most likely the caller is an old script that is still |
5693 |
-+ * using the obsolete GNU syntax '-perm +MODE'. This old |
5694 |
-+ * syntax was withdrawn in favor of '-perm /MODE' because |
5695 |
-+ * it is incompatible with POSIX in some cases, but we |
5696 |
-+ * still support uses of it that are not incompatible with |
5697 |
-+ * POSIX. |
5698 |
-+ * |
5699 |
-+ * Example: POSIXLY_CORRECT=y find -perm +a+x |
5700 |
-+ */ |
5701 |
-+ non_posix_mode(perm_expr); |
5702 |
-+ |
5703 |
-+ /* support the previous behaviour. */ |
5704 |
-+ mode_start = 1; |
5705 |
-+ kind = PERM_ANY; |
5706 |
-+ rate = 0.3; |
5707 |
-+ } |
5708 |
-+ else |
5709 |
-+ { |
5710 |
-+ /* This is a POSIX-compatible usage */ |
5711 |
-+ mode_start = 0; |
5712 |
-+ kind = PERM_EXACT; |
5713 |
-+ rate = 0.1; |
5714 |
-+ } |
5715 |
-+ havekind = true; |
5716 |
-+ break; |
5717 |
-+ |
5718 |
-+ case '/': /* GNU extension */ |
5719 |
-+ non_posix_mode(perm_expr); |
5720 |
-+ mode_start = 1; |
5721 |
-+ kind = PERM_ANY; |
5722 |
-+ havekind = true; |
5723 |
-+ rate = 0.3; |
5724 |
-+ break; |
5725 |
-+ |
5726 |
-+ default: |
5727 |
-+ /* For example, '-perm 0644', which is valid and matches |
5728 |
-+ * only files whose mode is exactly 0644. |
5729 |
-+ */ |
5730 |
-+ mode_start = 0; |
5731 |
-+ kind = PERM_EXACT; |
5732 |
-+ havekind = true; |
5733 |
-+ rate = 0.01; |
5734 |
-+ break; |
5735 |
-+ } |
5736 |
-+ |
5737 |
-+ if (NULL == change) |
5738 |
-+ { |
5739 |
-+ change = mode_compile (perm_expr + mode_start); |
5740 |
-+ if (NULL == change) |
5741 |
-+ error (1, 0, _("invalid mode %s" |
5742 |
-+ /* TRANSLATORS: the argument is a |
5743 |
-+ * file permission string like 'u=rw,go=' |
5744 |
-+ */), |
5745 |
-+ quotearg_n_style(0, options.err_quoting_style, perm_expr)); |
5746 |
-+ } |
5747 |
-+ perm_val[0] = mode_adjust (0, false, 0, change, NULL); |
5748 |
-+ perm_val[1] = mode_adjust (0, true, 0, change, NULL); |
5749 |
-+ free (change); |
5750 |
-+ |
5751 |
-+ if (('/' == perm_expr[0]) && (0 == perm_val[0]) && (0 == perm_val[1])) |
5752 |
-+ { |
5753 |
-+ /* The meaning of -perm /000 will change in the future. It |
5754 |
-+ * currently matches no files, but like -perm -000 it should |
5755 |
-+ * match all files. |
5756 |
-+ * |
5757 |
-+ * Starting in 2005, we used to issue a warning message |
5758 |
-+ * informing the user that the behaviour would change in the |
5759 |
-+ * future. We have now changed the behaviour and issue a |
5760 |
-+ * warning message that the behaviour recently changed. |
5761 |
-+ */ |
5762 |
-+ error (0, 0, |
5763 |
-+ _("warning: you have specified a mode pattern %s (which is " |
5764 |
-+ "equivalent to /000). The meaning of -perm /000 has now been " |
5765 |
-+ "changed to be consistent with -perm -000; that is, while it " |
5766 |
-+ "used to match no files, it now matches all files."), |
5767 |
-+ perm_expr); |
5768 |
-+ |
5769 |
-+ kind = PERM_AT_LEAST; |
5770 |
-+ havekind = true; |
5771 |
-+ |
5772 |
-+ /* The "magic" number below is just the fraction of files on my |
5773 |
-+ * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks). |
5774 |
-+ * Actual totals are 1472 and 1073833. |
5775 |
-+ */ |
5776 |
-+ rate = 0.9986; /* probably matches anything but a broken symlink */ |
5777 |
-+ } |
5778 |
-+ |
5779 |
-+ our_pred = insert_primary (entry); |
5780 |
-+ our_pred->est_success_rate = rate; |
5781 |
-+ if (havekind) |
5782 |
-+ { |
5783 |
-+ our_pred->args.perm.kind = kind; |
5784 |
-+ } |
5785 |
-+ else |
5786 |
-+ { |
5787 |
-+ |
5788 |
-+ switch (perm_expr[0]) |
5789 |
-+ { |
5790 |
-+ case '-': |
5791 |
-+ our_pred->args.perm.kind = PERM_AT_LEAST; |
5792 |
-+ break; |
5793 |
-+ case '+': |
5794 |
-+ our_pred->args.perm.kind = PERM_ANY; |
5795 |
-+ break; |
5796 |
-+ default: |
5797 |
-+ our_pred->args.perm.kind = PERM_EXACT; |
5798 |
-+ break; |
5799 |
-+ } |
5800 |
-+ } |
5801 |
-+ memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val); |
5802 |
-+ return true; |
5803 |
-+} |
5804 |
-+ |
5805 |
-+boolean |
5806 |
-+parse_print (const struct parser_table* entry, char **argv, int *arg_ptr) |
5807 |
-+{ |
5808 |
-+ struct predicate *our_pred; |
5809 |
-+ |
5810 |
-+ (void) argv; |
5811 |
-+ (void) arg_ptr; |
5812 |
-+ |
5813 |
-+ our_pred = insert_primary (entry); |
5814 |
-+ /* -print has the side effect of printing. This prevents us |
5815 |
-+ from doing undesired multiple printing when the user has |
5816 |
-+ already specified -print. */ |
5817 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
5818 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5819 |
-+ open_stdout(&our_pred->args.printf_vec); |
5820 |
-+ return true; |
5821 |
-+} |
5822 |
-+ |
5823 |
-+static boolean |
5824 |
-+parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr) |
5825 |
-+{ |
5826 |
-+ return insert_fprint(entry, NULL); |
5827 |
-+} |
5828 |
-+ |
5829 |
-+static boolean |
5830 |
-+parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr) |
5831 |
-+{ |
5832 |
-+ const char *format; |
5833 |
-+ if (collect_arg(argv, arg_ptr, &format)) |
5834 |
-+ { |
5835 |
-+ struct format_val fmt; |
5836 |
-+ open_stdout(&fmt); |
5837 |
-+ return insert_fprintf (&fmt, entry, pred_fprintf, format); |
5838 |
-+ } |
5839 |
-+ return false; |
5840 |
-+} |
5841 |
-+ |
5842 |
-+static boolean |
5843 |
-+parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr) |
5844 |
-+{ |
5845 |
-+ const char *format, *filename; |
5846 |
-+ if (collect_arg(argv, arg_ptr, &filename)) |
5847 |
-+ { |
5848 |
-+ if (collect_arg(argv, arg_ptr, &format)) |
5849 |
-+ { |
5850 |
-+ struct format_val fmt; |
5851 |
-+ open_output_file (filename, &fmt); |
5852 |
-+ return insert_fprintf (&fmt, entry, pred_fprintf, format); |
5853 |
-+ } |
5854 |
-+ } |
5855 |
-+ return false; |
5856 |
-+} |
5857 |
-+ |
5858 |
-+static boolean |
5859 |
-+parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr) |
5860 |
-+{ |
5861 |
-+ struct predicate *our_pred; |
5862 |
-+ |
5863 |
-+ (void) argv; |
5864 |
-+ (void) arg_ptr; |
5865 |
-+ |
5866 |
-+ our_pred = insert_primary (entry); |
5867 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5868 |
-+ /* -prune has a side effect that it does not descend into |
5869 |
-+ the current directory. */ |
5870 |
-+ our_pred->side_effects = true; |
5871 |
-+ our_pred->no_default_print = false; |
5872 |
-+ return true; |
5873 |
-+} |
5874 |
-+ |
5875 |
-+static boolean |
5876 |
-+parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr) |
5877 |
-+{ |
5878 |
-+ struct predicate *our_pred = insert_primary (entry); |
5879 |
-+ (void) argv; |
5880 |
-+ (void) arg_ptr; |
5881 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5882 |
-+ our_pred->side_effects = true; /* Exiting is a side effect... */ |
5883 |
-+ our_pred->no_default_print = false; /* Don't inhibit the default print, though. */ |
5884 |
-+ our_pred->est_success_rate = 1.0f; |
5885 |
-+ return true; |
5886 |
-+} |
5887 |
-+ |
5888 |
-+ |
5889 |
-+static boolean |
5890 |
-+parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr) |
5891 |
-+{ |
5892 |
-+ const char *type_name; |
5893 |
-+ if (collect_arg(argv, arg_ptr, &type_name)) |
5894 |
-+ { |
5895 |
-+ /* collect the regex type name */ |
5896 |
-+ options.regex_options = get_regex_type(type_name); |
5897 |
-+ return parse_noop(entry, argv, arg_ptr); |
5898 |
-+ } |
5899 |
-+ return false; |
5900 |
-+} |
5901 |
-+ |
5902 |
-+ |
5903 |
-+static boolean |
5904 |
-+parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr) |
5905 |
-+{ |
5906 |
-+ return insert_regex (argv, arg_ptr, entry, options.regex_options); |
5907 |
-+} |
5908 |
-+ |
5909 |
-+static boolean |
5910 |
-+insert_regex (char **argv, |
5911 |
-+ int *arg_ptr, |
5912 |
-+ const struct parser_table *entry, |
5913 |
-+ int regex_options) |
5914 |
-+{ |
5915 |
-+ const char *rx; |
5916 |
-+ if (collect_arg(argv, arg_ptr, &rx)) |
5917 |
-+ { |
5918 |
-+ struct re_pattern_buffer *re; |
5919 |
-+ const char *error_message; |
5920 |
-+ struct predicate *our_pred = insert_primary_withpred (entry, pred_regex); |
5921 |
-+ our_pred->need_stat = our_pred->need_type = false; |
5922 |
-+ re = xmalloc (sizeof (struct re_pattern_buffer)); |
5923 |
-+ our_pred->args.regex = re; |
5924 |
-+ re->allocated = 100; |
5925 |
-+ re->buffer = xmalloc (re->allocated); |
5926 |
-+ re->fastmap = NULL; |
5927 |
-+ |
5928 |
-+ re_set_syntax(regex_options); |
5929 |
-+ re->syntax = regex_options; |
5930 |
-+ re->translate = NULL; |
5931 |
-+ |
5932 |
-+ error_message = re_compile_pattern (rx, strlen(rx), re); |
5933 |
-+ if (error_message) |
5934 |
-+ error (1, 0, "%s", error_message); |
5935 |
-+ our_pred->est_success_rate = estimate_pattern_match_rate(rx, 1); |
5936 |
-+ return true; |
5937 |
-+ } |
5938 |
-+ return false; |
5939 |
-+} |
5940 |
-+ |
5941 |
-+static boolean |
5942 |
-+parse_size (const struct parser_table* entry, char **argv, int *arg_ptr) |
5943 |
-+{ |
5944 |
-+ struct predicate *our_pred; |
5945 |
-+ uintmax_t num; |
5946 |
-+ char suffix; |
5947 |
-+ enum comparison_type c_type; |
5948 |
-+ |
5949 |
-+ int blksize = 512; |
5950 |
-+ int len; |
5951 |
-+ |
5952 |
-+ /* XXX: cannot (yet) convert to ue collect_arg() as this |
5953 |
-+ * function modifies the args in-place. |
5954 |
-+ */ |
5955 |
-+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
5956 |
-+ return false; |
5957 |
-+ |
5958 |
-+ len = strlen (argv[*arg_ptr]); |
5959 |
-+ if (len == 0) |
5960 |
-+ error (1, 0, _("invalid null argument to -size")); |
5961 |
-+ |
5962 |
-+ suffix = argv[*arg_ptr][len - 1]; |
5963 |
-+ switch (suffix) |
5964 |
-+ { |
5965 |
-+ case 'b': |
5966 |
-+ blksize = 512; |
5967 |
-+ argv[*arg_ptr][len - 1] = '\0'; |
5968 |
-+ break; |
5969 |
-+ |
5970 |
-+ case 'c': |
5971 |
-+ blksize = 1; |
5972 |
-+ argv[*arg_ptr][len - 1] = '\0'; |
5973 |
-+ break; |
5974 |
-+ |
5975 |
-+ case 'k': |
5976 |
-+ blksize = 1024; |
5977 |
-+ argv[*arg_ptr][len - 1] = '\0'; |
5978 |
-+ break; |
5979 |
-+ |
5980 |
-+ case 'M': /* Megabytes */ |
5981 |
-+ blksize = 1024*1024; |
5982 |
-+ argv[*arg_ptr][len - 1] = '\0'; |
5983 |
-+ break; |
5984 |
-+ |
5985 |
-+ case 'G': /* Gigabytes */ |
5986 |
-+ blksize = 1024*1024*1024; |
5987 |
-+ argv[*arg_ptr][len - 1] = '\0'; |
5988 |
-+ break; |
5989 |
-+ |
5990 |
-+ case 'w': |
5991 |
-+ blksize = 2; |
5992 |
-+ argv[*arg_ptr][len - 1] = '\0'; |
5993 |
-+ break; |
5994 |
-+ |
5995 |
-+ case '0': |
5996 |
-+ case '1': |
5997 |
-+ case '2': |
5998 |
-+ case '3': |
5999 |
-+ case '4': |
6000 |
-+ case '5': |
6001 |
-+ case '6': |
6002 |
-+ case '7': |
6003 |
-+ case '8': |
6004 |
-+ case '9': |
6005 |
-+ break; |
6006 |
-+ |
6007 |
-+ default: |
6008 |
-+ error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]); |
6009 |
-+ } |
6010 |
-+ /* TODO: accept fractional megabytes etc. ? */ |
6011 |
-+ if (!get_num (argv[*arg_ptr], &num, &c_type)) |
6012 |
-+ { |
6013 |
-+ error(1, 0, |
6014 |
-+ _("Invalid argument `%s%c' to -size"), |
6015 |
-+ argv[*arg_ptr], (int)suffix); |
6016 |
-+ return false; |
6017 |
-+ } |
6018 |
-+ our_pred = insert_primary (entry); |
6019 |
-+ our_pred->args.size.kind = c_type; |
6020 |
-+ our_pred->args.size.blocksize = blksize; |
6021 |
-+ our_pred->args.size.size = num; |
6022 |
-+ our_pred->need_stat = true; |
6023 |
-+ our_pred->need_type = false; |
6024 |
-+ |
6025 |
-+ if (COMP_GT == c_type) |
6026 |
-+ our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9; |
6027 |
-+ else if (COMP_LT == c_type) |
6028 |
-+ our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1; |
6029 |
-+ else |
6030 |
-+ our_pred->est_success_rate = 0.01; |
6031 |
-+ |
6032 |
-+ (*arg_ptr)++; |
6033 |
-+ return true; |
6034 |
-+} |
6035 |
-+ |
6036 |
-+ |
6037 |
-+static boolean |
6038 |
-+parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr) |
6039 |
-+{ |
6040 |
-+ /* General idea: stat the file, remember device and inode numbers. |
6041 |
-+ * If a candidate file matches those, it's the same file. |
6042 |
-+ */ |
6043 |
-+ struct predicate *our_pred; |
6044 |
-+ struct stat st, fst; |
6045 |
-+ int fd, openflags; |
6046 |
-+ |
6047 |
-+ set_stat_placeholders(&st); |
6048 |
-+ if (!collect_arg_stat_info(argv, arg_ptr, &st)) |
6049 |
-+ return false; |
6050 |
-+ |
6051 |
-+ set_stat_placeholders(&fst); |
6052 |
-+ /* POSIX systems are free to re-use the inode number of a deleted |
6053 |
-+ * file. To ensure that we are not fooled by inode reuse, we hold |
6054 |
-+ * the file open if we can. This would prevent the system reusing |
6055 |
-+ * the file. |
6056 |
-+ */ |
6057 |
-+ fd = -3; /* means, uninitialised */ |
6058 |
-+ openflags = O_RDONLY; |
6059 |
-+ |
6060 |
-+ if (options.symlink_handling == SYMLINK_NEVER_DEREF) |
6061 |
-+ { |
6062 |
-+ if (options.open_nofollow_available) |
6063 |
-+ { |
6064 |
-+ assert (O_NOFOLLOW != 0); |
6065 |
-+ openflags |= O_NOFOLLOW; |
6066 |
-+ fd = -1; /* safe to open it. */ |
6067 |
-+ } |
6068 |
-+ else |
6069 |
-+ { |
6070 |
-+ if (S_ISLNK(st.st_mode)) |
6071 |
-+ { |
6072 |
-+ /* no way to ensure that a symlink will not be followed |
6073 |
-+ * by open(2), so fall back on using lstat(). Accept |
6074 |
-+ * the risk that the named file will be deleted and |
6075 |
-+ * replaced with another having the same inode. |
6076 |
-+ * |
6077 |
-+ * Avoid opening the file. |
6078 |
-+ */ |
6079 |
-+ fd = -2; /* Do not open it */ |
6080 |
-+ } |
6081 |
-+ else |
6082 |
-+ { |
6083 |
-+ fd = -1; |
6084 |
-+ /* Race condition here: the file might become a symlink here. */ |
6085 |
-+ } |
6086 |
-+ } |
6087 |
-+ } |
6088 |
-+ else |
6089 |
-+ { |
6090 |
-+ /* We want to dereference the symlink anyway */ |
6091 |
-+ fd = -1; /* safe to open it without O_NOFOLLOW */ |
6092 |
-+ } |
6093 |
-+ |
6094 |
-+ assert (fd != -3); /* check we made a decision */ |
6095 |
-+ if (fd == -1) |
6096 |
-+ { |
6097 |
-+ /* Race condition here. The file might become a |
6098 |
-+ * symbolic link in between out call to stat and |
6099 |
-+ * the call to open. |
6100 |
-+ */ |
6101 |
-+ fd = open(argv[*arg_ptr], openflags); |
6102 |
-+ |
6103 |
-+ if (fd >= 0) |
6104 |
-+ { |
6105 |
-+ /* We stat the file again here to prevent a race condition |
6106 |
-+ * between the first stat and the call to open(2). |
6107 |
-+ */ |
6108 |
-+ if (0 != fstat(fd, &fst)) |
6109 |
-+ { |
6110 |
-+ fatal_file_error(argv[*arg_ptr]); |
6111 |
-+ } |
6112 |
-+ else |
6113 |
-+ { |
6114 |
-+ /* Worry about the race condition. If the file became a |
6115 |
-+ * symlink after our first stat and before our call to |
6116 |
-+ * open, fst may contain the stat information for the |
6117 |
-+ * destination of the link, not the link itself. |
6118 |
-+ */ |
6119 |
-+ if ((*options.xstat) (argv[*arg_ptr], &st)) |
6120 |
-+ fatal_file_error(argv[*arg_ptr]); |
6121 |
-+ |
6122 |
-+ if ((options.symlink_handling == SYMLINK_NEVER_DEREF) |
6123 |
-+ && (!options.open_nofollow_available)) |
6124 |
-+ { |
6125 |
-+ if (S_ISLNK(st.st_mode)) |
6126 |
-+ { |
6127 |
-+ /* We lost the race. Leave the data in st. The |
6128 |
-+ * file descriptor points to the wrong thing. |
6129 |
-+ */ |
6130 |
-+ close(fd); |
6131 |
-+ fd = -1; |
6132 |
-+ } |
6133 |
-+ else |
6134 |
-+ { |
6135 |
-+ /* Several possibilities here: |
6136 |
-+ * 1. There was no race |
6137 |
-+ * 2. The file changed into a symlink after the stat and |
6138 |
-+ * before the open, and then back into a non-symlink |
6139 |
-+ * before the second stat. |
6140 |
-+ * |
6141 |
-+ * In case (1) there is no problem. In case (2), |
6142 |
-+ * the stat() and fstat() calls will have returned |
6143 |
-+ * different data. O_NOFOLLOW was not available, |
6144 |
-+ * so the open() call may have followed a symlink |
6145 |
-+ * even if the -P option is in effect. |
6146 |
-+ */ |
6147 |
-+ if ((st.st_dev == fst.st_dev) |
6148 |
-+ && (st.st_ino == fst.st_ino)) |
6149 |
-+ { |
6150 |
-+ /* No race. No need to copy fst to st, |
6151 |
-+ * since they should be identical (modulo |
6152 |
-+ * differences in padding bytes). |
6153 |
-+ */ |
6154 |
-+ } |
6155 |
-+ else |
6156 |
-+ { |
6157 |
-+ /* We lost the race. Leave the data in st. The |
6158 |
-+ * file descriptor points to the wrong thing. |
6159 |
-+ */ |
6160 |
-+ close(fd); |
6161 |
-+ fd = -1; |
6162 |
-+ } |
6163 |
-+ } |
6164 |
-+ } |
6165 |
-+ else |
6166 |
-+ { |
6167 |
-+ st = fst; |
6168 |
-+ } |
6169 |
-+ } |
6170 |
-+ } |
6171 |
-+ } |
6172 |
-+ |
6173 |
-+ our_pred = insert_primary (entry); |
6174 |
-+ our_pred->args.samefileid.ino = st.st_ino; |
6175 |
-+ our_pred->args.samefileid.dev = st.st_dev; |
6176 |
-+ our_pred->args.samefileid.fd = fd; |
6177 |
-+ our_pred->need_type = false; |
6178 |
-+ our_pred->need_stat = true; |
6179 |
-+ our_pred->est_success_rate = 0.01f; |
6180 |
-+ return true; |
6181 |
-+} |
6182 |
-+ |
6183 |
-+#if 0 |
6184 |
-+/* This function is commented out partly because support for it is |
6185 |
-+ * uneven. |
6186 |
-+ */ |
6187 |
-+static boolean |
6188 |
-+parse_show_control_chars (const struct parser_table* entry, |
6189 |
-+ char **argv, |
6190 |
-+ int *arg_ptr) |
6191 |
-+{ |
6192 |
-+ const char *arg; |
6193 |
-+ const char *errmsg = _("The -show-control-chars option takes " |
6194 |
-+ "a single argument which " |
6195 |
-+ "must be 'literal' or 'safe'"); |
6196 |
-+ |
6197 |
-+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
6198 |
-+ { |
6199 |
-+ error (1, errno, "%s", errmsg); |
6200 |
-+ return false; |
6201 |
-+ } |
6202 |
-+ else |
6203 |
-+ { |
6204 |
-+ arg = argv[*arg_ptr]; |
6205 |
-+ |
6206 |
-+ if (0 == strcmp("literal", arg)) |
6207 |
-+ { |
6208 |
-+ options.literal_control_chars = true; |
6209 |
-+ } |
6210 |
-+ else if (0 == strcmp("safe", arg)) |
6211 |
-+ { |
6212 |
-+ options.literal_control_chars = false; |
6213 |
-+ } |
6214 |
-+ else |
6215 |
-+ { |
6216 |
-+ error (1, errno, "%s", errmsg); |
6217 |
-+ return false; |
6218 |
-+ } |
6219 |
-+ (*arg_ptr)++; /* consume the argument. */ |
6220 |
-+ return true; |
6221 |
-+ } |
6222 |
-+} |
6223 |
-+#endif |
6224 |
-+ |
6225 |
-+ |
6226 |
-+static boolean |
6227 |
-+parse_true (const struct parser_table* entry, char **argv, int *arg_ptr) |
6228 |
-+{ |
6229 |
-+ struct predicate *our_pred; |
6230 |
-+ |
6231 |
-+ (void) argv; |
6232 |
-+ (void) arg_ptr; |
6233 |
-+ |
6234 |
-+ our_pred = insert_primary (entry); |
6235 |
-+ our_pred->need_stat = our_pred->need_type = false; |
6236 |
-+ our_pred->est_success_rate = 1.0f; |
6237 |
-+ return true; |
6238 |
-+} |
6239 |
-+ |
6240 |
-+static boolean |
6241 |
-+parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr) |
6242 |
-+{ |
6243 |
-+ (void) entry; |
6244 |
-+ return parse_true(get_noop(), argv, arg_ptr); |
6245 |
-+} |
6246 |
-+ |
6247 |
-+static boolean |
6248 |
-+parse_accesscheck (const struct parser_table* entry, char **argv, int *arg_ptr) |
6249 |
-+{ |
6250 |
-+ struct predicate *our_pred; |
6251 |
-+ (void) argv; |
6252 |
-+ (void) arg_ptr; |
6253 |
-+ our_pred = insert_primary (entry); |
6254 |
-+ our_pred->need_stat = our_pred->need_type = false; |
6255 |
-+ our_pred->side_effects = our_pred->no_default_print = false; |
6256 |
-+ if (pred_is(our_pred, pred_executable)) |
6257 |
-+ our_pred->est_success_rate = 0.2; |
6258 |
-+ else |
6259 |
-+ our_pred->est_success_rate = 0.9; |
6260 |
-+ return true; |
6261 |
-+} |
6262 |
-+ |
6263 |
-+static boolean |
6264 |
-+parse_type (const struct parser_table* entry, char **argv, int *arg_ptr) |
6265 |
-+{ |
6266 |
-+ return insert_type (argv, arg_ptr, entry, pred_type); |
6267 |
-+} |
6268 |
-+ |
6269 |
-+static boolean |
6270 |
-+parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr) |
6271 |
-+{ |
6272 |
-+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
6273 |
-+ if (p) |
6274 |
-+ { |
6275 |
-+ p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2; |
6276 |
-+ return true; |
6277 |
-+ } |
6278 |
-+ else |
6279 |
-+ { |
6280 |
-+ return false; |
6281 |
-+ } |
6282 |
-+} |
6283 |
-+ |
6284 |
-+static boolean |
6285 |
-+parse_used (const struct parser_table* entry, char **argv, int *arg_ptr) |
6286 |
-+{ |
6287 |
-+ struct predicate *our_pred; |
6288 |
-+ struct time_val tval; |
6289 |
-+ const char *offset_str; |
6290 |
-+ const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds"; |
6291 |
-+ |
6292 |
-+ if (collect_arg(argv, arg_ptr, &offset_str)) |
6293 |
-+ { |
6294 |
-+ /* The timespec is actually a delta value, so we use an origin of 0. */ |
6295 |
-+ if (get_relative_timestamp(offset_str, &tval, 0, DAYSECS, errmsg)) |
6296 |
-+ { |
6297 |
-+ our_pred = insert_primary (entry); |
6298 |
-+ our_pred->args.reftime = tval; |
6299 |
-+ our_pred->est_success_rate = estimate_file_age_success_rate(tval.ts.tv_sec / DAYSECS); |
6300 |
-+ return true; |
6301 |
-+ } |
6302 |
-+ else |
6303 |
-+ { |
6304 |
-+ error(1, 0, _("Invalid argument %s to -used"), offset_str); |
6305 |
-+ return false; |
6306 |
-+ } |
6307 |
-+ } |
6308 |
-+ else |
6309 |
-+ { |
6310 |
-+ return false; /* missing argument */ |
6311 |
-+ } |
6312 |
-+} |
6313 |
-+ |
6314 |
-+static boolean |
6315 |
-+parse_user (const struct parser_table* entry, char **argv, int *arg_ptr) |
6316 |
-+{ |
6317 |
-+ const char *username; |
6318 |
-+ |
6319 |
-+ if (collect_arg(argv, arg_ptr, &username)) |
6320 |
-+ { |
6321 |
-+ struct predicate *our_pred; |
6322 |
-+ uid_t uid; |
6323 |
-+ struct passwd *cur_pwd = getpwnam(username); |
6324 |
-+ endpwent(); |
6325 |
-+ if (cur_pwd != NULL) |
6326 |
-+ { |
6327 |
-+ uid = cur_pwd->pw_uid; |
6328 |
-+ } |
6329 |
-+ else |
6330 |
-+ { |
6331 |
-+ int uid_len = strspn (username, "0123456789"); |
6332 |
-+ if (uid_len && (username[uid_len]==0)) |
6333 |
-+ uid = safe_atoi (username); |
6334 |
-+ else |
6335 |
-+ return false; |
6336 |
-+ } |
6337 |
-+ our_pred = insert_primary (entry); |
6338 |
-+ our_pred->args.uid = uid; |
6339 |
-+ our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2; |
6340 |
-+ return true; |
6341 |
-+ } |
6342 |
-+ return false; |
6343 |
-+} |
6344 |
-+ |
6345 |
-+static boolean |
6346 |
-+parse_version (const struct parser_table* entry, char **argv, int *arg_ptr) |
6347 |
-+{ |
6348 |
-+ int features = 0; |
6349 |
-+ int flags; |
6350 |
-+ |
6351 |
-+ (void) argv; |
6352 |
-+ (void) arg_ptr; |
6353 |
-+ (void) entry; |
6354 |
-+ |
6355 |
-+ display_findutils_version("find"); |
6356 |
-+ printf (_("Features enabled: ")); |
6357 |
-+ |
6358 |
-+#if CACHE_IDS |
6359 |
-+ printf("CACHE_IDS "); |
6360 |
-+ ++features; |
6361 |
-+#endif |
6362 |
-+#if DEBUG |
6363 |
-+ printf("DEBUG "); |
6364 |
-+ ++features; |
6365 |
-+#endif |
6366 |
-+#if DEBUG_STAT |
6367 |
-+ printf("DEBUG_STAT "); |
6368 |
-+ ++features; |
6369 |
-+#endif |
6370 |
-+#if defined USE_STRUCT_DIRENT_D_TYPE && defined HAVE_STRUCT_DIRENT_D_TYPE |
6371 |
-+ printf("D_TYPE "); |
6372 |
-+ ++features; |
6373 |
-+#endif |
6374 |
-+#if defined O_NOFOLLOW |
6375 |
-+ printf("O_NOFOLLOW(%s) ", |
6376 |
-+ (options.open_nofollow_available ? "enabled" : "disabled")); |
6377 |
-+ ++features; |
6378 |
-+#endif |
6379 |
-+#if defined LEAF_OPTIMISATION |
6380 |
-+ printf("LEAF_OPTIMISATION "); |
6381 |
-+ ++features; |
6382 |
-+#endif |
6383 |
-+ |
6384 |
-+ flags = 0; |
6385 |
-+ if (is_fts_enabled(&flags)) |
6386 |
-+ { |
6387 |
-+ int nflags = 0; |
6388 |
-+ printf("FTS("); |
6389 |
-+ ++features; |
6390 |
-+ |
6391 |
-+ if (flags & FTS_CWDFD) |
6392 |
-+ { |
6393 |
-+ if (nflags) |
6394 |
-+ { |
6395 |
-+ printf(","); |
6396 |
-+ } |
6397 |
-+ printf("FTS_CWDFD"); |
6398 |
-+ ++nflags; |
6399 |
-+ } |
6400 |
-+ printf(") "); |
6401 |
-+ } |
6402 |
-+ |
6403 |
-+ printf("CBO(level=%d) ", (int)(options.optimisation_level)); |
6404 |
-+ ++features; |
6405 |
-+ |
6406 |
-+ if (0 == features) |
6407 |
-+ { |
6408 |
-+ /* For the moment, leave this as English in case someone wants |
6409 |
-+ to parse these strings. */ |
6410 |
-+ printf("none"); |
6411 |
-+ } |
6412 |
-+ printf("\n"); |
6413 |
-+ |
6414 |
-+ exit (0); |
6415 |
-+} |
6416 |
-+ |
6417 |
-+static boolean |
6418 |
-+parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr) |
6419 |
-+{ |
6420 |
-+ options.stay_on_filesystem = true; |
6421 |
-+ return parse_noop(entry, argv, arg_ptr); |
6422 |
-+} |
6423 |
-+ |
6424 |
-+static boolean |
6425 |
-+parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr) |
6426 |
-+{ |
6427 |
-+ options.ignore_readdir_race = true; |
6428 |
-+ return parse_noop(entry, argv, arg_ptr); |
6429 |
-+} |
6430 |
-+ |
6431 |
-+static boolean |
6432 |
-+parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr) |
6433 |
-+{ |
6434 |
-+ options.ignore_readdir_race = false; |
6435 |
-+ return parse_noop(entry, argv, arg_ptr); |
6436 |
-+} |
6437 |
-+ |
6438 |
-+static boolean |
6439 |
-+parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr) |
6440 |
-+{ |
6441 |
-+ options.warnings = true; |
6442 |
-+ return parse_noop(entry, argv, arg_ptr); |
6443 |
-+} |
6444 |
-+ |
6445 |
-+static boolean |
6446 |
-+parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr) |
6447 |
-+{ |
6448 |
-+ return insert_type (argv, arg_ptr, entry, pred_xtype); |
6449 |
-+} |
6450 |
-+ |
6451 |
-+static boolean |
6452 |
-+insert_type (char **argv, int *arg_ptr, |
6453 |
-+ const struct parser_table *entry, |
6454 |
-+ PRED_FUNC which_pred) |
6455 |
-+{ |
6456 |
-+ mode_t type_cell; |
6457 |
-+ struct predicate *our_pred; |
6458 |
-+ float rate = 0.5; |
6459 |
-+ const char *typeletter; |
6460 |
-+ |
6461 |
-+ if (collect_arg(argv, arg_ptr, &typeletter)) |
6462 |
-+ { |
6463 |
-+ if (strlen(typeletter) != 1u) |
6464 |
-+ { |
6465 |
-+ error(1, 0, _("Arguments to -type should contain only one letter")); |
6466 |
-+ return false; |
6467 |
-+ } |
6468 |
-+ |
6469 |
-+ switch (typeletter[0]) |
6470 |
-+ { |
6471 |
-+ case 'b': /* block special */ |
6472 |
-+ type_cell = S_IFBLK; |
6473 |
-+ rate = 0.01f; |
6474 |
-+ break; |
6475 |
-+ case 'c': /* character special */ |
6476 |
-+ type_cell = S_IFCHR; |
6477 |
-+ rate = 0.01f; |
6478 |
-+ break; |
6479 |
-+ case 'd': /* directory */ |
6480 |
-+ type_cell = S_IFDIR; |
6481 |
-+ rate = 0.4f; |
6482 |
-+ break; |
6483 |
-+ case 'f': /* regular file */ |
6484 |
-+ type_cell = S_IFREG; |
6485 |
-+ rate = 0.95f; |
6486 |
-+ break; |
6487 |
-+#ifdef S_IFLNK |
6488 |
-+ case 'l': /* symbolic link */ |
6489 |
-+ type_cell = S_IFLNK; |
6490 |
-+ rate = 0.1f; |
6491 |
-+ break; |
6492 |
-+#endif |
6493 |
-+#ifdef S_IFIFO |
6494 |
-+ case 'p': /* pipe */ |
6495 |
-+ type_cell = S_IFIFO; |
6496 |
-+ rate = 0.01f; |
6497 |
-+ break; |
6498 |
-+#endif |
6499 |
-+#ifdef S_IFSOCK |
6500 |
-+ case 's': /* socket */ |
6501 |
-+ type_cell = S_IFSOCK; |
6502 |
-+ rate = 0.01f; |
6503 |
-+ break; |
6504 |
-+#endif |
6505 |
-+#ifdef S_IFDOOR |
6506 |
-+ case 'D': /* Solaris door */ |
6507 |
-+ type_cell = S_IFDOOR; |
6508 |
-+ rate = 0.01f; |
6509 |
-+ break; |
6510 |
-+#endif |
6511 |
-+ default: /* None of the above ... nuke 'em. */ |
6512 |
-+ error(1, 0, _("Unknown argument to -type: %c"), (*typeletter)); |
6513 |
-+ return false; |
6514 |
-+ } |
6515 |
-+ our_pred = insert_primary_withpred (entry, which_pred); |
6516 |
-+ our_pred->est_success_rate = rate; |
6517 |
-+ |
6518 |
-+ /* Figure out if we will need to stat the file, because if we don't |
6519 |
-+ * need to follow symlinks, we can avoid a stat call by using |
6520 |
-+ * struct dirent.d_type. |
6521 |
-+ */ |
6522 |
-+ if (which_pred == pred_xtype) |
6523 |
-+ { |
6524 |
-+ our_pred->need_stat = true; |
6525 |
-+ our_pred->need_type = false; |
6526 |
-+ } |
6527 |
-+ else |
6528 |
-+ { |
6529 |
-+ our_pred->need_stat = false; /* struct dirent is enough */ |
6530 |
-+ our_pred->need_type = true; |
6531 |
-+ } |
6532 |
-+ our_pred->args.type = type_cell; |
6533 |
-+ return true; |
6534 |
-+ } |
6535 |
-+ return false; |
6536 |
-+} |
6537 |
-+ |
6538 |
-+ |
6539 |
-+/* Return true if the file accessed via FP is a terminal. |
6540 |
-+ */ |
6541 |
-+static boolean |
6542 |
-+stream_is_tty(FILE *fp) |
6543 |
-+{ |
6544 |
-+ int fd = fileno(fp); |
6545 |
-+ if (-1 == fd) |
6546 |
-+ { |
6547 |
-+ return false; /* not a valid stream */ |
6548 |
-+ } |
6549 |
-+ else |
6550 |
-+ { |
6551 |
-+ return isatty(fd) ? true : false; |
6552 |
-+ } |
6553 |
-+ |
6554 |
-+} |
6555 |
-+ |
6556 |
-+ |
6557 |
-+ |
6558 |
-+ |
6559 |
-+/* XXX: do we need to pass FUNC to this function? */ |
6560 |
-+static boolean |
6561 |
-+insert_fprintf (struct format_val *vec, |
6562 |
-+ const struct parser_table *entry, PRED_FUNC func, |
6563 |
-+ const char *format_const) |
6564 |
-+{ |
6565 |
-+ char *format = (char*)format_const; /* XXX: casting away constness */ |
6566 |
-+ register char *scan; /* Current address in scanning `format'. */ |
6567 |
-+ register char *scan2; /* Address inside of element being scanned. */ |
6568 |
-+ struct segment **segmentp; /* Address of current segment. */ |
6569 |
-+ struct predicate *our_pred; |
6570 |
-+ |
6571 |
-+ our_pred = insert_primary_withpred (entry, func); |
6572 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
6573 |
-+ our_pred->args.printf_vec = *vec; |
6574 |
-+ our_pred->need_type = false; |
6575 |
-+ our_pred->need_stat = false; |
6576 |
-+ our_pred->p_cost = NeedsNothing; |
6577 |
-+ |
6578 |
-+ segmentp = &our_pred->args.printf_vec.segment; |
6579 |
-+ *segmentp = NULL; |
6580 |
-+ |
6581 |
-+ for (scan = format; *scan; scan++) |
6582 |
-+ { |
6583 |
-+ if (*scan == '\\') |
6584 |
-+ { |
6585 |
-+ scan2 = scan + 1; |
6586 |
-+ if (*scan2 >= '0' && *scan2 <= '7') |
6587 |
-+ { |
6588 |
-+ register int n, i; |
6589 |
-+ |
6590 |
-+ for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7'); |
6591 |
-+ i++, scan2++) |
6592 |
-+ n = 8 * n + *scan2 - '0'; |
6593 |
-+ scan2--; |
6594 |
-+ *scan = n; |
6595 |
-+ } |
6596 |
-+ else |
6597 |
-+ { |
6598 |
-+ switch (*scan2) |
6599 |
-+ { |
6600 |
-+ case 'a': |
6601 |
-+ *scan = 7; |
6602 |
-+ break; |
6603 |
-+ case 'b': |
6604 |
-+ *scan = '\b'; |
6605 |
-+ break; |
6606 |
-+ case 'c': |
6607 |
-+ make_segment (segmentp, format, scan - format, |
6608 |
-+ KIND_STOP, 0, 0, |
6609 |
-+ our_pred); |
6610 |
-+ if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo)) |
6611 |
-+ our_pred->p_cost = NeedsStatInfo; |
6612 |
-+ return true; |
6613 |
-+ case 'f': |
6614 |
-+ *scan = '\f'; |
6615 |
-+ break; |
6616 |
-+ case 'n': |
6617 |
-+ *scan = '\n'; |
6618 |
-+ break; |
6619 |
-+ case 'r': |
6620 |
-+ *scan = '\r'; |
6621 |
-+ break; |
6622 |
-+ case 't': |
6623 |
-+ *scan = '\t'; |
6624 |
-+ break; |
6625 |
-+ case 'v': |
6626 |
-+ *scan = '\v'; |
6627 |
-+ break; |
6628 |
-+ case '\\': |
6629 |
-+ /* *scan = '\\'; * it already is */ |
6630 |
-+ break; |
6631 |
-+ default: |
6632 |
-+ error (0, 0, |
6633 |
-+ _("warning: unrecognized escape `\\%c'"), *scan2); |
6634 |
-+ scan++; |
6635 |
-+ continue; |
6636 |
-+ } |
6637 |
-+ } |
6638 |
-+ segmentp = make_segment (segmentp, format, scan - format + 1, |
6639 |
-+ KIND_PLAIN, 0, 0, |
6640 |
-+ our_pred); |
6641 |
-+ format = scan2 + 1; /* Move past the escape. */ |
6642 |
-+ scan = scan2; /* Incremented immediately by `for'. */ |
6643 |
-+ } |
6644 |
-+ else if (*scan == '%') |
6645 |
-+ { |
6646 |
-+ if (scan[1] == 0) |
6647 |
-+ { |
6648 |
-+ /* Trailing %. We don't like those. */ |
6649 |
-+ error (1, 0, _("error: %s at end of format string"), scan); |
6650 |
-+ } |
6651 |
-+ else if (scan[1] == '%') |
6652 |
-+ { |
6653 |
-+ segmentp = make_segment (segmentp, format, scan - format + 1, |
6654 |
-+ KIND_PLAIN, 0, 0, |
6655 |
-+ our_pred); |
6656 |
-+ scan++; |
6657 |
-+ format = scan + 1; |
6658 |
-+ continue; |
6659 |
-+ } |
6660 |
-+ /* Scan past flags, width and precision, to verify kind. */ |
6661 |
-+ for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);) |
6662 |
-+ /* Do nothing. */ ; |
6663 |
-+ while (ISDIGIT (*scan2)) |
6664 |
-+ scan2++; |
6665 |
-+ if (*scan2 == '.') |
6666 |
-+ for (scan2++; ISDIGIT (*scan2); scan2++) |
6667 |
-+ /* Do nothing. */ ; |
6668 |
-+ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2)) |
6669 |
-+ { |
6670 |
-+ segmentp = make_segment (segmentp, format, scan2 - format, |
6671 |
-+ KIND_FORMAT, *scan2, 0, |
6672 |
-+ our_pred); |
6673 |
-+ scan = scan2; |
6674 |
-+ format = scan + 1; |
6675 |
-+ } |
6676 |
-+ else if (strchr ("ABCT", *scan2) && scan2[1]) |
6677 |
-+ { |
6678 |
-+ segmentp = make_segment (segmentp, format, scan2 - format, |
6679 |
-+ KIND_FORMAT, scan2[0], scan2[1], |
6680 |
-+ our_pred); |
6681 |
-+ scan = scan2 + 1; |
6682 |
-+ format = scan + 1; |
6683 |
-+ continue; |
6684 |
-+ } |
6685 |
-+ else |
6686 |
-+ { |
6687 |
-+ /* An unrecognized % escape. Print the char after the %. */ |
6688 |
-+ error (0, 0, _("warning: unrecognized format directive `%%%c'"), |
6689 |
-+ *scan2); |
6690 |
-+ segmentp = make_segment (segmentp, format, scan - format, |
6691 |
-+ KIND_PLAIN, 0, 0, |
6692 |
-+ our_pred); |
6693 |
-+ format = scan + 1; |
6694 |
-+ continue; |
6695 |
-+ } |
6696 |
-+ } |
6697 |
-+ } |
6698 |
-+ |
6699 |
-+ if (scan > format) |
6700 |
-+ make_segment (segmentp, format, scan - format, KIND_PLAIN, 0, 0, |
6701 |
-+ our_pred); |
6702 |
-+ return true; |
6703 |
-+} |
6704 |
-+ |
6705 |
-+/* Create a new fprintf segment in *SEGMENT, with type KIND, |
6706 |
-+ from the text in FORMAT, which has length LEN. |
6707 |
-+ Return the address of the `next' pointer of the new segment. */ |
6708 |
-+ |
6709 |
-+static struct segment ** |
6710 |
-+make_segment (struct segment **segment, |
6711 |
-+ char *format, |
6712 |
-+ int len, |
6713 |
-+ int kind, |
6714 |
-+ char format_char, |
6715 |
-+ char aux_format_char, |
6716 |
-+ struct predicate *pred) |
6717 |
-+{ |
6718 |
-+ enum EvaluationCost mycost = NeedsNothing; |
6719 |
-+ char *fmt; |
6720 |
-+ |
6721 |
-+ *segment = xmalloc (sizeof (struct segment)); |
6722 |
-+ |
6723 |
-+ (*segment)->segkind = kind; |
6724 |
-+ (*segment)->format_char[0] = format_char; |
6725 |
-+ (*segment)->format_char[1] = aux_format_char; |
6726 |
-+ (*segment)->next = NULL; |
6727 |
-+ (*segment)->text_len = len; |
6728 |
-+ |
6729 |
-+ fmt = (*segment)->text = xmalloc (len + sizeof "d"); |
6730 |
-+ strncpy (fmt, format, len); |
6731 |
-+ fmt += len; |
6732 |
-+ |
6733 |
-+ switch (kind) |
6734 |
-+ { |
6735 |
-+ case KIND_PLAIN: /* Plain text string, no % conversion. */ |
6736 |
-+ case KIND_STOP: /* Terminate argument, no newline. */ |
6737 |
-+ assert (0 == format_char); |
6738 |
-+ assert (0 == aux_format_char); |
6739 |
-+ *fmt = '\0'; |
6740 |
-+ if (mycost > pred->p_cost) |
6741 |
-+ pred->p_cost = NeedsNothing; |
6742 |
-+ return &(*segment)->next; |
6743 |
-+ break; |
6744 |
-+ } |
6745 |
-+ |
6746 |
-+ assert (kind == KIND_FORMAT); |
6747 |
-+ switch (format_char) |
6748 |
-+ { |
6749 |
-+ case 'l': /* object of symlink */ |
6750 |
-+ pred->need_stat = true; |
6751 |
-+ mycost = NeedsLinkName; |
6752 |
-+ *fmt++ = 's'; |
6753 |
-+ break; |
6754 |
-+ |
6755 |
-+ case 'y': /* file type */ |
6756 |
-+ pred->need_type = true; |
6757 |
-+ mycost = NeedsType; |
6758 |
-+ *fmt++ = 's'; |
6759 |
-+ break; |
6760 |
-+ |
6761 |
-+ case 'a': /* atime in `ctime' format */ |
6762 |
-+ case 'A': /* atime in user-specified strftime format */ |
6763 |
-+ case 'B': /* birth time in user-specified strftime format */ |
6764 |
-+ case 'c': /* ctime in `ctime' format */ |
6765 |
-+ case 'C': /* ctime in user-specified strftime format */ |
6766 |
-+ case 'F': /* file system type */ |
6767 |
-+ case 'g': /* group name */ |
6768 |
-+ case 'i': /* inode number */ |
6769 |
-+ case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */ |
6770 |
-+ case 's': /* size in bytes */ |
6771 |
-+ case 't': /* mtime in `ctime' format */ |
6772 |
-+ case 'T': /* mtime in user-specified strftime format */ |
6773 |
-+ case 'u': /* user name */ |
6774 |
-+ pred->need_stat = true; |
6775 |
-+ mycost = NeedsStatInfo; |
6776 |
-+ *fmt++ = 's'; |
6777 |
-+ break; |
6778 |
-+ |
6779 |
-+ case 'S': /* sparseness */ |
6780 |
-+ pred->need_stat = true; |
6781 |
-+ mycost = NeedsStatInfo; |
6782 |
-+ *fmt++ = 'g'; |
6783 |
-+ break; |
6784 |
-+ |
6785 |
-+ case 'Y': /* symlink pointed file type */ |
6786 |
-+ pred->need_stat = true; |
6787 |
-+ mycost = NeedsType; /* true for amortised effect */ |
6788 |
-+ *fmt++ = 's'; |
6789 |
-+ break; |
6790 |
-+ |
6791 |
-+ case 'f': /* basename of path */ |
6792 |
-+ case 'h': /* leading directories part of path */ |
6793 |
-+ case 'p': /* pathname */ |
6794 |
-+ case 'P': /* pathname with ARGV element stripped */ |
6795 |
-+ *fmt++ = 's'; |
6796 |
-+ break; |
6797 |
-+ |
6798 |
-+ case 'H': /* ARGV element file was found under */ |
6799 |
-+ *fmt++ = 's'; |
6800 |
-+ break; |
6801 |
-+ |
6802 |
-+ /* Numeric items that one might expect to honour |
6803 |
-+ * #, 0, + flags but which do not. |
6804 |
-+ */ |
6805 |
-+ case 'G': /* GID number */ |
6806 |
-+ case 'U': /* UID number */ |
6807 |
-+ case 'b': /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/ |
6808 |
-+ case 'D': /* Filesystem device on which the file exits */ |
6809 |
-+ case 'k': /* size in 1K blocks */ |
6810 |
-+ case 'n': /* number of links */ |
6811 |
-+ pred->need_stat = true; |
6812 |
-+ mycost = NeedsStatInfo; |
6813 |
-+ *fmt++ = 's'; |
6814 |
-+ break; |
6815 |
-+ |
6816 |
-+ /* Numeric items that DO honour #, 0, + flags. |
6817 |
-+ */ |
6818 |
-+ case 'd': /* depth in search tree (0 = ARGV element) */ |
6819 |
-+ *fmt++ = 'd'; |
6820 |
-+ break; |
6821 |
-+ |
6822 |
-+ case 'm': /* mode as octal number (perms only) */ |
6823 |
-+ *fmt++ = 'o'; |
6824 |
-+ pred->need_stat = true; |
6825 |
-+ mycost = NeedsStatInfo; |
6826 |
-+ break; |
6827 |
-+ |
6828 |
-+ case '{': |
6829 |
-+ case '[': |
6830 |
-+ case '(': |
6831 |
-+ error (1, 0, |
6832 |
-+ _("error: the format directive `%%%c' is reserved for future use"), |
6833 |
-+ (int)kind); |
6834 |
-+ /*NOTREACHED*/ |
6835 |
-+ break; |
6836 |
-+ } |
6837 |
-+ *fmt = '\0'; |
6838 |
-+ |
6839 |
-+ if (mycost > pred->p_cost) |
6840 |
-+ pred->p_cost = mycost; |
6841 |
-+ return &(*segment)->next; |
6842 |
-+} |
6843 |
-+ |
6844 |
-+static void |
6845 |
-+check_path_safety(const char *action, char **argv) |
6846 |
-+{ |
6847 |
-+ char *s; |
6848 |
-+ const char *path = getenv("PATH"); |
6849 |
-+ if (NULL == path) |
6850 |
-+ { |
6851 |
-+ /* $PATH is not set. Assume the OS default is safe. |
6852 |
-+ * That may not be true on Windows, but I'm not aware |
6853 |
-+ * of a way to get Windows to avoid searching the |
6854 |
-+ * current directory anyway. |
6855 |
-+ */ |
6856 |
-+ return; |
6857 |
-+ } |
6858 |
-+ |
6859 |
-+ (void)argv; |
6860 |
-+ |
6861 |
-+ s = next_element(path, 1); |
6862 |
-+ while ((s = next_element ((char *) NULL, 1)) != NULL) |
6863 |
-+ { |
6864 |
-+ if (0 == strcmp(s, ".")) |
6865 |
-+ { |
6866 |
-+ error(1, 0, _("The current directory is included in the PATH " |
6867 |
-+ "environment variable, which is insecure in " |
6868 |
-+ "combination with the %s action of find. " |
6869 |
-+ "Please remove the current directory from your " |
6870 |
-+ "$PATH (that is, remove \".\" or leading or trailing " |
6871 |
-+ "colons)"), |
6872 |
-+ action); |
6873 |
-+ } |
6874 |
-+ else if ('/' != s[0]) |
6875 |
-+ { |
6876 |
-+ /* Relative paths are also dangerous in $PATH. */ |
6877 |
-+ error(1, 0, _("The relative path %1$s is included in the PATH " |
6878 |
-+ "environment variable, which is insecure in " |
6879 |
-+ "combination with the %2$s action of find. " |
6880 |
-+ "Please remove that entry from $PATH"), |
6881 |
-+ safely_quote_err_filename(0, s), |
6882 |
-+ action); |
6883 |
-+ } |
6884 |
-+ } |
6885 |
-+} |
6886 |
-+ |
6887 |
-+ |
6888 |
-+/* handles both exec and ok predicate */ |
6889 |
-+static boolean |
6890 |
-+new_insert_exec_ok (const char *action, |
6891 |
-+ const struct parser_table *entry, |
6892 |
-+ int dirfd, |
6893 |
-+ char **argv, |
6894 |
-+ int *arg_ptr) |
6895 |
-+{ |
6896 |
-+ int start, end; /* Indexes in ARGV of start & end of cmd. */ |
6897 |
-+ int i; /* Index into cmd args */ |
6898 |
-+ int saw_braces; /* True if previous arg was '{}'. */ |
6899 |
-+ boolean allow_plus; /* True if + is a valid terminator */ |
6900 |
-+ int brace_count; /* Number of instances of {}. */ |
6901 |
-+ PRED_FUNC func = entry->pred_func; |
6902 |
-+ enum BC_INIT_STATUS bcstatus; |
6903 |
-+ |
6904 |
-+ struct predicate *our_pred; |
6905 |
-+ struct exec_val *execp; /* Pointer for efficiency. */ |
6906 |
-+ |
6907 |
-+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
6908 |
-+ return false; |
6909 |
-+ |
6910 |
-+ our_pred = insert_primary_withpred (entry, func); |
6911 |
-+ our_pred->side_effects = our_pred->no_default_print = true; |
6912 |
-+ our_pred->need_type = our_pred->need_stat = false; |
6913 |
-+ |
6914 |
-+ execp = &our_pred->args.exec_vec; |
6915 |
-+ |
6916 |
-+ if ((func != pred_okdir) && (func != pred_ok)) |
6917 |
-+ { |
6918 |
-+ allow_plus = true; |
6919 |
-+ execp->close_stdin = false; |
6920 |
-+ } |
6921 |
-+ else |
6922 |
-+ { |
6923 |
-+ allow_plus = false; |
6924 |
-+ /* If find reads stdin (i.e. for -ok and similar), close stdin |
6925 |
-+ * in the child to prevent some script from consiming the output |
6926 |
-+ * intended for find. |
6927 |
-+ */ |
6928 |
-+ execp->close_stdin = true; |
6929 |
-+ } |
6930 |
-+ |
6931 |
-+ |
6932 |
-+ if ((func == pred_execdir) || (func == pred_okdir)) |
6933 |
-+ { |
6934 |
-+ options.ignore_readdir_race = false; |
6935 |
-+ check_path_safety(action, argv); |
6936 |
-+ execp->use_current_dir = true; |
6937 |
-+ } |
6938 |
-+ else |
6939 |
-+ { |
6940 |
-+ execp->use_current_dir = false; |
6941 |
-+ } |
6942 |
-+ |
6943 |
-+ our_pred->args.exec_vec.multiple = 0; |
6944 |
-+ |
6945 |
-+ /* Count the number of args with path replacements, up until the ';'. |
6946 |
-+ * Also figure out if the command is terminated by ";" or by "+". |
6947 |
-+ */ |
6948 |
-+ start = *arg_ptr; |
6949 |
-+ for (end = start, saw_braces=0, brace_count=0; |
6950 |
-+ (argv[end] != NULL) |
6951 |
-+ && ((argv[end][0] != ';') || (argv[end][1] != '\0')); |
6952 |
-+ end++) |
6953 |
-+ { |
6954 |
-+ /* For -exec and -execdir, "{} +" can terminate the command. */ |
6955 |
-+ if ( allow_plus |
6956 |
-+ && argv[end][0] == '+' && argv[end][1] == 0 |
6957 |
-+ && saw_braces) |
6958 |
-+ { |
6959 |
-+ our_pred->args.exec_vec.multiple = 1; |
6960 |
-+ break; |
6961 |
-+ } |
6962 |
-+ |
6963 |
-+ saw_braces = 0; |
6964 |
-+ if (mbsstr (argv[end], "{}")) |
6965 |
-+ { |
6966 |
-+ saw_braces = 1; |
6967 |
-+ ++brace_count; |
6968 |
-+ |
6969 |
-+ if (0 == end && (func == pred_execdir || func == pred_okdir)) |
6970 |
-+ { |
6971 |
-+ /* The POSIX standard says that {} replacement should |
6972 |
-+ * occur even in the utility name. This is insecure |
6973 |
-+ * since it means we will be executing a command whose |
6974 |
-+ * name is chosen according to whatever find finds in |
6975 |
-+ * the file system. That can be influenced by an |
6976 |
-+ * attacker. Hence for -execdir and -okdir this is not |
6977 |
-+ * allowed. We can specify this as those options are |
6978 |
-+ * not defined by POSIX. |
6979 |
-+ */ |
6980 |
-+ error(1, 0, _("You may not use {} within the utility name for " |
6981 |
-+ "-execdir and -okdir, because this is a potential " |
6982 |
-+ "security problem.")); |
6983 |
-+ } |
6984 |
-+ } |
6985 |
-+ } |
6986 |
-+ |
6987 |
-+ /* Fail if no command given or no semicolon found. */ |
6988 |
-+ if ((end == start) || (argv[end] == NULL)) |
6989 |
-+ { |
6990 |
-+ *arg_ptr = end; |
6991 |
-+ free(our_pred); |
6992 |
-+ return false; |
6993 |
-+ } |
6994 |
-+ |
6995 |
-+ if (our_pred->args.exec_vec.multiple && brace_count > 1) |
6996 |
-+ { |
6997 |
-+ |
6998 |
-+ const char *suffix; |
6999 |
-+ if (func == pred_execdir) |
7000 |
-+ suffix = "dir"; |
7001 |
-+ else |
7002 |
-+ suffix = ""; |
7003 |
-+ |
7004 |
-+ error(1, 0, |
7005 |
-+ _("Only one instance of {} is supported with -exec%s ... +"), |
7006 |
-+ suffix); |
7007 |
-+ } |
7008 |
-+ |
7009 |
-+ /* We use a switch statement here so that the compiler warns us when |
7010 |
-+ * we forget to handle a newly invented enum value. |
7011 |
-+ * |
7012 |
-+ * Like xargs, we allow 2KiB of headroom for the launched utility to |
7013 |
-+ * export its own environment variables before calling something |
7014 |
-+ * else. |
7015 |
-+ */ |
7016 |
-+ bcstatus = bc_init_controlinfo(&execp->ctl, 2048u); |
7017 |
-+ switch (bcstatus) |
7018 |
-+ { |
7019 |
-+ case BC_INIT_ENV_TOO_BIG: |
7020 |
-+ case BC_INIT_CANNOT_ACCOMODATE_HEADROOM: |
7021 |
-+ error(1, 0, |
7022 |
-+ _("The environment is too large for exec().")); |
7023 |
-+ break; |
7024 |
-+ case BC_INIT_OK: |
7025 |
-+ /* Good news. Carry on. */ |
7026 |
-+ break; |
7027 |
-+ } |
7028 |
-+ bc_use_sensible_arg_max(&execp->ctl); |
7029 |
-+ |
7030 |
-+ |
7031 |
-+ execp->ctl.exec_callback = launch; |
7032 |
-+ |
7033 |
-+ if (our_pred->args.exec_vec.multiple) |
7034 |
-+ { |
7035 |
-+ /* "+" terminator, so we can just append our arguments after the |
7036 |
-+ * command and initial arguments. |
7037 |
-+ */ |
7038 |
-+ execp->replace_vec = NULL; |
7039 |
-+ execp->ctl.replace_pat = NULL; |
7040 |
-+ execp->ctl.rplen = 0; |
7041 |
-+ execp->ctl.lines_per_exec = 0; /* no limit */ |
7042 |
-+ execp->ctl.args_per_exec = 0; /* no limit */ |
7043 |
-+ |
7044 |
-+ /* remember how many arguments there are */ |
7045 |
-+ execp->ctl.initial_argc = (end-start) - 1; |
7046 |
-+ |
7047 |
-+ /* execp->state = xmalloc(sizeof struct buildcmd_state); */ |
7048 |
-+ bc_init_state(&execp->ctl, &execp->state, execp); |
7049 |
-+ |
7050 |
-+ /* Gather the initial arguments. Skip the {}. */ |
7051 |
-+ for (i=start; i<end-1; ++i) |
7052 |
-+ { |
7053 |
-+ bc_push_arg(&execp->ctl, &execp->state, |
7054 |
-+ argv[i], strlen(argv[i])+1, |
7055 |
-+ NULL, 0, |
7056 |
-+ 1); |
7057 |
-+ } |
7058 |
-+ } |
7059 |
-+ else |
7060 |
-+ { |
7061 |
-+ /* Semicolon terminator - more than one {} is supported, so we |
7062 |
-+ * have to do brace-replacement. |
7063 |
-+ */ |
7064 |
-+ execp->num_args = end - start; |
7065 |
-+ |
7066 |
-+ execp->ctl.replace_pat = "{}"; |
7067 |
-+ execp->ctl.rplen = strlen(execp->ctl.replace_pat); |
7068 |
-+ execp->ctl.lines_per_exec = 0; /* no limit */ |
7069 |
-+ execp->ctl.args_per_exec = 0; /* no limit */ |
7070 |
-+ execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args); |
7071 |
-+ |
7072 |
-+ |
7073 |
-+ /* execp->state = xmalloc(sizeof(*(execp->state))); */ |
7074 |
-+ bc_init_state(&execp->ctl, &execp->state, execp); |
7075 |
-+ |
7076 |
-+ /* Remember the (pre-replacement) arguments for later. */ |
7077 |
-+ for (i=0; i<execp->num_args; ++i) |
7078 |
-+ { |
7079 |
-+ execp->replace_vec[i] = argv[i+start]; |
7080 |
-+ } |
7081 |
-+ } |
7082 |
-+ |
7083 |
-+ if (argv[end] == NULL) |
7084 |
-+ *arg_ptr = end; |
7085 |
-+ else |
7086 |
-+ *arg_ptr = end + 1; |
7087 |
-+ |
7088 |
-+ return true; |
7089 |
-+} |
7090 |
-+ |
7091 |
-+ |
7092 |
-+ |
7093 |
-+static boolean |
7094 |
-+insert_exec_ok (const char *action, |
7095 |
-+ const struct parser_table *entry, |
7096 |
-+ int dirfd, |
7097 |
-+ char **argv, |
7098 |
-+ int *arg_ptr) |
7099 |
-+{ |
7100 |
-+ return new_insert_exec_ok(action, entry, dirfd, argv, arg_ptr); |
7101 |
-+} |
7102 |
-+ |
7103 |
-+ |
7104 |
-+ |
7105 |
-+/* Get a timestamp and comparison type. |
7106 |
-+ |
7107 |
-+ STR is the ASCII representation. |
7108 |
-+ Set *NUM_DAYS to the number of days/minutes/whatever, taken as being |
7109 |
-+ relative to ORIGIN (usually the current moment or midnight). |
7110 |
-+ Thus the sense of the comparison type appears to be reversed. |
7111 |
-+ Set *COMP_TYPE to the kind of comparison that is requested. |
7112 |
-+ Issue OVERFLOWMESSAGE if overflow occurs. |
7113 |
-+ Return true if all okay, false if input error. |
7114 |
-+ |
7115 |
-+ Used by -atime, -ctime and -mtime (parsers) to |
7116 |
-+ get the appropriate information for a time predicate processor. */ |
7117 |
-+ |
7118 |
-+static boolean |
7119 |
-+get_relative_timestamp (const char *str, |
7120 |
-+ struct time_val *result, |
7121 |
-+ time_t origin, |
7122 |
-+ double sec_per_unit, |
7123 |
-+ const char *overflowmessage) |
7124 |
-+{ |
7125 |
-+ uintmax_t checkval; |
7126 |
-+ double offset, seconds, f; |
7127 |
-+ |
7128 |
-+ if (get_comp_type(&str, &result->kind)) |
7129 |
-+ { |
7130 |
-+ /* Invert the sense of the comparison */ |
7131 |
-+ switch (result->kind) |
7132 |
-+ { |
7133 |
-+ case COMP_LT: result->kind = COMP_GT; break; |
7134 |
-+ case COMP_GT: result->kind = COMP_LT; break; |
7135 |
-+ default: break; |
7136 |
-+ } |
7137 |
-+ |
7138 |
-+ /* Convert the ASCII number into floating-point. */ |
7139 |
-+ if (xstrtod(str, NULL, &offset, strtod)) |
7140 |
-+ { |
7141 |
-+ /* Separate the floating point number the user specified |
7142 |
-+ * (which is a number of days, or minutes, etc) into an |
7143 |
-+ * integral number of seconds (SECONDS) and a fraction (F). |
7144 |
-+ */ |
7145 |
-+ f = modf(offset * sec_per_unit, &seconds); |
7146 |
-+ |
7147 |
-+ result->ts.tv_sec = origin - seconds; |
7148 |
-+ result->ts.tv_nsec = fabs(f * 1e9); |
7149 |
-+ |
7150 |
-+ /* Check for overflow. */ |
7151 |
-+ checkval = (uintmax_t)origin - seconds; |
7152 |
-+ if (checkval != result->ts.tv_sec) |
7153 |
-+ { |
7154 |
-+ /* an overflow has occurred. */ |
7155 |
-+ error (1, 0, overflowmessage, str); |
7156 |
-+ } |
7157 |
-+ return true; |
7158 |
-+ } |
7159 |
-+ else |
7160 |
-+ { |
7161 |
-+ /* Conversion from ASCII to double failed. */ |
7162 |
-+ return false; |
7163 |
-+ } |
7164 |
-+ } |
7165 |
-+ else |
7166 |
-+ { |
7167 |
-+ return false; |
7168 |
-+ } |
7169 |
-+} |
7170 |
-+ |
7171 |
-+/* Insert a time predicate based on the information in ENTRY. |
7172 |
-+ ARGV is a pointer to the argument array. |
7173 |
-+ ARG_PTR is a pointer to an index into the array, incremented if |
7174 |
-+ all went well. |
7175 |
-+ |
7176 |
-+ Return true if input is valid, false if not. |
7177 |
-+ |
7178 |
-+ A new predicate node is assigned, along with an argument node |
7179 |
-+ obtained with malloc. |
7180 |
-+ |
7181 |
-+ Used by -atime, -ctime, and -mtime parsers. */ |
7182 |
-+ |
7183 |
-+static boolean |
7184 |
-+parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) |
7185 |
-+{ |
7186 |
-+ struct predicate *our_pred; |
7187 |
-+ struct time_val tval; |
7188 |
-+ enum comparison_type comp; |
7189 |
-+ const char *timearg, *orig_timearg; |
7190 |
-+ const char *errmsg = "arithmetic overflow while converting %s " |
7191 |
-+ "days to a number of seconds"; |
7192 |
-+ time_t origin; |
7193 |
-+ |
7194 |
-+ if (!collect_arg(argv, arg_ptr, &timearg)) |
7195 |
-+ return false; |
7196 |
-+ orig_timearg = timearg; |
7197 |
-+ |
7198 |
-+ /* Decide the origin by previewing the comparison type. */ |
7199 |
-+ origin = options.cur_day_start; |
7200 |
-+ |
7201 |
-+ if (get_comp_type(&timearg, &comp)) |
7202 |
-+ { |
7203 |
-+ /* Remember, we invert the sense of the comparison, so this tests |
7204 |
-+ * against COMP_LT instead of COMP_GT... |
7205 |
-+ */ |
7206 |
-+ if (COMP_LT == comp) |
7207 |
-+ { |
7208 |
-+ uintmax_t expected = origin + (DAYSECS-1); |
7209 |
-+ origin += (DAYSECS-1); |
7210 |
-+ if (origin != expected) |
7211 |
-+ { |
7212 |
-+ error(1, 0, |
7213 |
-+ _("arithmetic overflow when trying to calculate the end of today")); |
7214 |
-+ } |
7215 |
-+ } |
7216 |
-+ } |
7217 |
-+ /* We discard the value of comp here, as get_relative_timestamp |
7218 |
-+ * will set tval.kind. For that to work, we have to restore |
7219 |
-+ * timearg so that it points to the +/- prefix, if any. get_comp_type() |
7220 |
-+ * will have advanced timearg, so we restore it. |
7221 |
-+ */ |
7222 |
-+ timearg = orig_timearg; |
7223 |
-+ |
7224 |
-+ if (!get_relative_timestamp(timearg, &tval, origin, DAYSECS, errmsg)) |
7225 |
-+ return false; |
7226 |
-+ |
7227 |
-+ our_pred = insert_primary (entry); |
7228 |
-+ our_pred->args.reftime = tval; |
7229 |
-+ our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec); |
7230 |
-+ |
7231 |
-+ if (options.debug_options & DebugExpressionTree) |
7232 |
-+ { |
7233 |
-+ time_t t; |
7234 |
-+ |
7235 |
-+ fprintf (stderr, "inserting %s\n", our_pred->p_name); |
7236 |
-+ fprintf (stderr, " type: %s %s ", |
7237 |
-+ (tval.kind == COMP_GT) ? "gt" : |
7238 |
-+ ((tval.kind == COMP_LT) ? "lt" : ((tval.kind == COMP_EQ) ? "eq" : "?")), |
7239 |
-+ (tval.kind == COMP_GT) ? " >" : |
7240 |
-+ ((tval.kind == COMP_LT) ? " <" : ((tval.kind == COMP_EQ) ? ">=" : " ?"))); |
7241 |
-+ t = our_pred->args.reftime.ts.tv_sec; |
7242 |
-+ fprintf (stderr, "%ju %s", |
7243 |
-+ (uintmax_t) our_pred->args.reftime.ts.tv_sec, |
7244 |
-+ ctime (&t)); |
7245 |
-+ if (tval.kind == COMP_EQ) |
7246 |
-+ { |
7247 |
-+ t = our_pred->args.reftime.ts.tv_sec + DAYSECS; |
7248 |
-+ fprintf (stderr, " < %ju %s", |
7249 |
-+ (uintmax_t) t, ctime (&t)); |
7250 |
-+ } |
7251 |
-+ } |
7252 |
-+ |
7253 |
-+ return true; |
7254 |
-+} |
7255 |
-+ |
7256 |
-+/* Get the comparison type prefix (if any) from a number argument. |
7257 |
-+ The prefix is at *STR. |
7258 |
-+ Set *COMP_TYPE to the kind of comparison that is requested. |
7259 |
-+ Advance *STR beyond any initial comparison prefix. |
7260 |
-+ |
7261 |
-+ Return true if all okay, false if input error. */ |
7262 |
-+static boolean |
7263 |
-+get_comp_type(const char **str, enum comparison_type *comp_type) |
7264 |
-+{ |
7265 |
-+ switch (**str) |
7266 |
-+ { |
7267 |
-+ case '+': |
7268 |
-+ *comp_type = COMP_GT; |
7269 |
-+ (*str)++; |
7270 |
-+ break; |
7271 |
-+ case '-': |
7272 |
-+ *comp_type = COMP_LT; |
7273 |
-+ (*str)++; |
7274 |
-+ break; |
7275 |
-+ default: |
7276 |
-+ *comp_type = COMP_EQ; |
7277 |
-+ break; |
7278 |
-+ } |
7279 |
-+ return true; |
7280 |
-+} |
7281 |
-+ |
7282 |
-+ |
7283 |
-+ |
7284 |
-+ |
7285 |
-+ |
7286 |
-+/* Get a number with comparison information. |
7287 |
-+ The sense of the comparison information is 'normal'; that is, |
7288 |
-+ '+' looks for a count > than the number and '-' less than. |
7289 |
-+ |
7290 |
-+ STR is the ASCII representation of the number. |
7291 |
-+ Set *NUM to the number. |
7292 |
-+ Set *COMP_TYPE to the kind of comparison that is requested. |
7293 |
-+ |
7294 |
-+ Return true if all okay, false if input error. */ |
7295 |
-+ |
7296 |
-+static boolean |
7297 |
-+get_num (const char *str, |
7298 |
-+ uintmax_t *num, |
7299 |
-+ enum comparison_type *comp_type) |
7300 |
-+{ |
7301 |
-+ char *pend; |
7302 |
-+ |
7303 |
-+ if (str == NULL) |
7304 |
-+ return false; |
7305 |
-+ |
7306 |
-+ /* Figure out the comparison type if the caller accepts one. */ |
7307 |
-+ if (comp_type) |
7308 |
-+ { |
7309 |
-+ if (!get_comp_type(&str, comp_type)) |
7310 |
-+ return false; |
7311 |
-+ } |
7312 |
-+ |
7313 |
-+ return xstrtoumax (str, &pend, 10, num, "") == LONGINT_OK; |
7314 |
-+} |
7315 |
-+ |
7316 |
-+/* Insert a number predicate. |
7317 |
-+ ARGV is a pointer to the argument array. |
7318 |
-+ *ARG_PTR is an index into ARGV, incremented if all went well. |
7319 |
-+ *PRED is the predicate processor to insert. |
7320 |
-+ |
7321 |
-+ Return true if input is valid, false if error. |
7322 |
-+ |
7323 |
-+ A new predicate node is assigned, along with an argument node |
7324 |
-+ obtained with malloc. |
7325 |
-+ |
7326 |
-+ Used by -inum and -links parsers. */ |
7327 |
-+ |
7328 |
-+static struct predicate * |
7329 |
-+insert_num (char **argv, int *arg_ptr, const struct parser_table *entry) |
7330 |
-+{ |
7331 |
-+ const char *numstr; |
7332 |
-+ |
7333 |
-+ if (collect_arg(argv, arg_ptr, &numstr)) |
7334 |
-+ { |
7335 |
-+ uintmax_t num; |
7336 |
-+ enum comparison_type c_type; |
7337 |
-+ |
7338 |
-+ if (get_num (numstr, &num, &c_type)) |
7339 |
-+ { |
7340 |
-+ struct predicate *our_pred = insert_primary (entry); |
7341 |
-+ our_pred->args.numinfo.kind = c_type; |
7342 |
-+ our_pred->args.numinfo.l_val = num; |
7343 |
-+ |
7344 |
-+ if (options.debug_options & DebugExpressionTree) |
7345 |
-+ { |
7346 |
-+ fprintf (stderr, "inserting %s\n", our_pred->p_name); |
7347 |
-+ fprintf (stderr, " type: %s %s ", |
7348 |
-+ (c_type == COMP_GT) ? "gt" : |
7349 |
-+ ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")), |
7350 |
-+ (c_type == COMP_GT) ? " >" : |
7351 |
-+ ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?"))); |
7352 |
-+ fprintf (stderr, "%ju\n", our_pred->args.numinfo.l_val); |
7353 |
-+ } |
7354 |
-+ return our_pred; |
7355 |
-+ } |
7356 |
-+ } |
7357 |
-+ return NULL; |
7358 |
-+} |
7359 |
-+ |
7360 |
-+static void |
7361 |
-+open_output_file (const char *path, struct format_val *p) |
7362 |
-+{ |
7363 |
-+ p->segment = NULL; |
7364 |
-+ p->quote_opts = clone_quoting_options (NULL); |
7365 |
-+ |
7366 |
-+ if (!strcmp (path, "/dev/stderr")) |
7367 |
-+ { |
7368 |
-+ p->stream = stderr; |
7369 |
-+ p->filename = _("standard error"); |
7370 |
-+ } |
7371 |
-+ else if (!strcmp (path, "/dev/stdout")) |
7372 |
-+ { |
7373 |
-+ p->stream = stdout; |
7374 |
-+ p->filename = _("standard output"); |
7375 |
-+ } |
7376 |
-+ else |
7377 |
-+ { |
7378 |
-+ p->stream = fopen_safer (path, "w"); |
7379 |
-+ p->filename = path; |
7380 |
-+ |
7381 |
-+ if (p->stream == NULL) |
7382 |
-+ { |
7383 |
-+ fatal_file_error(path); |
7384 |
-+ } |
7385 |
-+ } |
7386 |
-+ |
7387 |
-+ p->dest_is_tty = stream_is_tty(p->stream); |
7388 |
-+} |
7389 |
-+ |
7390 |
-+static void |
7391 |
-+open_stdout (struct format_val *p) |
7392 |
-+{ |
7393 |
-+ open_output_file("/dev/stdout", p); |
7394 |
-+} |
7395 |
-diff -purN findutils-4.3.12.orig/find/pred.c findutils-4.3.12/find/pred.c |
7396 |
---- findutils-4.3.12.orig/find/pred.c 2007-12-19 16:12:34.000000000 -0500 |
7397 |
-+++ findutils-4.3.12/find/pred.c 2008-01-30 08:46:05.758843847 -0500 |
7398 |
-@@ -47,6 +47,14 @@ |
7399 |
- #include "error.h" |
7400 |
- #include "verify.h" |
7401 |
- |
7402 |
-+#ifdef WITH_SELINUX |
7403 |
-+#include <selinux/selinux.h> |
7404 |
-+#endif /*WITH_SELINUX*/ |
7405 |
-+ |
7406 |
-+#ifndef FNM_CASEFOLD |
7407 |
-+#define FNM_CASEFOLD (1<<4) |
7408 |
-+#endif /*FNM_CASEFOLD*/ |
7409 |
-+ |
7410 |
- #if ENABLE_NLS |
7411 |
- # include <libintl.h> |
7412 |
- # define _(Text) gettext (Text) |
7413 |
-@@ -229,6 +237,9 @@ struct pred_assoc pred_table[] = |
7414 |
- {pred_user, "user "}, |
7415 |
- {pred_writable, "writable "}, |
7416 |
- {pred_xtype, "xtype "}, |
7417 |
-+#ifdef WITH_SELINUX |
7418 |
-+ {pred_scontext, "context"}, |
7419 |
-+#endif /*WITH_SELINUX*/ |
7420 |
- {0, "none "} |
7421 |
- }; |
7422 |
- #endif |
7423 |
-@@ -1045,6 +1056,26 @@ do_fprintf(struct format_val *dest, |
7424 |
- mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
7425 |
- } |
7426 |
- break; |
7427 |
-+#ifdef WITH_SELINUX |
7428 |
-+ case 'Z': /* SELinux security context */ |
7429 |
-+ { |
7430 |
-+ security_context_t scontext; |
7431 |
-+ int rv; |
7432 |
-+ rv = (*options.x_getfilecon)(state.rel_pathname, &scontext); |
7433 |
-+ |
7434 |
-+ if ( rv < 0 ) { |
7435 |
-+ fprintf(stderr, "getfileconf(%s): %s", |
7436 |
-+ pathname, strerror(errno)); |
7437 |
-+ fflush(stderr); |
7438 |
-+ } |
7439 |
-+ else { |
7440 |
-+ segment->text[segment->text_len] = 's'; |
7441 |
-+ checked_fprintf (dest, segment->text, scontext); |
7442 |
-+ freecon(scontext); |
7443 |
-+ } |
7444 |
-+ } |
7445 |
-+ break ; |
7446 |
-+#endif /* WITH_SELINUX */ |
7447 |
- } |
7448 |
- /* end of KIND_FORMAT case */ |
7449 |
- break; |
7450 |
-@@ -1838,6 +1869,31 @@ pred_xtype (const char *pathname, struct |
7451 |
- */ |
7452 |
- return (pred_type (pathname, &sbuf, pred_ptr)); |
7453 |
- } |
7454 |
-+ |
7455 |
-+ |
7456 |
-+#ifdef WITH_SELINUX |
7457 |
-+ |
7458 |
-+boolean |
7459 |
-+pred_scontext (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7460 |
-+{ |
7461 |
-+ int rv; |
7462 |
-+ security_context_t scontext; |
7463 |
-+ |
7464 |
-+ rv = (* options.x_getfilecon)(state.rel_pathname, &scontext); |
7465 |
-+ |
7466 |
-+ if ( rv < 0 ) { |
7467 |
-+ (void) fprintf(stderr, "getfilecon(%s): %s\n", pathname, strerror(errno)); |
7468 |
-+ (void) fflush(stderr); |
7469 |
-+ return ( false ); |
7470 |
-+ } |
7471 |
-+ |
7472 |
-+ rv = (fnmatch(pred_ptr->args.scontext, scontext,0)==0); |
7473 |
-+ freecon(scontext); |
7474 |
-+ return rv; |
7475 |
-+} |
7476 |
-+ |
7477 |
-+#endif /*WITH_SELINUX*/ |
7478 |
-+ |
7479 |
- |
7480 |
- /* 1) fork to get a child; parent remembers the child pid |
7481 |
- 2) child execs the command requested |
7482 |
-diff -purN findutils-4.3.12.orig/find/pred.c.orig findutils-4.3.12/find/pred.c.orig |
7483 |
---- findutils-4.3.12.orig/find/pred.c.orig 1969-12-31 19:00:00.000000000 -0500 |
7484 |
-+++ findutils-4.3.12/find/pred.c.orig 2007-12-19 16:12:34.000000000 -0500 |
7485 |
-@@ -0,0 +1,2405 @@ |
7486 |
-+/* pred.c -- execute the expression tree. |
7487 |
-+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003, |
7488 |
-+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc. |
7489 |
-+ |
7490 |
-+ This program is free software: you can redistribute it and/or modify |
7491 |
-+ it under the terms of the GNU General Public License as published by |
7492 |
-+ the Free Software Foundation, either version 3 of the License, or |
7493 |
-+ (at your option) any later version. |
7494 |
-+ |
7495 |
-+ This program is distributed in the hope that it will be useful, |
7496 |
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
7497 |
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
7498 |
-+ GNU General Public License for more details. |
7499 |
-+ |
7500 |
-+ You should have received a copy of the GNU General Public License |
7501 |
-+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
7502 |
-+*/ |
7503 |
-+ |
7504 |
-+#include <config.h> |
7505 |
-+#include "defs.h" |
7506 |
-+ |
7507 |
-+#include <fnmatch.h> |
7508 |
-+#include <signal.h> |
7509 |
-+#include <math.h> |
7510 |
-+#include <pwd.h> |
7511 |
-+#include <grp.h> |
7512 |
-+#include <sys/types.h> |
7513 |
-+#include <sys/stat.h> |
7514 |
-+#include <errno.h> |
7515 |
-+#include <assert.h> |
7516 |
-+#include <stdarg.h> |
7517 |
-+#include <fcntl.h> |
7518 |
-+#include <locale.h> |
7519 |
-+#include <openat.h> |
7520 |
-+#include "xalloc.h" |
7521 |
-+#include "dirname.h" |
7522 |
-+#include "human.h" |
7523 |
-+#include "modetype.h" |
7524 |
-+#include "filemode.h" |
7525 |
-+#include "wait.h" |
7526 |
-+#include "printquoted.h" |
7527 |
-+#include "buildcmd.h" |
7528 |
-+#include "yesno.h" |
7529 |
-+#include "listfile.h" |
7530 |
-+#include "stat-time.h" |
7531 |
-+#include "dircallback.h" |
7532 |
-+#include "error.h" |
7533 |
-+#include "verify.h" |
7534 |
-+ |
7535 |
-+#if ENABLE_NLS |
7536 |
-+# include <libintl.h> |
7537 |
-+# define _(Text) gettext (Text) |
7538 |
-+#else |
7539 |
-+# define _(Text) Text |
7540 |
-+#endif |
7541 |
-+#ifdef gettext_noop |
7542 |
-+# define N_(String) gettext_noop (String) |
7543 |
-+#else |
7544 |
-+/* See locate.c for explanation as to why not use (String) */ |
7545 |
-+# define N_(String) String |
7546 |
-+#endif |
7547 |
-+ |
7548 |
-+#if !defined(SIGCHLD) && defined(SIGCLD) |
7549 |
-+#define SIGCHLD SIGCLD |
7550 |
-+#endif |
7551 |
-+ |
7552 |
-+ |
7553 |
-+ |
7554 |
-+#if HAVE_DIRENT_H |
7555 |
-+# include <dirent.h> |
7556 |
-+# define NAMLEN(dirent) strlen((dirent)->d_name) |
7557 |
-+#else |
7558 |
-+# define dirent direct |
7559 |
-+# define NAMLEN(dirent) (dirent)->d_namlen |
7560 |
-+# if HAVE_SYS_NDIR_H |
7561 |
-+# include <sys/ndir.h> |
7562 |
-+# endif |
7563 |
-+# if HAVE_SYS_DIR_H |
7564 |
-+# include <sys/dir.h> |
7565 |
-+# endif |
7566 |
-+# if HAVE_NDIR_H |
7567 |
-+# include <ndir.h> |
7568 |
-+# endif |
7569 |
-+#endif |
7570 |
-+ |
7571 |
-+#ifdef CLOSEDIR_VOID |
7572 |
-+/* Fake a return value. */ |
7573 |
-+#define CLOSEDIR(d) (closedir (d), 0) |
7574 |
-+#else |
7575 |
-+#define CLOSEDIR(d) closedir (d) |
7576 |
-+#endif |
7577 |
-+ |
7578 |
-+ |
7579 |
-+ |
7580 |
-+ |
7581 |
-+/* Get or fake the disk device blocksize. |
7582 |
-+ Usually defined by sys/param.h (if at all). */ |
7583 |
-+#ifndef DEV_BSIZE |
7584 |
-+# ifdef BSIZE |
7585 |
-+# define DEV_BSIZE BSIZE |
7586 |
-+# else /* !BSIZE */ |
7587 |
-+# define DEV_BSIZE 4096 |
7588 |
-+# endif /* !BSIZE */ |
7589 |
-+#endif /* !DEV_BSIZE */ |
7590 |
-+ |
7591 |
-+/* Extract or fake data from a `struct stat'. |
7592 |
-+ ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes. |
7593 |
-+ ST_NBLOCKS: Number of blocks in the file, including indirect blocks. |
7594 |
-+ ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */ |
7595 |
-+#ifndef HAVE_STRUCT_STAT_ST_BLOCKS |
7596 |
-+# define ST_BLKSIZE(statbuf) DEV_BSIZE |
7597 |
-+# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */ |
7598 |
-+# define ST_NBLOCKS(statbuf) \ |
7599 |
-+ (S_ISREG ((statbuf).st_mode) \ |
7600 |
-+ || S_ISDIR ((statbuf).st_mode) \ |
7601 |
-+ ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0) |
7602 |
-+# else /* !_POSIX_SOURCE && BSIZE */ |
7603 |
-+# define ST_NBLOCKS(statbuf) \ |
7604 |
-+ (S_ISREG ((statbuf).st_mode) \ |
7605 |
-+ || S_ISDIR ((statbuf).st_mode) \ |
7606 |
-+ ? st_blocks ((statbuf).st_size) : 0) |
7607 |
-+# endif /* !_POSIX_SOURCE && BSIZE */ |
7608 |
-+#else /* HAVE_STRUCT_STAT_ST_BLOCKS */ |
7609 |
-+/* Some systems, like Sequents, return st_blksize of 0 on pipes. */ |
7610 |
-+# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \ |
7611 |
-+ ? (statbuf).st_blksize : DEV_BSIZE) |
7612 |
-+# if defined hpux || defined __hpux__ || defined __hpux |
7613 |
-+/* HP-UX counts st_blocks in 1024-byte units. |
7614 |
-+ This loses when mixing HP-UX and BSD file systems with NFS. */ |
7615 |
-+# define ST_NBLOCKSIZE 1024 |
7616 |
-+# else /* !hpux */ |
7617 |
-+# if defined _AIX && defined _I386 |
7618 |
-+/* AIX PS/2 counts st_blocks in 4K units. */ |
7619 |
-+# define ST_NBLOCKSIZE (4 * 1024) |
7620 |
-+# else /* not AIX PS/2 */ |
7621 |
-+# if defined _CRAY |
7622 |
-+# define ST_NBLOCKS(statbuf) \ |
7623 |
-+ (S_ISREG ((statbuf).st_mode) \ |
7624 |
-+ || S_ISDIR ((statbuf).st_mode) \ |
7625 |
-+ ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0) |
7626 |
-+# endif /* _CRAY */ |
7627 |
-+# endif /* not AIX PS/2 */ |
7628 |
-+# endif /* !hpux */ |
7629 |
-+#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */ |
7630 |
-+ |
7631 |
-+#ifndef ST_NBLOCKS |
7632 |
-+# define ST_NBLOCKS(statbuf) \ |
7633 |
-+ (S_ISREG ((statbuf).st_mode) \ |
7634 |
-+ || S_ISDIR ((statbuf).st_mode) \ |
7635 |
-+ ? (statbuf).st_blocks : 0) |
7636 |
-+#endif |
7637 |
-+ |
7638 |
-+#ifndef ST_NBLOCKSIZE |
7639 |
-+# define ST_NBLOCKSIZE 512 |
7640 |
-+#endif |
7641 |
-+ |
7642 |
-+ |
7643 |
-+#undef MAX |
7644 |
-+#define MAX(a, b) ((a) > (b) ? (a) : (b)) |
7645 |
-+ |
7646 |
-+static boolean match_lname PARAMS((const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); |
7647 |
-+ |
7648 |
-+static char *format_date PARAMS((struct timespec ts, int kind)); |
7649 |
-+static char *ctime_format PARAMS((struct timespec ts)); |
7650 |
-+ |
7651 |
-+#ifdef DEBUG |
7652 |
-+struct pred_assoc |
7653 |
-+{ |
7654 |
-+ PRED_FUNC pred_func; |
7655 |
-+ char *pred_name; |
7656 |
-+}; |
7657 |
-+ |
7658 |
-+struct pred_assoc pred_table[] = |
7659 |
-+{ |
7660 |
-+ {pred_amin, "amin "}, |
7661 |
-+ {pred_and, "and "}, |
7662 |
-+ {pred_anewer, "anewer "}, |
7663 |
-+ {pred_atime, "atime "}, |
7664 |
-+ {pred_closeparen, ") "}, |
7665 |
-+ {pred_cmin, "cmin "}, |
7666 |
-+ {pred_cnewer, "cnewer "}, |
7667 |
-+ {pred_comma, ", "}, |
7668 |
-+ {pred_ctime, "ctime "}, |
7669 |
-+ {pred_delete, "delete "}, |
7670 |
-+ {pred_empty, "empty "}, |
7671 |
-+ {pred_exec, "exec "}, |
7672 |
-+ {pred_execdir, "execdir "}, |
7673 |
-+ {pred_executable, "executable "}, |
7674 |
-+ {pred_false, "false "}, |
7675 |
-+ {pred_fprint, "fprint "}, |
7676 |
-+ {pred_fprint0, "fprint0 "}, |
7677 |
-+ {pred_fprintf, "fprintf "}, |
7678 |
-+ {pred_fstype, "fstype "}, |
7679 |
-+ {pred_gid, "gid "}, |
7680 |
-+ {pred_group, "group "}, |
7681 |
-+ {pred_ilname, "ilname "}, |
7682 |
-+ {pred_iname, "iname "}, |
7683 |
-+ {pred_inum, "inum "}, |
7684 |
-+ {pred_ipath, "ipath "}, |
7685 |
-+ {pred_links, "links "}, |
7686 |
-+ {pred_lname, "lname "}, |
7687 |
-+ {pred_ls, "ls "}, |
7688 |
-+ {pred_mmin, "mmin "}, |
7689 |
-+ {pred_mtime, "mtime "}, |
7690 |
-+ {pred_name, "name "}, |
7691 |
-+ {pred_negate, "not "}, |
7692 |
-+ {pred_newer, "newer "}, |
7693 |
-+ {pred_newerXY, "newerXY "}, |
7694 |
-+ {pred_nogroup, "nogroup "}, |
7695 |
-+ {pred_nouser, "nouser "}, |
7696 |
-+ {pred_ok, "ok "}, |
7697 |
-+ {pred_okdir, "okdir "}, |
7698 |
-+ {pred_openparen, "( "}, |
7699 |
-+ {pred_or, "or "}, |
7700 |
-+ {pred_path, "path "}, |
7701 |
-+ {pred_perm, "perm "}, |
7702 |
-+ {pred_print, "print "}, |
7703 |
-+ {pred_print0, "print0 "}, |
7704 |
-+ {pred_prune, "prune "}, |
7705 |
-+ {pred_quit, "quit "}, |
7706 |
-+ {pred_readable, "readable "}, |
7707 |
-+ {pred_regex, "regex "}, |
7708 |
-+ {pred_samefile,"samefile "}, |
7709 |
-+ {pred_size, "size "}, |
7710 |
-+ {pred_true, "true "}, |
7711 |
-+ {pred_type, "type "}, |
7712 |
-+ {pred_uid, "uid "}, |
7713 |
-+ {pred_used, "used "}, |
7714 |
-+ {pred_user, "user "}, |
7715 |
-+ {pred_writable, "writable "}, |
7716 |
-+ {pred_xtype, "xtype "}, |
7717 |
-+ {0, "none "} |
7718 |
-+}; |
7719 |
-+#endif |
7720 |
-+ |
7721 |
-+/* Returns ts1 - ts2 */ |
7722 |
-+static double ts_difference(struct timespec ts1, |
7723 |
-+ struct timespec ts2) |
7724 |
-+{ |
7725 |
-+ double d = difftime(ts1.tv_sec, ts2.tv_sec) |
7726 |
-+ + (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec)); |
7727 |
-+ return d; |
7728 |
-+} |
7729 |
-+ |
7730 |
-+ |
7731 |
-+static int |
7732 |
-+compare_ts(struct timespec ts1, |
7733 |
-+ struct timespec ts2) |
7734 |
-+{ |
7735 |
-+ if ((ts1.tv_sec == ts2.tv_sec) && |
7736 |
-+ (ts1.tv_nsec == ts2.tv_nsec)) |
7737 |
-+ { |
7738 |
-+ return 0; |
7739 |
-+ } |
7740 |
-+ else |
7741 |
-+ { |
7742 |
-+ double diff = ts_difference(ts1, ts2); |
7743 |
-+ return diff < 0.0 ? -1 : +1; |
7744 |
-+ } |
7745 |
-+} |
7746 |
-+ |
7747 |
-+/* Predicate processing routines. |
7748 |
-+ |
7749 |
-+ PATHNAME is the full pathname of the file being checked. |
7750 |
-+ *STAT_BUF contains information about PATHNAME. |
7751 |
-+ *PRED_PTR contains information for applying the predicate. |
7752 |
-+ |
7753 |
-+ Return true if the file passes this predicate, false if not. */ |
7754 |
-+ |
7755 |
-+ |
7756 |
-+/* pred_timewindow |
7757 |
-+ * |
7758 |
-+ * Returns true if THE_TIME is |
7759 |
-+ * COMP_GT: after the specified time |
7760 |
-+ * COMP_LT: before the specified time |
7761 |
-+ * COMP_EQ: less than WINDOW seconds after the specified time. |
7762 |
-+ */ |
7763 |
-+static boolean |
7764 |
-+pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window) |
7765 |
-+{ |
7766 |
-+ switch (pred_ptr->args.reftime.kind) |
7767 |
-+ { |
7768 |
-+ case COMP_GT: |
7769 |
-+ return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; |
7770 |
-+ |
7771 |
-+ case COMP_LT: |
7772 |
-+ return compare_ts(ts, pred_ptr->args.reftime.ts) < 0; |
7773 |
-+ |
7774 |
-+ case COMP_EQ: |
7775 |
-+ { |
7776 |
-+ double delta = ts_difference(ts, pred_ptr->args.reftime.ts); |
7777 |
-+ return (delta >= 0.0 && delta < window); |
7778 |
-+ } |
7779 |
-+ } |
7780 |
-+ assert (0); |
7781 |
-+ abort (); |
7782 |
-+} |
7783 |
-+ |
7784 |
-+ |
7785 |
-+boolean |
7786 |
-+pred_amin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7787 |
-+{ |
7788 |
-+ (void) &pathname; |
7789 |
-+ return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60); |
7790 |
-+} |
7791 |
-+ |
7792 |
-+boolean |
7793 |
-+pred_and (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7794 |
-+{ |
7795 |
-+ if (pred_ptr->pred_left == NULL |
7796 |
-+ || apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) |
7797 |
-+ { |
7798 |
-+ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
7799 |
-+ } |
7800 |
-+ else |
7801 |
-+ return false; |
7802 |
-+} |
7803 |
-+ |
7804 |
-+boolean |
7805 |
-+pred_anewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7806 |
-+{ |
7807 |
-+ (void) &pathname; |
7808 |
-+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
7809 |
-+ return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0; |
7810 |
-+} |
7811 |
-+ |
7812 |
-+boolean |
7813 |
-+pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7814 |
-+{ |
7815 |
-+ (void) &pathname; |
7816 |
-+ return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS); |
7817 |
-+} |
7818 |
-+ |
7819 |
-+boolean |
7820 |
-+pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7821 |
-+{ |
7822 |
-+ (void) &pathname; |
7823 |
-+ (void) &stat_buf; |
7824 |
-+ (void) &pred_ptr; |
7825 |
-+ |
7826 |
-+ return true; |
7827 |
-+} |
7828 |
-+ |
7829 |
-+boolean |
7830 |
-+pred_cmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7831 |
-+{ |
7832 |
-+ (void) pathname; |
7833 |
-+ return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60); |
7834 |
-+} |
7835 |
-+ |
7836 |
-+boolean |
7837 |
-+pred_cnewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7838 |
-+{ |
7839 |
-+ (void) pathname; |
7840 |
-+ |
7841 |
-+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
7842 |
-+ return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0; |
7843 |
-+} |
7844 |
-+ |
7845 |
-+boolean |
7846 |
-+pred_comma (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7847 |
-+{ |
7848 |
-+ if (pred_ptr->pred_left != NULL) |
7849 |
-+ { |
7850 |
-+ apply_predicate(pathname, stat_buf,pred_ptr->pred_left); |
7851 |
-+ } |
7852 |
-+ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
7853 |
-+} |
7854 |
-+ |
7855 |
-+boolean |
7856 |
-+pred_ctime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7857 |
-+{ |
7858 |
-+ (void) &pathname; |
7859 |
-+ return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS); |
7860 |
-+} |
7861 |
-+ |
7862 |
-+static boolean |
7863 |
-+perform_delete(int flags) |
7864 |
-+{ |
7865 |
-+ return 0 == unlinkat(state.cwd_dir_fd, state.rel_pathname, flags); |
7866 |
-+} |
7867 |
-+ |
7868 |
-+ |
7869 |
-+boolean |
7870 |
-+pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7871 |
-+{ |
7872 |
-+ (void) pred_ptr; |
7873 |
-+ (void) stat_buf; |
7874 |
-+ if (strcmp (state.rel_pathname, ".")) |
7875 |
-+ { |
7876 |
-+ int flags=0; |
7877 |
-+ if (state.have_stat && S_ISDIR(stat_buf->st_mode)) |
7878 |
-+ flags |= AT_REMOVEDIR; |
7879 |
-+ if (perform_delete(flags)) |
7880 |
-+ { |
7881 |
-+ return true; |
7882 |
-+ } |
7883 |
-+ else |
7884 |
-+ { |
7885 |
-+ if (EISDIR == errno) |
7886 |
-+ { |
7887 |
-+ if ((flags & AT_REMOVEDIR) == 0) |
7888 |
-+ { |
7889 |
-+ /* unlink() operation failed because we should have done rmdir(). */ |
7890 |
-+ flags |= AT_REMOVEDIR; |
7891 |
-+ if (perform_delete(flags)) |
7892 |
-+ return true; |
7893 |
-+ } |
7894 |
-+ } |
7895 |
-+ } |
7896 |
-+ error (0, errno, _("cannot delete %s" |
7897 |
-+ /* TRANSLATORS: the argument is either a |
7898 |
-+ * file or a directory, but we do not know which. |
7899 |
-+ * Mail bug-findutils@×××.org if you cannot correctly |
7900 |
-+ * translate the string without knowing. |
7901 |
-+ */), |
7902 |
-+ safely_quote_err_filename(0, pathname)); |
7903 |
-+ /* Previously I had believed that having the -delete action |
7904 |
-+ * return false provided the user with control over whether an |
7905 |
-+ * error message is issued. While this is true, the policy of |
7906 |
-+ * not affecting the exit status is contrary to the POSIX |
7907 |
-+ * requirement that diagnostic messages are accompanied by a |
7908 |
-+ * nonzero exit status. While -delete is not a POSIX option and |
7909 |
-+ * we can therefore opt not to follow POSIX in this case, that |
7910 |
-+ * seems somewhat arbitrary and confusing. So, as of |
7911 |
-+ * findutils-4.3.11, we also set the exit status in this case. |
7912 |
-+ */ |
7913 |
-+ state.exit_status = 1; |
7914 |
-+ return false; |
7915 |
-+ } |
7916 |
-+ else |
7917 |
-+ { |
7918 |
-+ /* nothing to do. */ |
7919 |
-+ return true; |
7920 |
-+ } |
7921 |
-+} |
7922 |
-+ |
7923 |
-+boolean |
7924 |
-+pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7925 |
-+{ |
7926 |
-+ (void) pathname; |
7927 |
-+ (void) pred_ptr; |
7928 |
-+ |
7929 |
-+ if (S_ISDIR (stat_buf->st_mode)) |
7930 |
-+ { |
7931 |
-+ int fd; |
7932 |
-+ DIR *d; |
7933 |
-+ struct dirent *dp; |
7934 |
-+ boolean empty = true; |
7935 |
-+ |
7936 |
-+ errno = 0; |
7937 |
-+ if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY |
7938 |
-+#if defined O_LARGEFILE |
7939 |
-+ |O_LARGEFILE |
7940 |
-+#endif |
7941 |
-+ )) < 0) |
7942 |
-+ { |
7943 |
-+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
7944 |
-+ state.exit_status = 1; |
7945 |
-+ return false; |
7946 |
-+ } |
7947 |
-+ d = fdopendir (fd); |
7948 |
-+ if (d == NULL) |
7949 |
-+ { |
7950 |
-+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
7951 |
-+ state.exit_status = 1; |
7952 |
-+ return false; |
7953 |
-+ } |
7954 |
-+ for (dp = readdir (d); dp; dp = readdir (d)) |
7955 |
-+ { |
7956 |
-+ if (dp->d_name[0] != '.' |
7957 |
-+ || (dp->d_name[1] != '\0' |
7958 |
-+ && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) |
7959 |
-+ { |
7960 |
-+ empty = false; |
7961 |
-+ break; |
7962 |
-+ } |
7963 |
-+ } |
7964 |
-+ if (CLOSEDIR (d)) |
7965 |
-+ { |
7966 |
-+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
7967 |
-+ state.exit_status = 1; |
7968 |
-+ return false; |
7969 |
-+ } |
7970 |
-+ return (empty); |
7971 |
-+ } |
7972 |
-+ else if (S_ISREG (stat_buf->st_mode)) |
7973 |
-+ return (stat_buf->st_size == 0); |
7974 |
-+ else |
7975 |
-+ return (false); |
7976 |
-+} |
7977 |
-+ |
7978 |
-+static boolean |
7979 |
-+new_impl_pred_exec (int dirfd, const char *pathname, |
7980 |
-+ struct stat *stat_buf, |
7981 |
-+ struct predicate *pred_ptr, |
7982 |
-+ const char *prefix, size_t pfxlen) |
7983 |
-+{ |
7984 |
-+ struct exec_val *execp = &pred_ptr->args.exec_vec; |
7985 |
-+ size_t len = strlen(pathname); |
7986 |
-+ |
7987 |
-+ (void) stat_buf; |
7988 |
-+ execp->dirfd = dirfd; |
7989 |
-+ if (execp->multiple) |
7990 |
-+ { |
7991 |
-+ /* Push the argument onto the current list. |
7992 |
-+ * The command may or may not be run at this point, |
7993 |
-+ * depending on the command line length limits. |
7994 |
-+ */ |
7995 |
-+ bc_push_arg(&execp->ctl, |
7996 |
-+ &execp->state, |
7997 |
-+ pathname, len+1, |
7998 |
-+ prefix, pfxlen, |
7999 |
-+ 0); |
8000 |
-+ |
8001 |
-+ /* remember that there are pending execdirs. */ |
8002 |
-+ state.execdirs_outstanding = true; |
8003 |
-+ |
8004 |
-+ /* POSIX: If the primary expression is punctuated by a plus |
8005 |
-+ * sign, the primary shall always evaluate as true |
8006 |
-+ */ |
8007 |
-+ return true; |
8008 |
-+ } |
8009 |
-+ else |
8010 |
-+ { |
8011 |
-+ int i; |
8012 |
-+ |
8013 |
-+ for (i=0; i<execp->num_args; ++i) |
8014 |
-+ { |
8015 |
-+ bc_do_insert(&execp->ctl, |
8016 |
-+ &execp->state, |
8017 |
-+ execp->replace_vec[i], |
8018 |
-+ strlen(execp->replace_vec[i]), |
8019 |
-+ prefix, pfxlen, |
8020 |
-+ pathname, len, |
8021 |
-+ 0); |
8022 |
-+ } |
8023 |
-+ |
8024 |
-+ /* Actually invoke the command. */ |
8025 |
-+ return execp->ctl.exec_callback(&execp->ctl, |
8026 |
-+ &execp->state); |
8027 |
-+ } |
8028 |
-+} |
8029 |
-+ |
8030 |
-+ |
8031 |
-+boolean |
8032 |
-+pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8033 |
-+{ |
8034 |
-+ return new_impl_pred_exec(get_start_dirfd(), |
8035 |
-+ pathname, stat_buf, pred_ptr, NULL, 0); |
8036 |
-+} |
8037 |
-+ |
8038 |
-+boolean |
8039 |
-+pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8040 |
-+{ |
8041 |
-+ const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; |
8042 |
-+ (void) &pathname; |
8043 |
-+ return new_impl_pred_exec (get_current_dirfd(), |
8044 |
-+ state.rel_pathname, stat_buf, pred_ptr, |
8045 |
-+ prefix, (prefix ? 2 : 0)); |
8046 |
-+} |
8047 |
-+ |
8048 |
-+boolean |
8049 |
-+pred_false (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8050 |
-+{ |
8051 |
-+ (void) &pathname; |
8052 |
-+ (void) &stat_buf; |
8053 |
-+ (void) &pred_ptr; |
8054 |
-+ |
8055 |
-+ |
8056 |
-+ return (false); |
8057 |
-+} |
8058 |
-+ |
8059 |
-+boolean |
8060 |
-+pred_fls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8061 |
-+{ |
8062 |
-+ FILE * stream = pred_ptr->args.printf_vec.stream; |
8063 |
-+ list_file (pathname, state.cwd_dir_fd, state.rel_pathname, stat_buf, |
8064 |
-+ options.start_time.tv_sec, |
8065 |
-+ options.output_block_size, |
8066 |
-+ pred_ptr->literal_control_chars, stream); |
8067 |
-+ return true; |
8068 |
-+} |
8069 |
-+ |
8070 |
-+boolean |
8071 |
-+pred_fprint (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8072 |
-+{ |
8073 |
-+ (void) &pathname; |
8074 |
-+ (void) &stat_buf; |
8075 |
-+ |
8076 |
-+ print_quoted(pred_ptr->args.printf_vec.stream, |
8077 |
-+ pred_ptr->args.printf_vec.quote_opts, |
8078 |
-+ pred_ptr->args.printf_vec.dest_is_tty, |
8079 |
-+ "%s\n", |
8080 |
-+ pathname); |
8081 |
-+ return true; |
8082 |
-+} |
8083 |
-+ |
8084 |
-+boolean |
8085 |
-+pred_fprint0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8086 |
-+{ |
8087 |
-+ FILE * fp = pred_ptr->args.printf_vec.stream; |
8088 |
-+ |
8089 |
-+ (void) &stat_buf; |
8090 |
-+ |
8091 |
-+ fputs (pathname, fp); |
8092 |
-+ putc (0, fp); |
8093 |
-+ return true; |
8094 |
-+} |
8095 |
-+ |
8096 |
-+ |
8097 |
-+ |
8098 |
-+static char* |
8099 |
-+mode_to_filetype(mode_t m) |
8100 |
-+{ |
8101 |
-+#define HANDLE_TYPE(t,letter) if (m==t) { return letter; } |
8102 |
-+#ifdef S_IFREG |
8103 |
-+ HANDLE_TYPE(S_IFREG, "f"); /* regular file */ |
8104 |
-+#endif |
8105 |
-+#ifdef S_IFDIR |
8106 |
-+ HANDLE_TYPE(S_IFDIR, "d"); /* directory */ |
8107 |
-+#endif |
8108 |
-+#ifdef S_IFLNK |
8109 |
-+ HANDLE_TYPE(S_IFLNK, "l"); /* symbolic link */ |
8110 |
-+#endif |
8111 |
-+#ifdef S_IFSOCK |
8112 |
-+ HANDLE_TYPE(S_IFSOCK, "s"); /* Unix domain socket */ |
8113 |
-+#endif |
8114 |
-+#ifdef S_IFBLK |
8115 |
-+ HANDLE_TYPE(S_IFBLK, "b"); /* block device */ |
8116 |
-+#endif |
8117 |
-+#ifdef S_IFCHR |
8118 |
-+ HANDLE_TYPE(S_IFCHR, "c"); /* character device */ |
8119 |
-+#endif |
8120 |
-+#ifdef S_IFIFO |
8121 |
-+ HANDLE_TYPE(S_IFIFO, "p"); /* FIFO */ |
8122 |
-+#endif |
8123 |
-+#ifdef S_IFDOOR |
8124 |
-+ HANDLE_TYPE(S_IFDOOR, "D"); /* Door (e.g. on Solaris) */ |
8125 |
-+#endif |
8126 |
-+ return "U"; /* Unknown */ |
8127 |
-+} |
8128 |
-+ |
8129 |
-+static double |
8130 |
-+file_sparseness(const struct stat *p) |
8131 |
-+{ |
8132 |
-+#if defined HAVE_STRUCT_STAT_ST_BLOCKS |
8133 |
-+ if (0 == p->st_size) |
8134 |
-+ { |
8135 |
-+ if (0 == p->st_blocks) |
8136 |
-+ return 1.0; |
8137 |
-+ else |
8138 |
-+ return p->st_blocks < 0 ? -HUGE_VAL : HUGE_VAL; |
8139 |
-+ } |
8140 |
-+ else |
8141 |
-+ { |
8142 |
-+ double blklen = file_blocksize(p) * (double)p->st_blocks; |
8143 |
-+ return blklen / p->st_size; |
8144 |
-+ } |
8145 |
-+#else |
8146 |
-+ return 1.0; |
8147 |
-+#endif |
8148 |
-+} |
8149 |
-+ |
8150 |
-+ |
8151 |
-+ |
8152 |
-+static void |
8153 |
-+checked_fprintf(struct format_val *dest, const char *fmt, ...) |
8154 |
-+{ |
8155 |
-+ int rv; |
8156 |
-+ va_list ap; |
8157 |
-+ |
8158 |
-+ va_start(ap, fmt); |
8159 |
-+ rv = vfprintf(dest->stream, fmt, ap); |
8160 |
-+ if (rv < 0) |
8161 |
-+ nonfatal_file_error(dest->filename); |
8162 |
-+} |
8163 |
-+ |
8164 |
-+ |
8165 |
-+static void |
8166 |
-+checked_print_quoted (struct format_val *dest, |
8167 |
-+ const char *format, const char *s) |
8168 |
-+{ |
8169 |
-+ int rv = print_quoted(dest->stream, dest->quote_opts, dest->dest_is_tty, |
8170 |
-+ format, s); |
8171 |
-+ if (rv < 0) |
8172 |
-+ nonfatal_file_error(dest->filename); |
8173 |
-+} |
8174 |
-+ |
8175 |
-+ |
8176 |
-+static void |
8177 |
-+checked_fwrite(void *p, size_t siz, size_t nmemb, struct format_val *dest) |
8178 |
-+{ |
8179 |
-+ int items_written = fwrite(p, siz, nmemb, dest->stream); |
8180 |
-+ if (items_written < nmemb) |
8181 |
-+ nonfatal_file_error(dest->filename); |
8182 |
-+} |
8183 |
-+ |
8184 |
-+static void |
8185 |
-+checked_fflush(struct format_val *dest) |
8186 |
-+{ |
8187 |
-+ if (0 != fflush(dest->stream)) |
8188 |
-+ { |
8189 |
-+ nonfatal_file_error(dest->filename); |
8190 |
-+ } |
8191 |
-+} |
8192 |
-+ |
8193 |
-+static void |
8194 |
-+do_fprintf(struct format_val *dest, |
8195 |
-+ struct segment *segment, |
8196 |
-+ const char *pathname, |
8197 |
-+ const struct stat *stat_buf) |
8198 |
-+{ |
8199 |
-+ char hbuf[LONGEST_HUMAN_READABLE + 1]; |
8200 |
-+ const char *cp; |
8201 |
-+ |
8202 |
-+ switch (segment->segkind) |
8203 |
-+ { |
8204 |
-+ case KIND_PLAIN: /* Plain text string (no % conversion). */ |
8205 |
-+ /* trusted */ |
8206 |
-+ checked_fwrite(segment->text, 1, segment->text_len, dest); |
8207 |
-+ break; |
8208 |
-+ |
8209 |
-+ case KIND_STOP: /* Terminate argument and flush output. */ |
8210 |
-+ /* trusted */ |
8211 |
-+ checked_fwrite(segment->text, 1, segment->text_len, dest); |
8212 |
-+ checked_fflush(dest); |
8213 |
-+ break; |
8214 |
-+ |
8215 |
-+ case KIND_FORMAT: |
8216 |
-+ switch (segment->format_char[0]) |
8217 |
-+ { |
8218 |
-+ case 'a': /* atime in `ctime' format. */ |
8219 |
-+ /* UNTRUSTED, probably unexploitable */ |
8220 |
-+ checked_fprintf (dest, segment->text, ctime_format (get_stat_atime(stat_buf))); |
8221 |
-+ break; |
8222 |
-+ case 'b': /* size in 512-byte blocks */ |
8223 |
-+ /* UNTRUSTED, probably unexploitable */ |
8224 |
-+ checked_fprintf (dest, segment->text, |
8225 |
-+ human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), |
8226 |
-+ hbuf, human_ceiling, |
8227 |
-+ ST_NBLOCKSIZE, 512)); |
8228 |
-+ break; |
8229 |
-+ case 'c': /* ctime in `ctime' format */ |
8230 |
-+ /* UNTRUSTED, probably unexploitable */ |
8231 |
-+ checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime(stat_buf))); |
8232 |
-+ break; |
8233 |
-+ case 'd': /* depth in search tree */ |
8234 |
-+ /* UNTRUSTED, probably unexploitable */ |
8235 |
-+ checked_fprintf (dest, segment->text, state.curdepth); |
8236 |
-+ break; |
8237 |
-+ case 'D': /* Device on which file exists (stat.st_dev) */ |
8238 |
-+ /* trusted */ |
8239 |
-+ checked_fprintf (dest, segment->text, |
8240 |
-+ human_readable ((uintmax_t) stat_buf->st_dev, hbuf, |
8241 |
-+ human_ceiling, 1, 1)); |
8242 |
-+ break; |
8243 |
-+ case 'f': /* base name of path */ |
8244 |
-+ /* sanitised */ |
8245 |
-+ { |
8246 |
-+ char *base = base_name (pathname); |
8247 |
-+ checked_print_quoted (dest, segment->text, base); |
8248 |
-+ free (base); |
8249 |
-+ } |
8250 |
-+ break; |
8251 |
-+ case 'F': /* file system type */ |
8252 |
-+ /* trusted */ |
8253 |
-+ checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname)); |
8254 |
-+ break; |
8255 |
-+ case 'g': /* group name */ |
8256 |
-+ /* trusted */ |
8257 |
-+ /* (well, the actual group is selected by the user but |
8258 |
-+ * its name was selected by the system administrator) |
8259 |
-+ */ |
8260 |
-+ { |
8261 |
-+ struct group *g; |
8262 |
-+ |
8263 |
-+ g = getgrgid (stat_buf->st_gid); |
8264 |
-+ if (g) |
8265 |
-+ { |
8266 |
-+ segment->text[segment->text_len] = 's'; |
8267 |
-+ checked_fprintf (dest, segment->text, g->gr_name); |
8268 |
-+ break; |
8269 |
-+ } |
8270 |
-+ else |
8271 |
-+ { |
8272 |
-+ /* Do nothing. */ |
8273 |
-+ /*FALLTHROUGH*/ |
8274 |
-+ } |
8275 |
-+ } |
8276 |
-+ /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/ |
8277 |
-+ |
8278 |
-+ case 'G': /* GID number */ |
8279 |
-+ /* UNTRUSTED, probably unexploitable */ |
8280 |
-+ checked_fprintf (dest, segment->text, |
8281 |
-+ human_readable ((uintmax_t) stat_buf->st_gid, hbuf, |
8282 |
-+ human_ceiling, 1, 1)); |
8283 |
-+ break; |
8284 |
-+ case 'h': /* leading directories part of path */ |
8285 |
-+ /* sanitised */ |
8286 |
-+ { |
8287 |
-+ cp = strrchr (pathname, '/'); |
8288 |
-+ if (cp == NULL) /* No leading directories. */ |
8289 |
-+ { |
8290 |
-+ /* If there is no slash in the pathname, we still |
8291 |
-+ * print the string because it contains characters |
8292 |
-+ * other than just '%s'. The %h expands to ".". |
8293 |
-+ */ |
8294 |
-+ checked_print_quoted (dest, segment->text, "."); |
8295 |
-+ } |
8296 |
-+ else |
8297 |
-+ { |
8298 |
-+ char *s = strdup(pathname); |
8299 |
-+ s[cp - pathname] = 0; |
8300 |
-+ checked_print_quoted (dest, segment->text, s); |
8301 |
-+ free(s); |
8302 |
-+ } |
8303 |
-+ } |
8304 |
-+ break; |
8305 |
-+ |
8306 |
-+ case 'H': /* ARGV element file was found under */ |
8307 |
-+ /* trusted */ |
8308 |
-+ { |
8309 |
-+ char *s = xmalloc(state.starting_path_length+1); |
8310 |
-+ memcpy(s, pathname, state.starting_path_length); |
8311 |
-+ s[state.starting_path_length] = 0; |
8312 |
-+ checked_fprintf (dest, segment->text, s); |
8313 |
-+ free(s); |
8314 |
-+ } |
8315 |
-+ break; |
8316 |
-+ |
8317 |
-+ case 'i': /* inode number */ |
8318 |
-+ /* UNTRUSTED, but not exploitable I think */ |
8319 |
-+ checked_fprintf (dest, segment->text, |
8320 |
-+ human_readable ((uintmax_t) stat_buf->st_ino, hbuf, |
8321 |
-+ human_ceiling, |
8322 |
-+ 1, 1)); |
8323 |
-+ break; |
8324 |
-+ case 'k': /* size in 1K blocks */ |
8325 |
-+ /* UNTRUSTED, but not exploitable I think */ |
8326 |
-+ checked_fprintf (dest, segment->text, |
8327 |
-+ human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), |
8328 |
-+ hbuf, human_ceiling, |
8329 |
-+ ST_NBLOCKSIZE, 1024)); |
8330 |
-+ break; |
8331 |
-+ case 'l': /* object of symlink */ |
8332 |
-+ /* sanitised */ |
8333 |
-+#ifdef S_ISLNK |
8334 |
-+ { |
8335 |
-+ char *linkname = 0; |
8336 |
-+ |
8337 |
-+ if (S_ISLNK (stat_buf->st_mode)) |
8338 |
-+ { |
8339 |
-+ linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); |
8340 |
-+ if (linkname == 0) |
8341 |
-+ state.exit_status = 1; |
8342 |
-+ } |
8343 |
-+ if (linkname) |
8344 |
-+ { |
8345 |
-+ checked_print_quoted (dest, segment->text, linkname); |
8346 |
-+ free (linkname); |
8347 |
-+ } |
8348 |
-+ else |
8349 |
-+ { |
8350 |
-+ /* We still need to honour the field width etc., so this is |
8351 |
-+ * not a no-op. |
8352 |
-+ */ |
8353 |
-+ checked_print_quoted (dest, segment->text, ""); |
8354 |
-+ } |
8355 |
-+ } |
8356 |
-+#endif /* S_ISLNK */ |
8357 |
-+ break; |
8358 |
-+ |
8359 |
-+ case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */ |
8360 |
-+ /* UNTRUSTED, probably unexploitable */ |
8361 |
-+ { |
8362 |
-+ char modestring[16] ; |
8363 |
-+ filemodestring (stat_buf, modestring); |
8364 |
-+ modestring[10] = '\0'; |
8365 |
-+ checked_fprintf (dest, segment->text, modestring); |
8366 |
-+ } |
8367 |
-+ break; |
8368 |
-+ |
8369 |
-+ case 'm': /* mode as octal number (perms only) */ |
8370 |
-+ /* UNTRUSTED, probably unexploitable */ |
8371 |
-+ { |
8372 |
-+ /* Output the mode portably using the traditional numbers, |
8373 |
-+ even if the host unwisely uses some other numbering |
8374 |
-+ scheme. But help the compiler in the common case where |
8375 |
-+ the host uses the traditional numbering scheme. */ |
8376 |
-+ mode_t m = stat_buf->st_mode; |
8377 |
-+ boolean traditional_numbering_scheme = |
8378 |
-+ (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000 |
8379 |
-+ && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100 |
8380 |
-+ && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010 |
8381 |
-+ && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001); |
8382 |
-+ checked_fprintf (dest, segment->text, |
8383 |
-+ (traditional_numbering_scheme |
8384 |
-+ ? m & MODE_ALL |
8385 |
-+ : ((m & S_ISUID ? 04000 : 0) |
8386 |
-+ | (m & S_ISGID ? 02000 : 0) |
8387 |
-+ | (m & S_ISVTX ? 01000 : 0) |
8388 |
-+ | (m & S_IRUSR ? 00400 : 0) |
8389 |
-+ | (m & S_IWUSR ? 00200 : 0) |
8390 |
-+ | (m & S_IXUSR ? 00100 : 0) |
8391 |
-+ | (m & S_IRGRP ? 00040 : 0) |
8392 |
-+ | (m & S_IWGRP ? 00020 : 0) |
8393 |
-+ | (m & S_IXGRP ? 00010 : 0) |
8394 |
-+ | (m & S_IROTH ? 00004 : 0) |
8395 |
-+ | (m & S_IWOTH ? 00002 : 0) |
8396 |
-+ | (m & S_IXOTH ? 00001 : 0)))); |
8397 |
-+ } |
8398 |
-+ break; |
8399 |
-+ |
8400 |
-+ case 'n': /* number of links */ |
8401 |
-+ /* UNTRUSTED, probably unexploitable */ |
8402 |
-+ checked_fprintf (dest, segment->text, |
8403 |
-+ human_readable ((uintmax_t) stat_buf->st_nlink, |
8404 |
-+ hbuf, |
8405 |
-+ human_ceiling, |
8406 |
-+ 1, 1)); |
8407 |
-+ break; |
8408 |
-+ |
8409 |
-+ case 'p': /* pathname */ |
8410 |
-+ /* sanitised */ |
8411 |
-+ checked_print_quoted (dest, segment->text, pathname); |
8412 |
-+ break; |
8413 |
-+ |
8414 |
-+ case 'P': /* pathname with ARGV element stripped */ |
8415 |
-+ /* sanitised */ |
8416 |
-+ if (state.curdepth > 0) |
8417 |
-+ { |
8418 |
-+ cp = pathname + state.starting_path_length; |
8419 |
-+ if (*cp == '/') |
8420 |
-+ /* Move past the slash between the ARGV element |
8421 |
-+ and the rest of the pathname. But if the ARGV element |
8422 |
-+ ends in a slash, we didn't add another, so we've |
8423 |
-+ already skipped past it. */ |
8424 |
-+ cp++; |
8425 |
-+ } |
8426 |
-+ else |
8427 |
-+ { |
8428 |
-+ cp = ""; |
8429 |
-+ } |
8430 |
-+ checked_print_quoted (dest, segment->text, cp); |
8431 |
-+ break; |
8432 |
-+ |
8433 |
-+ case 's': /* size in bytes */ |
8434 |
-+ /* UNTRUSTED, probably unexploitable */ |
8435 |
-+ checked_fprintf (dest, segment->text, |
8436 |
-+ human_readable ((uintmax_t) stat_buf->st_size, |
8437 |
-+ hbuf, human_ceiling, 1, 1)); |
8438 |
-+ break; |
8439 |
-+ |
8440 |
-+ case 'S': /* sparseness */ |
8441 |
-+ /* UNTRUSTED, probably unexploitable */ |
8442 |
-+ checked_fprintf (dest, segment->text, file_sparseness(stat_buf));; |
8443 |
-+ break; |
8444 |
-+ |
8445 |
-+ case 't': /* mtime in `ctime' format */ |
8446 |
-+ /* UNTRUSTED, probably unexploitable */ |
8447 |
-+ checked_fprintf (dest, segment->text, |
8448 |
-+ ctime_format (get_stat_mtime(stat_buf))); |
8449 |
-+ break; |
8450 |
-+ |
8451 |
-+ case 'u': /* user name */ |
8452 |
-+ /* trusted */ |
8453 |
-+ /* (well, the actual user is selected by the user on systems |
8454 |
-+ * where chown is not restricted, but the user name was |
8455 |
-+ * selected by the system administrator) |
8456 |
-+ */ |
8457 |
-+ { |
8458 |
-+ struct passwd *p; |
8459 |
-+ |
8460 |
-+ p = getpwuid (stat_buf->st_uid); |
8461 |
-+ if (p) |
8462 |
-+ { |
8463 |
-+ segment->text[segment->text_len] = 's'; |
8464 |
-+ checked_fprintf (dest, segment->text, p->pw_name); |
8465 |
-+ break; |
8466 |
-+ } |
8467 |
-+ /* else fallthru */ |
8468 |
-+ } |
8469 |
-+ /* FALLTHROUGH*/ /* .. to case U */ |
8470 |
-+ |
8471 |
-+ case 'U': /* UID number */ |
8472 |
-+ /* UNTRUSTED, probably unexploitable */ |
8473 |
-+ checked_fprintf (dest, segment->text, |
8474 |
-+ human_readable ((uintmax_t) stat_buf->st_uid, hbuf, |
8475 |
-+ human_ceiling, 1, 1)); |
8476 |
-+ break; |
8477 |
-+ |
8478 |
-+ /* %Y: type of file system entry like `ls -l`: |
8479 |
-+ * (d,-,l,s,p,b,c,n) n=nonexistent(symlink) |
8480 |
-+ */ |
8481 |
-+ case 'Y': /* in case of symlink */ |
8482 |
-+ /* trusted */ |
8483 |
-+ { |
8484 |
-+#ifdef S_ISLNK |
8485 |
-+ if (S_ISLNK (stat_buf->st_mode)) |
8486 |
-+ { |
8487 |
-+ struct stat sbuf; |
8488 |
-+ /* If we would normally follow links, do not do so. |
8489 |
-+ * If we would normally not follow links, do so. |
8490 |
-+ */ |
8491 |
-+ if ((following_links() ? lstat : stat) |
8492 |
-+ (state.rel_pathname, &sbuf) != 0) |
8493 |
-+ { |
8494 |
-+ if ( errno == ENOENT ) |
8495 |
-+ { |
8496 |
-+ checked_fprintf (dest, segment->text, "N"); |
8497 |
-+ break; |
8498 |
-+ } |
8499 |
-+ else if ( errno == ELOOP ) |
8500 |
-+ { |
8501 |
-+ checked_fprintf (dest, segment->text, "L"); |
8502 |
-+ break; |
8503 |
-+ } |
8504 |
-+ else |
8505 |
-+ { |
8506 |
-+ checked_fprintf (dest, segment->text, "?"); |
8507 |
-+ error (0, errno, "%s", |
8508 |
-+ safely_quote_err_filename(0, pathname)); |
8509 |
-+ /* exit_status = 1; |
8510 |
-+ return ; */ |
8511 |
-+ break; |
8512 |
-+ } |
8513 |
-+ } |
8514 |
-+ checked_fprintf (dest, segment->text, |
8515 |
-+ mode_to_filetype(sbuf.st_mode & S_IFMT)); |
8516 |
-+ } |
8517 |
-+#endif /* S_ISLNK */ |
8518 |
-+ else |
8519 |
-+ { |
8520 |
-+ checked_fprintf (dest, segment->text, |
8521 |
-+ mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
8522 |
-+ } |
8523 |
-+ } |
8524 |
-+ break; |
8525 |
-+ |
8526 |
-+ case 'y': |
8527 |
-+ /* trusted */ |
8528 |
-+ { |
8529 |
-+ checked_fprintf (dest, segment->text, |
8530 |
-+ mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
8531 |
-+ } |
8532 |
-+ break; |
8533 |
-+ } |
8534 |
-+ /* end of KIND_FORMAT case */ |
8535 |
-+ break; |
8536 |
-+ } |
8537 |
-+} |
8538 |
-+ |
8539 |
-+boolean |
8540 |
-+pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8541 |
-+{ |
8542 |
-+ struct format_val *dest = &pred_ptr->args.printf_vec; |
8543 |
-+ struct segment *segment; |
8544 |
-+ |
8545 |
-+ for (segment = dest->segment; segment; segment = segment->next) |
8546 |
-+ { |
8547 |
-+ if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */ |
8548 |
-+ { |
8549 |
-+ struct timespec ts; |
8550 |
-+ int valid = 0; |
8551 |
-+ |
8552 |
-+ switch (segment->format_char[0]) |
8553 |
-+ { |
8554 |
-+ case 'A': |
8555 |
-+ ts = get_stat_atime(stat_buf); |
8556 |
-+ valid = 1; |
8557 |
-+ break; |
8558 |
-+ case 'B': |
8559 |
-+ ts = get_stat_birthtime(stat_buf); |
8560 |
-+ if ('@' == segment->format_char[1]) |
8561 |
-+ valid = 1; |
8562 |
-+ else |
8563 |
-+ valid = (ts.tv_nsec >= 0); |
8564 |
-+ break; |
8565 |
-+ case 'C': |
8566 |
-+ ts = get_stat_ctime(stat_buf); |
8567 |
-+ valid = 1; |
8568 |
-+ break; |
8569 |
-+ case 'T': |
8570 |
-+ ts = get_stat_mtime(stat_buf); |
8571 |
-+ valid = 1; |
8572 |
-+ break; |
8573 |
-+ default: |
8574 |
-+ assert (0); |
8575 |
-+ abort (); |
8576 |
-+ } |
8577 |
-+ /* We trust the output of format_date not to contain |
8578 |
-+ * nasty characters, though the value of the date |
8579 |
-+ * is itself untrusted data. |
8580 |
-+ */ |
8581 |
-+ if (valid) |
8582 |
-+ { |
8583 |
-+ /* trusted */ |
8584 |
-+ checked_fprintf (dest, segment->text, |
8585 |
-+ format_date (ts, segment->format_char[1])); |
8586 |
-+ } |
8587 |
-+ else |
8588 |
-+ { |
8589 |
-+ /* The specified timestamp is not available, output |
8590 |
-+ * nothing for the timestamp, but use the rest (so that |
8591 |
-+ * for example find foo -printf '[%Bs] %p\n' can print |
8592 |
-+ * "[] foo"). |
8593 |
-+ */ |
8594 |
-+ /* trusted */ |
8595 |
-+ checked_fprintf (dest, segment->text, ""); |
8596 |
-+ } |
8597 |
-+ } |
8598 |
-+ else |
8599 |
-+ { |
8600 |
-+ /* Print a segment which is not a date. */ |
8601 |
-+ do_fprintf(dest, segment, pathname, stat_buf); |
8602 |
-+ } |
8603 |
-+ } |
8604 |
-+ return true; |
8605 |
-+} |
8606 |
-+ |
8607 |
-+boolean |
8608 |
-+pred_fstype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8609 |
-+{ |
8610 |
-+ (void) pathname; |
8611 |
-+ |
8612 |
-+ if (strcmp (filesystem_type (stat_buf, pathname), pred_ptr->args.str) == 0) |
8613 |
-+ return true; |
8614 |
-+ else |
8615 |
-+ return false; |
8616 |
-+} |
8617 |
-+ |
8618 |
-+boolean |
8619 |
-+pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8620 |
-+{ |
8621 |
-+ (void) pathname; |
8622 |
-+ |
8623 |
-+ switch (pred_ptr->args.numinfo.kind) |
8624 |
-+ { |
8625 |
-+ case COMP_GT: |
8626 |
-+ if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val) |
8627 |
-+ return (true); |
8628 |
-+ break; |
8629 |
-+ case COMP_LT: |
8630 |
-+ if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val) |
8631 |
-+ return (true); |
8632 |
-+ break; |
8633 |
-+ case COMP_EQ: |
8634 |
-+ if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val) |
8635 |
-+ return (true); |
8636 |
-+ break; |
8637 |
-+ } |
8638 |
-+ return (false); |
8639 |
-+} |
8640 |
-+ |
8641 |
-+boolean |
8642 |
-+pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8643 |
-+{ |
8644 |
-+ (void) pathname; |
8645 |
-+ |
8646 |
-+ if (pred_ptr->args.gid == stat_buf->st_gid) |
8647 |
-+ return (true); |
8648 |
-+ else |
8649 |
-+ return (false); |
8650 |
-+} |
8651 |
-+ |
8652 |
-+boolean |
8653 |
-+pred_ilname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8654 |
-+{ |
8655 |
-+ return match_lname (pathname, stat_buf, pred_ptr, true); |
8656 |
-+} |
8657 |
-+ |
8658 |
-+/* Common code between -name, -iname. PATHNAME is being visited, STR |
8659 |
-+ is name to compare basename against, and FLAGS are passed to |
8660 |
-+ fnmatch. Recall that 'find / -name /' is one of the few times where a '/' |
8661 |
-+ in the -name must actually find something. */ |
8662 |
-+static boolean |
8663 |
-+pred_name_common (const char *pathname, const char *str, int flags) |
8664 |
-+{ |
8665 |
-+ char *p; |
8666 |
-+ boolean b; |
8667 |
-+ /* We used to use last_component() here, but that would not allow us to modify the |
8668 |
-+ * input string, which is const. We could optimise by duplicating the string only |
8669 |
-+ * if we need to modify it, and I'll do that if there is a measurable |
8670 |
-+ * performance difference on a machine built after 1990... |
8671 |
-+ */ |
8672 |
-+ char *base = base_name (pathname); |
8673 |
-+ /* remove trailing slashes, but leave "/" or "//foo" unchanged. */ |
8674 |
-+ strip_trailing_slashes(base); |
8675 |
-+ |
8676 |
-+ /* FNM_PERIOD is not used here because POSIX requires that it not be. |
8677 |
-+ * See http://standards.ieee.org/reading/ieee/interp/1003-2-92_int/pasc-1003.2-126.html |
8678 |
-+ */ |
8679 |
-+ b = fnmatch (str, base, flags) == 0; |
8680 |
-+ free (base); |
8681 |
-+ return b; |
8682 |
-+} |
8683 |
-+ |
8684 |
-+boolean |
8685 |
-+pred_iname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8686 |
-+{ |
8687 |
-+ (void) stat_buf; |
8688 |
-+ return pred_name_common (pathname, pred_ptr->args.str, FNM_CASEFOLD); |
8689 |
-+} |
8690 |
-+ |
8691 |
-+boolean |
8692 |
-+pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8693 |
-+{ |
8694 |
-+ (void) pathname; |
8695 |
-+ |
8696 |
-+ switch (pred_ptr->args.numinfo.kind) |
8697 |
-+ { |
8698 |
-+ case COMP_GT: |
8699 |
-+ if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val) |
8700 |
-+ return (true); |
8701 |
-+ break; |
8702 |
-+ case COMP_LT: |
8703 |
-+ if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val) |
8704 |
-+ return (true); |
8705 |
-+ break; |
8706 |
-+ case COMP_EQ: |
8707 |
-+ if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val) |
8708 |
-+ return (true); |
8709 |
-+ break; |
8710 |
-+ } |
8711 |
-+ return (false); |
8712 |
-+} |
8713 |
-+ |
8714 |
-+boolean |
8715 |
-+pred_ipath (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8716 |
-+{ |
8717 |
-+ (void) stat_buf; |
8718 |
-+ |
8719 |
-+ if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0) |
8720 |
-+ return (true); |
8721 |
-+ return (false); |
8722 |
-+} |
8723 |
-+ |
8724 |
-+boolean |
8725 |
-+pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8726 |
-+{ |
8727 |
-+ (void) pathname; |
8728 |
-+ |
8729 |
-+ switch (pred_ptr->args.numinfo.kind) |
8730 |
-+ { |
8731 |
-+ case COMP_GT: |
8732 |
-+ if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val) |
8733 |
-+ return (true); |
8734 |
-+ break; |
8735 |
-+ case COMP_LT: |
8736 |
-+ if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val) |
8737 |
-+ return (true); |
8738 |
-+ break; |
8739 |
-+ case COMP_EQ: |
8740 |
-+ if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val) |
8741 |
-+ return (true); |
8742 |
-+ break; |
8743 |
-+ } |
8744 |
-+ return (false); |
8745 |
-+} |
8746 |
-+ |
8747 |
-+boolean |
8748 |
-+pred_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8749 |
-+{ |
8750 |
-+ return match_lname (pathname, stat_buf, pred_ptr, false); |
8751 |
-+} |
8752 |
-+ |
8753 |
-+static boolean |
8754 |
-+match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case) |
8755 |
-+{ |
8756 |
-+ boolean ret = false; |
8757 |
-+#ifdef S_ISLNK |
8758 |
-+ if (S_ISLNK (stat_buf->st_mode)) |
8759 |
-+ { |
8760 |
-+ char *linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); |
8761 |
-+ if (linkname) |
8762 |
-+ { |
8763 |
-+ if (fnmatch (pred_ptr->args.str, linkname, |
8764 |
-+ ignore_case ? FNM_CASEFOLD : 0) == 0) |
8765 |
-+ ret = true; |
8766 |
-+ free (linkname); |
8767 |
-+ } |
8768 |
-+ } |
8769 |
-+#endif /* S_ISLNK */ |
8770 |
-+ return ret; |
8771 |
-+} |
8772 |
-+ |
8773 |
-+boolean |
8774 |
-+pred_ls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8775 |
-+{ |
8776 |
-+ return pred_fls(pathname, stat_buf, pred_ptr); |
8777 |
-+} |
8778 |
-+ |
8779 |
-+boolean |
8780 |
-+pred_mmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8781 |
-+{ |
8782 |
-+ (void) &pathname; |
8783 |
-+ return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60); |
8784 |
-+} |
8785 |
-+ |
8786 |
-+boolean |
8787 |
-+pred_mtime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8788 |
-+{ |
8789 |
-+ (void) pathname; |
8790 |
-+ return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS); |
8791 |
-+} |
8792 |
-+ |
8793 |
-+boolean |
8794 |
-+pred_name (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8795 |
-+{ |
8796 |
-+ (void) stat_buf; |
8797 |
-+ return pred_name_common (pathname, pred_ptr->args.str, 0); |
8798 |
-+} |
8799 |
-+ |
8800 |
-+boolean |
8801 |
-+pred_negate (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8802 |
-+{ |
8803 |
-+ return !apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
8804 |
-+} |
8805 |
-+ |
8806 |
-+boolean |
8807 |
-+pred_newer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8808 |
-+{ |
8809 |
-+ (void) pathname; |
8810 |
-+ |
8811 |
-+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
8812 |
-+ return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0; |
8813 |
-+} |
8814 |
-+ |
8815 |
-+boolean |
8816 |
-+pred_newerXY (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8817 |
-+{ |
8818 |
-+ struct timespec ts; |
8819 |
-+ boolean collected = false; |
8820 |
-+ |
8821 |
-+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
8822 |
-+ |
8823 |
-+ switch (pred_ptr->args.reftime.xval) |
8824 |
-+ { |
8825 |
-+ case XVAL_TIME: |
8826 |
-+ assert (pred_ptr->args.reftime.xval != XVAL_TIME); |
8827 |
-+ return false; |
8828 |
-+ |
8829 |
-+ case XVAL_ATIME: |
8830 |
-+ ts = get_stat_atime(stat_buf); |
8831 |
-+ collected = true; |
8832 |
-+ break; |
8833 |
-+ |
8834 |
-+ case XVAL_BIRTHTIME: |
8835 |
-+ ts = get_stat_birthtime(stat_buf); |
8836 |
-+ collected = true; |
8837 |
-+ if (ts.tv_nsec < 0); |
8838 |
-+ { |
8839 |
-+ /* XXX: Cannot determine birth time. Warn once. */ |
8840 |
-+ error(0, 0, _("Warning: cannot determine birth time of file %s"), |
8841 |
-+ safely_quote_err_filename(0, pathname)); |
8842 |
-+ return false; |
8843 |
-+ } |
8844 |
-+ break; |
8845 |
-+ |
8846 |
-+ case XVAL_CTIME: |
8847 |
-+ ts = get_stat_ctime(stat_buf); |
8848 |
-+ collected = true; |
8849 |
-+ break; |
8850 |
-+ |
8851 |
-+ case XVAL_MTIME: |
8852 |
-+ ts = get_stat_mtime(stat_buf); |
8853 |
-+ collected = true; |
8854 |
-+ break; |
8855 |
-+ } |
8856 |
-+ |
8857 |
-+ assert (collected); |
8858 |
-+ return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; |
8859 |
-+} |
8860 |
-+ |
8861 |
-+boolean |
8862 |
-+pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8863 |
-+{ |
8864 |
-+ (void) pathname; |
8865 |
-+ (void) pred_ptr; |
8866 |
-+ |
8867 |
-+#ifdef CACHE_IDS |
8868 |
-+ extern char *gid_unused; |
8869 |
-+ |
8870 |
-+ return gid_unused[(unsigned) stat_buf->st_gid]; |
8871 |
-+#else |
8872 |
-+ return getgrgid (stat_buf->st_gid) == NULL; |
8873 |
-+#endif |
8874 |
-+} |
8875 |
-+ |
8876 |
-+boolean |
8877 |
-+pred_nouser (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8878 |
-+{ |
8879 |
-+#ifdef CACHE_IDS |
8880 |
-+ extern char *uid_unused; |
8881 |
-+#endif |
8882 |
-+ |
8883 |
-+ (void) pathname; |
8884 |
-+ (void) pred_ptr; |
8885 |
-+ |
8886 |
-+#ifdef CACHE_IDS |
8887 |
-+ return uid_unused[(unsigned) stat_buf->st_uid]; |
8888 |
-+#else |
8889 |
-+ return getpwuid (stat_buf->st_uid) == NULL; |
8890 |
-+#endif |
8891 |
-+} |
8892 |
-+ |
8893 |
-+ |
8894 |
-+static boolean |
8895 |
-+is_ok(const char *program, const char *arg) |
8896 |
-+{ |
8897 |
-+ fflush (stdout); |
8898 |
-+ /* The draft open standard requires that, in the POSIX locale, |
8899 |
-+ the last non-blank character of this prompt be '?'. |
8900 |
-+ The exact format is not specified. |
8901 |
-+ This standard does not have requirements for locales other than POSIX |
8902 |
-+ */ |
8903 |
-+ /* XXX: printing UNTRUSTED data here. */ |
8904 |
-+ fprintf (stderr, _("< %s ... %s > ? " |
8905 |
-+ /* TRANSLATORS: we would like, if possible, the final non-blank |
8906 |
-+ * character of this string to be '?', but it is not |
8907 |
-+ * wholly essential. */ |
8908 |
-+ ), program, arg); |
8909 |
-+ fflush (stderr); |
8910 |
-+ return yesno(); |
8911 |
-+} |
8912 |
-+ |
8913 |
-+boolean |
8914 |
-+pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8915 |
-+{ |
8916 |
-+ if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) |
8917 |
-+ return new_impl_pred_exec (get_start_dirfd(), |
8918 |
-+ pathname, stat_buf, pred_ptr, NULL, 0); |
8919 |
-+ else |
8920 |
-+ return false; |
8921 |
-+} |
8922 |
-+ |
8923 |
-+boolean |
8924 |
-+pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8925 |
-+{ |
8926 |
-+ const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; |
8927 |
-+ if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) |
8928 |
-+ return new_impl_pred_exec (get_current_dirfd(), |
8929 |
-+ state.rel_pathname, stat_buf, pred_ptr, |
8930 |
-+ prefix, (prefix ? 2 : 0)); |
8931 |
-+ else |
8932 |
-+ return false; |
8933 |
-+} |
8934 |
-+ |
8935 |
-+boolean |
8936 |
-+pred_openparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8937 |
-+{ |
8938 |
-+ (void) pathname; |
8939 |
-+ (void) stat_buf; |
8940 |
-+ (void) pred_ptr; |
8941 |
-+ return true; |
8942 |
-+} |
8943 |
-+ |
8944 |
-+boolean |
8945 |
-+pred_or (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8946 |
-+{ |
8947 |
-+ if (pred_ptr->pred_left == NULL |
8948 |
-+ || !apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) |
8949 |
-+ { |
8950 |
-+ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
8951 |
-+ } |
8952 |
-+ else |
8953 |
-+ return true; |
8954 |
-+} |
8955 |
-+ |
8956 |
-+boolean |
8957 |
-+pred_path (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8958 |
-+{ |
8959 |
-+ (void) stat_buf; |
8960 |
-+ if (fnmatch (pred_ptr->args.str, pathname, 0) == 0) |
8961 |
-+ return (true); |
8962 |
-+ return (false); |
8963 |
-+} |
8964 |
-+ |
8965 |
-+boolean |
8966 |
-+pred_perm (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8967 |
-+{ |
8968 |
-+ mode_t mode = stat_buf->st_mode; |
8969 |
-+ mode_t perm_val = pred_ptr->args.perm.val[S_ISDIR (mode) != 0]; |
8970 |
-+ (void) pathname; |
8971 |
-+ switch (pred_ptr->args.perm.kind) |
8972 |
-+ { |
8973 |
-+ case PERM_AT_LEAST: |
8974 |
-+ return (mode & perm_val) == perm_val; |
8975 |
-+ break; |
8976 |
-+ |
8977 |
-+ case PERM_ANY: |
8978 |
-+ /* True if any of the bits set in the mask are also set in the file's mode. |
8979 |
-+ * |
8980 |
-+ * |
8981 |
-+ * Otherwise, if onum is prefixed by a hyphen, the primary shall |
8982 |
-+ * evaluate as true if at least all of the bits specified in |
8983 |
-+ * onum that are also set in the octal mask 07777 are set. |
8984 |
-+ * |
8985 |
-+ * Eric Blake's interpretation is that the mode argument is zero, |
8986 |
-+ |
8987 |
-+ */ |
8988 |
-+ if (0 == perm_val) |
8989 |
-+ return true; /* Savannah bug 14748; we used to return false */ |
8990 |
-+ else |
8991 |
-+ return (mode & perm_val) != 0; |
8992 |
-+ break; |
8993 |
-+ |
8994 |
-+ case PERM_EXACT: |
8995 |
-+ return (mode & MODE_ALL) == perm_val; |
8996 |
-+ break; |
8997 |
-+ |
8998 |
-+ default: |
8999 |
-+ abort (); |
9000 |
-+ break; |
9001 |
-+ } |
9002 |
-+} |
9003 |
-+ |
9004 |
-+ |
9005 |
-+struct access_check_args |
9006 |
-+{ |
9007 |
-+ const char *filename; |
9008 |
-+ int access_type; |
9009 |
-+ int cb_errno; |
9010 |
-+}; |
9011 |
-+ |
9012 |
-+ |
9013 |
-+static int |
9014 |
-+access_callback(void *context) |
9015 |
-+{ |
9016 |
-+ int rv; |
9017 |
-+ struct access_check_args *args = context; |
9018 |
-+ if ((rv = access(args->filename, args->access_type)) < 0) |
9019 |
-+ args->cb_errno = errno; |
9020 |
-+ return rv; |
9021 |
-+} |
9022 |
-+ |
9023 |
-+static int |
9024 |
-+can_access(int access_type) |
9025 |
-+{ |
9026 |
-+ struct access_check_args args; |
9027 |
-+ args.filename = state.rel_pathname; |
9028 |
-+ args.access_type = access_type; |
9029 |
-+ args.cb_errno = 0; |
9030 |
-+ return 0 == run_in_dir(state.cwd_dir_fd, access_callback, &args); |
9031 |
-+} |
9032 |
-+ |
9033 |
-+ |
9034 |
-+boolean |
9035 |
-+pred_executable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9036 |
-+{ |
9037 |
-+ (void) pathname; |
9038 |
-+ (void) stat_buf; |
9039 |
-+ (void) pred_ptr; |
9040 |
-+ |
9041 |
-+ return can_access(X_OK); |
9042 |
-+} |
9043 |
-+ |
9044 |
-+boolean |
9045 |
-+pred_readable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9046 |
-+{ |
9047 |
-+ (void) pathname; |
9048 |
-+ (void) stat_buf; |
9049 |
-+ (void) pred_ptr; |
9050 |
-+ |
9051 |
-+ return can_access(R_OK); |
9052 |
-+} |
9053 |
-+ |
9054 |
-+boolean |
9055 |
-+pred_writable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9056 |
-+{ |
9057 |
-+ (void) pathname; |
9058 |
-+ (void) stat_buf; |
9059 |
-+ (void) pred_ptr; |
9060 |
-+ |
9061 |
-+ return can_access(W_OK); |
9062 |
-+} |
9063 |
-+ |
9064 |
-+boolean |
9065 |
-+pred_print (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9066 |
-+{ |
9067 |
-+ (void) stat_buf; |
9068 |
-+ (void) pred_ptr; |
9069 |
-+ |
9070 |
-+ print_quoted(pred_ptr->args.printf_vec.stream, |
9071 |
-+ pred_ptr->args.printf_vec.quote_opts, |
9072 |
-+ pred_ptr->args.printf_vec.dest_is_tty, |
9073 |
-+ "%s\n", pathname); |
9074 |
-+ return true; |
9075 |
-+} |
9076 |
-+ |
9077 |
-+boolean |
9078 |
-+pred_print0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9079 |
-+{ |
9080 |
-+ return pred_fprint0(pathname, stat_buf, pred_ptr); |
9081 |
-+} |
9082 |
-+ |
9083 |
-+boolean |
9084 |
-+pred_prune (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9085 |
-+{ |
9086 |
-+ (void) pathname; |
9087 |
-+ (void) pred_ptr; |
9088 |
-+ |
9089 |
-+ if (options.do_dir_first == true && /* no effect with -depth */ |
9090 |
-+ stat_buf != NULL && |
9091 |
-+ S_ISDIR(stat_buf->st_mode)) |
9092 |
-+ state.stop_at_current_level = true; |
9093 |
-+ |
9094 |
-+ /* findutils used to return options.do_dir_first here, so that -prune |
9095 |
-+ * returns true only if -depth is not in effect. But POSIX requires |
9096 |
-+ * that -prune always evaluate as true. |
9097 |
-+ */ |
9098 |
-+ return true; |
9099 |
-+} |
9100 |
-+ |
9101 |
-+boolean |
9102 |
-+pred_quit (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9103 |
-+{ |
9104 |
-+ (void) pathname; |
9105 |
-+ (void) stat_buf; |
9106 |
-+ (void) pred_ptr; |
9107 |
-+ |
9108 |
-+ /* Run any cleanups. This includes executing any command lines |
9109 |
-+ * we have partly built but not executed. |
9110 |
-+ */ |
9111 |
-+ cleanup(); |
9112 |
-+ |
9113 |
-+ /* Since -exec and friends don't leave child processes running in the |
9114 |
-+ * background, there is no need to wait for them here. |
9115 |
-+ */ |
9116 |
-+ exit(state.exit_status); /* 0 for success, etc. */ |
9117 |
-+} |
9118 |
-+ |
9119 |
-+boolean |
9120 |
-+pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9121 |
-+{ |
9122 |
-+ int len = strlen (pathname); |
9123 |
-+(void) stat_buf; |
9124 |
-+ if (re_match (pred_ptr->args.regex, pathname, len, 0, |
9125 |
-+ (struct re_registers *) NULL) == len) |
9126 |
-+ return (true); |
9127 |
-+ return (false); |
9128 |
-+} |
9129 |
-+ |
9130 |
-+boolean |
9131 |
-+pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9132 |
-+{ |
9133 |
-+ uintmax_t f_val; |
9134 |
-+ |
9135 |
-+ (void) pathname; |
9136 |
-+ f_val = ((stat_buf->st_size / pred_ptr->args.size.blocksize) |
9137 |
-+ + (stat_buf->st_size % pred_ptr->args.size.blocksize != 0)); |
9138 |
-+ switch (pred_ptr->args.size.kind) |
9139 |
-+ { |
9140 |
-+ case COMP_GT: |
9141 |
-+ if (f_val > pred_ptr->args.size.size) |
9142 |
-+ return (true); |
9143 |
-+ break; |
9144 |
-+ case COMP_LT: |
9145 |
-+ if (f_val < pred_ptr->args.size.size) |
9146 |
-+ return (true); |
9147 |
-+ break; |
9148 |
-+ case COMP_EQ: |
9149 |
-+ if (f_val == pred_ptr->args.size.size) |
9150 |
-+ return (true); |
9151 |
-+ break; |
9152 |
-+ } |
9153 |
-+ return (false); |
9154 |
-+} |
9155 |
-+ |
9156 |
-+boolean |
9157 |
-+pred_samefile (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9158 |
-+{ |
9159 |
-+ /* Potential optimisation: because of the loop protection, we always |
9160 |
-+ * know the device of the current directory, hence the device number |
9161 |
-+ * of the file we're currently considering. If -L is not in effect, |
9162 |
-+ * and the device number of the file we're looking for is not the |
9163 |
-+ * same as the device number of the current directory, this |
9164 |
-+ * predicate cannot return true. Hence there would be no need to |
9165 |
-+ * stat the file we're looking at. |
9166 |
-+ */ |
9167 |
-+ (void) pathname; |
9168 |
-+ |
9169 |
-+ /* We will often still have an fd open on the file under consideration, |
9170 |
-+ * but that's just to ensure inode number stability by maintaining |
9171 |
-+ * a reference to it; we don't need the file for anything else. |
9172 |
-+ */ |
9173 |
-+ return stat_buf->st_ino == pred_ptr->args.samefileid.ino |
9174 |
-+ && stat_buf->st_dev == pred_ptr->args.samefileid.dev; |
9175 |
-+} |
9176 |
-+ |
9177 |
-+boolean |
9178 |
-+pred_true (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9179 |
-+{ |
9180 |
-+ (void) pathname; |
9181 |
-+ (void) stat_buf; |
9182 |
-+ (void) pred_ptr; |
9183 |
-+ return true; |
9184 |
-+} |
9185 |
-+ |
9186 |
-+boolean |
9187 |
-+pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9188 |
-+{ |
9189 |
-+ mode_t mode; |
9190 |
-+ mode_t type = pred_ptr->args.type; |
9191 |
-+ |
9192 |
-+ assert (state.have_type); |
9193 |
-+ |
9194 |
-+ if (0 == state.type) |
9195 |
-+ { |
9196 |
-+ /* This can sometimes happen with broken NFS servers. |
9197 |
-+ * See Savannah bug #16378. |
9198 |
-+ */ |
9199 |
-+ return false; |
9200 |
-+ } |
9201 |
-+ |
9202 |
-+ (void) pathname; |
9203 |
-+ |
9204 |
-+ if (state.have_stat) |
9205 |
-+ mode = stat_buf->st_mode; |
9206 |
-+ else |
9207 |
-+ mode = state.type; |
9208 |
-+ |
9209 |
-+#ifndef S_IFMT |
9210 |
-+ /* POSIX system; check `mode' the slow way. */ |
9211 |
-+ if ((S_ISBLK (mode) && type == S_IFBLK) |
9212 |
-+ || (S_ISCHR (mode) && type == S_IFCHR) |
9213 |
-+ || (S_ISDIR (mode) && type == S_IFDIR) |
9214 |
-+ || (S_ISREG (mode) && type == S_IFREG) |
9215 |
-+#ifdef S_IFLNK |
9216 |
-+ || (S_ISLNK (mode) && type == S_IFLNK) |
9217 |
-+#endif |
9218 |
-+#ifdef S_IFIFO |
9219 |
-+ || (S_ISFIFO (mode) && type == S_IFIFO) |
9220 |
-+#endif |
9221 |
-+#ifdef S_IFSOCK |
9222 |
-+ || (S_ISSOCK (mode) && type == S_IFSOCK) |
9223 |
-+#endif |
9224 |
-+#ifdef S_IFDOOR |
9225 |
-+ || (S_ISDOOR (mode) && type == S_IFDOOR) |
9226 |
-+#endif |
9227 |
-+ ) |
9228 |
-+#else /* S_IFMT */ |
9229 |
-+ /* Unix system; check `mode' the fast way. */ |
9230 |
-+ if ((mode & S_IFMT) == type) |
9231 |
-+#endif /* S_IFMT */ |
9232 |
-+ return (true); |
9233 |
-+ else |
9234 |
-+ return (false); |
9235 |
-+} |
9236 |
-+ |
9237 |
-+boolean |
9238 |
-+pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9239 |
-+{ |
9240 |
-+ (void) pathname; |
9241 |
-+ switch (pred_ptr->args.numinfo.kind) |
9242 |
-+ { |
9243 |
-+ case COMP_GT: |
9244 |
-+ if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val) |
9245 |
-+ return (true); |
9246 |
-+ break; |
9247 |
-+ case COMP_LT: |
9248 |
-+ if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val) |
9249 |
-+ return (true); |
9250 |
-+ break; |
9251 |
-+ case COMP_EQ: |
9252 |
-+ if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val) |
9253 |
-+ return (true); |
9254 |
-+ break; |
9255 |
-+ } |
9256 |
-+ return (false); |
9257 |
-+} |
9258 |
-+ |
9259 |
-+boolean |
9260 |
-+pred_used (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9261 |
-+{ |
9262 |
-+ struct timespec delta, at, ct; |
9263 |
-+ |
9264 |
-+ (void) pathname; |
9265 |
-+ |
9266 |
-+ /* TODO: this needs to be retested carefully (manually, if necessary) */ |
9267 |
-+ at = get_stat_atime(stat_buf); |
9268 |
-+ ct = get_stat_ctime(stat_buf); |
9269 |
-+ delta.tv_sec = at.tv_sec - ct.tv_sec; |
9270 |
-+ delta.tv_nsec = at.tv_nsec - ct.tv_nsec; |
9271 |
-+ if (delta.tv_nsec < 0) |
9272 |
-+ { |
9273 |
-+ delta.tv_nsec += 1000000000; |
9274 |
-+ delta.tv_sec -= 1; |
9275 |
-+ } |
9276 |
-+ return pred_timewindow(delta, pred_ptr, DAYSECS); |
9277 |
-+} |
9278 |
-+ |
9279 |
-+boolean |
9280 |
-+pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9281 |
-+{ |
9282 |
-+ (void) pathname; |
9283 |
-+ if (pred_ptr->args.uid == stat_buf->st_uid) |
9284 |
-+ return (true); |
9285 |
-+ else |
9286 |
-+ return (false); |
9287 |
-+} |
9288 |
-+ |
9289 |
-+boolean |
9290 |
-+pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9291 |
-+{ |
9292 |
-+ struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */ |
9293 |
-+ int (*ystat) (const char*, struct stat *p); |
9294 |
-+ |
9295 |
-+ /* If we would normally stat the link itself, stat the target instead. |
9296 |
-+ * If we would normally follow the link, stat the link itself instead. |
9297 |
-+ */ |
9298 |
-+ if (following_links()) |
9299 |
-+ ystat = optionp_stat; |
9300 |
-+ else |
9301 |
-+ ystat = optionl_stat; |
9302 |
-+ |
9303 |
-+ set_stat_placeholders(&sbuf); |
9304 |
-+ if ((*ystat) (state.rel_pathname, &sbuf) != 0) |
9305 |
-+ { |
9306 |
-+ if (following_links() && errno == ENOENT) |
9307 |
-+ { |
9308 |
-+ /* If we failed to follow the symlink, |
9309 |
-+ * fall back on looking at the symlink itself. |
9310 |
-+ */ |
9311 |
-+ /* Mimic behavior of ls -lL. */ |
9312 |
-+ return (pred_type (pathname, stat_buf, pred_ptr)); |
9313 |
-+ } |
9314 |
-+ else |
9315 |
-+ { |
9316 |
-+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
9317 |
-+ state.exit_status = 1; |
9318 |
-+ } |
9319 |
-+ return false; |
9320 |
-+ } |
9321 |
-+ /* Now that we have our stat() information, query it in the same |
9322 |
-+ * way that -type does. |
9323 |
-+ */ |
9324 |
-+ return (pred_type (pathname, &sbuf, pred_ptr)); |
9325 |
-+} |
9326 |
-+ |
9327 |
-+/* 1) fork to get a child; parent remembers the child pid |
9328 |
-+ 2) child execs the command requested |
9329 |
-+ 3) parent waits for child; checks for proper pid of child |
9330 |
-+ |
9331 |
-+ Possible returns: |
9332 |
-+ |
9333 |
-+ ret errno status(h) status(l) |
9334 |
-+ |
9335 |
-+ pid x signal# 0177 stopped |
9336 |
-+ pid x exit arg 0 term by _exit |
9337 |
-+ pid x 0 signal # term by signal |
9338 |
-+ -1 EINTR parent got signal |
9339 |
-+ -1 other some other kind of error |
9340 |
-+ |
9341 |
-+ Return true only if the pid matches, status(l) is |
9342 |
-+ zero, and the exit arg (status high) is 0. |
9343 |
-+ Otherwise return false, possibly printing an error message. */ |
9344 |
-+ |
9345 |
-+ |
9346 |
-+static boolean |
9347 |
-+prep_child_for_exec (boolean close_stdin, int dirfd) |
9348 |
-+{ |
9349 |
-+ boolean ok = true; |
9350 |
-+ if (close_stdin) |
9351 |
-+ { |
9352 |
-+ const char inputfile[] = "/dev/null"; |
9353 |
-+ |
9354 |
-+ if (close(0) < 0) |
9355 |
-+ { |
9356 |
-+ error(0, errno, _("Cannot close standard input")); |
9357 |
-+ ok = false; |
9358 |
-+ } |
9359 |
-+ else |
9360 |
-+ { |
9361 |
-+ if (open(inputfile, O_RDONLY |
9362 |
-+#if defined O_LARGEFILE |
9363 |
-+ |O_LARGEFILE |
9364 |
-+#endif |
9365 |
-+ ) < 0) |
9366 |
-+ { |
9367 |
-+ /* This is not entirely fatal, since |
9368 |
-+ * executing the child with a closed |
9369 |
-+ * stdin is almost as good as executing it |
9370 |
-+ * with its stdin attached to /dev/null. |
9371 |
-+ */ |
9372 |
-+ error (0, errno, "%s", safely_quote_err_filename(0, inputfile)); |
9373 |
-+ /* do not set ok=false, it is OK to continue anyway. */ |
9374 |
-+ } |
9375 |
-+ } |
9376 |
-+ } |
9377 |
-+ |
9378 |
-+ /* Even if DebugSearch is set, don't announce our change of |
9379 |
-+ * directory, since we're not going to emit a subsequent |
9380 |
-+ * announcement of a call to stat() anyway, as we're about to exec |
9381 |
-+ * something. |
9382 |
-+ */ |
9383 |
-+ if (dirfd != AT_FDCWD) |
9384 |
-+ { |
9385 |
-+ assert (dirfd >= 0); |
9386 |
-+ if (0 != fchdir(dirfd)) |
9387 |
-+ { |
9388 |
-+ /* If we cannot execute our command in the correct directory, |
9389 |
-+ * we should not execute it at all. |
9390 |
-+ */ |
9391 |
-+ error(0, errno, _("Failed to change directory")); |
9392 |
-+ ok = false; |
9393 |
-+ } |
9394 |
-+ } |
9395 |
-+ return ok; |
9396 |
-+} |
9397 |
-+ |
9398 |
-+ |
9399 |
-+ |
9400 |
-+int |
9401 |
-+launch (const struct buildcmd_control *ctl, |
9402 |
-+ struct buildcmd_state *buildstate) |
9403 |
-+{ |
9404 |
-+ int wait_status; |
9405 |
-+ pid_t child_pid; |
9406 |
-+ static int first_time = 1; |
9407 |
-+ const struct exec_val *execp = buildstate->usercontext; |
9408 |
-+ |
9409 |
-+ if (!execp->use_current_dir) |
9410 |
-+ { |
9411 |
-+ assert (starting_desc >= 0); |
9412 |
-+ assert (execp->dirfd == starting_desc); |
9413 |
-+ } |
9414 |
-+ |
9415 |
-+ |
9416 |
-+ /* Null terminate the arg list. */ |
9417 |
-+ bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false); |
9418 |
-+ |
9419 |
-+ /* Make sure output of command doesn't get mixed with find output. */ |
9420 |
-+ fflush (stdout); |
9421 |
-+ fflush (stderr); |
9422 |
-+ |
9423 |
-+ /* Make sure to listen for the kids. */ |
9424 |
-+ if (first_time) |
9425 |
-+ { |
9426 |
-+ first_time = 0; |
9427 |
-+ signal (SIGCHLD, SIG_DFL); |
9428 |
-+ } |
9429 |
-+ |
9430 |
-+ child_pid = fork (); |
9431 |
-+ if (child_pid == -1) |
9432 |
-+ error (1, errno, _("cannot fork")); |
9433 |
-+ if (child_pid == 0) |
9434 |
-+ { |
9435 |
-+ /* We are the child. */ |
9436 |
-+ assert (starting_desc >= 0); |
9437 |
-+ if (!prep_child_for_exec(execp->close_stdin, execp->dirfd)) |
9438 |
-+ { |
9439 |
-+ _exit(1); |
9440 |
-+ } |
9441 |
-+ |
9442 |
-+ execvp (buildstate->cmd_argv[0], buildstate->cmd_argv); |
9443 |
-+ error (0, errno, "%s", |
9444 |
-+ safely_quote_err_filename(0, buildstate->cmd_argv[0])); |
9445 |
-+ _exit (1); |
9446 |
-+ } |
9447 |
-+ |
9448 |
-+ |
9449 |
-+ /* In parent; set up for next time. */ |
9450 |
-+ bc_clear_args(ctl, buildstate); |
9451 |
-+ |
9452 |
-+ |
9453 |
-+ while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) |
9454 |
-+ { |
9455 |
-+ if (errno != EINTR) |
9456 |
-+ { |
9457 |
-+ error (0, errno, _("error waiting for %s"), |
9458 |
-+ safely_quote_err_filename(0, buildstate->cmd_argv[0])); |
9459 |
-+ state.exit_status = 1; |
9460 |
-+ return 0; /* FAIL */ |
9461 |
-+ } |
9462 |
-+ } |
9463 |
-+ |
9464 |
-+ if (WIFSIGNALED (wait_status)) |
9465 |
++ switch (errno) |
9466 |
+ { |
9467 |
-+ error (0, 0, _("%1$s terminated by signal %2$d"), |
9468 |
-+ quotearg_n_style(0, options.err_quoting_style, |
9469 |
-+ buildstate->cmd_argv[0]), |
9470 |
-+ WTERMSIG (wait_status)); |
9471 |
-+ |
9472 |
-+ if (execp->multiple) |
9473 |
-+ { |
9474 |
-+ /* -exec \; just returns false if the invoked command fails. |
9475 |
-+ * -exec {} + returns true if the invoked command fails, but |
9476 |
-+ * sets the program exit status. |
9477 |
-+ */ |
9478 |
-+ state.exit_status = 1; |
9479 |
-+ } |
9480 |
-+ |
9481 |
-+ return 1; /* OK */ |
9482 |
-+ } |
9483 |
++ case ENOENT: |
9484 |
++ case ENOTDIR: |
9485 |
++#ifdef DEBUG_STAT |
9486 |
++ fprintf(stderr, "fallback_getfilecon(): getfilecon(%s) failed; falling back on lgetfilecon()\n", name); |
9487 |
++#endif |
9488 |
++ return lgetfilecon(name, p); |
9489 |
+ |
9490 |
-+ if (0 == WEXITSTATUS (wait_status)) |
9491 |
-+ { |
9492 |
-+ return 1; /* OK */ |
9493 |
-+ } |
9494 |
-+ else |
9495 |
-+ { |
9496 |
-+ if (execp->multiple) |
9497 |
-+ { |
9498 |
-+ /* -exec \; just returns false if the invoked command fails. |
9499 |
-+ * -exec {} + returns true if the invoked command fails, but |
9500 |
-+ * sets the program exit status. |
9501 |
-+ */ |
9502 |
-+ state.exit_status = 1; |
9503 |
-+ } |
9504 |
-+ return 0; /* FAIL */ |
9505 |
++ case EACCES: |
9506 |
++ case EIO: |
9507 |
++ case ELOOP: |
9508 |
++ case ENAMETOOLONG: |
9509 |
++#ifdef EOVERFLOW |
9510 |
++ case EOVERFLOW: /* EOVERFLOW is not #defined on UNICOS. */ |
9511 |
++#endif |
9512 |
++ default: |
9513 |
++ return prev_rv; |
9514 |
+ } |
9515 |
-+ |
9516 |
+} |
9517 |
+ |
9518 |
-+ |
9519 |
-+/* Return a static string formatting the time WHEN according to the |
9520 |
-+ * strftime format character KIND. |
9521 |
++ |
9522 |
++/* optionh_getfilecon() implements the getfilecon operation when the |
9523 |
++ * -H option is in effect. |
9524 |
++ * |
9525 |
++ * If the item to be examined is a command-line argument, we follow |
9526 |
++ * symbolic links. If the getfilecon() call fails on the command-line |
9527 |
++ * item, we fall back on the properties of the symbolic link. |
9528 |
+ * |
9529 |
-+ * This function contains a number of assertions. These look like |
9530 |
-+ * runtime checks of the results of computations, which would be a |
9531 |
-+ * problem since external events should not be tested for with |
9532 |
-+ * "assert" (instead you should use "if"). However, they are not |
9533 |
-+ * really runtime checks. The assertions actually exist to verify |
9534 |
-+ * that the various buffers are correctly sized. |
9535 |
++ * If the item to be examined is not a command-line argument, we |
9536 |
++ * examine the link itself. |
9537 |
+ */ |
9538 |
-+static char * |
9539 |
-+format_date (struct timespec ts, int kind) |
9540 |
++int |
9541 |
++optionh_getfilecon(const char *name, security_context_t *p) |
9542 |
+{ |
9543 |
-+ /* In theory, we use an extra 10 characters for 9 digits of |
9544 |
-+ * nanoseconds and 1 for the decimal point. However, the real |
9545 |
-+ * world is more complex than that. |
9546 |
-+ * |
9547 |
-+ * For example, some systems return junk in the tv_nsec part of |
9548 |
-+ * st_birthtime. An example of this is the NetBSD-4.0-RELENG kernel |
9549 |
-+ * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE |
9550 |
-+ * runtime and examining files on an msdos filesytem. So for that |
9551 |
-+ * reason we set NS_BUF_LEN to 32, which is simply "long enough" as |
9552 |
-+ * opposed to "exactly the right size". Note that the behaviour of |
9553 |
-+ * NetBSD appears to be a result of the use of uninitialised data, |
9554 |
-+ * as it's not 100% reproducible (more like 25%). |
9555 |
-+ */ |
9556 |
-+ enum { |
9557 |
-+ NS_BUF_LEN = 32, |
9558 |
-+ DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/ |
9559 |
-+ }; |
9560 |
-+ static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS, |
9561 |
-+ MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))]; |
9562 |
-+ char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/ |
9563 |
-+ int charsprinted, need_ns_suffix; |
9564 |
-+ struct tm *tm; |
9565 |
-+ char fmt[6]; |
9566 |
-+ |
9567 |
-+ /* human_readable() assumes we pass a buffer which is at least as |
9568 |
-+ * long as LONGEST_HUMAN_READABLE. We use an assertion here to |
9569 |
-+ * ensure that no nasty unsigned overflow happend in our calculation |
9570 |
-+ * of the size of buf. Do the assertion here rather than in the |
9571 |
-+ * code for %@ so that we find the problem quickly if it exists. If |
9572 |
-+ * you want to submit a patch to move this into the if statement, go |
9573 |
-+ * ahead, I'll apply it. But include performance timings |
9574 |
-+ * demonstrating that the performance difference is actually |
9575 |
-+ * measurable. |
9576 |
-+ */ |
9577 |
-+ verify (sizeof(buf) >= LONGEST_HUMAN_READABLE); |
9578 |
-+ |
9579 |
-+ charsprinted = 0; |
9580 |
-+ need_ns_suffix = 0; |
9581 |
-+ |
9582 |
-+ /* Format the main part of the time. */ |
9583 |
-+ if (kind == '+') |
9584 |
-+ { |
9585 |
-+ strcpy (fmt, "%F+%T"); |
9586 |
-+ need_ns_suffix = 1; |
9587 |
-+ } |
9588 |
-+ else |
9589 |
-+ { |
9590 |
-+ fmt[0] = '%'; |
9591 |
-+ fmt[1] = kind; |
9592 |
-+ fmt[2] = '\0'; |
9593 |
-+ |
9594 |
-+ /* %a, %c, and %t are handled in ctime_format() */ |
9595 |
-+ switch (kind) |
9596 |
-+ { |
9597 |
-+ case 'S': |
9598 |
-+ case 'T': |
9599 |
-+ case 'X': |
9600 |
-+ case '@': |
9601 |
-+ need_ns_suffix = 1; |
9602 |
-+ break; |
9603 |
-+ default: |
9604 |
-+ need_ns_suffix = 0; |
9605 |
-+ break; |
9606 |
-+ } |
9607 |
-+ } |
9608 |
-+ |
9609 |
-+ if (need_ns_suffix) |
9610 |
-+ { |
9611 |
-+ /* Format the nanoseconds part. Leave a trailing zero to |
9612 |
-+ * discourage people from writing scripts which extract the |
9613 |
-+ * fractional part of the timestamp by using column offsets. |
9614 |
-+ * The reason for discouraging this is that in the future, the |
9615 |
-+ * granularity may not be nanoseconds. |
9616 |
-+ */ |
9617 |
-+ ns_buf[0] = 0; |
9618 |
-+ charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec); |
9619 |
-+ assert (charsprinted < NS_BUF_LEN); |
9620 |
-+ } |
9621 |
-+ |
9622 |
-+ if (kind != '@' |
9623 |
-+ && (tm = localtime (&ts.tv_sec)) |
9624 |
-+ && strftime (buf, sizeof buf, fmt, tm)) |
9625 |
++ if (0 == state.curdepth) |
9626 |
+ { |
9627 |
-+ /* For %AS, %CS, %TS, add the fractional part of the seconds |
9628 |
-+ * information. |
9629 |
++ /* This file is from the command line; deference the link (if it |
9630 |
++ * is a link). |
9631 |
+ */ |
9632 |
-+ if (need_ns_suffix) |
9633 |
-+ { |
9634 |
-+ assert ((sizeof buf - strlen(buf)) > strlen(ns_buf)); |
9635 |
-+ strcat(buf, ns_buf); |
9636 |
-+ } |
9637 |
-+ return buf; |
9638 |
++ int rv = getfilecon(name, p); |
9639 |
++ if (0 == rv) |
9640 |
++ return 0; /* success */ |
9641 |
++ else |
9642 |
++ return fallback_getfilecon(name, p, rv); |
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 |
++ /* Not a file on the command line; do not derefernce the link. |
9664 |
+ */ |
9665 |
-+ if (need_ns_suffix) |
9666 |
-+ { |
9667 |
-+ len = strlen(p); |
9668 |
-+ used = (p-buf) + len; /* Offset into buf of current end */ |
9669 |
-+ assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */ |
9670 |
-+ remaining = sizeof buf - used - 1u; /* allow space for NUL */ |
9671 |
-+ |
9672 |
-+ if (strlen(ns_buf) >= remaining) |
9673 |
-+ { |
9674 |
-+ error(0, 0, |
9675 |
-+ "charsprinted=%ld but remaining=%lu: ns_buf=%s", |
9676 |
-+ (long)charsprinted, (unsigned long)remaining, ns_buf); |
9677 |
-+ } |
9678 |
-+ assert (strlen(ns_buf) < remaining); |
9679 |
-+ strcat(p, ns_buf); |
9680 |
-+ } |
9681 |
-+ return p; |
9682 |
-+ } |
9683 |
-+} |
9684 |
-+ |
9685 |
-+static const char *weekdays[] = |
9686 |
-+ { |
9687 |
-+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
9688 |
-+ }; |
9689 |
-+static char * months[] = |
9690 |
-+ { |
9691 |
-+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
9692 |
-+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
9693 |
-+ }; |
9694 |
-+ |
9695 |
-+ |
9696 |
-+static char * |
9697 |
-+ctime_format (struct timespec ts) |
9698 |
-+{ |
9699 |
-+ const struct tm * ptm; |
9700 |
-+#define TIME_BUF_LEN 1024u |
9701 |
-+ static char resultbuf[TIME_BUF_LEN]; |
9702 |
-+ int nout; |
9703 |
-+ |
9704 |
-+ ptm = localtime(&ts.tv_sec); |
9705 |
-+ if (ptm) |
9706 |
-+ { |
9707 |
-+ assert (ptm->tm_wday >= 0); |
9708 |
-+ assert (ptm->tm_wday < 7); |
9709 |
-+ assert (ptm->tm_mon >= 0); |
9710 |
-+ assert (ptm->tm_mon < 12); |
9711 |
-+ assert (ptm->tm_hour >= 0); |
9712 |
-+ assert (ptm->tm_hour < 24); |
9713 |
-+ assert (ptm->tm_min < 60); |
9714 |
-+ assert (ptm->tm_sec <= 61); /* allows 2 leap seconds. */ |
9715 |
-+ |
9716 |
-+ /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */ |
9717 |
-+ nout = snprintf(resultbuf, TIME_BUF_LEN, |
9718 |
-+ "%3s %3s %2d %02d:%02d:%02d.%010ld %04d", |
9719 |
-+ weekdays[ptm->tm_wday], |
9720 |
-+ months[ptm->tm_mon], |
9721 |
-+ ptm->tm_mday, |
9722 |
-+ ptm->tm_hour, |
9723 |
-+ ptm->tm_min, |
9724 |
-+ ptm->tm_sec, |
9725 |
-+ (long int)ts.tv_nsec, |
9726 |
-+ 1900 + ptm->tm_year); |
9727 |
-+ |
9728 |
-+ assert (nout < TIME_BUF_LEN); |
9729 |
-+ return resultbuf; |
9730 |
-+ } |
9731 |
-+ else |
9732 |
-+ { |
9733 |
-+ /* The time cannot be represented as a struct tm. |
9734 |
-+ Output it as an integer. */ |
9735 |
-+ return format_date (ts, '@'); |
9736 |
++ return lgetfilecon(name, p); |
9737 |
+ } |
9738 |
+} |
9739 |
+ |
9740 |
-+/* Copy STR into BUF and trim blanks from the end of BUF. |
9741 |
-+ Return BUF. */ |
9742 |
-+ |
9743 |
-+static char * |
9744 |
-+blank_rtrim (str, buf) |
9745 |
-+ char *str; |
9746 |
-+ char *buf; |
9747 |
++/* optionl_getfilecon() implements the getfilecon operation when the |
9748 |
++ * -L option is in effect. That option makes us examine the thing the |
9749 |
++ * symbolic link points to, not the symbolic link itself. |
9750 |
++ */ |
9751 |
++int |
9752 |
++optionl_getfilecon(const char *name, security_context_t *p) |
9753 |
+{ |
9754 |
-+ int i; |
9755 |
-+ |
9756 |
-+ if (str == NULL) |
9757 |
-+ return (NULL); |
9758 |
-+ strcpy (buf, str); |
9759 |
-+ i = strlen (buf) - 1; |
9760 |
-+ while ((i >= 0) && ((buf[i] == ' ') || buf[i] == '\t')) |
9761 |
-+ i--; |
9762 |
-+ buf[++i] = '\0'; |
9763 |
-+ return (buf); |
9764 |
++ int rv = getfilecon(name, p); |
9765 |
++ if (0 == rv) |
9766 |
++ return 0; /* normal case. */ |
9767 |
++ else |
9768 |
++ return fallback_getfilecon(name, p, rv); |
9769 |
+} |
9770 |
+ |
9771 |
-+/* Print out the predicate list starting at NODE. */ |
9772 |
-+void |
9773 |
-+print_list (FILE *fp, struct predicate *node) |
9774 |
++/* optionp_getfilecon() implements the stat operation when the -P |
9775 |
++ * option is in effect (this is also the default). That option makes |
9776 |
++ * us examine the symbolic link itself, not the thing it points to. |
9777 |
++ */ |
9778 |
++int |
9779 |
++optionp_getfilecon(const char *name, security_context_t *p) |
9780 |
+{ |
9781 |
-+ struct predicate *cur; |
9782 |
-+ char name[256]; |
9783 |
-+ |
9784 |
-+ cur = node; |
9785 |
-+ while (cur != NULL) |
9786 |
-+ { |
9787 |
-+ fprintf (fp, "[%s] ", blank_rtrim (cur->p_name, name)); |
9788 |
-+ cur = cur->pred_next; |
9789 |
-+ } |
9790 |
-+ fprintf (fp, "\n"); |
9791 |
++ return lgetfilecon(name, p); |
9792 |
+} |
9793 |
-+ |
9794 |
-+/* Print out the predicate list starting at NODE. */ |
9795 |
-+static void |
9796 |
-+print_parenthesised(FILE *fp, struct predicate *node) |
9797 |
-+{ |
9798 |
-+ int parens = 0; |
9799 |
++#endif /* WITH_SELINUX */ |
9800 |
+ |
9801 |
-+ if (node) |
9802 |
-+ { |
9803 |
-+ if ((pred_is(node, pred_or) || pred_is(node, pred_and)) |
9804 |
-+ && node->pred_left == NULL) |
9805 |
-+ { |
9806 |
-+ /* We print "<nothing> or X" as just "X" |
9807 |
-+ * We print "<nothing> and X" as just "X" |
9808 |
-+ */ |
9809 |
-+ print_parenthesised(fp, node->pred_right); |
9810 |
-+ } |
9811 |
-+ else |
9812 |
-+ { |
9813 |
-+ if (node->pred_left || node->pred_right) |
9814 |
-+ parens = 1; |
9815 |
+ |
9816 |
+ static boolean |
9817 |
+ parse_and (const struct parser_table* entry, char **argv, int *arg_ptr) |
9818 |
+@@ -1124,6 +1233,10 @@ tests (N can be +N or -N or N): -amin N |
9819 |
+ -readable -writable -executable\n\ |
9820 |
+ -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\ |
9821 |
+ -used N -user NAME -xtype [bcdpfls]\n")); |
9822 |
++#ifdef WITH_SELINUX |
9823 |
++ puts (_("\ |
9824 |
++ -context CONTEXT\n")); |
9825 |
++#endif /*WITH_SELINUX*/ |
9826 |
+ puts (_("\ |
9827 |
+ actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\ |
9828 |
+ -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\ |
9829 |
+@@ -2522,6 +2635,29 @@ parse_version (const struct parser_table |
9830 |
+ exit (0); |
9831 |
+ } |
9832 |
+ |
9833 |
++#ifdef WITH_SELINUX |
9834 |
+ |
9835 |
-+ if (parens) |
9836 |
-+ fprintf(fp, "%s", " ( "); |
9837 |
-+ print_optlist(fp, node); |
9838 |
-+ if (parens) |
9839 |
-+ fprintf(fp, "%s", " ) "); |
9840 |
-+ } |
9841 |
-+ } |
9842 |
-+} |
9843 |
-+ |
9844 |
-+void |
9845 |
-+print_optlist (FILE *fp, const struct predicate *p) |
9846 |
-+{ |
9847 |
-+ if (p) |
9848 |
-+ { |
9849 |
-+ print_parenthesised(fp, p->pred_left); |
9850 |
-+ fprintf (fp, |
9851 |
-+ "%s%s", |
9852 |
-+ p->need_stat ? "[call stat] " : "", |
9853 |
-+ p->need_type ? "[need type] " : ""); |
9854 |
-+ print_predicate(fp, p); |
9855 |
-+ fprintf(fp, " [%g] ", p->est_success_rate); |
9856 |
-+ if (options.debug_options & DebugSuccessRates) |
9857 |
-+ { |
9858 |
-+ fprintf(fp, "[%ld/%ld", p->perf.successes, p->perf.visits); |
9859 |
-+ if (p->perf.visits) |
9860 |
-+ { |
9861 |
-+ double real_rate = (double)p->perf.successes / (double)p->perf.visits; |
9862 |
-+ fprintf(fp, "=%g] ", real_rate); |
9863 |
-+ } |
9864 |
-+ else |
9865 |
-+ { |
9866 |
-+ fprintf(fp, "=_] "); |
9867 |
-+ } |
9868 |
-+ } |
9869 |
-+ print_parenthesised(fp, p->pred_right); |
9870 |
-+ } |
9871 |
-+} |
9872 |
-+ |
9873 |
-+void show_success_rates(const struct predicate *p) |
9874 |
++static boolean |
9875 |
++parse_scontext ( const struct parser_table* entry, char **argv, int *arg_ptr) |
9876 |
+{ |
9877 |
-+ if (options.debug_options & DebugSuccessRates) |
9878 |
-+ { |
9879 |
-+ fprintf(stderr, "Predicate success rates after completion:\n"); |
9880 |
-+ print_optlist(stderr, p); |
9881 |
-+ fprintf(stderr, "\n"); |
9882 |
-+ } |
9883 |
-+} |
9884 |
++ struct predicate *our_pred; |
9885 |
+ |
9886 |
++ if ( (argv == NULL) || (argv[*arg_ptr] == NULL) ) |
9887 |
++ return( false ); |
9888 |
+ |
9889 |
++ our_pred = insert_primary(entry); |
9890 |
++ our_pred->need_stat = false; |
9891 |
++#ifdef DEBUG |
9892 |
++ our_pred->p_name = find_pred_name (pred_scontext); |
9893 |
++#endif /*DEBUG*/ |
9894 |
++ our_pred->args.scontext = argv[*arg_ptr];; |
9895 |
+ |
9896 |
-+ |
9897 |
-+#ifdef _NDEBUG |
9898 |
-+/* If _NDEBUG is defined, the assertions will do nothing. Hence |
9899 |
-+ * there is no point in having a function body for pred_sanity_check() |
9900 |
-+ * if that preprocessor macro is defined. |
9901 |
-+ */ |
9902 |
-+void |
9903 |
-+pred_sanity_check(const struct predicate *predicates) |
9904 |
-+{ |
9905 |
-+ /* Do nothing, since assert is a no-op with _NDEBUG set */ |
9906 |
-+ return; |
9907 |
++ (*arg_ptr)++; |
9908 |
++ return( true ); |
9909 |
+} |
9910 |
++ |
9911 |
++#endif /*WITH_SELINUX*/ |
9912 |
++ |
9913 |
+ static boolean |
9914 |
+ parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr) |
9915 |
+ { |
9916 |
+@@ -2773,7 +2909,11 @@ insert_fprintf (struct format_val *vec, |
9917 |
+ if (*scan2 == '.') |
9918 |
+ for (scan2++; ISDIGIT (*scan2); scan2++) |
9919 |
+ /* Do nothing. */ ; |
9920 |
++#ifdef WITH_SELINUX |
9921 |
++ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyYZ", *scan2)) |
9922 |
+#else |
9923 |
-+void |
9924 |
-+pred_sanity_check(const struct predicate *predicates) |
9925 |
-+{ |
9926 |
-+ const struct predicate *p; |
9927 |
+ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2)) |
9928 |
++#endif |
9929 |
+ { |
9930 |
+ segmentp = make_segment (segmentp, format, scan2 - format, |
9931 |
+ KIND_FORMAT, *scan2, 0, |
9932 |
+diff -purN findutils-4.3.12.orig/find/pred.c findutils-4.3.12/find/pred.c |
9933 |
+--- findutils-4.3.12.orig/find/pred.c 2007-12-19 16:12:34.000000000 -0500 |
9934 |
++++ findutils-4.3.12/find/pred.c 2008-01-30 08:46:05.758843847 -0500 |
9935 |
+@@ -47,6 +47,14 @@ |
9936 |
+ #include "error.h" |
9937 |
+ #include "verify.h" |
9938 |
+ |
9939 |
++#ifdef WITH_SELINUX |
9940 |
++#include <selinux/selinux.h> |
9941 |
++#endif /*WITH_SELINUX*/ |
9942 |
++ |
9943 |
++#ifndef FNM_CASEFOLD |
9944 |
++#define FNM_CASEFOLD (1<<4) |
9945 |
++#endif /*FNM_CASEFOLD*/ |
9946 |
++ |
9947 |
+ #if ENABLE_NLS |
9948 |
+ # include <libintl.h> |
9949 |
+ # define _(Text) gettext (Text) |
9950 |
+@@ -229,6 +237,9 @@ struct pred_assoc pred_table[] = |
9951 |
+ {pred_user, "user "}, |
9952 |
+ {pred_writable, "writable "}, |
9953 |
+ {pred_xtype, "xtype "}, |
9954 |
++#ifdef WITH_SELINUX |
9955 |
++ {pred_scontext, "context"}, |
9956 |
++#endif /*WITH_SELINUX*/ |
9957 |
+ {0, "none "} |
9958 |
+ }; |
9959 |
+ #endif |
9960 |
+@@ -1045,6 +1056,26 @@ do_fprintf(struct format_val *dest, |
9961 |
+ mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
9962 |
+ } |
9963 |
+ break; |
9964 |
++#ifdef WITH_SELINUX |
9965 |
++ case 'Z': /* SELinux security context */ |
9966 |
++ { |
9967 |
++ security_context_t scontext; |
9968 |
++ int rv; |
9969 |
++ rv = (*options.x_getfilecon)(state.rel_pathname, &scontext); |
9970 |
++ |
9971 |
++ if ( rv < 0 ) { |
9972 |
++ fprintf(stderr, "getfileconf(%s): %s", |
9973 |
++ pathname, strerror(errno)); |
9974 |
++ fflush(stderr); |
9975 |
++ } |
9976 |
++ else { |
9977 |
++ segment->text[segment->text_len] = 's'; |
9978 |
++ checked_fprintf (dest, segment->text, scontext); |
9979 |
++ freecon(scontext); |
9980 |
++ } |
9981 |
++ } |
9982 |
++ break ; |
9983 |
++#endif /* WITH_SELINUX */ |
9984 |
+ } |
9985 |
+ /* end of KIND_FORMAT case */ |
9986 |
+ break; |
9987 |
+@@ -1838,6 +1869,31 @@ pred_xtype (const char *pathname, struct |
9988 |
+ */ |
9989 |
+ return (pred_type (pathname, &sbuf, pred_ptr)); |
9990 |
+ } |
9991 |
+ |
9992 |
-+ for (p=predicates; p != NULL; p=p->pred_next) |
9993 |
-+ { |
9994 |
-+ /* All predicates must do something. */ |
9995 |
-+ assert (p->pred_func != NULL); |
9996 |
+ |
9997 |
-+ /* All predicates must have a parser table entry. */ |
9998 |
-+ assert (p->parser_entry != NULL); |
9999 |
-+ |
10000 |
-+ /* If the parser table tells us that just one predicate function is |
10001 |
-+ * possible, verify that that is still the one that is in effect. |
10002 |
-+ * If the parser has NULL for the predicate function, that means that |
10003 |
-+ * the parse_xxx function fills it in, so we can't check it. |
10004 |
-+ */ |
10005 |
-+ if (p->parser_entry->pred_func) |
10006 |
-+ { |
10007 |
-+ assert (p->parser_entry->pred_func == p->pred_func); |
10008 |
-+ } |
10009 |
-+ |
10010 |
-+ switch (p->parser_entry->type) |
10011 |
-+ { |
10012 |
-+ /* Options all take effect during parsing, so there should |
10013 |
-+ * be no predicate entries corresponding to them. Hence we |
10014 |
-+ * should not see any ARG_OPTION or ARG_POSITIONAL_OPTION |
10015 |
-+ * items. |
10016 |
-+ * |
10017 |
-+ * This is a silly way of coding this test, but it prevents |
10018 |
-+ * a compiler warning (i.e. otherwise it would think that |
10019 |
-+ * there would be case statements missing). |
10020 |
-+ */ |
10021 |
-+ case ARG_OPTION: |
10022 |
-+ case ARG_POSITIONAL_OPTION: |
10023 |
-+ assert (p->parser_entry->type != ARG_OPTION); |
10024 |
-+ assert (p->parser_entry->type != ARG_POSITIONAL_OPTION); |
10025 |
-+ break; |
10026 |
-+ |
10027 |
-+ case ARG_ACTION: |
10028 |
-+ assert(p->side_effects); /* actions have side effects. */ |
10029 |
-+ if (!pred_is(p, pred_prune) && !pred_is(p, pred_quit)) |
10030 |
-+ { |
10031 |
-+ /* actions other than -prune and -quit should |
10032 |
-+ * inhibit the default -print |
10033 |
-+ */ |
10034 |
-+ assert (p->no_default_print); |
10035 |
-+ } |
10036 |
-+ break; |
10037 |
++#ifdef WITH_SELINUX |
10038 |
+ |
10039 |
-+ /* We happen to know that the only user of ARG_SPECIAL_PARSE |
10040 |
-+ * is a test, so handle it like ARG_TEST. |
10041 |
-+ */ |
10042 |
-+ case ARG_SPECIAL_PARSE: |
10043 |
-+ case ARG_TEST: |
10044 |
-+ case ARG_PUNCTUATION: |
10045 |
-+ case ARG_NOOP: |
10046 |
-+ /* Punctuation and tests should have no side |
10047 |
-+ * effects and not inhibit default print. |
10048 |
-+ */ |
10049 |
-+ assert (!p->no_default_print); |
10050 |
-+ assert (!p->side_effects); |
10051 |
-+ break; |
10052 |
-+ } |
10053 |
-+ } |
10054 |
++boolean |
10055 |
++pred_scontext (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
10056 |
++{ |
10057 |
++ int rv; |
10058 |
++ security_context_t scontext; |
10059 |
++ |
10060 |
++ rv = (* options.x_getfilecon)(state.rel_pathname, &scontext); |
10061 |
++ |
10062 |
++ if ( rv < 0 ) { |
10063 |
++ (void) fprintf(stderr, "getfilecon(%s): %s\n", pathname, strerror(errno)); |
10064 |
++ (void) fflush(stderr); |
10065 |
++ return ( false ); |
10066 |
++ } |
10067 |
++ |
10068 |
++ rv = (fnmatch(pred_ptr->args.scontext, scontext,0)==0); |
10069 |
++ freecon(scontext); |
10070 |
++ return rv; |
10071 |
+} |
10072 |
-+#endif |
10073 |
++ |
10074 |
++#endif /*WITH_SELINUX*/ |
10075 |
++ |
10076 |
+ |
10077 |
+ /* 1) fork to get a child; parent remembers the child pid |
10078 |
+ 2) child execs the command requested |
10079 |
diff -purN findutils-4.3.12.orig/find/tree.c findutils-4.3.12/find/tree.c |
10080 |
--- findutils-4.3.12.orig/find/tree.c 2007-12-19 16:12:34.000000000 -0500 |
10081 |
+++ findutils-4.3.12/find/tree.c 2008-01-30 08:46:05.758843847 -0500 |
10082 |
|
10083 |
|
10084 |
|
10085 |
-- |
10086 |
gentoo-commits@l.g.o mailing list |