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 "libpspp/bit-vector.h"
25 #include "output/spv/spv.h"
27 #include "gl/c-ctype.h"
28 #include "gl/xalloc.h"
30 /* Returns true if ITEM represents a command, false otherwise.
32 The root item and each of its immediate children are considered to be
35 is_command_item (const struct spv_item *item)
37 return !item->parent || !item->parent->parent;
40 static struct spv_item *
41 find_command_item (struct spv_item *item)
43 while (!is_command_item (item))
49 string_matches (const char *pattern, const char *s)
51 /* XXX This should be a Unicode case insensitive comparison. */
52 while (c_tolower (*pattern) == c_tolower (*s))
61 return pattern[0] == '*' && pattern[1] == '\0';
65 string_array_matches (const char *name, const struct string_array *array)
72 for (size_t i = 0; i < array->n; i++)
73 if (string_matches (array->strings[i], name))
80 match (const char *name,
81 const struct string_array *white,
82 const struct string_array *black)
84 return (string_array_matches (name, white) != false
85 && string_array_matches (name, black) != true);
89 match_instance (const int *instances, size_t n_instances,
90 int instance_within_command)
93 for (size_t i = 0; i < n_instances; i++)
95 if (instances[i] == instance_within_command)
97 else if (instances[i] == -1)
104 match_command (size_t nth_command, size_t *commands, size_t n_commands)
106 for (size_t i = 0; i < n_commands; i++)
107 if (nth_command == commands[i])
113 select_matches (const struct spv_reader *spv, const struct spv_criteria *c,
114 unsigned long int *include)
116 /* Counting instances within a command. */
117 struct spv_item *instance_command_item = NULL;
118 int instance_within_command = 0;
119 int last_instance = -1;
121 /* Counting commands. */
122 struct spv_item *command_command_item = NULL;
123 size_t nth_command = 0;
125 struct spv_item *item;
127 SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
131 struct spv_item *new_command_item = find_command_item (item);
132 if (new_command_item != instance_command_item)
134 if (last_instance >= 0)
136 bitvector_set1 (include, last_instance);
140 instance_command_item = new_command_item;
141 instance_within_command = 0;
144 if (!((1u << spv_item_get_class (item)) & c->classes))
147 if (!c->include_hidden && !spv_item_is_visible (item))
152 spv_item_load (item);
157 if (!match (spv_item_get_command_id (item),
158 &c->include.commands, &c->exclude.commands))
163 if (new_command_item != command_command_item)
165 command_command_item = new_command_item;
169 if (!match_command (nth_command, c->commands, c->n_commands))
173 if (!match (spv_item_get_subtype (item),
174 &c->include.subtypes, &c->exclude.subtypes))
177 if (!match (spv_item_get_label (item),
178 &c->include.labels, &c->exclude.labels))
182 && !((item->xml_member
183 && string_array_matches (item->xml_member, &c->members)) ||
185 && string_array_matches (item->bin_member, &c->members))))
190 if (is_command_item (item))
192 instance_within_command++;
194 int include_instance = match_instance (c->instances, c->n_instances,
195 instance_within_command);
196 if (!include_instance)
198 else if (include_instance < 0)
200 last_instance = index;
205 bitvector_set1 (include, index);
208 if (last_instance >= 0)
209 bitvector_set1 (include, last_instance);
213 spv_select (const struct spv_reader *spv,
214 const struct spv_criteria c[], size_t nc,
215 struct spv_item ***itemsp, size_t *n_itemsp)
217 struct spv_item *item;
219 struct spv_criteria default_criteria = SPV_CRITERIA_INITIALIZER;
223 c = &default_criteria;
227 size_t max_items = 0;
228 SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
231 /* Allocate bitmap for items then fill it in with selected items. */
232 unsigned long int *include = bitvector_allocate (max_items);
233 for (size_t i = 0; i < nc; i++)
234 select_matches (spv, &c[i], include);
236 /* Copy selected items into output array. */
238 struct spv_item **items = xnmalloc (bitvector_count (include, max_items),
241 SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
242 if (bitvector_is_set (include, i++))
243 items[n_items++] = item;