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