1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017, 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/pivot-table.h"
24 #include "data/data-out.h"
25 #include "data/settings.h"
26 #include "data/value.h"
27 #include "data/variable.h"
28 #include "data/file-name.h"
29 #include "libpspp/array.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/hash-functions.h"
32 #include "libpspp/i18n.h"
33 #include "output/driver.h"
34 #include "output/spv/spv-table-look.h"
36 #include "gl/c-ctype.h"
37 #include "gl/configmake.h"
38 #include "gl/intprops.h"
39 #include "gl/minmax.h"
40 #include "gl/relocatable.h"
41 #include "gl/xalloc.h"
42 #include "gl/xmemdup0.h"
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) msgid
49 static void pivot_table_use_rc (const struct pivot_table *, const char *s,
50 struct fmt_spec *, bool *honor_small);
52 /* Pivot table display styling. */
54 /* Returns the name of AREA. */
56 pivot_area_to_string (enum pivot_area area)
60 case PIVOT_AREA_TITLE: return "title";
61 case PIVOT_AREA_CAPTION: return "caption";
62 case PIVOT_AREA_FOOTER: return "footer";
63 case PIVOT_AREA_CORNER: return "corner";
64 case PIVOT_AREA_COLUMN_LABELS: return "column labels";
65 case PIVOT_AREA_ROW_LABELS: return "row labels";
66 case PIVOT_AREA_DATA: return "data";
67 case PIVOT_AREA_LAYERS: return "layers";
68 case PIVOT_N_AREAS: default: return "**error**";
72 /* Returns the name of BORDER. */
74 pivot_border_to_string (enum pivot_border border)
78 case PIVOT_BORDER_TITLE:
81 case PIVOT_BORDER_OUTER_LEFT:
82 return "left outer frame";
83 case PIVOT_BORDER_OUTER_TOP:
84 return "top outer frame";
85 case PIVOT_BORDER_OUTER_RIGHT:
86 return "right outer frame";
87 case PIVOT_BORDER_OUTER_BOTTOM:
88 return "bottom outer frame";
90 case PIVOT_BORDER_INNER_LEFT:
91 return "left inner frame";
92 case PIVOT_BORDER_INNER_TOP:
93 return "top inner frame";
94 case PIVOT_BORDER_INNER_RIGHT:
95 return "right inner frame";
96 case PIVOT_BORDER_INNER_BOTTOM:
97 return "bottom inner frame";
99 case PIVOT_BORDER_DATA_LEFT:
100 return "data area left";
101 case PIVOT_BORDER_DATA_TOP:
102 return "data area top";
104 case PIVOT_BORDER_DIM_ROW_HORZ:
105 return "row label horizontal dimension border";
106 case PIVOT_BORDER_DIM_ROW_VERT:
107 return "row label vertical dimension border";
108 case PIVOT_BORDER_DIM_COL_HORZ:
109 return "column label horizontal dimension border";
110 case PIVOT_BORDER_DIM_COL_VERT:
111 return "column label vertical dimension border";
113 case PIVOT_BORDER_CAT_ROW_HORZ:
114 return "row label horizontal category border";
115 case PIVOT_BORDER_CAT_ROW_VERT:
116 return "row label vertical category border";
117 case PIVOT_BORDER_CAT_COL_HORZ:
118 return "column label horizontal category border";
119 case PIVOT_BORDER_CAT_COL_VERT:
120 return "column label vertical category border";
122 case PIVOT_N_BORDERS:
129 pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
133 free (sizing->widths);
134 free (sizing->breaks);
135 free (sizing->keeps);
139 /* Pivot table looks. */
141 static const struct pivot_table_look *
142 default_look (const struct pivot_table_look *new)
144 static struct pivot_table_look *look;
147 pivot_table_look_unref (look);
148 look = pivot_table_look_ref (new);
152 char *error = pivot_table_look_read ("default.stt", &look);
156 look = pivot_table_look_ref (pivot_table_look_builtin_default ());
162 const struct pivot_table_look *
163 pivot_table_look_get_default (void)
165 return default_look (NULL);
169 pivot_table_look_set_default (const struct pivot_table_look *look)
174 char * WARN_UNUSED_RESULT
175 pivot_table_look_read (const char *name, struct pivot_table_look **lookp)
179 /* Construct search path. */
183 const char *home = getenv ("HOME");
184 char *allocated = NULL;
186 path[n++] = allocated = xasprintf ("%s/.pspp/looks", home);
188 path[n++] = relocate2 (PKGDATADIR "/looks", &allocated2);
192 char *file = fn_search_path (name, (char **) path);
195 char *name2 = xasprintf ("%s.stt", name);
196 file = fn_search_path (name2, (char **) path);
202 return xasprintf ("%s: not found", name);
205 char *error = spv_table_look_read (file, lookp);
210 const struct pivot_table_look *
211 pivot_table_look_builtin_default (void)
213 static struct pivot_table_look look = {
217 .row_labels_in_corner = true,
219 [TABLE_HORZ] = { 36, 72 },
220 [TABLE_VERT] = { 36, 120 },
224 #define AREA(BOLD, H, V, L, R, T, B) { \
226 .halign = TABLE_HALIGN_##H, \
227 .valign = TABLE_VALIGN_##V, \
228 .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \
229 [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \
233 .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \
234 .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \
236 .typeface = (char *) "Sans Serif", \
239 [PIVOT_AREA_TITLE] = AREA(true, CENTER, CENTER, 8,11,1,8),
240 [PIVOT_AREA_CAPTION] = AREA(false, LEFT, TOP, 8,11,1,1),
241 [PIVOT_AREA_FOOTER] = AREA(false, LEFT, TOP, 11, 8,2,3),
242 [PIVOT_AREA_CORNER] = AREA(false, LEFT, BOTTOM, 8,11,1,1),
243 [PIVOT_AREA_COLUMN_LABELS] = AREA(false, CENTER, BOTTOM, 8,11,1,3),
244 [PIVOT_AREA_ROW_LABELS] = AREA(false, LEFT, TOP, 8,11,1,3),
245 [PIVOT_AREA_DATA] = AREA(false, MIXED, TOP, 8,11,1,1),
246 [PIVOT_AREA_LAYERS] = AREA(false, LEFT, BOTTOM, 8,11,1,3),
251 #define BORDER(STROKE) { .stroke = STROKE, .color = CELL_COLOR_BLACK }
252 [PIVOT_BORDER_TITLE] = BORDER(TABLE_STROKE_NONE),
253 [PIVOT_BORDER_OUTER_LEFT] = BORDER(TABLE_STROKE_NONE),
254 [PIVOT_BORDER_OUTER_TOP] = BORDER(TABLE_STROKE_NONE),
255 [PIVOT_BORDER_OUTER_RIGHT] = BORDER(TABLE_STROKE_NONE),
256 [PIVOT_BORDER_OUTER_BOTTOM] = BORDER(TABLE_STROKE_NONE),
257 [PIVOT_BORDER_INNER_LEFT] = BORDER(TABLE_STROKE_THICK),
258 [PIVOT_BORDER_INNER_TOP] = BORDER(TABLE_STROKE_THICK),
259 [PIVOT_BORDER_INNER_RIGHT] = BORDER(TABLE_STROKE_THICK),
260 [PIVOT_BORDER_INNER_BOTTOM] = BORDER(TABLE_STROKE_THICK),
261 [PIVOT_BORDER_DATA_LEFT] = BORDER(TABLE_STROKE_THICK),
262 [PIVOT_BORDER_DATA_TOP] = BORDER(TABLE_STROKE_THICK),
263 [PIVOT_BORDER_DIM_ROW_HORZ] = BORDER(TABLE_STROKE_SOLID),
264 [PIVOT_BORDER_DIM_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
265 [PIVOT_BORDER_DIM_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
266 [PIVOT_BORDER_DIM_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
267 [PIVOT_BORDER_CAT_ROW_HORZ] = BORDER(TABLE_STROKE_NONE),
268 [PIVOT_BORDER_CAT_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
269 [PIVOT_BORDER_CAT_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
270 [PIVOT_BORDER_CAT_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
277 struct pivot_table_look *
278 pivot_table_look_new_builtin_default (void)
280 return pivot_table_look_unshare (
281 pivot_table_look_ref (pivot_table_look_builtin_default ()));
284 struct pivot_table_look *
285 pivot_table_look_ref (const struct pivot_table_look *look_)
287 assert (look_->ref_cnt > 0);
289 struct pivot_table_look *look = CONST_CAST (struct pivot_table_look *, look_);
295 xstrdup_if_nonempty (const char *s)
297 return s && s[0] ? xstrdup (s) : NULL;
300 struct pivot_table_look *
301 pivot_table_look_unshare (struct pivot_table_look *old)
303 assert (old->ref_cnt > 0);
304 if (old->ref_cnt == 1)
307 pivot_table_look_unref (old);
309 struct pivot_table_look *new = xmemdup (old, sizeof *old);
311 new->name = xstrdup_if_nonempty (old->name);
312 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
313 table_area_style_copy (NULL, &new->areas[i], &old->areas[i]);
314 new->continuation = xstrdup_if_nonempty (old->continuation);
320 pivot_table_look_unref (struct pivot_table_look *look)
324 assert (look->ref_cnt > 0);
325 if (!--look->ref_cnt)
328 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
329 table_area_style_uninit (&look->areas[i]);
330 free (look->continuation);
338 /* Returns the name of AXIS_TYPE. */
340 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
344 case PIVOT_AXIS_LAYER:
350 case PIVOT_AXIS_COLUMN:
358 static enum pivot_axis_type
359 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
361 assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
362 return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
365 /* Implementation of PIVOT_AXIS_FOR_EACH. */
367 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
371 if (axis->n_dimensions)
372 for (size_t i = 0; i < axis->n_dimensions; i++)
373 if (axis->dimensions[i]->n_leaves == 0)
376 size_t size = axis->n_dimensions * sizeof *indexes;
377 return xzalloc (MAX (size, 1));
380 for (size_t i = 0; i < axis->n_dimensions; i++)
382 const struct pivot_dimension *d = axis->dimensions[i];
383 if (++indexes[i] < d->n_leaves)
396 pivot_category_set_rc (struct pivot_category *category, const char *s)
401 pivot_table_use_rc (category->dimension->table, s,
402 &category->format, &category->honor_small);
404 /* Ensure that the category itself, in addition to the cells within it, takes
405 the format. (It's kind of rare for a category to have a numeric format
407 struct pivot_value *name = category->name;
408 if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
409 pivot_table_use_rc (category->dimension->table, s,
410 &name->numeric.format, &name->numeric.honor_small);
414 pivot_category_create_leaves_valist (struct pivot_category *parent,
418 while ((s = va_arg (args, const char *)))
420 if (!strncmp (s, "RC_", 3))
422 assert (parent->n_subs);
423 pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
426 pivot_category_create_leaf (parent, pivot_value_new_text (s));
430 /* Creates a new dimension with the given NAME in TABLE and returns it. The
431 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
434 NAME should be a translatable name, but not actually translated yet,
435 e.g. enclosed in N_(). To use a different kind of value for a name, use
436 pivot_dimension_create__() instead.
438 The optional varargs parameters may be used to add an initial set of
439 categories to the dimension. Each string should be a translatable category
440 name, but not actually translated yet, e.g. enclosed in N_(). Each string
441 may optionally be followod by a PIVOT_RC_* string that specifies the default
442 numeric format for cells in this category. */
443 struct pivot_dimension * SENTINEL (0)
444 (pivot_dimension_create) (struct pivot_table *table,
445 enum pivot_axis_type axis_type,
446 const char *name, ...)
448 struct pivot_dimension *d = pivot_dimension_create__ (
449 table, axis_type, pivot_value_new_text (name));
452 va_start (args, name);
453 pivot_category_create_leaves_valist (d->root, args);
459 /* Creates a new dimension with the given NAME in TABLE and returns it. The
460 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
462 struct pivot_dimension *
463 pivot_dimension_create__ (struct pivot_table *table,
464 enum pivot_axis_type axis_type,
465 struct pivot_value *name)
467 assert (pivot_table_is_empty (table));
469 struct pivot_dimension *d = xmalloc (sizeof *d);
470 *d = (struct pivot_dimension) {
472 .axis_type = axis_type,
473 .level = table->axes[axis_type].n_dimensions,
474 .top_index = table->n_dimensions,
475 .root = xmalloc (sizeof *d->root),
478 struct pivot_category *root = d->root;
479 *root = (struct pivot_category) {
484 .data_index = SIZE_MAX,
485 .presentation_index = SIZE_MAX,
488 table->dimensions = xrealloc (
489 table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
490 table->dimensions[table->n_dimensions++] = d;
492 struct pivot_axis *axis = &table->axes[axis_type];
493 axis->dimensions = xrealloc (
494 axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
495 axis->dimensions[axis->n_dimensions++] = d;
497 if (axis_type == PIVOT_AXIS_LAYER)
499 free (table->current_layer);
500 table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
501 sizeof *table->current_layer);
504 /* axis->extent and axis->label_depth will be calculated later. */
510 pivot_dimension_destroy (struct pivot_dimension *d)
515 pivot_category_destroy (d->root);
516 free (d->data_leaves);
517 free (d->presentation_leaves);
521 /* Returns the first leaf node in an in-order traversal that is a child of
523 static const struct pivot_category * UNUSED
524 pivot_category_first_leaf (const struct pivot_category *cat)
526 if (pivot_category_is_leaf (cat))
529 for (size_t i = 0; i < cat->n_subs; i++)
531 const struct pivot_category *first
532 = pivot_category_first_leaf (cat->subs[i]);
540 /* Returns the next leaf node in an in-order traversal starting at CAT, which
542 static const struct pivot_category * UNUSED
543 pivot_category_next_leaf (const struct pivot_category *cat)
545 assert (pivot_category_is_leaf (cat));
549 const struct pivot_category *parent = cat->parent;
552 for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
554 const struct pivot_category *next
555 = pivot_category_first_leaf (parent->subs[i]);
565 pivot_category_add_child (struct pivot_category *child)
567 struct pivot_category *parent = child->parent;
569 assert (pivot_category_is_group (parent));
570 if (parent->n_subs >= parent->allocated_subs)
571 parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
572 sizeof *parent->subs);
573 parent->subs[parent->n_subs++] = child;
576 /* Adds leaf categories as a child of PARENT. To create top-level categories
577 within dimension 'd', pass 'd->root' for PARENT.
579 Each of the varargs parameters should be a string, each of which should be a
580 translatable category name, but not actually translated yet, e.g. enclosed
581 in N_(). Each string may optionally be followod by a PIVOT_RC_* string that
582 specifies the default numeric format for cells in this category.
584 Returns the category index, which is just a 0-based array index, for the
587 Leaves have to be created in in-order, that is, don't create a group and add
588 some leaves, then add leaves outside the group and try to add more leaves
591 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
593 int retval = parent->dimension->n_leaves;
596 va_start (args, parent);
597 pivot_category_create_leaves_valist (parent, args);
603 /* Creates a new leaf category with the given NAME as a child of PARENT. To
604 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
605 Returns the category index, which is just a 0-based array index, for the new
608 Leaves have to be created in in-order, that is, don't create a group and add
609 some leaves, then add leaves outside the group and try to add more leaves
612 pivot_category_create_leaf (struct pivot_category *parent,
613 struct pivot_value *name)
615 return pivot_category_create_leaf_rc (parent, name, NULL);
618 /* Creates a new leaf category with the given NAME as a child of PARENT. To
619 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
620 Returns the category index, which is just a 0-based array index, for the new
623 If RC is nonnull and the name of a result category, the category is assigned
624 that result category.
626 Leaves have to be created in in-order, that is, don't create a group and add
627 some leaves, then add leaves outside the group and try to add more leaves
630 pivot_category_create_leaf_rc (struct pivot_category *parent,
631 struct pivot_value *name, const char *rc)
633 struct pivot_dimension *d = parent->dimension;
635 struct pivot_category *leaf = xmalloc (sizeof *leaf);
636 *leaf = (struct pivot_category) {
640 .group_index = parent->n_subs,
641 .data_index = d->n_leaves,
642 .presentation_index = d->n_leaves,
645 if (d->n_leaves >= d->allocated_leaves)
647 d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
648 sizeof *d->data_leaves);
649 d->presentation_leaves = xrealloc (
650 d->presentation_leaves,
651 d->allocated_leaves * sizeof *d->presentation_leaves);
654 d->data_leaves[d->n_leaves] = leaf;
655 d->presentation_leaves[d->n_leaves] = leaf;
658 pivot_category_add_child (leaf);
660 /* Make sure that the new child is the last in in-order. */
661 assert (!pivot_category_next_leaf (leaf));
663 pivot_category_set_rc (leaf, rc);
665 return leaf->data_index;
668 /* Adds a new category group named NAME as a child of PARENT. To create a
669 top-level group within dimension 'd', pass 'd->root' for PARENT.
671 NAME should be a translatable name, but not actually translated yet,
672 e.g. enclosed in N_(). To use a different kind of value for a name, use
673 pivot_category_create_group__() instead.
675 The optional varargs parameters may be used to add an initial set of
676 categories to the group. Each string should be a translatable category
677 name, but not actually translated yet, e.g. enclosed in N_(). Each string
678 may optionally be followod by a PIVOT_RC_* string that specifies the default
679 numeric format for cells in this category.
681 Returns the new group. */
682 struct pivot_category * SENTINEL (0)
683 (pivot_category_create_group) (struct pivot_category *parent,
684 const char *name, ...)
686 struct pivot_category *group = pivot_category_create_group__ (
687 parent, pivot_value_new_text (name));
690 va_start (args, name);
691 pivot_category_create_leaves_valist (group, args);
697 /* Adds a new category group named NAME as a child of PARENT. To create a
698 top-level group within dimension 'd', pass 'd->root' for PARENT. Returns
700 struct pivot_category *
701 pivot_category_create_group__ (struct pivot_category *parent,
702 struct pivot_value *name)
704 struct pivot_dimension *d = parent->dimension;
706 struct pivot_category *group = xmalloc (sizeof *group);
707 *group = (struct pivot_category) {
712 .group_index = parent->n_subs,
713 .data_index = SIZE_MAX,
714 .presentation_index = SIZE_MAX,
717 pivot_category_add_child (group);
723 pivot_category_destroy (struct pivot_category *c)
728 pivot_value_destroy (c->name);
729 for (size_t i = 0; i < c->n_subs; i++)
730 pivot_category_destroy (c->subs[i]);
737 These are usually the easiest way to control the formatting of numeric data
738 in a pivot table. See pivot_dimension_create() for an explanation of their
742 const char *name; /* "RC_*". */
743 struct fmt_spec format;
746 /* Formats for most of the result classes. */
747 static struct result_class result_classes[] =
749 { PIVOT_RC_INTEGER, { FMT_F, 40, 0 } },
750 { PIVOT_RC_PERCENT, { FMT_PCT, 40, 1 } },
751 { PIVOT_RC_CORRELATION, { FMT_F, 40, 3 } },
752 { PIVOT_RC_SIGNIFICANCE, { FMT_F, 40, 3 } },
753 { PIVOT_RC_RESIDUAL, { FMT_F, 40, 2 } },
754 { PIVOT_RC_COUNT, { 0, 0, 0 } },
755 { PIVOT_RC_OTHER, { 0, 0, 0 } },
758 /* Has PIVOT_RC_COUNT been overridden by the user? */
759 static bool overridden_count_format;
761 static struct result_class *
762 pivot_result_class_find (const char *s)
764 for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
765 if (!strcmp (s, result_classes[i].name))
766 return &result_classes[i];
771 pivot_table_use_rc (const struct pivot_table *table, const char *s,
772 struct fmt_spec *format, bool *honor_small)
776 if (!strcmp (s, PIVOT_RC_OTHER))
778 *format = *settings_get_format ();
781 else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
783 *format = table->weight_format;
784 *honor_small = false;
788 const struct result_class *rc = pivot_result_class_find (s);
791 *format = rc->format;
792 *honor_small = false;
796 printf ("unknown class %s\n", s);
802 /* Sets the format specification for the result class named S (which should not
803 include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
804 does not name a known result class. */
806 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
808 char *s = xasprintf ("RC_%s", s_);
809 struct result_class *rc = pivot_result_class_find (s);
812 rc->format = *format;
813 if (!strcmp (s, PIVOT_RC_COUNT))
814 overridden_count_format = true;
822 is_pivot_result_class (const char *s)
824 return pivot_result_class_find (s) != NULL;
829 static struct pivot_cell *pivot_table_insert_cell (struct pivot_table *,
830 const size_t *dindexes);
831 static void pivot_table_delete_cell (struct pivot_table *,
832 struct pivot_cell *);
834 /* Creates and returns a new pivot table with the given TITLE. TITLE should be
835 a text string marked for translation but not actually translated yet,
836 e.g. N_("Descriptive Statistics"). The un-translated text string is used as
837 the pivot table's subtype.
839 This function is a shortcut for pivot_table_create__() for the most common
840 case. Use pivot_table_create__() directly if the title should be some kind
841 of value other than an ordinary text string, or if the subtype should be
842 different from the title.
844 See the large comment at the top of pivot-table.h for general advice on
845 creating pivot tables. */
847 pivot_table_create (const char *title)
849 return pivot_table_create__ (pivot_value_new_text (title), title);
852 /* Creates and returns a new pivot table with the given TITLE, and takes
853 ownership of TITLE. The new pivot table's subtype is SUBTYPE, which should
854 be an untranslated English string that describes the contents of the table
855 at a high level without being specific about the variables or other context
858 TITLE and SUBTYPE may be NULL, but in that case the client must add them
859 later because they are both mandatory for a pivot table.
861 See the large comment at the top of pivot-table.h for general advice on
862 creating pivot tables. */
864 pivot_table_create__ (struct pivot_value *title, const char *subtype)
866 struct pivot_table *table = xzalloc (sizeof *table);
868 table->show_title = true;
869 table->show_caption = true;
870 table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 };
871 table->title = title;
872 table->subtype = subtype ? pivot_value_new_text (subtype) : NULL;
873 table->command_c = output_get_command_name ();
874 table->look = pivot_table_look_ref (pivot_table_look_get_default ());
875 table->settings = fmt_settings_copy (settings_get_fmt_settings ());
876 table->small = settings_get_small ();
878 hmap_init (&table->cells);
883 /* Creates and returns a new pivot table with the given TITLE and a single cell
884 with the given CONTENT.
886 This is really just for error handling. */
888 pivot_table_create_for_text (struct pivot_value *title,
889 struct pivot_value *content)
891 struct pivot_table *table = pivot_table_create__ (title, "Error");
893 struct pivot_dimension *d = pivot_dimension_create (
894 table, PIVOT_AXIS_ROW, N_("Error"));
895 d->hide_all_labels = true;
896 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
898 pivot_table_put1 (table, 0, content);
903 /* Increases TABLE's reference count, indicating that it has an additional
904 owner. A pivot table that is shared among multiple owners must not be
907 pivot_table_ref (const struct pivot_table *table_)
909 struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
915 xstrdup_if_nonnull (const char *s)
917 return s ? xstrdup (s) : NULL;
920 static struct pivot_table_sizing
921 clone_sizing (const struct pivot_table_sizing *s)
923 return (struct pivot_table_sizing) {
924 .widths = (s->n_widths
925 ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
927 .n_widths = s->n_widths,
929 .breaks = (s->n_breaks
930 ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
932 .n_breaks = s->n_breaks,
935 ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
937 .n_keeps = s->n_keeps,
941 static struct pivot_footnote **
942 clone_footnotes (struct pivot_footnote **old, size_t n)
947 struct pivot_footnote **new = xmalloc (n * sizeof *new);
948 for (size_t i = 0; i < n; i++)
950 new[i] = xmalloc (sizeof *new[i]);
951 *new[i] = (struct pivot_footnote) {
953 .content = pivot_value_clone (old[i]->content),
954 .marker = pivot_value_clone (old[i]->marker),
955 .show = old[i]->show,
961 static struct pivot_category *
962 clone_category (struct pivot_category *old,
963 struct pivot_dimension *new_dimension,
964 struct pivot_category *new_parent)
966 struct pivot_category *new = xmalloc (sizeof *new);
967 *new = (struct pivot_category) {
968 .name = pivot_value_clone (old->name),
969 .parent = new_parent,
970 .dimension = new_dimension,
971 .label_depth = old->label_depth,
972 .extra_depth = old->extra_depth,
975 ? xzalloc (old->n_subs * sizeof *new->subs)
977 .n_subs = old->n_subs,
978 .allocated_subs = old->n_subs,
980 .show_label = old->show_label,
981 .show_label_in_corner = old->show_label_in_corner,
983 .format = old->format,
984 .group_index = old->group_index,
985 .data_index = old->data_index,
986 .presentation_index = old->presentation_index,
989 if (pivot_category_is_leaf (old))
991 assert (new->data_index < new_dimension->n_leaves);
992 new->dimension->data_leaves[new->data_index] = new;
994 assert (new->presentation_index < new_dimension->n_leaves);
995 new->dimension->presentation_leaves[new->presentation_index] = new;
998 for (size_t i = 0; i < new->n_subs; i++)
999 new->subs[i] = clone_category (old->subs[i], new_dimension, new);
1004 static struct pivot_dimension *
1005 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1007 struct pivot_dimension *new = xmalloc (sizeof *new);
1008 *new = (struct pivot_dimension) {
1010 .axis_type = old->axis_type,
1011 .level = old->level,
1012 .top_index = old->top_index,
1013 .data_leaves = xzalloc (old->n_leaves * sizeof *new->data_leaves),
1014 .presentation_leaves = xzalloc (old->n_leaves
1015 * sizeof *new->presentation_leaves),
1016 .n_leaves = old->n_leaves,
1017 .allocated_leaves = old->n_leaves,
1018 .hide_all_labels = old->hide_all_labels,
1019 .label_depth = old->label_depth,
1022 new->root = clone_category (old->root, new, NULL);
1027 static struct pivot_dimension **
1028 clone_dimensions (struct pivot_dimension **old, size_t n,
1029 struct pivot_table *new_pt)
1034 struct pivot_dimension **new = xmalloc (n * sizeof *new);
1035 for (size_t i = 0; i < n; i++)
1036 new[i] = clone_dimension (old[i], new_pt);
1040 struct pivot_table *
1041 pivot_table_unshare (struct pivot_table *old)
1043 assert (old->ref_cnt > 0);
1044 if (old->ref_cnt == 1)
1047 pivot_table_unref (old);
1049 struct pivot_table *new = xmalloc (sizeof *new);
1050 *new = (struct pivot_table) {
1053 .look = pivot_table_look_ref (old->look),
1055 .rotate_inner_column_labels = old->rotate_inner_column_labels,
1056 .rotate_outer_row_labels = old->rotate_outer_row_labels,
1057 .show_grid_lines = old->show_grid_lines,
1058 .show_title = old->show_title,
1059 .show_caption = old->show_caption,
1060 .current_layer = (old->current_layer
1061 ? xmemdup (old->current_layer,
1062 old->axes[PIVOT_AXIS_LAYER].n_dimensions
1063 * sizeof *new->current_layer)
1065 .show_values = old->show_values,
1066 .show_variables = old->show_variables,
1067 .weight_format = old->weight_format,
1070 [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1071 [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1074 .settings = fmt_settings_copy (&old->settings),
1075 .grouping = old->grouping,
1076 .small = old->small,
1078 .command_local = xstrdup_if_nonnull (old->command_local),
1079 .command_c = xstrdup_if_nonnull (old->command_c),
1080 .language = xstrdup_if_nonnull (old->language),
1081 .locale = xstrdup_if_nonnull (old->locale),
1083 .dataset = xstrdup_if_nonnull (old->dataset),
1084 .datafile = xstrdup_if_nonnull (old->datafile),
1087 .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1088 .n_footnotes = old->n_footnotes,
1089 .allocated_footnotes = old->n_footnotes,
1091 .title = pivot_value_clone (old->title),
1092 .subtype = pivot_value_clone (old->subtype),
1093 .corner_text = pivot_value_clone (old->corner_text),
1094 .caption = pivot_value_clone (old->caption),
1095 .notes = xstrdup_if_nonnull (old->notes),
1097 .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1098 .n_dimensions = old->n_dimensions,
1100 .cells = HMAP_INITIALIZER (new->cells),
1103 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1105 struct pivot_axis *new_axis = &new->axes[i];
1106 const struct pivot_axis *old_axis = &old->axes[i];
1108 *new_axis = (struct pivot_axis) {
1109 .dimensions = xmalloc (old_axis->n_dimensions
1110 * sizeof *new_axis->dimensions),
1111 .n_dimensions = old_axis->n_dimensions,
1112 .extent = old_axis->extent,
1113 .label_depth = old_axis->label_depth,
1116 for (size_t i = 0; i < new_axis->n_dimensions; i++)
1117 new_axis->dimensions[i] = new->dimensions[
1118 old_axis->dimensions[i]->top_index];
1121 const struct pivot_cell *old_cell;
1122 size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1123 HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1125 for (size_t i = 0; i < old->n_dimensions; i++)
1126 dindexes[i] = old_cell->idx[i];
1127 struct pivot_cell *new_cell
1128 = pivot_table_insert_cell (new, dindexes);
1129 new_cell->value = pivot_value_clone (old_cell->value);
1136 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1137 If TABLE no longer has any owners, it is freed. */
1139 pivot_table_unref (struct pivot_table *table)
1143 assert (table->ref_cnt > 0);
1144 if (--table->ref_cnt)
1147 free (table->current_layer);
1148 pivot_table_look_unref (table->look);
1150 for (int i = 0; i < TABLE_N_AXES; i++)
1151 pivot_table_sizing_uninit (&table->sizing[i]);
1153 fmt_settings_uninit (&table->settings);
1155 free (table->command_local);
1156 free (table->command_c);
1157 free (table->language);
1158 free (table->locale);
1160 free (table->dataset);
1161 free (table->datafile);
1163 for (size_t i = 0; i < table->n_footnotes; i++)
1164 pivot_footnote_destroy (table->footnotes[i]);
1165 free (table->footnotes);
1167 pivot_value_destroy (table->title);
1168 pivot_value_destroy (table->subtype);
1169 pivot_value_destroy (table->corner_text);
1170 pivot_value_destroy (table->caption);
1171 free (table->notes);
1173 for (size_t i = 0; i < table->n_dimensions; i++)
1174 pivot_dimension_destroy (table->dimensions[i]);
1175 free (table->dimensions);
1177 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1178 free (table->axes[i].dimensions);
1180 struct pivot_cell *cell, *next_cell;
1181 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1183 pivot_table_delete_cell (table, cell);
1185 hmap_destroy (&table->cells);
1190 /* Returns true if TABLE has more than one owner. A pivot table that is shared
1191 among multiple owners must not be modified. */
1193 pivot_table_is_shared (const struct pivot_table *table)
1195 return table->ref_cnt > 1;
1198 /* Swaps axes A and B in TABLE. */
1200 pivot_table_swap_axes (struct pivot_table *table,
1201 enum pivot_axis_type a, enum pivot_axis_type b)
1206 struct pivot_axis tmp = table->axes[a];
1207 table->axes[a] = table->axes[b];
1208 table->axes[b] = tmp;
1210 for (int a = 0; a < PIVOT_N_AXES; a++)
1212 struct pivot_axis *axis = &table->axes[a];
1213 for (size_t d = 0; d < axis->n_dimensions; d++)
1214 axis->dimensions[d]->axis_type = a;
1217 if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1219 free (table->current_layer);
1220 table->current_layer = xzalloc (
1221 table->axes[PIVOT_AXIS_LAYER].n_dimensions
1222 * sizeof *table->current_layer);
1226 /* Swaps the row and column axes in TABLE. */
1228 pivot_table_transpose (struct pivot_table *table)
1230 pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1234 pivot_table_update_axes (struct pivot_table *table)
1236 for (int a = 0; a < PIVOT_N_AXES; a++)
1238 struct pivot_axis *axis = &table->axes[a];
1240 for (size_t d = 0; d < axis->n_dimensions; d++)
1242 struct pivot_dimension *dim = axis->dimensions[d];
1249 /* Moves DIM from its current location in TABLE to POS within AXIS. POS of 0
1250 is the innermost dimension, 1 is the next one out, and so on. */
1252 pivot_table_move_dimension (struct pivot_table *table,
1253 struct pivot_dimension *dim,
1254 enum pivot_axis_type axis, size_t pos)
1256 assert (dim->table == table);
1258 struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1259 struct pivot_axis *new_axis = &table->axes[axis];
1260 pos = MIN (pos, new_axis->n_dimensions);
1262 if (old_axis == new_axis && pos == dim->level)
1269 /* Update the current layer, if necessary. If we're moving within the layer
1270 axis, preserve the current layer. */
1271 if (dim->axis_type == PIVOT_AXIS_LAYER)
1273 if (axis == PIVOT_AXIS_LAYER)
1275 /* Rearranging the layer axis. */
1276 move_element (table->current_layer, old_axis->n_dimensions,
1277 sizeof *table->current_layer,
1282 /* A layer is becoming a row or column. */
1283 remove_element (table->current_layer, old_axis->n_dimensions,
1284 sizeof *table->current_layer, dim->level);
1287 else if (axis == PIVOT_AXIS_LAYER)
1289 /* A row or column is becoming a layer. */
1290 table->current_layer = xrealloc (
1291 table->current_layer,
1292 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1293 insert_element (table->current_layer, new_axis->n_dimensions,
1294 sizeof *table->current_layer, pos);
1295 table->current_layer[pos] = 0;
1298 /* Remove DIM from its current axis. */
1299 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1300 sizeof *old_axis->dimensions, dim->level);
1301 old_axis->n_dimensions--;
1303 /* Insert DIM into its new axis. */
1304 new_axis->dimensions = xrealloc (
1305 new_axis->dimensions,
1306 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1307 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1308 sizeof *new_axis->dimensions, pos);
1309 new_axis->dimensions[pos] = dim;
1310 new_axis->n_dimensions++;
1312 pivot_table_update_axes (table);
1316 const struct pivot_table_look *
1317 pivot_table_get_look (const struct pivot_table *table)
1323 pivot_table_set_look (struct pivot_table *table,
1324 const struct pivot_table_look *look)
1326 pivot_table_look_unref (table->look);
1327 table->look = pivot_table_look_ref (look);
1330 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1331 WV, which should be the weight variable for the dictionary whose data or
1332 statistics are being put into TABLE.
1334 This has no effect if WV is NULL. */
1336 pivot_table_set_weight_var (struct pivot_table *table,
1337 const struct variable *wv)
1340 pivot_table_set_weight_format (table, var_get_print_format (wv));
1343 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1344 format for the dictionary whose data or statistics are being put into TABLE.
1346 This has no effect if WFMT is NULL. */
1348 pivot_table_set_weight_format (struct pivot_table *table,
1349 const struct fmt_spec *wfmt)
1352 table->weight_format = *wfmt;
1355 /* Returns true if TABLE has no cells, false otherwise. */
1357 pivot_table_is_empty (const struct pivot_table *table)
1359 return hmap_is_empty (&table->cells);
1363 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1365 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1369 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1371 for (size_t i = 0; i < n; i++)
1378 static struct pivot_cell *
1379 pivot_table_lookup_cell__ (const struct pivot_table *table,
1380 const size_t *dindexes, unsigned int hash)
1382 struct pivot_cell *cell;
1383 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1385 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1390 static struct pivot_cell *
1391 pivot_cell_allocate (size_t n_idx)
1393 struct pivot_cell *cell UNUSED;
1394 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1397 static struct pivot_cell *
1398 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1400 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1401 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1404 cell = pivot_cell_allocate (table->n_dimensions);
1405 for (size_t i = 0; i < table->n_dimensions; i++)
1406 cell->idx[i] = dindexes[i];
1408 hmap_insert (&table->cells, &cell->hmap_node, hash);
1413 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1414 DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of
1417 If VALUE is a numeric value without a specified format, this function checks
1418 each of the categories designated by DINDEXES[] and takes the format from
1419 the first category with a result class. If none has a result class, uses
1420 the overall default numeric format. */
1422 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1423 struct pivot_value *value)
1425 assert (n == table->n_dimensions);
1426 for (size_t i = 0; i < n; i++)
1427 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1429 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1431 for (size_t i = 0; i < table->n_dimensions; i++)
1433 const struct pivot_dimension *d = table->dimensions[i];
1434 if (dindexes[i] < d->n_leaves)
1436 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1439 value->numeric.format = c->format;
1440 value->numeric.honor_small = c->honor_small;
1445 value->numeric.format = *settings_get_format ();
1446 value->numeric.honor_small = true;
1451 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1452 pivot_value_destroy (cell->value);
1453 cell->value = value;
1456 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1457 dimension. Takes ownership of VALUE. */
1459 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1460 struct pivot_value *value)
1462 size_t dindexes[] = { idx1 };
1463 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1466 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1467 dimensions. Takes ownership of VALUE. */
1469 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1470 struct pivot_value *value)
1472 size_t dindexes[] = { idx1, idx2 };
1473 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1476 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1477 have 3 dimensions. Takes ownership of VALUE. */
1479 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1480 size_t idx3, struct pivot_value *value)
1482 size_t dindexes[] = { idx1, idx2, idx3 };
1483 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1486 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1487 must have 4 dimensions. Takes ownership of VALUE. */
1489 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1490 size_t idx3, size_t idx4, struct pivot_value *value)
1492 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1493 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1496 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1497 automatically assigned marker.
1499 The footnote will only appear in output if it is referenced. Use
1500 pivot_value_add_footnote() to add a reference to the footnote. */
1501 struct pivot_footnote *
1502 pivot_table_create_footnote (struct pivot_table *table,
1503 struct pivot_value *content)
1505 return pivot_table_create_footnote__ (table, table->n_footnotes,
1509 static struct pivot_value *
1510 pivot_make_default_footnote_marker (int idx, bool show_numeric_markers)
1512 char text[INT_BUFSIZE_BOUND (size_t)];
1513 if (show_numeric_markers)
1514 snprintf (text, sizeof text, "%d", idx + 1);
1516 str_format_26adic (idx + 1, false, text, sizeof text);
1517 return pivot_value_new_user_text (text, -1);
1520 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1521 all lower indexes as a side effect). If MARKER is nonnull, sets the
1522 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1523 struct pivot_footnote *
1524 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1525 struct pivot_value *marker,
1526 struct pivot_value *content)
1528 if (idx >= table->n_footnotes)
1530 while (idx >= table->allocated_footnotes)
1531 table->footnotes = x2nrealloc (table->footnotes,
1532 &table->allocated_footnotes,
1533 sizeof *table->footnotes);
1534 while (idx >= table->n_footnotes)
1536 struct pivot_footnote *f = xmalloc (sizeof *f);
1537 f->idx = table->n_footnotes;
1538 f->marker = pivot_make_default_footnote_marker (
1539 f->idx, table->look->show_numeric_markers);
1543 table->footnotes[table->n_footnotes++] = f;
1547 struct pivot_footnote *f = table->footnotes[idx];
1550 pivot_value_destroy (f->marker);
1555 pivot_value_destroy (f->content);
1556 f->content = content;
1561 /* Frees the data owned by F. */
1563 pivot_footnote_destroy (struct pivot_footnote *f)
1567 pivot_value_destroy (f->content);
1568 pivot_value_destroy (f->marker);
1573 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1574 indexes for each dimension in TABLE in DINDEXES[]. */
1576 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1577 const size_t *pindexes[PIVOT_N_AXES],
1578 size_t dindexes[/* table->n_dimensions */])
1580 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1582 const struct pivot_axis *axis = &table->axes[i];
1584 for (size_t j = 0; j < axis->n_dimensions; j++)
1586 const struct pivot_dimension *d = axis->dimensions[j];
1587 dindexes[d->top_index]
1588 = d->presentation_leaves[pindexes[i][j]]->data_index;
1594 pivot_table_enumerate_axis (const struct pivot_table *table,
1595 enum pivot_axis_type axis_type,
1596 const size_t *layer_indexes, bool omit_empty,
1599 const struct pivot_axis *axis = &table->axes[axis_type];
1600 if (!axis->n_dimensions)
1602 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1604 enumeration[1] = SIZE_MAX;
1609 else if (!axis->extent)
1611 size_t *enumeration = xmalloc (sizeof *enumeration);
1612 *enumeration = SIZE_MAX;
1618 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1619 axis->n_dimensions), 1),
1620 sizeof *enumeration);
1621 size_t *p = enumeration;
1622 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1624 size_t *axis_indexes;
1625 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1629 enum pivot_axis_type axis2_type
1630 = pivot_axis_type_transpose (axis_type);
1632 size_t *axis2_indexes;
1633 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1635 const size_t *pindexes[PIVOT_N_AXES];
1636 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1637 pindexes[axis_type] = axis_indexes;
1638 pindexes[axis2_type] = axis2_indexes;
1639 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1640 if (pivot_table_get (table, dindexes))
1646 free (axis2_indexes);
1649 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1650 p += axis->n_dimensions;
1652 if (omit_empty && p == enumeration)
1654 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1656 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1657 p += axis->n_dimensions;
1662 *n = (p - enumeration) / axis->n_dimensions;
1668 static struct pivot_cell *
1669 pivot_table_lookup_cell (const struct pivot_table *table,
1670 const size_t *dindexes)
1672 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1673 return pivot_table_lookup_cell__ (table, dindexes, hash);
1676 const struct pivot_value *
1677 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1679 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1680 return cell ? cell->value : NULL;
1683 struct pivot_value *
1684 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1686 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1688 cell->value = pivot_value_new_user_text ("", -1);
1693 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1695 hmap_delete (&table->cells, &cell->hmap_node);
1696 pivot_value_destroy (cell->value);
1701 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1703 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1706 pivot_table_delete_cell (table, cell);
1714 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1716 if (pivot_category_is_group (category) && category->n_subs)
1717 for (size_t i = 0; i < category->n_subs; i++)
1718 distribute_extra_depth (category->subs[i], extra_depth);
1720 category->extra_depth += extra_depth;
1724 pivot_category_assign_label_depth (struct pivot_category *category,
1725 bool dimension_labels_in_corner)
1727 category->extra_depth = 0;
1729 if (pivot_category_is_group (category))
1732 for (size_t i = 0; i < category->n_subs; i++)
1734 pivot_category_assign_label_depth (category->subs[i], false);
1735 depth = MAX (depth, category->subs[i]->label_depth);
1738 for (size_t i = 0; i < category->n_subs; i++)
1740 struct pivot_category *sub = category->subs[i];
1742 size_t extra_depth = depth - sub->label_depth;
1744 distribute_extra_depth (sub, extra_depth);
1746 sub->label_depth = depth;
1749 category->show_label_in_corner = (category->show_label
1750 && dimension_labels_in_corner);
1751 category->label_depth
1752 = (category->show_label && !category->show_label_in_corner
1753 ? depth + 1 : depth);
1756 category->label_depth = 1;
1760 pivot_axis_assign_label_depth (struct pivot_table *table,
1761 enum pivot_axis_type axis_type,
1762 bool dimension_labels_in_corner)
1764 struct pivot_axis *axis = &table->axes[axis_type];
1765 bool any_label_shown_in_corner = false;
1766 axis->label_depth = 0;
1768 for (size_t i = 0; i < axis->n_dimensions; i++)
1770 struct pivot_dimension *d = axis->dimensions[i];
1771 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1772 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1773 axis->label_depth += d->label_depth;
1774 axis->extent *= d->n_leaves;
1776 if (d->root->show_label_in_corner)
1777 any_label_shown_in_corner = true;
1779 return any_label_shown_in_corner;
1783 pivot_table_assign_label_depth (struct pivot_table *table)
1785 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1786 if (pivot_axis_assign_label_depth (
1787 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1788 && !table->corner_text))
1789 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1790 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1791 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1795 indent (int indentation)
1797 for (int i = 0; i < indentation * 2; i++)
1802 pivot_value_dump (const struct pivot_value *value,
1803 const struct pivot_table *pt)
1805 char *s = pivot_value_to_string (value, pt);
1811 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1812 const struct pivot_table *pt, int indentation)
1816 indent (indentation);
1817 printf ("%s: ", name);
1818 pivot_value_dump (value, pt);
1824 pivot_table_dump_string (const char *string, const char *name, int indentation)
1828 indent (indentation);
1829 printf ("%s: %s\n", name, string);
1834 pivot_category_dump (const struct pivot_category *c,
1835 const struct pivot_table *pt, int indentation)
1837 indent (indentation);
1838 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1839 pivot_value_dump (c->name, pt);
1842 if (pivot_category_is_leaf (c))
1843 printf ("data_index=%zu\n", c->data_index);
1846 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1849 for (size_t i = 0; i < c->n_subs; i++)
1850 pivot_category_dump (c->subs[i], pt, indentation + 1);
1855 pivot_dimension_dump (const struct pivot_dimension *d,
1856 const struct pivot_table *pt, int indentation)
1858 indent (indentation);
1859 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1860 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1862 pivot_category_dump (d->root, pt, indentation + 1);
1866 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1869 indent (indentation);
1870 printf ("%s: ", pivot_area_to_string (area));
1871 font_style_dump (&a->font_style);
1873 cell_style_dump (&a->cell_style);
1878 table_border_style_dump (enum pivot_border border,
1879 const struct table_border_style *b, int indentation)
1881 indent (indentation);
1882 printf ("%s: %s ", pivot_border_to_string (border),
1883 table_stroke_to_string (b->stroke));
1884 cell_color_dump (&b->color);
1889 compose_headings (const struct pivot_table *pt,
1890 const struct pivot_axis *axis,
1891 const size_t *column_enumeration)
1893 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1896 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1897 for (size_t i = 0; i < axis->label_depth; i++)
1898 headings[i] = xcalloc (axis->extent, sizeof **headings);
1900 const size_t *indexes;
1902 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1904 int row = axis->label_depth - 1;
1905 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1907 const struct pivot_dimension *d = axis->dimensions[dim_index];
1908 if (d->hide_all_labels)
1910 for (const struct pivot_category *c
1911 = d->presentation_leaves[indexes[dim_index]];
1915 if (pivot_category_is_leaf (c) || (c->show_label
1916 && !c->show_label_in_corner))
1918 headings[row][column] = pivot_value_to_string (c->name, pt);
1919 if (!*headings[row][column])
1920 headings[row][column] = xstrdup ("<blank>");
1932 free_headings (const struct pivot_axis *axis, char ***headings)
1934 for (size_t i = 0; i < axis->label_depth; i++)
1936 for (size_t j = 0; j < axis->extent; j++)
1937 free (headings[i][j]);
1944 pivot_table_sizing_dump (const char *name,
1945 const int width_ranges[2],
1946 const struct pivot_table_sizing *s,
1949 indent (indentation);
1950 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
1953 indent (indentation + 1);
1954 printf ("%s widths:", name);
1955 for (size_t i = 0; i < s->n_widths; i++)
1956 printf (" %d", s->widths[i]);
1961 indent (indentation + 1);
1962 printf ("break after %ss:", name);
1963 for (size_t i = 0; i < s->n_breaks; i++)
1964 printf (" %zu", s->breaks[i]);
1969 indent (indentation + 1);
1970 printf ("keep %ss together:", name);
1971 for (size_t i = 0; i < s->n_keeps; i++)
1972 printf (" [%zu,%zu]",
1974 s->keeps[i].ofs + s->keeps[i].n - 1);
1980 pivot_table_dump (const struct pivot_table *table, int indentation)
1985 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
1987 pivot_table_dump_value (table->title, "title", table, indentation);
1988 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
1989 pivot_table_dump_string (table->command_c, "command", indentation);
1990 pivot_table_dump_string (table->dataset, "dataset", indentation);
1991 pivot_table_dump_string (table->datafile, "datafile", indentation);
1992 pivot_table_dump_string (table->notes, "notes", indentation);
1993 pivot_table_dump_string (table->look->name, "table-look", indentation);
1996 indent (indentation);
1998 struct tm *tm = localtime (&table->date);
1999 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2000 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2004 indent (indentation);
2005 printf ("sizing:\n");
2006 pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2007 &table->sizing[TABLE_HORZ], indentation + 1);
2008 pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2009 &table->sizing[TABLE_VERT], indentation + 1);
2011 indent (indentation);
2012 printf ("areas:\n");
2013 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2014 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2016 indent (indentation);
2017 printf ("borders:\n");
2018 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2019 table_border_style_dump (border, &table->look->borders[border],
2022 for (size_t i = 0; i < table->n_dimensions; i++)
2023 pivot_dimension_dump (table->dimensions[i], table, indentation);
2025 /* Presentation and data indexes. */
2026 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2028 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2029 if (layer_axis->n_dimensions)
2031 indent (indentation);
2032 printf ("current layer:");
2034 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2036 const struct pivot_dimension *d = layer_axis->dimensions[i];
2037 char *name = pivot_value_to_string (d->root->name, table);
2038 char *value = pivot_value_to_string (
2039 d->data_leaves[table->current_layer[i]]->name, table);
2040 printf (" %s=%s", name, value);
2048 size_t *layer_indexes;
2049 size_t layer_iteration = 0;
2050 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2052 indent (indentation);
2053 printf ("layer %zu:", layer_iteration++);
2055 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2056 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2058 const struct pivot_dimension *d = layer_axis->dimensions[i];
2060 fputs (i == 0 ? " " : ", ", stdout);
2061 pivot_value_dump (d->root->name, table);
2062 fputs (" =", stdout);
2064 struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
2066 for (const struct pivot_category *c
2067 = d->presentation_leaves[layer_indexes[i]];
2071 if (pivot_category_is_leaf (c) || c->show_label)
2072 names[n_names++] = c->name;
2075 for (size_t i = n_names; i-- > 0;)
2078 pivot_value_dump (names[i], table);
2084 size_t *column_enumeration = pivot_table_enumerate_axis (
2085 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2086 size_t *row_enumeration = pivot_table_enumerate_axis (
2087 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2089 char ***column_headings = compose_headings (
2090 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2091 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2093 indent (indentation + 1);
2094 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2097 fputs ("; ", stdout);
2098 if (column_headings[y][x])
2099 fputs (column_headings[y][x], stdout);
2103 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2105 indent (indentation + 1);
2106 printf ("-----------------------------------------------\n");
2108 char ***row_headings = compose_headings (
2109 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2112 const size_t *pindexes[PIVOT_N_AXES]
2113 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2114 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2115 &table->axes[PIVOT_AXIS_ROW])
2117 indent (indentation + 1);
2120 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2123 fputs ("; ", stdout);
2124 if (row_headings[y][x])
2125 fputs (row_headings[y][x], stdout);
2131 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2133 &table->axes[PIVOT_AXIS_COLUMN])
2138 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2139 const struct pivot_value *value = pivot_table_get (
2142 pivot_value_dump (value, table);
2149 free (column_enumeration);
2150 free (row_enumeration);
2151 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2154 pivot_table_dump_value (table->caption, "caption", table, indentation);
2156 for (size_t i = 0; i < table->n_footnotes; i++)
2158 const struct pivot_footnote *f = table->footnotes[i];
2159 indent (indentation);
2162 pivot_value_dump (f->marker, table);
2164 printf ("%zu", f->idx);
2166 pivot_value_dump (f->content, table);
2174 consume_int (const char *p, size_t *n)
2177 while (c_isdigit (*p))
2178 *n = *n * 10 + (*p++ - '0');
2183 pivot_format_inner_template (struct string *out, const char *template,
2185 struct pivot_value **values, size_t n_values,
2186 const struct pivot_table *pt)
2188 size_t args_consumed = 0;
2189 while (*template && *template != ':')
2191 if (*template == '\\' && template[1])
2193 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2196 else if (*template == escape)
2199 template = consume_int (template + 1, &index);
2200 if (index >= 1 && index <= n_values)
2202 pivot_value_format (values[index - 1], pt, out);
2203 args_consumed = MAX (args_consumed, index);
2207 ds_put_byte (out, *template++);
2209 return args_consumed;
2213 pivot_extract_inner_template (const char *template, const char **p)
2219 if (*template == '\\' && template[1] != '\0')
2221 else if (*template == ':')
2222 return template + 1;
2223 else if (*template == '\0')
2231 pivot_format_template (struct string *out, const char *template,
2232 const struct pivot_argument *args, size_t n_args,
2233 const struct pivot_table *pt)
2237 if (*template == '\\' && template[1] != '\0')
2239 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2242 else if (*template == '^')
2245 template = consume_int (template + 1, &index);
2246 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2247 pivot_value_format (args[index - 1].values[0], pt, out);
2249 else if (*template == '[')
2251 const char *tmpl[2];
2252 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2253 template = pivot_extract_inner_template (template, &tmpl[1]);
2254 template += *template == ']';
2257 template = consume_int (template, &index);
2258 if (index < 1 || index > n_args)
2261 const struct pivot_argument *arg = &args[index - 1];
2262 size_t left = arg->n;
2265 struct pivot_value **values = arg->values + (arg->n - left);
2266 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2267 char escape = "%^"[tmpl_idx];
2268 size_t used = pivot_format_inner_template (
2269 out, tmpl[tmpl_idx], escape, values, left, pt);
2270 if (!used || used > left)
2276 ds_put_byte (out, *template++);
2280 static enum settings_value_show
2281 interpret_show (enum settings_value_show global_show,
2282 enum settings_value_show table_show,
2283 enum settings_value_show value_show,
2286 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2287 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2288 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2292 /* Appends a text representation of the body of VALUE to OUT. Settings on
2293 PT control whether variable and value labels are included.
2295 The "body" omits subscripts and superscripts and footnotes.
2297 Returns true if OUT is a number (or a number plus a value label), false
2300 pivot_value_format_body (const struct pivot_value *value,
2301 const struct pivot_table *pt,
2304 enum settings_value_show show;
2305 bool numeric = false;
2307 switch (value->type)
2309 case PIVOT_VALUE_NUMERIC:
2310 show = interpret_show (settings_get_show_values (),
2312 value->numeric.show,
2313 value->numeric.value_label != NULL);
2314 if (show & SETTINGS_VALUE_SHOW_VALUE)
2316 const struct fmt_spec *f = &value->numeric.format;
2317 const struct fmt_spec *format
2319 && value->numeric.honor_small
2320 && value->numeric.x != 0
2321 && fabs (value->numeric.x) < pt->small
2322 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2325 char *s = data_out (&(union value) { .f = value->numeric.x },
2326 "UTF-8", format, &pt->settings);
2327 ds_put_cstr (out, s + strspn (s, " "));
2330 if (show & SETTINGS_VALUE_SHOW_LABEL)
2332 if (show & SETTINGS_VALUE_SHOW_VALUE)
2333 ds_put_byte (out, ' ');
2334 ds_put_cstr (out, value->numeric.value_label);
2336 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2339 case PIVOT_VALUE_STRING:
2340 show = interpret_show (settings_get_show_values (),
2343 value->string.value_label != NULL);
2344 if (show & SETTINGS_VALUE_SHOW_VALUE)
2346 if (value->string.hex)
2348 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2350 ds_put_format (out, "%02X", *p);
2353 ds_put_cstr (out, value->string.s);
2355 if (show & SETTINGS_VALUE_SHOW_LABEL)
2357 if (show & SETTINGS_VALUE_SHOW_VALUE)
2358 ds_put_byte (out, ' ');
2359 ds_put_cstr (out, value->string.value_label);
2363 case PIVOT_VALUE_VARIABLE:
2364 show = interpret_show (settings_get_show_variables (),
2366 value->variable.show,
2367 value->variable.var_label != NULL);
2368 if (show & SETTINGS_VALUE_SHOW_VALUE)
2369 ds_put_cstr (out, value->variable.var_name);
2370 if (show & SETTINGS_VALUE_SHOW_LABEL)
2372 if (show & SETTINGS_VALUE_SHOW_VALUE)
2373 ds_put_byte (out, ' ');
2374 ds_put_cstr (out, value->variable.var_label);
2378 case PIVOT_VALUE_TEXT:
2379 ds_put_cstr (out, value->text.local);
2382 case PIVOT_VALUE_TEMPLATE:
2383 pivot_format_template (out, value->template.local, value->template.args,
2384 value->template.n_args, pt);
2391 /* Appends a text representation of VALUE to OUT. Settings on
2392 PT control whether variable and value labels are included.
2394 Subscripts and footnotes are included. */
2396 pivot_value_format (const struct pivot_value *value,
2397 const struct pivot_table *pt,
2400 pivot_value_format_body (value, pt, out);
2402 if (value->n_subscripts)
2404 for (size_t i = 0; i < value->n_subscripts; i++)
2405 ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
2408 for (size_t i = 0; i < value->n_footnotes; i++)
2410 ds_put_byte (out, '[');
2412 size_t idx = value->footnote_indexes[i];
2413 const struct pivot_footnote *f = pt->footnotes[idx];
2414 pivot_value_format (f->marker, pt, out);
2416 ds_put_byte (out, ']');
2420 /* Returns a text representation of VALUE. The caller must free the string,
2423 pivot_value_to_string (const struct pivot_value *value,
2424 const struct pivot_table *pt)
2426 struct string s = DS_EMPTY_INITIALIZER;
2427 pivot_value_format (value, pt, &s);
2428 return ds_steal_cstr (&s);
2432 pivot_value_to_string_defaults (const struct pivot_value *value)
2434 static const struct pivot_table pt = {
2435 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2436 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2438 return pivot_value_to_string (value, &pt);
2441 struct pivot_value *
2442 pivot_value_clone (const struct pivot_value *old)
2447 struct pivot_value *new = xmemdup (old, sizeof *new);
2448 if (old->font_style)
2450 new->font_style = xmalloc (sizeof *new->font_style);
2451 font_style_copy (NULL, new->font_style, old->font_style);
2453 if (old->cell_style)
2454 new->cell_style = xmemdup (old->cell_style, sizeof *new->cell_style);
2455 if (old->n_subscripts)
2457 new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
2458 for (size_t i = 0; i < old->n_subscripts; i++)
2459 new->subscripts[i] = xstrdup (old->subscripts[i]);
2461 if (old->n_footnotes)
2462 new->footnote_indexes = xmemdup (
2463 old->footnote_indexes, old->n_footnotes * sizeof *new->footnote_indexes);
2467 case PIVOT_VALUE_NUMERIC:
2468 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2469 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2472 case PIVOT_VALUE_STRING:
2473 new->string.s = xstrdup (new->string.s);
2474 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2475 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2478 case PIVOT_VALUE_VARIABLE:
2479 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2480 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2483 case PIVOT_VALUE_TEXT:
2484 new->text.local = xstrdup (old->text.local);
2485 new->text.c = (old->text.c == old->text.local ? new->text.local
2486 : xstrdup_if_nonnull (old->text.c));
2487 new->text.id = (old->text.id == old->text.local ? new->text.local
2488 : old->text.id == old->text.c ? new->text.c
2489 : xstrdup_if_nonnull (old->text.id));
2492 case PIVOT_VALUE_TEMPLATE:
2493 new->template.local = xstrdup (old->template.local);
2494 new->template.id = (old->template.id == old->template.local
2495 ? new->template.local
2496 : xstrdup (old->template.id));
2497 new->template.args = xmalloc (new->template.n_args
2498 * sizeof *new->template.args);
2499 for (size_t i = 0; i < old->template.n_args; i++)
2500 pivot_argument_copy (&new->template.args[i],
2501 &old->template.args[i]);
2510 /* Frees the data owned by V. */
2512 pivot_value_destroy (struct pivot_value *value)
2516 font_style_uninit (value->font_style);
2517 free (value->font_style);
2518 free (value->cell_style);
2519 free (value->footnote_indexes);
2521 for (size_t i = 0; i < value->n_subscripts; i++)
2522 free (value->subscripts[i]);
2523 free (value->subscripts);
2525 switch (value->type)
2527 case PIVOT_VALUE_NUMERIC:
2528 free (value->numeric.var_name);
2529 free (value->numeric.value_label);
2532 case PIVOT_VALUE_STRING:
2533 free (value->string.s);
2534 free (value->string.var_name);
2535 free (value->string.value_label);
2538 case PIVOT_VALUE_VARIABLE:
2539 free (value->variable.var_name);
2540 free (value->variable.var_label);
2543 case PIVOT_VALUE_TEXT:
2544 free (value->text.local);
2545 if (value->text.c != value->text.local)
2546 free (value->text.c);
2547 if (value->text.id != value->text.local
2548 && value->text.id != value->text.c)
2549 free (value->text.id);
2552 case PIVOT_VALUE_TEMPLATE:
2553 free (value->template.local);
2554 if (value->template.id != value->template.local)
2555 free (value->template.id);
2556 for (size_t i = 0; i < value->template.n_args; i++)
2557 pivot_argument_uninit (&value->template.args[i]);
2558 free (value->template.args);
2568 /* Sets AREA to the style to use for VALUE, with defaults coming from
2569 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2571 pivot_value_get_style (struct pivot_value *value,
2572 const struct font_style *base_font_style,
2573 const struct cell_style *base_cell_style,
2574 struct table_area_style *area)
2576 font_style_copy (NULL, &area->font_style, (value->font_style
2578 : base_font_style));
2579 area->cell_style = *(value->cell_style
2584 /* Copies AREA into VALUE's style. */
2586 pivot_value_set_style (struct pivot_value *value,
2587 const struct table_area_style *area)
2589 pivot_value_set_font_style (value, &area->font_style);
2590 pivot_value_set_cell_style (value, &area->cell_style);
2594 pivot_value_set_font_style (struct pivot_value *value,
2595 const struct font_style *font_style)
2597 if (value->font_style)
2598 font_style_uninit (value->font_style);
2600 value->font_style = xmalloc (sizeof *value->font_style);
2601 font_style_copy (NULL, value->font_style, font_style);
2605 pivot_value_set_cell_style (struct pivot_value *value,
2606 const struct cell_style *cell_style)
2608 if (!value->cell_style)
2609 value->cell_style = xmalloc (sizeof *value->cell_style);
2610 *value->cell_style = *cell_style;
2614 pivot_argument_copy (struct pivot_argument *dst,
2615 const struct pivot_argument *src)
2617 *dst = (struct pivot_argument) {
2619 .values = xmalloc (src->n * sizeof *dst->values),
2622 for (size_t i = 0; i < src->n; i++)
2623 dst->values[i] = pivot_value_clone (src->values[i]);
2626 /* Frees the data owned by ARG (but not ARG itself). */
2628 pivot_argument_uninit (struct pivot_argument *arg)
2632 for (size_t i = 0; i < arg->n; i++)
2633 pivot_value_destroy (arg->values[i]);
2638 /* Creates and returns a new pivot_value whose contents is the null-terminated
2639 string TEXT. Takes ownership of TEXT.
2641 This function is for text strings provided by the user (with the exception
2642 that pivot_value_new_variable() should be used for variable names). For
2643 strings that are part of the PSPP user interface, such as names of
2644 procedures, statistics, annotations, error messages, etc., use
2645 pivot_value_new_text(). */
2646 struct pivot_value *
2647 pivot_value_new_user_text_nocopy (char *text)
2649 struct pivot_value *value = xmalloc (sizeof *value);
2650 *value = (struct pivot_value) {
2651 .type = PIVOT_VALUE_TEXT,
2656 .user_provided = true,
2662 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2663 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2666 This function is for text strings provided by the user (with the exception
2667 that pivot_value_new_variable() should be used for variable names). For
2668 strings that are part of the PSPP user interface, such as names of
2669 procedures, statistics, annotations, error messages, etc., use
2670 pivot_value_new_text().j
2672 The caller retains ownership of TEXT.*/
2673 struct pivot_value *
2674 pivot_value_new_user_text (const char *text, size_t length)
2676 return pivot_value_new_user_text_nocopy (
2677 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2680 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2681 a translatable string, but not actually translated yet, e.g. enclosed in
2682 N_(). This function is for text strings that are part of the PSPP user
2683 interface, such as names of procedures, statistics, annotations, error
2684 messages, etc. For strings that come from the user, use
2685 pivot_value_new_user_text(). */
2686 struct pivot_value *
2687 pivot_value_new_text (const char *text)
2689 char *c = xstrdup (text);
2690 char *local = xstrdup (gettext (c));
2692 struct pivot_value *value = xmalloc (sizeof *value);
2693 *value = (struct pivot_value) {
2694 .type = PIVOT_VALUE_TEXT,
2699 .user_provided = false,
2705 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2707 struct pivot_value * PRINTF_FORMAT (1, 2)
2708 pivot_value_new_text_format (const char *format, ...)
2711 va_start (args, format);
2712 char *c = xvasprintf (format, args);
2715 va_start (args, format);
2716 char *local = xvasprintf (gettext (format), args);
2719 struct pivot_value *value = xmalloc (sizeof *value);
2720 *value = (struct pivot_value) {
2721 .type = PIVOT_VALUE_TEXT,
2726 .user_provided = false,
2732 /* Returns a new pivot_value that represents X.
2734 The format to use for X is unspecified. Usually the easiest way to specify
2735 a format is through assigning a result class to one of the categories that
2736 the pivot_value will end up in. If that is not suitable, then the caller
2737 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2738 struct pivot_value *
2739 pivot_value_new_number (double x)
2741 struct pivot_value *value = xmalloc (sizeof *value);
2742 *value = (struct pivot_value) {
2743 .type = PIVOT_VALUE_NUMERIC,
2744 .numeric = { .x = x, },
2749 /* Returns a new pivot_value that represents X, formatted as an integer. */
2750 struct pivot_value *
2751 pivot_value_new_integer (double x)
2753 struct pivot_value *value = pivot_value_new_number (x);
2754 value->numeric.format = (struct fmt_spec) { FMT_F, 40, 0 };
2758 /* Returns a new pivot_value that represents VALUE, formatted as for
2760 struct pivot_value *
2761 pivot_value_new_var_value (const struct variable *variable,
2762 const union value *value)
2764 struct pivot_value *pv = pivot_value_new_value (
2765 value, var_get_width (variable), var_get_print_format (variable),
2766 var_get_encoding (variable));
2768 char *var_name = xstrdup (var_get_name (variable));
2769 if (var_is_alpha (variable))
2770 pv->string.var_name = var_name;
2772 pv->numeric.var_name = var_name;
2774 const char *label = var_lookup_value_label (variable, value);
2777 if (var_is_alpha (variable))
2778 pv->string.value_label = xstrdup (label);
2780 pv->numeric.value_label = xstrdup (label);
2786 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2787 formatted with FORMAT. For a string value, ENCODING must be its character
2789 struct pivot_value *
2790 pivot_value_new_value (const union value *value, int width,
2791 const struct fmt_spec *format, const char *encoding)
2793 struct pivot_value *pv = xzalloc (sizeof *pv);
2796 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2798 size_t n = strlen (s);
2799 while (n > 0 && s[n - 1] == ' ')
2802 pv->type = PIVOT_VALUE_STRING;
2804 pv->string.hex = format->type == FMT_AHEX;
2808 pv->type = PIVOT_VALUE_NUMERIC;
2809 pv->numeric.x = value->f;
2810 pv->numeric.format = *format;
2816 /* Returns a new pivot_value for VARIABLE. */
2817 struct pivot_value *
2818 pivot_value_new_variable (const struct variable *variable)
2820 struct pivot_value *value = xmalloc (sizeof *value);
2821 *value = (struct pivot_value) {
2822 .type = PIVOT_VALUE_VARIABLE,
2824 .var_name = xstrdup (var_get_name (variable)),
2825 .var_label = xstrdup_if_nonempty (var_get_label (variable)),
2831 /* Attaches a reference to FOOTNOTE to V. */
2833 pivot_value_add_footnote (struct pivot_value *v,
2834 const struct pivot_footnote *footnote)
2836 /* Some legacy tables include numerous duplicate footnotes. Suppress
2838 for (size_t i = 0; i < v->n_footnotes; i++)
2839 if (v->footnote_indexes[i] == footnote->idx)
2842 v->footnote_indexes = xrealloc (
2843 v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
2844 v->footnote_indexes[v->n_footnotes++] = footnote->idx;
2847 /* If VALUE is a numeric value, and RC is a result class such as
2848 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2850 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2853 if (value->type == PIVOT_VALUE_NUMERIC)
2854 pivot_table_use_rc (table, rc,
2855 &value->numeric.format, &value->numeric.honor_small);