1*cc610a3cSpgoyette /* $NetBSD: find.c,v 1.30 2016/06/13 00:04:40 pgoyette Exp $ */
29d225a17Stls
361f28255Scgd /*-
47b983ca6Smrg * Copyright (c) 1991, 1993, 1994
53dfa59ecSjtc * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * This code is derived from software contributed to Berkeley by
861f28255Scgd * Cimarron D. Taylor of the University of California, Berkeley.
961f28255Scgd *
1061f28255Scgd * Redistribution and use in source and binary forms, with or without
1161f28255Scgd * modification, are permitted provided that the following conditions
1261f28255Scgd * are met:
1361f28255Scgd * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd * notice, this list of conditions and the following disclaimer.
1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd * notice, this list of conditions and the following disclaimer in the
1761f28255Scgd * documentation and/or other materials provided with the distribution.
1889aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd * may be used to endorse or promote products derived from this software
2061f28255Scgd * without specific prior written permission.
2161f28255Scgd *
2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd * SUCH DAMAGE.
3361f28255Scgd */
3461f28255Scgd
35403b699bSlukem #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
37403b699bSlukem #if 0
387b983ca6Smrg static char sccsid[] = "from: @(#)find.c 8.5 (Berkeley) 8/5/94";
39403b699bSlukem #else
40*cc610a3cSpgoyette __RCSID("$NetBSD: find.c,v 1.30 2016/06/13 00:04:40 pgoyette Exp $");
41403b699bSlukem #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd
4461f28255Scgd #include <sys/types.h>
4561f28255Scgd #include <sys/stat.h>
463dfa59ecSjtc
473dfa59ecSjtc #include <err.h>
483dfa59ecSjtc #include <errno.h>
4961f28255Scgd #include <fts.h>
501db82cf8Slukem #include <signal.h>
5161f28255Scgd #include <stdio.h>
5261f28255Scgd #include <string.h>
5361f28255Scgd #include <stdlib.h>
54a6dac2b7Schristos #include <stdbool.h>
55a6dac2b7Schristos #include <unistd.h>
563dfa59ecSjtc
5761f28255Scgd #include "find.h"
5861f28255Scgd
592eed134bSapb static int ftscompare(const FTSENT **, const FTSENT **);
606baa6f27Sitohy
6161f28255Scgd /*
6261f28255Scgd * find_formplan --
6361f28255Scgd * process the command line and create a "plan" corresponding to the
6461f28255Scgd * command arguments.
6561f28255Scgd */
6661f28255Scgd PLAN *
find_formplan(char ** argv)672eed134bSapb find_formplan(char **argv)
6861f28255Scgd {
6961f28255Scgd PLAN *plan, *tail, *new;
7061f28255Scgd
7161f28255Scgd /*
7261f28255Scgd * for each argument in the command line, determine what kind of node
7361f28255Scgd * it is, create the appropriate node type and add the new plan node
7461f28255Scgd * to the end of the existing plan. The resulting plan is a linked
7561f28255Scgd * list of plan nodes. For example, the string:
7661f28255Scgd *
7761f28255Scgd * % find . -name foo -newer bar -print
7861f28255Scgd *
7961f28255Scgd * results in the plan:
8061f28255Scgd *
8161f28255Scgd * [-name foo]--> [-newer bar]--> [-print]
8261f28255Scgd *
8361f28255Scgd * in this diagram, `[-name foo]' represents the plan node generated
8461f28255Scgd * by c_name() with an argument of foo and `-->' represents the
8561f28255Scgd * plan->next pointer.
8661f28255Scgd */
873dfa59ecSjtc for (plan = tail = NULL; *argv;) {
8861f28255Scgd if (!(new = find_create(&argv)))
8961f28255Scgd continue;
9061f28255Scgd if (plan == NULL)
9161f28255Scgd tail = plan = new;
9261f28255Scgd else {
9361f28255Scgd tail->next = new;
9461f28255Scgd tail = new;
9561f28255Scgd }
9661f28255Scgd }
9761f28255Scgd
9861f28255Scgd /*
99a32f19d4Sjschauma * if the user didn't specify one of -print, -ok, -fprint, -exec, or
100a32f19d4Sjschauma * -exit, then -print is assumed so we bracket the current expression
101a32f19d4Sjschauma * with parens, if necessary, and add a -print node on the end.
10261f28255Scgd */
10361f28255Scgd if (!isoutput) {
104d731b5b8Scgd if (plan == NULL) {
105*cc610a3cSpgoyette new = c_print(NULL, 0, NULL);
10661f28255Scgd tail = plan = new;
107d731b5b8Scgd } else {
108*cc610a3cSpgoyette new = c_openparen(NULL, 0, NULL);
109d731b5b8Scgd new->next = plan;
110d731b5b8Scgd plan = new;
111*cc610a3cSpgoyette new = c_closeparen(NULL, 0, NULL);
112d731b5b8Scgd tail->next = new;
113d731b5b8Scgd tail = new;
114*cc610a3cSpgoyette new = c_print(NULL, 0, NULL);
11561f28255Scgd tail->next = new;
11661f28255Scgd tail = new;
11761f28255Scgd }
11861f28255Scgd }
11961f28255Scgd
12061f28255Scgd /*
12161f28255Scgd * the command line has been completely processed into a search plan
12261f28255Scgd * except for the (, ), !, and -o operators. Rearrange the plan so
12361f28255Scgd * that the portions of the plan which are affected by the operators
12461f28255Scgd * are moved into operator nodes themselves. For example:
12561f28255Scgd *
12661f28255Scgd * [!]--> [-name foo]--> [-print]
12761f28255Scgd *
12861f28255Scgd * becomes
12961f28255Scgd *
13061f28255Scgd * [! [-name foo] ]--> [-print]
13161f28255Scgd *
13261f28255Scgd * and
13361f28255Scgd *
13461f28255Scgd * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
13561f28255Scgd *
13661f28255Scgd * becomes
13761f28255Scgd *
13861f28255Scgd * [expr [-depth]-->[-name foo] ]--> [-print]
13961f28255Scgd *
14061f28255Scgd * operators are handled in order of precedence.
14161f28255Scgd */
14261f28255Scgd
14361f28255Scgd plan = paren_squish(plan); /* ()'s */
14461f28255Scgd plan = not_squish(plan); /* !'s */
14561f28255Scgd plan = or_squish(plan); /* -o's */
14661f28255Scgd return (plan);
14761f28255Scgd }
14861f28255Scgd
1496baa6f27Sitohy static int
ftscompare(const FTSENT ** e1,const FTSENT ** e2)1502eed134bSapb ftscompare(const FTSENT **e1, const FTSENT **e2)
1516baa6f27Sitohy {
1527b4bdbc1Senami
1537b4bdbc1Senami return (strcoll((*e1)->fts_name, (*e2)->fts_name));
1546baa6f27Sitohy }
1556baa6f27Sitohy
156a6dac2b7Schristos static sigset_t ss;
157a6dac2b7Schristos static bool notty;
1580c0dee33Syamt
159a6dac2b7Schristos static __inline void
sig_init(void)160a6dac2b7Schristos sig_init(void)
161a6dac2b7Schristos {
162d6d145eeSchristos struct sigaction sa;
163a6dac2b7Schristos notty = !(isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) ||
164a6dac2b7Schristos isatty(STDERR_FILENO));
165a6dac2b7Schristos if (notty)
166a6dac2b7Schristos return;
167a6dac2b7Schristos sigemptyset(&ss);
168a6dac2b7Schristos sigaddset(&ss, SIGINFO); /* block SIGINFO */
169d6d145eeSchristos
170d6d145eeSchristos memset(&sa, 0, sizeof(sa));
171d6d145eeSchristos sa.sa_flags = SA_RESTART;
172d6d145eeSchristos sa.sa_handler = show_path;
173d6d145eeSchristos (void)sigaction(SIGINFO, &sa, NULL);
174d6d145eeSchristos
1750c0dee33Syamt }
1760c0dee33Syamt
177a6dac2b7Schristos static __inline void
sig_lock(sigset_t * s)178a6dac2b7Schristos sig_lock(sigset_t *s)
179a6dac2b7Schristos {
180a6dac2b7Schristos if (notty)
181a6dac2b7Schristos return;
182a6dac2b7Schristos sigprocmask(SIG_BLOCK, &ss, s);
183a6dac2b7Schristos }
184a6dac2b7Schristos
185a6dac2b7Schristos static __inline void
sig_unlock(const sigset_t * s)1862eed134bSapb sig_unlock(const sigset_t *s)
1870c0dee33Syamt {
188a6dac2b7Schristos if (notty)
189a6dac2b7Schristos return;
1900c0dee33Syamt sigprocmask(SIG_SETMASK, s, NULL);
1910c0dee33Syamt }
1920c0dee33Syamt
19361f28255Scgd FTS *tree; /* pointer to top of FTS hierarchy */
1949847a814Syamt FTSENT *g_entry; /* shared with SIGINFO handler */
19561f28255Scgd
19661f28255Scgd /*
19761f28255Scgd * find_execute --
19861f28255Scgd * take a search plan and an array of search paths and executes the plan
19961f28255Scgd * over all FTSENT's returned for the given search paths.
20061f28255Scgd */
2017b983ca6Smrg int
find_execute(PLAN * plan,char ** paths)2022eed134bSapb find_execute(PLAN *plan, char **paths)
20361f28255Scgd {
20461f28255Scgd PLAN *p;
205b90dcb5dSapb int r, rval, cval;
2060c0dee33Syamt sigset_t s;
20761f28255Scgd
208a32f19d4Sjschauma cval = 1;
209a32f19d4Sjschauma
2106baa6f27Sitohy if (!(tree = fts_open(paths, ftsoptions, issort ? ftscompare : NULL)))
2113dfa59ecSjtc err(1, "ftsopen");
21261f28255Scgd
213a6dac2b7Schristos sig_init();
2140c0dee33Syamt sig_lock(&s);
215a6dac2b7Schristos for (rval = 0; cval && (g_entry = fts_read(tree)) != NULL;) {
2169847a814Syamt switch (g_entry->fts_info) {
21761f28255Scgd case FTS_D:
21861f28255Scgd if (isdepth)
21961f28255Scgd continue;
22061f28255Scgd break;
22161f28255Scgd case FTS_DP:
22261f28255Scgd if (!isdepth)
22361f28255Scgd continue;
22461f28255Scgd break;
22561f28255Scgd case FTS_DNR:
22661f28255Scgd case FTS_ERR:
22761f28255Scgd case FTS_NS:
228a6dac2b7Schristos sig_unlock(&s);
2293dfa59ecSjtc (void)fflush(stdout);
2307b983ca6Smrg warnx("%s: %s",
2319847a814Syamt g_entry->fts_path, strerror(g_entry->fts_errno));
2327b983ca6Smrg rval = 1;
233a6dac2b7Schristos sig_lock(&s);
23461f28255Scgd continue;
23561f28255Scgd }
23661f28255Scgd #define BADCH " \t\n\\'\""
2379847a814Syamt if (isxargs && strpbrk(g_entry->fts_path, BADCH)) {
238a6dac2b7Schristos sig_unlock(&s);
2393dfa59ecSjtc (void)fflush(stdout);
2409847a814Syamt warnx("%s: illegal path", g_entry->fts_path);
2417b983ca6Smrg rval = 1;
242a6dac2b7Schristos sig_lock(&s);
24361f28255Scgd continue;
24461f28255Scgd }
24561f28255Scgd
24661f28255Scgd /*
2477b983ca6Smrg * Call all the functions in the execution plan until one is
24861f28255Scgd * false or all have been executed. This is where we do all
24961f28255Scgd * the work specified by the user on the command line.
25061f28255Scgd */
251a6dac2b7Schristos sig_unlock(&s);
2529847a814Syamt for (p = plan; p && (p->eval)(p, g_entry); p = p->next)
253a32f19d4Sjschauma if (p->type == N_EXIT) {
254a32f19d4Sjschauma rval = p->exit_val;
255a32f19d4Sjschauma cval = 0;
25661f28255Scgd }
257a6dac2b7Schristos sig_lock(&s);
258a32f19d4Sjschauma }
259a32f19d4Sjschauma
2600c0dee33Syamt sig_unlock(&s);
26183889b37Sdholland if (g_entry == NULL && errno)
2627b983ca6Smrg err(1, "fts_read");
26361f28255Scgd (void)fts_close(tree);
264b90dcb5dSapb
265b90dcb5dSapb /*
266b90dcb5dSapb * Cleanup any plans with leftover state.
267b90dcb5dSapb * Keep the last non-zero return value.
268b90dcb5dSapb */
269b90dcb5dSapb if ((r = find_traverse(plan, plan_cleanup, NULL)) != 0)
270b90dcb5dSapb rval = r;
271b90dcb5dSapb
2727b983ca6Smrg return (rval);
27361f28255Scgd }
274b90dcb5dSapb
275b90dcb5dSapb /*
276b90dcb5dSapb * find_traverse --
277b90dcb5dSapb * traverse the plan tree and execute func() on all plans. This
278b90dcb5dSapb * does not evaluate each plan's eval() function; it is intended
279b90dcb5dSapb * for operations that must run on all plans, such as state
280b90dcb5dSapb * cleanup.
281b90dcb5dSapb *
282b90dcb5dSapb * If any func() returns non-zero, then so will find_traverse().
283b90dcb5dSapb */
284b90dcb5dSapb int
find_traverse(PLAN * plan,int (* func)(PLAN *,void *),void * arg)285d34c2845Smatt find_traverse(PLAN *plan, int (*func)(PLAN *, void *), void *arg)
286b90dcb5dSapb {
287b90dcb5dSapb PLAN *p;
288b90dcb5dSapb int r, rval;
289b90dcb5dSapb
290b90dcb5dSapb rval = 0;
291b90dcb5dSapb for (p = plan; p; p = p->next) {
292b90dcb5dSapb if ((r = func(p, arg)) != 0)
293b90dcb5dSapb rval = r;
294b90dcb5dSapb if (p->type == N_EXPR || p->type == N_OR) {
295b90dcb5dSapb if (p->p_data[0])
296b90dcb5dSapb if ((r = find_traverse(p->p_data[0],
297b90dcb5dSapb func, arg)) != 0)
298b90dcb5dSapb rval = r;
299b90dcb5dSapb if (p->p_data[1])
300b90dcb5dSapb if ((r = find_traverse(p->p_data[1],
301b90dcb5dSapb func, arg)) != 0)
302b90dcb5dSapb rval = r;
303b90dcb5dSapb }
304b90dcb5dSapb }
305b90dcb5dSapb return rval;
306b90dcb5dSapb }
307