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"
21 #include <libxml/parser.h>
22 #include <libxml/tree.h>
26 #include "data/data-out.h"
27 #include "data/settings.h"
28 #include "data/value.h"
29 #include "data/variable.h"
30 #include "data/file-name.h"
31 #include "libpspp/array.h"
32 #include "libpspp/assertion.h"
33 #include "libpspp/hash-functions.h"
34 #include "libpspp/i18n.h"
35 #include "output/driver.h"
36 #include "output/spv/spv-table-look.h"
38 #include "gl/c-ctype.h"
39 #include "gl/configmake.h"
40 #include "gl/intprops.h"
41 #include "gl/minmax.h"
42 #include "gl/relocatable.h"
43 #include "gl/xalloc.h"
44 #include "gl/xmemdup0.h"
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
51 static void pivot_table_use_rc (const struct pivot_table *, const char *s,
52 struct fmt_spec *, bool *honor_small);
54 /* Pivot table display styling. */
56 /* Returns the name of AREA. */
58 pivot_area_to_string (enum pivot_area area)
62 case PIVOT_AREA_TITLE: return "title";
63 case PIVOT_AREA_CAPTION: return "caption";
64 case PIVOT_AREA_FOOTER: return "footer";
65 case PIVOT_AREA_CORNER: return "corner";
66 case PIVOT_AREA_COLUMN_LABELS: return "column labels";
67 case PIVOT_AREA_ROW_LABELS: return "row labels";
68 case PIVOT_AREA_DATA: return "data";
69 case PIVOT_AREA_LAYERS: return "layers";
70 case PIVOT_N_AREAS: default: return "**error**";
74 /* Returns the name of BORDER. */
76 pivot_border_to_string (enum pivot_border border)
80 case PIVOT_BORDER_TITLE:
83 case PIVOT_BORDER_OUTER_LEFT:
84 return "left outer frame";
85 case PIVOT_BORDER_OUTER_TOP:
86 return "top outer frame";
87 case PIVOT_BORDER_OUTER_RIGHT:
88 return "right outer frame";
89 case PIVOT_BORDER_OUTER_BOTTOM:
90 return "bottom outer frame";
92 case PIVOT_BORDER_INNER_LEFT:
93 return "left inner frame";
94 case PIVOT_BORDER_INNER_TOP:
95 return "top inner frame";
96 case PIVOT_BORDER_INNER_RIGHT:
97 return "right inner frame";
98 case PIVOT_BORDER_INNER_BOTTOM:
99 return "bottom inner frame";
101 case PIVOT_BORDER_DATA_LEFT:
102 return "data area left";
103 case PIVOT_BORDER_DATA_TOP:
104 return "data area top";
106 case PIVOT_BORDER_DIM_ROW_HORZ:
107 return "row label horizontal dimension border";
108 case PIVOT_BORDER_DIM_ROW_VERT:
109 return "row label vertical dimension border";
110 case PIVOT_BORDER_DIM_COL_HORZ:
111 return "column label horizontal dimension border";
112 case PIVOT_BORDER_DIM_COL_VERT:
113 return "column label vertical dimension border";
115 case PIVOT_BORDER_CAT_ROW_HORZ:
116 return "row label horizontal category border";
117 case PIVOT_BORDER_CAT_ROW_VERT:
118 return "row label vertical category border";
119 case PIVOT_BORDER_CAT_COL_HORZ:
120 return "column label horizontal category border";
121 case PIVOT_BORDER_CAT_COL_VERT:
122 return "column label vertical category border";
124 case PIVOT_N_BORDERS:
131 pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
135 free (sizing->widths);
136 free (sizing->breaks);
137 free (sizing->keeps);
141 /* Pivot table looks. */
143 static const struct pivot_table_look *
144 default_look (const struct pivot_table_look *new)
146 static struct pivot_table_look *look;
149 pivot_table_look_unref (look);
150 look = pivot_table_look_ref (new);
154 char *error = pivot_table_look_read ("default.stt", &look);
158 look = pivot_table_look_ref (pivot_table_look_builtin_default ());
164 const struct pivot_table_look *
165 pivot_table_look_get_default (void)
167 return default_look (NULL);
171 pivot_table_look_set_default (const struct pivot_table_look *look)
176 char * WARN_UNUSED_RESULT
177 pivot_table_look_read (const char *name, struct pivot_table_look **lookp)
181 /* Construct search path. */
185 const char *home = getenv ("HOME");
186 char *allocated = NULL;
188 path[n++] = allocated = xasprintf ("%s/.pspp/looks", home);
190 path[n++] = relocate2 (PKGDATADIR "/looks", &allocated2);
194 char *file = fn_search_path (name, (char **) path);
197 char *name2 = xasprintf ("%s.stt", name);
198 file = fn_search_path (name2, (char **) path);
204 return xasprintf ("%s: not found", name);
207 char *error = spv_table_look_read (file, lookp);
212 const struct pivot_table_look *
213 pivot_table_look_builtin_default (void)
215 static struct pivot_table_look look = {
219 .row_labels_in_corner = true,
221 [TABLE_HORZ] = { 36, 72 },
222 [TABLE_VERT] = { 36, 120 },
226 #define AREA(BOLD, H, V, L, R, T, B) { \
228 .halign = TABLE_HALIGN_##H, \
229 .valign = TABLE_VALIGN_##V, \
230 .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \
231 [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \
235 .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \
236 .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \
238 .typeface = (char *) "Sans Serif", \
241 [PIVOT_AREA_TITLE] = AREA(true, CENTER, CENTER, 8,11,1,8),
242 [PIVOT_AREA_CAPTION] = AREA(false, LEFT, TOP, 8,11,1,1),
243 [PIVOT_AREA_FOOTER] = AREA(false, LEFT, TOP, 11, 8,2,3),
244 [PIVOT_AREA_CORNER] = AREA(false, LEFT, BOTTOM, 8,11,1,1),
245 [PIVOT_AREA_COLUMN_LABELS] = AREA(false, CENTER, BOTTOM, 8,11,1,3),
246 [PIVOT_AREA_ROW_LABELS] = AREA(false, LEFT, TOP, 8,11,1,3),
247 [PIVOT_AREA_DATA] = AREA(false, MIXED, TOP, 8,11,1,1),
248 [PIVOT_AREA_LAYERS] = AREA(false, LEFT, BOTTOM, 8,11,1,3),
253 #define BORDER(STROKE) { .stroke = STROKE, .color = CELL_COLOR_BLACK }
254 [PIVOT_BORDER_TITLE] = BORDER(TABLE_STROKE_NONE),
255 [PIVOT_BORDER_OUTER_LEFT] = BORDER(TABLE_STROKE_NONE),
256 [PIVOT_BORDER_OUTER_TOP] = BORDER(TABLE_STROKE_NONE),
257 [PIVOT_BORDER_OUTER_RIGHT] = BORDER(TABLE_STROKE_NONE),
258 [PIVOT_BORDER_OUTER_BOTTOM] = BORDER(TABLE_STROKE_NONE),
259 [PIVOT_BORDER_INNER_LEFT] = BORDER(TABLE_STROKE_THICK),
260 [PIVOT_BORDER_INNER_TOP] = BORDER(TABLE_STROKE_THICK),
261 [PIVOT_BORDER_INNER_RIGHT] = BORDER(TABLE_STROKE_THICK),
262 [PIVOT_BORDER_INNER_BOTTOM] = BORDER(TABLE_STROKE_THICK),
263 [PIVOT_BORDER_DATA_LEFT] = BORDER(TABLE_STROKE_THICK),
264 [PIVOT_BORDER_DATA_TOP] = BORDER(TABLE_STROKE_THICK),
265 [PIVOT_BORDER_DIM_ROW_HORZ] = BORDER(TABLE_STROKE_SOLID),
266 [PIVOT_BORDER_DIM_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
267 [PIVOT_BORDER_DIM_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
268 [PIVOT_BORDER_DIM_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
269 [PIVOT_BORDER_CAT_ROW_HORZ] = BORDER(TABLE_STROKE_NONE),
270 [PIVOT_BORDER_CAT_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
271 [PIVOT_BORDER_CAT_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
272 [PIVOT_BORDER_CAT_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
279 struct pivot_table_look *
280 pivot_table_look_new_builtin_default (void)
282 return pivot_table_look_unshare (
283 pivot_table_look_ref (pivot_table_look_builtin_default ()));
286 struct pivot_table_look *
287 pivot_table_look_ref (const struct pivot_table_look *look_)
289 assert (look_->ref_cnt > 0);
291 struct pivot_table_look *look = CONST_CAST (struct pivot_table_look *, look_);
297 xstrdup_if_nonempty (const char *s)
299 return s && s[0] ? xstrdup (s) : NULL;
302 struct pivot_table_look *
303 pivot_table_look_unshare (struct pivot_table_look *old)
305 assert (old->ref_cnt > 0);
306 if (old->ref_cnt == 1)
309 pivot_table_look_unref (old);
311 struct pivot_table_look *new = xmemdup (old, sizeof *old);
313 new->name = xstrdup_if_nonempty (old->name);
314 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
315 table_area_style_copy (NULL, &new->areas[i], &old->areas[i]);
316 new->continuation = xstrdup_if_nonempty (old->continuation);
322 pivot_table_look_unref (struct pivot_table_look *look)
326 assert (look->ref_cnt > 0);
327 if (!--look->ref_cnt)
330 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
331 table_area_style_uninit (&look->areas[i]);
332 free (look->continuation);
340 /* Returns the name of AXIS_TYPE. */
342 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
346 case PIVOT_AXIS_LAYER:
352 case PIVOT_AXIS_COLUMN:
360 static enum pivot_axis_type
361 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
363 assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
364 return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
367 /* Implementation of PIVOT_AXIS_FOR_EACH. */
369 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
373 if (axis->n_dimensions)
374 for (size_t i = 0; i < axis->n_dimensions; i++)
375 if (axis->dimensions[i]->n_leaves == 0)
378 size_t size = axis->n_dimensions * sizeof *indexes;
379 return xzalloc (MAX (size, 1));
382 for (size_t i = 0; i < axis->n_dimensions; i++)
384 const struct pivot_dimension *d = axis->dimensions[i];
385 if (++indexes[i] < d->n_leaves)
398 pivot_category_set_rc (struct pivot_category *category, const char *s)
403 pivot_table_use_rc (category->dimension->table, s,
404 &category->format, &category->honor_small);
406 /* Ensure that the category itself, in addition to the cells within it, takes
407 the format. (It's kind of rare for a category to have a numeric format
409 struct pivot_value *name = category->name;
410 if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
411 pivot_table_use_rc (category->dimension->table, s,
412 &name->numeric.format, &name->numeric.honor_small);
416 pivot_category_create_leaves_valist (struct pivot_category *parent,
420 while ((s = va_arg (args, const char *)))
422 if (!strncmp (s, "RC_", 3))
424 assert (parent->n_subs);
425 pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
428 pivot_category_create_leaf (parent, pivot_value_new_text (s));
432 /* Creates a new dimension with the given NAME in TABLE and returns it. The
433 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
436 NAME should be a translatable name, but not actually translated yet,
437 e.g. enclosed in N_(). To use a different kind of value for a name, use
438 pivot_dimension_create__() instead.
440 The optional varargs parameters may be used to add an initial set of
441 categories to the dimension. Each string should be a translatable category
442 name, but not actually translated yet, e.g. enclosed in N_(). Each string
443 may optionally be followod by a PIVOT_RC_* string that specifies the default
444 numeric format for cells in this category. */
445 struct pivot_dimension * SENTINEL (0)
446 (pivot_dimension_create) (struct pivot_table *table,
447 enum pivot_axis_type axis_type,
448 const char *name, ...)
450 struct pivot_dimension *d = pivot_dimension_create__ (
451 table, axis_type, pivot_value_new_text (name));
454 va_start (args, name);
455 pivot_category_create_leaves_valist (d->root, args);
461 /* Creates a new dimension with the given NAME in TABLE and returns it. The
462 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
464 struct pivot_dimension *
465 pivot_dimension_create__ (struct pivot_table *table,
466 enum pivot_axis_type axis_type,
467 struct pivot_value *name)
469 assert (pivot_table_is_empty (table));
471 struct pivot_dimension *d = xmalloc (sizeof *d);
472 *d = (struct pivot_dimension) {
474 .axis_type = axis_type,
475 .level = table->axes[axis_type].n_dimensions,
476 .top_index = table->n_dimensions,
477 .root = xmalloc (sizeof *d->root),
480 struct pivot_category *root = d->root;
481 *root = (struct pivot_category) {
486 .data_index = SIZE_MAX,
487 .presentation_index = SIZE_MAX,
490 table->dimensions = xrealloc (
491 table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
492 table->dimensions[table->n_dimensions++] = d;
494 struct pivot_axis *axis = &table->axes[axis_type];
495 axis->dimensions = xrealloc (
496 axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
497 axis->dimensions[axis->n_dimensions++] = d;
499 if (axis_type == PIVOT_AXIS_LAYER)
501 free (table->current_layer);
502 table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
503 sizeof *table->current_layer);
506 /* axis->extent and axis->label_depth will be calculated later. */
512 pivot_dimension_destroy (struct pivot_dimension *d)
517 pivot_category_destroy (d->root);
518 free (d->data_leaves);
519 free (d->presentation_leaves);
523 /* Returns the first leaf node in an in-order traversal that is a child of
525 static const struct pivot_category * UNUSED
526 pivot_category_first_leaf (const struct pivot_category *cat)
528 if (pivot_category_is_leaf (cat))
531 for (size_t i = 0; i < cat->n_subs; i++)
533 const struct pivot_category *first
534 = pivot_category_first_leaf (cat->subs[i]);
542 /* Returns the next leaf node in an in-order traversal starting at CAT, which
544 static const struct pivot_category * UNUSED
545 pivot_category_next_leaf (const struct pivot_category *cat)
547 assert (pivot_category_is_leaf (cat));
551 const struct pivot_category *parent = cat->parent;
554 for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
556 const struct pivot_category *next
557 = pivot_category_first_leaf (parent->subs[i]);
567 pivot_category_add_child (struct pivot_category *child)
569 struct pivot_category *parent = child->parent;
571 assert (pivot_category_is_group (parent));
572 if (parent->n_subs >= parent->allocated_subs)
573 parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
574 sizeof *parent->subs);
575 parent->subs[parent->n_subs++] = child;
578 /* Adds leaf categories as a child of PARENT. To create top-level categories
579 within dimension 'd', pass 'd->root' for PARENT.
581 Each of the varargs parameters should be a string, each of which should be a
582 translatable category name, but not actually translated yet, e.g. enclosed
583 in N_(). Each string may optionally be followod by a PIVOT_RC_* string that
584 specifies the default numeric format for cells in this category.
586 Returns the category index, which is just a 0-based array index, for the
589 Leaves have to be created in in-order, that is, don't create a group and add
590 some leaves, then add leaves outside the group and try to add more leaves
593 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
595 int retval = parent->dimension->n_leaves;
598 va_start (args, parent);
599 pivot_category_create_leaves_valist (parent, args);
605 /* Creates a new leaf category with the given NAME as a child of PARENT. To
606 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
607 Returns the category index, which is just a 0-based array index, for the new
610 Leaves have to be created in in-order, that is, don't create a group and add
611 some leaves, then add leaves outside the group and try to add more leaves
614 pivot_category_create_leaf (struct pivot_category *parent,
615 struct pivot_value *name)
617 return pivot_category_create_leaf_rc (parent, name, NULL);
620 /* Creates a new leaf category with the given NAME as a child of PARENT. To
621 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
622 Returns the category index, which is just a 0-based array index, for the new
625 If RC is nonnull and the name of a result category, the category is assigned
626 that result category.
628 Leaves have to be created in in-order, that is, don't create a group and add
629 some leaves, then add leaves outside the group and try to add more leaves
632 pivot_category_create_leaf_rc (struct pivot_category *parent,
633 struct pivot_value *name, const char *rc)
635 struct pivot_dimension *d = parent->dimension;
637 struct pivot_category *leaf = xmalloc (sizeof *leaf);
638 *leaf = (struct pivot_category) {
642 .group_index = parent->n_subs,
643 .data_index = d->n_leaves,
644 .presentation_index = d->n_leaves,
647 if (d->n_leaves >= d->allocated_leaves)
649 d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
650 sizeof *d->data_leaves);
651 d->presentation_leaves = xrealloc (
652 d->presentation_leaves,
653 d->allocated_leaves * sizeof *d->presentation_leaves);
656 d->data_leaves[d->n_leaves] = leaf;
657 d->presentation_leaves[d->n_leaves] = leaf;
660 pivot_category_add_child (leaf);
662 /* Make sure that the new child is the last in in-order. */
663 assert (!pivot_category_next_leaf (leaf));
665 pivot_category_set_rc (leaf, rc);
667 return leaf->data_index;
670 /* Adds a new category group named NAME as a child of PARENT. To create a
671 top-level group within dimension 'd', pass 'd->root' for PARENT.
673 NAME should be a translatable name, but not actually translated yet,
674 e.g. enclosed in N_(). To use a different kind of value for a name, use
675 pivot_category_create_group__() instead.
677 The optional varargs parameters may be used to add an initial set of
678 categories to the group. Each string should be a translatable category
679 name, but not actually translated yet, e.g. enclosed in N_(). Each string
680 may optionally be followod by a PIVOT_RC_* string that specifies the default
681 numeric format for cells in this category.
683 Returns the new group. */
684 struct pivot_category * SENTINEL (0)
685 (pivot_category_create_group) (struct pivot_category *parent,
686 const char *name, ...)
688 struct pivot_category *group = pivot_category_create_group__ (
689 parent, pivot_value_new_text (name));
692 va_start (args, name);
693 pivot_category_create_leaves_valist (group, args);
699 /* Adds a new category group named NAME as a child of PARENT. To create a
700 top-level group within dimension 'd', pass 'd->root' for PARENT. Returns
702 struct pivot_category *
703 pivot_category_create_group__ (struct pivot_category *parent,
704 struct pivot_value *name)
706 struct pivot_dimension *d = parent->dimension;
708 struct pivot_category *group = xmalloc (sizeof *group);
709 *group = (struct pivot_category) {
714 .group_index = parent->n_subs,
715 .data_index = SIZE_MAX,
716 .presentation_index = SIZE_MAX,
719 pivot_category_add_child (group);
725 pivot_category_destroy (struct pivot_category *c)
730 pivot_value_destroy (c->name);
731 for (size_t i = 0; i < c->n_subs; i++)
732 pivot_category_destroy (c->subs[i]);
739 These are usually the easiest way to control the formatting of numeric data
740 in a pivot table. See pivot_dimension_create() for an explanation of their
744 const char *name; /* "RC_*". */
745 struct fmt_spec format;
748 /* Formats for most of the result classes. */
749 static struct result_class result_classes[] =
751 { PIVOT_RC_INTEGER, { FMT_F, 40, 0 } },
752 { PIVOT_RC_PERCENT, { FMT_PCT, 40, 1 } },
753 { PIVOT_RC_CORRELATION, { FMT_F, 40, 3 } },
754 { PIVOT_RC_SIGNIFICANCE, { FMT_F, 40, 3 } },
755 { PIVOT_RC_RESIDUAL, { FMT_F, 40, 2 } },
756 { PIVOT_RC_COUNT, { 0, 0, 0 } },
757 { PIVOT_RC_OTHER, { 0, 0, 0 } },
760 /* Has PIVOT_RC_COUNT been overridden by the user? */
761 static bool overridden_count_format;
763 static struct result_class *
764 pivot_result_class_find (const char *s)
766 for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
767 if (!strcmp (s, result_classes[i].name))
768 return &result_classes[i];
773 pivot_table_use_rc (const struct pivot_table *table, const char *s,
774 struct fmt_spec *format, bool *honor_small)
778 if (!strcmp (s, PIVOT_RC_OTHER))
780 *format = *settings_get_format ();
783 else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
785 *format = table->weight_format;
786 *honor_small = false;
790 const struct result_class *rc = pivot_result_class_find (s);
793 *format = rc->format;
794 *honor_small = false;
798 printf ("unknown class %s\n", s);
804 /* Sets the format specification for the result class named S (which should not
805 include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
806 does not name a known result class. */
808 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
810 char *s = xasprintf ("RC_%s", s_);
811 struct result_class *rc = pivot_result_class_find (s);
814 rc->format = *format;
815 if (!strcmp (s, PIVOT_RC_COUNT))
816 overridden_count_format = true;
824 is_pivot_result_class (const char *s)
826 return pivot_result_class_find (s) != NULL;
831 static struct pivot_cell *pivot_table_insert_cell (struct pivot_table *,
832 const size_t *dindexes);
833 static void pivot_table_delete_cell (struct pivot_table *,
834 struct pivot_cell *);
836 /* Creates and returns a new pivot table with the given TITLE. TITLE should be
837 a text string marked for translation but not actually translated yet,
838 e.g. N_("Descriptive Statistics"). The un-translated text string is used as
839 the pivot table's subtype.
841 This function is a shortcut for pivot_table_create__() for the most common
842 case. Use pivot_table_create__() directly if the title should be some kind
843 of value other than an ordinary text string, or if the subtype should be
844 different from the title.
846 See the large comment at the top of pivot-table.h for general advice on
847 creating pivot tables. */
849 pivot_table_create (const char *title)
851 return pivot_table_create__ (pivot_value_new_text (title), title);
854 /* Creates and returns a new pivot table with the given TITLE, and takes
855 ownership of TITLE. The new pivot table's subtype is SUBTYPE, which should
856 be an untranslated English string that describes the contents of the table
857 at a high level without being specific about the variables or other context
860 TITLE and SUBTYPE may be NULL, but in that case the client must add them
861 later because they are both mandatory for a pivot table.
863 See the large comment at the top of pivot-table.h for general advice on
864 creating pivot tables. */
866 pivot_table_create__ (struct pivot_value *title, const char *subtype)
868 struct pivot_table *table = xzalloc (sizeof *table);
870 table->show_title = true;
871 table->show_caption = true;
872 table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 };
873 table->title = title;
874 table->subtype = subtype ? pivot_value_new_text (subtype) : NULL;
875 table->command_c = output_get_command_name ();
876 table->look = pivot_table_look_ref (pivot_table_look_get_default ());
877 table->settings = fmt_settings_copy (settings_get_fmt_settings ());
878 table->small = settings_get_small ();
880 hmap_init (&table->cells);
885 /* Creates and returns a new pivot table with the given TITLE and a single cell
886 with the given CONTENT.
888 This is really just for error handling. */
890 pivot_table_create_for_text (struct pivot_value *title,
891 struct pivot_value *content)
893 struct pivot_table *table = pivot_table_create__ (title, "Error");
895 struct pivot_dimension *d = pivot_dimension_create (
896 table, PIVOT_AXIS_ROW, N_("Error"));
897 d->hide_all_labels = true;
898 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
900 pivot_table_put1 (table, 0, content);
905 /* Increases TABLE's reference count, indicating that it has an additional
906 owner. A pivot table that is shared among multiple owners must not be
909 pivot_table_ref (const struct pivot_table *table_)
911 struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
917 xstrdup_if_nonnull (const char *s)
919 return s ? xstrdup (s) : NULL;
922 static struct pivot_table_sizing
923 clone_sizing (const struct pivot_table_sizing *s)
925 return (struct pivot_table_sizing) {
926 .widths = (s->n_widths
927 ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
929 .n_widths = s->n_widths,
931 .breaks = (s->n_breaks
932 ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
934 .n_breaks = s->n_breaks,
937 ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
939 .n_keeps = s->n_keeps,
943 static struct pivot_footnote **
944 clone_footnotes (struct pivot_footnote **old, size_t n)
949 struct pivot_footnote **new = xmalloc (n * sizeof *new);
950 for (size_t i = 0; i < n; i++)
952 new[i] = xmalloc (sizeof *new[i]);
953 *new[i] = (struct pivot_footnote) {
955 .content = pivot_value_clone (old[i]->content),
956 .marker = pivot_value_clone (old[i]->marker),
957 .show = old[i]->show,
963 static struct pivot_category *
964 clone_category (struct pivot_category *old,
965 struct pivot_dimension *new_dimension,
966 struct pivot_category *new_parent)
968 struct pivot_category *new = xmalloc (sizeof *new);
969 *new = (struct pivot_category) {
970 .name = pivot_value_clone (old->name),
971 .parent = new_parent,
972 .dimension = new_dimension,
973 .label_depth = old->label_depth,
974 .extra_depth = old->extra_depth,
977 ? xzalloc (old->n_subs * sizeof *new->subs)
979 .n_subs = old->n_subs,
980 .allocated_subs = old->n_subs,
982 .show_label = old->show_label,
983 .show_label_in_corner = old->show_label_in_corner,
985 .format = old->format,
986 .group_index = old->group_index,
987 .data_index = old->data_index,
988 .presentation_index = old->presentation_index,
991 if (pivot_category_is_leaf (old))
993 assert (new->data_index < new_dimension->n_leaves);
994 new->dimension->data_leaves[new->data_index] = new;
996 assert (new->presentation_index < new_dimension->n_leaves);
997 new->dimension->presentation_leaves[new->presentation_index] = new;
1000 for (size_t i = 0; i < new->n_subs; i++)
1001 new->subs[i] = clone_category (old->subs[i], new_dimension, new);
1006 static struct pivot_dimension *
1007 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1009 struct pivot_dimension *new = xmalloc (sizeof *new);
1010 *new = (struct pivot_dimension) {
1012 .axis_type = old->axis_type,
1013 .level = old->level,
1014 .top_index = old->top_index,
1015 .data_leaves = xzalloc (old->n_leaves * sizeof *new->data_leaves),
1016 .presentation_leaves = xzalloc (old->n_leaves
1017 * sizeof *new->presentation_leaves),
1018 .n_leaves = old->n_leaves,
1019 .allocated_leaves = old->n_leaves,
1020 .hide_all_labels = old->hide_all_labels,
1021 .label_depth = old->label_depth,
1024 new->root = clone_category (old->root, new, NULL);
1029 static struct pivot_dimension **
1030 clone_dimensions (struct pivot_dimension **old, size_t n,
1031 struct pivot_table *new_pt)
1036 struct pivot_dimension **new = xmalloc (n * sizeof *new);
1037 for (size_t i = 0; i < n; i++)
1038 new[i] = clone_dimension (old[i], new_pt);
1042 struct pivot_table *
1043 pivot_table_unshare (struct pivot_table *old)
1045 assert (old->ref_cnt > 0);
1046 if (old->ref_cnt == 1)
1049 pivot_table_unref (old);
1051 struct pivot_table *new = xmalloc (sizeof *new);
1052 *new = (struct pivot_table) {
1055 .look = pivot_table_look_ref (old->look),
1057 .rotate_inner_column_labels = old->rotate_inner_column_labels,
1058 .rotate_outer_row_labels = old->rotate_outer_row_labels,
1059 .show_grid_lines = old->show_grid_lines,
1060 .show_title = old->show_title,
1061 .show_caption = old->show_caption,
1062 .current_layer = (old->current_layer
1063 ? xmemdup (old->current_layer,
1064 old->axes[PIVOT_AXIS_LAYER].n_dimensions
1065 * sizeof *new->current_layer)
1067 .show_values = old->show_values,
1068 .show_variables = old->show_variables,
1069 .weight_format = old->weight_format,
1072 [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1073 [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1076 .settings = fmt_settings_copy (&old->settings),
1077 .grouping = old->grouping,
1078 .small = old->small,
1080 .command_local = xstrdup_if_nonnull (old->command_local),
1081 .command_c = xstrdup_if_nonnull (old->command_c),
1082 .language = xstrdup_if_nonnull (old->language),
1083 .locale = xstrdup_if_nonnull (old->locale),
1085 .dataset = xstrdup_if_nonnull (old->dataset),
1086 .datafile = xstrdup_if_nonnull (old->datafile),
1089 .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1090 .n_footnotes = old->n_footnotes,
1091 .allocated_footnotes = old->n_footnotes,
1093 .title = pivot_value_clone (old->title),
1094 .subtype = pivot_value_clone (old->subtype),
1095 .corner_text = pivot_value_clone (old->corner_text),
1096 .caption = pivot_value_clone (old->caption),
1097 .notes = xstrdup_if_nonnull (old->notes),
1099 .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1100 .n_dimensions = old->n_dimensions,
1102 .cells = HMAP_INITIALIZER (new->cells),
1105 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1107 struct pivot_axis *new_axis = &new->axes[i];
1108 const struct pivot_axis *old_axis = &old->axes[i];
1110 *new_axis = (struct pivot_axis) {
1111 .dimensions = xmalloc (old_axis->n_dimensions
1112 * sizeof *new_axis->dimensions),
1113 .n_dimensions = old_axis->n_dimensions,
1114 .extent = old_axis->extent,
1115 .label_depth = old_axis->label_depth,
1118 for (size_t i = 0; i < new_axis->n_dimensions; i++)
1119 new_axis->dimensions[i] = new->dimensions[
1120 old_axis->dimensions[i]->top_index];
1123 const struct pivot_cell *old_cell;
1124 size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1125 HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1127 for (size_t i = 0; i < old->n_dimensions; i++)
1128 dindexes[i] = old_cell->idx[i];
1129 struct pivot_cell *new_cell
1130 = pivot_table_insert_cell (new, dindexes);
1131 new_cell->value = pivot_value_clone (old_cell->value);
1138 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1139 If TABLE no longer has any owners, it is freed. */
1141 pivot_table_unref (struct pivot_table *table)
1145 assert (table->ref_cnt > 0);
1146 if (--table->ref_cnt)
1149 free (table->current_layer);
1150 pivot_table_look_unref (table->look);
1152 for (int i = 0; i < TABLE_N_AXES; i++)
1153 pivot_table_sizing_uninit (&table->sizing[i]);
1155 fmt_settings_uninit (&table->settings);
1157 free (table->command_local);
1158 free (table->command_c);
1159 free (table->language);
1160 free (table->locale);
1162 free (table->dataset);
1163 free (table->datafile);
1165 for (size_t i = 0; i < table->n_footnotes; i++)
1166 pivot_footnote_destroy (table->footnotes[i]);
1167 free (table->footnotes);
1169 pivot_value_destroy (table->title);
1170 pivot_value_destroy (table->subtype);
1171 pivot_value_destroy (table->corner_text);
1172 pivot_value_destroy (table->caption);
1173 free (table->notes);
1175 for (size_t i = 0; i < table->n_dimensions; i++)
1176 pivot_dimension_destroy (table->dimensions[i]);
1177 free (table->dimensions);
1179 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1180 free (table->axes[i].dimensions);
1182 struct pivot_cell *cell, *next_cell;
1183 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1185 pivot_table_delete_cell (table, cell);
1187 hmap_destroy (&table->cells);
1192 /* Returns true if TABLE has more than one owner. A pivot table that is shared
1193 among multiple owners must not be modified. */
1195 pivot_table_is_shared (const struct pivot_table *table)
1197 return table->ref_cnt > 1;
1200 /* Swaps axes A and B in TABLE. */
1202 pivot_table_swap_axes (struct pivot_table *table,
1203 enum pivot_axis_type a, enum pivot_axis_type b)
1208 struct pivot_axis tmp = table->axes[a];
1209 table->axes[a] = table->axes[b];
1210 table->axes[b] = tmp;
1212 for (int a = 0; a < PIVOT_N_AXES; a++)
1214 struct pivot_axis *axis = &table->axes[a];
1215 for (size_t d = 0; d < axis->n_dimensions; d++)
1216 axis->dimensions[d]->axis_type = a;
1219 if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1221 free (table->current_layer);
1222 table->current_layer = xzalloc (
1223 table->axes[PIVOT_AXIS_LAYER].n_dimensions
1224 * sizeof *table->current_layer);
1228 /* Swaps the row and column axes in TABLE. */
1230 pivot_table_transpose (struct pivot_table *table)
1232 pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1236 pivot_table_update_axes (struct pivot_table *table)
1238 for (int a = 0; a < PIVOT_N_AXES; a++)
1240 struct pivot_axis *axis = &table->axes[a];
1242 for (size_t d = 0; d < axis->n_dimensions; d++)
1244 struct pivot_dimension *dim = axis->dimensions[d];
1251 /* Moves DIM from its current location in TABLE to POS within AXIS. POS of 0
1252 is the innermost dimension, 1 is the next one out, and so on. */
1254 pivot_table_move_dimension (struct pivot_table *table,
1255 struct pivot_dimension *dim,
1256 enum pivot_axis_type axis, size_t pos)
1258 assert (dim->table == table);
1260 struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1261 struct pivot_axis *new_axis = &table->axes[axis];
1262 pos = MIN (pos, new_axis->n_dimensions);
1264 if (old_axis == new_axis && pos == dim->level)
1270 /* Update the current layer, if necessary. If we're moving within the layer
1271 axis, preserve the current layer. */
1272 if (dim->axis_type == PIVOT_AXIS_LAYER)
1274 if (axis == PIVOT_AXIS_LAYER)
1276 /* Rearranging the layer axis. */
1277 move_element (table->current_layer, old_axis->n_dimensions,
1278 sizeof *table->current_layer,
1283 /* A layer is becoming a row or column. */
1284 remove_element (table->current_layer, old_axis->n_dimensions,
1285 sizeof *table->current_layer, dim->level);
1288 else if (axis == PIVOT_AXIS_LAYER)
1290 /* A row or column is becoming a layer. */
1291 table->current_layer = xrealloc (
1292 table->current_layer,
1293 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1294 insert_element (table->current_layer, new_axis->n_dimensions,
1295 sizeof *table->current_layer, pos);
1296 table->current_layer[pos] = 0;
1299 /* Remove DIM from its current axis. */
1300 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1301 sizeof *old_axis->dimensions, dim->level);
1302 old_axis->n_dimensions--;
1304 /* Insert DIM into its new axis. */
1305 new_axis->dimensions = xrealloc (
1306 new_axis->dimensions,
1307 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1308 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1309 sizeof *new_axis->dimensions, pos);
1310 new_axis->dimensions[pos] = dim;
1311 new_axis->n_dimensions++;
1313 pivot_table_update_axes (table);
1317 const struct pivot_table_look *
1318 pivot_table_get_look (const struct pivot_table *table)
1324 pivot_table_set_look (struct pivot_table *table,
1325 const struct pivot_table_look *look)
1327 pivot_table_look_unref (table->look);
1328 table->look = pivot_table_look_ref (look);
1331 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1332 WV, which should be the weight variable for the dictionary whose data or
1333 statistics are being put into TABLE.
1335 This has no effect if WV is NULL. */
1337 pivot_table_set_weight_var (struct pivot_table *table,
1338 const struct variable *wv)
1341 pivot_table_set_weight_format (table, var_get_print_format (wv));
1344 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1345 format for the dictionary whose data or statistics are being put into TABLE.
1347 This has no effect if WFMT is NULL. */
1349 pivot_table_set_weight_format (struct pivot_table *table,
1350 const struct fmt_spec *wfmt)
1353 table->weight_format = *wfmt;
1356 /* Returns true if TABLE has no cells, false otherwise. */
1358 pivot_table_is_empty (const struct pivot_table *table)
1360 return hmap_is_empty (&table->cells);
1364 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1366 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1370 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1372 for (size_t i = 0; i < n; i++)
1379 static struct pivot_cell *
1380 pivot_table_lookup_cell__ (const struct pivot_table *table,
1381 const size_t *dindexes, unsigned int hash)
1383 struct pivot_cell *cell;
1384 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1386 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1391 static struct pivot_cell *
1392 pivot_cell_allocate (size_t n_idx)
1394 struct pivot_cell *cell UNUSED;
1395 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1398 static struct pivot_cell *
1399 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1401 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1402 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1405 cell = pivot_cell_allocate (table->n_dimensions);
1406 for (size_t i = 0; i < table->n_dimensions; i++)
1407 cell->idx[i] = dindexes[i];
1409 hmap_insert (&table->cells, &cell->hmap_node, hash);
1414 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1415 DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of
1418 If VALUE is a numeric value without a specified format, this function checks
1419 each of the categories designated by DINDEXES[] and takes the format from
1420 the first category with a result class. If none has a result class, uses
1421 the overall default numeric format. */
1423 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1424 struct pivot_value *value)
1426 assert (n == table->n_dimensions);
1427 for (size_t i = 0; i < n; i++)
1428 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1430 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1432 for (size_t i = 0; i < table->n_dimensions; i++)
1434 const struct pivot_dimension *d = table->dimensions[i];
1435 if (dindexes[i] < d->n_leaves)
1437 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1440 value->numeric.format = c->format;
1441 value->numeric.honor_small = c->honor_small;
1446 value->numeric.format = *settings_get_format ();
1447 value->numeric.honor_small = true;
1452 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1453 pivot_value_destroy (cell->value);
1454 cell->value = value;
1457 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1458 dimension. Takes ownership of VALUE. */
1460 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1461 struct pivot_value *value)
1463 size_t dindexes[] = { idx1 };
1464 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1467 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1468 dimensions. Takes ownership of VALUE. */
1470 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1471 struct pivot_value *value)
1473 size_t dindexes[] = { idx1, idx2 };
1474 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1477 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1478 have 3 dimensions. Takes ownership of VALUE. */
1480 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1481 size_t idx3, struct pivot_value *value)
1483 size_t dindexes[] = { idx1, idx2, idx3 };
1484 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1487 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1488 must have 4 dimensions. Takes ownership of VALUE. */
1490 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1491 size_t idx3, size_t idx4, struct pivot_value *value)
1493 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1494 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1497 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1498 automatically assigned marker.
1500 The footnote will only appear in output if it is referenced. Use
1501 pivot_value_add_footnote() to add a reference to the footnote. */
1502 struct pivot_footnote *
1503 pivot_table_create_footnote (struct pivot_table *table,
1504 struct pivot_value *content)
1506 return pivot_table_create_footnote__ (table, table->n_footnotes,
1511 pivot_footnote_format_marker (const struct pivot_footnote *f,
1512 const struct pivot_table *pt,
1516 pivot_value_format_body (f->marker, pt, s);
1517 else if (pt->look->show_numeric_markers)
1518 ds_put_format (s, "%zu", f->idx + 1);
1521 char text[INT_BUFSIZE_BOUND (size_t)];
1522 str_format_26adic (f->idx + 1, false, text, sizeof text);
1523 ds_put_cstr (s, text);
1528 pivot_footnote_marker_string (const struct pivot_footnote *f,
1529 const struct pivot_table *pt)
1531 struct string s = DS_EMPTY_INITIALIZER;
1532 pivot_footnote_format_marker (f, pt, &s);
1533 return ds_steal_cstr (&s);
1536 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1537 all lower indexes as a side effect). If MARKER is nonnull, sets the
1538 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1539 struct pivot_footnote *
1540 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1541 struct pivot_value *marker,
1542 struct pivot_value *content)
1544 if (idx >= table->n_footnotes)
1546 while (idx >= table->allocated_footnotes)
1547 table->footnotes = x2nrealloc (table->footnotes,
1548 &table->allocated_footnotes,
1549 sizeof *table->footnotes);
1550 while (idx >= table->n_footnotes)
1552 struct pivot_footnote *f = xmalloc (sizeof *f);
1553 *f = (struct pivot_footnote) {
1554 .idx = table->n_footnotes,
1557 table->footnotes[table->n_footnotes++] = f;
1561 struct pivot_footnote *f = table->footnotes[idx];
1564 pivot_value_destroy (f->marker);
1569 pivot_value_destroy (f->content);
1570 f->content = content;
1575 /* Frees the data owned by F. */
1577 pivot_footnote_destroy (struct pivot_footnote *f)
1581 pivot_value_destroy (f->content);
1582 pivot_value_destroy (f->marker);
1587 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1588 indexes for each dimension in TABLE in DINDEXES[]. */
1590 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1591 const size_t *pindexes[PIVOT_N_AXES],
1592 size_t dindexes[/* table->n_dimensions */])
1594 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1596 const struct pivot_axis *axis = &table->axes[i];
1598 for (size_t j = 0; j < axis->n_dimensions; j++)
1600 const struct pivot_dimension *d = axis->dimensions[j];
1601 size_t pindex = pindexes[i][j];
1602 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1608 pivot_table_enumerate_axis (const struct pivot_table *table,
1609 enum pivot_axis_type axis_type,
1610 const size_t *layer_indexes, bool omit_empty,
1613 const struct pivot_axis *axis = &table->axes[axis_type];
1614 if (!axis->n_dimensions)
1616 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1618 enumeration[1] = SIZE_MAX;
1623 else if (!axis->extent)
1625 size_t *enumeration = xmalloc (sizeof *enumeration);
1626 *enumeration = SIZE_MAX;
1632 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1633 axis->n_dimensions), 1),
1634 sizeof *enumeration);
1635 size_t *p = enumeration;
1636 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1638 size_t *axis_indexes;
1639 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1643 enum pivot_axis_type axis2_type
1644 = pivot_axis_type_transpose (axis_type);
1646 size_t *axis2_indexes;
1647 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1649 const size_t *pindexes[PIVOT_N_AXES];
1650 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1651 pindexes[axis_type] = axis_indexes;
1652 pindexes[axis2_type] = axis2_indexes;
1653 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1654 if (pivot_table_get (table, dindexes))
1660 free (axis2_indexes);
1663 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1664 p += axis->n_dimensions;
1666 if (omit_empty && p == enumeration)
1668 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1670 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1671 p += axis->n_dimensions;
1676 *n = (p - enumeration) / axis->n_dimensions;
1682 static struct pivot_cell *
1683 pivot_table_lookup_cell (const struct pivot_table *table,
1684 const size_t *dindexes)
1686 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1687 return pivot_table_lookup_cell__ (table, dindexes, hash);
1690 const struct pivot_value *
1691 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1693 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1694 return cell ? cell->value : NULL;
1697 struct pivot_value *
1698 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1700 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1702 cell->value = pivot_value_new_user_text ("", -1);
1707 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1709 hmap_delete (&table->cells, &cell->hmap_node);
1710 pivot_value_destroy (cell->value);
1715 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1717 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1720 pivot_table_delete_cell (table, cell);
1728 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1730 if (pivot_category_is_group (category) && category->n_subs)
1731 for (size_t i = 0; i < category->n_subs; i++)
1732 distribute_extra_depth (category->subs[i], extra_depth);
1734 category->extra_depth += extra_depth;
1738 pivot_category_assign_label_depth (struct pivot_category *category,
1739 bool dimension_labels_in_corner)
1741 category->extra_depth = 0;
1743 if (pivot_category_is_group (category))
1746 for (size_t i = 0; i < category->n_subs; i++)
1748 pivot_category_assign_label_depth (category->subs[i], false);
1749 depth = MAX (depth, category->subs[i]->label_depth);
1752 for (size_t i = 0; i < category->n_subs; i++)
1754 struct pivot_category *sub = category->subs[i];
1756 size_t extra_depth = depth - sub->label_depth;
1758 distribute_extra_depth (sub, extra_depth);
1760 sub->label_depth = depth;
1763 category->show_label_in_corner = (category->show_label
1764 && dimension_labels_in_corner);
1765 category->label_depth
1766 = (category->show_label && !category->show_label_in_corner
1767 ? depth + 1 : depth);
1770 category->label_depth = 1;
1774 pivot_axis_assign_label_depth (struct pivot_table *table,
1775 enum pivot_axis_type axis_type,
1776 bool dimension_labels_in_corner)
1778 struct pivot_axis *axis = &table->axes[axis_type];
1779 bool any_label_shown_in_corner = false;
1780 axis->label_depth = 0;
1782 for (size_t i = 0; i < axis->n_dimensions; i++)
1784 struct pivot_dimension *d = axis->dimensions[i];
1785 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1786 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1787 axis->label_depth += d->label_depth;
1788 axis->extent *= d->n_leaves;
1790 if (d->root->show_label_in_corner)
1791 any_label_shown_in_corner = true;
1793 return any_label_shown_in_corner;
1797 pivot_table_assign_label_depth (struct pivot_table *table)
1799 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1800 if (pivot_axis_assign_label_depth (
1801 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1802 && !table->corner_text))
1803 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1804 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1805 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1809 indent (int indentation)
1811 for (int i = 0; i < indentation * 2; i++)
1816 pivot_value_dump (const struct pivot_value *value,
1817 const struct pivot_table *pt)
1819 char *s = pivot_value_to_string (value, pt);
1825 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1826 const struct pivot_table *pt, int indentation)
1830 indent (indentation);
1831 printf ("%s: ", name);
1832 pivot_value_dump (value, pt);
1838 pivot_table_dump_string (const char *string, const char *name, int indentation)
1842 indent (indentation);
1843 printf ("%s: %s\n", name, string);
1848 pivot_category_dump (const struct pivot_category *c,
1849 const struct pivot_table *pt, int indentation)
1851 indent (indentation);
1852 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1853 pivot_value_dump (c->name, pt);
1856 if (pivot_category_is_leaf (c))
1857 printf ("data_index=%zu\n", c->data_index);
1860 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1863 for (size_t i = 0; i < c->n_subs; i++)
1864 pivot_category_dump (c->subs[i], pt, indentation + 1);
1869 pivot_dimension_dump (const struct pivot_dimension *d,
1870 const struct pivot_table *pt, int indentation)
1872 indent (indentation);
1873 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1874 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1876 pivot_category_dump (d->root, pt, indentation + 1);
1880 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1883 indent (indentation);
1884 printf ("%s: ", pivot_area_to_string (area));
1885 font_style_dump (&a->font_style);
1887 cell_style_dump (&a->cell_style);
1892 table_border_style_dump (enum pivot_border border,
1893 const struct table_border_style *b, int indentation)
1895 indent (indentation);
1896 printf ("%s: %s ", pivot_border_to_string (border),
1897 table_stroke_to_string (b->stroke));
1898 cell_color_dump (&b->color);
1903 compose_headings (const struct pivot_table *pt,
1904 const struct pivot_axis *axis,
1905 const size_t *column_enumeration)
1907 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1910 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1911 for (size_t i = 0; i < axis->label_depth; i++)
1912 headings[i] = xcalloc (axis->extent, sizeof **headings);
1914 const size_t *indexes;
1916 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1918 int row = axis->label_depth - 1;
1919 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1921 const struct pivot_dimension *d = axis->dimensions[dim_index];
1922 if (d->hide_all_labels)
1924 for (const struct pivot_category *c
1925 = d->presentation_leaves[indexes[dim_index]];
1929 if (pivot_category_is_leaf (c) || (c->show_label
1930 && !c->show_label_in_corner))
1932 headings[row][column] = pivot_value_to_string (c->name, pt);
1933 if (!*headings[row][column])
1934 headings[row][column] = xstrdup ("<blank>");
1946 free_headings (const struct pivot_axis *axis, char ***headings)
1948 for (size_t i = 0; i < axis->label_depth; i++)
1950 for (size_t j = 0; j < axis->extent; j++)
1951 free (headings[i][j]);
1958 pivot_table_sizing_dump (const char *name,
1959 const int width_ranges[2],
1960 const struct pivot_table_sizing *s,
1963 indent (indentation);
1964 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
1967 indent (indentation + 1);
1968 printf ("%s widths:", name);
1969 for (size_t i = 0; i < s->n_widths; i++)
1970 printf (" %d", s->widths[i]);
1975 indent (indentation + 1);
1976 printf ("break after %ss:", name);
1977 for (size_t i = 0; i < s->n_breaks; i++)
1978 printf (" %zu", s->breaks[i]);
1983 indent (indentation + 1);
1984 printf ("keep %ss together:", name);
1985 for (size_t i = 0; i < s->n_keeps; i++)
1986 printf (" [%zu,%zu]",
1988 s->keeps[i].ofs + s->keeps[i].n - 1);
1994 pivot_table_dump (const struct pivot_table *table, int indentation)
1999 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
2001 pivot_table_dump_value (table->title, "title", table, indentation);
2002 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
2003 pivot_table_dump_string (table->command_c, "command", indentation);
2004 pivot_table_dump_string (table->dataset, "dataset", indentation);
2005 pivot_table_dump_string (table->datafile, "datafile", indentation);
2006 pivot_table_dump_string (table->notes, "notes", indentation);
2007 pivot_table_dump_string (table->look->name, "table-look", indentation);
2010 indent (indentation);
2012 struct tm *tm = localtime (&table->date);
2013 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2014 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2018 indent (indentation);
2019 printf ("sizing:\n");
2020 pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2021 &table->sizing[TABLE_HORZ], indentation + 1);
2022 pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2023 &table->sizing[TABLE_VERT], indentation + 1);
2025 indent (indentation);
2026 printf ("areas:\n");
2027 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2028 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2030 indent (indentation);
2031 printf ("borders:\n");
2032 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2033 table_border_style_dump (border, &table->look->borders[border],
2036 for (size_t i = 0; i < table->n_dimensions; i++)
2037 pivot_dimension_dump (table->dimensions[i], table, indentation);
2039 /* Presentation and data indexes. */
2040 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2042 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2043 if (layer_axis->n_dimensions)
2045 indent (indentation);
2046 printf ("current layer:");
2048 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2050 const struct pivot_dimension *d = layer_axis->dimensions[i];
2051 char *name = pivot_value_to_string (d->root->name, table);
2052 char *value = pivot_value_to_string (
2053 d->data_leaves[table->current_layer[i]]->name, table);
2054 printf (" %s=%s", name, value);
2062 size_t *layer_indexes;
2063 size_t layer_iteration = 0;
2064 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2066 indent (indentation);
2067 printf ("layer %zu:", layer_iteration++);
2069 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2070 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2072 const struct pivot_dimension *d = layer_axis->dimensions[i];
2074 fputs (i == 0 ? " " : ", ", stdout);
2075 pivot_value_dump (d->root->name, table);
2076 fputs (" =", stdout);
2078 struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
2080 for (const struct pivot_category *c
2081 = d->presentation_leaves[layer_indexes[i]];
2085 if (pivot_category_is_leaf (c) || c->show_label)
2086 names[n_names++] = c->name;
2089 for (size_t i = n_names; i-- > 0;)
2092 pivot_value_dump (names[i], table);
2098 size_t *column_enumeration = pivot_table_enumerate_axis (
2099 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2100 size_t *row_enumeration = pivot_table_enumerate_axis (
2101 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2103 char ***column_headings = compose_headings (
2104 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2105 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2107 indent (indentation + 1);
2108 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2111 fputs ("; ", stdout);
2112 if (column_headings[y][x])
2113 fputs (column_headings[y][x], stdout);
2117 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2119 indent (indentation + 1);
2120 printf ("-----------------------------------------------\n");
2122 char ***row_headings = compose_headings (
2123 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2126 const size_t *pindexes[PIVOT_N_AXES]
2127 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2128 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2129 &table->axes[PIVOT_AXIS_ROW])
2131 indent (indentation + 1);
2134 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2137 fputs ("; ", stdout);
2138 if (row_headings[y][x])
2139 fputs (row_headings[y][x], stdout);
2145 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2147 &table->axes[PIVOT_AXIS_COLUMN])
2152 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2153 const struct pivot_value *value = pivot_table_get (
2156 pivot_value_dump (value, table);
2163 free (column_enumeration);
2164 free (row_enumeration);
2165 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2168 pivot_table_dump_value (table->caption, "caption", table, indentation);
2170 for (size_t i = 0; i < table->n_footnotes; i++)
2172 const struct pivot_footnote *f = table->footnotes[i];
2173 indent (indentation);
2176 pivot_value_dump (f->marker, table);
2178 printf ("%zu", f->idx);
2180 pivot_value_dump (f->content, table);
2188 consume_int (const char *p, size_t *n)
2191 while (c_isdigit (*p))
2192 *n = *n * 10 + (*p++ - '0');
2197 pivot_format_inner_template (struct string *out, const char *template,
2199 struct pivot_value **values, size_t n_values,
2200 const struct pivot_table *pt)
2202 size_t args_consumed = 0;
2203 while (*template && *template != ':')
2205 if (*template == '\\' && template[1])
2207 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2210 else if (*template == escape)
2213 template = consume_int (template + 1, &index);
2214 if (index >= 1 && index <= n_values)
2216 pivot_value_format (values[index - 1], pt, out);
2217 args_consumed = MAX (args_consumed, index);
2221 ds_put_byte (out, *template++);
2223 return args_consumed;
2227 pivot_extract_inner_template (const char *template, const char **p)
2233 if (*template == '\\' && template[1] != '\0')
2235 else if (*template == ':')
2236 return template + 1;
2237 else if (*template == '\0')
2245 pivot_format_template (struct string *out, const char *template,
2246 const struct pivot_argument *args, size_t n_args,
2247 const struct pivot_table *pt)
2251 if (*template == '\\' && template[1] != '\0')
2253 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2256 else if (*template == '^')
2259 template = consume_int (template + 1, &index);
2260 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2261 pivot_value_format (args[index - 1].values[0], pt, out);
2263 else if (*template == '[')
2265 const char *tmpl[2];
2266 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2267 template = pivot_extract_inner_template (template, &tmpl[1]);
2268 template += *template == ']';
2271 template = consume_int (template, &index);
2272 if (index < 1 || index > n_args)
2275 const struct pivot_argument *arg = &args[index - 1];
2276 size_t left = arg->n;
2279 struct pivot_value **values = arg->values + (arg->n - left);
2280 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2281 char escape = "%^"[tmpl_idx];
2282 size_t used = pivot_format_inner_template (
2283 out, tmpl[tmpl_idx], escape, values, left, pt);
2284 if (!used || used > left)
2290 ds_put_byte (out, *template++);
2294 static enum settings_value_show
2295 interpret_show (enum settings_value_show global_show,
2296 enum settings_value_show table_show,
2297 enum settings_value_show value_show,
2300 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2301 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2302 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2306 /* Appends to OUT the actual text content from the given Pango MARKUP. */
2308 get_text_from_markup (const char *markup, struct string *out)
2310 xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
2313 ds_put_cstr (out, markup);
2317 xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
2318 xmlParseChunk (parser, markup, strlen (markup), false);
2319 xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
2321 if (parser->wellFormed)
2323 xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
2324 ds_put_cstr (out, CHAR_CAST (char *, s));
2328 ds_put_cstr (out, markup);
2329 xmlFreeDoc (parser->myDoc);
2330 xmlFreeParserCtxt (parser);
2333 /* Appends a text representation of the body of VALUE to OUT. Settings on
2334 PT control whether variable and value labels are included.
2336 The "body" omits subscripts and superscripts and footnotes.
2338 Returns true if OUT is a number (or a number plus a value label), false
2341 pivot_value_format_body (const struct pivot_value *value,
2342 const struct pivot_table *pt,
2345 enum settings_value_show show;
2346 bool numeric = false;
2348 switch (value->type)
2350 case PIVOT_VALUE_NUMERIC:
2351 show = interpret_show (settings_get_show_values (),
2353 value->numeric.show,
2354 value->numeric.value_label != NULL);
2355 if (show & SETTINGS_VALUE_SHOW_VALUE)
2357 const struct fmt_spec *f = &value->numeric.format;
2358 const struct fmt_spec *format
2360 && value->numeric.honor_small
2361 && value->numeric.x != 0
2362 && fabs (value->numeric.x) < pt->small
2363 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2366 char *s = data_out (&(union value) { .f = value->numeric.x },
2367 "UTF-8", format, &pt->settings);
2368 ds_put_cstr (out, s + strspn (s, " "));
2371 if (show & SETTINGS_VALUE_SHOW_LABEL)
2373 if (show & SETTINGS_VALUE_SHOW_VALUE)
2374 ds_put_byte (out, ' ');
2375 ds_put_cstr (out, value->numeric.value_label);
2377 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2380 case PIVOT_VALUE_STRING:
2381 show = interpret_show (settings_get_show_values (),
2384 value->string.value_label != NULL);
2385 if (show & SETTINGS_VALUE_SHOW_VALUE)
2387 if (value->string.hex)
2389 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2391 ds_put_format (out, "%02X", *p);
2394 ds_put_cstr (out, value->string.s);
2396 if (show & SETTINGS_VALUE_SHOW_LABEL)
2398 if (show & SETTINGS_VALUE_SHOW_VALUE)
2399 ds_put_byte (out, ' ');
2400 ds_put_cstr (out, value->string.value_label);
2404 case PIVOT_VALUE_VARIABLE:
2405 show = interpret_show (settings_get_show_variables (),
2407 value->variable.show,
2408 value->variable.var_label != NULL);
2409 if (show & SETTINGS_VALUE_SHOW_VALUE)
2410 ds_put_cstr (out, value->variable.var_name);
2411 if (show & SETTINGS_VALUE_SHOW_LABEL)
2413 if (show & SETTINGS_VALUE_SHOW_VALUE)
2414 ds_put_byte (out, ' ');
2415 ds_put_cstr (out, value->variable.var_label);
2419 case PIVOT_VALUE_TEXT:
2420 if (value->font_style && value->font_style->markup)
2421 get_text_from_markup (value->text.local, out);
2423 ds_put_cstr (out, value->text.local);
2426 case PIVOT_VALUE_TEMPLATE:
2427 pivot_format_template (out, value->template.local, value->template.args,
2428 value->template.n_args, pt);
2435 /* Appends a text representation of VALUE to OUT. Settings on
2436 PT control whether variable and value labels are included.
2438 Subscripts and footnotes are included.
2440 Returns true if OUT is a number (or a number plus a value label), false
2443 pivot_value_format (const struct pivot_value *value,
2444 const struct pivot_table *pt,
2447 bool numeric = pivot_value_format_body (value, pt, out);
2449 if (value->n_subscripts)
2451 for (size_t i = 0; i < value->n_subscripts; i++)
2452 ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
2455 for (size_t i = 0; i < value->n_footnotes; i++)
2457 ds_put_byte (out, '[');
2459 size_t idx = value->footnote_indexes[i];
2460 const struct pivot_footnote *f = pt->footnotes[idx];
2461 pivot_footnote_format_marker (f, pt, out);
2463 ds_put_byte (out, ']');
2469 /* Returns a text representation of VALUE. The caller must free the string,
2472 pivot_value_to_string (const struct pivot_value *value,
2473 const struct pivot_table *pt)
2475 struct string s = DS_EMPTY_INITIALIZER;
2476 pivot_value_format (value, pt, &s);
2477 return ds_steal_cstr (&s);
2481 pivot_value_to_string_defaults (const struct pivot_value *value)
2483 static const struct pivot_table pt = {
2484 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2485 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2486 .settings = FMT_SETTINGS_INIT,
2488 return pivot_value_to_string (value, &pt);
2491 struct pivot_value *
2492 pivot_value_clone (const struct pivot_value *old)
2497 struct pivot_value *new = xmemdup (old, sizeof *new);
2498 if (old->font_style)
2500 new->font_style = xmalloc (sizeof *new->font_style);
2501 font_style_copy (NULL, new->font_style, old->font_style);
2503 if (old->cell_style)
2504 new->cell_style = xmemdup (old->cell_style, sizeof *new->cell_style);
2505 if (old->n_subscripts)
2507 new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
2508 for (size_t i = 0; i < old->n_subscripts; i++)
2509 new->subscripts[i] = xstrdup (old->subscripts[i]);
2511 if (old->n_footnotes)
2512 new->footnote_indexes = xmemdup (
2513 old->footnote_indexes, old->n_footnotes * sizeof *new->footnote_indexes);
2517 case PIVOT_VALUE_NUMERIC:
2518 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2519 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2522 case PIVOT_VALUE_STRING:
2523 new->string.s = xstrdup (new->string.s);
2524 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2525 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2528 case PIVOT_VALUE_VARIABLE:
2529 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2530 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2533 case PIVOT_VALUE_TEXT:
2534 new->text.local = xstrdup (old->text.local);
2535 new->text.c = (old->text.c == old->text.local ? new->text.local
2536 : xstrdup_if_nonnull (old->text.c));
2537 new->text.id = (old->text.id == old->text.local ? new->text.local
2538 : old->text.id == old->text.c ? new->text.c
2539 : xstrdup_if_nonnull (old->text.id));
2542 case PIVOT_VALUE_TEMPLATE:
2543 new->template.local = xstrdup (old->template.local);
2544 new->template.id = (old->template.id == old->template.local
2545 ? new->template.local
2546 : xstrdup (old->template.id));
2547 new->template.args = xmalloc (new->template.n_args
2548 * sizeof *new->template.args);
2549 for (size_t i = 0; i < old->template.n_args; i++)
2550 pivot_argument_copy (&new->template.args[i],
2551 &old->template.args[i]);
2560 /* Frees the data owned by V. */
2562 pivot_value_destroy (struct pivot_value *value)
2566 font_style_uninit (value->font_style);
2567 free (value->font_style);
2568 free (value->cell_style);
2569 free (value->footnote_indexes);
2571 for (size_t i = 0; i < value->n_subscripts; i++)
2572 free (value->subscripts[i]);
2573 free (value->subscripts);
2575 switch (value->type)
2577 case PIVOT_VALUE_NUMERIC:
2578 free (value->numeric.var_name);
2579 free (value->numeric.value_label);
2582 case PIVOT_VALUE_STRING:
2583 free (value->string.s);
2584 free (value->string.var_name);
2585 free (value->string.value_label);
2588 case PIVOT_VALUE_VARIABLE:
2589 free (value->variable.var_name);
2590 free (value->variable.var_label);
2593 case PIVOT_VALUE_TEXT:
2594 free (value->text.local);
2595 if (value->text.c != value->text.local)
2596 free (value->text.c);
2597 if (value->text.id != value->text.local
2598 && value->text.id != value->text.c)
2599 free (value->text.id);
2602 case PIVOT_VALUE_TEMPLATE:
2603 free (value->template.local);
2604 if (value->template.id != value->template.local)
2605 free (value->template.id);
2606 for (size_t i = 0; i < value->template.n_args; i++)
2607 pivot_argument_uninit (&value->template.args[i]);
2608 free (value->template.args);
2618 /* Sets AREA to the style to use for VALUE, with defaults coming from
2619 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2621 pivot_value_get_style (struct pivot_value *value,
2622 const struct font_style *base_font_style,
2623 const struct cell_style *base_cell_style,
2624 struct table_area_style *area)
2626 font_style_copy (NULL, &area->font_style, (value->font_style
2628 : base_font_style));
2629 area->cell_style = *(value->cell_style
2634 /* Copies AREA into VALUE's style. */
2636 pivot_value_set_style (struct pivot_value *value,
2637 const struct table_area_style *area)
2639 pivot_value_set_font_style (value, &area->font_style);
2640 pivot_value_set_cell_style (value, &area->cell_style);
2644 pivot_value_set_font_style (struct pivot_value *value,
2645 const struct font_style *font_style)
2647 if (value->font_style)
2648 font_style_uninit (value->font_style);
2650 value->font_style = xmalloc (sizeof *value->font_style);
2651 font_style_copy (NULL, value->font_style, font_style);
2655 pivot_value_set_cell_style (struct pivot_value *value,
2656 const struct cell_style *cell_style)
2658 if (!value->cell_style)
2659 value->cell_style = xmalloc (sizeof *value->cell_style);
2660 *value->cell_style = *cell_style;
2664 pivot_argument_copy (struct pivot_argument *dst,
2665 const struct pivot_argument *src)
2667 *dst = (struct pivot_argument) {
2669 .values = xmalloc (src->n * sizeof *dst->values),
2672 for (size_t i = 0; i < src->n; i++)
2673 dst->values[i] = pivot_value_clone (src->values[i]);
2676 /* Frees the data owned by ARG (but not ARG itself). */
2678 pivot_argument_uninit (struct pivot_argument *arg)
2682 for (size_t i = 0; i < arg->n; i++)
2683 pivot_value_destroy (arg->values[i]);
2688 /* Creates and returns a new pivot_value whose contents is the null-terminated
2689 string TEXT. Takes ownership of TEXT.
2691 This function is for text strings provided by the user (with the exception
2692 that pivot_value_new_variable() should be used for variable names). For
2693 strings that are part of the PSPP user interface, such as names of
2694 procedures, statistics, annotations, error messages, etc., use
2695 pivot_value_new_text(). */
2696 struct pivot_value *
2697 pivot_value_new_user_text_nocopy (char *text)
2699 struct pivot_value *value = xmalloc (sizeof *value);
2700 *value = (struct pivot_value) {
2701 .type = PIVOT_VALUE_TEXT,
2706 .user_provided = true,
2712 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2713 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2716 This function is for text strings provided by the user (with the exception
2717 that pivot_value_new_variable() should be used for variable names). For
2718 strings that are part of the PSPP user interface, such as names of
2719 procedures, statistics, annotations, error messages, etc., use
2720 pivot_value_new_text().j
2722 The caller retains ownership of TEXT.*/
2723 struct pivot_value *
2724 pivot_value_new_user_text (const char *text, size_t length)
2726 return pivot_value_new_user_text_nocopy (
2727 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2730 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2731 a translatable string, but not actually translated yet, e.g. enclosed in
2732 N_(). This function is for text strings that are part of the PSPP user
2733 interface, such as names of procedures, statistics, annotations, error
2734 messages, etc. For strings that come from the user, use
2735 pivot_value_new_user_text(). */
2736 struct pivot_value *
2737 pivot_value_new_text (const char *text)
2739 char *c = xstrdup (text);
2740 char *local = xstrdup (gettext (c));
2742 struct pivot_value *value = xmalloc (sizeof *value);
2743 *value = (struct pivot_value) {
2744 .type = PIVOT_VALUE_TEXT,
2749 .user_provided = false,
2755 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2757 struct pivot_value * PRINTF_FORMAT (1, 2)
2758 pivot_value_new_text_format (const char *format, ...)
2761 va_start (args, format);
2762 char *c = xvasprintf (format, args);
2765 va_start (args, format);
2766 char *local = xvasprintf (gettext (format), args);
2769 struct pivot_value *value = xmalloc (sizeof *value);
2770 *value = (struct pivot_value) {
2771 .type = PIVOT_VALUE_TEXT,
2776 .user_provided = false,
2782 /* Returns a new pivot_value that represents X.
2784 The format to use for X is unspecified. Usually the easiest way to specify
2785 a format is through assigning a result class to one of the categories that
2786 the pivot_value will end up in. If that is not suitable, then the caller
2787 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2788 struct pivot_value *
2789 pivot_value_new_number (double x)
2791 struct pivot_value *value = xmalloc (sizeof *value);
2792 *value = (struct pivot_value) {
2793 .type = PIVOT_VALUE_NUMERIC,
2794 .numeric = { .x = x, },
2799 /* Returns a new pivot_value that represents X, formatted as an integer. */
2800 struct pivot_value *
2801 pivot_value_new_integer (double x)
2803 struct pivot_value *value = pivot_value_new_number (x);
2804 value->numeric.format = (struct fmt_spec) { FMT_F, 40, 0 };
2808 /* Returns a new pivot_value that represents VALUE, formatted as for
2810 struct pivot_value *
2811 pivot_value_new_var_value (const struct variable *variable,
2812 const union value *value)
2814 struct pivot_value *pv = pivot_value_new_value (
2815 value, var_get_width (variable), var_get_print_format (variable),
2816 var_get_encoding (variable));
2818 char *var_name = xstrdup (var_get_name (variable));
2819 if (var_is_alpha (variable))
2820 pv->string.var_name = var_name;
2822 pv->numeric.var_name = var_name;
2824 const char *label = var_lookup_value_label (variable, value);
2827 if (var_is_alpha (variable))
2828 pv->string.value_label = xstrdup (label);
2830 pv->numeric.value_label = xstrdup (label);
2836 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2837 formatted with FORMAT. For a string value, ENCODING must be its character
2839 struct pivot_value *
2840 pivot_value_new_value (const union value *value, int width,
2841 const struct fmt_spec *format, const char *encoding)
2843 struct pivot_value *pv = xzalloc (sizeof *pv);
2846 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2848 size_t n = strlen (s);
2849 while (n > 0 && s[n - 1] == ' ')
2852 pv->type = PIVOT_VALUE_STRING;
2854 pv->string.hex = format->type == FMT_AHEX;
2858 pv->type = PIVOT_VALUE_NUMERIC;
2859 pv->numeric.x = value->f;
2860 pv->numeric.format = *format;
2866 /* Returns a new pivot_value for VARIABLE. */
2867 struct pivot_value *
2868 pivot_value_new_variable (const struct variable *variable)
2870 struct pivot_value *value = xmalloc (sizeof *value);
2871 *value = (struct pivot_value) {
2872 .type = PIVOT_VALUE_VARIABLE,
2874 .var_name = xstrdup (var_get_name (variable)),
2875 .var_label = xstrdup_if_nonempty (var_get_label (variable)),
2881 /* Attaches a reference to FOOTNOTE to V. */
2883 pivot_value_add_footnote (struct pivot_value *v,
2884 const struct pivot_footnote *footnote)
2886 /* Some legacy tables include numerous duplicate footnotes. Suppress
2888 for (size_t i = 0; i < v->n_footnotes; i++)
2889 if (v->footnote_indexes[i] == footnote->idx)
2892 v->footnote_indexes = xrealloc (
2893 v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
2894 v->footnote_indexes[v->n_footnotes++] = footnote->idx;
2895 pivot_value_sort_footnotes (v);
2899 compare_footnote_indexes (const void *a_, const void *b_)
2901 const size_t *ap = a_;
2902 const size_t *bp = b_;
2905 return a < b ? -1 : a > b;
2908 /* Sorts the footnote references in V in the standard ascending order.
2910 This is only necessary if code adds (plural) footnotes to a pivot_value by
2911 itself, because pivot_value_add_footnote() does it automatically. */
2913 pivot_value_sort_footnotes (struct pivot_value *v)
2915 if (v->n_footnotes > 1)
2916 qsort (v->footnote_indexes, v->n_footnotes, sizeof *v->footnote_indexes,
2917 compare_footnote_indexes);
2920 /* If VALUE is a numeric value, and RC is a result class such as
2921 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2923 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2926 if (value->type == PIVOT_VALUE_NUMERIC)
2927 pivot_table_use_rc (table, rc,
2928 &value->numeric.format, &value->numeric.honor_small);