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