1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2018 Free Software Foundation, Inc.
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 3 of the License, or
7 (at your option) any later version.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "spv-select.h"
23 #include "libpspp/assertion.h"
24 #include "output/spv/spv.h"
26 #include "gl/c-strcase.h"
27 #include "gl/xalloc.h"
30 is_descendant (const struct spv_item *ancestor,
31 const struct spv_item *descendant)
33 for (; descendant; descendant = descendant->parent)
34 if (descendant == ancestor)
39 static struct spv_item *
40 find_command_item (struct spv_item *item)
42 /* A command item itself does not have a command item. */
43 if (!item->parent || !item->parent->parent)
50 while (item->parent && item->parent->parent);
55 spv_select (const struct spv_reader *spv, const struct spv_criteria *c,
56 struct spv_item ***itemsp, size_t *n_itemsp)
59 size_t allocated_items = 0;
60 struct spv_item **items = NULL;
62 struct spv_item **nth_command = xcalloc (c->n_commands, sizeof *nth_command);
63 const struct spv_item *root = spv_get_root (spv);
64 for (size_t i = 0; i < c->n_commands; i++)
66 const struct spv_command_match *cm = &c->commands[i];
69 for (size_t j = root->n_children; j--; )
71 struct spv_item *item = root->children[j];
73 && (!cm->name || !strcmp (item->command_id, cm->name)))
75 nth_command[i] = item;
80 else if (cm->instance > 0)
83 for (size_t j = 0; j < root->n_children; j++)
85 struct spv_item *item = root->children[j];
87 && (!cm->name || !strcmp (item->command_id, cm->name))
88 && ++n == cm->instance)
90 nth_command[i] = item;
97 struct spv_item *item;
98 struct spv_item *command_item = NULL;
99 int instance_within_command = 0;
100 bool included_as_last_instance = false;
101 SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
103 if (!((1u << spv_item_get_class (item)) & c->classes))
106 if (!c->include_hidden && !spv_item_is_visible (item))
111 spv_item_load (item);
118 const char *id = spv_item_get_command_id (item);
122 for (size_t i = 0; i < c->n_commands; i++)
124 const struct spv_command_match *cm = &c->commands[i];
125 if ((!cm->name || !c_strcasecmp (cm->name, id))
128 && is_descendant (nth_command[i], item))))
135 if (!string_set_is_empty (&c->subtypes))
137 const char *subtype = spv_item_get_subtype (item);
138 if (!subtype || !string_set_contains (&c->subtypes, subtype))
144 const char *label = spv_item_get_label (item);
148 size_t label_len = strlen (label);
150 for (size_t i = 0; !match && i < c->n_labels; i++)
152 const char *arg = c->labels[i].arg;
153 size_t arg_len = strlen (arg);
154 switch (c->labels[i].op)
156 case SPV_LABEL_MATCH_EQUALS:
157 match = !strcmp (label, arg);
159 case SPV_LABEL_MATCH_CONTAINS:
160 match = strstr (label, arg);
162 case SPV_LABEL_MATCH_STARTS:
163 match = !strncmp (label, arg, arg_len);
165 case SPV_LABEL_MATCH_ENDS:
166 match = (label_len >= arg_len
167 && !memcmp (label + (label_len - arg_len), arg,
180 struct spv_item *new_command_item = find_command_item (item);
181 if (new_command_item != command_item)
183 command_item = new_command_item;
184 instance_within_command = 0;
185 included_as_last_instance = false;
189 instance_within_command++;
191 bool include_last = false;
192 for (size_t i = 0; i < c->n_instances; i++)
193 if (instance_within_command == c->instances[i])
195 else if (c->instances[i] == -1)
200 if (included_as_last_instance)
203 included_as_last_instance = true;
208 if (n_items >= allocated_items)
209 items = x2nrealloc (items, &allocated_items, sizeof *items);
210 items[n_items++] = item;