xref: /netbsd-src/usr.bin/find/find.c (revision cc610a3c945563f04e0dcf6e3e40263ea4fe07c8)
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