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