find.c revision 116333
150565Sphk/*-
250565Sphk * Copyright (c) 1991, 1993, 1994
350565Sphk *	The Regents of the University of California.  All rights reserved.
450565Sphk *
550565Sphk * This code is derived from software contributed to Berkeley by
650565Sphk * Cimarron D. Taylor of the University of California, Berkeley.
750565Sphk *
850565Sphk * Redistribution and use in source and binary forms, with or without
950565Sphk * modification, are permitted provided that the following conditions
1050565Sphk * are met:
1150565Sphk * 1. Redistributions of source code must retain the above copyright
1250565Sphk *    notice, this list of conditions and the following disclaimer.
1350565Sphk * 2. Redistributions in binary form must reproduce the above copyright
1450565Sphk *    notice, this list of conditions and the following disclaimer in the
1550565Sphk *    documentation and/or other materials provided with the distribution.
1651111Sjulian * 3. All advertising materials mentioning features or use of this software
1760041Sphk *    must display the following acknowledgement:
1850565Sphk *	This product includes software developed by the University of
1950565Sphk *	California, Berkeley and its contributors.
2050565Sphk * 4. Neither the name of the University nor the names of its contributors
2161953Snbm *    may be used to endorse or promote products derived from this software
2250728Sphk *    without specific prior written permission.
2350565Sphk *
2450565Sphk * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2550565Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2650565Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2750565Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2850565Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2950565Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3050565Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3161717Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3261717Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3350565Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3462617Simp * SUCH DAMAGE.
3562617Simp */
3662617Simp
3762617Simp#ifndef lint
3862617Simp#if 0
3962617Simpstatic char sccsid[] = "@(#)find.c	8.5 (Berkeley) 8/5/94";
4062617Simp#else
4162617Simp#endif
4262617Simp#endif /* not lint */
4362617Simp
4462617Simp#include <sys/cdefs.h>
4550565Sphk__FBSDID("$FreeBSD: head/usr.bin/find/find.c 116333 2003-06-14 13:00:21Z markm $");
4651215Sphk
4750565Sphk#include <sys/types.h>
4850565Sphk#include <sys/stat.h>
4950565Sphk
5051198Sphk#include <err.h>
5151198Sphk#include <errno.h>
5250565Sphk#include <fts.h>
5351198Sphk#include <regex.h>
5451215Sphk#include <stdio.h>
5551215Sphk#include <stdlib.h>
5651215Sphk#include <string.h>
5751215Sphk
5851215Sphk#include "find.h"
5951215Sphk
6051215Sphkstatic int find_compare(const FTSENT * const *s1, const FTSENT * const *s2);
6150565Sphk
6250565Sphk/*
6353437Sjkh * find_compare --
6453437Sjkh *	tell fts_open() how to order the traversal of the hierarchy.
6551243Sphk *	This variant gives lexicographical order, i.e., alphabetical
6661717Sphk *	order within each directory.
6750565Sphk */
6850565Sphkstatic int
6950565Sphkfind_compare(const FTSENT * const *s1, const FTSENT * const *s2)
7052917Sphk{
7151215Sphk
7261717Sphk	return (strcoll((*s1)->fts_name, (*s2)->fts_name));
7350565Sphk}
7450565Sphk
7550565Sphk/*
7650728Sphk * find_formplan --
7750728Sphk *	process the command line and create a "plan" corresponding to the
7850728Sphk *	command arguments.
7950728Sphk */
8050728SphkPLAN *
8150728Sphkfind_formplan(char *argv[])
8250728Sphk{
8350728Sphk	PLAN *plan, *tail, *new;
8450728Sphk
8550728Sphk	/*
8650728Sphk	 * for each argument in the command line, determine what kind of node
8750728Sphk	 * it is, create the appropriate node type and add the new plan node
8850728Sphk	 * to the end of the existing plan.  The resulting plan is a linked
8950728Sphk	 * list of plan nodes.  For example, the string:
9050728Sphk	 *
9150728Sphk	 *	% find . -name foo -newer bar -print
9250728Sphk	 *
9350728Sphk	 * results in the plan:
9450728Sphk	 *
9550728Sphk	 *	[-name foo]--> [-newer bar]--> [-print]
9650728Sphk	 *
9750728Sphk	 * in this diagram, `[-name foo]' represents the plan node generated
9850728Sphk	 * by c_name() with an argument of foo and `-->' represents the
9950728Sphk	 * plan->next pointer.
10050728Sphk	 */
10150728Sphk	for (plan = tail = NULL; *argv;) {
10250728Sphk		if (!(new = find_create(&argv)))
10350728Sphk			continue;
10450728Sphk		if (plan == NULL)
10550728Sphk			tail = plan = new;
10657325Ssos		else {
10757325Ssos			tail->next = new;
10850728Sphk			tail = new;
10950728Sphk		}
11050565Sphk	}
11156767Sphk
11250565Sphk	/*
11361717Sphk	 * if the user didn't specify one of -print, -ok or -exec, then -print
11461717Sphk	 * is assumed so we bracket the current expression with parens, if
11557325Ssos	 * necessary, and add a -print node on the end.
11657325Ssos	 */
11750565Sphk	if (!isoutput) {
11850565Sphk		OPTION *p;
11950565Sphk		char **argv1 = 0;
12061717Sphk
12161717Sphk		if (plan == NULL) {
12261717Sphk			p = lookup_option("-print");
12361717Sphk			new = (p->create)(p, &argv1);
12461717Sphk			tail = plan = new;
12561717Sphk		} else {
12661717Sphk			p = lookup_option("(");
12761717Sphk			new = (p->create)(p, &argv1);
12861717Sphk			new->next = plan;
12961953Snbm			plan = new;
13062573Sphk			p = lookup_option(")");
13161953Snbm			new = (p->create)(p, &argv1);
13261953Snbm			tail->next = new;
13361953Snbm			tail = new;
13461953Snbm			p = lookup_option("-print");
13561953Snbm			new = (p->create)(p, &argv1);
13661953Snbm			tail->next = new;
13761953Snbm			tail = new;
13861953Snbm		}
13961953Snbm	}
14061953Snbm
14161953Snbm	/*
14261953Snbm	 * the command line has been completely processed into a search plan
14361953Snbm	 * except for the (, ), !, and -o operators.  Rearrange the plan so
14461953Snbm	 * that the portions of the plan which are affected by the operators
14561953Snbm	 * are moved into operator nodes themselves.  For example:
14661953Snbm	 *
14761953Snbm	 *	[!]--> [-name foo]--> [-print]
14861953Snbm	 *
14961953Snbm	 * becomes
15061953Snbm	 *
15161953Snbm	 *	[! [-name foo] ]--> [-print]
15261953Snbm	 *
15361953Snbm	 * and
15461953Snbm	 *
15561953Snbm	 *	[(]--> [-depth]--> [-name foo]--> [)]--> [-print]
15661953Snbm	 *
15750728Sphk	 * becomes
15850728Sphk	 *
15950728Sphk	 *	[expr [-depth]-->[-name foo] ]--> [-print]
16050728Sphk	 *
16150565Sphk	 * operators are handled in order of precedence.
16250565Sphk	 */
16350565Sphk
16450565Sphk	plan = paren_squish(plan);		/* ()'s */
16550565Sphk	plan = not_squish(plan);		/* !'s */
16650565Sphk	plan = or_squish(plan);			/* -o's */
16750565Sphk	return (plan);
16850728Sphk}
16950565Sphk
17050728SphkFTS *tree;			/* pointer to top of FTS hierarchy */
17150565Sphk
17250565Sphk/*
17350565Sphk * find_execute --
17450728Sphk *	take a search plan and an array of search paths and executes the plan
17552917Sphk *	over all FTSENT's returned for the given search paths.
17652917Sphk */
17754815Sphkint
17854815Sphkfind_execute(PLAN *plan, char *paths[])
17954815Sphk{
18052917Sphk	FTSENT *entry;
18152917Sphk	PLAN *p;
18252917Sphk	int rval;
18351860Sphk
18451878Ssos	tree = fts_open(paths, ftsoptions, (issort ? find_compare : NULL));
18551878Ssos	if (tree == NULL)
18651826Sphk		err(1, "ftsopen");
18751860Sphk
18851826Sphk	for (rval = 0; (entry = fts_read(tree)) != NULL;) {
18951826Sphk		switch (entry->fts_info) {
19062617Simp		case FTS_D:
19150728Sphk			if (isdepth)
19250728Sphk				continue;
19352917Sphk			break;
19450728Sphk		case FTS_DP:
19552917Sphk			if (!isdepth)
19650565Sphk				continue;
19750728Sphk			break;
19851924Sphk		case FTS_DNR:
19952917Sphk		case FTS_ERR:
20052917Sphk		case FTS_NS:
20152917Sphk			(void)fflush(stdout);
20252917Sphk			warnx("%s: %s",
20352917Sphk			    entry->fts_path, strerror(entry->fts_errno));
20452917Sphk			rval = 1;
20550728Sphk			continue;
20650565Sphk#ifdef FTS_W
20750565Sphk		case FTS_W:
20850565Sphk			continue;
20950565Sphk#endif /* FTS_W */
21050565Sphk		}
21150565Sphk#define	BADCH	" \t\n\\'\""
21250565Sphk		if (isxargs && strpbrk(entry->fts_path, BADCH)) {
21350565Sphk			(void)fflush(stdout);
21462617Simp			warnx("%s: illegal path", entry->fts_path);
21550565Sphk			rval = 1;
21650565Sphk			continue;
21762617Simp		}
21862617Simp
21951822Sphk		if (mindepth != -1 && entry->fts_level < mindepth)
22051826Sphk			continue;
22151924Sphk
22251826Sphk		/*
22350565Sphk		 * Call all the functions in the execution plan until one is
22450565Sphk		 * false or all have been executed.  This is where we do all
22550565Sphk		 * the work specified by the user on the command line.
22650565Sphk		 */
22759249Sphk		for (p = plan; p && (p->execute)(p, entry); p = p->next);
22850565Sphk
22950565Sphk		if (maxdepth != -1 && entry->fts_level >= maxdepth) {
23050565Sphk			if (fts_set(tree, entry, FTS_SKIP))
23150565Sphk			err(1, "%s", entry->fts_path);
23262617Simp			continue;
23362617Simp		}
23462617Simp	}
23562617Simp	/* Finish any pending -exec ... {} + functions. */
23650565Sphk	for (p = plan; p != NULL; p = p->next)
23750565Sphk		if (p->execute == f_exec && p->flags & F_EXECPLUS)
23859249Sphk			(p->execute)(p, NULL);
23959249Sphk	if (errno)
24050565Sphk		err(1, "fts_read");
24150565Sphk	return (rval);
24250565Sphk}
24350565Sphk