xref: /freebsd/usr.bin/find/operator.c (revision 1de7b4b805ddbf2429da511c053686ac4591ed89)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Cimarron D. Taylor of the University of California, Berkeley.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #ifndef lint
36 #if 0
37 static char sccsid[] = "@(#)operator.c	8.1 (Berkeley) 6/6/93";
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/types.h>
45 
46 #include <err.h>
47 #include <fts.h>
48 #include <stdio.h>
49 
50 #include "find.h"
51 
52 static PLAN *yanknode(PLAN **);
53 static PLAN *yankexpr(PLAN **);
54 
55 /*
56  * yanknode --
57  *	destructively removes the top from the plan
58  */
59 static PLAN *
60 yanknode(PLAN **planp)
61 {
62 	PLAN *node;		/* top node removed from the plan */
63 
64 	if ((node = (*planp)) == NULL)
65 		return (NULL);
66 	(*planp) = (*planp)->next;
67 	node->next = NULL;
68 	return (node);
69 }
70 
71 /*
72  * yankexpr --
73  *	Removes one expression from the plan.  This is used mainly by
74  *	paren_squish.  In comments below, an expression is either a
75  *	simple node or a f_expr node containing a list of simple nodes.
76  */
77 static PLAN *
78 yankexpr(PLAN **planp)
79 {
80 	PLAN *next;		/* temp node holding subexpression results */
81 	PLAN *node;		/* pointer to returned node or expression */
82 	PLAN *tail;		/* pointer to tail of subplan */
83 	PLAN *subplan;		/* pointer to head of ( ) expression */
84 
85 	/* first pull the top node from the plan */
86 	if ((node = yanknode(planp)) == NULL)
87 		return (NULL);
88 
89 	/*
90 	 * If the node is an '(' then we recursively slurp up expressions
91 	 * until we find its associated ')'.  If it's a closing paren we
92 	 * just return it and unwind our recursion; all other nodes are
93 	 * complete expressions, so just return them.
94 	 */
95 	if (node->execute == f_openparen)
96 		for (tail = subplan = NULL;;) {
97 			if ((next = yankexpr(planp)) == NULL)
98 				errx(1, "(: missing closing ')'");
99 			/*
100 			 * If we find a closing ')' we store the collected
101 			 * subplan in our '(' node and convert the node to
102 			 * a f_expr.  The ')' we found is ignored.  Otherwise,
103 			 * we just continue to add whatever we get to our
104 			 * subplan.
105 			 */
106 			if (next->execute == f_closeparen) {
107 				if (subplan == NULL)
108 					errx(1, "(): empty inner expression");
109 				node->p_data[0] = subplan;
110 				node->execute = f_expr;
111 				break;
112 			} else {
113 				if (subplan == NULL)
114 					tail = subplan = next;
115 				else {
116 					tail->next = next;
117 					tail = next;
118 				}
119 				tail->next = NULL;
120 			}
121 		}
122 	return (node);
123 }
124 
125 /*
126  * paren_squish --
127  *	replaces "parenthesized" plans in our search plan with "expr" nodes.
128  */
129 PLAN *
130 paren_squish(PLAN *plan)
131 {
132 	PLAN *expr;		/* pointer to next expression */
133 	PLAN *tail;		/* pointer to tail of result plan */
134 	PLAN *result;		/* pointer to head of result plan */
135 
136 	result = tail = NULL;
137 
138 	/*
139 	 * the basic idea is to have yankexpr do all our work and just
140 	 * collect its results together.
141 	 */
142 	while ((expr = yankexpr(&plan)) != NULL) {
143 		/*
144 		 * if we find an unclaimed ')' it means there is a missing
145 		 * '(' someplace.
146 		 */
147 		if (expr->execute == f_closeparen)
148 			errx(1, "): no beginning '('");
149 
150 		/* add the expression to our result plan */
151 		if (result == NULL)
152 			tail = result = expr;
153 		else {
154 			tail->next = expr;
155 			tail = expr;
156 		}
157 		tail->next = NULL;
158 	}
159 	return (result);
160 }
161 
162 /*
163  * not_squish --
164  *	compresses "!" expressions in our search plan.
165  */
166 PLAN *
167 not_squish(PLAN *plan)
168 {
169 	PLAN *next;		/* next node being processed */
170 	PLAN *node;		/* temporary node used in f_not processing */
171 	PLAN *tail;		/* pointer to tail of result plan */
172 	PLAN *result;		/* pointer to head of result plan */
173 
174 	tail = result = NULL;
175 
176 	while ((next = yanknode(&plan))) {
177 		/*
178 		 * if we encounter a ( expression ) then look for nots in
179 		 * the expr subplan.
180 		 */
181 		if (next->execute == f_expr)
182 			next->p_data[0] = not_squish(next->p_data[0]);
183 
184 		/*
185 		 * if we encounter a not, then snag the next node and place
186 		 * it in the not's subplan.  As an optimization we compress
187 		 * several not's to zero or one not.
188 		 */
189 		if (next->execute == f_not) {
190 			int notlevel = 1;
191 
192 			node = yanknode(&plan);
193 			while (node != NULL && node->execute == f_not) {
194 				++notlevel;
195 				node = yanknode(&plan);
196 			}
197 			if (node == NULL)
198 				errx(1, "!: no following expression");
199 			if (node->execute == f_or)
200 				errx(1, "!: nothing between ! and -o");
201 			/*
202 			 * If we encounter ! ( expr ) then look for nots in
203 			 * the expr subplan.
204 			 */
205 			if (node->execute == f_expr)
206 				node->p_data[0] = not_squish(node->p_data[0]);
207 			if (notlevel % 2 != 1)
208 				next = node;
209 			else
210 				next->p_data[0] = node;
211 		}
212 
213 		/* add the node to our result plan */
214 		if (result == NULL)
215 			tail = result = next;
216 		else {
217 			tail->next = next;
218 			tail = next;
219 		}
220 		tail->next = NULL;
221 	}
222 	return (result);
223 }
224 
225 /*
226  * or_squish --
227  *	compresses -o expressions in our search plan.
228  */
229 PLAN *
230 or_squish(PLAN *plan)
231 {
232 	PLAN *next;		/* next node being processed */
233 	PLAN *tail;		/* pointer to tail of result plan */
234 	PLAN *result;		/* pointer to head of result plan */
235 
236 	tail = result = next = NULL;
237 
238 	while ((next = yanknode(&plan)) != NULL) {
239 		/*
240 		 * if we encounter a ( expression ) then look for or's in
241 		 * the expr subplan.
242 		 */
243 		if (next->execute == f_expr)
244 			next->p_data[0] = or_squish(next->p_data[0]);
245 
246 		/* if we encounter a not then look for or's in the subplan */
247 		if (next->execute == f_not)
248 			next->p_data[0] = or_squish(next->p_data[0]);
249 
250 		/*
251 		 * if we encounter an or, then place our collected plan in the
252 		 * or's first subplan and then recursively collect the
253 		 * remaining stuff into the second subplan and return the or.
254 		 */
255 		if (next->execute == f_or) {
256 			if (result == NULL)
257 				errx(1, "-o: no expression before -o");
258 			next->p_data[0] = result;
259 			next->p_data[1] = or_squish(plan);
260 			if (next->p_data[1] == NULL)
261 				errx(1, "-o: no expression after -o");
262 			return (next);
263 		}
264 
265 		/* add the node to our result plan */
266 		if (result == NULL)
267 			tail = result = next;
268 		else {
269 			tail->next = next;
270 			tail = next;
271 		}
272 		tail->next = NULL;
273 	}
274 	return (result);
275 }
276