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