xref: /freebsd/usr.bin/find/operator.c (revision 5e3934b15a2741b2de6b217e77dc9d798d740804)
19b50d902SRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
49b50d902SRodney W. Grimes  * Copyright (c) 1990, 1993
59b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
69b50d902SRodney W. Grimes  *
79b50d902SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
89b50d902SRodney W. Grimes  * Cimarron D. Taylor of the University of California, Berkeley.
99b50d902SRodney W. Grimes  *
109b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
119b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
129b50d902SRodney W. Grimes  * are met:
139b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
149b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
159b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
169b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
179b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
199b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
209b50d902SRodney W. Grimes  *    without specific prior written permission.
219b50d902SRodney W. Grimes  *
229b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329b50d902SRodney W. Grimes  * SUCH DAMAGE.
339b50d902SRodney W. Grimes  */
349b50d902SRodney W. Grimes 
359b50d902SRodney W. Grimes #include <sys/types.h>
369b50d902SRodney W. Grimes 
379b50d902SRodney W. Grimes #include <err.h>
389b50d902SRodney W. Grimes #include <fts.h>
399b50d902SRodney W. Grimes #include <stdio.h>
40*c3a6ea5bSAlex Richardson #include <time.h>
419b50d902SRodney W. Grimes 
429b50d902SRodney W. Grimes #include "find.h"
439b50d902SRodney W. Grimes 
44ecca1f1cSMark Murray static PLAN *yanknode(PLAN **);
45ecca1f1cSMark Murray static PLAN *yankexpr(PLAN **);
46e98080b1SDavid Malone 
479b50d902SRodney W. Grimes /*
489b50d902SRodney W. Grimes  * yanknode --
499b50d902SRodney W. Grimes  *	destructively removes the top from the plan
509b50d902SRodney W. Grimes  */
519b50d902SRodney W. Grimes static PLAN *
yanknode(PLAN ** planp)52ef646f18SMark Murray yanknode(PLAN **planp)
539b50d902SRodney W. Grimes {
549b50d902SRodney W. Grimes 	PLAN *node;		/* top node removed from the plan */
559b50d902SRodney W. Grimes 
569b50d902SRodney W. Grimes 	if ((node = (*planp)) == NULL)
579b50d902SRodney W. Grimes 		return (NULL);
589b50d902SRodney W. Grimes 	(*planp) = (*planp)->next;
599b50d902SRodney W. Grimes 	node->next = NULL;
609b50d902SRodney W. Grimes 	return (node);
619b50d902SRodney W. Grimes }
629b50d902SRodney W. Grimes 
639b50d902SRodney W. Grimes /*
649b50d902SRodney W. Grimes  * yankexpr --
659b50d902SRodney W. Grimes  *	Removes one expression from the plan.  This is used mainly by
669b50d902SRodney W. Grimes  *	paren_squish.  In comments below, an expression is either a
67ea92232aSPoul-Henning Kamp  *	simple node or a f_expr node containing a list of simple nodes.
689b50d902SRodney W. Grimes  */
699b50d902SRodney W. Grimes static PLAN *
yankexpr(PLAN ** planp)70ef646f18SMark Murray yankexpr(PLAN **planp)
719b50d902SRodney W. Grimes {
72e98080b1SDavid Malone 	PLAN *next;		/* temp node holding subexpression results */
739b50d902SRodney W. Grimes 	PLAN *node;		/* pointer to returned node or expression */
749b50d902SRodney W. Grimes 	PLAN *tail;		/* pointer to tail of subplan */
759b50d902SRodney W. Grimes 	PLAN *subplan;		/* pointer to head of ( ) expression */
769b50d902SRodney W. Grimes 
779b50d902SRodney W. Grimes 	/* first pull the top node from the plan */
789b50d902SRodney W. Grimes 	if ((node = yanknode(planp)) == NULL)
799b50d902SRodney W. Grimes 		return (NULL);
809b50d902SRodney W. Grimes 
819b50d902SRodney W. Grimes 	/*
829b50d902SRodney W. Grimes 	 * If the node is an '(' then we recursively slurp up expressions
839b50d902SRodney W. Grimes 	 * until we find its associated ')'.  If it's a closing paren we
849b50d902SRodney W. Grimes 	 * just return it and unwind our recursion; all other nodes are
859b50d902SRodney W. Grimes 	 * complete expressions, so just return them.
869b50d902SRodney W. Grimes 	 */
87ea92232aSPoul-Henning Kamp 	if (node->execute == f_openparen)
889b50d902SRodney W. Grimes 		for (tail = subplan = NULL;;) {
899b50d902SRodney W. Grimes 			if ((next = yankexpr(planp)) == NULL)
904ed27a6fSPhilippe Charnier 				errx(1, "(: missing closing ')'");
919b50d902SRodney W. Grimes 			/*
929b50d902SRodney W. Grimes 			 * If we find a closing ')' we store the collected
939b50d902SRodney W. Grimes 			 * subplan in our '(' node and convert the node to
94ea92232aSPoul-Henning Kamp 			 * a f_expr.  The ')' we found is ignored.  Otherwise,
959b50d902SRodney W. Grimes 			 * we just continue to add whatever we get to our
969b50d902SRodney W. Grimes 			 * subplan.
979b50d902SRodney W. Grimes 			 */
98ea92232aSPoul-Henning Kamp 			if (next->execute == f_closeparen) {
999b50d902SRodney W. Grimes 				if (subplan == NULL)
1009b50d902SRodney W. Grimes 					errx(1, "(): empty inner expression");
1019b50d902SRodney W. Grimes 				node->p_data[0] = subplan;
102ea92232aSPoul-Henning Kamp 				node->execute = f_expr;
1039b50d902SRodney W. Grimes 				break;
1049b50d902SRodney W. Grimes 			} else {
1059b50d902SRodney W. Grimes 				if (subplan == NULL)
1069b50d902SRodney W. Grimes 					tail = subplan = next;
1079b50d902SRodney W. Grimes 				else {
1089b50d902SRodney W. Grimes 					tail->next = next;
1099b50d902SRodney W. Grimes 					tail = next;
1109b50d902SRodney W. Grimes 				}
1119b50d902SRodney W. Grimes 				tail->next = NULL;
1129b50d902SRodney W. Grimes 			}
1139b50d902SRodney W. Grimes 		}
1149b50d902SRodney W. Grimes 	return (node);
1159b50d902SRodney W. Grimes }
1169b50d902SRodney W. Grimes 
1179b50d902SRodney W. Grimes /*
1189b50d902SRodney W. Grimes  * paren_squish --
1199725a7b9SPhilippe Charnier  *	replaces "parenthesized" plans in our search plan with "expr" nodes.
1209b50d902SRodney W. Grimes  */
1219b50d902SRodney W. Grimes PLAN *
paren_squish(PLAN * plan)122ef646f18SMark Murray paren_squish(PLAN *plan)
1239b50d902SRodney W. Grimes {
124e98080b1SDavid Malone 	PLAN *expr;		/* pointer to next expression */
125e98080b1SDavid Malone 	PLAN *tail;		/* pointer to tail of result plan */
1269b50d902SRodney W. Grimes 	PLAN *result;		/* pointer to head of result plan */
1279b50d902SRodney W. Grimes 
1289b50d902SRodney W. Grimes 	result = tail = NULL;
1299b50d902SRodney W. Grimes 
1309b50d902SRodney W. Grimes 	/*
1319b50d902SRodney W. Grimes 	 * the basic idea is to have yankexpr do all our work and just
132ea92232aSPoul-Henning Kamp 	 * collect its results together.
1339b50d902SRodney W. Grimes 	 */
1349b50d902SRodney W. Grimes 	while ((expr = yankexpr(&plan)) != NULL) {
1359b50d902SRodney W. Grimes 		/*
1369b50d902SRodney W. Grimes 		 * if we find an unclaimed ')' it means there is a missing
1379b50d902SRodney W. Grimes 		 * '(' someplace.
1389b50d902SRodney W. Grimes 		 */
139ea92232aSPoul-Henning Kamp 		if (expr->execute == f_closeparen)
1409b50d902SRodney W. Grimes 			errx(1, "): no beginning '('");
1419b50d902SRodney W. Grimes 
1429b50d902SRodney W. Grimes 		/* add the expression to our result plan */
1439b50d902SRodney W. Grimes 		if (result == NULL)
1449b50d902SRodney W. Grimes 			tail = result = expr;
1459b50d902SRodney W. Grimes 		else {
1469b50d902SRodney W. Grimes 			tail->next = expr;
1479b50d902SRodney W. Grimes 			tail = expr;
1489b50d902SRodney W. Grimes 		}
1499b50d902SRodney W. Grimes 		tail->next = NULL;
1509b50d902SRodney W. Grimes 	}
1519b50d902SRodney W. Grimes 	return (result);
1529b50d902SRodney W. Grimes }
1539b50d902SRodney W. Grimes 
1549b50d902SRodney W. Grimes /*
1559b50d902SRodney W. Grimes  * not_squish --
1569b50d902SRodney W. Grimes  *	compresses "!" expressions in our search plan.
1579b50d902SRodney W. Grimes  */
1589b50d902SRodney W. Grimes PLAN *
not_squish(PLAN * plan)159ef646f18SMark Murray not_squish(PLAN *plan)
1609b50d902SRodney W. Grimes {
161e98080b1SDavid Malone 	PLAN *next;		/* next node being processed */
162e98080b1SDavid Malone 	PLAN *node;		/* temporary node used in f_not processing */
163e98080b1SDavid Malone 	PLAN *tail;		/* pointer to tail of result plan */
1649b50d902SRodney W. Grimes 	PLAN *result;		/* pointer to head of result plan */
1659b50d902SRodney W. Grimes 
166ea92232aSPoul-Henning Kamp 	tail = result = NULL;
1679b50d902SRodney W. Grimes 
168065fe727SDavid E. O'Brien 	while ((next = yanknode(&plan))) {
1699b50d902SRodney W. Grimes 		/*
1709b50d902SRodney W. Grimes 		 * if we encounter a ( expression ) then look for nots in
1719b50d902SRodney W. Grimes 		 * the expr subplan.
1729b50d902SRodney W. Grimes 		 */
173ea92232aSPoul-Henning Kamp 		if (next->execute == f_expr)
1749b50d902SRodney W. Grimes 			next->p_data[0] = not_squish(next->p_data[0]);
1759b50d902SRodney W. Grimes 
1769b50d902SRodney W. Grimes 		/*
1779b50d902SRodney W. Grimes 		 * if we encounter a not, then snag the next node and place
1789b50d902SRodney W. Grimes 		 * it in the not's subplan.  As an optimization we compress
1799b50d902SRodney W. Grimes 		 * several not's to zero or one not.
1809b50d902SRodney W. Grimes 		 */
181ea92232aSPoul-Henning Kamp 		if (next->execute == f_not) {
1829b50d902SRodney W. Grimes 			int notlevel = 1;
1839b50d902SRodney W. Grimes 
1849b50d902SRodney W. Grimes 			node = yanknode(&plan);
185ea92232aSPoul-Henning Kamp 			while (node != NULL && node->execute == f_not) {
1869b50d902SRodney W. Grimes 				++notlevel;
1879b50d902SRodney W. Grimes 				node = yanknode(&plan);
1889b50d902SRodney W. Grimes 			}
1899b50d902SRodney W. Grimes 			if (node == NULL)
1909b50d902SRodney W. Grimes 				errx(1, "!: no following expression");
191ea92232aSPoul-Henning Kamp 			if (node->execute == f_or)
1929b50d902SRodney W. Grimes 				errx(1, "!: nothing between ! and -o");
193704969a2SJoerg Wunsch 			/*
194704969a2SJoerg Wunsch 			 * If we encounter ! ( expr ) then look for nots in
195704969a2SJoerg Wunsch 			 * the expr subplan.
196704969a2SJoerg Wunsch 			 */
197ea92232aSPoul-Henning Kamp 			if (node->execute == f_expr)
198704969a2SJoerg Wunsch 				node->p_data[0] = not_squish(node->p_data[0]);
1999b50d902SRodney W. Grimes 			if (notlevel % 2 != 1)
2009b50d902SRodney W. Grimes 				next = node;
2019b50d902SRodney W. Grimes 			else
2029b50d902SRodney W. Grimes 				next->p_data[0] = node;
2039b50d902SRodney W. Grimes 		}
2049b50d902SRodney W. Grimes 
2059b50d902SRodney W. Grimes 		/* add the node to our result plan */
2069b50d902SRodney W. Grimes 		if (result == NULL)
2079b50d902SRodney W. Grimes 			tail = result = next;
2089b50d902SRodney W. Grimes 		else {
2099b50d902SRodney W. Grimes 			tail->next = next;
2109b50d902SRodney W. Grimes 			tail = next;
2119b50d902SRodney W. Grimes 		}
2129b50d902SRodney W. Grimes 		tail->next = NULL;
2139b50d902SRodney W. Grimes 	}
2149b50d902SRodney W. Grimes 	return (result);
2159b50d902SRodney W. Grimes }
2169b50d902SRodney W. Grimes 
2179b50d902SRodney W. Grimes /*
2189b50d902SRodney W. Grimes  * or_squish --
2199b50d902SRodney W. Grimes  *	compresses -o expressions in our search plan.
2209b50d902SRodney W. Grimes  */
2219b50d902SRodney W. Grimes PLAN *
or_squish(PLAN * plan)222ef646f18SMark Murray or_squish(PLAN *plan)
2239b50d902SRodney W. Grimes {
224e98080b1SDavid Malone 	PLAN *next;		/* next node being processed */
225e98080b1SDavid Malone 	PLAN *tail;		/* pointer to tail of result plan */
2269b50d902SRodney W. Grimes 	PLAN *result;		/* pointer to head of result plan */
2279b50d902SRodney W. Grimes 
2289b50d902SRodney W. Grimes 	tail = result = next = NULL;
2299b50d902SRodney W. Grimes 
2309b50d902SRodney W. Grimes 	while ((next = yanknode(&plan)) != NULL) {
2319b50d902SRodney W. Grimes 		/*
2329b50d902SRodney W. Grimes 		 * if we encounter a ( expression ) then look for or's in
2339b50d902SRodney W. Grimes 		 * the expr subplan.
2349b50d902SRodney W. Grimes 		 */
235ea92232aSPoul-Henning Kamp 		if (next->execute == f_expr)
2369b50d902SRodney W. Grimes 			next->p_data[0] = or_squish(next->p_data[0]);
2379b50d902SRodney W. Grimes 
238704969a2SJoerg Wunsch 		/* if we encounter a not then look for or's in the subplan */
239ea92232aSPoul-Henning Kamp 		if (next->execute == f_not)
2409b50d902SRodney W. Grimes 			next->p_data[0] = or_squish(next->p_data[0]);
2419b50d902SRodney W. Grimes 
2429b50d902SRodney W. Grimes 		/*
2439b50d902SRodney W. Grimes 		 * if we encounter an or, then place our collected plan in the
2449b50d902SRodney W. Grimes 		 * or's first subplan and then recursively collect the
2459b50d902SRodney W. Grimes 		 * remaining stuff into the second subplan and return the or.
2469b50d902SRodney W. Grimes 		 */
247ea92232aSPoul-Henning Kamp 		if (next->execute == f_or) {
2489b50d902SRodney W. Grimes 			if (result == NULL)
2499b50d902SRodney W. Grimes 				errx(1, "-o: no expression before -o");
2509b50d902SRodney W. Grimes 			next->p_data[0] = result;
2519b50d902SRodney W. Grimes 			next->p_data[1] = or_squish(plan);
2529b50d902SRodney W. Grimes 			if (next->p_data[1] == NULL)
2539b50d902SRodney W. Grimes 				errx(1, "-o: no expression after -o");
2549b50d902SRodney W. Grimes 			return (next);
2559b50d902SRodney W. Grimes 		}
2569b50d902SRodney W. Grimes 
2579b50d902SRodney W. Grimes 		/* add the node to our result plan */
2589b50d902SRodney W. Grimes 		if (result == NULL)
2599b50d902SRodney W. Grimes 			tail = result = next;
2609b50d902SRodney W. Grimes 		else {
2619b50d902SRodney W. Grimes 			tail->next = next;
2629b50d902SRodney W. Grimes 			tail = next;
2639b50d902SRodney W. Grimes 		}
2649b50d902SRodney W. Grimes 		tail->next = NULL;
2659b50d902SRodney W. Grimes 	}
2669b50d902SRodney W. Grimes 	return (result);
2679b50d902SRodney W. Grimes }
268