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)
1268 /* Update the current layer, if necessary. If we're moving within the layer
1269 axis, preserve the current layer. */
1270 if (dim->axis_type == PIVOT_AXIS_LAYER)
1272 if (axis == PIVOT_AXIS_LAYER)
1274 /* Rearranging the layer axis. */
1275 move_element (table->current_layer, old_axis->n_dimensions,
1276 sizeof *table->current_layer,
1281 /* A layer is becoming a row or column. */
1282 remove_element (table->current_layer, old_axis->n_dimensions,
1283 sizeof *table->current_layer, dim->level);
1286 else if (axis == PIVOT_AXIS_LAYER)
1288 /* A row or column is becoming a layer. */
1289 table->current_layer = xrealloc (
1290 table->current_layer,
1291 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1292 insert_element (table->current_layer, new_axis->n_dimensions,
1293 sizeof *table->current_layer, pos);
1294 table->current_layer[pos] = 0;
1297 /* Remove DIM from its current axis. */
1298 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1299 sizeof *old_axis->dimensions, dim->level);
1300 old_axis->n_dimensions--;
1302 /* Insert DIM into its new axis. */
1303 new_axis->dimensions = xrealloc (
1304 new_axis->dimensions,
1305 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1306 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1307 sizeof *new_axis->dimensions, pos);
1308 new_axis->dimensions[pos] = dim;
1309 new_axis->n_dimensions++;
1311 pivot_table_update_axes (table);
1315 const struct pivot_table_look *
1316 pivot_table_get_look (const struct pivot_table *table)
1322 pivot_table_set_look (struct pivot_table *table,
1323 const struct pivot_table_look *look)
1325 pivot_table_look_unref (table->look);
1326 table->look = pivot_table_look_ref (look);
1329 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1330 WV, which should be the weight variable for the dictionary whose data or
1331 statistics are being put into TABLE.
1333 This has no effect if WV is NULL. */
1335 pivot_table_set_weight_var (struct pivot_table *table,
1336 const struct variable *wv)
1339 pivot_table_set_weight_format (table, var_get_print_format (wv));
1342 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1343 format for the dictionary whose data or statistics are being put into TABLE.
1345 This has no effect if WFMT is NULL. */
1347 pivot_table_set_weight_format (struct pivot_table *table,
1348 const struct fmt_spec *wfmt)
1351 table->weight_format = *wfmt;
1354 /* Returns true if TABLE has no cells, false otherwise. */
1356 pivot_table_is_empty (const struct pivot_table *table)
1358 return hmap_is_empty (&table->cells);
1362 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1364 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1368 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1370 for (size_t i = 0; i < n; i++)
1377 static struct pivot_cell *
1378 pivot_table_lookup_cell__ (const struct pivot_table *table,
1379 const size_t *dindexes, unsigned int hash)
1381 struct pivot_cell *cell;
1382 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1384 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1389 static struct pivot_cell *
1390 pivot_cell_allocate (size_t n_idx)
1392 struct pivot_cell *cell UNUSED;
1393 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1396 static struct pivot_cell *
1397 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1399 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1400 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1403 cell = pivot_cell_allocate (table->n_dimensions);
1404 for (size_t i = 0; i < table->n_dimensions; i++)
1405 cell->idx[i] = dindexes[i];
1407 hmap_insert (&table->cells, &cell->hmap_node, hash);
1412 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1413 DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of
1416 If VALUE is a numeric value without a specified format, this function checks
1417 each of the categories designated by DINDEXES[] and takes the format from
1418 the first category with a result class. If none has a result class, uses
1419 the overall default numeric format. */
1421 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1422 struct pivot_value *value)
1424 assert (n == table->n_dimensions);
1425 for (size_t i = 0; i < n; i++)
1426 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1428 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1430 for (size_t i = 0; i < table->n_dimensions; i++)
1432 const struct pivot_dimension *d = table->dimensions[i];
1433 if (dindexes[i] < d->n_leaves)
1435 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1438 value->numeric.format = c->format;
1439 value->numeric.honor_small = c->honor_small;
1444 value->numeric.format = *settings_get_format ();
1445 value->numeric.honor_small = true;
1450 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1451 pivot_value_destroy (cell->value);
1452 cell->value = value;
1455 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1456 dimension. Takes ownership of VALUE. */
1458 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1459 struct pivot_value *value)
1461 size_t dindexes[] = { idx1 };
1462 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1465 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1466 dimensions. Takes ownership of VALUE. */
1468 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1469 struct pivot_value *value)
1471 size_t dindexes[] = { idx1, idx2 };
1472 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1475 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1476 have 3 dimensions. Takes ownership of VALUE. */
1478 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1479 size_t idx3, struct pivot_value *value)
1481 size_t dindexes[] = { idx1, idx2, idx3 };
1482 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1485 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1486 must have 4 dimensions. Takes ownership of VALUE. */
1488 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1489 size_t idx3, size_t idx4, struct pivot_value *value)
1491 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1492 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1495 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1496 automatically assigned marker.
1498 The footnote will only appear in output if it is referenced. Use
1499 pivot_value_add_footnote() to add a reference to the footnote. */
1500 struct pivot_footnote *
1501 pivot_table_create_footnote (struct pivot_table *table,
1502 struct pivot_value *content)
1504 return pivot_table_create_footnote__ (table, table->n_footnotes,
1508 static struct pivot_value *
1509 pivot_make_default_footnote_marker (int idx, bool show_numeric_markers)
1511 char text[INT_BUFSIZE_BOUND (size_t)];
1512 if (show_numeric_markers)
1513 snprintf (text, sizeof text, "%d", idx + 1);
1515 str_format_26adic (idx + 1, false, text, sizeof text);
1516 return pivot_value_new_user_text (text, -1);
1519 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1520 all lower indexes as a side effect). If MARKER is nonnull, sets the
1521 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1522 struct pivot_footnote *
1523 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1524 struct pivot_value *marker,
1525 struct pivot_value *content)
1527 if (idx >= table->n_footnotes)
1529 while (idx >= table->allocated_footnotes)
1530 table->footnotes = x2nrealloc (table->footnotes,
1531 &table->allocated_footnotes,
1532 sizeof *table->footnotes);
1533 while (idx >= table->n_footnotes)
1535 struct pivot_footnote *f = xmalloc (sizeof *f);
1536 f->idx = table->n_footnotes;
1537 f->marker = pivot_make_default_footnote_marker (
1538 f->idx, table->look->show_numeric_markers);
1542 table->footnotes[table->n_footnotes++] = f;
1546 struct pivot_footnote *f = table->footnotes[idx];
1549 pivot_value_destroy (f->marker);
1554 pivot_value_destroy (f->content);
1555 f->content = content;
1560 /* Frees the data owned by F. */
1562 pivot_footnote_destroy (struct pivot_footnote *f)
1566 pivot_value_destroy (f->content);
1567 pivot_value_destroy (f->marker);
1572 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1573 indexes for each dimension in TABLE in DINDEXES[]. */
1575 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1576 const size_t *pindexes[PIVOT_N_AXES],
1577 size_t dindexes[/* table->n_dimensions */])
1579 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1581 const struct pivot_axis *axis = &table->axes[i];
1583 for (size_t j = 0; j < axis->n_dimensions; j++)
1585 const struct pivot_dimension *d = axis->dimensions[j];
1586 size_t pindex = pindexes[i][j];
1587 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1593 pivot_table_enumerate_axis (const struct pivot_table *table,
1594 enum pivot_axis_type axis_type,
1595 const size_t *layer_indexes, bool omit_empty,
1598 const struct pivot_axis *axis = &table->axes[axis_type];
1599 if (!axis->n_dimensions)
1601 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1603 enumeration[1] = SIZE_MAX;
1608 else if (!axis->extent)
1610 size_t *enumeration = xmalloc (sizeof *enumeration);
1611 *enumeration = SIZE_MAX;
1617 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1618 axis->n_dimensions), 1),
1619 sizeof *enumeration);
1620 size_t *p = enumeration;
1621 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1623 size_t *axis_indexes;
1624 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1628 enum pivot_axis_type axis2_type
1629 = pivot_axis_type_transpose (axis_type);
1631 size_t *axis2_indexes;
1632 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1634 const size_t *pindexes[PIVOT_N_AXES];
1635 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1636 pindexes[axis_type] = axis_indexes;
1637 pindexes[axis2_type] = axis2_indexes;
1638 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1639 if (pivot_table_get (table, dindexes))
1645 free (axis2_indexes);
1648 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1649 p += axis->n_dimensions;
1651 if (omit_empty && p == enumeration)
1653 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1655 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1656 p += axis->n_dimensions;
1661 *n = (p - enumeration) / axis->n_dimensions;
1667 static struct pivot_cell *
1668 pivot_table_lookup_cell (const struct pivot_table *table,
1669 const size_t *dindexes)
1671 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1672 return pivot_table_lookup_cell__ (table, dindexes, hash);
1675 const struct pivot_value *
1676 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1678 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1679 return cell ? cell->value : NULL;
1682 struct pivot_value *
1683 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1685 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1687 cell->value = pivot_value_new_user_text ("", -1);
1692 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1694 hmap_delete (&table->cells, &cell->hmap_node);
1695 pivot_value_destroy (cell->value);
1700 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1702 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1705 pivot_table_delete_cell (table, cell);
1713 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1715 if (pivot_category_is_group (category) && category->n_subs)
1716 for (size_t i = 0; i < category->n_subs; i++)
1717 distribute_extra_depth (category->subs[i], extra_depth);
1719 category->extra_depth += extra_depth;
1723 pivot_category_assign_label_depth (struct pivot_category *category,
1724 bool dimension_labels_in_corner)
1726 category->extra_depth = 0;
1728 if (pivot_category_is_group (category))
1731 for (size_t i = 0; i < category->n_subs; i++)
1733 pivot_category_assign_label_depth (category->subs[i], false);
1734 depth = MAX (depth, category->subs[i]->label_depth);
1737 for (size_t i = 0; i < category->n_subs; i++)
1739 struct pivot_category *sub = category->subs[i];
1741 size_t extra_depth = depth - sub->label_depth;
1743 distribute_extra_depth (sub, extra_depth);
1745 sub->label_depth = depth;
1748 category->show_label_in_corner = (category->show_label
1749 && dimension_labels_in_corner);
1750 category->label_depth
1751 = (category->show_label && !category->show_label_in_corner
1752 ? depth + 1 : depth);
1755 category->label_depth = 1;
1759 pivot_axis_assign_label_depth (struct pivot_table *table,
1760 enum pivot_axis_type axis_type,
1761 bool dimension_labels_in_corner)
1763 struct pivot_axis *axis = &table->axes[axis_type];
1764 bool any_label_shown_in_corner = false;
1765 axis->label_depth = 0;
1767 for (size_t i = 0; i < axis->n_dimensions; i++)
1769 struct pivot_dimension *d = axis->dimensions[i];
1770 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1771 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1772 axis->label_depth += d->label_depth;
1773 axis->extent *= d->n_leaves;
1775 if (d->root->show_label_in_corner)
1776 any_label_shown_in_corner = true;
1778 return any_label_shown_in_corner;
1782 pivot_table_assign_label_depth (struct pivot_table *table)
1784 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1785 if (pivot_axis_assign_label_depth (
1786 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1787 && !table->corner_text))
1788 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1789 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1790 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1794 indent (int indentation)
1796 for (int i = 0; i < indentation * 2; i++)
1801 pivot_value_dump (const struct pivot_value *value,
1802 const struct pivot_table *pt)
1804 char *s = pivot_value_to_string (value, pt);
1810 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1811 const struct pivot_table *pt, int indentation)
1815 indent (indentation);
1816 printf ("%s: ", name);
1817 pivot_value_dump (value, pt);
1823 pivot_table_dump_string (const char *string, const char *name, int indentation)
1827 indent (indentation);
1828 printf ("%s: %s\n", name, string);
1833 pivot_category_dump (const struct pivot_category *c,
1834 const struct pivot_table *pt, int indentation)
1836 indent (indentation);
1837 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1838 pivot_value_dump (c->name, pt);
1841 if (pivot_category_is_leaf (c))
1842 printf ("data_index=%zu\n", c->data_index);
1845 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1848 for (size_t i = 0; i < c->n_subs; i++)
1849 pivot_category_dump (c->subs[i], pt, indentation + 1);
1854 pivot_dimension_dump (const struct pivot_dimension *d,
1855 const struct pivot_table *pt, int indentation)
1857 indent (indentation);
1858 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1859 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1861 pivot_category_dump (d->root, pt, indentation + 1);
1865 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1868 indent (indentation);
1869 printf ("%s: ", pivot_area_to_string (area));
1870 font_style_dump (&a->font_style);
1872 cell_style_dump (&a->cell_style);
1877 table_border_style_dump (enum pivot_border border,
1878 const struct table_border_style *b, int indentation)
1880 indent (indentation);
1881 printf ("%s: %s ", pivot_border_to_string (border),
1882 table_stroke_to_string (b->stroke));
1883 cell_color_dump (&b->color);
1888 compose_headings (const struct pivot_table *pt,
1889 const struct pivot_axis *axis,
1890 const size_t *column_enumeration)
1892 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1895 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1896 for (size_t i = 0; i < axis->label_depth; i++)
1897 headings[i] = xcalloc (axis->extent, sizeof **headings);
1899 const size_t *indexes;
1901 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1903 int row = axis->label_depth - 1;
1904 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1906 const struct pivot_dimension *d = axis->dimensions[dim_index];
1907 if (d->hide_all_labels)
1909 for (const struct pivot_category *c
1910 = d->presentation_leaves[indexes[dim_index]];
1914 if (pivot_category_is_leaf (c) || (c->show_label
1915 && !c->show_label_in_corner))
1917 headings[row][column] = pivot_value_to_string (c->name, pt);
1918 if (!*headings[row][column])
1919 headings[row][column] = xstrdup ("<blank>");
1931 free_headings (const struct pivot_axis *axis, char ***headings)
1933 for (size_t i = 0; i < axis->label_depth; i++)
1935 for (size_t j = 0; j < axis->extent; j++)
1936 free (headings[i][j]);
1943 pivot_table_sizing_dump (const char *name,
1944 const int width_ranges[2],
1945 const struct pivot_table_sizing *s,
1948 indent (indentation);
1949 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
1952 indent (indentation + 1);
1953 printf ("%s widths:", name);
1954 for (size_t i = 0; i < s->n_widths; i++)
1955 printf (" %d", s->widths[i]);
1960 indent (indentation + 1);
1961 printf ("break after %ss:", name);
1962 for (size_t i = 0; i < s->n_breaks; i++)
1963 printf (" %zu", s->breaks[i]);
1968 indent (indentation + 1);
1969 printf ("keep %ss together:", name);
1970 for (size_t i = 0; i < s->n_keeps; i++)
1971 printf (" [%zu,%zu]",
1973 s->keeps[i].ofs + s->keeps[i].n - 1);
1979 pivot_table_dump (const struct pivot_table *table, int indentation)
1984 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
1986 pivot_table_dump_value (table->title, "title", table, indentation);
1987 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
1988 pivot_table_dump_string (table->command_c, "command", indentation);
1989 pivot_table_dump_string (table->dataset, "dataset", indentation);
1990 pivot_table_dump_string (table->datafile, "datafile", indentation);
1991 pivot_table_dump_string (table->notes, "notes", indentation);
1992 pivot_table_dump_string (table->look->name, "table-look", indentation);
1995 indent (indentation);
1997 struct tm *tm = localtime (&table->date);
1998 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
1999 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2003 indent (indentation);
2004 printf ("sizing:\n");
2005 pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2006 &table->sizing[TABLE_HORZ], indentation + 1);
2007 pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2008 &table->sizing[TABLE_VERT], indentation + 1);
2010 indent (indentation);
2011 printf ("areas:\n");
2012 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2013 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2015 indent (indentation);
2016 printf ("borders:\n");
2017 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2018 table_border_style_dump (border, &table->look->borders[border],
2021 for (size_t i = 0; i < table->n_dimensions; i++)
2022 pivot_dimension_dump (table->dimensions[i], table, indentation);
2024 /* Presentation and data indexes. */
2025 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2027 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2028 if (layer_axis->n_dimensions)
2030 indent (indentation);
2031 printf ("current layer:");
2033 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2035 const struct pivot_dimension *d = layer_axis->dimensions[i];
2036 char *name = pivot_value_to_string (d->root->name, table);
2037 char *value = pivot_value_to_string (
2038 d->data_leaves[table->current_layer[i]]->name, table);
2039 printf (" %s=%s", name, value);
2047 size_t *layer_indexes;
2048 size_t layer_iteration = 0;
2049 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2051 indent (indentation);
2052 printf ("layer %zu:", layer_iteration++);
2054 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2055 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2057 const struct pivot_dimension *d = layer_axis->dimensions[i];
2059 fputs (i == 0 ? " " : ", ", stdout);
2060 pivot_value_dump (d->root->name, table);
2061 fputs (" =", stdout);
2063 struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
2065 for (const struct pivot_category *c
2066 = d->presentation_leaves[layer_indexes[i]];
2070 if (pivot_category_is_leaf (c) || c->show_label)
2071 names[n_names++] = c->name;
2074 for (size_t i = n_names; i-- > 0;)
2077 pivot_value_dump (names[i], table);
2083 size_t *column_enumeration = pivot_table_enumerate_axis (
2084 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2085 size_t *row_enumeration = pivot_table_enumerate_axis (
2086 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2088 char ***column_headings = compose_headings (
2089 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2090 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2092 indent (indentation + 1);
2093 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2096 fputs ("; ", stdout);
2097 if (column_headings[y][x])
2098 fputs (column_headings[y][x], stdout);
2102 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2104 indent (indentation + 1);
2105 printf ("-----------------------------------------------\n");
2107 char ***row_headings = compose_headings (
2108 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2111 const size_t *pindexes[PIVOT_N_AXES]
2112 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2113 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2114 &table->axes[PIVOT_AXIS_ROW])
2116 indent (indentation + 1);
2119 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2122 fputs ("; ", stdout);
2123 if (row_headings[y][x])
2124 fputs (row_headings[y][x], stdout);
2130 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2132 &table->axes[PIVOT_AXIS_COLUMN])
2137 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2138 const struct pivot_value *value = pivot_table_get (
2141 pivot_value_dump (value, table);
2148 free (column_enumeration);
2149 free (row_enumeration);
2150 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2153 pivot_table_dump_value (table->caption, "caption", table, indentation);
2155 for (size_t i = 0; i < table->n_footnotes; i++)
2157 const struct pivot_footnote *f = table->footnotes[i];
2158 indent (indentation);
2161 pivot_value_dump (f->marker, table);
2163 printf ("%zu", f->idx);
2165 pivot_value_dump (f->content, table);
2173 consume_int (const char *p, size_t *n)
2176 while (c_isdigit (*p))
2177 *n = *n * 10 + (*p++ - '0');
2182 pivot_format_inner_template (struct string *out, const char *template,
2184 struct pivot_value **values, size_t n_values,
2185 const struct pivot_table *pt)
2187 size_t args_consumed = 0;
2188 while (*template && *template != ':')
2190 if (*template == '\\' && template[1])
2192 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2195 else if (*template == escape)
2198 template = consume_int (template + 1, &index);
2199 if (index >= 1 && index <= n_values)
2201 pivot_value_format (values[index - 1], pt, out);
2202 args_consumed = MAX (args_consumed, index);
2206 ds_put_byte (out, *template++);
2208 return args_consumed;
2212 pivot_extract_inner_template (const char *template, const char **p)
2218 if (*template == '\\' && template[1] != '\0')
2220 else if (*template == ':')
2221 return template + 1;
2222 else if (*template == '\0')
2230 pivot_format_template (struct string *out, const char *template,
2231 const struct pivot_argument *args, size_t n_args,
2232 const struct pivot_table *pt)
2236 if (*template == '\\' && template[1] != '\0')
2238 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2241 else if (*template == '^')
2244 template = consume_int (template + 1, &index);
2245 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2246 pivot_value_format (args[index - 1].values[0], pt, out);
2248 else if (*template == '[')
2250 const char *tmpl[2];
2251 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2252 template = pivot_extract_inner_template (template, &tmpl[1]);
2253 template += *template == ']';
2256 template = consume_int (template, &index);
2257 if (index < 1 || index > n_args)
2260 const struct pivot_argument *arg = &args[index - 1];
2261 size_t left = arg->n;
2264 struct pivot_value **values = arg->values + (arg->n - left);
2265 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2266 char escape = "%^"[tmpl_idx];
2267 size_t used = pivot_format_inner_template (
2268 out, tmpl[tmpl_idx], escape, values, left, pt);
2269 if (!used || used > left)
2275 ds_put_byte (out, *template++);
2279 static enum settings_value_show
2280 interpret_show (enum settings_value_show global_show,
2281 enum settings_value_show table_show,
2282 enum settings_value_show value_show,
2285 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2286 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2287 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2291 /* Appends a text representation of the body of VALUE to OUT. Settings on
2292 PT control whether variable and value labels are included.
2294 The "body" omits subscripts and superscripts and footnotes.
2296 Returns true if OUT is a number (or a number plus a value label), false
2299 pivot_value_format_body (const struct pivot_value *value,
2300 const struct pivot_table *pt,
2303 enum settings_value_show show;
2304 bool numeric = false;
2306 switch (value->type)
2308 case PIVOT_VALUE_NUMERIC:
2309 show = interpret_show (settings_get_show_values (),
2311 value->numeric.show,
2312 value->numeric.value_label != NULL);
2313 if (show & SETTINGS_VALUE_SHOW_VALUE)
2315 const struct fmt_spec *f = &value->numeric.format;
2316 const struct fmt_spec *format
2318 && value->numeric.honor_small
2319 && value->numeric.x != 0
2320 && fabs (value->numeric.x) < pt->small
2321 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2324 char *s = data_out (&(union value) { .f = value->numeric.x },
2325 "UTF-8", format, &pt->settings);
2326 ds_put_cstr (out, s + strspn (s, " "));
2329 if (show & SETTINGS_VALUE_SHOW_LABEL)
2331 if (show & SETTINGS_VALUE_SHOW_VALUE)
2332 ds_put_byte (out, ' ');
2333 ds_put_cstr (out, value->numeric.value_label);
2335 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2338 case PIVOT_VALUE_STRING:
2339 show = interpret_show (settings_get_show_values (),
2342 value->string.value_label != NULL);
2343 if (show & SETTINGS_VALUE_SHOW_VALUE)
2345 if (value->string.hex)
2347 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2349 ds_put_format (out, "%02X", *p);
2352 ds_put_cstr (out, value->string.s);
2354 if (show & SETTINGS_VALUE_SHOW_LABEL)
2356 if (show & SETTINGS_VALUE_SHOW_VALUE)
2357 ds_put_byte (out, ' ');
2358 ds_put_cstr (out, value->string.value_label);
2362 case PIVOT_VALUE_VARIABLE:
2363 show = interpret_show (settings_get_show_variables (),
2365 value->variable.show,
2366 value->variable.var_label != NULL);
2367 if (show & SETTINGS_VALUE_SHOW_VALUE)
2368 ds_put_cstr (out, value->variable.var_name);
2369 if (show & SETTINGS_VALUE_SHOW_LABEL)
2371 if (show & SETTINGS_VALUE_SHOW_VALUE)
2372 ds_put_byte (out, ' ');
2373 ds_put_cstr (out, value->variable.var_label);
2377 case PIVOT_VALUE_TEXT:
2378 ds_put_cstr (out, value->text.local);
2381 case PIVOT_VALUE_TEMPLATE:
2382 pivot_format_template (out, value->template.local, value->template.args,
2383 value->template.n_args, pt);
2390 /* Appends a text representation of VALUE to OUT. Settings on
2391 PT control whether variable and value labels are included.
2393 Subscripts and footnotes are included. */
2395 pivot_value_format (const struct pivot_value *value,
2396 const struct pivot_table *pt,
2399 pivot_value_format_body (value, pt, out);
2401 if (value->n_subscripts)
2403 for (size_t i = 0; i < value->n_subscripts; i++)
2404 ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
2407 for (size_t i = 0; i < value->n_footnotes; i++)
2409 ds_put_byte (out, '[');
2411 size_t idx = value->footnote_indexes[i];
2412 const struct pivot_footnote *f = pt->footnotes[idx];
2413 pivot_value_format (f->marker, pt, out);
2415 ds_put_byte (out, ']');
2419 /* Returns a text representation of VALUE. The caller must free the string,
2422 pivot_value_to_string (const struct pivot_value *value,
2423 const struct pivot_table *pt)
2425 struct string s = DS_EMPTY_INITIALIZER;
2426 pivot_value_format (value, pt, &s);
2427 return ds_steal_cstr (&s);
2431 pivot_value_to_string_defaults (const struct pivot_value *value)
2433 static const struct pivot_table pt = {
2434 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2435 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2437 return pivot_value_to_string (value, &pt);
2440 struct pivot_value *
2441 pivot_value_clone (const struct pivot_value *old)
2446 struct pivot_value *new = xmemdup (old, sizeof *new);
2447 if (old->font_style)
2449 new->font_style = xmalloc (sizeof *new->font_style);
2450 font_style_copy (NULL, new->font_style, old->font_style);
2452 if (old->cell_style)
2453 new->cell_style = xmemdup (old->cell_style, sizeof *new->cell_style);
2454 if (old->n_subscripts)
2456 new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
2457 for (size_t i = 0; i < old->n_subscripts; i++)
2458 new->subscripts[i] = xstrdup (old->subscripts[i]);
2460 if (old->n_footnotes)
2461 new->footnote_indexes = xmemdup (
2462 old->footnote_indexes, old->n_footnotes * sizeof *new->footnote_indexes);
2466 case PIVOT_VALUE_NUMERIC:
2467 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2468 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2471 case PIVOT_VALUE_STRING:
2472 new->string.s = xstrdup (new->string.s);
2473 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2474 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2477 case PIVOT_VALUE_VARIABLE:
2478 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2479 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2482 case PIVOT_VALUE_TEXT:
2483 new->text.local = xstrdup (old->text.local);
2484 new->text.c = (old->text.c == old->text.local ? new->text.local
2485 : xstrdup_if_nonnull (old->text.c));
2486 new->text.id = (old->text.id == old->text.local ? new->text.local
2487 : old->text.id == old->text.c ? new->text.c
2488 : xstrdup_if_nonnull (old->text.id));
2491 case PIVOT_VALUE_TEMPLATE:
2492 new->template.local = xstrdup (old->template.local);
2493 new->template.id = (old->template.id == old->template.local
2494 ? new->template.local
2495 : xstrdup (old->template.id));
2496 new->template.args = xmalloc (new->template.n_args
2497 * sizeof *new->template.args);
2498 for (size_t i = 0; i < old->template.n_args; i++)
2499 pivot_argument_copy (&new->template.args[i],
2500 &old->template.args[i]);
2509 /* Frees the data owned by V. */
2511 pivot_value_destroy (struct pivot_value *value)
2515 font_style_uninit (value->font_style);
2516 free (value->font_style);
2517 free (value->cell_style);
2518 free (value->footnote_indexes);
2520 for (size_t i = 0; i < value->n_subscripts; i++)
2521 free (value->subscripts[i]);
2522 free (value->subscripts);
2524 switch (value->type)
2526 case PIVOT_VALUE_NUMERIC:
2527 free (value->numeric.var_name);
2528 free (value->numeric.value_label);
2531 case PIVOT_VALUE_STRING:
2532 free (value->string.s);
2533 free (value->string.var_name);
2534 free (value->string.value_label);
2537 case PIVOT_VALUE_VARIABLE:
2538 free (value->variable.var_name);
2539 free (value->variable.var_label);
2542 case PIVOT_VALUE_TEXT:
2543 free (value->text.local);
2544 if (value->text.c != value->text.local)
2545 free (value->text.c);
2546 if (value->text.id != value->text.local
2547 && value->text.id != value->text.c)
2548 free (value->text.id);
2551 case PIVOT_VALUE_TEMPLATE:
2552 free (value->template.local);
2553 if (value->template.id != value->template.local)
2554 free (value->template.id);
2555 for (size_t i = 0; i < value->template.n_args; i++)
2556 pivot_argument_uninit (&value->template.args[i]);
2557 free (value->template.args);
2567 /* Sets AREA to the style to use for VALUE, with defaults coming from
2568 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2570 pivot_value_get_style (struct pivot_value *value,
2571 const struct font_style *base_font_style,
2572 const struct cell_style *base_cell_style,
2573 struct table_area_style *area)
2575 font_style_copy (NULL, &area->font_style, (value->font_style
2577 : base_font_style));
2578 area->cell_style = *(value->cell_style
2583 /* Copies AREA into VALUE's style. */
2585 pivot_value_set_style (struct pivot_value *value,
2586 const struct table_area_style *area)
2588 pivot_value_set_font_style (value, &area->font_style);
2589 pivot_value_set_cell_style (value, &area->cell_style);
2593 pivot_value_set_font_style (struct pivot_value *value,
2594 const struct font_style *font_style)
2596 if (value->font_style)
2597 font_style_uninit (value->font_style);
2599 value->font_style = xmalloc (sizeof *value->font_style);
2600 font_style_copy (NULL, value->font_style, font_style);
2604 pivot_value_set_cell_style (struct pivot_value *value,
2605 const struct cell_style *cell_style)
2607 if (!value->cell_style)
2608 value->cell_style = xmalloc (sizeof *value->cell_style);
2609 *value->cell_style = *cell_style;
2613 pivot_argument_copy (struct pivot_argument *dst,
2614 const struct pivot_argument *src)
2616 *dst = (struct pivot_argument) {
2618 .values = xmalloc (src->n * sizeof *dst->values),
2621 for (size_t i = 0; i < src->n; i++)
2622 dst->values[i] = pivot_value_clone (src->values[i]);
2625 /* Frees the data owned by ARG (but not ARG itself). */
2627 pivot_argument_uninit (struct pivot_argument *arg)
2631 for (size_t i = 0; i < arg->n; i++)
2632 pivot_value_destroy (arg->values[i]);
2637 /* Creates and returns a new pivot_value whose contents is the null-terminated
2638 string TEXT. Takes ownership of TEXT.
2640 This function is for text strings provided by the user (with the exception
2641 that pivot_value_new_variable() should be used for variable names). For
2642 strings that are part of the PSPP user interface, such as names of
2643 procedures, statistics, annotations, error messages, etc., use
2644 pivot_value_new_text(). */
2645 struct pivot_value *
2646 pivot_value_new_user_text_nocopy (char *text)
2648 struct pivot_value *value = xmalloc (sizeof *value);
2649 *value = (struct pivot_value) {
2650 .type = PIVOT_VALUE_TEXT,
2655 .user_provided = true,
2661 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2662 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2665 This function is for text strings provided by the user (with the exception
2666 that pivot_value_new_variable() should be used for variable names). For
2667 strings that are part of the PSPP user interface, such as names of
2668 procedures, statistics, annotations, error messages, etc., use
2669 pivot_value_new_text().j
2671 The caller retains ownership of TEXT.*/
2672 struct pivot_value *
2673 pivot_value_new_user_text (const char *text, size_t length)
2675 return pivot_value_new_user_text_nocopy (
2676 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2679 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2680 a translatable string, but not actually translated yet, e.g. enclosed in
2681 N_(). This function is for text strings that are part of the PSPP user
2682 interface, such as names of procedures, statistics, annotations, error
2683 messages, etc. For strings that come from the user, use
2684 pivot_value_new_user_text(). */
2685 struct pivot_value *
2686 pivot_value_new_text (const char *text)
2688 char *c = xstrdup (text);
2689 char *local = xstrdup (gettext (c));
2691 struct pivot_value *value = xmalloc (sizeof *value);
2692 *value = (struct pivot_value) {
2693 .type = PIVOT_VALUE_TEXT,
2698 .user_provided = false,
2704 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2706 struct pivot_value * PRINTF_FORMAT (1, 2)
2707 pivot_value_new_text_format (const char *format, ...)
2710 va_start (args, format);
2711 char *c = xvasprintf (format, args);
2714 va_start (args, format);
2715 char *local = xvasprintf (gettext (format), args);
2718 struct pivot_value *value = xmalloc (sizeof *value);
2719 *value = (struct pivot_value) {
2720 .type = PIVOT_VALUE_TEXT,
2725 .user_provided = false,
2731 /* Returns a new pivot_value that represents X.
2733 The format to use for X is unspecified. Usually the easiest way to specify
2734 a format is through assigning a result class to one of the categories that
2735 the pivot_value will end up in. If that is not suitable, then the caller
2736 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2737 struct pivot_value *
2738 pivot_value_new_number (double x)
2740 struct pivot_value *value = xmalloc (sizeof *value);
2741 *value = (struct pivot_value) {
2742 .type = PIVOT_VALUE_NUMERIC,
2743 .numeric = { .x = x, },
2748 /* Returns a new pivot_value that represents X, formatted as an integer. */
2749 struct pivot_value *
2750 pivot_value_new_integer (double x)
2752 struct pivot_value *value = pivot_value_new_number (x);
2753 value->numeric.format = (struct fmt_spec) { FMT_F, 40, 0 };
2757 /* Returns a new pivot_value that represents VALUE, formatted as for
2759 struct pivot_value *
2760 pivot_value_new_var_value (const struct variable *variable,
2761 const union value *value)
2763 struct pivot_value *pv = pivot_value_new_value (
2764 value, var_get_width (variable), var_get_print_format (variable),
2765 var_get_encoding (variable));
2767 char *var_name = xstrdup (var_get_name (variable));
2768 if (var_is_alpha (variable))
2769 pv->string.var_name = var_name;
2771 pv->numeric.var_name = var_name;
2773 const char *label = var_lookup_value_label (variable, value);
2776 if (var_is_alpha (variable))
2777 pv->string.value_label = xstrdup (label);
2779 pv->numeric.value_label = xstrdup (label);
2785 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2786 formatted with FORMAT. For a string value, ENCODING must be its character
2788 struct pivot_value *
2789 pivot_value_new_value (const union value *value, int width,
2790 const struct fmt_spec *format, const char *encoding)
2792 struct pivot_value *pv = xzalloc (sizeof *pv);
2795 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2797 size_t n = strlen (s);
2798 while (n > 0 && s[n - 1] == ' ')
2801 pv->type = PIVOT_VALUE_STRING;
2803 pv->string.hex = format->type == FMT_AHEX;
2807 pv->type = PIVOT_VALUE_NUMERIC;
2808 pv->numeric.x = value->f;
2809 pv->numeric.format = *format;
2815 /* Returns a new pivot_value for VARIABLE. */
2816 struct pivot_value *
2817 pivot_value_new_variable (const struct variable *variable)
2819 struct pivot_value *value = xmalloc (sizeof *value);
2820 *value = (struct pivot_value) {
2821 .type = PIVOT_VALUE_VARIABLE,
2823 .var_name = xstrdup (var_get_name (variable)),
2824 .var_label = xstrdup_if_nonempty (var_get_label (variable)),
2830 /* Attaches a reference to FOOTNOTE to V. */
2832 pivot_value_add_footnote (struct pivot_value *v,
2833 const struct pivot_footnote *footnote)
2835 /* Some legacy tables include numerous duplicate footnotes. Suppress
2837 for (size_t i = 0; i < v->n_footnotes; i++)
2838 if (v->footnote_indexes[i] == footnote->idx)
2841 v->footnote_indexes = xrealloc (
2842 v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
2843 v->footnote_indexes[v->n_footnotes++] = footnote->idx;
2846 /* If VALUE is a numeric value, and RC is a result class such as
2847 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2849 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2852 if (value->type == PIVOT_VALUE_NUMERIC)
2853 pivot_table_use_rc (table, rc,
2854 &value->numeric.format, &value->numeric.honor_small);