]> git.saurik.com Git - apple/shell_cmds.git/blame_incremental - find/find.c
shell_cmds-149.tar.gz
[apple/shell_cmds.git] / find / find.c
... / ...
CommitLineData
1/*-
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Cimarron D. Taylor of the University of California, Berkeley.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)find.c 8.5 (Berkeley) 8/5/94";
40#else
41#endif
42#endif /* not lint */
43
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: src/usr.bin/find/find.c,v 1.18 2006/05/14 20:23:00 krion Exp $");
46
47#include <sys/types.h>
48#include <sys/stat.h>
49
50#include <err.h>
51#include <errno.h>
52#include <fts.h>
53#include <regex.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57
58#ifdef __APPLE__
59#include <get_compat.h>
60#include <unistd.h>
61#else
62#define COMPAT_MODE(func, mode) 1
63#endif
64
65#include "find.h"
66
67#ifdef __APPLE__
68static int find_compare(const FTSENT **s1, const FTSENT **s2);
69#else /* !__APPLE__ */
70static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2);
71#endif /* __APPLE__ */
72
73/*
74 * find_compare --
75 * tell fts_open() how to order the traversal of the hierarchy.
76 * This variant gives lexicographical order, i.e., alphabetical
77 * order within each directory.
78 */
79static int
80#ifdef __APPLE__
81find_compare(const FTSENT **s1, const FTSENT **s2)
82#else /* !__APPLE__ */
83find_compare(const FTSENT * const *s1, const FTSENT * const *s2)
84#endif /* __APPLE__ */
85{
86
87 return (strcoll((*s1)->fts_name, (*s2)->fts_name));
88}
89
90/*
91 * find_formplan --
92 * process the command line and create a "plan" corresponding to the
93 * command arguments.
94 */
95PLAN *
96find_formplan(char *argv[])
97{
98 PLAN *plan, *tail, *new;
99
100 /*
101 * for each argument in the command line, determine what kind of node
102 * it is, create the appropriate node type and add the new plan node
103 * to the end of the existing plan. The resulting plan is a linked
104 * list of plan nodes. For example, the string:
105 *
106 * % find . -name foo -newer bar -print
107 *
108 * results in the plan:
109 *
110 * [-name foo]--> [-newer bar]--> [-print]
111 *
112 * in this diagram, `[-name foo]' represents the plan node generated
113 * by c_name() with an argument of foo and `-->' represents the
114 * plan->next pointer.
115 */
116 for (plan = tail = NULL; *argv;) {
117 if (!(new = find_create(&argv)))
118 continue;
119 if (plan == NULL)
120 tail = plan = new;
121 else {
122 tail->next = new;
123 tail = new;
124 }
125 }
126
127 /*
128 * if the user didn't specify one of -print, -ok or -exec, then -print
129 * is assumed so we bracket the current expression with parens, if
130 * necessary, and add a -print node on the end.
131 */
132 if (!isoutput) {
133 OPTION *p;
134 char **argv1 = 0;
135
136 if (plan == NULL) {
137 p = lookup_option("-print");
138 new = (p->create)(p, &argv1);
139 tail = plan = new;
140 } else {
141 p = lookup_option("(");
142 new = (p->create)(p, &argv1);
143 new->next = plan;
144 plan = new;
145 p = lookup_option(")");
146 new = (p->create)(p, &argv1);
147 tail->next = new;
148 tail = new;
149 p = lookup_option("-print");
150 new = (p->create)(p, &argv1);
151 tail->next = new;
152 tail = new;
153 }
154 }
155
156 /*
157 * the command line has been completely processed into a search plan
158 * except for the (, ), !, and -o operators. Rearrange the plan so
159 * that the portions of the plan which are affected by the operators
160 * are moved into operator nodes themselves. For example:
161 *
162 * [!]--> [-name foo]--> [-print]
163 *
164 * becomes
165 *
166 * [! [-name foo] ]--> [-print]
167 *
168 * and
169 *
170 * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
171 *
172 * becomes
173 *
174 * [expr [-depth]-->[-name foo] ]--> [-print]
175 *
176 * operators are handled in order of precedence.
177 */
178
179 plan = paren_squish(plan); /* ()'s */
180 plan = not_squish(plan); /* !'s */
181 plan = or_squish(plan); /* -o's */
182 return (plan);
183}
184
185/* addPath - helper function used to build a list of paths that were
186 * specified on the command line that we are allowed to search.
187 */
188static char **addPath(char **array, char *newPath)
189{
190 static int pathCounter = 0;
191
192 if (newPath == NULL) { /* initialize array */
193 if ((array = malloc(sizeof(char *))) == NULL)
194 err(2, "out of memory");
195 array[0] = NULL;
196 }
197 else {
198 array = realloc(array, (++pathCounter + 1) * sizeof(char *));
199 if (array == NULL)
200 err(2, "out of memory");
201 else {
202 array[pathCounter - 1] = newPath;
203 array[pathCounter] = NULL; /* ensure array is null terminated */
204 }
205 }
206 return (array);
207}
208
209FTS *tree; /* pointer to top of FTS hierarchy */
210
211/*
212 * find_execute --
213 * take a search plan and an array of search paths and executes the plan
214 * over all FTSENT's returned for the given search paths.
215 */
216int
217find_execute(PLAN *plan, char *paths[])
218{
219 FTSENT *entry;
220 PLAN *p;
221 int rval;
222 char **myPaths;
223 int nonSearchableDirFound = 0;
224 int pathIndex;
225 struct stat statInfo;
226
227 /* special-case directories specified on command line - explicitly examine
228 * mode bits, to ensure failure if the directory cannot be searched
229 * (whether or not it's empty). UNIX conformance... <sigh>
230 */
231
232 int strict_symlinks = (ftsoptions & (FTS_COMFOLLOW|FTS_LOGICAL))
233 && COMPAT_MODE("bin/find", "unix2003");
234
235 myPaths = addPath(NULL, NULL);
236 for (pathIndex = 0; paths[pathIndex] != NULL; ++pathIndex) {
237 int stat_ret = stat(paths[pathIndex], &statInfo);
238 int stat_errno = errno;
239 if (strict_symlinks && stat_ret < 0) {
240 if (stat_errno == ELOOP) {
241 errx(1, "Symlink loop resolving %s", paths[pathIndex]);
242 }
243 }
244
245 /* retrieve mode bits, and examine "searchable" bit of
246 directories, exempt root from POSIX conformance */
247 if (COMPAT_MODE("bin/find", "unix2003") && getuid()
248 && stat_ret == 0
249 && ((statInfo.st_mode & S_IFMT) == S_IFDIR)) {
250 if (access(paths[pathIndex], X_OK) == 0) {
251 myPaths = addPath(myPaths, paths[pathIndex]);
252 } else {
253 if (stat_errno != ENAMETOOLONG) { /* if name is too long, just let existing logic handle it */
254 warnx("%s: Permission denied", paths[pathIndex]);
255 nonSearchableDirFound = 1;
256 }
257 }
258 } else {
259 /* not a directory, so add path to array */
260 myPaths = addPath(myPaths, paths[pathIndex]);
261 }
262 }
263 if (myPaths[0] == NULL) { /* were any directories searchable? */
264 free(myPaths);
265 return(nonSearchableDirFound); /* no... */
266 }
267
268 tree = fts_open(myPaths, ftsoptions, (issort ? find_compare : NULL));
269 if (tree == NULL)
270 err(1, "ftsopen");
271
272 for (rval = nonSearchableDirFound; (entry = fts_read(tree)) != NULL;) {
273 if (maxdepth != -1 && entry->fts_level >= maxdepth) {
274 if (fts_set(tree, entry, FTS_SKIP))
275 err(1, "%s", entry->fts_path);
276 }
277
278 switch (entry->fts_info) {
279 case FTS_D:
280 if (isdepth)
281 continue;
282 break;
283 case FTS_DP:
284 if (!isdepth)
285 continue;
286 break;
287 case FTS_DNR:
288 case FTS_ERR:
289 case FTS_NS:
290 (void)fflush(stdout);
291 warnx("%s: %s",
292 entry->fts_path, strerror(entry->fts_errno));
293 rval = 1;
294 continue;
295#ifdef FTS_W
296 case FTS_W:
297 continue;
298#endif /* FTS_W */
299 }
300#define BADCH " \t\n\\'\""
301 if (isxargs && strpbrk(entry->fts_path, BADCH)) {
302 (void)fflush(stdout);
303 warnx("%s: illegal path", entry->fts_path);
304 rval = 1;
305 continue;
306 }
307
308 if (mindepth != -1 && entry->fts_level < mindepth)
309 continue;
310
311 /*
312 * Call all the functions in the execution plan until one is
313 * false or all have been executed. This is where we do all
314 * the work specified by the user on the command line.
315 */
316 for (p = plan; p && (p->execute)(p, entry); p = p->next);
317 }
318 free (myPaths);
319 finish_execplus();
320 if (errno)
321 err(1, "fts_read");
322 fts_close(tree);
323 return (rval);
324}