| 1 | /* tree.c -- helper functions to build and evaluate the expression tree.
|
|---|
| 2 | Copyright (C) 1990, 91, 92, 93, 94, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
|
|---|
| 3 |
|
|---|
| 4 | This program is free software; you can redistribute it and/or modify
|
|---|
| 5 | it under the terms of the GNU General Public License as published by
|
|---|
| 6 | the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 7 | any later version.
|
|---|
| 8 |
|
|---|
| 9 | This program is distributed in the hope that it will be useful,
|
|---|
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 12 | GNU General Public License for more details.
|
|---|
| 13 |
|
|---|
| 14 | You should have received a copy of the GNU General Public License
|
|---|
| 15 | along with this program; if not, write to the Free Software
|
|---|
| 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|---|
| 17 | USA.
|
|---|
| 18 | */
|
|---|
| 19 |
|
|---|
| 20 | #include "defs.h"
|
|---|
| 21 | #include "../gnulib/lib/xalloc.h"
|
|---|
| 22 |
|
|---|
| 23 | #include <assert.h>
|
|---|
| 24 | #include <stdlib.h>
|
|---|
| 25 |
|
|---|
| 26 | #if ENABLE_NLS
|
|---|
| 27 | # include <libintl.h>
|
|---|
| 28 | # define _(Text) gettext (Text)
|
|---|
| 29 | #else
|
|---|
| 30 | # define _(Text) Text
|
|---|
| 31 | #endif
|
|---|
| 32 | #ifdef gettext_noop
|
|---|
| 33 | # define N_(String) gettext_noop (String)
|
|---|
| 34 | #else
|
|---|
| 35 | /* See locate.c for explanation as to why not use (String) */
|
|---|
| 36 | # define N_(String) String
|
|---|
| 37 | #endif
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 | /* All predicates for each path to process. */
|
|---|
| 42 | static struct predicate *predicates = NULL;
|
|---|
| 43 |
|
|---|
| 44 | /* The root of the evaluation tree. */
|
|---|
| 45 | static struct predicate *eval_tree = NULL;
|
|---|
| 46 |
|
|---|
| 47 | /* The last predicate allocated. */
|
|---|
| 48 | static struct predicate *last_pred = NULL;
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 | static struct predicate *scan_rest PARAMS((struct predicate **input,
|
|---|
| 52 | struct predicate *head,
|
|---|
| 53 | short int prev_prec));
|
|---|
| 54 | static void merge_pred PARAMS((struct predicate *beg_list, struct predicate *end_list, struct predicate **last_p));
|
|---|
| 55 | static struct predicate *set_new_parent PARAMS((struct predicate *curr, enum predicate_precedence high_prec, struct predicate **prevp));
|
|---|
| 56 | static const char *cost_name PARAMS((enum EvaluationCost cost));
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 | /* Return a pointer to a tree that represents the
|
|---|
| 60 | expression prior to non-unary operator *INPUT.
|
|---|
| 61 | Set *INPUT to point at the next input predicate node.
|
|---|
| 62 |
|
|---|
| 63 | Only accepts the following:
|
|---|
| 64 |
|
|---|
| 65 | <primary>
|
|---|
| 66 | expression [operators of higher precedence]
|
|---|
| 67 | <uni_op><primary>
|
|---|
| 68 | (arbitrary expression)
|
|---|
| 69 | <uni_op>(arbitrary expression)
|
|---|
| 70 |
|
|---|
| 71 | In other words, you can not start out with a bi_op or close_paren.
|
|---|
| 72 |
|
|---|
| 73 | If the following operator (if any) is of a higher precedence than
|
|---|
| 74 | PREV_PREC, the expression just nabbed is part of a following
|
|---|
| 75 | expression, which really is the expression that should be handed to
|
|---|
| 76 | our caller, so get_expr recurses. */
|
|---|
| 77 |
|
|---|
| 78 | struct predicate *
|
|---|
| 79 | get_expr (struct predicate **input,
|
|---|
| 80 | short int prev_prec,
|
|---|
| 81 | const struct predicate* prev_pred)
|
|---|
| 82 | {
|
|---|
| 83 | struct predicate *next = NULL;
|
|---|
| 84 | struct predicate *this_pred = (*input);
|
|---|
| 85 |
|
|---|
| 86 | if (*input == NULL)
|
|---|
| 87 | error (1, 0, _("invalid expression"));
|
|---|
| 88 |
|
|---|
| 89 | switch ((*input)->p_type)
|
|---|
| 90 | {
|
|---|
| 91 | case NO_TYPE:
|
|---|
| 92 | error (1, 0, _("invalid expression"));
|
|---|
| 93 | break;
|
|---|
| 94 |
|
|---|
| 95 | case BI_OP:
|
|---|
| 96 | /* e.g. "find . -a" */
|
|---|
| 97 | error (1, 0, _("invalid expression; you have used a binary operator '%s' with nothing before it."), this_pred->p_name);
|
|---|
| 98 | break;
|
|---|
| 99 |
|
|---|
| 100 | case CLOSE_PAREN:
|
|---|
| 101 | if ((UNI_OP == prev_pred->p_type
|
|---|
| 102 | || BI_OP == prev_pred->p_type)
|
|---|
| 103 | && !this_pred->artificial)
|
|---|
| 104 | {
|
|---|
| 105 | /* e.g. "find \( -not \)" or "find \( -true -a \" */
|
|---|
| 106 | error(1, 0, _("expected an expression between '%s' and ')'"),
|
|---|
| 107 | prev_pred->p_name);
|
|---|
| 108 | }
|
|---|
| 109 | else if ( (*input)->artificial )
|
|---|
| 110 | {
|
|---|
| 111 | /* We have reached the end of the user-supplied predicates
|
|---|
| 112 | * unexpectedly.
|
|---|
| 113 | */
|
|---|
| 114 | /* e.g. "find . -true -a" */
|
|---|
| 115 | error (1, 0, _("expected an expression after '%s'"), prev_pred->p_name);
|
|---|
| 116 | }
|
|---|
| 117 | else
|
|---|
| 118 | {
|
|---|
| 119 | error (1, 0, _("invalid expression; you have too many ')'"));
|
|---|
| 120 | }
|
|---|
| 121 | break;
|
|---|
| 122 |
|
|---|
| 123 | case PRIMARY_TYPE:
|
|---|
| 124 | next = *input;
|
|---|
| 125 | *input = (*input)->pred_next;
|
|---|
| 126 | break;
|
|---|
| 127 |
|
|---|
| 128 | case UNI_OP:
|
|---|
| 129 | next = *input;
|
|---|
| 130 | *input = (*input)->pred_next;
|
|---|
| 131 | next->pred_right = get_expr (input, NEGATE_PREC, next);
|
|---|
| 132 | break;
|
|---|
| 133 |
|
|---|
| 134 | case OPEN_PAREN:
|
|---|
| 135 | if ( (NULL == (*input)->pred_next) || (*input)->pred_next->artificial )
|
|---|
| 136 | {
|
|---|
| 137 | /* user typed something like "find . (", and so the ) we are
|
|---|
| 138 | * looking at is from the artificial "( ) -print" that we
|
|---|
| 139 | * add.
|
|---|
| 140 | */
|
|---|
| 141 | error (1, 0, _("invalid expression; expected to find a ')' but didn't see one. Perhaps you need an extra predicate after '%s'"), this_pred->p_name);
|
|---|
| 142 | }
|
|---|
| 143 | prev_pred = (*input);
|
|---|
| 144 | *input = (*input)->pred_next;
|
|---|
| 145 | if ( (*input)->p_type == CLOSE_PAREN )
|
|---|
| 146 | {
|
|---|
| 147 | error (1, 0, _("invalid expression; empty parentheses are not allowed."));
|
|---|
| 148 | }
|
|---|
| 149 | next = get_expr (input, NO_PREC, prev_pred);
|
|---|
| 150 | if ((*input == NULL)
|
|---|
| 151 | || ((*input)->p_type != CLOSE_PAREN))
|
|---|
| 152 | error (1, 0, _("invalid expression; I was expecting to find a ')' somewhere but did not see one."));
|
|---|
| 153 | *input = (*input)->pred_next; /* move over close */
|
|---|
| 154 | break;
|
|---|
| 155 |
|
|---|
| 156 | default:
|
|---|
| 157 | error (1, 0, _("oops -- invalid expression type!"));
|
|---|
| 158 | break;
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | /* We now have the first expression and are positioned to check
|
|---|
| 162 | out the next operator. If NULL, all done. Otherwise, if
|
|---|
| 163 | PREV_PREC < the current node precedence, we must continue;
|
|---|
| 164 | the expression we just nabbed is more tightly bound to the
|
|---|
| 165 | following expression than to the previous one. */
|
|---|
| 166 | if (*input == NULL)
|
|---|
| 167 | return (next);
|
|---|
| 168 | if ((int) (*input)->p_prec > (int) prev_prec)
|
|---|
| 169 | {
|
|---|
| 170 | next = scan_rest (input, next, prev_prec);
|
|---|
| 171 | if (next == NULL)
|
|---|
| 172 | error (1, 0, _("invalid expression"));
|
|---|
| 173 | }
|
|---|
| 174 | return (next);
|
|---|
| 175 | }
|
|---|
| 176 | |
|---|
| 177 |
|
|---|
| 178 | /* Scan across the remainder of a predicate input list starting
|
|---|
| 179 | at *INPUT, building the rest of the expression tree to return.
|
|---|
| 180 | Stop at the first close parenthesis or the end of the input list.
|
|---|
| 181 | Assumes that get_expr has been called to nab the first element
|
|---|
| 182 | of the expression tree.
|
|---|
| 183 |
|
|---|
| 184 | *INPUT points to the current input predicate list element.
|
|---|
| 185 | It is updated as we move along the list to point to the
|
|---|
| 186 | terminating input element.
|
|---|
| 187 | HEAD points to the predicate element that was obtained
|
|---|
| 188 | by the call to get_expr.
|
|---|
| 189 | PREV_PREC is the precedence of the previous predicate element. */
|
|---|
| 190 |
|
|---|
| 191 | static struct predicate *
|
|---|
| 192 | scan_rest (struct predicate **input,
|
|---|
| 193 | struct predicate *head,
|
|---|
| 194 | short int prev_prec)
|
|---|
| 195 | {
|
|---|
| 196 | struct predicate *tree; /* The new tree we are building. */
|
|---|
| 197 |
|
|---|
| 198 | if ((*input == NULL) || ((*input)->p_type == CLOSE_PAREN))
|
|---|
| 199 | return (NULL);
|
|---|
| 200 | tree = head;
|
|---|
| 201 | while ((*input != NULL) && ((int) (*input)->p_prec > (int) prev_prec))
|
|---|
| 202 | {
|
|---|
| 203 | switch ((*input)->p_type)
|
|---|
| 204 | {
|
|---|
| 205 | case NO_TYPE:
|
|---|
| 206 | case PRIMARY_TYPE:
|
|---|
| 207 | case UNI_OP:
|
|---|
| 208 | case OPEN_PAREN:
|
|---|
| 209 | /* I'm not sure how we get here, so it is not obvious what
|
|---|
| 210 | * sort of mistakes might give rise to this condition.
|
|---|
| 211 | */
|
|---|
| 212 | error (1, 0, _("invalid expression"));
|
|---|
| 213 | break;
|
|---|
| 214 |
|
|---|
| 215 | case BI_OP:
|
|---|
| 216 | {
|
|---|
| 217 | struct predicate *prev = (*input);
|
|---|
| 218 | (*input)->pred_left = tree;
|
|---|
| 219 | tree = *input;
|
|---|
| 220 | *input = (*input)->pred_next;
|
|---|
| 221 | tree->pred_right = get_expr (input, tree->p_prec, prev);
|
|---|
| 222 | break;
|
|---|
| 223 | }
|
|---|
| 224 |
|
|---|
| 225 | case CLOSE_PAREN:
|
|---|
| 226 | return tree;
|
|---|
| 227 |
|
|---|
| 228 | default:
|
|---|
| 229 | error (1, 0,
|
|---|
| 230 | _("oops -- invalid expression type (%d)!"),
|
|---|
| 231 | (int)(*input)->p_type);
|
|---|
| 232 | break;
|
|---|
| 233 | }
|
|---|
| 234 | }
|
|---|
| 235 | return tree;
|
|---|
| 236 | }
|
|---|
| 237 | |
|---|
| 238 |
|
|---|
| 239 | /* Returns true if the specified predicate is reorderable. */
|
|---|
| 240 | static boolean
|
|---|
| 241 | predicate_is_cost_free(const struct predicate *p)
|
|---|
| 242 | {
|
|---|
| 243 | PRED_FUNC pred_func = p->pred_func;
|
|---|
| 244 |
|
|---|
| 245 | if (pred_func == pred_name || pred_func == pred_path ||
|
|---|
| 246 | pred_func == pred_iname || pred_func == pred_ipath)
|
|---|
| 247 | {
|
|---|
| 248 | /* Traditionally (at least 4.1.7 through 4.2.x) GNU find always
|
|---|
| 249 | * optimised these cases.
|
|---|
| 250 | */
|
|---|
| 251 | return true;
|
|---|
| 252 | }
|
|---|
| 253 | else if (options.optimisation_level > 0)
|
|---|
| 254 | {
|
|---|
| 255 | if (pred_func == pred_and || pred_func == pred_negate ||
|
|---|
| 256 | pred_func == pred_comma || pred_func == pred_or)
|
|---|
| 257 | return false;
|
|---|
| 258 | else
|
|---|
| 259 | return NeedsNothing == p->p_cost;
|
|---|
| 260 | }
|
|---|
| 261 | else
|
|---|
| 262 | {
|
|---|
| 263 | return false;
|
|---|
| 264 | }
|
|---|
| 265 | }
|
|---|
| 266 | |
|---|
| 267 |
|
|---|
| 268 | /* Prints a predicate */
|
|---|
| 269 | void print_predicate(FILE *fp, const struct predicate *p)
|
|---|
| 270 | {
|
|---|
| 271 | fprintf (fp, "%s%s%s",
|
|---|
| 272 | p->p_name,
|
|---|
| 273 | p->arg_text ? " " : "",
|
|---|
| 274 | p->arg_text ? p->arg_text : "");
|
|---|
| 275 | }
|
|---|
| 276 |
|
|---|
| 277 | |
|---|
| 278 |
|
|---|
| 279 | struct predlist
|
|---|
| 280 | {
|
|---|
| 281 | struct predicate *head;
|
|---|
| 282 | struct predicate *tail;
|
|---|
| 283 | };
|
|---|
| 284 | |
|---|
| 285 |
|
|---|
| 286 | static void
|
|---|
| 287 | predlist_init(struct predlist *p)
|
|---|
| 288 | {
|
|---|
| 289 | p->head = p->tail = NULL;
|
|---|
| 290 | }
|
|---|
| 291 |
|
|---|
| 292 | static void
|
|---|
| 293 | predlist_insert(struct predlist *list,
|
|---|
| 294 | struct predicate *curr,
|
|---|
| 295 | struct predicate **pprev)
|
|---|
| 296 | {
|
|---|
| 297 | struct predicate **insertpos = &(list->head);
|
|---|
| 298 |
|
|---|
| 299 | *pprev = curr->pred_left;
|
|---|
| 300 | if (options.optimisation_level > 2)
|
|---|
| 301 | {
|
|---|
| 302 | /* Insert the new node in the list after any other entries which
|
|---|
| 303 | * are more selective.
|
|---|
| 304 | */
|
|---|
| 305 | if (0)
|
|---|
| 306 | while ( (*insertpos) && ((*insertpos)->est_success_rate < curr->est_success_rate) )
|
|---|
| 307 | {
|
|---|
| 308 | insertpos = &((*insertpos)->pred_left);
|
|---|
| 309 | }
|
|---|
| 310 | }
|
|---|
| 311 | curr->pred_left = (*insertpos);
|
|---|
| 312 | (*insertpos) = curr;
|
|---|
| 313 | if (NULL == list->tail)
|
|---|
| 314 | list->tail = list->head;
|
|---|
| 315 | }
|
|---|
| 316 | |
|---|
| 317 |
|
|---|
| 318 | static void
|
|---|
| 319 | predlist_dump(FILE *fp, const char *label, const struct predlist *list)
|
|---|
| 320 | {
|
|---|
| 321 | const struct predicate *p;
|
|---|
| 322 | fprintf(fp, "%s:\n", label);
|
|---|
| 323 | for (p=list->head; p; p=p->pred_left)
|
|---|
| 324 | {
|
|---|
| 325 | print_optlist(fp, p);
|
|---|
| 326 | fprintf(fp, " ");
|
|---|
| 327 | }
|
|---|
| 328 | fprintf(fp, "\n");
|
|---|
| 329 | }
|
|---|
| 330 | |
|---|
| 331 |
|
|---|
| 332 | static void
|
|---|
| 333 | predlist_merge_nosort(struct predlist *list,
|
|---|
| 334 | struct predicate **last)
|
|---|
| 335 | {
|
|---|
| 336 | if (list->head)
|
|---|
| 337 | {
|
|---|
| 338 | calculate_derived_rates(list->head);
|
|---|
| 339 | merge_pred(list->head, list->tail, last);
|
|---|
| 340 | predlist_init(list);
|
|---|
| 341 | }
|
|---|
| 342 | }
|
|---|
| 343 | |
|---|
| 344 |
|
|---|
| 345 | static int
|
|---|
| 346 | pred_cost_compare(const struct predicate *p1, const struct predicate *p2, boolean wantfailure)
|
|---|
| 347 | {
|
|---|
| 348 | if (p1->p_cost == p2->p_cost)
|
|---|
| 349 | {
|
|---|
| 350 | if (p1->est_success_rate == p2->est_success_rate)
|
|---|
| 351 | return 0;
|
|---|
| 352 | else if (wantfailure)
|
|---|
| 353 | return p1->est_success_rate < p2->est_success_rate ? -1 : 1;
|
|---|
| 354 | else
|
|---|
| 355 | return p1->est_success_rate < p2->est_success_rate ? 1 : -1;
|
|---|
| 356 | }
|
|---|
| 357 | else
|
|---|
| 358 | {
|
|---|
| 359 | return p1->p_cost < p2->p_cost ? -1 : 1;
|
|---|
| 360 | }
|
|---|
| 361 | }
|
|---|
| 362 |
|
|---|
| 363 | |
|---|
| 364 |
|
|---|
| 365 | static void
|
|---|
| 366 | predlist_merge_sort(struct predlist *list,
|
|---|
| 367 | struct predicate **last)
|
|---|
| 368 | {
|
|---|
| 369 | struct predlist new_list;
|
|---|
| 370 | struct predicate *p, *q;
|
|---|
| 371 |
|
|---|
| 372 | if (NULL == list->head)
|
|---|
| 373 | return; /* nothing to do */
|
|---|
| 374 |
|
|---|
| 375 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 376 | {
|
|---|
| 377 | fprintf(stderr, "%s:\n", "predlist before merge sort");
|
|---|
| 378 | print_tree(stderr, list->head, 2);
|
|---|
| 379 | }
|
|---|
| 380 |
|
|---|
| 381 | calculate_derived_rates(list->head);
|
|---|
| 382 | predlist_init(&new_list);
|
|---|
| 383 | while (list->head)
|
|---|
| 384 | {
|
|---|
| 385 | /* remove head of source list */
|
|---|
| 386 | q = list->head;
|
|---|
| 387 | list->head = list->head->pred_left;
|
|---|
| 388 | q->pred_left = NULL;
|
|---|
| 389 |
|
|---|
| 390 | /* insert it into the new list */
|
|---|
| 391 | for (p=new_list.head; p; p=p->pred_left)
|
|---|
| 392 | {
|
|---|
| 393 | /* If these operations are OR operations, we want to get a
|
|---|
| 394 | * successful test as soon as possible, to take advantage of
|
|---|
| 395 | * the short-circuit evaluation. If they're AND, we want to
|
|---|
| 396 | * get an unsuccessful result early for the same reason.
|
|---|
| 397 | * Therefore we invert the sense of the comparison for the
|
|---|
| 398 | * OR case. We only want to invert the sense of the success
|
|---|
| 399 | * rate comparison, not the operation cost comparison. Hence we
|
|---|
| 400 | * pass a flag into pred_cost_compare().
|
|---|
| 401 | */
|
|---|
| 402 | boolean wantfailure = (OR_PREC != p->p_prec);
|
|---|
| 403 | if (pred_cost_compare(p->pred_right, q->pred_right, wantfailure) >= 0)
|
|---|
| 404 | break;
|
|---|
| 405 | }
|
|---|
| 406 | if (p)
|
|---|
| 407 | {
|
|---|
| 408 | /* insert into existing list */
|
|---|
| 409 | q->pred_left = p->pred_left;
|
|---|
| 410 | if (NULL == q->pred_left)
|
|---|
| 411 | new_list.tail = q;
|
|---|
| 412 | p->pred_left = q;
|
|---|
| 413 | }
|
|---|
| 414 | else
|
|---|
| 415 | {
|
|---|
| 416 | q->pred_left = new_list.head; /* prepend */
|
|---|
| 417 | new_list.head = q;
|
|---|
| 418 | if (NULL == new_list.tail)
|
|---|
| 419 | new_list.tail = q; /* first item in new list */
|
|---|
| 420 | }
|
|---|
| 421 | }
|
|---|
| 422 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 423 | {
|
|---|
| 424 | fprintf(stderr, "%s:\n", "predlist after merge sort");
|
|---|
| 425 | print_tree(stderr, new_list.head, 2);
|
|---|
| 426 | }
|
|---|
| 427 |
|
|---|
| 428 | calculate_derived_rates(new_list.head);
|
|---|
| 429 | merge_pred(new_list.head, new_list.tail, last);
|
|---|
| 430 | predlist_init(list);
|
|---|
| 431 | }
|
|---|
| 432 | |
|---|
| 433 |
|
|---|
| 434 | static void
|
|---|
| 435 | merge_lists(struct predlist lists[], int nlists,
|
|---|
| 436 | struct predlist *name_list,
|
|---|
| 437 | struct predlist *regex_list,
|
|---|
| 438 | struct predicate **last)
|
|---|
| 439 | {
|
|---|
| 440 | int i;
|
|---|
| 441 | static void (*mergefn)(struct predlist *, struct predicate**);
|
|---|
| 442 |
|
|---|
| 443 | mergefn = predlist_merge_sort;
|
|---|
| 444 |
|
|---|
| 445 | mergefn(name_list, last);
|
|---|
| 446 | mergefn(regex_list, last);
|
|---|
| 447 |
|
|---|
| 448 | for (i=0; i<nlists; i++)
|
|---|
| 449 | mergefn(&lists[i], last);
|
|---|
| 450 | }
|
|---|
| 451 |
|
|---|
| 452 |
|
|---|
| 453 | |
|---|
| 454 |
|
|---|
| 455 | static boolean
|
|---|
| 456 | subtree_has_side_effects(const struct predicate *p)
|
|---|
| 457 | {
|
|---|
| 458 | if (p)
|
|---|
| 459 | {
|
|---|
| 460 | return p->side_effects
|
|---|
| 461 | || subtree_has_side_effects(p->pred_left)
|
|---|
| 462 | || subtree_has_side_effects(p->pred_right);
|
|---|
| 463 | }
|
|---|
| 464 | else
|
|---|
| 465 | {
|
|---|
| 466 |
|
|---|
| 467 | return false;
|
|---|
| 468 | }
|
|---|
| 469 | }
|
|---|
| 470 | |
|---|
| 471 |
|
|---|
| 472 | static int
|
|---|
| 473 | worst_cost (const struct predicate *p)
|
|---|
| 474 | {
|
|---|
| 475 | if (p)
|
|---|
| 476 | {
|
|---|
| 477 | unsigned int cost_r, cost_l, worst;
|
|---|
| 478 | cost_l = worst_cost(p->pred_left);
|
|---|
| 479 | cost_r = worst_cost(p->pred_right);
|
|---|
| 480 | worst = (cost_l > cost_r) ? cost_l : cost_r;
|
|---|
| 481 | if (worst < p->p_cost)
|
|---|
| 482 | worst = p->p_cost;
|
|---|
| 483 | return worst;
|
|---|
| 484 | }
|
|---|
| 485 | else
|
|---|
| 486 | {
|
|---|
| 487 | return 0;
|
|---|
| 488 | }
|
|---|
| 489 | }
|
|---|
| 490 |
|
|---|
| 491 |
|
|---|
| 492 | |
|---|
| 493 |
|
|---|
| 494 | static void
|
|---|
| 495 | perform_arm_swap(struct predicate *p)
|
|---|
| 496 | {
|
|---|
| 497 | struct predicate *tmp = p->pred_left->pred_right;
|
|---|
| 498 | p->pred_left->pred_right = p->pred_right;
|
|---|
| 499 | p->pred_right = tmp;
|
|---|
| 500 | }
|
|---|
| 501 | |
|---|
| 502 |
|
|---|
| 503 | /* Consider swapping p->pred_left->pred_right with p->pred_right,
|
|---|
| 504 | * if that yields a faster evaluation. Normally the left predicate is
|
|---|
| 505 | * evaluated first.
|
|---|
| 506 | *
|
|---|
| 507 | * If the operation is an OR, we want the left predicate to be the one that
|
|---|
| 508 | * succeeds most often. If it is an AND, we want it to be the predicate that
|
|---|
| 509 | * fails most often.
|
|---|
| 510 | *
|
|---|
| 511 | * We don't consider swapping arms of an operator where their cost is
|
|---|
| 512 | * different or where they have side effects.
|
|---|
| 513 | *
|
|---|
| 514 | * A viable test case for this is
|
|---|
| 515 | * ./find -D opt -O3 . \! -type f -o -type d
|
|---|
| 516 | * Here, the ! -type f should be evaluated first,
|
|---|
| 517 | * as we assume that 95% of inodes are vanilla files.
|
|---|
| 518 | */
|
|---|
| 519 | static boolean
|
|---|
| 520 | consider_arm_swap(struct predicate *p)
|
|---|
| 521 | {
|
|---|
| 522 | int left_cost, right_cost;
|
|---|
| 523 | const char *reason = NULL;
|
|---|
| 524 | struct predicate **pl, **pr;
|
|---|
| 525 |
|
|---|
| 526 | if (BI_OP != p->p_type)
|
|---|
| 527 | reason = "Not a binary operation";
|
|---|
| 528 |
|
|---|
| 529 | if (!reason)
|
|---|
| 530 | {
|
|---|
| 531 | if (NULL == p->pred_left || NULL == p->pred_right)
|
|---|
| 532 | reason = "Doesn't have two arms";
|
|---|
| 533 | }
|
|---|
| 534 |
|
|---|
| 535 |
|
|---|
| 536 | if (!reason)
|
|---|
| 537 | {
|
|---|
| 538 | if (NULL == p->pred_left->pred_right)
|
|---|
| 539 | reason = "Left arm has no child on RHS";
|
|---|
| 540 | }
|
|---|
| 541 | pr = &p->pred_right;
|
|---|
| 542 | pl = &p->pred_left->pred_right;
|
|---|
| 543 |
|
|---|
| 544 | if (!reason)
|
|---|
| 545 | {
|
|---|
| 546 | if (subtree_has_side_effects(*pl))
|
|---|
| 547 | reason = "Left subtree has side-effects";
|
|---|
| 548 | }
|
|---|
| 549 | if (!reason)
|
|---|
| 550 | {
|
|---|
| 551 | if (subtree_has_side_effects(*pr))
|
|---|
| 552 | reason = "Right subtree has side-effects";
|
|---|
| 553 | }
|
|---|
| 554 |
|
|---|
| 555 | if (!reason)
|
|---|
| 556 | {
|
|---|
| 557 | left_cost = worst_cost(*pl);
|
|---|
| 558 | right_cost = worst_cost(*pr);
|
|---|
| 559 |
|
|---|
| 560 | if (left_cost < right_cost)
|
|---|
| 561 | {
|
|---|
| 562 | reason = "efficient as-is";
|
|---|
| 563 | }
|
|---|
| 564 | }
|
|---|
| 565 | if (!reason)
|
|---|
| 566 | {
|
|---|
| 567 | boolean want_swap;
|
|---|
| 568 |
|
|---|
| 569 | if (left_cost == right_cost)
|
|---|
| 570 | {
|
|---|
| 571 | /* it's a candidate */
|
|---|
| 572 | float succ_rate_l = (*pl)->est_success_rate;
|
|---|
| 573 | float succ_rate_r = (*pr)->est_success_rate;
|
|---|
| 574 |
|
|---|
| 575 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 576 | {
|
|---|
| 577 | fprintf(stderr, "Success rates: l=%f, r=%f\n", succ_rate_l, succ_rate_r);
|
|---|
| 578 | }
|
|---|
| 579 |
|
|---|
| 580 | if (pred_or == p->pred_func)
|
|---|
| 581 | {
|
|---|
| 582 | want_swap = succ_rate_r < succ_rate_l;
|
|---|
| 583 | if (!want_swap)
|
|---|
| 584 | reason = "Operation is OR and right success rate >= left";
|
|---|
| 585 | }
|
|---|
| 586 | else if (pred_and == p->pred_func)
|
|---|
| 587 | {
|
|---|
| 588 | want_swap = succ_rate_r > succ_rate_l;
|
|---|
| 589 | if (!want_swap)
|
|---|
| 590 | reason = "Operation is AND and right success rate <= left";
|
|---|
| 591 | }
|
|---|
| 592 | else
|
|---|
| 593 | {
|
|---|
| 594 | want_swap = false;
|
|---|
| 595 | reason = "Not AND or OR";
|
|---|
| 596 | }
|
|---|
| 597 | }
|
|---|
| 598 | else
|
|---|
| 599 | {
|
|---|
| 600 | want_swap = true;
|
|---|
| 601 | }
|
|---|
| 602 |
|
|---|
| 603 | if (want_swap)
|
|---|
| 604 | {
|
|---|
| 605 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 606 | {
|
|---|
| 607 | fprintf(stderr, "Performing arm swap on:\n");
|
|---|
| 608 | print_tree (stderr, p, 0);
|
|---|
| 609 | }
|
|---|
| 610 | perform_arm_swap(p);
|
|---|
| 611 | return true;
|
|---|
| 612 | }
|
|---|
| 613 | }
|
|---|
| 614 |
|
|---|
| 615 |
|
|---|
| 616 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 617 | {
|
|---|
| 618 | fprintf(stderr, "Not an arm swap candidate (%s):\n", reason);
|
|---|
| 619 | print_tree (stderr, p, 0);
|
|---|
| 620 | }
|
|---|
| 621 | return false;
|
|---|
| 622 | }
|
|---|
| 623 | |
|---|
| 624 |
|
|---|
| 625 | static boolean
|
|---|
| 626 | do_arm_swaps(struct predicate *p)
|
|---|
| 627 | {
|
|---|
| 628 | if (p)
|
|---|
| 629 | {
|
|---|
| 630 | boolean swapped;
|
|---|
| 631 | do
|
|---|
| 632 | {
|
|---|
| 633 | swapped = false;
|
|---|
| 634 | if (consider_arm_swap(p)
|
|---|
| 635 | || do_arm_swaps(p->pred_left)
|
|---|
| 636 | || do_arm_swaps(p->pred_right))
|
|---|
| 637 | {
|
|---|
| 638 | swapped = true;
|
|---|
| 639 | }
|
|---|
| 640 | } while (swapped);
|
|---|
| 641 | return swapped;
|
|---|
| 642 | }
|
|---|
| 643 | else
|
|---|
| 644 | {
|
|---|
| 645 | return false;
|
|---|
| 646 | }
|
|---|
| 647 | }
|
|---|
| 648 |
|
|---|
| 649 |
|
|---|
| 650 | |
|---|
| 651 |
|
|---|
| 652 | /* Optimize the ordering of the predicates in the tree. Rearrange
|
|---|
| 653 | them to minimize work. Strategies:
|
|---|
| 654 | * Evaluate predicates that don't need inode information first;
|
|---|
| 655 | the predicates are divided into 1 or more groups separated by
|
|---|
| 656 | predicates (if any) which have "side effects", such as printing.
|
|---|
| 657 | The grouping implements the partial ordering on predicates which
|
|---|
| 658 | those with side effects impose.
|
|---|
| 659 |
|
|---|
| 660 | * Place -name, -iname, -path, -ipath, -regex and -iregex at the front
|
|---|
| 661 | of a group, with -name, -iname, -path and -ipath ahead of
|
|---|
| 662 | -regex and -iregex. Predicates which are moved to the front
|
|---|
| 663 | of a group by definition do not have side effects. Both
|
|---|
| 664 | -regex and -iregex both use pred_regex.
|
|---|
| 665 |
|
|---|
| 666 | If higher optimisation levels have been selected, reordering also
|
|---|
| 667 | occurs according to the p_cost member of each predicate (which
|
|---|
| 668 | reflects the performance cost of the test). The ordering also
|
|---|
| 669 | bears in mind whether these operations are more likely to succeed
|
|---|
| 670 | or fail. When evauating a chain of OR conditions, we prefer
|
|---|
| 671 | tests likely to succeed at the front of the list. For AND, we
|
|---|
| 672 | prefer tests likely to fail at the front of the list.
|
|---|
| 673 |
|
|---|
| 674 | This routine "normalizes" the predicate tree by ensuring that
|
|---|
| 675 | all expression predicates have AND (or OR or COMMA) parent nodes
|
|---|
| 676 | which are linked along the left edge of the expression tree.
|
|---|
| 677 | This makes manipulation of subtrees easier.
|
|---|
| 678 |
|
|---|
| 679 | EVAL_TREEP points to the root pointer of the predicate tree
|
|---|
| 680 | to be rearranged. opt_expr may return a new root pointer there.
|
|---|
| 681 | Return true if the tree contains side effects, false if not. */
|
|---|
| 682 |
|
|---|
| 683 | static boolean
|
|---|
| 684 | opt_expr (struct predicate **eval_treep)
|
|---|
| 685 | {
|
|---|
| 686 | struct predlist regex_list={NULL,NULL}, name_list={NULL,NULL};
|
|---|
| 687 | struct predlist cbo_list[NumEvaluationCosts];
|
|---|
| 688 | int i;
|
|---|
| 689 | struct predicate *curr;
|
|---|
| 690 | struct predicate **prevp; /* Address of `curr' node. */
|
|---|
| 691 | struct predicate **last_sidep; /* Last predicate with side effects. */
|
|---|
| 692 | PRED_FUNC pred_func;
|
|---|
| 693 | enum predicate_type p_type;
|
|---|
| 694 | boolean has_side_effects = false; /* Return value. */
|
|---|
| 695 | enum predicate_precedence prev_prec, /* precedence of last BI_OP in branch */
|
|---|
| 696 | biop_prec; /* topmost BI_OP precedence in branch */
|
|---|
| 697 |
|
|---|
| 698 | if (eval_treep == NULL || *eval_treep == NULL)
|
|---|
| 699 | return (false);
|
|---|
| 700 |
|
|---|
| 701 | for (i=0; i<NumEvaluationCosts; i++)
|
|---|
| 702 | predlist_init(&cbo_list[i]);
|
|---|
| 703 |
|
|---|
| 704 | /* Set up to normalize tree as a left-linked list of ANDs or ORs.
|
|---|
| 705 | Set `curr' to the leftmost node, `prevp' to its address, and
|
|---|
| 706 | `pred_func' to the predicate type of its parent. */
|
|---|
| 707 | prevp = eval_treep;
|
|---|
| 708 | prev_prec = AND_PREC;
|
|---|
| 709 | curr = *prevp;
|
|---|
| 710 | while (curr->pred_left != NULL)
|
|---|
| 711 | {
|
|---|
| 712 | prevp = &curr->pred_left;
|
|---|
| 713 | prev_prec = curr->p_prec; /* must be a BI_OP */
|
|---|
| 714 | curr = curr->pred_left;
|
|---|
| 715 | }
|
|---|
| 716 |
|
|---|
| 717 | /* Link in the appropriate BI_OP for the last expression, if needed. */
|
|---|
| 718 | if (curr->p_type != BI_OP)
|
|---|
| 719 | set_new_parent (curr, prev_prec, prevp);
|
|---|
| 720 |
|
|---|
| 721 | if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
|
|---|
| 722 | {
|
|---|
| 723 | /* Normalized tree. */
|
|---|
| 724 | fprintf (stderr, "Normalized Eval Tree:\n");
|
|---|
| 725 | print_tree (stderr, *eval_treep, 0);
|
|---|
| 726 | }
|
|---|
| 727 |
|
|---|
| 728 | /* Rearrange the predicates. */
|
|---|
| 729 | prevp = eval_treep;
|
|---|
| 730 | biop_prec = NO_PREC; /* not COMMA_PREC */
|
|---|
| 731 | if ((*prevp) && (*prevp)->p_type == BI_OP)
|
|---|
| 732 | biop_prec = (*prevp)->p_prec;
|
|---|
| 733 | while ((curr = *prevp) != NULL)
|
|---|
| 734 | {
|
|---|
| 735 | /* If there is a BI_OP of different precedence from the first
|
|---|
| 736 | in the pred_left chain, create a new parent of the
|
|---|
| 737 | original precedence, link the new parent to the left of the
|
|---|
| 738 | previous and link CURR to the right of the new parent.
|
|---|
| 739 | This preserves the precedence of expressions in the tree
|
|---|
| 740 | in case we rearrange them. */
|
|---|
| 741 | if (curr->p_type == BI_OP)
|
|---|
| 742 | {
|
|---|
| 743 | if (curr->p_prec != biop_prec)
|
|---|
| 744 | curr = set_new_parent(curr, biop_prec, prevp);
|
|---|
| 745 | }
|
|---|
| 746 |
|
|---|
| 747 | /* See which predicate type we have. */
|
|---|
| 748 | p_type = curr->pred_right->p_type;
|
|---|
| 749 | pred_func = curr->pred_right->pred_func;
|
|---|
| 750 |
|
|---|
| 751 |
|
|---|
| 752 | switch (p_type)
|
|---|
| 753 | {
|
|---|
| 754 | case NO_TYPE:
|
|---|
| 755 | case PRIMARY_TYPE:
|
|---|
| 756 | /* Don't rearrange the arguments of the comma operator, it is
|
|---|
| 757 | not commutative. */
|
|---|
| 758 | if (biop_prec == COMMA_PREC)
|
|---|
| 759 | break;
|
|---|
| 760 |
|
|---|
| 761 | /* If this predicate has no side effects, consider reordering it. */
|
|---|
| 762 | if (!curr->pred_right->side_effects)
|
|---|
| 763 | {
|
|---|
| 764 | boolean reorder;
|
|---|
| 765 |
|
|---|
| 766 | /* If it's one of our special primaries, move it to the
|
|---|
| 767 | front of the list for that primary. */
|
|---|
| 768 | if (predicate_is_cost_free(curr->pred_right))
|
|---|
| 769 | {
|
|---|
| 770 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 771 | {
|
|---|
| 772 | fprintf(stderr, "-O%d: promoting cheap predicate ",
|
|---|
| 773 | (int)options.optimisation_level);
|
|---|
| 774 | print_predicate(stderr, curr->pred_right);
|
|---|
| 775 | fprintf(stderr, " into name_list\n");
|
|---|
| 776 | }
|
|---|
| 777 | predlist_insert(&name_list, curr, prevp);
|
|---|
| 778 | continue;
|
|---|
| 779 | }
|
|---|
| 780 |
|
|---|
| 781 | if (pred_func == pred_regex)
|
|---|
| 782 | {
|
|---|
| 783 | predlist_insert(®ex_list, curr, prevp);
|
|---|
| 784 | continue;
|
|---|
| 785 | }
|
|---|
| 786 |
|
|---|
| 787 | reorder = ((options.optimisation_level > 1)
|
|---|
| 788 | && (NeedsType == curr->pred_right->p_cost)
|
|---|
| 789 | && !curr->pred_right->need_stat) ||
|
|---|
| 790 | (options.optimisation_level > 2);
|
|---|
| 791 |
|
|---|
| 792 | if (reorder)
|
|---|
| 793 | {
|
|---|
| 794 | if (options.debug_options & DebugTreeOpt)
|
|---|
| 795 | {
|
|---|
| 796 | fprintf(stderr, "-O%d: categorising predicate ",
|
|---|
| 797 | (int)options.optimisation_level);
|
|---|
| 798 | print_predicate(stderr, curr->pred_right);
|
|---|
| 799 | fprintf(stderr, " by cost (%s)\n",
|
|---|
| 800 | cost_name(curr->pred_right->p_cost));
|
|---|
| 801 | }
|
|---|
| 802 | predlist_insert(&cbo_list[curr->pred_right->p_cost], curr, prevp);
|
|---|
| 803 | continue;
|
|---|
| 804 | }
|
|---|
| 805 | }
|
|---|
| 806 |
|
|---|
| 807 | break;
|
|---|
| 808 |
|
|---|
| 809 | case UNI_OP:
|
|---|
| 810 | /* For NOT, check the expression trees below the NOT. */
|
|---|
| 811 | curr->pred_right->side_effects
|
|---|
| 812 | = opt_expr (&curr->pred_right->pred_right);
|
|---|
| 813 | break;
|
|---|
| 814 |
|
|---|
| 815 | case BI_OP:
|
|---|
| 816 | /* For nested AND or OR, recurse (AND/OR form layers on the left of
|
|---|
| 817 | the tree), and continue scanning this level of AND or OR. */
|
|---|
| 818 | curr->pred_right->side_effects = opt_expr (&curr->pred_right);
|
|---|
| 819 | break;
|
|---|
| 820 |
|
|---|
| 821 | /* At this point, get_expr and scan_rest have already removed
|
|---|
| 822 | all of the user's parentheses. */
|
|---|
| 823 |
|
|---|
| 824 | default:
|
|---|
| 825 | error (1, 0, _("oops -- invalid expression type!"));
|
|---|
| 826 | break;
|
|---|
| 827 | }
|
|---|
| 828 |
|
|---|
| 829 | if (curr->pred_right->side_effects == true)
|
|---|
| 830 | {
|
|---|
| 831 | last_sidep = prevp;
|
|---|
| 832 |
|
|---|
| 833 | /* Incorporate lists and reset list pointers for this group. */
|
|---|
| 834 | merge_lists(cbo_list, NumEvaluationCosts, &name_list, ®ex_list, last_sidep);
|
|---|
| 835 | has_side_effects = true;
|
|---|
| 836 | }
|
|---|
| 837 |
|
|---|
| 838 | prevp = &curr->pred_left;
|
|---|
| 839 | }
|
|---|
| 840 |
|
|---|
| 841 | /* Do final list merges. */
|
|---|
| 842 | last_sidep = prevp;
|
|---|
| 843 | merge_lists(cbo_list, NumEvaluationCosts, &name_list, ®ex_list, last_sidep);
|
|---|
| 844 | return has_side_effects;
|
|---|
| 845 | }
|
|---|
| 846 | |
|---|
| 847 |
|
|---|
| 848 | static float
|
|---|
| 849 | constrain_rate(float rate)
|
|---|
| 850 | {
|
|---|
| 851 | if (rate > 1.0f)
|
|---|
| 852 | return 1.0;
|
|---|
| 853 | else if (rate < 0.0)
|
|---|
| 854 | return 0.0;
|
|---|
| 855 | else
|
|---|
| 856 | return rate;
|
|---|
| 857 | }
|
|---|
| 858 | |
|---|
| 859 |
|
|---|
| 860 | /* Link in a new parent BI_OP node for CURR, at *PREVP, with precedence
|
|---|
| 861 | HIGH_PREC. */
|
|---|
| 862 |
|
|---|
| 863 | static struct predicate *
|
|---|
| 864 | set_new_parent (struct predicate *curr, enum predicate_precedence high_prec, struct predicate **prevp)
|
|---|
| 865 | {
|
|---|
| 866 | struct predicate *new_parent;
|
|---|
| 867 |
|
|---|
| 868 | new_parent = (struct predicate *) xmalloc (sizeof (struct predicate));
|
|---|
| 869 | new_parent->p_type = BI_OP;
|
|---|
| 870 | new_parent->p_prec = high_prec;
|
|---|
| 871 | new_parent->need_stat = false;
|
|---|
| 872 | new_parent->need_type = false;
|
|---|
| 873 | new_parent->p_cost = NeedsNothing;
|
|---|
| 874 |
|
|---|
| 875 | switch (high_prec)
|
|---|
| 876 | {
|
|---|
| 877 | case COMMA_PREC:
|
|---|
| 878 | new_parent->pred_func = pred_comma;
|
|---|
| 879 | new_parent->p_name = ",";
|
|---|
| 880 | new_parent->est_success_rate = 1.0;
|
|---|
| 881 | break;
|
|---|
| 882 | case OR_PREC:
|
|---|
| 883 | new_parent->pred_func = pred_or;
|
|---|
| 884 | new_parent->p_name = "-o";
|
|---|
| 885 | new_parent->est_success_rate = constrain_rate(curr->est_success_rate);
|
|---|
| 886 | break;
|
|---|
| 887 | case AND_PREC:
|
|---|
| 888 | new_parent->pred_func = pred_and;
|
|---|
| 889 | new_parent->p_name = "-a";
|
|---|
| 890 | new_parent->est_success_rate = constrain_rate(curr->est_success_rate);
|
|---|
| 891 | break;
|
|---|
| 892 | default:
|
|---|
| 893 | ; /* empty */
|
|---|
| 894 | }
|
|---|
| 895 |
|
|---|
| 896 | new_parent->side_effects = false;
|
|---|
| 897 | new_parent->no_default_print = false;
|
|---|
| 898 | new_parent->args.str = NULL;
|
|---|
| 899 | new_parent->pred_next = NULL;
|
|---|
| 900 |
|
|---|
| 901 | /* Link in new_parent.
|
|---|
| 902 | Pushes rest of left branch down 1 level to new_parent->pred_right. */
|
|---|
| 903 | new_parent->pred_left = NULL;
|
|---|
| 904 | new_parent->pred_right = curr;
|
|---|
| 905 | *prevp = new_parent;
|
|---|
| 906 |
|
|---|
| 907 | return new_parent;
|
|---|
| 908 | }
|
|---|
| 909 |
|
|---|
| 910 | /* Merge the predicate list that starts at BEG_LIST and ends at END_LIST
|
|---|
| 911 | into the tree at LAST_P. */
|
|---|
| 912 |
|
|---|
| 913 | static void
|
|---|
| 914 | merge_pred (struct predicate *beg_list, struct predicate *end_list, struct predicate **last_p)
|
|---|
| 915 | {
|
|---|
| 916 | end_list->pred_left = *last_p;
|
|---|
| 917 | *last_p = beg_list;
|
|---|
| 918 | }
|
|---|
| 919 | |
|---|
| 920 |
|
|---|
| 921 | /* Find the first node in expression tree TREE that requires
|
|---|
| 922 | a stat call and mark the operator above it as needing a stat
|
|---|
| 923 | before calling the node. Since the expression precedences
|
|---|
| 924 | are represented in the tree, some preds that need stat may not
|
|---|
| 925 | get executed (because the expression value is determined earlier.)
|
|---|
| 926 | So every expression needing stat must be marked as such, not just
|
|---|
| 927 | the earliest, to be sure to obtain the stat. This still guarantees
|
|---|
| 928 | that a stat is made as late as possible. Return true if the top node
|
|---|
| 929 | in TREE requires a stat, false if not. */
|
|---|
| 930 |
|
|---|
| 931 | static boolean
|
|---|
| 932 | mark_stat (struct predicate *tree)
|
|---|
| 933 | {
|
|---|
| 934 | /* The tree is executed in-order, so walk this way (apologies to Aerosmith)
|
|---|
| 935 | to find the first predicate for which the stat is needed. */
|
|---|
| 936 | switch (tree->p_type)
|
|---|
| 937 | {
|
|---|
| 938 | case NO_TYPE:
|
|---|
| 939 | case PRIMARY_TYPE:
|
|---|
| 940 | return tree->need_stat;
|
|---|
| 941 |
|
|---|
| 942 | case UNI_OP:
|
|---|
| 943 | if (mark_stat (tree->pred_right))
|
|---|
| 944 | tree->need_stat = true;
|
|---|
| 945 | return (false);
|
|---|
| 946 |
|
|---|
| 947 | case BI_OP:
|
|---|
| 948 | /* ANDs and ORs are linked along ->left ending in NULL. */
|
|---|
| 949 | if (tree->pred_left != NULL)
|
|---|
| 950 | mark_stat (tree->pred_left);
|
|---|
| 951 |
|
|---|
| 952 | if (mark_stat (tree->pred_right))
|
|---|
| 953 | tree->need_stat = true;
|
|---|
| 954 |
|
|---|
| 955 | return (false);
|
|---|
| 956 |
|
|---|
| 957 | default:
|
|---|
| 958 | error (1, 0, _("oops -- invalid expression type in mark_stat!"));
|
|---|
| 959 | return (false);
|
|---|
| 960 | }
|
|---|
| 961 | }
|
|---|
| 962 | |
|---|
| 963 |
|
|---|
| 964 | /* Find the first node in expression tree TREE that we will
|
|---|
| 965 | need to know the file type, if any. Operates in the same
|
|---|
| 966 | was as mark_stat().
|
|---|
| 967 | */
|
|---|
| 968 | static boolean
|
|---|
| 969 | mark_type (struct predicate *tree)
|
|---|
| 970 | {
|
|---|
| 971 | /* The tree is executed in-order, so walk this way (apologies to Aerosmith)
|
|---|
| 972 | to find the first predicate for which the type information is needed. */
|
|---|
| 973 | switch (tree->p_type)
|
|---|
| 974 | {
|
|---|
| 975 | case NO_TYPE:
|
|---|
| 976 | case PRIMARY_TYPE:
|
|---|
| 977 | return tree->need_type;
|
|---|
| 978 |
|
|---|
| 979 | case UNI_OP:
|
|---|
| 980 | if (mark_type (tree->pred_right))
|
|---|
| 981 | tree->need_type = true;
|
|---|
| 982 | return false;
|
|---|
| 983 |
|
|---|
| 984 | case BI_OP:
|
|---|
| 985 | /* ANDs and ORs are linked along ->left ending in NULL. */
|
|---|
| 986 | if (tree->pred_left != NULL)
|
|---|
| 987 | mark_type (tree->pred_left);
|
|---|
| 988 |
|
|---|
| 989 | if (mark_type (tree->pred_right))
|
|---|
| 990 | tree->need_type = true;
|
|---|
| 991 |
|
|---|
| 992 | return false;
|
|---|
| 993 |
|
|---|
| 994 | default:
|
|---|
| 995 | error (1, 0, _("oops -- invalid expression type in mark_type!"));
|
|---|
| 996 | return (false);
|
|---|
| 997 | }
|
|---|
| 998 | }
|
|---|
| 999 | |
|---|
| 1000 |
|
|---|
| 1001 | struct pred_cost_lookup
|
|---|
| 1002 | {
|
|---|
| 1003 | PRED_FUNC fn;
|
|---|
| 1004 | enum EvaluationCost cost;
|
|---|
| 1005 | };
|
|---|
| 1006 | static struct pred_cost_lookup costlookup[] =
|
|---|
| 1007 | {
|
|---|
| 1008 | { pred_amin , NeedsStatInfo },
|
|---|
| 1009 | { pred_and , NeedsNothing, },
|
|---|
| 1010 | { pred_anewer , NeedsStatInfo, },
|
|---|
| 1011 | { pred_atime , NeedsStatInfo, },
|
|---|
| 1012 | { pred_close , NeedsNothing },
|
|---|
| 1013 | { pred_cmin , NeedsStatInfo, },
|
|---|
| 1014 | { pred_cnewer , NeedsStatInfo, },
|
|---|
| 1015 | { pred_comma , NeedsNothing, },
|
|---|
| 1016 | { pred_ctime , NeedsStatInfo, },
|
|---|
| 1017 | { pred_delete , NeedsSyncDiskHit },
|
|---|
| 1018 | { pred_empty , NeedsStatInfo },
|
|---|
| 1019 | { pred_exec , NeedsEventualExec },
|
|---|
| 1020 | { pred_execdir , NeedsEventualExec },
|
|---|
| 1021 | { pred_executable, NeedsAccessInfo },
|
|---|
| 1022 | { pred_false , NeedsNothing },
|
|---|
| 1023 | { pred_fprint , NeedsNothing },
|
|---|
| 1024 | { pred_fprint0 , NeedsNothing },
|
|---|
| 1025 | { pred_fprintf , NeedsNothing },
|
|---|
| 1026 | { pred_fstype , NeedsStatInfo }, /* true for amortised cost */
|
|---|
| 1027 | { pred_gid , NeedsStatInfo },
|
|---|
| 1028 | { pred_group , NeedsStatInfo },
|
|---|
| 1029 | { pred_ilname , NeedsLinkName },
|
|---|
| 1030 | { pred_iname , NeedsNothing },
|
|---|
| 1031 | { pred_inum , NeedsStatInfo },
|
|---|
| 1032 | { pred_ipath , NeedsNothing },
|
|---|
| 1033 | { pred_links , NeedsStatInfo },
|
|---|
| 1034 | { pred_lname , NeedsLinkName },
|
|---|
| 1035 | { pred_ls , NeedsStatInfo },
|
|---|
| 1036 | { pred_mmin , NeedsStatInfo },
|
|---|
| 1037 | { pred_mtime , NeedsStatInfo },
|
|---|
| 1038 | { pred_name , NeedsNothing },
|
|---|
| 1039 | { pred_negate , NeedsNothing, },
|
|---|
| 1040 | { pred_newer , NeedsStatInfo, },
|
|---|
| 1041 | { pred_nogroup , NeedsStatInfo }, /* true for amortised cost if caching is on */
|
|---|
| 1042 | { pred_nouser , NeedsStatInfo }, /* true for amortised cost if caching is on */
|
|---|
| 1043 | { pred_ok , NeedsUserInteraction },
|
|---|
| 1044 | { pred_okdir , NeedsUserInteraction },
|
|---|
| 1045 | { pred_open , NeedsNothing },
|
|---|
| 1046 | { pred_or , NeedsNothing, },
|
|---|
| 1047 | { pred_path , NeedsNothing },
|
|---|
| 1048 | { pred_perm , NeedsStatInfo },
|
|---|
| 1049 | { pred_print , NeedsNothing },
|
|---|
| 1050 | { pred_print0 , NeedsNothing },
|
|---|
| 1051 | { pred_prune , NeedsNothing },
|
|---|
| 1052 | { pred_quit , NeedsNothing },
|
|---|
| 1053 | { pred_readable , NeedsAccessInfo },
|
|---|
| 1054 | { pred_regex , NeedsNothing },
|
|---|
| 1055 | { pred_samefile , NeedsStatInfo },
|
|---|
| 1056 | { pred_size , NeedsStatInfo },
|
|---|
| 1057 | { pred_true , NeedsNothing },
|
|---|
| 1058 | { pred_type , NeedsType },
|
|---|
| 1059 | { pred_uid , NeedsStatInfo },
|
|---|
| 1060 | { pred_used , NeedsStatInfo },
|
|---|
| 1061 | { pred_user , NeedsStatInfo },
|
|---|
| 1062 | { pred_writable , NeedsAccessInfo },
|
|---|
| 1063 | { pred_xtype , NeedsType } /* roughly correct unless most files are symlinks */
|
|---|
| 1064 | };
|
|---|
| 1065 | static int pred_table_sorted = 0;
|
|---|
| 1066 | |
|---|
| 1067 |
|
|---|
| 1068 | static boolean
|
|---|
| 1069 | check_sorted(void *base, size_t members, size_t membersize,
|
|---|
| 1070 | int (*cmpfn)(const void*, const void*))
|
|---|
| 1071 | {
|
|---|
| 1072 | const char *p = base;
|
|---|
| 1073 | size_t i;
|
|---|
| 1074 | for (i=1u; i<members; ++i)
|
|---|
| 1075 | {
|
|---|
| 1076 | int result = cmpfn(p+i*membersize, p+(i-1)*membersize);
|
|---|
| 1077 | if (result < 0)
|
|---|
| 1078 | return false;
|
|---|
| 1079 | result = cmpfn(p+(i-1)*membersize, p+i*membersize);
|
|---|
| 1080 | assert(result <= 0);
|
|---|
| 1081 | }
|
|---|
| 1082 | for (i=1u; i<members; ++i)
|
|---|
| 1083 | {
|
|---|
| 1084 | const struct pred_cost_lookup *pl1 = (const struct pred_cost_lookup*)(p+(i-1)*membersize);
|
|---|
| 1085 | const struct pred_cost_lookup *pl2 = (const struct pred_cost_lookup*)(p+(i-0)*membersize);
|
|---|
| 1086 | assert(pl1->fn <= pl2->fn);
|
|---|
| 1087 | }
|
|---|
| 1088 | return true;
|
|---|
| 1089 | }
|
|---|
| 1090 |
|
|---|
| 1091 | |
|---|
| 1092 |
|
|---|
| 1093 | static int
|
|---|
| 1094 | cost_table_comparison(const void *p1, const void *p2)
|
|---|
| 1095 | {
|
|---|
| 1096 | const struct pred_cost_lookup *pc1 = p1;
|
|---|
| 1097 | const struct pred_cost_lookup *pc2 = p2;
|
|---|
| 1098 |
|
|---|
| 1099 |
|
|---|
| 1100 | if (pc1->fn == pc2->fn)
|
|---|
| 1101 | return 0;
|
|---|
| 1102 | else if (pc1->fn > pc2->fn)
|
|---|
| 1103 | return 1;
|
|---|
| 1104 | else
|
|---|
| 1105 | return -1;
|
|---|
| 1106 | }
|
|---|
| 1107 | |
|---|
| 1108 |
|
|---|
| 1109 | static enum EvaluationCost
|
|---|
| 1110 | get_pred_cost(struct predicate *p)
|
|---|
| 1111 | {
|
|---|
| 1112 | enum EvaluationCost data_requirement_cost = NeedsNothing;
|
|---|
| 1113 | enum EvaluationCost inherent_cost = NeedsUnknown;
|
|---|
| 1114 | PRED_FUNC f = p->pred_func;
|
|---|
| 1115 |
|
|---|
| 1116 | if (p->need_stat)
|
|---|
| 1117 | {
|
|---|
| 1118 | data_requirement_cost = NeedsStatInfo;
|
|---|
| 1119 | }
|
|---|
| 1120 | else if (p->need_type)
|
|---|
| 1121 | {
|
|---|
| 1122 | data_requirement_cost = NeedsType;
|
|---|
| 1123 | }
|
|---|
| 1124 | else
|
|---|
| 1125 | {
|
|---|
| 1126 | data_requirement_cost = NeedsNothing;
|
|---|
| 1127 | }
|
|---|
| 1128 |
|
|---|
| 1129 | if (pred_exec == f || pred_execdir == f)
|
|---|
| 1130 | {
|
|---|
| 1131 | if (p->args.exec_vec.multiple)
|
|---|
| 1132 | inherent_cost = NeedsEventualExec;
|
|---|
| 1133 | else
|
|---|
| 1134 | inherent_cost = NeedsImmediateExec;
|
|---|
| 1135 | }
|
|---|
| 1136 | else if (pred_fprintf == f)
|
|---|
| 1137 | {
|
|---|
| 1138 | /* the parser calculated the cost for us. */
|
|---|
| 1139 | inherent_cost = p->p_cost;
|
|---|
| 1140 | }
|
|---|
| 1141 | else
|
|---|
| 1142 | {
|
|---|
| 1143 | struct pred_cost_lookup key;
|
|---|
| 1144 | void *entry;
|
|---|
| 1145 |
|
|---|
| 1146 | if (!pred_table_sorted)
|
|---|
| 1147 | {
|
|---|
| 1148 | qsort(costlookup,
|
|---|
| 1149 | sizeof(costlookup)/sizeof(costlookup[0]),
|
|---|
| 1150 | sizeof(costlookup[0]),
|
|---|
| 1151 | cost_table_comparison);
|
|---|
| 1152 |
|
|---|
| 1153 | if (!check_sorted(costlookup,
|
|---|
| 1154 | sizeof(costlookup)/sizeof(costlookup[0]),
|
|---|
| 1155 | sizeof(costlookup[0]),
|
|---|
| 1156 | cost_table_comparison))
|
|---|
| 1157 | {
|
|---|
| 1158 | error(1, 0, "Failed to sort the costlookup array.");
|
|---|
| 1159 | }
|
|---|
| 1160 | pred_table_sorted = 1;
|
|---|
| 1161 | }
|
|---|
| 1162 | key.fn = p->pred_func;
|
|---|
| 1163 | entry = bsearch(&key, costlookup,
|
|---|
| 1164 | sizeof(costlookup)/sizeof(costlookup[0]),
|
|---|
| 1165 | sizeof(costlookup[0]),
|
|---|
| 1166 | cost_table_comparison);
|
|---|
| 1167 | if (entry)
|
|---|
| 1168 | {
|
|---|
| 1169 | inherent_cost = ((const struct pred_cost_lookup*)entry)->cost;
|
|---|
| 1170 | }
|
|---|
| 1171 | else
|
|---|
| 1172 | {
|
|---|
| 1173 | error(0, 0, "warning: no cost entry for predicate %s", p->p_name);
|
|---|
| 1174 | inherent_cost = NeedsUnknown;
|
|---|
| 1175 | }
|
|---|
| 1176 | }
|
|---|
| 1177 |
|
|---|
| 1178 | if (inherent_cost > data_requirement_cost)
|
|---|
| 1179 | return inherent_cost;
|
|---|
| 1180 | else
|
|---|
| 1181 | return data_requirement_cost;
|
|---|
| 1182 | }
|
|---|
| 1183 | |
|---|
| 1184 |
|
|---|
| 1185 | static void
|
|---|
| 1186 | estimate_costs (struct predicate *tree)
|
|---|
| 1187 | {
|
|---|
| 1188 | if (tree)
|
|---|
| 1189 | {
|
|---|
| 1190 | estimate_costs(tree->pred_right);
|
|---|
| 1191 | estimate_costs(tree->pred_left);
|
|---|
| 1192 |
|
|---|
| 1193 | tree->p_cost = get_pred_cost(tree);
|
|---|
| 1194 | }
|
|---|
| 1195 | }
|
|---|
| 1196 |
|
|---|
| 1197 | struct predicate*
|
|---|
| 1198 | get_eval_tree(void)
|
|---|
| 1199 | {
|
|---|
| 1200 | return eval_tree;
|
|---|
| 1201 | }
|
|---|
| 1202 |
|
|---|
| 1203 | static float
|
|---|
| 1204 | getrate(const struct predicate *p)
|
|---|
| 1205 | {
|
|---|
| 1206 | if (p)
|
|---|
| 1207 | return p->est_success_rate;
|
|---|
| 1208 | else
|
|---|
| 1209 | return 1.0;
|
|---|
| 1210 | }
|
|---|
| 1211 |
|
|---|
| 1212 |
|
|---|
| 1213 | float
|
|---|
| 1214 | calculate_derived_rates(struct predicate *p)
|
|---|
| 1215 | {
|
|---|
| 1216 | float rate;
|
|---|
| 1217 |
|
|---|
| 1218 | assert(NULL != p);
|
|---|
| 1219 |
|
|---|
| 1220 | if (p->pred_right)
|
|---|
| 1221 | calculate_derived_rates(p->pred_right);
|
|---|
| 1222 | if (p->pred_left)
|
|---|
| 1223 | calculate_derived_rates(p->pred_left);
|
|---|
| 1224 |
|
|---|
| 1225 | assert(p->p_type != CLOSE_PAREN);
|
|---|
| 1226 | assert(p->p_type != OPEN_PAREN);
|
|---|
| 1227 |
|
|---|
| 1228 | if (p->p_type == PRIMARY_TYPE)
|
|---|
| 1229 | {
|
|---|
| 1230 | assert(NULL == p->pred_right);
|
|---|
| 1231 | assert(NULL == p->pred_left);
|
|---|
| 1232 | return p->est_success_rate;
|
|---|
| 1233 | }
|
|---|
| 1234 | else if (p->p_type == UNI_OP)
|
|---|
| 1235 | {
|
|---|
| 1236 | /* Unary operators must have exactly one operand */
|
|---|
| 1237 | assert(pred_negate == p->pred_func);
|
|---|
| 1238 | assert(NULL == p->pred_left);
|
|---|
| 1239 | rate = 1.0 - p->pred_right->est_success_rate;
|
|---|
| 1240 | }
|
|---|
| 1241 | else if (p->p_type == BI_OP)
|
|---|
| 1242 | {
|
|---|
| 1243 | /* Binary operators must have two operands */
|
|---|
| 1244 | if (pred_and == p->pred_func)
|
|---|
| 1245 | {
|
|---|
| 1246 | rate = getrate(p->pred_right) * getrate(p->pred_left);
|
|---|
| 1247 | }
|
|---|
| 1248 | else if (pred_comma == p->pred_func)
|
|---|
| 1249 | {
|
|---|
| 1250 | rate = 1.0;
|
|---|
| 1251 | }
|
|---|
| 1252 | else if (pred_or == p->pred_func)
|
|---|
| 1253 | {
|
|---|
| 1254 | rate = getrate(p->pred_right) + getrate(p->pred_left);
|
|---|
| 1255 | }
|
|---|
| 1256 | }
|
|---|
| 1257 | else if (p->p_type == CLOSE_PAREN || p->p_type == OPEN_PAREN)
|
|---|
| 1258 | {
|
|---|
| 1259 | rate = 1.0;
|
|---|
| 1260 | }
|
|---|
| 1261 | else if (p->p_type == NO_TYPE)
|
|---|
| 1262 | {
|
|---|
| 1263 | assert(NULL == p->pred_right);
|
|---|
| 1264 | assert(NULL == p->pred_left);
|
|---|
| 1265 | return p->est_success_rate;
|
|---|
| 1266 | }
|
|---|
| 1267 | p->est_success_rate = constrain_rate(rate);
|
|---|
| 1268 | return p->est_success_rate;
|
|---|
| 1269 | }
|
|---|
| 1270 | |
|---|
| 1271 |
|
|---|
| 1272 | /* opt_expr() rearranges predicates such that each left subtree is
|
|---|
| 1273 | * rooted at a logical predicate (e.g. and or or). check_normalization()
|
|---|
| 1274 | * asserts that this property still holds.
|
|---|
| 1275 | *
|
|---|
| 1276 | */
|
|---|
| 1277 | static void check_normalization(struct predicate *p, boolean at_root)
|
|---|
| 1278 | {
|
|---|
| 1279 | if (at_root)
|
|---|
| 1280 | {
|
|---|
| 1281 | assert(BI_OP == p->p_type);
|
|---|
| 1282 | }
|
|---|
| 1283 |
|
|---|
| 1284 | if (p->pred_left)
|
|---|
| 1285 | {
|
|---|
| 1286 | assert(BI_OP == p->pred_left->p_type);
|
|---|
| 1287 | check_normalization(p->pred_left, false);
|
|---|
| 1288 | }
|
|---|
| 1289 | if (p->pred_right)
|
|---|
| 1290 | {
|
|---|
| 1291 | check_normalization(p->pred_right, false);
|
|---|
| 1292 | }
|
|---|
| 1293 | }
|
|---|
| 1294 | |
|---|
| 1295 |
|
|---|
| 1296 | struct predicate*
|
|---|
| 1297 | build_expression_tree(int argc, char *argv[], int end_of_leading_options)
|
|---|
| 1298 | {
|
|---|
| 1299 | const struct parser_table *parse_entry; /* Pointer to the parsing table entry for this expression. */
|
|---|
| 1300 | char *predicate_name; /* Name of predicate being parsed. */
|
|---|
| 1301 | struct predicate *cur_pred;
|
|---|
| 1302 | const struct parser_table *entry_close, *entry_print, *entry_open;
|
|---|
| 1303 | int i, oldi;
|
|---|
| 1304 |
|
|---|
| 1305 | predicates = NULL;
|
|---|
| 1306 |
|
|---|
| 1307 | /* Find where in ARGV the predicates begin by skipping the list of start points. */
|
|---|
| 1308 | for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++)
|
|---|
| 1309 | {
|
|---|
| 1310 | /* fprintf(stderr, "Looks like %s is not a predicate\n", argv[i]); */
|
|---|
| 1311 | /* Do nothing. */ ;
|
|---|
| 1312 | }
|
|---|
| 1313 |
|
|---|
| 1314 | /* XXX: beginning of bit we need factor out of both find.c and ftsfind.c */
|
|---|
| 1315 | /* Enclose the expression in `( ... )' so a default -print will
|
|---|
| 1316 | apply to the whole expression. */
|
|---|
| 1317 | entry_open = find_parser("(");
|
|---|
| 1318 | entry_close = find_parser(")");
|
|---|
| 1319 | entry_print = find_parser("print");
|
|---|
| 1320 | assert(entry_open != NULL);
|
|---|
| 1321 | assert(entry_close != NULL);
|
|---|
| 1322 | assert(entry_print != NULL);
|
|---|
| 1323 |
|
|---|
| 1324 | parse_open (entry_open, argv, &argc);
|
|---|
| 1325 | last_pred->p_name = "(";
|
|---|
| 1326 | predicates->artificial = true;
|
|---|
| 1327 | parse_begin_user_args(argv, argc, last_pred, predicates);
|
|---|
| 1328 | pred_sanity_check(last_pred);
|
|---|
| 1329 |
|
|---|
| 1330 | /* Build the input order list. */
|
|---|
| 1331 | while (i < argc )
|
|---|
| 1332 | {
|
|---|
| 1333 | if (!looks_like_expression(argv[i], false))
|
|---|
| 1334 | {
|
|---|
| 1335 | error (0, 0, _("paths must precede expression: %s"), argv[i]);
|
|---|
| 1336 | usage(stderr, 1, NULL);
|
|---|
| 1337 | }
|
|---|
| 1338 |
|
|---|
| 1339 | predicate_name = argv[i];
|
|---|
| 1340 | parse_entry = find_parser (predicate_name);
|
|---|
| 1341 | if (parse_entry == NULL)
|
|---|
| 1342 | {
|
|---|
| 1343 | /* Command line option not recognized */
|
|---|
| 1344 | error (1, 0, _("invalid predicate `%s'"), predicate_name);
|
|---|
| 1345 | }
|
|---|
| 1346 |
|
|---|
| 1347 | i++;
|
|---|
| 1348 | oldi = i;
|
|---|
| 1349 | if (!(*(parse_entry->parser_func)) (parse_entry, argv, &i))
|
|---|
| 1350 | {
|
|---|
| 1351 | if (argv[i] == NULL)
|
|---|
| 1352 | /* Command line option requires an argument */
|
|---|
| 1353 | error (1, 0, _("missing argument to `%s'"), predicate_name);
|
|---|
| 1354 | else
|
|---|
| 1355 | error (1, 0, _("invalid argument `%s' to `%s'"),
|
|---|
| 1356 | argv[i], predicate_name);
|
|---|
| 1357 | }
|
|---|
| 1358 | else
|
|---|
| 1359 | {
|
|---|
| 1360 | last_pred->p_name = predicate_name;
|
|---|
| 1361 |
|
|---|
| 1362 | /* If the parser consumed an argument, save it. */
|
|---|
| 1363 | if (i != oldi)
|
|---|
| 1364 | last_pred->arg_text = argv[oldi];
|
|---|
| 1365 | else
|
|---|
| 1366 | last_pred->arg_text = NULL;
|
|---|
| 1367 | }
|
|---|
| 1368 | pred_sanity_check(last_pred);
|
|---|
| 1369 | pred_sanity_check(predicates); /* XXX: expensive */
|
|---|
| 1370 | }
|
|---|
| 1371 | parse_end_user_args(argv, argc, last_pred, predicates);
|
|---|
| 1372 | if (predicates->pred_next == NULL)
|
|---|
| 1373 | {
|
|---|
| 1374 | /* No predicates that do something other than set a global variable
|
|---|
| 1375 | were given; remove the unneeded initial `(' and add `-print'. */
|
|---|
| 1376 | cur_pred = predicates;
|
|---|
| 1377 | predicates = last_pred = predicates->pred_next;
|
|---|
| 1378 | free ((char *) cur_pred);
|
|---|
| 1379 | parse_print (entry_print, argv, &argc);
|
|---|
| 1380 | last_pred->p_name = "-print";
|
|---|
| 1381 | pred_sanity_check(last_pred);
|
|---|
| 1382 | pred_sanity_check(predicates); /* XXX: expensive */
|
|---|
| 1383 | }
|
|---|
| 1384 | else if (!default_prints (predicates->pred_next))
|
|---|
| 1385 | {
|
|---|
| 1386 | /* One or more predicates that produce output were given;
|
|---|
| 1387 | remove the unneeded initial `('. */
|
|---|
| 1388 | cur_pred = predicates;
|
|---|
| 1389 | predicates = predicates->pred_next;
|
|---|
| 1390 | pred_sanity_check(predicates); /* XXX: expensive */
|
|---|
| 1391 | free ((char *) cur_pred);
|
|---|
| 1392 | }
|
|---|
| 1393 | else
|
|---|
| 1394 | {
|
|---|
| 1395 | /* `( user-supplied-expression ) -print'. */
|
|---|
| 1396 | parse_close (entry_close, argv, &argc);
|
|---|
| 1397 | last_pred->p_name = ")";
|
|---|
| 1398 | last_pred->artificial = true;
|
|---|
| 1399 | pred_sanity_check(last_pred);
|
|---|
| 1400 | parse_print (entry_print, argv, &argc);
|
|---|
| 1401 | last_pred->p_name = "-print";
|
|---|
| 1402 | last_pred->artificial = true;
|
|---|
| 1403 | pred_sanity_check(last_pred);
|
|---|
| 1404 | pred_sanity_check(predicates); /* XXX: expensive */
|
|---|
| 1405 | }
|
|---|
| 1406 |
|
|---|
| 1407 | if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
|
|---|
| 1408 | {
|
|---|
| 1409 | fprintf (stderr, "Predicate List:\n");
|
|---|
| 1410 | print_list (stderr, predicates);
|
|---|
| 1411 | }
|
|---|
| 1412 |
|
|---|
| 1413 | /* do a sanity check */
|
|---|
| 1414 | pred_sanity_check(predicates);
|
|---|
| 1415 |
|
|---|
| 1416 | /* Done parsing the predicates. Build the evaluation tree. */
|
|---|
| 1417 | cur_pred = predicates;
|
|---|
| 1418 | eval_tree = get_expr (&cur_pred, NO_PREC, NULL);
|
|---|
| 1419 | calculate_derived_rates(eval_tree);
|
|---|
| 1420 |
|
|---|
| 1421 | /* Check if we have any left-over predicates (this fixes
|
|---|
| 1422 | * Debian bug #185202).
|
|---|
| 1423 | */
|
|---|
| 1424 | if (cur_pred != NULL)
|
|---|
| 1425 | {
|
|---|
| 1426 | /* cur_pred->p_name is often NULL here */
|
|---|
| 1427 | if (cur_pred->pred_func == pred_close)
|
|---|
| 1428 | {
|
|---|
| 1429 | /* e.g. "find \( -true \) \)" */
|
|---|
| 1430 | error (1, 0, _("you have too many ')'"), cur_pred->p_name);
|
|---|
| 1431 | }
|
|---|
| 1432 | else
|
|---|
| 1433 | {
|
|---|
| 1434 | if (cur_pred->p_name)
|
|---|
| 1435 | error (1, 0, _("unexpected extra predicate '%s'"), cur_pred->p_name);
|
|---|
| 1436 | else
|
|---|
| 1437 | error (1, 0, _("unexpected extra predicate"));
|
|---|
| 1438 | }
|
|---|
| 1439 | }
|
|---|
| 1440 |
|
|---|
| 1441 | if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
|
|---|
| 1442 | {
|
|---|
| 1443 | fprintf (stderr, "Eval Tree:\n");
|
|---|
| 1444 | print_tree (stderr, eval_tree, 0);
|
|---|
| 1445 | }
|
|---|
| 1446 |
|
|---|
| 1447 | estimate_costs(eval_tree);
|
|---|
| 1448 |
|
|---|
| 1449 | /* Rearrange the eval tree in optimal-predicate order. */
|
|---|
| 1450 | opt_expr (&eval_tree);
|
|---|
| 1451 |
|
|---|
| 1452 | /* Check that the tree is in normalised order (opt_expr does this) */
|
|---|
| 1453 | check_normalization(eval_tree, true);
|
|---|
| 1454 |
|
|---|
| 1455 | do_arm_swaps(eval_tree);
|
|---|
| 1456 |
|
|---|
| 1457 | /* Check that the tree is still in normalised order */
|
|---|
| 1458 | check_normalization(eval_tree, true);
|
|---|
| 1459 |
|
|---|
| 1460 | /* Determine the point, if any, at which to stat the file. */
|
|---|
| 1461 | mark_stat (eval_tree);
|
|---|
| 1462 | /* Determine the point, if any, at which to determine file type. */
|
|---|
| 1463 | mark_type (eval_tree);
|
|---|
| 1464 |
|
|---|
| 1465 | if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
|
|---|
| 1466 | {
|
|---|
| 1467 | fprintf (stderr, "Optimized Eval Tree:\n");
|
|---|
| 1468 | print_tree (stderr, eval_tree, 0);
|
|---|
| 1469 | fprintf (stderr, "Optimized command line:\n");
|
|---|
| 1470 | print_optlist(stderr, eval_tree);
|
|---|
| 1471 | fprintf(stderr, "\n");
|
|---|
| 1472 | }
|
|---|
| 1473 |
|
|---|
| 1474 | return eval_tree;
|
|---|
| 1475 | }
|
|---|
| 1476 | |
|---|
| 1477 |
|
|---|
| 1478 | /* Return a pointer to a new predicate structure, which has been
|
|---|
| 1479 | linked in as the last one in the predicates list.
|
|---|
| 1480 |
|
|---|
| 1481 | Set `predicates' to point to the start of the predicates list.
|
|---|
| 1482 | Set `last_pred' to point to the new last predicate in the list.
|
|---|
| 1483 |
|
|---|
| 1484 | Set all cells in the new structure to the default values. */
|
|---|
| 1485 |
|
|---|
| 1486 | struct predicate *
|
|---|
| 1487 | get_new_pred (const struct parser_table *entry)
|
|---|
| 1488 | {
|
|---|
| 1489 | register struct predicate *new_pred;
|
|---|
| 1490 | (void) entry;
|
|---|
| 1491 |
|
|---|
| 1492 | /* Options should not be turned into predicates. */
|
|---|
| 1493 | assert(entry->type != ARG_OPTION);
|
|---|
| 1494 | assert(entry->type != ARG_POSITIONAL_OPTION);
|
|---|
| 1495 |
|
|---|
| 1496 | if (predicates == NULL)
|
|---|
| 1497 | {
|
|---|
| 1498 | predicates = (struct predicate *)
|
|---|
| 1499 | xmalloc (sizeof (struct predicate));
|
|---|
| 1500 | last_pred = predicates;
|
|---|
| 1501 | }
|
|---|
| 1502 | else
|
|---|
| 1503 | {
|
|---|
| 1504 | new_pred = (struct predicate *) xmalloc (sizeof (struct predicate));
|
|---|
| 1505 | last_pred->pred_next = new_pred;
|
|---|
| 1506 | last_pred = new_pred;
|
|---|
| 1507 | }
|
|---|
| 1508 | last_pred->parser_entry = entry;
|
|---|
| 1509 | last_pred->pred_func = NULL;
|
|---|
| 1510 | last_pred->p_name = NULL;
|
|---|
| 1511 | last_pred->p_type = NO_TYPE;
|
|---|
| 1512 | last_pred->p_prec = NO_PREC;
|
|---|
| 1513 | last_pred->side_effects = false;
|
|---|
| 1514 | last_pred->no_default_print = false;
|
|---|
| 1515 | last_pred->need_stat = true;
|
|---|
| 1516 | last_pred->need_type = true;
|
|---|
| 1517 | last_pred->args.str = NULL;
|
|---|
| 1518 | last_pred->pred_next = NULL;
|
|---|
| 1519 | last_pred->pred_left = NULL;
|
|---|
| 1520 | last_pred->pred_right = NULL;
|
|---|
| 1521 | last_pred->literal_control_chars = options.literal_control_chars;
|
|---|
| 1522 | last_pred->artificial = false;
|
|---|
| 1523 | last_pred->est_success_rate = 1.0;
|
|---|
| 1524 | return last_pred;
|
|---|
| 1525 | }
|
|---|
| 1526 | |
|---|
| 1527 |
|
|---|
| 1528 | /* Return a pointer to a new predicate, with operator check.
|
|---|
| 1529 | Like get_new_pred, but it checks to make sure that the previous
|
|---|
| 1530 | predicate is an operator. If it isn't, the AND operator is inserted. */
|
|---|
| 1531 |
|
|---|
| 1532 | struct predicate *
|
|---|
| 1533 | get_new_pred_chk_op (const struct parser_table *entry)
|
|---|
| 1534 | {
|
|---|
| 1535 | struct predicate *new_pred;
|
|---|
| 1536 | static const struct parser_table *entry_and = NULL;
|
|---|
| 1537 |
|
|---|
| 1538 | /* Locate the entry in the parser table for the "and" operator */
|
|---|
| 1539 | if (NULL == entry_and)
|
|---|
| 1540 | entry_and = find_parser("and");
|
|---|
| 1541 |
|
|---|
| 1542 | /* Check that it's actually there. If not, that is a bug.*/
|
|---|
| 1543 | assert(entry_and != NULL);
|
|---|
| 1544 |
|
|---|
| 1545 | if (last_pred)
|
|---|
| 1546 | switch (last_pred->p_type)
|
|---|
| 1547 | {
|
|---|
| 1548 | case NO_TYPE:
|
|---|
| 1549 | error (1, 0, _("oops -- invalid default insertion of and!"));
|
|---|
| 1550 | break;
|
|---|
| 1551 |
|
|---|
| 1552 | case PRIMARY_TYPE:
|
|---|
| 1553 | case CLOSE_PAREN:
|
|---|
| 1554 | /* We need to interpose the and operator. */
|
|---|
| 1555 | new_pred = get_new_pred (entry_and);
|
|---|
| 1556 | new_pred->pred_func = pred_and;
|
|---|
| 1557 | new_pred->p_name = "-a";
|
|---|
| 1558 | new_pred->p_type = BI_OP;
|
|---|
| 1559 | new_pred->p_prec = AND_PREC;
|
|---|
| 1560 | new_pred->need_stat = false;
|
|---|
| 1561 | new_pred->need_type = false;
|
|---|
| 1562 | new_pred->args.str = NULL;
|
|---|
| 1563 | new_pred->side_effects = false;
|
|---|
| 1564 | new_pred->no_default_print = false;
|
|---|
| 1565 | break;
|
|---|
| 1566 |
|
|---|
| 1567 | default:
|
|---|
| 1568 | break;
|
|---|
| 1569 | }
|
|---|
| 1570 |
|
|---|
| 1571 | new_pred = get_new_pred (entry);
|
|---|
| 1572 | new_pred->parser_entry = entry;
|
|---|
| 1573 | return new_pred;
|
|---|
| 1574 | }
|
|---|
| 1575 | |
|---|
| 1576 |
|
|---|
| 1577 | struct cost_assoc
|
|---|
| 1578 | {
|
|---|
| 1579 | enum EvaluationCost cost;
|
|---|
| 1580 | char *name;
|
|---|
| 1581 | };
|
|---|
| 1582 | struct cost_assoc cost_table[] =
|
|---|
| 1583 | {
|
|---|
| 1584 | { NeedsNothing, "Nothing" },
|
|---|
| 1585 | { NeedsType, "Type" },
|
|---|
| 1586 | { NeedsStatInfo, "StatInfo" },
|
|---|
| 1587 | { NeedsLinkName, "LinkName" },
|
|---|
| 1588 | { NeedsAccessInfo, "AccessInfo" },
|
|---|
| 1589 | { NeedsSyncDiskHit, "SyncDiskHit" },
|
|---|
| 1590 | { NeedsEventualExec, "EventualExec" },
|
|---|
| 1591 | { NeedsImmediateExec, "ImmediateExec" },
|
|---|
| 1592 | { NeedsUserInteraction, "UserInteraction" },
|
|---|
| 1593 | { NeedsUnknown, "Unknown" }
|
|---|
| 1594 | };
|
|---|
| 1595 | |
|---|
| 1596 |
|
|---|
| 1597 | struct prec_assoc
|
|---|
| 1598 | {
|
|---|
| 1599 | short prec;
|
|---|
| 1600 | char *prec_name;
|
|---|
| 1601 | };
|
|---|
| 1602 |
|
|---|
| 1603 | static struct prec_assoc prec_table[] =
|
|---|
| 1604 | {
|
|---|
| 1605 | {NO_PREC, "no"},
|
|---|
| 1606 | {COMMA_PREC, "comma"},
|
|---|
| 1607 | {OR_PREC, "or"},
|
|---|
| 1608 | {AND_PREC, "and"},
|
|---|
| 1609 | {NEGATE_PREC, "negate"},
|
|---|
| 1610 | {MAX_PREC, "max"},
|
|---|
| 1611 | {-1, "unknown "}
|
|---|
| 1612 | };
|
|---|
| 1613 | |
|---|
| 1614 |
|
|---|
| 1615 | struct op_assoc
|
|---|
| 1616 | {
|
|---|
| 1617 | short type;
|
|---|
| 1618 | char *type_name;
|
|---|
| 1619 | };
|
|---|
| 1620 |
|
|---|
| 1621 | static struct op_assoc type_table[] =
|
|---|
| 1622 | {
|
|---|
| 1623 | {NO_TYPE, "no"},
|
|---|
| 1624 | {PRIMARY_TYPE, "primary"},
|
|---|
| 1625 | {UNI_OP, "uni_op"},
|
|---|
| 1626 | {BI_OP, "bi_op"},
|
|---|
| 1627 | {OPEN_PAREN, "open_paren "},
|
|---|
| 1628 | {CLOSE_PAREN, "close_paren "},
|
|---|
| 1629 | {-1, "unknown"}
|
|---|
| 1630 | };
|
|---|
| 1631 | |
|---|
| 1632 |
|
|---|
| 1633 | static const char *
|
|---|
| 1634 | cost_name (enum EvaluationCost cost)
|
|---|
| 1635 | {
|
|---|
| 1636 | unsigned int i;
|
|---|
| 1637 | unsigned int n = sizeof(cost_table)/sizeof(cost_table[0]);
|
|---|
| 1638 |
|
|---|
| 1639 | for (i = 0; i<n; ++i)
|
|---|
| 1640 | if (cost_table[i].cost == cost)
|
|---|
| 1641 | return cost_table[i].name;
|
|---|
| 1642 | return "unknown";
|
|---|
| 1643 | }
|
|---|
| 1644 | |
|---|
| 1645 |
|
|---|
| 1646 |
|
|---|
| 1647 | static char *
|
|---|
| 1648 | type_name (type)
|
|---|
| 1649 | short type;
|
|---|
| 1650 | {
|
|---|
| 1651 | int i;
|
|---|
| 1652 |
|
|---|
| 1653 | for (i = 0; type_table[i].type != (short) -1; i++)
|
|---|
| 1654 | if (type_table[i].type == type)
|
|---|
| 1655 | break;
|
|---|
| 1656 | return (type_table[i].type_name);
|
|---|
| 1657 | }
|
|---|
| 1658 | |
|---|
| 1659 |
|
|---|
| 1660 | static char *
|
|---|
| 1661 | prec_name (prec)
|
|---|
| 1662 | short prec;
|
|---|
| 1663 | {
|
|---|
| 1664 | int i;
|
|---|
| 1665 |
|
|---|
| 1666 | for (i = 0; prec_table[i].prec != (short) -1; i++)
|
|---|
| 1667 | if (prec_table[i].prec == prec)
|
|---|
| 1668 | break;
|
|---|
| 1669 | return (prec_table[i].prec_name);
|
|---|
| 1670 | }
|
|---|
| 1671 |
|
|---|
| 1672 | |
|---|
| 1673 |
|
|---|
| 1674 | /* Walk the expression tree NODE to stdout.
|
|---|
| 1675 | INDENT is the number of levels to indent the left margin. */
|
|---|
| 1676 |
|
|---|
| 1677 | void
|
|---|
| 1678 | print_tree (FILE *fp, struct predicate *node, int indent)
|
|---|
| 1679 | {
|
|---|
| 1680 | int i;
|
|---|
| 1681 |
|
|---|
| 1682 | if (node == NULL)
|
|---|
| 1683 | return;
|
|---|
| 1684 | for (i = 0; i < indent; i++)
|
|---|
| 1685 | fprintf (fp, " ");
|
|---|
| 1686 | fprintf (fp, "pred=[");
|
|---|
| 1687 | print_predicate(fp, node);
|
|---|
| 1688 | fprintf (fp, "] type=%s prec=%s",
|
|---|
| 1689 | type_name (node->p_type), prec_name (node->p_prec));
|
|---|
| 1690 | fprintf (fp, " cost=%s rate=%#03.2g %sside effects ",
|
|---|
| 1691 | cost_name(node->p_cost),
|
|---|
| 1692 | node->est_success_rate,
|
|---|
| 1693 | (node->side_effects ? "" : "no "));
|
|---|
| 1694 |
|
|---|
| 1695 | if (node->need_stat || node->need_type)
|
|---|
| 1696 | {
|
|---|
| 1697 | int comma = 0;
|
|---|
| 1698 |
|
|---|
| 1699 | fprintf (fp, "Needs ");
|
|---|
| 1700 | if (node->need_stat)
|
|---|
| 1701 | {
|
|---|
| 1702 | fprintf (fp, "stat");
|
|---|
| 1703 | comma = 1;
|
|---|
| 1704 | }
|
|---|
| 1705 | if (node->need_type)
|
|---|
| 1706 | {
|
|---|
| 1707 | fprintf (fp, "%stype", comma ? "," : "");
|
|---|
| 1708 | }
|
|---|
| 1709 | }
|
|---|
| 1710 | fprintf (fp, "\n");
|
|---|
| 1711 |
|
|---|
| 1712 |
|
|---|
| 1713 | for (i = 0; i < indent; i++)
|
|---|
| 1714 | fprintf (fp, " ");
|
|---|
| 1715 | if (NULL == node->pred_left && NULL == node->pred_right)
|
|---|
| 1716 | {
|
|---|
| 1717 | fprintf (fp, "no children.\n");
|
|---|
| 1718 | }
|
|---|
| 1719 | else
|
|---|
| 1720 | {
|
|---|
| 1721 | if (node->pred_left)
|
|---|
| 1722 | {
|
|---|
| 1723 | fprintf (fp, "left:\n");
|
|---|
| 1724 | print_tree (fp, node->pred_left, indent + 1);
|
|---|
| 1725 | }
|
|---|
| 1726 | else
|
|---|
| 1727 | {
|
|---|
| 1728 | fprintf (fp, "no left.\n");
|
|---|
| 1729 | }
|
|---|
| 1730 |
|
|---|
| 1731 | for (i = 0; i < indent; i++)
|
|---|
| 1732 | fprintf (fp, " ");
|
|---|
| 1733 | if (node->pred_right)
|
|---|
| 1734 | {
|
|---|
| 1735 | fprintf (fp, "right:\n");
|
|---|
| 1736 | print_tree (fp, node->pred_right, indent + 1);
|
|---|
| 1737 | }
|
|---|
| 1738 | else
|
|---|
| 1739 | {
|
|---|
| 1740 | fprintf (fp, "no right.\n");
|
|---|
| 1741 | }
|
|---|
| 1742 | }
|
|---|
| 1743 | }
|
|---|