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 "output/select.h"
23 #include "libpspp/assertion.h"
24 #include "libpspp/bit-vector.h"
25 #include "libpspp/message.h"
27 #include "gl/c-ctype.h"
28 #include "gl/xalloc.h"
31 output_item_class_to_string (enum output_item_class class)
35 #define OUTPUT_CLASS(ENUM, NAME) case OUTPUT_CLASS_##ENUM: return NAME;
42 enum output_item_class
43 output_item_class_from_string (const char *name)
45 #define OUTPUT_CLASS(ENUM, NAME) \
46 if (!strcmp (name, NAME)) return OUTPUT_CLASS_##ENUM;
50 return (enum output_item_class) OUTPUT_N_CLASSES;
53 enum output_item_class
54 output_item_classify (const struct output_item *item)
56 const char *label = output_item_get_label (item);
62 case OUTPUT_ITEM_CHART:
63 return OUTPUT_CLASS_CHARTS;
65 case OUTPUT_ITEM_GROUP:
66 return OUTPUT_CLASS_OUTLINEHEADERS;
68 case OUTPUT_ITEM_IMAGE:
69 return OUTPUT_CLASS_OTHER;
71 case OUTPUT_ITEM_MESSAGE:
72 return (item->message->severity == MSG_S_NOTE
74 : OUTPUT_CLASS_WARNINGS);
76 case OUTPUT_ITEM_PAGE_BREAK:
77 return OUTPUT_CLASS_OTHER;
79 case OUTPUT_ITEM_PAGE_SETUP:
80 return OUTPUT_CLASS_OTHER;
82 case OUTPUT_ITEM_TABLE:
83 return (!strcmp (label, "Warnings") ? OUTPUT_CLASS_WARNINGS
84 : !strcmp (label, "Notes") ? OUTPUT_CLASS_NOTES
85 : OUTPUT_CLASS_TABLES);
87 case OUTPUT_ITEM_TEXT:
88 return (!strcmp (label, "Title") ? OUTPUT_CLASS_HEADINGS
89 : !strcmp (label, "Log") ? OUTPUT_CLASS_LOGS
90 : !strcmp (label, "Page Title") ? OUTPUT_CLASS_PAGETITLE
91 : OUTPUT_CLASS_TEXTS);
94 return OUTPUT_CLASS_UNKNOWN;
99 string_matches (const char *pattern, const char *s)
101 /* XXX This should be a Unicode case insensitive comparison. */
102 while (c_tolower (*pattern) == c_tolower (*s))
104 if (*pattern == '\0')
111 return pattern[0] == '*' && pattern[1] == '\0';
115 string_array_matches (const char *name, const struct string_array *array)
122 for (size_t i = 0; i < array->n; i++)
123 if (string_matches (array->strings[i], name))
130 match (const char *name,
131 const struct string_array *white,
132 const struct string_array *black)
134 return (string_array_matches (name, white) != false
135 && string_array_matches (name, black) != true);
139 match_instance (const int *instances, size_t n_instances,
140 int instance_within_command)
143 for (size_t i = 0; i < n_instances; i++)
145 if (instances[i] == instance_within_command)
147 else if (instances[i] == -1)
154 match_command (size_t nth_command, size_t *commands, size_t n_commands)
156 for (size_t i = 0; i < n_commands; i++)
157 if (nth_command == commands[i])
163 select_matches (const struct output_item **items,
164 unsigned int *depths, size_t n_items,
165 const struct output_criteria *c, unsigned long int *include)
167 /* Counting instances within a command. */
168 int instance_within_command = 0;
169 int last_instance = -1;
171 /* Counting commands. */
172 const struct output_item *command_item = NULL;
173 const struct output_item *command_command_item = NULL;
174 size_t nth_command = 0;
176 for (size_t i = 0; i < n_items; i++)
178 const struct output_item *item = items[i];
183 if (last_instance >= 0)
185 bitvector_set1 (include, last_instance);
188 instance_within_command = 0;
191 if (!((1u << output_item_classify (item)) & c->classes))
194 if (!c->include_hidden && item->type != OUTPUT_ITEM_GROUP && !item->show)
197 if (c->error && (!item->spv_info || !item->spv_info->error))
200 if (!match (item->command_name,
201 &c->include.commands, &c->exclude.commands))
206 if (command_item != command_command_item)
208 command_command_item = command_item;
212 if (!match_command (nth_command, c->commands, c->n_commands))
216 char *subtype = output_item_get_subtype (item);
217 bool match_subtype = match (subtype,
218 &c->include.subtypes, &c->exclude.subtypes);
222 if (!match (output_item_get_label (item),
223 &c->include.labels, &c->exclude.labels))
228 const char *members[4];
229 size_t n = spv_info_get_members (item->spv_info, members,
230 sizeof members / sizeof *members);
233 for (size_t j = 0; j < n; j++)
234 if (string_array_matches (members[j], &c->members) == true)
247 instance_within_command++;
249 int include_instance = match_instance (c->instances, c->n_instances,
250 instance_within_command);
251 if (!include_instance)
253 else if (include_instance < 0)
260 bitvector_set1 (include, i);
263 if (last_instance >= 0)
264 bitvector_set1 (include, last_instance);
268 count_items (const struct output_item *item)
271 if (item->type == OUTPUT_ITEM_GROUP)
272 for (size_t i = 0; i < item->group.n_children; i++)
273 n += count_items (item->group.children[i]);
278 flatten_items (const struct output_item *item, size_t index, size_t depth,
279 const struct output_item **items, unsigned int *depths)
282 depths[index] = depth;
285 if (item->type == OUTPUT_ITEM_GROUP)
286 for (size_t i = 0; i < item->group.n_children; i++)
287 index = flatten_items (item->group.children[i], index, depth + 1,
294 unflatten_items (const struct output_item *in, size_t index,
295 unsigned long *include, struct output_item *out)
297 bool include_item = bitvector_is_set (include, index++);
298 if (in->type == OUTPUT_ITEM_GROUP)
300 /* If we should include the group itself, then clone IN inside OUT, and
301 add any children to the clone instead to OUT directly. */
304 struct output_item *group = group_item_clone_empty (in);
305 group_item_add_child (out, group);
309 for (size_t i = 0; i < in->group.n_children; i++)
310 index = unflatten_items (in->group.children[i], index, include, out);
315 group_item_add_child (out, output_item_ref (in));
320 /* Consumes IN, which must be a group, and returns a new output item whose
321 children are all the (direct and indirect) children of IN that meet the NC
324 output_select (struct output_item *in,
325 const struct output_criteria c[], size_t nc)
327 assert (in->type == OUTPUT_ITEM_GROUP);
331 /* Number of items (not counting the root). */
332 size_t n_items = count_items (in) - 1;
334 const struct output_item **items = xnmalloc (n_items, sizeof *items);
335 unsigned int *depths = xnmalloc (n_items, sizeof *depths);
336 size_t n_flattened = 0;
337 for (size_t i = 0; i < in->group.n_children; i++)
338 n_flattened = flatten_items (in->group.children[i], n_flattened,
340 assert (n_flattened == n_items);
342 unsigned long int *include = bitvector_allocate (n_items);
343 for (size_t i = 0; i < nc; i++)
344 select_matches (items, depths, n_items, &c[i], include);
346 struct output_item *out = root_item_create ();
347 size_t n_unflattened = 0;
348 for (size_t i = 0; i < in->group.n_children; i++)
349 n_unflattened = unflatten_items (in->group.children[i], n_unflattened,
351 assert (n_unflattened == n_items);
357 output_item_unref (in);