BRL-CAD
search.c
Go to the documentation of this file.
1 /* S E A R C H . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2008-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 /* OpenBSD:
22  *
23  * Copyright (c) 1990, 1993
24  * The Regents of the University of California. All rights reserved.
25  *
26  * Redistribution and use in source and binary forms, with or without
27  * modification, are permitted provided that the following conditions
28  * are met:
29  * 1. Redistributions of source code must retain the above copyright
30  * notice, this list of conditions and the following disclaimer.
31  * 2. Redistributions in binary form must reproduce the above copyright
32  * notice, this list of conditions and the following disclaimer in the
33  * documentation and/or other materials provided with the distribution.
34  * 3. Neither the name of the University nor the names of its contributors
35  * may be used to endorse or promote products derived from this software
36  * without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  *
50  * NetBSD:
51  *
52  * Copyright (c) 1990, 1993, 1994
53  * The Regents of the University of California. All rights reserved.
54  *
55  * This code is derived from software contributed to Berkeley by
56  * Cimarron D. Taylor of the University of California, Berkeley.
57  *
58  * Redistribution and use in source and binary forms, with or without
59  * modification, are permitted provided that the following conditions
60  * are met:
61  * 1. Redistributions of source code must retain the above copyright
62  * notice, this list of conditions and the following disclaimer.
63  * 2. Redistributions in binary form must reproduce the above copyright
64  * notice, this list of conditions and the following disclaimer in the
65  * documentation and/or other materials provided with the distribution.
66  * 3. Neither the name of the University nor the names of its contributors
67  * may be used to endorse or promote products derived from this software
68  * without specific prior written permission.
69  *
70  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
71  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
74  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
76  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
77  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
78  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
79  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
80  * SUCH DAMAGE.
81  */
82 
83 #include "common.h"
84 
85 #include <string.h>
86 #include <stdlib.h>
87 #include <ctype.h>
88 #include <time.h>
89 #include <regex.h>
90 #include <limits.h> /* for INT_MAX */
91 
92 #include "bu/cmd.h"
93 
94 #include "db.h"
95 #include "./librt_private.h"
96 #include "./search.h"
97 
98 
99 /* NB: the following table must be sorted lexically. */
100 static OPTION options[] = {
101  { "!", N_NOT, c_not, O_ZERO },
102  { "(", N_OPENPAREN, c_openparen, O_ZERO },
103  { ")", N_CLOSEPAREN, c_closeparen, O_ZERO },
104  { "-a", N_AND, NULL, O_NONE },
105  { "-ab", N_ABOVE, c_above, O_ZERO },
106  { "-above", N_ABOVE, c_above, O_ZERO },
107  { "-and", N_AND, NULL, O_NONE },
108  { "-attr", N_ATTR, c_attr, O_ARGV },
109  { "-below", N_BELOW, c_below, O_ZERO },
110  { "-bl", N_BELOW, c_below, O_ZERO },
111  { "-bool", N_BOOL, c_bool, O_ARGV },
112  { "-depth", N_DEPTH, c_depth, O_ARGV },
113  { "-iname", N_INAME, c_iname, O_ARGV },
114  { "-iregex", N_IREGEX, c_iregex, O_ARGV },
115  { "-maxdepth", N_MAXDEPTH, c_maxdepth, O_ARGV },
116  { "-mindepth", N_MINDEPTH, c_mindepth, O_ARGV },
117  { "-name", N_NAME, c_name, O_ARGV },
118  { "-nnodes", N_NNODES, c_nnodes, O_ARGV },
119  { "-not", N_NOT, c_not, O_ZERO },
120  { "-o", N_OR, c_or, O_ZERO },
121  { "-or", N_OR, c_or, O_ZERO },
122  { "-param", N_PARAM, c_objparam, O_ARGV },
123  { "-path", N_PATH, c_path, O_ARGV },
124  { "-print", N_PRINT, c_print, O_ZERO },
125  { "-regex", N_REGEX, c_regex, O_ARGV },
126  { "-stdattr", N_STDATTR, c_stdattr, O_ZERO },
127  { "-type", N_TYPE, c_type, O_ARGV },
128 };
129 
130 
131 /* Search client data container */
133  struct db_i *dbip;
135  int flags;
136 };
137 
138 
139 HIDDEN void
141 {
142  struct bu_vls vls1 = BU_VLS_INIT_ZERO;
143  struct bu_vls vls2 = BU_VLS_INIT_ZERO;
144  struct db_full_path *newpath;
145  BU_ALLOC(newpath, struct db_full_path);
146  db_full_path_init(newpath);
147  db_dup_full_path(newpath, full_path);
148  while (newpath->fp_len > 0) {
149  struct directory *dp = DB_FULL_PATH_CUR_DIR(newpath);
150  int curr_bool = DB_FULL_PATH_CUR_BOOL(newpath);
151  bu_vls_sprintf(&vls1, "/%s(%d)%s", dp->d_namep, curr_bool, bu_vls_addr(&vls2));
152  bu_vls_sprintf(&vls2, "%s", bu_vls_addr(&vls1));
153  DB_FULL_PATH_POP(newpath);
154  }
155  bu_log("%s", bu_vls_addr(&vls2));
156  db_free_full_path(newpath);
157 }
158 
159 
160 /**
161  * A generic traversal function maintaining awareness of the full path
162  * to a given object.
163  */
164 HIDDEN void
165 db_fullpath_list_subtree(struct db_full_path *path, int curr_bool, union tree *tp,
166  void (*traverse_func) (struct db_full_path *path,
167  void *),
168  void *client_data)
169 {
170  struct directory *dp;
171  struct list_client_data_t *lcd= (struct list_client_data_t *)client_data;
172  int bool_val = curr_bool;
173 
174  if (!tp)
175  return;
176 
177  RT_CK_FULL_PATH(path);
178  RT_CHECK_DBI(lcd->dbip);
179  RT_CK_TREE(tp);
180 
181  switch (tp->tr_op) {
182  case OP_UNION:
183  case OP_INTERSECT:
184  case OP_SUBTRACT:
185  case OP_XOR:
186  if (tp->tr_op == OP_UNION) bool_val = 2;
187  if (tp->tr_op == OP_INTERSECT) bool_val = 3;
188  if (tp->tr_op == OP_SUBTRACT) bool_val = 4;
189  db_fullpath_list_subtree(path, bool_val, tp->tr_b.tb_right, traverse_func, client_data);
190  case OP_NOT:
191  case OP_GUARD:
192  case OP_XNOP:
193  db_fullpath_list_subtree(path, OP_UNION, tp->tr_b.tb_left, traverse_func, client_data);
194  break;
195  case OP_DB_LEAF:
196  if ((dp=db_lookup(lcd->dbip, tp->tr_l.tl_name, LOOKUP_QUIET)) == RT_DIR_NULL) {
197  return;
198  } else {
199  /* Create the new path, if doing so doesn't create a cyclic
200  * path (for search, that would constitute an infinite loop) */
201  if ((lcd->flags & DB_SEARCH_HIDDEN) || !(dp->d_flags & RT_DIR_HIDDEN)) {
202  struct db_full_path *newpath;
203  db_add_node_to_full_path(path, dp);
204  DB_FULL_PATH_SET_CUR_BOOL(path, bool_val);
205  BU_ALLOC(newpath, struct db_full_path);
206  db_full_path_init(newpath);
207  db_dup_full_path(newpath, path);
208  /* Insert the path in the bu_ptbl collecting paths */
209  bu_ptbl_ins(lcd->full_paths, (long *)newpath);
210  if (!cyclic_path(path, NULL)) {
211  /* Keep going */
212  traverse_func(path, client_data);
213  } else {
214  char *path_string = db_path_to_string(path);
215  bu_log("WARNING: not traversing cyclic path %s\n", path_string);
216  bu_free(path_string, "free path str");
217  }
218  }
219  DB_FULL_PATH_POP(path);
220  break;
221  }
222 
223  default:
224  bu_log("db_functree_subtree: unrecognized operator %d\n", tp->tr_op);
225  bu_bomb("db_functree_subtree: unrecognized operator\n");
226  }
227 }
228 
229 
230 /**
231  * This walker builds a list of db_full_path entries corresponding to
232  * the contents of the tree under *path. It does so while assigning
233  * the boolean operation associated with each path entry to the
234  * db_full_path structure. This list is then used for further
235  * processing and filtering by the search routines.
236  */
237 HIDDEN void
239  void *client_data)
240 {
241  struct directory *dp;
242  struct list_client_data_t *lcd= (struct list_client_data_t *)client_data;
243  RT_CK_FULL_PATH(path);
244  RT_CK_DBI(lcd->dbip);
245 
246  dp = DB_FULL_PATH_CUR_DIR(path);
247  if (!dp) return;
248  if (dp->d_flags & RT_DIR_COMB) {
249  struct rt_db_internal in;
250  struct rt_comb_internal *comb;
251 
252  if (rt_db_get_internal(&in, dp, lcd->dbip, NULL, &rt_uniresource) < 0) return;
253 
254  comb = (struct rt_comb_internal *)in.idb_ptr;
255  db_fullpath_list_subtree(path, OP_UNION, comb->tree, db_fullpath_list, client_data);
256  rt_db_free_internal(&in);
257  }
258 }
259 
260 
261 HIDDEN struct db_plan_t *
262 palloc(enum db_search_ntype t, int (*f)(struct db_plan_t *, struct db_node_t *, struct db_i *, struct bu_ptbl *))
263 {
264  struct db_plan_t *newplan;
265 
266  BU_GET(newplan, struct db_plan_t);
267  newplan->type = t;
268  newplan->eval = f;
269  return newplan;
270 }
271 
272 
273 /*
274  * (expression) functions --
275  *
276  * True if expression is true.
277  */
278 HIDDEN int
279 f_expr(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
280 {
281  struct db_plan_t *p = NULL;
282  int state = 0;
283 
284  for (p = plan->p_data[0]; p && (state = (p->eval)(p, db_node, dbip, results)); p = p->next)
285  ; /* do nothing */
286 
287  if (!state) db_node->matched_filters = 0;
288  return state;
289 }
290 
291 
292 /*
293  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
294  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
295  * to a N_EXPR node containing the expression and the ')' node is discarded.
296  */
297 HIDDEN int
298 c_openparen(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
299 {
300  (*resultplan) = (palloc(N_OPENPAREN, (int (*)(struct db_plan_t *, struct db_node_t *, struct db_i *, struct bu_ptbl *))-1));
301  return BRLCAD_OK;
302 }
303 
304 
305 HIDDEN int
306 c_closeparen(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
307 {
308  (*resultplan) = (palloc(N_CLOSEPAREN, (int (*)(struct db_plan_t *, struct db_node_t *, struct db_i *, struct bu_ptbl *))-1));
309  return BRLCAD_OK;
310 }
311 
312 
313 /*
314  * ! expression functions --
315  *
316  * Negation of a primary; the unary NOT operator.
317  */
318 HIDDEN int
319 f_not(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
320 {
321  struct db_plan_t *p = NULL;
322  int state = 0;
323 
324  for (p = plan->p_data[0]; p && (state = (p->eval)(p, db_node, dbip, results)); p = p->next)
325  ; /* do nothing */
326 
327  if (!state && db_node->matched_filters == 0) db_node->matched_filters = 1;
328  return !state;
329 }
330 
331 
332 HIDDEN int
333 c_not(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
334 {
335  (*resultplan) = (palloc(N_NOT, f_not));
336  return BRLCAD_OK;
337 }
338 
339 
340 HIDDEN int
341 find_execute_nested_plans(struct db_i *dbip, struct bu_ptbl *results, struct db_node_t *db_node, struct db_plan_t *plan) {
342  struct db_plan_t *p = NULL;
343  int state = 0;
344  for (p = plan; p && (state = (p->eval)(p, db_node, dbip, results)); p = p->next)
345  ; /* do nothing */
346 
347  return state;
348 }
349 
350 
351 /*
352  * -below expression functions --
353  *
354  * Find objects above objects matching an expression. In this case,
355  * this means following the tree path back to the root.
356  */
357 HIDDEN int
358 f_below(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
359 {
360  int state = 0;
361  int distance = 0;
362  struct db_node_t curr_node;
363  struct db_full_path parent_path;
364 
365  db_full_path_init(&parent_path);
366  db_dup_full_path(&parent_path, db_node->path);
367  DB_FULL_PATH_POP(&parent_path);
368  curr_node.path = &parent_path;
369  distance = db_node->path->fp_len - parent_path.fp_len;
370 
371  while ((parent_path.fp_len > 0) && (state == 0) && !(db_node->flags & DB_SEARCH_FLAT)) {
372  distance++;
373  if ((distance <= plan->max_depth) && (distance >= plan->min_depth)) {
374  state += find_execute_nested_plans(dbip, results, &curr_node, plan->ab_data[0]);
375  }
376  DB_FULL_PATH_POP(&parent_path);
377  }
378 
379  db_free_full_path(&parent_path);
380 
381  if (!state) db_node->matched_filters = 0;
382  return (state > 0) ? 1 : 0;
383 }
384 
385 
386 HIDDEN int
387 c_below(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
388 {
389  (*resultplan) = (palloc(N_BELOW, f_below));
390  return BRLCAD_OK;
391 }
392 
393 
394 /*
395  * -above expression functions --
396  *
397  * Find objects below objects matching an expression. Look at all
398  * objects below the current object in the tree.
399  */
400 HIDDEN int
401 f_above(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *UNUSED(results))
402 {
403  int i = 0;
404  int state = 0;
405  struct db_node_t curr_node;
406  struct bu_ptbl *full_paths = db_node->full_paths;
407 
408  unsigned int f_path_len = db_node->path->fp_len;
409 
410  for (i = 0; i < (int)BU_PTBL_LEN(full_paths); i++) {
411  struct db_full_path *this_path = (struct db_full_path *)BU_PTBL_GET(full_paths, i);
412 
413  /* Check depth criteria by comparing to db_node->path - if OK execute nested plans */
414  if (this_path->fp_len > f_path_len && db_full_path_match_top(db_node->path, this_path)) {
415  int relative_depth = this_path->fp_len - f_path_len;
416 
417  if (relative_depth >= plan->min_depth && relative_depth <= plan->max_depth) {
418  curr_node.path = this_path;
419  curr_node.flags = db_node->flags;
420  curr_node.full_paths = full_paths;
421 
422  state = find_execute_nested_plans(dbip, NULL, &curr_node, plan->bl_data[0]);
423  if (state)
424  return 1;
425  }
426  }
427  }
428 
429  db_node->matched_filters = 0;
430  return 0;
431 }
432 
433 
434 HIDDEN int
435 c_above(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
436 {
437  (*resultplan) = (palloc(N_ABOVE, f_above));
438  return BRLCAD_OK;
439 }
440 
441 
442 /*
443  * expression -o expression functions --
444  *
445  * Alternation of primaries; the OR operator. The second expression is
446  * not evaluated if the first expression is true.
447  */
448 HIDDEN int
449 f_or(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
450 {
451  struct db_plan_t *p = NULL;
452  int state = 0;
453 
454  for (p = plan->p_data[0]; p && (state = (p->eval)(p, db_node, dbip, results)); p = p->next)
455  ; /* do nothing */
456 
457  if (state)
458  return 1;
459 
460  for (p = plan->p_data[1]; p && (state = (p->eval)(p, db_node, dbip, results)); p = p->next)
461  ; /* do nothing */
462 
463  if (!state) db_node->matched_filters = 0;
464  return state;
465 }
466 
467 
468 HIDDEN int
469 c_or(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
470 {
471  (*resultplan) = (palloc(N_OR, f_or));
472  return BRLCAD_OK;
473 }
474 
475 
476 /*
477  * -name functions --
478  *
479  * True if the basename of the filename being examined
480  * matches pattern using Pattern Matching Notation S3.14
481  */
482 HIDDEN int
483 f_name(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
484 {
485  int ret = 0;
486  struct directory *dp;
487 
488  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
489  if (!dp) {
490  db_node->matched_filters = 0;
491  return 0;
492  }
493 
494  ret = !bu_fnmatch(plan->c_data, dp->d_namep, 0);
495 
496  if (!ret) db_node->matched_filters = 0;
497  return ret;
498 }
499 
500 
501 HIDDEN int
502 c_name(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
503 {
504  struct db_plan_t *newplan;
505 
506  newplan = palloc(N_NAME, f_name);
507  newplan->c_data = pattern;
508  (*resultplan) = newplan;
509  return BRLCAD_OK;
510 }
511 
512 
513 /*
514  * -iname function --
515  *
516  * True if the basename of the filename being examined
517  * matches pattern using case insensitive Pattern Matching Notation S3.14
518  */
519 HIDDEN int
520 f_iname(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
521 {
522  struct directory *dp;
523  int ret = 0;
524 
525  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
526 
527  if (!dp) {
528  db_node->matched_filters = 0;
529  return 0;
530  }
531 
532  ret = !bu_fnmatch(plan->c_data, dp->d_namep, BU_FNMATCH_CASEFOLD);
533  if (!ret) db_node->matched_filters = 0;
534  return ret;
535 }
536 
537 
538 HIDDEN int
539 c_iname(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
540 {
541  struct db_plan_t *newplan;
542 
543  newplan = palloc(N_INAME, f_iname);
544  newplan->ci_data = pattern;
545  (*resultplan) = newplan;
546 
547  return BRLCAD_OK;
548 }
549 
550 
551 /*
552  * -regex regexp (and related) functions --
553  *
554  * True if the complete file path matches the regular expression regexp.
555  * For -regex, regexp is a case-sensitive (basic) regular expression.
556  * For -iregex, regexp is a case-insensitive (basic) regular expression.
557  */
558 HIDDEN int
559 f_regex(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
560 {
561  int ret = 0;
562  ret = !(regexec(&plan->regexp_data, db_path_to_string(db_node->path), 0, NULL, 0));
563  if (!ret) db_node->matched_filters = 0;
564  return ret;
565 }
566 
567 
568 HIDDEN int
569 c_regex_common(enum db_search_ntype type, char *regexp, int icase, struct db_plan_t **resultplan)
570 {
571  regex_t reg;
572  struct db_plan_t *newplan;
573  int rv;
574 
575  if (icase == 1) {
576  rv = regcomp(&reg, regexp, REG_NOSUB|REG_EXTENDED|REG_ICASE);
577  } else {
578  rv = regcomp(&reg, regexp, REG_NOSUB|REG_EXTENDED);
579  }
580  if (rv != 0) {
581  bu_log("ERROR: regular expression failed to compile: %s\n", regexp);
582  return BRLCAD_ERROR;
583  }
584 
585  newplan = palloc(type, f_regex);
586  newplan->regexp_data = reg;
587  (*resultplan) = newplan;
588  regfree(&reg);
589 
590  return BRLCAD_OK;
591 }
592 
593 
594 HIDDEN int
595 c_regex(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
596 {
597  return c_regex_common(N_REGEX, pattern, 0, resultplan);
598 }
599 
600 
601 HIDDEN int
602 c_iregex(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
603 {
604  return c_regex_common(N_IREGEX, pattern, 1, resultplan);
605 }
606 
607 
608 HIDDEN int
609 string_to_name_and_val(const char *in, struct bu_vls *name, struct bu_vls *value) {
610  size_t equalpos = 0;
611  int checkval = 0;
612 
613  while ((equalpos < strlen(in)) &&
614  (in[equalpos] != '=') &&
615  (in[equalpos] != '>') &&
616  (in[equalpos] != '<')) {
617  if ((in[equalpos] == '/') && (in[equalpos + 1] == '=')) {equalpos++;}
618  if ((in[equalpos] == '/') && (in[equalpos + 1] == '<')) {equalpos++;}
619  if ((in[equalpos] == '/') && (in[equalpos + 1] == '>')) {equalpos++;}
620  equalpos++;
621  }
622 
623  if (equalpos == strlen(in)) {
624  /*No logical expression given - just copy attribute name*/
625  bu_vls_strcpy(name, in);
626  } else {
627  checkval = 1; /*Assume simple equality comparison, then check for other cases and change if found.*/
628  if ((in[equalpos] == '>') && (in[equalpos + 1] != '=')) {checkval = 2;}
629  if ((in[equalpos] == '<') && (in[equalpos + 1] != '=')) {checkval = 3;}
630  if ((in[equalpos] == '=') && (in[equalpos + 1] == '>')) {checkval = 4;}
631  if ((in[equalpos] == '=') && (in[equalpos + 1] == '<')) {checkval = 5;}
632  if ((in[equalpos] == '>') && (in[equalpos + 1] == '=')) {checkval = 4;}
633  if ((in[equalpos] == '<') && (in[equalpos + 1] == '=')) {checkval = 5;}
634 
635  bu_vls_strncpy(name, in, equalpos);
636  if (checkval < 4) {
637  bu_vls_strncpy(value, &(in[equalpos+1]), strlen(in) - equalpos - 1);
638  } else {
639  bu_vls_strncpy(value, &(in[equalpos+2]), strlen(in) - equalpos - 1);
640  }
641  }
642  return checkval;
643 }
644 
645 /* Check all attributes for a match to the requested attribute.
646  * If an expression was supplied, check the value of any matches
647  * to the attribute name in the logical expression before
648  * returning success
649  */
650 HIDDEN int
651 avs_check(const char *keystr, const char *value, int checkval, int strcomparison, struct bu_attribute_value_set *avs)
652 {
653  struct bu_attribute_value_pair *avpp;
654 
655  for (BU_AVS_FOR(avpp, avs)) {
656  if (!bu_fnmatch(keystr, avpp->name, 0)) {
657  if (checkval >= 1) {
658 
659  /* String based comparisons */
660  if ((checkval == 1) && (strcomparison == 1)) {
661  if (!bu_fnmatch(value, avpp->value, 0)) {
662  return 1;
663  } else {
664  return 0;
665  }
666  }
667  if ((checkval == 2) && (strcomparison == 1)) {
668  if (bu_strcmp(value, avpp->value) < 0) {
669  return 1;
670  } else {
671  return 0;
672  }
673  }
674  if ((checkval == 3) && (strcomparison == 1)) {
675  if (bu_strcmp(value, avpp->value) > 0) {
676  return 1;
677  } else {
678  return 0;
679  }
680  }
681  if ((checkval == 4) && (strcomparison == 1)) {
682  if ((!bu_fnmatch(value, avpp->value, 0)) || (bu_strcmp(value, avpp->value) < 0)) {
683  return 1;
684  } else {
685  return 0;
686  }
687  }
688  if ((checkval == 5) && (strcomparison == 1)) {
689  if ((!bu_fnmatch(value, avpp->value, 0)) || (bu_strcmp(value, avpp->value) > 0)) {
690  return 1;
691  } else {
692  return 0;
693  }
694  }
695 
696 
697  /* Numerical Comparisons */
698  if ((checkval == 1) && (strcomparison == 0)) {
699  if (atol(value) == atol(avpp->value)) {
700  return 1;
701  } else {
702  return 0;
703  }
704  }
705  if ((checkval == 2) && (strcomparison == 0)) {
706  if (atol(value) < atol(avpp->value)) {
707  return 1;
708  } else {
709  return 0;
710  }
711  }
712  if ((checkval == 3) && (strcomparison == 0)) {
713  if (atol(value) > atol(avpp->value)) {
714  return 1;
715  } else {
716  return 0;
717  }
718  }
719  if ((checkval == 4) && (strcomparison == 0)) {
720  if (atol(value) <= atol(avpp->value)) {
721  return 1;
722  } else {
723  return 0;
724  }
725  }
726  if ((checkval == 5) && (strcomparison == 0)) {
727  if (atol(value) >= atol(avpp->value)) {
728  return 1;
729  } else {
730  return 0;
731  }
732  }
733  return 0;
734  } else {
735  return 1;
736  }
737  }
738  }
739  return 0;
740 }
741 
742 
743 /*
744  * -param functions --
745  *
746  * True if the database object being examined has the parameter
747  * supplied to the param option
748  */
749 HIDDEN int
750 f_objparam(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *UNUSED(results))
751 {
752  struct bu_vls paramname = BU_VLS_INIT_ZERO;
753  struct bu_vls value = BU_VLS_INIT_ZERO;
754  struct bu_vls s_tcl = BU_VLS_INIT_ZERO;
755  struct rt_db_internal in;
756  struct bu_attribute_value_set avs;
757  int checkval = 0;
758  int strcomparison = 0;
759  size_t i;
760  struct directory *dp;
761  int ret = 0;
762 
763  /* Check for unescaped >, < or = characters. If present, the
764  * attribute must not only be present but the value assigned to
765  * the attribute must satisfy the logical expression. In the case
766  * where a > or < is used with a string argument the behavior will
767  * follow ASCII lexicographical order. In the case of equality
768  * between strings, fnmatch is used to support pattern matching
769  */
770 
771  checkval = string_to_name_and_val(plan->attr_data, &paramname, &value);
772 
773  /* Now that we have the value, check to see if it is all numbers.
774  * If so, use numerical comparison logic - otherwise use string
775  * logic.
776  */
777 
778  for (i = 0; i < strlen(bu_vls_addr(&value)); i++) {
779  if (!(isdigit((int)(bu_vls_addr(&value)[i])))) strcomparison = 1;
780  }
781 
782  /* Get parameters for object as an avs.
783  */
784 
785  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
786  if (!dp) {
787  db_node->matched_filters = 0;
788  return 0;
789  }
790 
791  RT_DB_INTERNAL_INIT(&in);
792  if (rt_db_get_internal(&in, dp, dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
793  rt_db_free_internal(&in);
794  db_node->matched_filters = 0;
795  return 0;
796  }
797 
798 
799  if ((&in)->idb_meth->ft_get(&s_tcl, &in, NULL) == BRLCAD_ERROR) {
800  rt_db_free_internal(&in);
801  db_node->matched_filters = 0;
802  return 0;
803  }
804  rt_db_free_internal(&in);
805 
806  bu_avs_init_empty(&avs);
807  if (tcl_list_to_avs(bu_vls_addr(&s_tcl), &avs, 1)) {
808  bu_avs_free(&avs);
809  bu_vls_free(&s_tcl);
810  db_node->matched_filters = 0;
811  return 0;
812  }
813 
814  ret = avs_check(bu_vls_addr(&paramname), bu_vls_addr(&value), checkval, strcomparison, &avs);
815  bu_avs_free(&avs);
816  bu_vls_free(&paramname);
817  bu_vls_free(&value);
818  if (!ret) db_node->matched_filters = 0;
819  return ret;
820 }
821 
822 
823 HIDDEN int
824 c_objparam(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
825 {
826  struct db_plan_t *newplan;
827 
828  newplan = palloc(N_ATTR, f_objparam);
829  newplan->attr_data = pattern;
830  (*resultplan) = newplan;
831  return BRLCAD_OK;
832 }
833 
834 
835 /*
836  * -attr functions --
837  *
838  * True if the database object being examined has the attribute
839  * supplied to the attr option
840  */
841 HIDDEN int
842 f_attr(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *UNUSED(results))
843 {
844  struct bu_vls attribname = BU_VLS_INIT_ZERO;
845  struct bu_vls value = BU_VLS_INIT_ZERO;
846  struct bu_attribute_value_set avs;
847  int checkval = 0;
848  int strcomparison = 0;
849  size_t i;
850  struct directory *dp;
851  int ret = 0;
852 
853  /* Check for unescaped >, < or = characters. If present, the
854  * attribute must not only be present but the value assigned to
855  * the attribute must satisfy the logical expression. In the case
856  * where a > or < is used with a string argument the behavior will
857  * follow ASCII lexicographical order. In the case of equality
858  * between strings, fnmatch is used to support pattern matching
859  */
860 
861  checkval = string_to_name_and_val(plan->attr_data, &attribname, &value);
862 
863  /* Now that we have the value, check to see if it is all numbers.
864  * If so, use numerical comparison logic - otherwise use string
865  * logic.
866  */
867 
868  for (i = 0; i < strlen(bu_vls_addr(&value)); i++) {
869  if (!(isdigit((int)(bu_vls_addr(&value)[i])))) strcomparison = 1;
870  }
871 
872  /* Get attributes for object.
873  */
874 
875  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
876  if (!dp) {
877  db_node->matched_filters = 0;
878  return 0;
879  }
880 
881  bu_avs_init_empty(&avs);
882  if (db5_get_attributes(dbip, &avs, dp) < 0) {
883  bu_avs_free(&avs);
884  db_node->matched_filters = 0;
885  return 0;
886  }
887 
888  ret = avs_check(bu_vls_addr(&attribname), bu_vls_addr(&value), checkval, strcomparison, &avs);
889  bu_avs_free(&avs);
890  bu_vls_free(&attribname);
891  bu_vls_free(&value);
892  if (!ret) db_node->matched_filters = 0;
893  return ret;
894 }
895 
896 
897 HIDDEN int
898 c_attr(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
899 {
900  struct db_plan_t *newplan;
901 
902  newplan = palloc(N_ATTR, f_attr);
903  newplan->attr_data = pattern;
904  (*resultplan) = newplan;
905  return BRLCAD_OK;
906 }
907 
908 
909 /*
910  * -stdattr function --
911  *
912  * Search based on the presence of the
913  * "standard" attributes - matches when there
914  * are ONLY "standard" attributes
915  * associated with an object.
916  */
917 HIDDEN int
918 f_stdattr(struct db_plan_t *UNUSED(plan), struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *UNUSED(results))
919 {
920  struct bu_attribute_value_pair *avpp;
921  struct bu_attribute_value_set avs;
922  struct directory *dp;
923  int found_nonstd_attr = 0;
924  int found_attr = 0;
925 
926  /* Get attributes for object and check all of them to see if there
927  * is not a match to the standard attributes. If any is found
928  * return failure, otherwise success.
929  */
930 
931  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
932  if (!dp) {
933  db_node->matched_filters = 0;
934  return 0;
935  }
936 
937  bu_avs_init_empty(&avs);
938  if (db5_get_attributes(dbip, &avs, dp) < 0) {
939  bu_avs_free(&avs);
940  db_node->matched_filters = 0;
941  return 0;
942  }
943 
944  for (BU_AVS_FOR(avpp, &avs)) {
945  found_attr = 1;
946  if (!BU_STR_EQUAL(avpp->name, "GIFTmater") &&
947  !BU_STR_EQUAL(avpp->name, "aircode") &&
948  !BU_STR_EQUAL(avpp->name, "inherit") &&
949  !BU_STR_EQUAL(avpp->name, "los") &&
950  !BU_STR_EQUAL(avpp->name, "material_id") &&
951  !BU_STR_EQUAL(avpp->name, "oshader") &&
952  !BU_STR_EQUAL(avpp->name, "region") &&
953  !BU_STR_EQUAL(avpp->name, "region_id") &&
954  !BU_STR_EQUAL(avpp->name, "rgb")) {
955 
956  found_nonstd_attr = 1;
957  }
958  }
959 
960  bu_avs_free(&avs);
961 
962  if (!found_nonstd_attr && found_attr) {
963  return 1;
964  } else {
965  db_node->matched_filters = 0;
966  return 0;
967  }
968 }
969 
970 
971 HIDDEN int
972 c_stdattr(char *UNUSED(pattern), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
973 {
974  struct db_plan_t *newplan;
975 
976  newplan = palloc(N_STDATTR, f_stdattr);
977  (*resultplan) = newplan;
978  return BRLCAD_OK;
979 }
980 
981 
982 /*
983  * -type function --
984  *
985  * Search based on the type of the object - primitives are matched
986  * based on their primitive type (tor, tgc, arb4, etc.) and
987  * combinations are matched based on whether they are a combination or
988  * region.
989  */
990 HIDDEN int
991 f_type(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *UNUSED(results))
992 {
993  struct rt_db_internal intern;
994  struct directory *dp;
995  int type_match = 0;
996  int type;
997  const struct bn_tol arb_tol = {BN_TOL_MAGIC, BN_TOL_DIST, BN_TOL_DIST * BN_TOL_DIST, 1.0e-6, 1.0 - 1.0e-6 };
998 
999  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
1000  if (!dp) return 0;
1001  if (dp->d_major_type == DB5_MAJORTYPE_ATTRIBUTE_ONLY) return 0;
1002 
1003  /* We can handle combs without needing to perform the rt_db_internal unpacking - do so
1004  * to help performance. */
1005  if (dp->d_flags & RT_DIR_COMB) {
1006  if (dp->d_flags & RT_DIR_REGION) {
1007  if ((!bu_fnmatch(plan->type_data, "r", 0)) || (!bu_fnmatch(plan->type_data, "reg", 0)) || (!bu_fnmatch(plan->type_data, "region", 0))) {
1008  type_match = 1;
1009  }
1010  }
1011  if ((!bu_fnmatch(plan->type_data, "c", 0)) || (!bu_fnmatch(plan->type_data, "comb", 0)) || (!bu_fnmatch(plan->type_data, "combination", 0))) {
1012  type_match = 1;
1013  }
1014  goto return_label;
1015  } else {
1016  if ((!bu_fnmatch(plan->type_data, "r", 0)) || (!bu_fnmatch(plan->type_data, "reg", 0)) || (!bu_fnmatch(plan->type_data, "region", 0)) || (!bu_fnmatch(plan->type_data, "c", 0)) || (!bu_fnmatch(plan->type_data, "comb", 0)) || (!bu_fnmatch(plan->type_data, "combination", 0))) {
1017  goto return_label;
1018  }
1019 
1020  }
1021 
1022  if (rt_db_get_internal(&intern, dp, dbip, (fastf_t *)NULL, &rt_uniresource) < 0) return 0;
1023  if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || !intern.idb_meth->ft_label) {
1024  rt_db_free_internal(&intern);
1025  db_node->matched_filters = 0;
1026  return 0;
1027  }
1028 
1029  switch (intern.idb_minor_type) {
1030  case DB5_MINORTYPE_BRLCAD_ARB8:
1031  type = rt_arb_std_type(&intern, &arb_tol);
1032  switch (type) {
1033  case 4:
1034  type_match = (!bu_fnmatch(plan->type_data, "arb4", 0));
1035  break;
1036  case 5:
1037  type_match = (!bu_fnmatch(plan->type_data, "arb5", 0));
1038  break;
1039  case 6:
1040  type_match = (!bu_fnmatch(plan->type_data, "arb6", 0));
1041  break;
1042  case 7:
1043  type_match = (!bu_fnmatch(plan->type_data, "arb7", 0));
1044  break;
1045  case 8:
1046  type_match = (!bu_fnmatch(plan->type_data, "arb8", 0));
1047  break;
1048  default:
1049  type_match = (!bu_fnmatch(plan->type_data, "invalid", 0));
1050  break;
1051  }
1052  break;
1053  case DB5_MINORTYPE_BRLCAD_METABALL:
1054  /* Because ft_label is only 8 characters, ft_label doesn't work in fnmatch for metaball*/
1055  type_match = (!bu_fnmatch(plan->type_data, "metaball", 0));
1056  break;
1057  default:
1058  type_match = !bu_fnmatch(plan->type_data, intern.idb_meth->ft_label, 0);
1059  break;
1060  }
1061 
1062  /* Match anything that doesn't define a 2D or 3D shape - unfortunately, this list will have to
1063  * be updated manually unless/until some functionality is added to generate it */
1064  if (!bu_fnmatch(plan->type_data, "shape", 0) &&
1065  intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_COMBINATION &&
1066  intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_ANNOTATION &&
1067  intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_CONSTRAINT &&
1068  intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_GRIP &&
1069  intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_JOINT
1070  ) {
1071  type_match = 1;
1072  }
1073 
1074  rt_db_free_internal(&intern);
1075 
1076 return_label:
1077 
1078  if (!type_match) db_node->matched_filters = 0;
1079  return type_match;
1080 }
1081 
1082 
1083 HIDDEN int
1084 c_type(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1085 {
1086  struct db_plan_t *newplan;
1087 
1088  newplan = palloc(N_TYPE, f_type);
1089  newplan->type_data = pattern;
1090  (*resultplan) = newplan;
1091 
1092  return BRLCAD_OK;
1093 }
1094 
1095 
1096 /*
1097  * -bool function --
1098  *
1099  * True if the boolean operation combining the object into the tree matches
1100  * the supplied boolean flag.
1101  *
1102  */
1103 HIDDEN int
1104 f_bool(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
1105 {
1106  int bool_match = 0;
1107  int bool_type = DB_FULL_PATH_CUR_BOOL(db_node->path);
1108  if (plan->bool_data == bool_type) bool_match = 1;
1109  if (!bool_match) db_node->matched_filters = 0;
1110  return bool_match;
1111 }
1112 
1113 
1114 HIDDEN int
1115 c_bool(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1116 {
1117  int bool_type = 0;
1118  struct db_plan_t *newplan;
1119 
1120  newplan = palloc(N_BOOL, f_bool);
1121 
1122  if (!bu_fnmatch(pattern, "u", 0) || !bu_fnmatch(pattern, "U", 0)) bool_type = 2;
1123  if (!bu_fnmatch(pattern, "+", 0)) bool_type = 3;
1124  if (!bu_fnmatch(pattern, "-", 0)) bool_type = 4;
1125 
1126  newplan->bool_data = bool_type;
1127  (*resultplan) = newplan;
1128  return BRLCAD_OK;
1129 }
1130 
1131 
1132 /*
1133  * -maxdepth function --
1134  *
1135  * True if the object being examined is at depth <= the supplied
1136  * depth.
1137  *
1138  */
1139 HIDDEN int
1140 f_maxdepth(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
1141 {
1142  int ret = ((int)db_node->path->fp_len - 1 <= plan->max_data) ? 1 : 0;
1143  if (!ret) db_node->matched_filters = 0;
1144  return ret;
1145 }
1146 
1147 
1148 HIDDEN int
1149 c_maxdepth(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1150 {
1151  struct db_plan_t *newplan;
1152 
1153  newplan = palloc(N_MAXDEPTH, f_maxdepth);
1154  newplan->max_data = atoi(pattern);
1155  (*resultplan) = newplan;
1156 
1157  return BRLCAD_OK;
1158 }
1159 
1160 
1161 /*
1162  * -mindepth function --
1163  *
1164  * True if the object being examined is at depth >= the supplied
1165  * depth.
1166  *
1167  */
1168 HIDDEN int
1169 f_mindepth(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
1170 {
1171  int ret = ((int)db_node->path->fp_len - 1 >= plan->min_data) ? 1 : 0;
1172  if (!ret) db_node->matched_filters = 0;
1173  return ret;
1174 }
1175 
1176 
1177 HIDDEN int
1178 c_mindepth(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1179 {
1180  struct db_plan_t *newplan;
1181 
1182  newplan = palloc(N_MINDEPTH, f_mindepth);
1183  newplan->min_data = atoi(pattern);
1184  (*resultplan) = newplan;
1185 
1186  return BRLCAD_OK;
1187 }
1188 
1189 
1190 /*
1191  * -depth function --
1192  *
1193  * True if the database object being examined satisfies
1194  * the depth criteria: [><=]depth
1195  */
1196 HIDDEN int
1197 f_depth(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
1198 {
1199  int ret = 0;
1200  int checkval = 0;
1201  struct bu_vls name = BU_VLS_INIT_ZERO;
1202  struct bu_vls value = BU_VLS_INIT_ZERO;
1203 
1204  /* Check for unescaped >, < or = characters. If present, the
1205  * attribute must not only be present but the value assigned to
1206  * the attribute must satisfy the logical expression. In the case
1207  * where a > or < is used with a string argument the behavior will
1208  * follow ASCII lexicographical order. In the case of equality
1209  * between strings, fnmatch is used to support pattern matching
1210  */
1211 
1212  checkval = string_to_name_and_val(plan->depth_data, &name, &value);
1213 
1214  if ((bu_vls_strlen(&value) > 0 && isdigit((int)bu_vls_addr(&value)[0]))
1215  || (bu_vls_strlen(&value) == 0 && isdigit((int)bu_vls_addr(&name)[0]))) {
1216  switch (checkval) {
1217  case 0:
1218  ret = ((int)db_node->path->fp_len - 1 == atol(bu_vls_addr(&name))) ? 1 : 0;
1219  break;
1220  case 1:
1221  ret = ((int)db_node->path->fp_len - 1 == atol(bu_vls_addr(&value))) ? 1 : 0;
1222  break;
1223  case 2:
1224  ret = ((int)db_node->path->fp_len - 1 > atol(bu_vls_addr(&value))) ? 1 : 0;
1225  break;
1226  case 3:
1227  ret = ((int)db_node->path->fp_len - 1 < atol(bu_vls_addr(&value))) ? 1 : 0;
1228  break;
1229  case 4:
1230  ret = ((int)db_node->path->fp_len - 1 >= atol(bu_vls_addr(&value))) ? 1 : 0;
1231  break;
1232  case 5:
1233  ret = ((int)db_node->path->fp_len - 1 <= atol(bu_vls_addr(&value))) ? 1 : 0;
1234  break;
1235  default:
1236  ret = 0;
1237  break;
1238  }
1239  }
1240  bu_vls_free(&name);
1241  bu_vls_free(&value);
1242 
1243  if (!ret) db_node->matched_filters = 0;
1244  return ret;
1245 }
1246 
1247 
1248 HIDDEN int
1249 c_depth(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1250 {
1251  struct db_plan_t *newplan;
1252 
1253  newplan = palloc(N_DEPTH, f_depth);
1254  newplan->attr_data = pattern;
1255  (*resultplan) = newplan;
1256 
1257  return BRLCAD_OK;
1258 }
1259 
1260 
1261 /*
1262  * -nnodes function --
1263  *
1264  * True if the object being examined is a COMB and has # nodes. If an
1265  * expression ># or <# is supplied, true if object has greater than or
1266  * less than that number of nodes. if >=# or <=# is supplied, true if
1267  * object has greater than or equal to / less than or equal to # of
1268  * nodes.
1269  *
1270  */
1271 HIDDEN int
1272 f_nnodes(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *UNUSED(results))
1273 {
1274  int dogreaterthan = 0;
1275  int dolessthan = 0;
1276  int doequal = 0;
1277  size_t node_count_target = 0;
1278  size_t node_count = 0;
1279  struct directory *dp;
1280  struct rt_db_internal in;
1281  struct rt_comb_internal *comb;
1282 
1283  /* Check for >, < and = in the first and second character
1284  * positions.
1285  */
1286 
1287  if (isdigit((int)plan->node_data[0])) {
1288  doequal = 1;
1289  node_count_target = (size_t)atoi(plan->node_data);
1290  } else {
1291  if (plan->node_data[0] == '>') dogreaterthan = 1;
1292  if (plan->node_data[0] == '<') dolessthan = 1;
1293  if (plan->node_data[0] == '=') doequal = 1;
1294  if (plan->node_data[0] != '>' && plan->node_data[0] != '<' && plan->node_data[0] != '=') {
1295  return 0;
1296  }
1297  if (plan->node_data[1] == '=') {
1298  doequal = 1;
1299  if (isdigit((int)plan->node_data[2])) {
1300  node_count_target = (size_t)atoi((plan->node_data)+2);
1301  } else {
1302  return 0;
1303  }
1304  } else {
1305  if (isdigit((int)plan->node_data[1])) {
1306  node_count_target = (size_t)atoi((plan->node_data)+1);
1307  } else {
1308  return 0;
1309  }
1310  }
1311  }
1312 
1313  /* Get the number of nodes for the current object and check if the
1314  * value satisfied the logical conditions specified in the
1315  * argument string.
1316  */
1317 
1318  dp = DB_FULL_PATH_CUR_DIR(db_node->path);
1319  if (!dp) {
1320  db_node->matched_filters = 0;
1321  return 0;
1322  }
1323 
1324  if (dp->d_flags & RT_DIR_COMB) {
1325  rt_db_get_internal(&in, dp, dbip, (fastf_t *)NULL, &rt_uniresource);
1326  comb = (struct rt_comb_internal *)in.idb_ptr;
1327  if (comb->tree == NULL) {
1328  node_count = 0;
1329  } else {
1330  node_count = db_tree_nleaves(comb->tree);
1331  }
1332  rt_db_free_internal(&in);
1333  } else {
1334  db_node->matched_filters = 0;
1335  return 0;
1336  }
1337 
1338  if (doequal) {
1339  if (node_count == node_count_target) {
1340  return 1;
1341  }
1342  }
1343  if (dolessthan) {
1344  if (node_count < node_count_target) {
1345  return 1;
1346  }
1347  } else if (dogreaterthan) {
1348  if (node_count > node_count_target) {
1349  return 1;
1350  }
1351  }
1352 
1353  db_node->matched_filters = 0;
1354  return 0;
1355 }
1356 
1357 
1358 HIDDEN int
1359 c_nnodes(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1360 {
1361  struct db_plan_t *newplan;
1362 
1363  newplan = palloc(N_NNODES, f_nnodes);
1364  newplan->node_data = pattern;
1365  (*resultplan) = newplan;
1366  return BRLCAD_OK;
1367 }
1368 
1369 
1370 /*
1371  * -path function --
1372  *
1373  * True if the object being examined shares the pattern as part of its
1374  * path. To exclude results of certain directories use the -not option
1375  * with this option.
1376  */
1377 HIDDEN int
1378 f_path(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *UNUSED(results))
1379 {
1380  int ret = !bu_fnmatch(plan->path_data, db_path_to_string(db_node->path), 0);
1381  if (!ret) db_node->matched_filters = 0;
1382  return ret;
1383 }
1384 
1385 
1386 HIDDEN int
1387 c_path(char *pattern, char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *UNUSED(db_search_isoutput))
1388 {
1389  struct db_plan_t *newplan;
1390 
1391  newplan = palloc(N_PATH, f_path);
1392  newplan->path_data = pattern;
1393  (*resultplan) = newplan;
1394  return BRLCAD_OK;
1395 }
1396 
1397 
1398 /*
1399  * -print functions --
1400  *
1401  * Always true, causes the current pathname to be added to the results
1402  * list.
1403  */
1404 HIDDEN int
1405 f_print(struct db_plan_t *UNUSED(plan), struct db_node_t *db_node, struct db_i *UNUSED(dbip), struct bu_ptbl *results)
1406 {
1407  if (!results)
1408  return 1;
1409 
1410  if (db_node->flags & DB_SEARCH_FLAT || db_node->flags & DB_SEARCH_RETURN_UNIQ_DP) {
1411  bu_ptbl_ins_unique(results, (long *)DB_FULL_PATH_CUR_DIR(db_node->path));
1412  } else {
1413  struct db_full_path *new_entry;
1414  BU_ALLOC(new_entry, struct db_full_path);
1415  db_full_path_init(new_entry);
1416  db_dup_full_path(new_entry, (const struct db_full_path *)(db_node->path));
1417  bu_ptbl_ins(results, (long *)new_entry);
1418  }
1419  return 1;
1420 }
1421 
1422 
1423 HIDDEN int
1424 c_print(char *UNUSED(ignore), char ***UNUSED(ignored), int UNUSED(unused), struct db_plan_t **resultplan, int *db_search_isoutput)
1425 {
1426  *db_search_isoutput = 1;
1427 
1428  (*resultplan) = palloc(N_PRINT, f_print);
1429  return BRLCAD_OK;
1430 }
1431 
1432 
1433 HIDDEN int
1434 typecompare(const void *a, const void *b)
1435 {
1436  return bu_strcmp(((OPTION *)a)->name, ((OPTION *)b)->name);
1437 }
1438 
1439 
1440 HIDDEN OPTION *
1441 option(char *name)
1442 {
1443  OPTION tmp;
1444 
1445  tmp.name = name;
1446  tmp.flags = 0;
1447  tmp.token = N_ABOVE;
1448  tmp.create = NULL;
1449 
1450  return ((OPTION *)bsearch(&tmp, options, sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare));
1451 }
1452 
1453 
1454 /*
1455  * create a node corresponding to a command line argument.
1456  *
1457  * TODO: add create/process function pointers to node, so we can skip
1458  * this switch stuff.
1459  */
1460 HIDDEN int
1461 find_create(char ***argvp, struct db_plan_t **resultplan, struct bu_ptbl *UNUSED(results), int *db_search_isoutput, int quiet)
1462 {
1463  OPTION *p;
1464  struct db_plan_t *newplan = NULL;
1465  char **argv;
1466  int checkval;
1467  struct bu_vls name = BU_VLS_INIT_ZERO;
1468  struct bu_vls value = BU_VLS_INIT_ZERO;
1469 
1470  if (!argvp || !resultplan)
1471  return BRLCAD_ERROR;
1472 
1473  argv = *argvp;
1474 
1475  checkval = string_to_name_and_val(argv[0], &name, &value);
1476 
1477  p = option(bu_vls_addr(&name));
1478  if (!p) {
1479  if (!quiet)
1480  bu_log("%s: unknown option passed to find_create\n", *argv);
1481  return BRLCAD_ERROR;
1482  }
1483 
1484  ++argv;
1485  if (p->flags & (O_ARGV|O_ARGVP) && !*argv) {
1486  if (!quiet)
1487  bu_log("%s: requires additional arguments\n", *--argv);
1488  return BRLCAD_ERROR;
1489  }
1490 
1491  switch (p->flags) {
1492  case O_NONE:
1493  break;
1494  case O_ZERO:
1495  (p->create)(NULL, NULL, 0, &newplan, db_search_isoutput);
1496  break;
1497  case O_ARGV:
1498  (p->create)(*argv++, NULL, 0, &newplan, db_search_isoutput);
1499  break;
1500  case O_ARGVP:
1501  (p->create)(NULL, &argv, p->token == N_OK, &newplan, db_search_isoutput);
1502  break;
1503  default:
1504  return BRLCAD_OK;
1505  }
1506 
1507  if (newplan) {
1508  if (bu_vls_strlen(&value) > 0 && isdigit((int)bu_vls_addr(&value)[0])) {
1509  switch (checkval) {
1510  case 1:
1511  newplan->min_depth = atol(bu_vls_addr(&value));
1512  newplan->max_depth = atol(bu_vls_addr(&value));
1513  break;
1514  case 2:
1515  newplan->min_depth = atol(bu_vls_addr(&value)) + 1;
1516  newplan->max_depth = INT_MAX;
1517  break;
1518  case 3:
1519  newplan->min_depth = 0;
1520  newplan->max_depth = atol(bu_vls_addr(&value)) - 1;
1521  break;
1522  case 4:
1523  newplan->min_depth = atol(bu_vls_addr(&value));
1524  newplan->max_depth = INT_MAX;
1525  break;
1526  case 5:
1527  newplan->min_depth = 0;
1528  newplan->max_depth = atol(bu_vls_addr(&value));
1529  break;
1530  default:
1531  newplan->min_depth = 0;
1532  newplan->max_depth = INT_MAX;
1533  break;
1534  }
1535  } else {
1536  newplan->min_depth = 0;
1537  newplan->max_depth = INT_MAX;
1538  }
1539  }
1540 
1541  *argvp = argv;
1542  (*resultplan) = newplan;
1543  bu_vls_free(&name);
1544  bu_vls_free(&value);
1545 
1546  return BRLCAD_OK;
1547 }
1548 
1549 
1550 /*
1551  * destructively removes the top from the plan
1552  */
1553 HIDDEN struct db_plan_t *
1554 yanknode(struct db_plan_t **planp) /* pointer to top of plan (modified) */
1555 {
1556  struct db_plan_t *node; /* top node removed from the plan */
1557 
1558  if ((node = (*planp)) == NULL)
1559  return NULL;
1560 
1561  (*planp) = (*planp)->next;
1562  node->next = NULL;
1563 
1564  return node;
1565 }
1566 
1567 
1568 /*
1569  * Removes one expression from the plan. This is used mainly by
1570  * paren_squish. In comments below, an expression is either a simple
1571  * node or a N_EXPR node containing a list of simple nodes.
1572  */
1573 HIDDEN int
1574 yankexpr(struct db_plan_t **planp, struct db_plan_t **resultplan) /* pointer to top of plan (modified) */
1575 {
1576  struct db_plan_t *next; /* temp node holding subexpression results */
1577  struct db_plan_t *node; /* pointer to returned node or expression */
1578  struct db_plan_t *tail; /* pointer to tail of subplan */
1579  struct db_plan_t *subplan; /* pointer to head of () expression */
1580  extern int f_expr(struct db_plan_t *, struct db_node_t *, struct db_i *, struct bu_ptbl *);
1581  int error_return = BRLCAD_OK;
1582 
1583  /* first pull the top node from the plan */
1584  if ((node = yanknode(planp)) == NULL) {
1585  (*resultplan) = NULL;
1586  return BRLCAD_OK;
1587  }
1588  /*
1589  * If the node is an '(' then we recursively slurp up expressions
1590  * until we find its associated ')'. If it's a closing paren we
1591  * just return it and unwind our recursion; all other nodes are
1592  * complete expressions, so just return them.
1593  */
1594  if (node->type == N_OPENPAREN)
1595  for (tail = subplan = NULL;;) {
1596  if ((error_return = yankexpr(planp, &next)) != BRLCAD_OK) return BRLCAD_ERROR;
1597  if (next == NULL) {
1598  bu_log("(: missing closing ')'\n");
1599  return BRLCAD_ERROR;
1600  }
1601  /*
1602  * If we find a closing ')' we store the collected subplan
1603  * in our '(' node and convert the node to a N_EXPR. The
1604  * ')' we found is ignored. Otherwise, we just continue
1605  * to add whatever we get to our subplan.
1606  */
1607  if (next->type == N_CLOSEPAREN) {
1608  if (subplan == NULL) {
1609  bu_log("(): empty inner expression");
1610  return BRLCAD_ERROR;
1611  }
1612  node->p_data[0] = subplan;
1613  node->type = N_EXPR;
1614  node->eval = f_expr;
1615  break;
1616  } else {
1617  if (subplan == NULL)
1618  tail = subplan = next;
1619  else {
1620  tail->next = next;
1621  tail = next;
1622  }
1623  tail->next = NULL;
1624  }
1625  }
1626  (*resultplan) = node;
1627 
1628  /* If we get here, we're OK */
1629  return BRLCAD_OK;
1630 }
1631 
1632 
1633 /*
1634  * replaces "parenthesized" plans in our search plan with "expr"
1635  * nodes.
1636  */
1637 HIDDEN int
1638 paren_squish(struct db_plan_t *plan, struct db_plan_t **resultplan) /* plan with () nodes */
1639 {
1640  struct db_plan_t *expr; /* pointer to next expression */
1641  struct db_plan_t *tail; /* pointer to tail of result plan */
1642  struct db_plan_t *result; /* pointer to head of result plan */
1643 
1644  result = tail = NULL;
1645 
1646  /*
1647  * the basic idea is to have yankexpr do all our work and just
1648  * collect its results together.
1649  */
1650  if (yankexpr(&plan, &expr) != BRLCAD_OK) return BRLCAD_ERROR;
1651  while (expr != NULL) {
1652  /*
1653  * if we find an unclaimed ')' it means there is a missing
1654  * '(' someplace.
1655  */
1656  if (expr->type == N_CLOSEPAREN) {
1657  bu_log("): no beginning '('");
1658  return BRLCAD_ERROR;
1659  }
1660 
1661  /* add the expression to our result plan */
1662  if (result == NULL)
1663  tail = result = expr;
1664  else {
1665  tail->next = expr;
1666  tail = expr;
1667  }
1668  tail->next = NULL;
1669  if (yankexpr(&plan, &expr) != BRLCAD_OK) return BRLCAD_ERROR;
1670  }
1671  (*resultplan) = result;
1672  return BRLCAD_OK;
1673 }
1674 
1675 
1676 /*
1677  * compresses "!" expressions in our search plan.
1678  */
1679 HIDDEN int
1680 not_squish(struct db_plan_t *plan, struct db_plan_t **resultplan) /* plan to process */
1681 {
1682  struct db_plan_t *next; /* next node being processed */
1683  struct db_plan_t *node; /* temporary node used in N_NOT processing */
1684  struct db_plan_t *tail; /* pointer to tail of result plan */
1685  struct db_plan_t *result; /* pointer to head of result plan */
1686 
1687  tail = result = next = NULL;
1688 
1689  while ((next = yanknode(&plan)) != NULL) {
1690  /*
1691  * if we encounter a (expression) then look for nots in the
1692  * expr subplan.
1693  */
1694  if (next->type == N_EXPR)
1695  if (not_squish(next->p_data[0], &(next->p_data[0])) != BRLCAD_OK) return BRLCAD_ERROR;
1696 
1697  /*
1698  * if we encounter a not, then snag the next node and place it
1699  * in the not's subplan. As an optimization we compress
1700  * several not's to zero or one not.
1701  */
1702  if (next->type == N_NOT) {
1703  int notlevel = 1;
1704 
1705  node = yanknode(&plan);
1706  while (node != NULL && node->type == N_NOT) {
1707  ++notlevel;
1708  node = yanknode(&plan);
1709  }
1710  if (node == NULL) {
1711  bu_log("!: no following expression");
1712  return BRLCAD_ERROR;
1713  }
1714  if (node->type == N_OR) {
1715  bu_log("!: nothing between ! and -o");
1716  return BRLCAD_ERROR;
1717  }
1718  if (node->type == N_EXPR)
1719  if (not_squish(node, &node) != BRLCAD_OK) return BRLCAD_ERROR;
1720  if (notlevel % 2 != 1)
1721  next = node;
1722  else
1723  next->p_data[0] = node;
1724  }
1725 
1726  /* add the node to our result plan */
1727  if (result == NULL)
1728  tail = result = next;
1729  else {
1730  tail->next = next;
1731  tail = next;
1732  }
1733  tail->next = NULL;
1734  }
1735  (*resultplan) = result;
1736  return BRLCAD_OK;
1737 }
1738 
1739 
1740 /*
1741  * compresses "-above" expressions in our search plan.
1742  */
1743 HIDDEN int
1744 above_squish(struct db_plan_t *plan, struct db_plan_t **resultplan) /* plan to process */
1745 {
1746  struct db_plan_t *next; /* next node being processed */
1747  struct db_plan_t *node; /* temporary node used in N_NOT processing */
1748  struct db_plan_t *tail; /* pointer to tail of result plan */
1749  struct db_plan_t *result; /* pointer to head of result plan */
1750 
1751  tail = result = next = NULL;
1752 
1753  while ((next = yanknode(&plan)) != NULL) {
1754  /*
1755  * if we encounter a (expression) then look for aboves in the
1756  * expr subplan.
1757  */
1758  if (next->type == N_EXPR)
1759  if (above_squish(next->ab_data[0], &(next->ab_data[0])) != BRLCAD_OK) return BRLCAD_ERROR;
1760 
1761  /*
1762  * if we encounter an above, then snag the next node and place it
1763  * in the not's subplan.
1764  */
1765  if (next->type == N_ABOVE) {
1766 
1767  node = yanknode(&plan);
1768  if (node != NULL && node->type == N_ABOVE) {
1769  bu_log("Error - repeated -above node in plan.\n");
1770  return BRLCAD_ERROR;
1771  }
1772  if (node == NULL) {
1773  bu_log("-above: no following expression");
1774  return BRLCAD_ERROR;
1775  }
1776  if (node->type == N_OR) {
1777  bu_log("-above: nothing between -above and -o");
1778  return BRLCAD_ERROR;
1779  }
1780  if (node->type == N_EXPR)
1781  if (above_squish(node, &node) != BRLCAD_OK) return BRLCAD_ERROR;
1782  /*Made it*/
1783  next->ab_data[0] = node;
1784  }
1785 
1786  /* add the node to our result plan */
1787  if (result == NULL)
1788  tail = result = next;
1789  else {
1790  tail->next = next;
1791  tail = next;
1792  }
1793  tail->next = NULL;
1794  }
1795  (*resultplan) = result;
1796  return BRLCAD_OK;
1797 }
1798 
1799 
1800 /*
1801  * compresses "-below" expressions in our search plan.
1802  */
1803 HIDDEN int
1804 below_squish(struct db_plan_t *plan, struct db_plan_t **resultplan) /* plan to process */
1805 {
1806  struct db_plan_t *next; /* next node being processed */
1807  struct db_plan_t *node; /* temporary node used in N_NOT processing */
1808  struct db_plan_t *tail; /* pointer to tail of result plan */
1809  struct db_plan_t *result; /* pointer to head of result plan */
1810 
1811  tail = result = next = NULL;
1812 
1813  while ((next = yanknode(&plan)) != NULL) {
1814  /*
1815  * if we encounter a (expression) then look for nots in
1816  * the expr subplan.
1817  */
1818  if (next->type == N_EXPR)
1819  if (below_squish(next->bl_data[0], &(next->bl_data[0])) != BRLCAD_OK) return BRLCAD_ERROR;
1820 
1821  /*
1822  * if we encounter a not, then snag the next node and place it
1823  * in the not's subplan.
1824  */
1825  if (next->type == N_BELOW) {
1826 
1827  node = yanknode(&plan);
1828  if (node != NULL && node->type == N_BELOW) {
1829  bu_log("Error - repeated -below node in plan.\n");
1830  return BRLCAD_ERROR;
1831  }
1832  if (node == NULL) {
1833  bu_log("-below: no following expression");
1834  return BRLCAD_ERROR;
1835  }
1836  if (node->type == N_OR) {
1837  bu_log("-below: nothing between -below and -o");
1838  return BRLCAD_ERROR;
1839  }
1840  if (node->type == N_EXPR)
1841  if (below_squish(node, &node) != BRLCAD_OK) return BRLCAD_ERROR;
1842  /* Made it */
1843  next->bl_data[0] = node;
1844  }
1845 
1846  /* add the node to our result plan */
1847  if (result == NULL)
1848  tail = result = next;
1849  else {
1850  tail->next = next;
1851  tail = next;
1852  }
1853  tail->next = NULL;
1854  }
1855  (*resultplan) = result;
1856  return BRLCAD_OK;
1857 }
1858 
1859 
1860 /*
1861  * compresses -o expressions in our search plan.
1862  */
1863 HIDDEN int
1864 or_squish(struct db_plan_t *plan, struct db_plan_t **resultplan) /* plan with ors to be squished */
1865 {
1866  struct db_plan_t *next; /* next node being processed */
1867  struct db_plan_t *tail; /* pointer to tail of result plan */
1868  struct db_plan_t *result; /* pointer to head of result plan */
1869 
1870  tail = result = next = NULL;
1871 
1872  while ((next = yanknode(&plan)) != NULL) {
1873  /*
1874  * if we encounter a (expression) then look for or's in
1875  * the expr subplan.
1876  */
1877  if (next->type == N_EXPR)
1878  if (or_squish(next->p_data[0], &(next->p_data[0])) != BRLCAD_OK) return BRLCAD_ERROR;
1879 
1880  /* if we encounter a not then look for not's in the subplan */
1881  if (next->type == N_NOT)
1882  if (or_squish(next->p_data[0], &(next->p_data[0])) != BRLCAD_OK) return BRLCAD_ERROR;
1883 
1884  /*
1885  * if we encounter an or, then place our collected plan in the
1886  * or's first subplan and then recursively collect the
1887  * remaining stuff into the second subplan and return the or.
1888  */
1889  if (next->type == N_OR) {
1890  if (result == NULL) {
1891  bu_log("-o: no expression before -o");
1892  return BRLCAD_ERROR;
1893  }
1894  next->p_data[0] = result;
1895  if (or_squish(plan, &(next->p_data[1])) != BRLCAD_OK) return BRLCAD_ERROR;
1896  if (next->p_data[1] == NULL) {
1897  bu_log("-o: no expression after -o");
1898  return BRLCAD_ERROR;
1899  }
1900  (*resultplan) = next;
1901  return BRLCAD_OK;
1902  }
1903 
1904  /* add the node to our result plan */
1905  if (result == NULL)
1906  tail = result = next;
1907  else {
1908  tail->next = next;
1909  tail = next;
1910  }
1911  tail->next = NULL;
1912  }
1913  (*resultplan) = result;
1914  return BRLCAD_OK;
1915 }
1916 
1917 
1918 HIDDEN struct db_plan_t *
1919 db_search_form_plan(char **argv, int quiet) {
1920  struct db_plan_t *plan, *tail;
1921  struct db_plan_t *newplan = NULL;
1922  struct bu_ptbl *results = NULL;
1923  int db_search_isoutput = 0;
1924 
1925  /*
1926  * for each argument in the command line, determine what kind of
1927  * node it is, create the appropriate node type and add the new
1928  * plan node to the end of the existing plan. The resulting plan
1929  * is a linked list of plan nodes. For example, the string:
1930  *
1931  * % find . -name foo -newer bar -print
1932  *
1933  * results in the plan:
1934  *
1935  * [-name foo]--> [-newer bar]--> [-print]
1936  *
1937  * in this diagram, `[-name foo]' represents the plan node generated
1938  * by c_name() with an argument of foo and `-->' represents the
1939  * plan->next pointer.
1940  */
1941  for (plan = tail = NULL; *argv;) {
1942  if (find_create(&argv, &newplan, results, &db_search_isoutput, quiet) != BRLCAD_OK) return NULL;
1943  if (!newplan)
1944  continue;
1945  if (plan == NULL)
1946  tail = plan = newplan;
1947  else {
1948  tail->next = newplan;
1949  tail = newplan;
1950  }
1951  }
1952 
1953  /*
1954  * if the user didn't specify one of -print, -ok or -exec, then
1955  * -print is assumed so we bracket the current expression with
1956  * parens, if necessary, and add a -print node on the end.
1957  */
1958  if (!db_search_isoutput) {
1959  if (plan == NULL) {
1960  c_print(NULL, NULL, 0, &newplan, &db_search_isoutput);
1961  tail = plan = newplan;
1962  } else {
1963  c_openparen(NULL, NULL, 0, &newplan, &db_search_isoutput);
1964  newplan->next = plan;
1965  plan = newplan;
1966  c_closeparen(NULL, NULL, 0, &newplan, &db_search_isoutput);
1967  tail->next = newplan;
1968  tail = newplan;
1969  c_print(NULL, NULL, 0, &newplan, &db_search_isoutput);
1970  tail->next = newplan;
1971  tail = newplan;
1972  }
1973  }
1974 
1975  /*
1976  * the command line has been completely processed into a search
1977  * plan except for the (,), !, and -o operators. Rearrange the
1978  * plan so that the portions of the plan which are affected by the
1979  * operators are moved into operator nodes themselves. For
1980  * example:
1981  *
1982  * [!]--> [-name foo]--> [-print]
1983  *
1984  * becomes
1985  *
1986  * [! [-name foo] ]--> [-print]
1987  *
1988  * and
1989  *
1990  * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
1991  *
1992  * becomes
1993  *
1994  * [expr [-depth]-->[-name foo] ]--> [-print]
1995  *
1996  * operators are handled in order of precedence.
1997  */
1998 
1999  if (paren_squish(plan, &plan) != BRLCAD_OK) return NULL; /* ()'s */
2000  if (above_squish(plan, &plan) != BRLCAD_OK) return NULL; /* above's */
2001  if (below_squish(plan, &plan) != BRLCAD_OK) return NULL; /* below's */
2002  if (not_squish(plan, &plan) != BRLCAD_OK) return NULL; /* !'s */
2003  if (or_squish(plan, &plan) != BRLCAD_OK) return NULL; /* -o's */
2004  return plan;
2005 }
2006 
2007 
2008 HIDDEN void
2009 find_execute_plans(struct db_i *dbip, struct bu_ptbl *results, struct db_node_t *db_node, struct db_plan_t *plan) {
2010  struct db_plan_t *p;
2011  for (p = plan; p && (p->eval)(p, db_node, dbip, results); p = p->next)
2012  ;
2013 }
2014 
2015 HIDDEN void
2016 db_search_free_plan(void **vplan) {
2017  struct db_plan_t *p;
2018  struct db_plan_t *plan = (struct db_plan_t *)*vplan;
2019  for (p = plan; p;) {
2020  plan = p->next;
2021  BU_PUT(p, struct db_plan_t);
2022  p = plan;
2023  }
2024  /* sanity */
2025  *vplan = NULL;
2026 }
2027 
2028 
2029 /*********** DEPRECATED search functionality ******************/
2030 int
2031 db_full_path_list_add(const char *path, int local, struct db_i *dbip, struct db_full_path_list *path_list)
2032 {
2033  struct db_full_path dfp;
2034  struct db_full_path_list *new_entry;
2035  db_full_path_init(&dfp);
2036  /* Turn string path into a full path structure */
2037  if (db_string_to_path(&dfp, dbip, path) == -1) {
2038  db_free_full_path(&dfp);
2039  return 1;
2040  }
2041  /* Make a search list and add the entry from the directory name full path */
2042  BU_ALLOC(new_entry, struct db_full_path_list);
2043  BU_ALLOC(new_entry->path, struct db_full_path);
2044  db_full_path_init(new_entry->path);
2045  db_dup_full_path(new_entry->path, (const struct db_full_path *)&dfp);
2046  new_entry->local = local;
2047  BU_LIST_PUSH(&(path_list->l), &(new_entry->l));
2048  db_free_full_path(&dfp);
2049  return 0;
2050 }
2051 
2052 
2053 /* Internal version of deprecated function */
2054 void
2056 {
2057  struct db_full_path_list *currentpath;
2058 
2059  if (!path_list)
2060  return;
2061 
2062  if (!BU_LIST_IS_EMPTY(&(path_list->l))) {
2063  while (BU_LIST_WHILE(currentpath, db_full_path_list, &(path_list->l))) {
2064  db_free_full_path(currentpath->path);
2065  BU_LIST_DEQUEUE((struct bu_list *)currentpath);
2066  bu_free(currentpath->path, "free db_full_path_list path entry");
2067  bu_free(currentpath, "free db_full_path_list entry");
2068  }
2069  }
2070 
2071  bu_free(path_list, "free path_list");
2072 }
2073 
2074 
2075 void
2077 {
2078  _db_free_full_path_list(path_list);
2079 }
2080 
2081 
2082 void
2083 db_search_freeplan(void **vplan) {
2084  db_search_free_plan(vplan);
2085 }
2086 
2087 
2088 void
2089 db_search_free(struct bu_ptbl *search_results)
2090 {
2091  int i;
2092  if (!search_results || search_results->l.magic != BU_PTBL_MAGIC)
2093  return;
2094 
2095  for (i = (int)BU_PTBL_LEN(search_results) - 1; i >= 0; i--) {
2096  struct db_full_path *path = (struct db_full_path *)BU_PTBL_GET(search_results, i);
2097  if (path->magic && path->magic == DB_FULL_PATH_MAGIC) {
2098  db_free_full_path(path);
2099  bu_free(path, "free search path container");
2100  }
2101  }
2102  bu_ptbl_free(search_results);
2103 }
2104 
2105 
2106 /* Internal version of deprecated function */
2107 struct db_full_path_list *
2108 _db_search_full_paths(void *searchplan,
2109  struct db_full_path_list *pathnames,
2110  struct db_i *dbip)
2111 {
2112  int i;
2113  struct directory *dp;
2114  struct db_full_path dfp;
2115  struct db_full_path *dfptr;
2116  struct db_full_path_list *new_entry = NULL;
2117  struct db_full_path_list *currentpath = NULL;
2118  struct db_full_path_list *searchresults_list = NULL;
2119  struct bu_ptbl *searchresults = NULL;
2120 
2121  BU_ALLOC(searchresults_list, struct db_full_path_list);
2122  BU_LIST_INIT(&(searchresults_list->l));
2123  BU_ALLOC(searchresults, struct bu_ptbl);
2124  bu_ptbl_init(searchresults, 8, "initialize searchresults table");
2125 
2126  /* If nothing is passed in, try to get the list of toplevel objects */
2127  if (BU_LIST_IS_EMPTY(&(pathnames->l))) {
2128  db_full_path_init(&dfp);
2129  for (i = 0; i < RT_DBNHASH; i++) {
2130  for (dp = dbip->dbi_Head[i]; dp != RT_DIR_NULL; dp = dp->d_forw) {
2131  if (dp->d_nref == 0 && (dp->d_addr != RT_DIR_PHONY_ADDR)) {
2132  if (db_string_to_path(&dfp, dbip, dp->d_namep) < 0)
2133  continue; /* skip */
2134 
2135  BU_ALLOC(new_entry, struct db_full_path_list);
2136  BU_ALLOC(new_entry->path, struct db_full_path);
2137 
2138  db_full_path_init(new_entry->path);
2139  db_dup_full_path(new_entry->path, (const struct db_full_path *)&dfp);
2140  BU_LIST_PUSH(&(pathnames->l), &(new_entry->l));
2141  }
2142  }
2143  }
2144  db_free_full_path(&dfp);
2145  }
2146 
2147  for (BU_LIST_FOR(currentpath, db_full_path_list, &(pathnames->l))) {
2148  struct bu_ptbl *full_paths = NULL;
2149  struct db_full_path *start_path = NULL;
2150  struct db_node_t curr_node;
2151  struct list_client_data_t lcd;
2152 
2153  BU_ALLOC(full_paths, struct bu_ptbl);
2154  BU_PTBL_INIT(full_paths);
2155  BU_ALLOC(start_path, struct db_full_path);
2156  db_dup_full_path(start_path, currentpath->path);
2157  curr_node.path = start_path;
2158  /* by convention, a top level node is "unioned" into the global database */
2159  DB_FULL_PATH_SET_CUR_BOOL(curr_node.path, 2);
2160  bu_ptbl_ins(full_paths, (long *)start_path);
2161  lcd.dbip = dbip;
2162  lcd.full_paths = full_paths;
2163  db_fullpath_list(start_path, (void **)&lcd);
2164 
2165  for (i = 0; i < (int)BU_PTBL_LEN(full_paths); i++) {
2166  curr_node.path = (struct db_full_path *)BU_PTBL_GET(full_paths, i);
2167  curr_node.flags = 0;
2168  curr_node.full_paths = full_paths;
2169  find_execute_plans(dbip, searchresults, &curr_node, (struct db_plan_t *)searchplan);
2170  }
2171  db_search_free(full_paths);
2172  bu_free(full_paths, "free search container");
2173 
2174  }
2175  for (i = 0; i < (int)BU_PTBL_LEN(searchresults); i++) {
2176  BU_ALLOC(new_entry, struct db_full_path_list);
2177  BU_ALLOC(new_entry->path, struct db_full_path);
2178  dfptr = (struct db_full_path *)BU_PTBL_GET(searchresults, i);
2179  db_full_path_init(new_entry->path);
2180  db_dup_full_path(new_entry->path, dfptr);
2181  BU_LIST_PUSH(&(searchresults_list->l), &(new_entry->l));
2182  }
2183  for (i = (int)BU_PTBL_LEN(searchresults) - 1; i >= 0; i--) {
2184  dfptr = (struct db_full_path *)BU_PTBL_GET(searchresults, i);
2185  db_free_full_path(dfptr);
2186  }
2187  bu_ptbl_free(searchresults);
2188  return searchresults_list;
2189 }
2190 
2191 
2192 struct db_full_path_list *
2193 db_search_full_paths(void *searchplan,
2194  struct db_full_path_list *pathnames,
2195  struct db_i *dbip)
2196 {
2197  return _db_search_full_paths(searchplan, pathnames, dbip);
2198 }
2199 
2200 
2201 struct bu_ptbl *
2202 db_search_unique_objects(void *searchplan,
2203  struct db_full_path_list *pathnames,
2204  struct db_i *dbip)
2205 {
2206  struct bu_ptbl *uniq_db_objs = NULL;
2207  struct db_full_path_list *entry = NULL;
2208  struct db_full_path_list *search_results = NULL;
2209 
2210  BU_ALLOC(uniq_db_objs, struct bu_ptbl);
2211 
2212  search_results = _db_search_full_paths(searchplan, pathnames, dbip);
2213  bu_ptbl_init(uniq_db_objs, 8, "initialize ptr table");
2214  for (BU_LIST_FOR(entry, db_full_path_list, &(search_results->l))) {
2215  bu_ptbl_ins_unique(uniq_db_objs, (long *)entry->path->fp_names[entry->path->fp_len - 1]);
2216  }
2217  _db_free_full_path_list(search_results);
2218 
2219  return uniq_db_objs;
2220 }
2221 
2222 
2223 void *
2224 db_search_formplan(char **argv, struct db_i *UNUSED(dbip)) {
2225  return (void *)db_search_form_plan(argv, 0);
2226 }
2227 
2228 
2229 /*********** New search functionality ******************/
2230 
2231 int
2232 db_search(struct bu_ptbl *search_results,
2233  int search_flags,
2234  const char *plan_str,
2235  int input_path_cnt,
2236  struct directory **input_paths,
2237  struct db_i *dbip)
2238 {
2239  int i = 0;
2240  int result_cnt = 0;
2241  struct db_plan_t *dbplan = NULL;
2242  struct directory **top_level_objects = NULL;
2243  struct directory **paths = input_paths;
2244  int path_cnt = input_path_cnt;
2245 
2246  /* Note that dbplan references strings using memory
2247  * in the following two objects, so they mustn't be
2248  * freed until the plan is freed.*/
2249  char **plan_argv = (char **)bu_calloc(strlen(plan_str) + 1, sizeof(char *), "plan argv");
2250 
2251  /* make a copy so we can mess with it */
2252  char *mutable_plan_str = bu_strdup(plan_str);
2253 
2254 
2255  /* get the plan string into an argv array */
2256  bu_argv_from_string(&plan_argv[0], strlen(plan_str), mutable_plan_str);
2257  if (!(search_flags & DB_SEARCH_QUIET)) {
2258  dbplan = db_search_form_plan(plan_argv, 0);
2259  } else {
2260  dbplan = db_search_form_plan(plan_argv, 1);
2261  }
2262  /* No plan, no search */
2263  if (!dbplan) {
2264  bu_free(mutable_plan_str, "free strdup");
2265  bu_free((char *)plan_argv, "free plan argv");
2266  return -1;
2267  }
2268  /* If the idea was to test the plan string and we *do* have
2269  * a plan, return success */
2270  if (!dbip) {
2271  db_search_free_plan((void **)&dbplan);
2272  bu_free(mutable_plan_str, "free strdup");
2273  bu_free((char *)plan_argv, "free plan argv");
2274  return -2;
2275  }
2276 
2277  if (!paths) {
2278  if (search_flags & DB_SEARCH_HIDDEN) {
2279  path_cnt = db_ls(dbip, DB_LS_TOPS | DB_LS_HIDDEN, NULL, &top_level_objects);
2280  } else {
2281  path_cnt = db_ls(dbip, DB_LS_TOPS, NULL, &top_level_objects);
2282  }
2283  paths = top_level_objects;
2284  }
2285 
2286  /* execute the plan */
2287  {
2288  struct bu_ptbl *full_paths = NULL;
2289  struct list_client_data_t lcd;
2290 
2291  /* First, check if search_results is initialized - don't trust the caller to do it,
2292  * but it's fine if they did */
2293  if (search_results && search_results != BU_PTBL_NULL) {
2294  if (!BU_PTBL_IS_INITIALIZED(search_results))
2295  BU_PTBL_INIT(search_results);
2296  }
2297 
2298  /* Build a set of all full paths under the supplied paths, including the starting
2299  * paths themselves */
2300  if (!(search_flags & DB_SEARCH_FLAT)) {
2301  BU_ALLOC(full_paths, struct bu_ptbl);
2302  BU_PTBL_INIT(full_paths);
2303  lcd.dbip = dbip;
2304  lcd.full_paths = full_paths;
2305  lcd.flags = search_flags;
2306  }
2307 
2308  /* If we're doing a flat search, we can handle everything in this loop.
2309  * Otherwise, we're building a list of full paths to be handled in a
2310  * second pass. */
2311  for (i = 0; i < path_cnt; i++) {
2312  struct directory *curr_dp = paths[i];
2313  struct db_full_path *start_path = NULL;
2314 
2315  if (curr_dp == RT_DIR_NULL) {
2316  continue;
2317  }
2318 
2319  if ((search_flags & DB_SEARCH_HIDDEN) || !(curr_dp->d_flags & RT_DIR_HIDDEN)) {
2320 
2321  BU_ALLOC(start_path, struct db_full_path);
2322  db_full_path_init(start_path);
2323  db_add_node_to_full_path(start_path, curr_dp);
2324  DB_FULL_PATH_SET_CUR_BOOL(start_path, 2);
2325 
2326  /* For a flat search, we don't need to build a table of paths -
2327  * just run the filters on the path */
2328  if (search_flags & DB_SEARCH_FLAT) {
2329  struct db_node_t curr_node;
2330  /* by convention, a top level node is "unioned" into the global database */
2331  curr_node.path = start_path;
2332  curr_node.flags = search_flags;
2333  curr_node.matched_filters = 1;
2334  find_execute_plans(dbip, search_results, &curr_node, dbplan);
2335  result_cnt += curr_node.matched_filters;
2336  DB_FULL_PATH_POP(start_path);
2337  } else {
2338  /* by convention, a top level node is "unioned" into the global database */
2339  bu_ptbl_ins(full_paths, (long *)start_path);
2340  /* Use the initial path to tree-walk and build a set of all paths below
2341  * start_path */
2342  db_fullpath_list(start_path, (void **)&lcd);
2343  }
2344  }
2345  }
2346 
2347  if (!(search_flags & DB_SEARCH_FLAT)) {
2348  for (i = 0; i < (int)BU_PTBL_LEN(full_paths); i++) {
2349  struct db_node_t curr_node;
2350  curr_node.path = (struct db_full_path *)BU_PTBL_GET(full_paths, i);
2351  curr_node.full_paths = full_paths;
2352  curr_node.flags = search_flags;
2353  curr_node.matched_filters = 1;
2354  find_execute_plans(dbip, search_results, &curr_node, dbplan);
2355  result_cnt += curr_node.matched_filters;
2356  }
2357 
2358  /* Done with the paths now - we have our answer */
2359  db_search_free(full_paths);
2360  bu_free(full_paths, "free search container");
2361  }
2362  }
2363 
2364  db_search_free_plan((void **)&dbplan);
2365  bu_free(mutable_plan_str, "free strdup");
2366  bu_free((char *)plan_argv, "free plan argv");
2367 
2368  if (top_level_objects) {
2369  bu_free((void *)top_level_objects, "free tops");
2370  }
2371 
2372  return result_cnt;
2373 }
2374 
2375 
2376 /*
2377  * Local Variables:
2378  * tab-width: 8
2379  * mode: C
2380  * indent-tabs-mode: t
2381  * c-file-style: "stroustrup"
2382  * End:
2383  * ex: shiftwidth=4 tabstop=8
2384  */
char * name
Definition: search.h:152
Definition: search.h:74
char * d_namep
pointer to name string
Definition: raytrace.h:859
#define BU_LIST_FOR(p, structure, hp)
Definition: list.h:365
Definition: raytrace.h:800
void db_search_freeplan(void **vplan)
Definition: search.c:2083
struct db_i * dbip
Definition: search.c:133
unsigned char d_major_type
object major type
Definition: raytrace.h:870
void db_add_node_to_full_path(struct db_full_path *pp, struct directory *dp)
Definition: db_fullpath.c:54
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
#define RT_CHECK_DBI(_p)
Definition: raytrace.h:828
HIDDEN int f_attr(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:842
int rt_db_get_internal(struct rt_db_internal *ip, const struct directory *dp, const struct db_i *dbip, const mat_t mat, struct resource *resp)
Definition: dir.c:76
HIDDEN int f_not(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:319
#define RT_DBNHASH
hash table is an array of linked lists with this many array pointer elements (Memory use for 32-bit: ...
Definition: raytrace.h:755
void _db_free_full_path_list(struct db_full_path_list *path_list)
Definition: search.c:2055
HIDDEN int c_bool(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1115
void bu_avs_init_empty(struct bu_attribute_value_set *avp)
Definition: avs.c:36
void db_search_free(struct bu_ptbl *search_results)
Definition: search.c:2089
#define DB_SEARCH_HIDDEN
Search using hidden objects.
Definition: search.h:112
Definition: list.h:118
enum db_search_ntype type
Definition: search.h:96
Definition: search.h:77
#define BU_FNMATCH_CASEFOLD
Definition: file.h:173
size_t fp_len
Definition: db_fullpath.h:44
Definition: search.h:78
#define DB_SEARCH_FLAT
Do a flat search without hierarchy.
Definition: search.h:111
void db_dup_full_path(struct db_full_path *newp, const struct db_full_path *oldp)
Definition: db_fullpath.c:87
size_t db_tree_nleaves(const union tree *tp)
Definition: db_comb.c:99
long d_nref
times ref'ed by COMBs
Definition: raytrace.h:868
struct db_plan_t * next
Definition: search.h:83
Definition: clone.c:90
HIDDEN int c_regex(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:595
void bu_ptbl_init(struct bu_ptbl *b, size_t len, const char *str)
Definition: ptbl.c:32
HIDDEN int c_objparam(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:824
HIDDEN int f_name(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:483
#define OP_XOR
Binary: L xor R, not both.
Definition: raytrace.h:1130
Definition: search.h:78
Definition: search.h:77
#define BU_LIST_IS_EMPTY(hp)
Definition: list.h:295
HIDDEN int c_mindepth(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1178
struct directory * db_lookup(const struct db_i *, const char *name, int noisy)
Definition: db_lookup.c:153
HIDDEN int f_print(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1405
db_search_ntype
Definition: search.h:72
#define BN_TOL_MAGIC
Definition: magic.h:74
Definition: search.h:75
void bu_vls_strncpy(struct bu_vls *vp, const char *s, size_t n)
Definition: vls.c:339
HIDDEN int not_squish(struct db_plan_t *plan, struct db_plan_t **resultplan)
Definition: search.c:1680
HIDDEN int f_depth(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1197
#define DB_SEARCH_RETURN_UNIQ_DP
Return the set of unique directory pointers instead of full paths.
Definition: search.h:113
void * db_search_formplan(char **argv, struct db_i *dbip)
Definition: search.c:2224
Header file for the BRL-CAD common definitions.
int bu_ptbl_ins_unique(struct bu_ptbl *b, long *p)
Definition: search.h:74
#define OP_XNOP
Unary: L, mark region.
Definition: raytrace.h:1136
#define DB_FULL_PATH_CUR_DIR(_pp)
Definition: db_fullpath.h:51
const char * value
Definition: avs.h:62
#define RT_DIR_REGION
region
Definition: raytrace.h:885
HIDDEN int c_openparen(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:298
HIDDEN int f_iname(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:520
HIDDEN int f_above(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:401
HIDDEN int f_path(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1378
HIDDEN int c_maxdepth(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1149
int bu_ptbl_ins(struct bu_ptbl *b, long *p)
HIDDEN int c_or(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:469
HIDDEN int typecompare(const void *a, const void *b)
Definition: search.c:1434
struct directory * d_forw
link to next dir entry
Definition: raytrace.h:864
void db_full_path_init(struct db_full_path *pathp)
Definition: db_fullpath.c:40
char ft_label[9]
Definition: raytrace.h:2044
HIDDEN void db_search_free_plan(void **vplan)
Definition: search.c:2016
#define HIDDEN
Definition: common.h:86
union tree * tb_left
Definition: raytrace.h:1149
int max_depth
Definition: search.h:94
Definition: ptbl.h:62
int(* create)(char *, char ***, int, struct db_plan_t **, int *)
Definition: search.h:154
int db_full_path_match_top(const struct db_full_path *a, const struct db_full_path *b)
Definition: db_fullpath.c:559
HIDDEN int c_above(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:435
if(share_geom)
Definition: nmg_mod.c:3829
int idb_major_type
Definition: raytrace.h:192
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
HIDDEN int f_objparam(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:750
#define OP_SUBTRACT
Binary: L subtract R.
Definition: raytrace.h:1129
HIDDEN int f_expr(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:279
HIDDEN int f_type(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:991
HIDDEN int below_squish(struct db_plan_t *plan, struct db_plan_t **resultplan)
Definition: search.c:1804
HIDDEN int c_nnodes(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1359
struct resource rt_uniresource
default. Defined in librt/globals.c
Definition: globals.c:41
#define OP_INTERSECT
Binary: L intersect R.
Definition: raytrace.h:1128
HIDDEN int c_below(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:387
#define OP_DB_LEAF
Leaf of combination, db fmt.
Definition: raytrace.h:1139
Definition: search.h:73
HIDDEN int c_print(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1424
HIDDEN int above_squish(struct db_plan_t *plan, struct db_plan_t **resultplan)
Definition: search.c:1744
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
struct bu_ptbl * full_paths
Definition: search.c:134
#define BU_PTBL_IS_INITIALIZED(_p)
Definition: ptbl.h:98
HIDDEN void db_fullpath_list_subtree(struct db_full_path *path, int curr_bool, union tree *tp, void(*traverse_func)(struct db_full_path *path, void *), void *client_data)
Definition: search.c:165
HIDDEN int f_below(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:358
int db_string_to_path(struct db_full_path *pp, const struct db_i *dbip, const char *str)
Definition: db_fullpath.c:361
#define BU_PTBL_GET(ptbl, i)
Definition: ptbl.h:108
#define O_NONE
Definition: search.h:155
#define RT_DB_INTERNAL_INIT(_p)
Definition: raytrace.h:199
void bu_vls_sprintf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:707
#define LOOKUP_QUIET
Definition: raytrace.h:893
#define DB_LS_TOPS
Definition: raytrace.h:4657
const struct rt_functab * idb_meth
for ft_ifree(), etc.
Definition: raytrace.h:194
void db_free_full_path(struct db_full_path *pp)
Definition: db_fullpath.c:473
Definition: search.h:77
#define BRLCAD_OK
Definition: defines.h:71
int bu_fnmatch(const char *pattern, const char *pathname, int flags)
Definition: fnmatch.c:311
#define BU_GET(_ptr, _type)
Definition: malloc.h:201
struct db_full_path * path
Definition: search.h:133
HIDDEN int c_iregex(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:602
char * options
Definition: gqa.c:56
HIDDEN int c_not(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:333
#define BU_AVS_FOR(_pp, _avp)
Definition: avs.h:141
int flags
Definition: search.h:67
HIDDEN int c_attr(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:898
#define RT_DIR_PHONY_ADDR
Special marker for d_addr field.
Definition: raytrace.h:879
#define RT_DIR_HIDDEN
object name is hidden
Definition: raytrace.h:886
#define O_ARGV
Definition: search.h:157
HIDDEN OPTION * option(char *name)
Definition: search.c:1441
#define BN_TOL_DIST
Definition: tol.h:109
size_t bu_vls_strlen(const struct bu_vls *vp)
Definition: vls.c:189
char * tl_name
Name of this leaf (bu_strdup'ed)
Definition: raytrace.h:1174
struct bu_list l
Definition: ptbl.h:63
struct tree::tree_node tr_b
uint32_t magic
Definition: db_fullpath.h:43
HIDDEN int c_name(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:502
void db_free_full_path_list(struct db_full_path_list *path_list)
Definition: search.c:2076
#define UNUSED(parameter)
Definition: common.h:239
#define BU_PTBL_INIT(_p)
Definition: ptbl.h:80
Definition: search.h:77
#define OP_GUARD
Unary: not L, or else!
Definition: raytrace.h:1135
#define BU_PUT(_ptr, _type)
Definition: malloc.h:215
#define BU_PTBL_MAGIC
Definition: magic.h:59
Support for uniform tolerances.
Definition: tol.h:71
struct bu_list l
Definition: search.h:132
HIDDEN int f_stdattr(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:918
struct directory ** fp_names
array of dir pointers
Definition: db_fullpath.h:46
char * db_path_to_string(const struct db_full_path *pp)
Definition: db_fullpath.c:191
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
HIDDEN int c_iname(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:539
uint32_t magic
Magic # for mem id/check.
Definition: list.h:119
#define BU_LIST_WHILE(p, structure, hp)
Definition: list.h:410
size_t db_ls(const struct db_i *dbip, int flags, const char *pattern, struct directory ***dpv)
Definition: ls.c:58
HIDDEN void find_execute_plans(struct db_i *dbip, struct bu_ptbl *results, struct db_node_t *db_node, struct db_plan_t *plan)
Definition: search.c:2009
HIDDEN int f_maxdepth(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1140
#define BU_PTBL_LEN(ptbl)
Definition: ptbl.h:107
Definition: search.h:77
HIDDEN void db_fullpath_list(struct db_full_path *path, void *client_data)
Definition: search.c:238
void bu_ptbl_free(struct bu_ptbl *b)
Definition: ptbl.c:226
HIDDEN int f_or(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:449
#define BU_LIST_PUSH(hp, p)
Definition: list.h:246
#define RT_CK_DBI(_p)
Definition: raytrace.h:829
#define RT_CK_FULL_PATH(_p)
Definition: db_fullpath.h:59
#define BU_PTBL_NULL
Definition: ptbl.h:69
int bu_strcmp(const char *string1, const char *string2)
Definition: str.c:171
HIDDEN int paren_squish(struct db_plan_t *plan, struct db_plan_t **resultplan)
Definition: search.c:1638
HIDDEN int c_type(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1084
HIDDEN int f_mindepth(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1169
struct tree::tree_db_leaf tr_l
union tree * tb_right
Definition: raytrace.h:1150
#define BU_LIST_INIT(_hp)
Definition: list.h:148
void * idb_ptr
Definition: raytrace.h:195
Definition: search.h:75
#define DB_FULL_PATH_CUR_BOOL(_pp)
Definition: db_fullpath.h:52
Definition: search.h:78
#define RT_DIR_COMB
combination
Definition: raytrace.h:884
#define DB_FULL_PATH_SET_CUR_BOOL(_pp, _i)
Definition: db_fullpath.h:53
struct directory * dbi_Head[RT_DBNHASH]
Definition: raytrace.h:814
#define DB_SEARCH_QUIET
Silence all warnings.
Definition: search.h:114
struct bu_ptbl * full_paths
Definition: search.h:66
Definition: search.h:76
int(* eval)(struct db_plan_t *, struct db_node_t *, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.h:84
union tree * tree
Leading to tree_db_leaf leaves.
Definition: raytrace.h:938
struct db_full_path_list * db_search_full_paths(void *searchplan, struct db_full_path_list *pathnames, struct db_i *dbip)
Definition: search.c:2193
HIDDEN int c_path(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1387
#define DB_FULL_PATH_MAGIC
Definition: magic.h:203
#define RT_DIR_NULL
Definition: raytrace.h:875
HIDDEN struct db_plan_t * palloc(enum db_search_ntype t, int(*f)(struct db_plan_t *, struct db_node_t *, struct db_i *, struct bu_ptbl *))
Definition: search.c:262
HIDDEN struct db_plan_t * db_search_form_plan(char **argv, int quiet)
Definition: search.c:1919
HIDDEN int avs_check(const char *keystr, const char *value, int checkval, int strcomparison, struct bu_attribute_value_set *avs)
Definition: search.c:651
Definition: search.h:74
int cyclic_path(const struct db_full_path *fp, const char *name)
Definition: db_fullpath.c:598
HIDDEN int f_regex(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:559
int min_depth
Definition: search.h:93
HIDDEN int or_squish(struct db_plan_t *plan, struct db_plan_t **resultplan)
Definition: search.c:1864
int idb_minor_type
ID_xxx.
Definition: raytrace.h:193
Definition: search.h:78
HIDDEN int yankexpr(struct db_plan_t **planp, struct db_plan_t **resultplan)
Definition: search.c:1574
HIDDEN int find_execute_nested_plans(struct db_i *dbip, struct bu_ptbl *results, struct db_node_t *db_node, struct db_plan_t *plan)
Definition: search.c:341
void bu_vls_strcpy(struct bu_vls *vp, const char *s)
Definition: vls.c:310
const char * name
Definition: avs.h:61
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
int rt_arb_std_type(const struct rt_db_internal *ip, const struct bn_tol *tol)
Definition: arb8.c:317
#define RT_CK_TREE(_p)
Definition: raytrace.h:1182
int tcl_list_to_avs(const char *tcl_list, struct bu_attribute_value_set *avs, int offset)
Definition: db_diff.c:187
#define O_ARGVP
Definition: search.h:158
Definition: search.h:78
HIDDEN int c_depth(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:1249
size_t bu_argv_from_string(char *argv[], size_t lim, char *lp)
Definition: argv.c:32
HIDDEN int find_create(char ***argvp, struct db_plan_t **resultplan, struct bu_ptbl *results, int *db_search_isoutput, int quiet)
Definition: search.c:1461
int matched_filters
Definition: search.h:68
struct db_full_path * path
Definition: search.h:65
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
HIDDEN void print_path_with_bools(struct db_full_path *full_path)
Definition: search.c:140
#define O_ZERO
Definition: search.h:156
#define BU_LIST_DEQUEUE(cur)
Definition: list.h:209
HIDDEN int c_regex_common(enum db_search_ntype type, char *regexp, int icase, struct db_plan_t **resultplan)
Definition: search.c:569
enum db_search_ntype token
Definition: search.h:153
struct bu_ptbl * db_search_unique_objects(void *searchplan, struct db_full_path_list *pathnames, struct db_i *dbip)
Definition: search.c:2202
int db5_get_attributes(const struct db_i *dbip, struct bu_attribute_value_set *avs, const struct directory *dp)
Definition: db5_io.c:1027
int d_flags
flags
Definition: raytrace.h:869
Definition: vls.h:56
#define BRLCAD_ERROR
Definition: defines.h:72
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
HIDDEN int c_stdattr(char *pattern, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:972
double fastf_t
Definition: defines.h:300
#define OP_UNION
Binary: L union R.
Definition: raytrace.h:1127
HIDDEN int string_to_name_and_val(const char *in, struct bu_vls *name, struct bu_vls *value)
Definition: search.c:609
int flags
Definition: search.h:159
HIDDEN int c_closeparen(char *ignore, char ***ignored, int unused, struct db_plan_t **resultplan, int *db_search_isoutput)
Definition: search.c:306
void bu_avs_free(struct bu_attribute_value_set *avp)
Definition: avs.c:235
#define OP_NOT
Unary: not L.
Definition: raytrace.h:1134
HIDDEN int f_nnodes(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1272
HIDDEN int f_bool(struct db_plan_t *plan, struct db_node_t *db_node, struct db_i *dbip, struct bu_ptbl *results)
Definition: search.c:1104
void rt_db_free_internal(struct rt_db_internal *ip)
Definition: dir.c:216
struct db_full_path_list * _db_search_full_paths(void *searchplan, struct db_full_path_list *pathnames, struct db_i *dbip)
Definition: search.c:2108
#define DB_FULL_PATH_POP(_pp)
Definition: db_fullpath.h:49
HIDDEN struct db_plan_t * yanknode(struct db_plan_t **planp)
Definition: search.c:1554
int db_full_path_list_add(const char *path, int local, struct db_i *dbip, struct db_full_path_list *path_list)
Definition: search.c:2031
#define bu_strdup(s)
Definition: str.h:71
#define DB_LS_HIDDEN
Definition: raytrace.h:4655
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126
int db_search(struct bu_ptbl *search_results, int search_flags, const char *plan_str, int input_path_cnt, struct directory **input_paths, struct db_i *dbip)
Search for objects in a geometry database using filters.
Definition: search.c:2232