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