X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fspv%2Fspv-select.c;fp=src%2Foutput%2Fspv%2Fspv-select.c;h=b3beda873016fb625e266465230b8066ebff4e0c;hb=bcaaee5f0bd21f443c8dcb5f67114e63d43673af;hp=0000000000000000000000000000000000000000;hpb=1abd7f599dd0d773add0a98fa3b612bc15aaf422;p=pspp diff --git a/src/output/spv/spv-select.c b/src/output/spv/spv-select.c new file mode 100644 index 0000000000..b3beda8730 --- /dev/null +++ b/src/output/spv/spv-select.c @@ -0,0 +1,217 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "spv-select.h" + +#include + +#include "libpspp/assertion.h" +#include "output/spv/spv.h" + +#include "gl/c-strcase.h" +#include "gl/xalloc.h" + +static bool +is_descendant (const struct spv_item *ancestor, + const struct spv_item *descendant) +{ + for (; descendant; descendant = descendant->parent) + if (descendant == ancestor) + return true; + return false; +} + +static struct spv_item * +find_command_item (struct spv_item *item) +{ + /* A command item itself does not have a command item. */ + if (!item->parent || !item->parent->parent) + return NULL; + + do + { + item = item->parent; + } + while (item->parent && item->parent->parent); + return item; +} + +void +spv_select (const struct spv_reader *spv, const struct spv_criteria *c, + struct spv_item ***itemsp, size_t *n_itemsp) +{ + size_t n_items = 0; + size_t allocated_items = 0; + struct spv_item **items = NULL; + + struct spv_item **nth_command = xcalloc (c->n_commands, sizeof *nth_command); + const struct spv_item *root = spv_get_root (spv); + for (size_t i = 0; i < c->n_commands; i++) + { + const struct spv_command_match *cm = &c->commands[i]; + if (cm->instance < 0) + { + for (size_t j = root->n_children; j--; ) + { + struct spv_item *item = root->children[j]; + if (item->command_id + && (!cm->name || !strcmp (item->command_id, cm->name))) + { + nth_command[i] = item; + break; + } + } + } + else if (cm->instance > 0) + { + size_t n = 0; + for (size_t j = 0; j < root->n_children; j++) + { + struct spv_item *item = root->children[j]; + if (item->command_id + && (!cm->name || !strcmp (item->command_id, cm->name)) + && ++n == cm->instance) + { + nth_command[i] = item; + break; + } + } + } + } + + struct spv_item *item; + struct spv_item *command_item = NULL; + int instance_within_command = 0; + bool included_as_last_instance = false; + SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv)) + { + if (!((1u << spv_item_get_class (item)) & c->classes)) + continue; + + if (!c->include_hidden && !spv_item_is_visible (item)) + continue; + + if (c->error) + { + spv_item_load (item); + if (!item->error) + continue; + } + + if (c->commands) + { + const char *id = spv_item_get_command_id (item); + if (!id) + continue; + + for (size_t i = 0; i < c->n_commands; i++) + { + const struct spv_command_match *cm = &c->commands[i]; + if ((!cm->name || !c_strcasecmp (cm->name, id)) + && (!cm->instance + || (nth_command[i] + && is_descendant (nth_command[i], item)))) + goto ok; + } + continue; + ok:; + } + + if (!string_set_is_empty (&c->subtypes)) + { + const char *subtype = spv_item_get_subtype (item); + if (!subtype || !string_set_contains (&c->subtypes, subtype)) + continue; + } + + if (c->n_labels) + { + const char *label = spv_item_get_label (item); + if (!label) + continue; + + size_t label_len = strlen (label); + bool match = false; + for (size_t i = 0; !match && i < c->n_labels; i++) + { + const char *arg = c->labels[i].arg; + size_t arg_len = strlen (arg); + switch (c->labels[i].op) + { + case SPV_LABEL_MATCH_EQUALS: + match = !strcmp (label, arg); + break; + case SPV_LABEL_MATCH_CONTAINS: + match = strstr (label, arg); + break; + case SPV_LABEL_MATCH_STARTS: + match = !strncmp (label, arg, arg_len); + break; + case SPV_LABEL_MATCH_ENDS: + match = (label_len >= arg_len + && !memcmp (label + (label_len - arg_len), arg, + arg_len)); + break; + default: + NOT_REACHED (); + } + } + if (!match) + continue; + } + + if (c->n_instances) + { + struct spv_item *new_command_item = find_command_item (item); + if (new_command_item != command_item) + { + command_item = new_command_item; + instance_within_command = 0; + included_as_last_instance = false; + } + if (!command_item) + continue; + instance_within_command++; + + bool include_last = false; + for (size_t i = 0; i < c->n_instances; i++) + if (instance_within_command == c->instances[i]) + goto ok2; + else if (c->instances[i] == -1) + include_last = true; + + if (!include_last) + continue; + if (included_as_last_instance) + n_items--; + else + included_as_last_instance = true; + + ok2:; + } + + if (n_items >= allocated_items) + items = x2nrealloc (items, &allocated_items, sizeof *items); + items[n_items++] = item; + } + + free (nth_command); + + *itemsp = items; + *n_itemsp = n_items; +}