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