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, { .type = FMT_F, .w = 40, .d = 0 } },
752 { PIVOT_RC_PERCENT, { .type = FMT_PCT, .w = 40, .d = 1 } },
753 { PIVOT_RC_CORRELATION, { .type = FMT_F, .w = 40, .d = 3 } },
754 { PIVOT_RC_SIGNIFICANCE, { .type = FMT_F, .w = 40, .d = 3 } },
755 { PIVOT_RC_RESIDUAL, { .type = FMT_F, .w = 40, .d = 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 = xmalloc (sizeof *table);
869 *table = (struct pivot_table) {
872 .show_caption = true,
873 .weight_format = (struct fmt_spec) { .type = FMT_F, .w = 40 },
875 .subtype = subtype ? pivot_value_new_text (subtype) : NULL,
876 .command_c = xstrdup_if_nonempty (output_get_command_name ()),
877 .look = pivot_table_look_ref (pivot_table_look_get_default ()),
878 .settings = fmt_settings_copy (settings_get_fmt_settings ()),
879 .small = settings_get_small (),
880 .cells = HMAP_INITIALIZER (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_);
916 static struct pivot_table_sizing
917 clone_sizing (const struct pivot_table_sizing *s)
919 return (struct pivot_table_sizing) {
920 .widths = (s->n_widths
921 ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
923 .n_widths = s->n_widths,
925 .breaks = (s->n_breaks
926 ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
928 .n_breaks = s->n_breaks,
931 ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
933 .n_keeps = s->n_keeps,
937 static struct pivot_footnote **
938 clone_footnotes (struct pivot_footnote **old, size_t n)
943 struct pivot_footnote **new = xmalloc (n * sizeof *new);
944 for (size_t i = 0; i < n; i++)
946 new[i] = xmalloc (sizeof *new[i]);
947 *new[i] = (struct pivot_footnote) {
949 .content = pivot_value_clone (old[i]->content),
950 .marker = pivot_value_clone (old[i]->marker),
951 .show = old[i]->show,
957 static struct pivot_category *
958 clone_category (struct pivot_category *old,
959 struct pivot_dimension *new_dimension,
960 struct pivot_category *new_parent)
962 struct pivot_category *new = xmalloc (sizeof *new);
963 *new = (struct pivot_category) {
964 .name = pivot_value_clone (old->name),
965 .parent = new_parent,
966 .dimension = new_dimension,
967 .label_depth = old->label_depth,
968 .extra_depth = old->extra_depth,
971 ? xcalloc (old->n_subs, sizeof *new->subs)
973 .n_subs = old->n_subs,
974 .allocated_subs = old->n_subs,
976 .show_label = old->show_label,
977 .show_label_in_corner = old->show_label_in_corner,
979 .format = old->format,
980 .group_index = old->group_index,
981 .data_index = old->data_index,
982 .presentation_index = old->presentation_index,
985 if (pivot_category_is_leaf (old))
987 assert (new->data_index < new_dimension->n_leaves);
988 new->dimension->data_leaves[new->data_index] = new;
990 assert (new->presentation_index < new_dimension->n_leaves);
991 new->dimension->presentation_leaves[new->presentation_index] = new;
994 for (size_t i = 0; i < new->n_subs; i++)
995 new->subs[i] = clone_category (old->subs[i], new_dimension, new);
1000 static struct pivot_dimension *
1001 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1003 struct pivot_dimension *new = xmalloc (sizeof *new);
1004 *new = (struct pivot_dimension) {
1006 .axis_type = old->axis_type,
1007 .level = old->level,
1008 .top_index = old->top_index,
1009 .data_leaves = xcalloc (old->n_leaves , sizeof *new->data_leaves),
1010 .presentation_leaves = xcalloc (old->n_leaves
1011 , sizeof *new->presentation_leaves),
1012 .n_leaves = old->n_leaves,
1013 .allocated_leaves = old->n_leaves,
1014 .hide_all_labels = old->hide_all_labels,
1015 .label_depth = old->label_depth,
1018 new->root = clone_category (old->root, new, NULL);
1023 static struct pivot_dimension **
1024 clone_dimensions (struct pivot_dimension **old, size_t n,
1025 struct pivot_table *new_pt)
1030 struct pivot_dimension **new = xmalloc (n * sizeof *new);
1031 for (size_t i = 0; i < n; i++)
1032 new[i] = clone_dimension (old[i], new_pt);
1036 struct pivot_table *
1037 pivot_table_unshare (struct pivot_table *old)
1039 assert (old->ref_cnt > 0);
1040 if (old->ref_cnt == 1)
1043 pivot_table_unref (old);
1045 struct pivot_table *new = xmalloc (sizeof *new);
1046 *new = (struct pivot_table) {
1049 .look = pivot_table_look_ref (old->look),
1051 .rotate_inner_column_labels = old->rotate_inner_column_labels,
1052 .rotate_outer_row_labels = old->rotate_outer_row_labels,
1053 .show_grid_lines = old->show_grid_lines,
1054 .show_title = old->show_title,
1055 .show_caption = old->show_caption,
1056 .current_layer = (old->current_layer
1057 ? xmemdup (old->current_layer,
1058 old->axes[PIVOT_AXIS_LAYER].n_dimensions
1059 * sizeof *new->current_layer)
1061 .show_values = old->show_values,
1062 .show_variables = old->show_variables,
1063 .weight_format = old->weight_format,
1066 [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1067 [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1070 .settings = fmt_settings_copy (&old->settings),
1071 .grouping = old->grouping,
1072 .small = old->small,
1074 .command_local = xstrdup_if_nonnull (old->command_local),
1075 .command_c = xstrdup_if_nonnull (old->command_c),
1076 .language = xstrdup_if_nonnull (old->language),
1077 .locale = xstrdup_if_nonnull (old->locale),
1079 .dataset = xstrdup_if_nonnull (old->dataset),
1080 .datafile = xstrdup_if_nonnull (old->datafile),
1083 .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1084 .n_footnotes = old->n_footnotes,
1085 .allocated_footnotes = old->n_footnotes,
1087 .title = pivot_value_clone (old->title),
1088 .subtype = pivot_value_clone (old->subtype),
1089 .corner_text = pivot_value_clone (old->corner_text),
1090 .caption = pivot_value_clone (old->caption),
1091 .notes = xstrdup_if_nonnull (old->notes),
1093 .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1094 .n_dimensions = old->n_dimensions,
1096 .cells = HMAP_INITIALIZER (new->cells),
1099 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1101 struct pivot_axis *new_axis = &new->axes[i];
1102 const struct pivot_axis *old_axis = &old->axes[i];
1104 *new_axis = (struct pivot_axis) {
1105 .dimensions = xmalloc (old_axis->n_dimensions
1106 * sizeof *new_axis->dimensions),
1107 .n_dimensions = old_axis->n_dimensions,
1108 .extent = old_axis->extent,
1109 .label_depth = old_axis->label_depth,
1112 for (size_t i = 0; i < new_axis->n_dimensions; i++)
1113 new_axis->dimensions[i] = new->dimensions[
1114 old_axis->dimensions[i]->top_index];
1117 const struct pivot_cell *old_cell;
1118 size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1119 HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1121 for (size_t i = 0; i < old->n_dimensions; i++)
1122 dindexes[i] = old_cell->idx[i];
1123 struct pivot_cell *new_cell
1124 = pivot_table_insert_cell (new, dindexes);
1125 new_cell->value = pivot_value_clone (old_cell->value);
1132 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1133 If TABLE no longer has any owners, it is freed. */
1135 pivot_table_unref (struct pivot_table *table)
1139 assert (table->ref_cnt > 0);
1140 if (--table->ref_cnt)
1143 free (table->current_layer);
1144 pivot_table_look_unref (table->look);
1146 for (int i = 0; i < TABLE_N_AXES; i++)
1147 pivot_table_sizing_uninit (&table->sizing[i]);
1149 fmt_settings_uninit (&table->settings);
1151 free (table->command_local);
1152 free (table->command_c);
1153 free (table->language);
1154 free (table->locale);
1156 free (table->dataset);
1157 free (table->datafile);
1159 for (size_t i = 0; i < table->n_footnotes; i++)
1160 pivot_footnote_destroy (table->footnotes[i]);
1161 free (table->footnotes);
1163 pivot_value_destroy (table->title);
1164 pivot_value_destroy (table->subtype);
1165 pivot_value_destroy (table->corner_text);
1166 pivot_value_destroy (table->caption);
1167 free (table->notes);
1169 for (size_t i = 0; i < table->n_dimensions; i++)
1170 pivot_dimension_destroy (table->dimensions[i]);
1171 free (table->dimensions);
1173 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1174 free (table->axes[i].dimensions);
1176 struct pivot_cell *cell, *next_cell;
1177 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1179 pivot_table_delete_cell (table, cell);
1181 hmap_destroy (&table->cells);
1186 /* Returns true if TABLE has more than one owner. A pivot table that is shared
1187 among multiple owners must not be modified. */
1189 pivot_table_is_shared (const struct pivot_table *table)
1191 return table->ref_cnt > 1;
1195 pivot_table_set_value__ (struct pivot_value **dstp, struct pivot_value *src)
1197 pivot_value_destroy (*dstp);
1201 /* Changes the title of TABLE to TITLE. Takes ownership of TITLE. */
1203 pivot_table_set_title (struct pivot_table *table, struct pivot_value *title)
1205 pivot_table_set_value__ (&table->title, title);
1208 /* Changes the subtype of TABLE to SUBTYPE. Takes ownership of SUBTYPE. */
1210 pivot_table_set_subtype (struct pivot_table *table, struct pivot_value *subtype)
1212 pivot_table_set_value__ (&table->subtype, subtype);
1215 /* Changes the corner text of TABLE to CORNER_TEXT. Takes ownership of
1218 pivot_table_set_corner_text (struct pivot_table *table,
1219 struct pivot_value *corner_text)
1221 pivot_table_set_value__ (&table->corner_text, corner_text);
1224 /* Changes the caption of TABLE to CAPTION. Takes ownership of CAPTION. */
1226 pivot_table_set_caption (struct pivot_table *table, struct pivot_value *caption)
1228 pivot_table_set_value__ (&table->caption, caption);
1231 /* Swaps axes A and B in TABLE. */
1233 pivot_table_swap_axes (struct pivot_table *table,
1234 enum pivot_axis_type a, enum pivot_axis_type b)
1239 struct pivot_axis tmp = table->axes[a];
1240 table->axes[a] = table->axes[b];
1241 table->axes[b] = tmp;
1243 for (int a = 0; a < PIVOT_N_AXES; a++)
1245 struct pivot_axis *axis = &table->axes[a];
1246 for (size_t d = 0; d < axis->n_dimensions; d++)
1247 axis->dimensions[d]->axis_type = a;
1250 if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1252 free (table->current_layer);
1253 table->current_layer = xzalloc (
1254 table->axes[PIVOT_AXIS_LAYER].n_dimensions
1255 * sizeof *table->current_layer);
1259 /* Swaps the row and column axes in TABLE. */
1261 pivot_table_transpose (struct pivot_table *table)
1263 pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1267 pivot_table_update_axes (struct pivot_table *table)
1269 for (int a = 0; a < PIVOT_N_AXES; a++)
1271 struct pivot_axis *axis = &table->axes[a];
1273 for (size_t d = 0; d < axis->n_dimensions; d++)
1275 struct pivot_dimension *dim = axis->dimensions[d];
1282 /* Moves DIM from its current location in TABLE to POS within AXIS. POS of 0
1283 is the innermost dimension, 1 is the next one out, and so on. */
1285 pivot_table_move_dimension (struct pivot_table *table,
1286 struct pivot_dimension *dim,
1287 enum pivot_axis_type axis, size_t pos)
1289 assert (dim->table == table);
1291 struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1292 struct pivot_axis *new_axis = &table->axes[axis];
1293 pos = MIN (pos, new_axis->n_dimensions);
1295 if (old_axis == new_axis && pos == dim->level)
1301 /* Update the current layer, if necessary. If we're moving within the layer
1302 axis, preserve the current layer. */
1303 if (dim->axis_type == PIVOT_AXIS_LAYER)
1305 if (axis == PIVOT_AXIS_LAYER)
1307 /* Rearranging the layer axis. */
1308 move_element (table->current_layer, old_axis->n_dimensions,
1309 sizeof *table->current_layer,
1314 /* A layer is becoming a row or column. */
1315 remove_element (table->current_layer, old_axis->n_dimensions,
1316 sizeof *table->current_layer, dim->level);
1319 else if (axis == PIVOT_AXIS_LAYER)
1321 /* A row or column is becoming a layer. */
1322 table->current_layer = xrealloc (
1323 table->current_layer,
1324 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1325 insert_element (table->current_layer, new_axis->n_dimensions,
1326 sizeof *table->current_layer, pos);
1327 table->current_layer[pos] = 0;
1330 /* Remove DIM from its current axis. */
1331 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1332 sizeof *old_axis->dimensions, dim->level);
1333 old_axis->n_dimensions--;
1335 /* Insert DIM into its new axis. */
1336 new_axis->dimensions = xrealloc (
1337 new_axis->dimensions,
1338 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1339 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1340 sizeof *new_axis->dimensions, pos);
1341 new_axis->dimensions[pos] = dim;
1342 new_axis->n_dimensions++;
1344 pivot_table_update_axes (table);
1348 const struct pivot_table_look *
1349 pivot_table_get_look (const struct pivot_table *table)
1355 pivot_table_set_look (struct pivot_table *table,
1356 const struct pivot_table_look *look)
1358 pivot_table_look_unref (table->look);
1359 table->look = pivot_table_look_ref (look);
1362 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1363 WV, which should be the weight variable for the dictionary whose data or
1364 statistics are being put into TABLE.
1366 This has no effect if WV is NULL. */
1368 pivot_table_set_weight_var (struct pivot_table *table,
1369 const struct variable *wv)
1372 pivot_table_set_weight_format (table, var_get_print_format (wv));
1375 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1376 format for the dictionary whose data or statistics are being put into TABLE.
1378 This has no effect if WFMT is NULL. */
1380 pivot_table_set_weight_format (struct pivot_table *table,
1381 const struct fmt_spec *wfmt)
1384 table->weight_format = *wfmt;
1387 /* Returns true if TABLE has no cells, false otherwise. */
1389 pivot_table_is_empty (const struct pivot_table *table)
1391 return hmap_is_empty (&table->cells);
1395 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1397 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1401 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1403 for (size_t i = 0; i < n; i++)
1410 static struct pivot_cell *
1411 pivot_table_lookup_cell__ (const struct pivot_table *table,
1412 const size_t *dindexes, unsigned int hash)
1414 struct pivot_cell *cell;
1415 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1417 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1422 static struct pivot_cell *
1423 pivot_cell_allocate (size_t n_idx)
1425 struct pivot_cell *cell UNUSED;
1426 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1429 static struct pivot_cell *
1430 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1432 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1433 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1436 cell = pivot_cell_allocate (table->n_dimensions);
1437 for (size_t i = 0; i < table->n_dimensions; i++)
1438 cell->idx[i] = dindexes[i];
1440 hmap_insert (&table->cells, &cell->hmap_node, hash);
1445 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1446 DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of
1449 If VALUE is a numeric value without a specified format, this function checks
1450 each of the categories designated by DINDEXES[] and takes the format from
1451 the first category with a result class. If none has a result class, uses
1452 the overall default numeric format. */
1454 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1455 struct pivot_value *value)
1457 assert (n == table->n_dimensions);
1458 for (size_t i = 0; i < n; i++)
1459 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1461 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1463 for (size_t i = 0; i < table->n_dimensions; i++)
1465 const struct pivot_dimension *d = table->dimensions[i];
1466 if (dindexes[i] < d->n_leaves)
1468 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1471 value->numeric.format = c->format;
1472 value->numeric.honor_small = c->honor_small;
1477 value->numeric.format = *settings_get_format ();
1478 value->numeric.honor_small = true;
1483 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1484 pivot_value_destroy (cell->value);
1485 cell->value = value;
1488 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1489 dimension. Takes ownership of VALUE. */
1491 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1492 struct pivot_value *value)
1494 size_t dindexes[] = { idx1 };
1495 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1498 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1499 dimensions. Takes ownership of VALUE. */
1501 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1502 struct pivot_value *value)
1504 size_t dindexes[] = { idx1, idx2 };
1505 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1508 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1509 have 3 dimensions. Takes ownership of VALUE. */
1511 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1512 size_t idx3, struct pivot_value *value)
1514 size_t dindexes[] = { idx1, idx2, idx3 };
1515 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1518 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1519 must have 4 dimensions. Takes ownership of VALUE. */
1521 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1522 size_t idx3, size_t idx4, struct pivot_value *value)
1524 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1525 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1528 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1529 automatically assigned marker.
1531 The footnote will only appear in output if it is referenced. Use
1532 pivot_value_add_footnote() to add a reference to the footnote. */
1533 struct pivot_footnote *
1534 pivot_table_create_footnote (struct pivot_table *table,
1535 struct pivot_value *content)
1537 return pivot_table_create_footnote__ (table, table->n_footnotes,
1542 pivot_footnote_format_marker (const struct pivot_footnote *f,
1543 const struct pivot_table *pt,
1547 pivot_value_format_body (f->marker, pt, s);
1548 else if (pt->look->show_numeric_markers)
1549 ds_put_format (s, "%zu", f->idx + 1);
1552 char text[INT_BUFSIZE_BOUND (size_t)];
1553 str_format_26adic (f->idx + 1, false, text, sizeof text);
1554 ds_put_cstr (s, text);
1559 pivot_footnote_marker_string (const struct pivot_footnote *f,
1560 const struct pivot_table *pt)
1562 struct string s = DS_EMPTY_INITIALIZER;
1563 pivot_footnote_format_marker (f, pt, &s);
1564 return ds_steal_cstr (&s);
1567 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1568 all lower indexes as a side effect). If MARKER is nonnull, sets the
1569 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1570 struct pivot_footnote *
1571 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1572 struct pivot_value *marker,
1573 struct pivot_value *content)
1575 if (idx >= table->n_footnotes)
1577 while (idx >= table->allocated_footnotes)
1578 table->footnotes = x2nrealloc (table->footnotes,
1579 &table->allocated_footnotes,
1580 sizeof *table->footnotes);
1581 while (idx >= table->n_footnotes)
1583 struct pivot_footnote *f = xmalloc (sizeof *f);
1584 *f = (struct pivot_footnote) {
1585 .idx = table->n_footnotes,
1588 table->footnotes[table->n_footnotes++] = f;
1592 struct pivot_footnote *f = table->footnotes[idx];
1595 pivot_value_destroy (f->marker);
1600 pivot_value_destroy (f->content);
1601 f->content = content;
1606 /* Frees the data owned by F. */
1608 pivot_footnote_destroy (struct pivot_footnote *f)
1612 pivot_value_destroy (f->content);
1613 pivot_value_destroy (f->marker);
1618 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1619 indexes for each dimension in TABLE in DINDEXES[]. */
1621 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1622 const size_t *pindexes[PIVOT_N_AXES],
1623 size_t dindexes[/* table->n_dimensions */])
1625 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1627 const struct pivot_axis *axis = &table->axes[i];
1629 for (size_t j = 0; j < axis->n_dimensions; j++)
1631 const struct pivot_dimension *d = axis->dimensions[j];
1632 size_t pindex = pindexes[i][j];
1633 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1639 pivot_table_enumerate_axis (const struct pivot_table *table,
1640 enum pivot_axis_type axis_type,
1641 const size_t *layer_indexes, bool omit_empty,
1644 const struct pivot_axis *axis = &table->axes[axis_type];
1645 if (!axis->n_dimensions)
1647 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1649 enumeration[1] = SIZE_MAX;
1654 else if (!axis->extent)
1656 size_t *enumeration = xmalloc (sizeof *enumeration);
1657 *enumeration = SIZE_MAX;
1663 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1664 axis->n_dimensions), 1),
1665 sizeof *enumeration);
1666 size_t *p = enumeration;
1667 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1669 size_t *axis_indexes;
1670 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1674 enum pivot_axis_type axis2_type
1675 = pivot_axis_type_transpose (axis_type);
1677 size_t *axis2_indexes;
1678 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1680 const size_t *pindexes[PIVOT_N_AXES];
1681 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1682 pindexes[axis_type] = axis_indexes;
1683 pindexes[axis2_type] = axis2_indexes;
1684 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1685 if (pivot_table_get (table, dindexes))
1691 free (axis2_indexes);
1694 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1695 p += axis->n_dimensions;
1697 if (omit_empty && p == enumeration)
1699 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1701 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1702 p += axis->n_dimensions;
1707 *n = (p - enumeration) / axis->n_dimensions;
1713 static struct pivot_cell *
1714 pivot_table_lookup_cell (const struct pivot_table *table,
1715 const size_t *dindexes)
1717 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1718 return pivot_table_lookup_cell__ (table, dindexes, hash);
1721 const struct pivot_value *
1722 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1724 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1725 return cell ? cell->value : NULL;
1728 struct pivot_value *
1729 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1731 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1733 cell->value = pivot_value_new_user_text ("", -1);
1738 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1740 hmap_delete (&table->cells, &cell->hmap_node);
1741 pivot_value_destroy (cell->value);
1746 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1748 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1751 pivot_table_delete_cell (table, cell);
1759 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1761 if (pivot_category_is_group (category) && category->n_subs)
1762 for (size_t i = 0; i < category->n_subs; i++)
1763 distribute_extra_depth (category->subs[i], extra_depth);
1765 category->extra_depth += extra_depth;
1769 pivot_category_assign_label_depth (struct pivot_category *category,
1770 bool dimension_labels_in_corner)
1772 category->extra_depth = 0;
1774 if (pivot_category_is_group (category))
1777 for (size_t i = 0; i < category->n_subs; i++)
1779 pivot_category_assign_label_depth (category->subs[i], false);
1780 depth = MAX (depth, category->subs[i]->label_depth);
1783 for (size_t i = 0; i < category->n_subs; i++)
1785 struct pivot_category *sub = category->subs[i];
1787 size_t extra_depth = depth - sub->label_depth;
1789 distribute_extra_depth (sub, extra_depth);
1791 sub->label_depth = depth;
1794 category->show_label_in_corner = (category->show_label
1795 && dimension_labels_in_corner);
1796 category->label_depth
1797 = (category->show_label && !category->show_label_in_corner
1798 ? depth + 1 : depth);
1801 category->label_depth = 1;
1805 pivot_axis_assign_label_depth (struct pivot_table *table,
1806 enum pivot_axis_type axis_type,
1807 bool dimension_labels_in_corner)
1809 struct pivot_axis *axis = &table->axes[axis_type];
1810 bool any_label_shown_in_corner = false;
1811 axis->label_depth = 0;
1813 for (size_t i = 0; i < axis->n_dimensions; i++)
1815 struct pivot_dimension *d = axis->dimensions[i];
1816 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1817 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1818 axis->label_depth += d->label_depth;
1819 axis->extent *= d->n_leaves;
1821 if (d->root->show_label_in_corner)
1822 any_label_shown_in_corner = true;
1824 return any_label_shown_in_corner;
1828 pivot_table_assign_label_depth (struct pivot_table *table)
1830 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1831 if (pivot_axis_assign_label_depth (
1832 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1833 && !table->corner_text))
1834 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1835 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1836 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1840 indent (int indentation)
1842 for (int i = 0; i < indentation * 2; i++)
1847 pivot_value_dump (const struct pivot_value *value,
1848 const struct pivot_table *pt)
1850 char *s = pivot_value_to_string (value, pt);
1856 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1857 const struct pivot_table *pt, int indentation)
1861 indent (indentation);
1862 printf ("%s: ", name);
1863 pivot_value_dump (value, pt);
1869 pivot_table_dump_string (const char *string, const char *name, int indentation)
1873 indent (indentation);
1874 printf ("%s: %s\n", name, string);
1879 pivot_category_dump (const struct pivot_category *c,
1880 const struct pivot_table *pt, int indentation)
1882 indent (indentation);
1883 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1884 pivot_value_dump (c->name, pt);
1887 if (pivot_category_is_leaf (c))
1888 printf ("data_index=%zu\n", c->data_index);
1891 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1894 for (size_t i = 0; i < c->n_subs; i++)
1895 pivot_category_dump (c->subs[i], pt, indentation + 1);
1900 pivot_dimension_dump (const struct pivot_dimension *d,
1901 const struct pivot_table *pt, int indentation)
1903 indent (indentation);
1904 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1905 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1907 pivot_category_dump (d->root, pt, indentation + 1);
1911 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1914 indent (indentation);
1915 printf ("%s: ", pivot_area_to_string (area));
1916 font_style_dump (&a->font_style);
1918 cell_style_dump (&a->cell_style);
1923 table_border_style_dump (enum pivot_border border,
1924 const struct table_border_style *b, int indentation)
1926 indent (indentation);
1927 printf ("%s: %s ", pivot_border_to_string (border),
1928 table_stroke_to_string (b->stroke));
1929 cell_color_dump (&b->color);
1934 compose_headings (const struct pivot_table *pt,
1935 const struct pivot_axis *axis,
1936 const size_t *column_enumeration)
1938 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1941 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1942 for (size_t i = 0; i < axis->label_depth; i++)
1943 headings[i] = xcalloc (axis->extent, sizeof **headings);
1945 const size_t *indexes;
1947 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1949 int row = axis->label_depth - 1;
1950 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1952 const struct pivot_dimension *d = axis->dimensions[dim_index];
1953 if (d->hide_all_labels)
1955 for (const struct pivot_category *c
1956 = d->presentation_leaves[indexes[dim_index]];
1960 if (pivot_category_is_leaf (c) || (c->show_label
1961 && !c->show_label_in_corner))
1963 headings[row][column] = pivot_value_to_string (c->name, pt);
1964 if (!*headings[row][column])
1965 headings[row][column] = xstrdup ("<blank>");
1977 free_headings (const struct pivot_axis *axis, char ***headings)
1981 for (size_t i = 0; i < axis->label_depth; i++)
1983 for (size_t j = 0; j < axis->extent; j++)
1984 free (headings[i][j]);
1991 pivot_table_sizing_dump (const char *name,
1992 const int width_ranges[2],
1993 const struct pivot_table_sizing *s,
1996 indent (indentation);
1997 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
2000 indent (indentation + 1);
2001 printf ("%s widths:", name);
2002 for (size_t i = 0; i < s->n_widths; i++)
2003 printf (" %d", s->widths[i]);
2008 indent (indentation + 1);
2009 printf ("break after %ss:", name);
2010 for (size_t i = 0; i < s->n_breaks; i++)
2011 printf (" %zu", s->breaks[i]);
2016 indent (indentation + 1);
2017 printf ("keep %ss together:", name);
2018 for (size_t i = 0; i < s->n_keeps; i++)
2019 printf (" [%zu,%zu]",
2021 s->keeps[i].ofs + s->keeps[i].n - 1);
2027 pivot_table_dump (const struct pivot_table *table, int indentation)
2032 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
2034 pivot_table_dump_value (table->title, "title", table, indentation);
2035 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
2036 pivot_table_dump_string (table->command_c, "command", indentation);
2037 pivot_table_dump_string (table->dataset, "dataset", indentation);
2038 pivot_table_dump_string (table->datafile, "datafile", indentation);
2039 pivot_table_dump_string (table->notes, "notes", indentation);
2040 pivot_table_dump_string (table->look->name, "table-look", indentation);
2043 indent (indentation);
2045 struct tm *tm = localtime (&table->date);
2046 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2047 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2051 indent (indentation);
2052 printf ("sizing:\n");
2053 pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2054 &table->sizing[TABLE_HORZ], indentation + 1);
2055 pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2056 &table->sizing[TABLE_VERT], indentation + 1);
2058 indent (indentation);
2059 printf ("areas:\n");
2060 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2061 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2063 indent (indentation);
2064 printf ("borders:\n");
2065 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2066 table_border_style_dump (border, &table->look->borders[border],
2069 for (size_t i = 0; i < table->n_dimensions; i++)
2070 pivot_dimension_dump (table->dimensions[i], table, indentation);
2072 /* Presentation and data indexes. */
2073 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2075 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2076 if (layer_axis->n_dimensions)
2078 indent (indentation);
2079 printf ("current layer:");
2081 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2083 const struct pivot_dimension *d = layer_axis->dimensions[i];
2084 char *name = pivot_value_to_string (d->root->name, table);
2085 char *value = pivot_value_to_string (
2086 d->data_leaves[table->current_layer[i]]->name, table);
2087 printf (" %s=%s", name, value);
2095 size_t *layer_indexes;
2096 size_t layer_iteration = 0;
2097 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2099 indent (indentation);
2100 printf ("layer %zu:", layer_iteration++);
2102 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2103 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2105 const struct pivot_dimension *d = layer_axis->dimensions[i];
2107 fputs (i == 0 ? " " : ", ", stdout);
2108 pivot_value_dump (d->root->name, table);
2109 fputs (" =", stdout);
2111 struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
2113 for (const struct pivot_category *c
2114 = d->presentation_leaves[layer_indexes[i]];
2118 if (pivot_category_is_leaf (c) || c->show_label)
2119 names[n_names++] = c->name;
2122 for (size_t i = n_names; i-- > 0;)
2125 pivot_value_dump (names[i], table);
2131 size_t *column_enumeration = pivot_table_enumerate_axis (
2132 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2133 size_t *row_enumeration = pivot_table_enumerate_axis (
2134 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2136 /* Print column headings.
2138 Ordinarily the test for nonnull 'column_headings' would be
2139 unnecessary, because 'column_headings' is null only if the axis's
2140 label_depth is 0, but there is a special case for the column axis only
2141 in pivot_table_assign_label_depth(). */
2142 char ***column_headings = compose_headings (
2143 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2144 if (column_headings)
2146 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2148 indent (indentation + 1);
2149 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2152 fputs ("; ", stdout);
2153 if (column_headings && column_headings[y] && column_headings[y][x])
2154 fputs (column_headings[y][x], stdout);
2158 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2161 indent (indentation + 1);
2162 printf ("-----------------------------------------------\n");
2164 char ***row_headings = compose_headings (
2165 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2168 const size_t *pindexes[PIVOT_N_AXES]
2169 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2170 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2171 &table->axes[PIVOT_AXIS_ROW])
2173 indent (indentation + 1);
2176 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2179 fputs ("; ", stdout);
2180 if (row_headings[y][x])
2181 fputs (row_headings[y][x], stdout);
2187 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2189 &table->axes[PIVOT_AXIS_COLUMN])
2194 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2195 const struct pivot_value *value = pivot_table_get (
2198 pivot_value_dump (value, table);
2205 free (column_enumeration);
2206 free (row_enumeration);
2207 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2210 pivot_table_dump_value (table->caption, "caption", table, indentation);
2212 for (size_t i = 0; i < table->n_footnotes; i++)
2214 const struct pivot_footnote *f = table->footnotes[i];
2215 indent (indentation);
2218 pivot_value_dump (f->marker, table);
2220 printf ("%zu", f->idx);
2222 pivot_value_dump (f->content, table);
2230 consume_int (const char *p, size_t *n)
2233 while (c_isdigit (*p))
2234 *n = *n * 10 + (*p++ - '0');
2239 pivot_format_inner_template (struct string *out, const char *template,
2241 struct pivot_value **values, size_t n_values,
2242 const struct pivot_table *pt)
2244 size_t args_consumed = 0;
2245 while (*template && *template != ':')
2247 if (*template == '\\' && template[1])
2249 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2252 else if (*template == escape)
2255 template = consume_int (template + 1, &index);
2256 if (index >= 1 && index <= n_values)
2258 pivot_value_format (values[index - 1], pt, out);
2259 args_consumed = MAX (args_consumed, index);
2263 ds_put_byte (out, *template++);
2265 return args_consumed;
2269 pivot_extract_inner_template (const char *template, const char **p)
2275 if (*template == '\\' && template[1] != '\0')
2277 else if (*template == ':')
2278 return template + 1;
2279 else if (*template == '\0')
2287 pivot_format_template (struct string *out, const char *template,
2288 const struct pivot_argument *args, size_t n_args,
2289 const struct pivot_table *pt)
2293 if (*template == '\\' && template[1] != '\0')
2295 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2298 else if (*template == '^')
2301 template = consume_int (template + 1, &index);
2302 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2303 pivot_value_format (args[index - 1].values[0], pt, out);
2305 else if (*template == '[')
2307 const char *tmpl[2];
2308 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2309 template = pivot_extract_inner_template (template, &tmpl[1]);
2310 template += *template == ']';
2313 template = consume_int (template, &index);
2314 if (index < 1 || index > n_args)
2317 const struct pivot_argument *arg = &args[index - 1];
2318 size_t left = arg->n;
2321 struct pivot_value **values = arg->values + (arg->n - left);
2322 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2323 char escape = "%^"[tmpl_idx];
2324 size_t used = pivot_format_inner_template (
2325 out, tmpl[tmpl_idx], escape, values, left, pt);
2326 if (!used || used > left)
2332 ds_put_byte (out, *template++);
2336 static enum settings_value_show
2337 interpret_show (enum settings_value_show global_show,
2338 enum settings_value_show table_show,
2339 enum settings_value_show value_show,
2342 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2343 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2344 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2348 /* Appends to OUT the actual text content from the given Pango MARKUP. */
2350 get_text_from_markup (const char *markup, struct string *out)
2352 xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
2355 ds_put_cstr (out, markup);
2359 xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
2360 xmlParseChunk (parser, markup, strlen (markup), false);
2361 xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
2363 if (parser->wellFormed)
2365 xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
2366 ds_put_cstr (out, CHAR_CAST (char *, s));
2370 ds_put_cstr (out, markup);
2371 xmlFreeDoc (parser->myDoc);
2372 xmlFreeParserCtxt (parser);
2375 /* Appends a text representation of the body of VALUE to OUT. Settings on
2376 PT control whether variable and value labels are included.
2378 The "body" omits subscripts and superscripts and footnotes.
2380 Returns true if OUT is a number (or a number plus a value label), false
2383 pivot_value_format_body (const struct pivot_value *value,
2384 const struct pivot_table *pt,
2387 enum settings_value_show show;
2388 bool numeric = false;
2390 switch (value->type)
2392 case PIVOT_VALUE_NUMERIC:
2393 show = interpret_show (settings_get_show_values (),
2395 value->numeric.show,
2396 value->numeric.value_label != NULL);
2397 if (show & SETTINGS_VALUE_SHOW_VALUE)
2399 const struct fmt_spec *f = &value->numeric.format;
2400 const struct fmt_spec *format
2402 && value->numeric.honor_small
2403 && value->numeric.x != 0
2404 && fabs (value->numeric.x) < pt->small
2405 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2408 char *s = data_out (&(union value) { .f = value->numeric.x },
2409 "UTF-8", format, &pt->settings);
2410 ds_put_cstr (out, s + strspn (s, " "));
2413 if (show & SETTINGS_VALUE_SHOW_LABEL)
2415 if (show & SETTINGS_VALUE_SHOW_VALUE)
2416 ds_put_byte (out, ' ');
2417 ds_put_cstr (out, value->numeric.value_label);
2419 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2422 case PIVOT_VALUE_STRING:
2423 show = interpret_show (settings_get_show_values (),
2426 value->string.value_label != NULL);
2427 if (show & SETTINGS_VALUE_SHOW_VALUE)
2429 if (value->string.hex)
2431 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2433 ds_put_format (out, "%02X", *p);
2436 ds_put_cstr (out, value->string.s);
2438 if (show & SETTINGS_VALUE_SHOW_LABEL)
2440 if (show & SETTINGS_VALUE_SHOW_VALUE)
2441 ds_put_byte (out, ' ');
2442 ds_put_cstr (out, value->string.value_label);
2446 case PIVOT_VALUE_VARIABLE:
2447 show = interpret_show (settings_get_show_variables (),
2449 value->variable.show,
2450 value->variable.var_label != NULL);
2451 if (show & SETTINGS_VALUE_SHOW_VALUE)
2452 ds_put_cstr (out, value->variable.var_name);
2453 if (show & SETTINGS_VALUE_SHOW_LABEL)
2455 if (show & SETTINGS_VALUE_SHOW_VALUE)
2456 ds_put_byte (out, ' ');
2457 ds_put_cstr (out, value->variable.var_label);
2461 case PIVOT_VALUE_TEXT:
2462 if (value->ex && value->ex->font_style && value->ex->font_style->markup)
2463 get_text_from_markup (value->text.local, out);
2465 ds_put_cstr (out, value->text.local);
2468 case PIVOT_VALUE_TEMPLATE:
2469 pivot_format_template (out, value->template.local, value->template.args,
2470 value->template.n_args, pt);
2477 /* Appends a text representation of VALUE to OUT. Settings on
2478 PT control whether variable and value labels are included.
2480 Subscripts and footnotes are included.
2482 Returns true if OUT is a number (or a number plus a value label), false
2485 pivot_value_format (const struct pivot_value *value,
2486 const struct pivot_table *pt,
2489 bool numeric = pivot_value_format_body (value, pt, out);
2491 const struct pivot_value_ex *ex = value->ex;
2494 if (ex->n_subscripts)
2496 for (size_t i = 0; i < ex->n_subscripts; i++)
2497 ds_put_format (out, "%c%s", i ? ',' : '_', ex->subscripts[i]);
2500 for (size_t i = 0; i < ex->n_footnotes; i++)
2502 ds_put_byte (out, '[');
2504 size_t idx = ex->footnote_indexes[i];
2505 const struct pivot_footnote *f = pt->footnotes[idx];
2506 pivot_footnote_format_marker (f, pt, out);
2508 ds_put_byte (out, ']');
2515 /* Returns a text representation of VALUE. The caller must free the string,
2518 pivot_value_to_string (const struct pivot_value *value,
2519 const struct pivot_table *pt)
2521 struct string s = DS_EMPTY_INITIALIZER;
2522 pivot_value_format (value, pt, &s);
2523 return ds_steal_cstr (&s);
2527 pivot_value_to_string_defaults (const struct pivot_value *value)
2529 static const struct pivot_table pt = {
2530 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2531 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2532 .settings = FMT_SETTINGS_INIT,
2534 return pivot_value_to_string (value, &pt);
2537 struct pivot_value *
2538 pivot_value_clone (const struct pivot_value *old)
2543 struct pivot_value *new = xmemdup (old, sizeof *new);
2545 new->ex = pivot_value_ex_clone (old->ex);
2549 case PIVOT_VALUE_NUMERIC:
2550 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2551 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2554 case PIVOT_VALUE_STRING:
2555 new->string.s = xstrdup (new->string.s);
2556 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2557 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2560 case PIVOT_VALUE_VARIABLE:
2561 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2562 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2565 case PIVOT_VALUE_TEXT:
2566 new->text.local = xstrdup (old->text.local);
2567 new->text.c = (old->text.c == old->text.local ? new->text.local
2568 : xstrdup_if_nonnull (old->text.c));
2569 new->text.id = (old->text.id == old->text.local ? new->text.local
2570 : old->text.id == old->text.c ? new->text.c
2571 : xstrdup_if_nonnull (old->text.id));
2574 case PIVOT_VALUE_TEMPLATE:
2575 new->template.local = xstrdup (old->template.local);
2576 new->template.id = (old->template.id == old->template.local
2577 ? new->template.local
2578 : xstrdup (old->template.id));
2579 new->template.args = xmalloc (new->template.n_args
2580 * sizeof *new->template.args);
2581 for (size_t i = 0; i < old->template.n_args; i++)
2582 pivot_argument_copy (&new->template.args[i],
2583 &old->template.args[i]);
2592 /* Frees the data owned by V. */
2594 pivot_value_destroy (struct pivot_value *value)
2598 pivot_value_ex_destroy (value->ex);
2599 switch (value->type)
2601 case PIVOT_VALUE_NUMERIC:
2602 free (value->numeric.var_name);
2603 free (value->numeric.value_label);
2606 case PIVOT_VALUE_STRING:
2607 free (value->string.s);
2608 free (value->string.var_name);
2609 free (value->string.value_label);
2612 case PIVOT_VALUE_VARIABLE:
2613 free (value->variable.var_name);
2614 free (value->variable.var_label);
2617 case PIVOT_VALUE_TEXT:
2618 free (value->text.local);
2619 if (value->text.c != value->text.local)
2620 free (value->text.c);
2621 if (value->text.id != value->text.local
2622 && value->text.id != value->text.c)
2623 free (value->text.id);
2626 case PIVOT_VALUE_TEMPLATE:
2627 free (value->template.local);
2628 if (value->template.id != value->template.local)
2629 free (value->template.id);
2630 for (size_t i = 0; i < value->template.n_args; i++)
2631 pivot_argument_uninit (&value->template.args[i]);
2632 free (value->template.args);
2642 /* Sets AREA to the style to use for VALUE, with defaults coming from
2643 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2645 pivot_value_get_style (struct pivot_value *value,
2646 const struct font_style *base_font_style,
2647 const struct cell_style *base_cell_style,
2648 struct table_area_style *area)
2650 const struct pivot_value_ex *ex = pivot_value_ex (value);
2651 font_style_copy (NULL, &area->font_style,
2652 ex->font_style ? ex->font_style : base_font_style);
2653 area->cell_style = *(ex->cell_style ? ex->cell_style : base_cell_style);
2656 /* Copies AREA into VALUE's style. */
2658 pivot_value_set_style (struct pivot_value *value,
2659 const struct table_area_style *area)
2661 pivot_value_set_font_style (value, &area->font_style);
2662 pivot_value_set_cell_style (value, &area->cell_style);
2666 pivot_value_set_font_style (struct pivot_value *value,
2667 const struct font_style *font_style)
2669 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2671 font_style_uninit (ex->font_style);
2673 ex->font_style = xmalloc (sizeof *ex->font_style);
2674 font_style_copy (NULL, ex->font_style, font_style);
2678 pivot_value_set_cell_style (struct pivot_value *value,
2679 const struct cell_style *cell_style)
2681 struct pivot_value_ex *ex = pivot_value_ex_rw (value);
2682 if (!ex->cell_style)
2683 ex->cell_style = xmalloc (sizeof *ex->cell_style);
2684 *ex->cell_style = *cell_style;
2688 pivot_argument_copy (struct pivot_argument *dst,
2689 const struct pivot_argument *src)
2691 *dst = (struct pivot_argument) {
2693 .values = xmalloc (src->n * sizeof *dst->values),
2696 for (size_t i = 0; i < src->n; i++)
2697 dst->values[i] = pivot_value_clone (src->values[i]);
2700 /* Frees the data owned by ARG (but not ARG itself). */
2702 pivot_argument_uninit (struct pivot_argument *arg)
2706 for (size_t i = 0; i < arg->n; i++)
2707 pivot_value_destroy (arg->values[i]);
2712 /* Creates and returns a new pivot_value whose contents is the null-terminated
2713 string TEXT. Takes ownership of TEXT.
2715 This function is for text strings provided by the user (with the exception
2716 that pivot_value_new_variable() should be used for variable names). For
2717 strings that are part of the PSPP user interface, such as names of
2718 procedures, statistics, annotations, error messages, etc., use
2719 pivot_value_new_text(). */
2720 struct pivot_value *
2721 pivot_value_new_user_text_nocopy (char *text)
2723 struct pivot_value *value = xmalloc (sizeof *value);
2724 *value = (struct pivot_value) {
2726 .type = PIVOT_VALUE_TEXT,
2730 .user_provided = true,
2736 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2737 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2740 This function is for text strings provided by the user (with the exception
2741 that pivot_value_new_variable() should be used for variable names). For
2742 strings that are part of the PSPP user interface, such as names of
2743 procedures, statistics, annotations, error messages, etc., use
2744 pivot_value_new_text().
2746 The caller retains ownership of TEXT. */
2747 struct pivot_value *
2748 pivot_value_new_user_text (const char *text, size_t length)
2750 return pivot_value_new_user_text_nocopy (
2751 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2754 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2755 a translatable string, but not actually translated yet, e.g. enclosed in
2756 N_(). This function is for text strings that are part of the PSPP user
2757 interface, such as names of procedures, statistics, annotations, error
2758 messages, etc. For strings that come from the user, use
2759 pivot_value_new_user_text(). */
2760 struct pivot_value *
2761 pivot_value_new_text (const char *text)
2763 char *c = xstrdup (text);
2764 char *local = xstrdup (gettext (c));
2766 struct pivot_value *value = xmalloc (sizeof *value);
2767 *value = (struct pivot_value) {
2769 .type = PIVOT_VALUE_TEXT,
2773 .user_provided = false,
2779 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2781 struct pivot_value * PRINTF_FORMAT (1, 2)
2782 pivot_value_new_text_format (const char *format, ...)
2785 va_start (args, format);
2786 char *c = xvasprintf (format, args);
2789 va_start (args, format);
2790 char *local = xvasprintf (gettext (format), args);
2793 struct pivot_value *value = xmalloc (sizeof *value);
2794 *value = (struct pivot_value) {
2796 .type = PIVOT_VALUE_TEXT,
2800 .user_provided = false,
2806 /* Returns a new pivot_value that represents X.
2808 The format to use for X is unspecified. Usually the easiest way to specify
2809 a format is through assigning a result class to one of the categories that
2810 the pivot_value will end up in. If that is not suitable, then the caller
2811 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2812 struct pivot_value *
2813 pivot_value_new_number (double x)
2815 struct pivot_value *value = xmalloc (sizeof *value);
2816 *value = (struct pivot_value) {
2818 .type = PIVOT_VALUE_NUMERIC,
2825 /* Returns a new pivot_value that represents X, formatted as an integer. */
2826 struct pivot_value *
2827 pivot_value_new_integer (double x)
2829 struct pivot_value *value = pivot_value_new_number (x);
2830 value->numeric.format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
2834 /* Returns a new pivot_value that represents VALUE, formatted as for
2836 struct pivot_value *
2837 pivot_value_new_var_value (const struct variable *variable,
2838 const union value *value)
2840 struct pivot_value *pv = pivot_value_new_value (
2841 value, var_get_width (variable), var_get_print_format (variable),
2842 var_get_encoding (variable));
2844 char *var_name = xstrdup (var_get_name (variable));
2845 if (var_is_alpha (variable))
2846 pv->string.var_name = var_name;
2848 pv->numeric.var_name = var_name;
2850 const char *label = var_lookup_value_label (variable, value);
2853 if (var_is_alpha (variable))
2854 pv->string.value_label = xstrdup (label);
2856 pv->numeric.value_label = xstrdup (label);
2862 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2863 formatted with FORMAT. For a string value, ENCODING must be its character
2865 struct pivot_value *
2866 pivot_value_new_value (const union value *value, int width,
2867 const struct fmt_spec *format, const char *encoding)
2869 struct pivot_value *pv = XZALLOC (struct pivot_value);
2872 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2874 size_t n = strlen (s);
2875 while (n > 0 && s[n - 1] == ' ')
2878 pv->type = PIVOT_VALUE_STRING;
2880 pv->string.hex = format->type == FMT_AHEX;
2884 pv->type = PIVOT_VALUE_NUMERIC;
2885 pv->numeric.x = value->f;
2886 pv->numeric.format = *format;
2892 /* Returns a new pivot_value for VARIABLE. */
2893 struct pivot_value *
2894 pivot_value_new_variable (const struct variable *variable)
2896 return pivot_value_new_variable__ (var_get_name (variable),
2897 var_get_label (variable));
2900 /* Returns a new pivot_value for a variable with the given NAME and optional
2902 struct pivot_value *
2903 pivot_value_new_variable__ (const char *name, const char *label)
2905 struct pivot_value *value = xmalloc (sizeof *value);
2906 *value = (struct pivot_value) {
2908 .type = PIVOT_VALUE_VARIABLE,
2909 .var_name = xstrdup (name),
2910 .var_label = xstrdup_if_nonempty (label),
2916 /* Attaches a reference to FOOTNOTE to V. */
2918 pivot_value_add_footnote (struct pivot_value *v,
2919 const struct pivot_footnote *footnote)
2921 struct pivot_value_ex *ex = pivot_value_ex_rw (v);
2923 /* Some legacy tables include numerous duplicate footnotes. Suppress
2925 for (size_t i = 0; i < ex->n_footnotes; i++)
2926 if (ex->footnote_indexes[i] == footnote->idx)
2929 ex->footnote_indexes = xrealloc (
2930 ex->footnote_indexes,
2931 (ex->n_footnotes + 1) * sizeof *ex->footnote_indexes);
2932 ex->footnote_indexes[ex->n_footnotes++] = footnote->idx;
2933 pivot_value_sort_footnotes (v);
2937 compare_footnote_indexes (const void *a_, const void *b_)
2939 const size_t *ap = a_;
2940 const size_t *bp = b_;
2943 return a < b ? -1 : a > b;
2946 /* Sorts the footnote references in V in the standard ascending order.
2948 This is only necessary if code adds (plural) footnotes to a pivot_value by
2949 itself, because pivot_value_add_footnote() does it automatically. */
2951 pivot_value_sort_footnotes (struct pivot_value *v)
2953 if (v->ex && v->ex->n_footnotes > 1)
2954 qsort (v->ex->footnote_indexes, v->ex->n_footnotes,
2955 sizeof *v->ex->footnote_indexes, compare_footnote_indexes);
2958 /* If VALUE is a numeric value, and RC is a result class such as
2959 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2961 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2964 if (value->type == PIVOT_VALUE_NUMERIC)
2965 pivot_table_use_rc (table, rc,
2966 &value->numeric.format, &value->numeric.honor_small);
2969 /* pivot_value_ex. */
2971 struct pivot_value_ex *
2972 pivot_value_ex_rw (struct pivot_value *value)
2975 value->ex = xzalloc (sizeof *value->ex);
2979 struct pivot_value_ex *
2980 pivot_value_ex_clone (const struct pivot_value_ex *old)
2982 struct font_style *font_style = NULL;
2983 if (old->font_style)
2985 font_style = xmalloc (sizeof *font_style);
2986 font_style_copy (NULL, font_style, old->font_style);
2989 char **subscripts = NULL;
2990 if (old->n_subscripts)
2992 subscripts = xnmalloc (old->n_subscripts, sizeof *subscripts);
2993 for (size_t i = 0; i < old->n_subscripts; i++)
2994 subscripts[i] = xstrdup (old->subscripts[i]);
2997 struct pivot_value_ex *new = xmalloc (sizeof *new);
2998 *new = (struct pivot_value_ex) {
2999 .font_style = font_style,
3000 .cell_style = (old->cell_style
3001 ? xmemdup (old->cell_style, sizeof *new->cell_style)
3003 .subscripts = subscripts,
3004 .n_subscripts = old->n_subscripts,
3005 .footnote_indexes = (
3007 ? xmemdup (old->footnote_indexes,
3008 old->n_footnotes * sizeof *new->footnote_indexes)
3010 .n_footnotes = old->n_footnotes
3016 pivot_value_ex_destroy (struct pivot_value_ex *ex)
3020 font_style_uninit (ex->font_style);
3021 free (ex->font_style);
3022 free (ex->cell_style);
3023 free (ex->footnote_indexes);
3025 for (size_t i = 0; i < ex->n_subscripts; i++)
3026 free (ex->subscripts[i]);
3027 free (ex->subscripts);