xref: /netbsd-src/usr.bin/find/operator.c (revision f0a7346d2148aa56c118f3e51cfffc26cebb09ce)
1*f0a7346dSsnj /*	$NetBSD: operator.c,v 1.10 2014/10/18 08:33:30 snj Exp $	*/
29d225a17Stls 
361f28255Scgd /*-
43dfa59ecSjtc  * Copyright (c) 1990, 1993
53dfa59ecSjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Cimarron D. Taylor of the University of California, Berkeley.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
1889aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35403b699bSlukem #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
37403b699bSlukem #if 0
38403b699bSlukem static char sccsid[] = "from: @(#)operator.c	8.1 (Berkeley) 6/6/93";
39403b699bSlukem #else
40*f0a7346dSsnj __RCSID("$NetBSD: operator.c,v 1.10 2014/10/18 08:33:30 snj Exp $");
41403b699bSlukem #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4461f28255Scgd #include <sys/types.h>
453dfa59ecSjtc 
463dfa59ecSjtc #include <err.h>
473dfa59ecSjtc #include <fts.h>
4861f28255Scgd #include <stdio.h>
493dfa59ecSjtc 
5061f28255Scgd #include "find.h"
5161f28255Scgd 
522eed134bSapb static PLAN *yanknode(PLAN **);
532eed134bSapb static PLAN *yankexpr(PLAN **);
549a80b4faSchristos 
5561f28255Scgd /*
5661f28255Scgd  * yanknode --
5761f28255Scgd  *	destructively removes the top from the plan
5861f28255Scgd  */
5961f28255Scgd static PLAN *
yanknode(PLAN ** planp)602eed134bSapb yanknode(PLAN **planp)		/* pointer to top of plan (modified) */
6161f28255Scgd {
6261f28255Scgd 	PLAN *node;		/* top node removed from the plan */
6361f28255Scgd 
6461f28255Scgd 	if ((node = (*planp)) == NULL)
6561f28255Scgd 		return (NULL);
6661f28255Scgd 	(*planp) = (*planp)->next;
6761f28255Scgd 	node->next = NULL;
6861f28255Scgd 	return (node);
6961f28255Scgd }
7061f28255Scgd 
7161f28255Scgd /*
7261f28255Scgd  * yankexpr --
7361f28255Scgd  *	Removes one expression from the plan.  This is used mainly by
7461f28255Scgd  *	paren_squish.  In comments below, an expression is either a
7561f28255Scgd  *	simple node or a N_EXPR node containing a list of simple nodes.
7661f28255Scgd  */
7761f28255Scgd static PLAN *
yankexpr(PLAN ** planp)782eed134bSapb yankexpr(PLAN **planp)		/* pointer to top of plan (modified) */
7961f28255Scgd {
80403b699bSlukem 	PLAN *next;		/* temp node holding subexpression results */
8161f28255Scgd 	PLAN *node;		/* pointer to returned node or expression */
8261f28255Scgd 	PLAN *tail;		/* pointer to tail of subplan */
8361f28255Scgd 	PLAN *subplan;		/* pointer to head of ( ) expression */
8461f28255Scgd 
8561f28255Scgd 	/* first pull the top node from the plan */
8661f28255Scgd 	if ((node = yanknode(planp)) == NULL)
8761f28255Scgd 		return (NULL);
8861f28255Scgd 
8961f28255Scgd 	/*
9061f28255Scgd 	 * If the node is an '(' then we recursively slurp up expressions
9161f28255Scgd 	 * until we find its associated ')'.  If it's a closing paren we
9261f28255Scgd 	 * just return it and unwind our recursion; all other nodes are
9361f28255Scgd 	 * complete expressions, so just return them.
9461f28255Scgd 	 */
9561f28255Scgd 	if (node->type == N_OPENPAREN)
9661f28255Scgd 		for (tail = subplan = NULL;;) {
9761f28255Scgd 			if ((next = yankexpr(planp)) == NULL)
983dfa59ecSjtc 				err(1, "(: missing closing ')'");
9961f28255Scgd 			/*
10061f28255Scgd 			 * If we find a closing ')' we store the collected
10161f28255Scgd 			 * subplan in our '(' node and convert the node to
10261f28255Scgd 			 * a N_EXPR.  The ')' we found is ignored.  Otherwise,
10361f28255Scgd 			 * we just continue to add whatever we get to our
10461f28255Scgd 			 * subplan.
10561f28255Scgd 			 */
10661f28255Scgd 			if (next->type == N_CLOSEPAREN) {
10761f28255Scgd 				if (subplan == NULL)
1083dfa59ecSjtc 					errx(1, "(): empty inner expression");
10961f28255Scgd 				node->p_data[0] = subplan;
11061f28255Scgd 				node->type = N_EXPR;
11161f28255Scgd 				node->eval = f_expr;
11261f28255Scgd 				break;
11361f28255Scgd 			} else {
11461f28255Scgd 				if (subplan == NULL)
11561f28255Scgd 					tail = subplan = next;
11661f28255Scgd 				else {
11761f28255Scgd 					tail->next = next;
11861f28255Scgd 					tail = next;
11961f28255Scgd 				}
12061f28255Scgd 				tail->next = NULL;
12161f28255Scgd 			}
12261f28255Scgd 		}
12361f28255Scgd 	return (node);
12461f28255Scgd }
12561f28255Scgd 
12661f28255Scgd /*
12761f28255Scgd  * paren_squish --
12861f28255Scgd  *	replaces "parentheisized" plans in our search plan with "expr" nodes.
12961f28255Scgd  */
13061f28255Scgd PLAN *
paren_squish(PLAN * plan)1312eed134bSapb paren_squish(PLAN *plan)	/* plan with ( ) nodes */
13261f28255Scgd {
133403b699bSlukem 	PLAN *expr;		/* pointer to next expression */
134403b699bSlukem 	PLAN *tail;		/* pointer to tail of result plan */
13561f28255Scgd 	PLAN *result;		/* pointer to head of result plan */
13661f28255Scgd 
13761f28255Scgd 	result = tail = NULL;
13861f28255Scgd 
13961f28255Scgd 	/*
14061f28255Scgd 	 * the basic idea is to have yankexpr do all our work and just
141*f0a7346dSsnj 	 * collect its results together.
14261f28255Scgd 	 */
14361f28255Scgd 	while ((expr = yankexpr(&plan)) != NULL) {
14461f28255Scgd 		/*
14561f28255Scgd 		 * if we find an unclaimed ')' it means there is a missing
14661f28255Scgd 		 * '(' someplace.
14761f28255Scgd 		 */
14861f28255Scgd 		if (expr->type == N_CLOSEPAREN)
1493dfa59ecSjtc 			errx(1, "): no beginning '('");
15061f28255Scgd 
15161f28255Scgd 		/* add the expression to our result plan */
15261f28255Scgd 		if (result == NULL)
15361f28255Scgd 			tail = result = expr;
15461f28255Scgd 		else {
15561f28255Scgd 			tail->next = expr;
15661f28255Scgd 			tail = expr;
15761f28255Scgd 		}
15861f28255Scgd 		tail->next = NULL;
15961f28255Scgd 	}
16061f28255Scgd 	return (result);
16161f28255Scgd }
16261f28255Scgd 
16361f28255Scgd /*
16461f28255Scgd  * not_squish --
16561f28255Scgd  *	compresses "!" expressions in our search plan.
16661f28255Scgd  */
16761f28255Scgd PLAN *
not_squish(PLAN * plan)1682eed134bSapb not_squish(PLAN *plan)		/* plan to process */
16961f28255Scgd {
170403b699bSlukem 	PLAN *next;		/* next node being processed */
171403b699bSlukem 	PLAN *node;		/* temporary node used in N_NOT processing */
172403b699bSlukem 	PLAN *tail;		/* pointer to tail of result plan */
17361f28255Scgd 	PLAN *result;		/* pointer to head of result plan */
17461f28255Scgd 
17561f28255Scgd 	tail = result = next = NULL;
17661f28255Scgd 
17761f28255Scgd 	while ((next = yanknode(&plan)) != NULL) {
17861f28255Scgd 		/*
17961f28255Scgd 		 * if we encounter a ( expression ) then look for nots in
18061f28255Scgd 		 * the expr subplan.
18161f28255Scgd 		 */
18261f28255Scgd 		if (next->type == N_EXPR)
18361f28255Scgd 			next->p_data[0] = not_squish(next->p_data[0]);
18461f28255Scgd 
18561f28255Scgd 		/*
18661f28255Scgd 		 * if we encounter a not, then snag the next node and place
18761f28255Scgd 		 * it in the not's subplan.  As an optimization we compress
18861f28255Scgd 		 * several not's to zero or one not.
18961f28255Scgd 		 */
19061f28255Scgd 		if (next->type == N_NOT) {
19161f28255Scgd 			int notlevel = 1;
19261f28255Scgd 
19361f28255Scgd 			node = yanknode(&plan);
194c5d402d1Slukem 			while (node != NULL && node->type == N_NOT) {
19561f28255Scgd 				++notlevel;
19661f28255Scgd 				node = yanknode(&plan);
19761f28255Scgd 			}
19861f28255Scgd 			if (node == NULL)
1993dfa59ecSjtc 				errx(1, "!: no following expression");
20061f28255Scgd 			if (node->type == N_OR)
2013dfa59ecSjtc 				errx(1, "!: nothing between ! and -o");
202c5d402d1Slukem 			if (node->type == N_EXPR)
203c5d402d1Slukem 				node = not_squish(node);
20461f28255Scgd 			if (notlevel % 2 != 1)
20561f28255Scgd 				next = node;
20661f28255Scgd 			else
20761f28255Scgd 				next->p_data[0] = node;
20861f28255Scgd 		}
20961f28255Scgd 
21061f28255Scgd 		/* add the node to our result plan */
21161f28255Scgd 		if (result == NULL)
21261f28255Scgd 			tail = result = next;
21361f28255Scgd 		else {
21461f28255Scgd 			tail->next = next;
21561f28255Scgd 			tail = next;
21661f28255Scgd 		}
21761f28255Scgd 		tail->next = NULL;
21861f28255Scgd 	}
21961f28255Scgd 	return (result);
22061f28255Scgd }
22161f28255Scgd 
22261f28255Scgd /*
22361f28255Scgd  * or_squish --
22461f28255Scgd  *	compresses -o expressions in our search plan.
22561f28255Scgd  */
22661f28255Scgd PLAN *
or_squish(PLAN * plan)2272eed134bSapb or_squish(PLAN *plan)		/* plan with ors to be squished */
22861f28255Scgd {
229403b699bSlukem 	PLAN *next;		/* next node being processed */
230403b699bSlukem 	PLAN *tail;		/* pointer to tail of result plan */
23161f28255Scgd 	PLAN *result;		/* pointer to head of result plan */
23261f28255Scgd 
23361f28255Scgd 	tail = result = next = NULL;
23461f28255Scgd 
23561f28255Scgd 	while ((next = yanknode(&plan)) != NULL) {
23661f28255Scgd 		/*
23761f28255Scgd 		 * if we encounter a ( expression ) then look for or's in
23861f28255Scgd 		 * the expr subplan.
23961f28255Scgd 		 */
24061f28255Scgd 		if (next->type == N_EXPR)
24161f28255Scgd 			next->p_data[0] = or_squish(next->p_data[0]);
24261f28255Scgd 
24361f28255Scgd 		/* if we encounter a not then look for not's in the subplan */
24461f28255Scgd 		if (next->type == N_NOT)
24561f28255Scgd 			next->p_data[0] = or_squish(next->p_data[0]);
24661f28255Scgd 
24761f28255Scgd 		/*
24861f28255Scgd 		 * if we encounter an or, then place our collected plan in the
24961f28255Scgd 		 * or's first subplan and then recursively collect the
25061f28255Scgd 		 * remaining stuff into the second subplan and return the or.
25161f28255Scgd 		 */
25261f28255Scgd 		if (next->type == N_OR) {
25361f28255Scgd 			if (result == NULL)
2543dfa59ecSjtc 				errx(1, "-o: no expression before -o");
25561f28255Scgd 			next->p_data[0] = result;
25661f28255Scgd 			next->p_data[1] = or_squish(plan);
25761f28255Scgd 			if (next->p_data[1] == NULL)
2583dfa59ecSjtc 				errx(1, "-o: no expression after -o");
25961f28255Scgd 			return (next);
26061f28255Scgd 		}
26161f28255Scgd 
26261f28255Scgd 		/* add the node to our result plan */
26361f28255Scgd 		if (result == NULL)
26461f28255Scgd 			tail = result = next;
26561f28255Scgd 		else {
26661f28255Scgd 			tail->next = next;
26761f28255Scgd 			tail = next;
26861f28255Scgd 		}
26961f28255Scgd 		tail->next = NULL;
27061f28255Scgd 	}
27161f28255Scgd 	return (result);
27261f28255Scgd }
273