1 |
|
|
/* $OpenBSD: find.c,v 1.22 2017/01/04 09:21:26 tb Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1991, 1993 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* This code is derived from software contributed to Berkeley by |
8 |
|
|
* Cimarron D. Taylor of the University of California, Berkeley. |
9 |
|
|
* |
10 |
|
|
* Redistribution and use in source and binary forms, with or without |
11 |
|
|
* modification, are permitted provided that the following conditions |
12 |
|
|
* are met: |
13 |
|
|
* 1. Redistributions of source code must retain the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer. |
15 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
16 |
|
|
* notice, this list of conditions and the following disclaimer in the |
17 |
|
|
* documentation and/or other materials provided with the distribution. |
18 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
19 |
|
|
* may be used to endorse or promote products derived from this software |
20 |
|
|
* without specific prior written permission. |
21 |
|
|
* |
22 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 |
|
|
* SUCH DAMAGE. |
33 |
|
|
*/ |
34 |
|
|
|
35 |
|
|
#include <sys/types.h> |
36 |
|
|
#include <sys/stat.h> |
37 |
|
|
|
38 |
|
|
#include <err.h> |
39 |
|
|
#include <errno.h> |
40 |
|
|
#include <fts.h> |
41 |
|
|
#include <signal.h> |
42 |
|
|
#include <stdio.h> |
43 |
|
|
#include <string.h> |
44 |
|
|
#include <stdlib.h> |
45 |
|
|
#include <unistd.h> |
46 |
|
|
|
47 |
|
|
int mayexecve; |
48 |
|
|
|
49 |
|
|
#include "find.h" |
50 |
|
|
|
51 |
|
|
/* |
52 |
|
|
* find_formplan -- |
53 |
|
|
* process the command line and create a "plan" corresponding to the |
54 |
|
|
* command arguments. |
55 |
|
|
*/ |
56 |
|
|
PLAN * |
57 |
|
|
find_formplan(char **argv) |
58 |
|
|
{ |
59 |
|
|
PLAN *plan, *tail, *new; |
60 |
|
|
|
61 |
|
|
/* |
62 |
|
|
* for each argument in the command line, determine what kind of node |
63 |
|
|
* it is, create the appropriate node type and add the new plan node |
64 |
|
|
* to the end of the existing plan. The resulting plan is a linked |
65 |
|
|
* list of plan nodes. For example, the string: |
66 |
|
|
* |
67 |
|
|
* % find . -name foo -newer bar -print |
68 |
|
|
* |
69 |
|
|
* results in the plan: |
70 |
|
|
* |
71 |
|
|
* [-name foo]--> [-newer bar]--> [-print] |
72 |
|
|
* |
73 |
|
|
* in this diagram, `[-name foo]' represents the plan node generated |
74 |
|
|
* by c_name() with an argument of foo and `-->' represents the |
75 |
|
|
* plan->next pointer. |
76 |
|
|
*/ |
77 |
✓✓ |
126 |
for (plan = tail = NULL; *argv;) { |
78 |
✗✓ |
43 |
if (!(new = find_create(&argv))) |
79 |
|
|
continue; |
80 |
✓✓ |
43 |
if (plan == NULL) |
81 |
|
20 |
tail = plan = new; |
82 |
|
|
else { |
83 |
|
23 |
tail->next = new; |
84 |
|
|
tail = new; |
85 |
|
|
} |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
/* |
89 |
|
|
* if the user didn't specify one of -print, -ok or -exec, then -print |
90 |
|
|
* is assumed so we bracket the current expression with parens, if |
91 |
|
|
* necessary, and add a -print node on the end. |
92 |
|
|
*/ |
93 |
✗✓ |
20 |
if (!isoutput) { |
94 |
|
|
if (plan == NULL) { |
95 |
|
|
new = c_print(NULL, NULL, 0); |
96 |
|
|
tail = plan = new; |
97 |
|
|
} else { |
98 |
|
|
new = c_openparen(NULL, NULL, 0); |
99 |
|
|
new->next = plan; |
100 |
|
|
plan = new; |
101 |
|
|
new = c_closeparen(NULL, NULL, 0); |
102 |
|
|
tail->next = new; |
103 |
|
|
tail = new; |
104 |
|
|
new = c_print(NULL, NULL, 0); |
105 |
|
|
tail->next = new; |
106 |
|
|
tail = new; |
107 |
|
|
} |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
/* |
111 |
|
|
* the command line has been completely processed into a search plan |
112 |
|
|
* except for the (, ), !, and -o operators. Rearrange the plan so |
113 |
|
|
* that the portions of the plan which are affected by the operators |
114 |
|
|
* are moved into operator nodes themselves. For example: |
115 |
|
|
* |
116 |
|
|
* [!]--> [-name foo]--> [-print] |
117 |
|
|
* |
118 |
|
|
* becomes |
119 |
|
|
* |
120 |
|
|
* [! [-name foo] ]--> [-print] |
121 |
|
|
* |
122 |
|
|
* and |
123 |
|
|
* |
124 |
|
|
* [(]--> [-depth]--> [-name foo]--> [)]--> [-print] |
125 |
|
|
* |
126 |
|
|
* becomes |
127 |
|
|
* |
128 |
|
|
* [expr [-depth]-->[-name foo] ]--> [-print] |
129 |
|
|
* |
130 |
|
|
* operators are handled in order of precedence. |
131 |
|
|
*/ |
132 |
|
|
|
133 |
|
20 |
plan = paren_squish(plan); /* ()'s */ |
134 |
|
20 |
plan = not_squish(plan); /* !'s */ |
135 |
|
20 |
plan = or_squish(plan); /* -o's */ |
136 |
|
20 |
return (plan); |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
FTS *tree; /* pointer to top of FTS hierarchy */ |
140 |
|
|
|
141 |
|
|
/* |
142 |
|
|
* find_execute -- |
143 |
|
|
* take a search plan and an array of search paths and executes the plan |
144 |
|
|
* over all FTSENT's returned for the given search paths. |
145 |
|
|
*/ |
146 |
|
|
|
147 |
|
|
FTSENT *entry; /* shared with SIGINFO handler */ |
148 |
|
|
|
149 |
|
|
int |
150 |
|
|
find_execute(PLAN *plan, /* search plan */ |
151 |
|
|
char **paths) /* array of pathnames to traverse */ |
152 |
|
|
{ |
153 |
|
40 |
sigset_t fullset, oset; |
154 |
|
|
int r, rval; |
155 |
|
|
PLAN *p; |
156 |
|
|
|
157 |
✓✓ |
20 |
if (mayexecve == 0) { |
158 |
✗✓ |
18 |
if (isdelete) { |
159 |
|
|
if (pledge("stdio rpath cpath getpw flock wpath", NULL) == -1) |
160 |
|
|
err(1, "pledge"); |
161 |
|
|
} else { |
162 |
✗✓ |
18 |
if (pledge("stdio rpath getpw flock cpath wpath", NULL) == -1) |
163 |
|
|
err(1, "pledge"); |
164 |
|
|
} |
165 |
|
|
} else { |
166 |
✗✓ |
2 |
if (isdelete) { |
167 |
|
|
if (pledge("stdio rpath cpath getpw proc exec flock wpath", NULL) |
168 |
|
|
== -1) |
169 |
|
|
err(1, "pledge"); |
170 |
|
|
} else { |
171 |
✗✓ |
2 |
if (pledge("stdio rpath getpw proc exec flock cpath wpath", NULL) == -1) |
172 |
|
|
err(1, "pledge"); |
173 |
|
|
} |
174 |
|
|
} |
175 |
|
|
|
176 |
|
|
rval = 0; |
177 |
|
|
|
178 |
✗✓ |
20 |
if (!(tree = fts_open(paths, ftsoptions, NULL))) |
179 |
|
|
err(1, "fts_open"); |
180 |
|
|
|
181 |
|
20 |
sigfillset(&fullset); |
182 |
|
5387 |
for (;;) { |
183 |
|
5511 |
(void)sigprocmask(SIG_BLOCK, &fullset, &oset); |
184 |
|
5511 |
entry = fts_read(tree); |
185 |
|
5511 |
(void)sigprocmask(SIG_SETMASK, &oset, NULL); |
186 |
✓✓ |
5511 |
if (entry == NULL) { |
187 |
✗✓ |
20 |
if (errno) |
188 |
|
|
err(1, "fts_read"); |
189 |
|
|
break; |
190 |
|
|
} |
191 |
|
|
|
192 |
✓✓✗✗ ✗✓ |
5615 |
switch (entry->fts_info) { |
193 |
|
|
case FTS_D: |
194 |
✗✓ |
124 |
if (isdepth) |
195 |
|
|
continue; |
196 |
|
|
break; |
197 |
|
|
case FTS_DP: |
198 |
✓✗ |
124 |
if (!isdepth) |
199 |
|
124 |
continue; |
200 |
|
|
break; |
201 |
|
|
case FTS_DNR: |
202 |
|
|
case FTS_ERR: |
203 |
|
|
case FTS_NS: |
204 |
|
|
(void)fflush(stdout); |
205 |
|
|
warnc(entry->fts_errno, "%s", entry->fts_path); |
206 |
|
|
rval = 1; |
207 |
|
|
continue; |
208 |
|
|
} |
209 |
|
|
#define BADCH " \t\n\\'\"" |
210 |
✗✓✗✗
|
5367 |
if (isxargs && strpbrk(entry->fts_path, BADCH)) { |
211 |
|
|
(void)fflush(stdout); |
212 |
|
|
warnx("%s: illegal path", entry->fts_path); |
213 |
|
|
rval = 1; |
214 |
|
|
continue; |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
/* |
218 |
|
|
* Call all the functions in the execution plan until one is |
219 |
|
|
* false or all have been executed. This is where we do all |
220 |
|
|
* the work specified by the user on the command line. |
221 |
|
|
*/ |
222 |
✓✓✓✓
|
37096 |
for (p = plan; p && (p->eval)(p, entry); p = p->next) |
223 |
|
|
; |
224 |
|
|
} |
225 |
|
20 |
(void)fts_close(tree); |
226 |
|
|
|
227 |
|
|
/* |
228 |
|
|
* Cleanup any plans with leftover state. |
229 |
|
|
* Keep the last non-zero return value. |
230 |
|
|
*/ |
231 |
✗✓ |
20 |
if ((r = find_traverse(plan, plan_cleanup, NULL)) != 0) |
232 |
|
|
rval = r; |
233 |
|
20 |
return (rval); |
234 |
|
20 |
} |
235 |
|
|
|
236 |
|
|
/* |
237 |
|
|
* find_traverse -- |
238 |
|
|
* traverse the plan tree and execute func() on all plans. This |
239 |
|
|
* does not evaluate each plan's eval() function; it is intended |
240 |
|
|
* for operations that must run on all plans, such as state |
241 |
|
|
* cleanup. |
242 |
|
|
* |
243 |
|
|
* If any func() returns non-zero, then so will find_traverse(). |
244 |
|
|
*/ |
245 |
|
|
int |
246 |
|
|
find_traverse(PLAN *plan, int (*func)(PLAN *, void *), void *arg) |
247 |
|
|
{ |
248 |
|
|
PLAN *p; |
249 |
|
|
int r, rval; |
250 |
|
|
|
251 |
|
|
rval = 0; |
252 |
✓✓ |
152 |
for (p = plan; p; p = p->next) { |
253 |
✗✓ |
43 |
if ((r = func(p, arg)) != 0) |
254 |
|
|
rval = r; |
255 |
✓✗✓✓
|
86 |
if (p->type == N_EXPR || p->type == N_OR) { |
256 |
✓✗ |
1 |
if (p->p_data[0]) |
257 |
✗✓ |
2 |
if ((r = find_traverse(p->p_data[0], |
258 |
|
1 |
func, arg)) != 0) |
259 |
|
|
rval = r; |
260 |
✓✗ |
1 |
if (p->p_data[1]) |
261 |
✗✓ |
2 |
if ((r = find_traverse(p->p_data[1], |
262 |
|
1 |
func, arg)) != 0) |
263 |
|
|
rval = r; |
264 |
|
|
} |
265 |
|
|
} |
266 |
|
22 |
return rval; |
267 |
|
|
} |