find.c revision 97736
11558Srgrimes/*-
21558Srgrimes * Copyright (c) 1991, 1993, 1994
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Cimarron D. Taylor of the University of California, Berkeley.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 3. All advertising materials mentioning features or use of this software
171558Srgrimes *    must display the following acknowledgement:
181558Srgrimes *	This product includes software developed by the University of
191558Srgrimes *	California, Berkeley and its contributors.
201558Srgrimes * 4. Neither the name of the University nor the names of its contributors
211558Srgrimes *    may be used to endorse or promote products derived from this software
221558Srgrimes *    without specific prior written permission.
231558Srgrimes *
241558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341558Srgrimes * SUCH DAMAGE.
3538040Scharnier */
361558Srgrimes
371558Srgrimes#ifndef lint
381558Srgrimes#if 0
391558Srgrimesstatic char sccsid[] = "@(#)find.c	8.5 (Berkeley) 8/5/94";
401558Srgrimes#else
4138040Scharnier#endif
421558Srgrimes#endif /* not lint */
4338040Scharnier#include <sys/cdefs.h>
4438040Scharnier__FBSDID("$FreeBSD: head/usr.bin/find/find.c 97736 2002-06-02 12:57:41Z tjr $");
4542873Sluoqi
461558Srgrimes#include <sys/types.h>
471558Srgrimes#include <sys/stat.h>
481558Srgrimes
491558Srgrimes#include <err.h>
501558Srgrimes#include <errno.h>
511558Srgrimes#include <fts.h>
5242873Sluoqi#include <regex.h>
531558Srgrimes#include <stdio.h>
541558Srgrimes#include <string.h>
551558Srgrimes#include <stdlib.h>
5642873Sluoqi
571558Srgrimes#include "find.h"
581558Srgrimes
591558Srgrimesstatic int	find_compare(const FTSENT **s1, const FTSENT **s2);
601558Srgrimes
6138040Scharnier/*
621558Srgrimes * find_compare --
631558Srgrimes *	tell fts_open() how to order the traversal of the hierarchy.
6442873Sluoqi *	This variant gives lexicographical order, i.e., alphabetical
651558Srgrimes *	order within each directory.
661558Srgrimes */
671558Srgrimesstatic int
681558Srgrimesfind_compare(s1, s2)
691558Srgrimes	const FTSENT **s1, **s2;
701558Srgrimes{
711558Srgrimes
721558Srgrimes	return (strcoll((*s1)->fts_name, (*s2)->fts_name));
731558Srgrimes}
741558Srgrimes
751558Srgrimes/*
761558Srgrimes * find_formplan --
771558Srgrimes *	process the command line and create a "plan" corresponding to the
781558Srgrimes *	command arguments.
791558Srgrimes */
801558SrgrimesPLAN *
811558Srgrimesfind_formplan(argv)
821558Srgrimes	char **argv;
839315Sjoerg{
8442873Sluoqi	PLAN *plan, *tail, *new;
851558Srgrimes
861558Srgrimes	/*
871558Srgrimes	 * for each argument in the command line, determine what kind of node
881558Srgrimes	 * it is, create the appropriate node type and add the new plan node
891558Srgrimes	 * to the end of the existing plan.  The resulting plan is a linked
901558Srgrimes	 * list of plan nodes.  For example, the string:
9134266Sjulian	 *
921558Srgrimes	 *	% find . -name foo -newer bar -print
931558Srgrimes	 *
9442873Sluoqi	 * results in the plan:
951558Srgrimes	 *
961558Srgrimes	 *	[-name foo]--> [-newer bar]--> [-print]
9742873Sluoqi	 *
9842873Sluoqi	 * in this diagram, `[-name foo]' represents the plan node generated
991558Srgrimes	 * by c_name() with an argument of foo and `-->' represents the
1008871Srgrimes	 * plan->next pointer.
1011558Srgrimes	 */
1021558Srgrimes	for (plan = tail = NULL; *argv;) {
1031558Srgrimes		if (!(new = find_create(&argv)))
1041558Srgrimes			continue;
10542873Sluoqi		if (plan == NULL)
10642873Sluoqi			tail = plan = new;
10742873Sluoqi		else {
10842873Sluoqi			tail->next = new;
10942873Sluoqi			tail = new;
11042873Sluoqi		}
11142873Sluoqi	}
11242873Sluoqi
11342873Sluoqi	/*
11442873Sluoqi	 * if the user didn't specify one of -print, -ok or -exec, then -print
1151558Srgrimes	 * is assumed so we bracket the current expression with parens, if
1161558Srgrimes	 * necessary, and add a -print node on the end.
1171558Srgrimes	 */
1181558Srgrimes	if (!isoutput) {
1191558Srgrimes		OPTION *p;
1201558Srgrimes		char **argv1 = 0;
1211558Srgrimes
1221558Srgrimes		if (plan == NULL) {
1231558Srgrimes			p = lookup_option("-print");
1241558Srgrimes			new = (p->create)(p, &argv1);
1251558Srgrimes			tail = plan = new;
1261558Srgrimes		} else {
1271558Srgrimes			p = lookup_option("(");
1281558Srgrimes			new = (p->create)(p, &argv1);
1291558Srgrimes			new->next = plan;
1301558Srgrimes			plan = new;
1311558Srgrimes			p = lookup_option(")");
1321558Srgrimes			new = (p->create)(p, &argv1);
1331558Srgrimes			tail->next = new;
1341558Srgrimes			tail = new;
1351558Srgrimes			p = lookup_option("-print");
1361558Srgrimes			new = (p->create)(p, &argv1);
1371558Srgrimes			tail->next = new;
1389315Sjoerg			tail = new;
1399315Sjoerg		}
1409315Sjoerg	}
1419315Sjoerg
1421558Srgrimes	/*
1431558Srgrimes	 * the command line has been completely processed into a search plan
1441558Srgrimes	 * except for the (, ), !, and -o operators.  Rearrange the plan so
1451558Srgrimes	 * that the portions of the plan which are affected by the operators
1461558Srgrimes	 * are moved into operator nodes themselves.  For example:
1471558Srgrimes	 *
1481558Srgrimes	 *	[!]--> [-name foo]--> [-print]
1491558Srgrimes	 *
1501558Srgrimes	 * becomes
1511558Srgrimes	 *
1521558Srgrimes	 *	[! [-name foo] ]--> [-print]
1531558Srgrimes	 *
1541558Srgrimes	 * and
1551558Srgrimes	 *
1561558Srgrimes	 *	[(]--> [-depth]--> [-name foo]--> [)]--> [-print]
1571558Srgrimes	 *
1581558Srgrimes	 * becomes
1591558Srgrimes	 *
1601558Srgrimes	 *	[expr [-depth]-->[-name foo] ]--> [-print]
1611558Srgrimes	 *
1621558Srgrimes	 * operators are handled in order of precedence.
1631558Srgrimes	 */
1641558Srgrimes
1651558Srgrimes	plan = paren_squish(plan);		/* ()'s */
1661558Srgrimes	plan = not_squish(plan);		/* !'s */
1671558Srgrimes	plan = or_squish(plan);			/* -o's */
1681558Srgrimes	return (plan);
1691558Srgrimes}
1701558Srgrimes
1711558SrgrimesFTS *tree;			/* pointer to top of FTS hierarchy */
1721558Srgrimes
1731558Srgrimes/*
1741558Srgrimes * find_execute --
1751558Srgrimes *	take a search plan and an array of search paths and executes the plan
1761558Srgrimes *	over all FTSENT's returned for the given search paths.
1771558Srgrimes */
1781558Srgrimesint
1791558Srgrimesfind_execute(plan, paths)
1801558Srgrimes	PLAN *plan;		/* search plan */
1811558Srgrimes	char **paths;		/* array of pathnames to traverse */
1821558Srgrimes{
1831558Srgrimes	FTSENT *entry;
1841558Srgrimes	PLAN *p;
1851558Srgrimes	int rval;
1861558Srgrimes
1871558Srgrimes	tree = fts_open(paths, ftsoptions, (issort ? find_compare : NULL));
1881558Srgrimes	if (tree == NULL)
1891558Srgrimes		err(1, "ftsopen");
1901558Srgrimes
1911558Srgrimes	for (rval = 0; (entry = fts_read(tree)) != NULL;) {
1921558Srgrimes		switch (entry->fts_info) {
1931558Srgrimes		case FTS_D:
1941558Srgrimes			if (isdepth)
1951558Srgrimes				continue;
1961558Srgrimes			break;
1971558Srgrimes		case FTS_DP:
1981558Srgrimes			if (!isdepth)
1991558Srgrimes				continue;
2001558Srgrimes			break;
2011558Srgrimes		case FTS_DNR:
20234266Sjulian		case FTS_ERR:
20334266Sjulian		case FTS_NS:
20434266Sjulian			(void)fflush(stdout);
20534266Sjulian			warnx("%s: %s",
20634266Sjulian			    entry->fts_path, strerror(entry->fts_errno));
20734266Sjulian			rval = 1;
20834266Sjulian			continue;
20934266Sjulian#ifdef FTS_W
21034266Sjulian		case FTS_W:
21134266Sjulian			continue;
21234266Sjulian#endif /* FTS_W */
21334266Sjulian		}
21434266Sjulian#define	BADCH	" \t\n\\'\""
21534266Sjulian		if (isxargs && strpbrk(entry->fts_path, BADCH)) {
21634266Sjulian			(void)fflush(stdout);
21734266Sjulian			warnx("%s: illegal path", entry->fts_path);
21834266Sjulian			rval = 1;
21934266Sjulian			continue;
2201558Srgrimes		}
2211558Srgrimes
2221558Srgrimes		if (mindepth != -1 && entry->fts_level < mindepth)
2231558Srgrimes			continue;
2241558Srgrimes
2251558Srgrimes		/*
2261558Srgrimes		 * Call all the functions in the execution plan until one is
2271558Srgrimes		 * false or all have been executed.  This is where we do all
2281558Srgrimes		 * the work specified by the user on the command line.
2291558Srgrimes		 */
2301558Srgrimes		for (p = plan; p && (p->execute)(p, entry); p = p->next);
2311558Srgrimes
2321558Srgrimes		if (maxdepth != -1 && entry->fts_level >= maxdepth) {
2331558Srgrimes			if (fts_set(tree, entry, FTS_SKIP))
2341558Srgrimes			err(1, "%s", entry->fts_path);
2351558Srgrimes			continue;
2361558Srgrimes		}
2371558Srgrimes	}
2381558Srgrimes	/* Finish any pending -exec ... {} + functions. */
2391558Srgrimes	for (p = plan; p != NULL; p = p->next)
2401558Srgrimes		if (p->execute == f_exec && p->flags & F_EXECPLUS)
2411558Srgrimes			(p->execute)(p, NULL);
2421558Srgrimes	if (errno)
2431558Srgrimes		err(1, "fts_read");
2441558Srgrimes	return (rval);
2451558Srgrimes}
2461558Srgrimes