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