1 /*        $NetBSD: operator.c,v 1.10 2014/10/18 08:33:30 snj Exp $    */
2 
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 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "from: @(#)operator.c      8.1 (Berkeley) 6/6/93";
39 #else
40 __RCSID("$NetBSD: operator.c,v 1.10 2014/10/18 08:33:30 snj Exp $");
41 #endif
42 #endif /* not lint */
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 *
yanknode(PLAN ** planp)60 yanknode(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 N_EXPR node containing a list of simple nodes.
76  */
77 static PLAN *
yankexpr(PLAN ** planp)78 yankexpr(PLAN **planp)                  /* pointer to top of plan (modified) */
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->type == N_OPENPAREN)
96                     for (tail = subplan = NULL;;) {
97                               if ((next = yankexpr(planp)) == NULL)
98                                         err(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 N_EXPR.  The ')' we found is ignored.  Otherwise,
103                                * we just continue to add whatever we get to our
104                                * subplan.
105                                */
106                               if (next->type == N_CLOSEPAREN) {
107                                         if (subplan == NULL)
108                                                   errx(1, "(): empty inner expression");
109                                         node->p_data[0] = subplan;
110                                         node->type = N_EXPR;
111                                         node->eval = 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  */
130 PLAN *
paren_squish(PLAN * plan)131 paren_squish(PLAN *plan)      /* plan with ( ) nodes */
132 {
133           PLAN *expr;                   /* pointer to next expression */
134           PLAN *tail;                   /* pointer to tail of result plan */
135           PLAN *result;                 /* pointer to head of result plan */
136 
137           result = tail = NULL;
138 
139           /*
140            * the basic idea is to have yankexpr do all our work and just
141            * collect its results together.
142            */
143           while ((expr = yankexpr(&plan)) != NULL) {
144                     /*
145                      * if we find an unclaimed ')' it means there is a missing
146                      * '(' someplace.
147                      */
148                     if (expr->type == N_CLOSEPAREN)
149                               errx(1, "): no beginning '('");
150 
151                     /* add the expression to our result plan */
152                     if (result == NULL)
153                               tail = result = expr;
154                     else {
155                               tail->next = expr;
156                               tail = expr;
157                     }
158                     tail->next = NULL;
159           }
160           return (result);
161 }
162 
163 /*
164  * not_squish --
165  *        compresses "!" expressions in our search plan.
166  */
167 PLAN *
not_squish(PLAN * plan)168 not_squish(PLAN *plan)                  /* plan to process */
169 {
170           PLAN *next;                   /* next node being processed */
171           PLAN *node;                   /* temporary node used in N_NOT processing */
172           PLAN *tail;                   /* pointer to tail of result plan */
173           PLAN *result;                 /* pointer to head of result plan */
174 
175           tail = result = next = NULL;
176 
177           while ((next = yanknode(&plan)) != NULL) {
178                     /*
179                      * if we encounter a ( expression ) then look for nots in
180                      * the expr subplan.
181                      */
182                     if (next->type == N_EXPR)
183                               next->p_data[0] = not_squish(next->p_data[0]);
184 
185                     /*
186                      * if we encounter a not, then snag the next node and place
187                      * it in the not's subplan.  As an optimization we compress
188                      * several not's to zero or one not.
189                      */
190                     if (next->type == N_NOT) {
191                               int notlevel = 1;
192 
193                               node = yanknode(&plan);
194                               while (node != NULL && node->type == N_NOT) {
195                                         ++notlevel;
196                                         node = yanknode(&plan);
197                               }
198                               if (node == NULL)
199                                         errx(1, "!: no following expression");
200                               if (node->type == N_OR)
201                                         errx(1, "!: nothing between ! and -o");
202                               if (node->type == N_EXPR)
203                                         node = not_squish(node);
204                               if (notlevel % 2 != 1)
205                                         next = node;
206                               else
207                                         next->p_data[0] = node;
208                     }
209 
210                     /* add the node to our result plan */
211                     if (result == NULL)
212                               tail = result = next;
213                     else {
214                               tail->next = next;
215                               tail = next;
216                     }
217                     tail->next = NULL;
218           }
219           return (result);
220 }
221 
222 /*
223  * or_squish --
224  *        compresses -o expressions in our search plan.
225  */
226 PLAN *
or_squish(PLAN * plan)227 or_squish(PLAN *plan)                   /* plan with ors to be squished */
228 {
229           PLAN *next;                   /* next node being processed */
230           PLAN *tail;                   /* pointer to tail of result plan */
231           PLAN *result;                 /* pointer to head of result plan */
232 
233           tail = result = next = NULL;
234 
235           while ((next = yanknode(&plan)) != NULL) {
236                     /*
237                      * if we encounter a ( expression ) then look for or's in
238                      * the expr subplan.
239                      */
240                     if (next->type == N_EXPR)
241                               next->p_data[0] = or_squish(next->p_data[0]);
242 
243                     /* if we encounter a not then look for not's in the subplan */
244                     if (next->type == N_NOT)
245                               next->p_data[0] = or_squish(next->p_data[0]);
246 
247                     /*
248                      * if we encounter an or, then place our collected plan in the
249                      * or's first subplan and then recursively collect the
250                      * remaining stuff into the second subplan and return the or.
251                      */
252                     if (next->type == N_OR) {
253                               if (result == NULL)
254                                         errx(1, "-o: no expression before -o");
255                               next->p_data[0] = result;
256                               next->p_data[1] = or_squish(plan);
257                               if (next->p_data[1] == NULL)
258                                         errx(1, "-o: no expression after -o");
259                               return (next);
260                     }
261 
262                     /* add the node to our result plan */
263                     if (result == NULL)
264                               tail = result = next;
265                     else {
266                               tail->next = next;
267                               tail = next;
268                     }
269                     tail->next = NULL;
270           }
271           return (result);
272 }
273