pivot-table: Always put footnote markers in sorted order.
[pspp.git] / src / output / pivot-table.c
blob85ac16d09c1bf2f311bdc7a02dd3cd8c8b8f0c14
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/>. */
17 #include <config.h>
19 #include "output/pivot-table.h"
21 #include <math.h>
22 #include <stdlib.h>
24 #include "data/data-out.h"
25 #include "data/settings.h"
26 #include "data/value.h"
27 #include "data/variable.h"
28 #include "data/file-name.h"
29 #include "libpspp/array.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/hash-functions.h"
32 #include "libpspp/i18n.h"
33 #include "output/driver.h"
34 #include "output/spv/spv-table-look.h"
36 #include "gl/c-ctype.h"
37 #include "gl/configmake.h"
38 #include "gl/intprops.h"
39 #include "gl/minmax.h"
40 #include "gl/relocatable.h"
41 #include "gl/xalloc.h"
42 #include "gl/xmemdup0.h"
43 #include "gl/xsize.h"
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) msgid
49 static void pivot_table_use_rc (const struct pivot_table *, const char *s,
50 struct fmt_spec *, bool *honor_small);
52 /* Pivot table display styling. */
54 /* Returns the name of AREA. */
55 const char *
56 pivot_area_to_string (enum pivot_area area)
58 switch (area)
60 case PIVOT_AREA_TITLE: return "title";
61 case PIVOT_AREA_CAPTION: return "caption";
62 case PIVOT_AREA_FOOTER: return "footer";
63 case PIVOT_AREA_CORNER: return "corner";
64 case PIVOT_AREA_COLUMN_LABELS: return "column labels";
65 case PIVOT_AREA_ROW_LABELS: return "row labels";
66 case PIVOT_AREA_DATA: return "data";
67 case PIVOT_AREA_LAYERS: return "layers";
68 case PIVOT_N_AREAS: default: return "**error**";
72 /* Returns the name of BORDER. */
73 const char *
74 pivot_border_to_string (enum pivot_border border)
76 switch (border)
78 case PIVOT_BORDER_TITLE:
79 return "title";
81 case PIVOT_BORDER_OUTER_LEFT:
82 return "left outer frame";
83 case PIVOT_BORDER_OUTER_TOP:
84 return "top outer frame";
85 case PIVOT_BORDER_OUTER_RIGHT:
86 return "right outer frame";
87 case PIVOT_BORDER_OUTER_BOTTOM:
88 return "bottom outer frame";
90 case PIVOT_BORDER_INNER_LEFT:
91 return "left inner frame";
92 case PIVOT_BORDER_INNER_TOP:
93 return "top inner frame";
94 case PIVOT_BORDER_INNER_RIGHT:
95 return "right inner frame";
96 case PIVOT_BORDER_INNER_BOTTOM:
97 return "bottom inner frame";
99 case PIVOT_BORDER_DATA_LEFT:
100 return "data area left";
101 case PIVOT_BORDER_DATA_TOP:
102 return "data area top";
104 case PIVOT_BORDER_DIM_ROW_HORZ:
105 return "row label horizontal dimension border";
106 case PIVOT_BORDER_DIM_ROW_VERT:
107 return "row label vertical dimension border";
108 case PIVOT_BORDER_DIM_COL_HORZ:
109 return "column label horizontal dimension border";
110 case PIVOT_BORDER_DIM_COL_VERT:
111 return "column label vertical dimension border";
113 case PIVOT_BORDER_CAT_ROW_HORZ:
114 return "row label horizontal category border";
115 case PIVOT_BORDER_CAT_ROW_VERT:
116 return "row label vertical category border";
117 case PIVOT_BORDER_CAT_COL_HORZ:
118 return "column label horizontal category border";
119 case PIVOT_BORDER_CAT_COL_VERT:
120 return "column label vertical category border";
122 case PIVOT_N_BORDERS:
123 default:
124 return "**error**";
128 void
129 pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
131 if (sizing)
133 free (sizing->widths);
134 free (sizing->breaks);
135 free (sizing->keeps);
139 /* Pivot table looks. */
141 static const struct pivot_table_look *
142 default_look (const struct pivot_table_look *new)
144 static struct pivot_table_look *look;
145 if (new)
147 pivot_table_look_unref (look);
148 look = pivot_table_look_ref (new);
150 else if (!look)
152 char *error = pivot_table_look_read ("default.stt", &look);
153 if (error)
155 free (error);
156 look = pivot_table_look_ref (pivot_table_look_builtin_default ());
159 return look;
162 const struct pivot_table_look *
163 pivot_table_look_get_default (void)
165 return default_look (NULL);
168 void
169 pivot_table_look_set_default (const struct pivot_table_look *look)
171 default_look (look);
174 char * WARN_UNUSED_RESULT
175 pivot_table_look_read (const char *name, struct pivot_table_look **lookp)
177 *lookp = NULL;
179 /* Construct search path. */
180 const char *path[4];
181 size_t n = 0;
182 path[n++] = ".";
183 const char *home = getenv ("HOME");
184 char *allocated = NULL;
185 if (home != NULL)
186 path[n++] = allocated = xasprintf ("%s/.pspp/looks", home);
187 char *allocated2;
188 path[n++] = relocate2 (PKGDATADIR "/looks", &allocated2);
189 path[n++] = NULL;
191 /* Search path. */
192 char *file = fn_search_path (name, (char **) path);
193 if (!file)
195 char *name2 = xasprintf ("%s.stt", name);
196 file = fn_search_path (name2, (char **) path);
197 free (name2);
199 free (allocated);
200 free (allocated2);
201 if (!file)
202 return xasprintf ("%s: not found", name);
204 /* Read file. */
205 char *error = spv_table_look_read (file, lookp);
206 free (file);
207 return error;
210 const struct pivot_table_look *
211 pivot_table_look_builtin_default (void)
213 static struct pivot_table_look look = {
214 .ref_cnt = 1,
216 .omit_empty = true,
217 .row_labels_in_corner = true,
218 .width_ranges = {
219 [TABLE_HORZ] = { 36, 72 },
220 [TABLE_VERT] = { 36, 120 },
223 .areas = {
224 #define AREA(BOLD, H, V, L, R, T, B) { \
225 .cell_style = { \
226 .halign = TABLE_HALIGN_##H, \
227 .valign = TABLE_VALIGN_##V, \
228 .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \
229 [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \
230 }, \
231 .font_style = { \
232 .bold = BOLD, \
233 .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \
234 .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \
235 .size = 9, \
236 .typeface = (char *) "Sans Serif", \
237 }, \
239 [PIVOT_AREA_TITLE] = AREA(true, CENTER, CENTER, 8,11,1,8),
240 [PIVOT_AREA_CAPTION] = AREA(false, LEFT, TOP, 8,11,1,1),
241 [PIVOT_AREA_FOOTER] = AREA(false, LEFT, TOP, 11, 8,2,3),
242 [PIVOT_AREA_CORNER] = AREA(false, LEFT, BOTTOM, 8,11,1,1),
243 [PIVOT_AREA_COLUMN_LABELS] = AREA(false, CENTER, BOTTOM, 8,11,1,3),
244 [PIVOT_AREA_ROW_LABELS] = AREA(false, LEFT, TOP, 8,11,1,3),
245 [PIVOT_AREA_DATA] = AREA(false, MIXED, TOP, 8,11,1,1),
246 [PIVOT_AREA_LAYERS] = AREA(false, LEFT, BOTTOM, 8,11,1,3),
247 #undef AREA
250 .borders = {
251 #define BORDER(STROKE) { .stroke = STROKE, .color = CELL_COLOR_BLACK }
252 [PIVOT_BORDER_TITLE] = BORDER(TABLE_STROKE_NONE),
253 [PIVOT_BORDER_OUTER_LEFT] = BORDER(TABLE_STROKE_NONE),
254 [PIVOT_BORDER_OUTER_TOP] = BORDER(TABLE_STROKE_NONE),
255 [PIVOT_BORDER_OUTER_RIGHT] = BORDER(TABLE_STROKE_NONE),
256 [PIVOT_BORDER_OUTER_BOTTOM] = BORDER(TABLE_STROKE_NONE),
257 [PIVOT_BORDER_INNER_LEFT] = BORDER(TABLE_STROKE_THICK),
258 [PIVOT_BORDER_INNER_TOP] = BORDER(TABLE_STROKE_THICK),
259 [PIVOT_BORDER_INNER_RIGHT] = BORDER(TABLE_STROKE_THICK),
260 [PIVOT_BORDER_INNER_BOTTOM] = BORDER(TABLE_STROKE_THICK),
261 [PIVOT_BORDER_DATA_LEFT] = BORDER(TABLE_STROKE_THICK),
262 [PIVOT_BORDER_DATA_TOP] = BORDER(TABLE_STROKE_THICK),
263 [PIVOT_BORDER_DIM_ROW_HORZ] = BORDER(TABLE_STROKE_SOLID),
264 [PIVOT_BORDER_DIM_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
265 [PIVOT_BORDER_DIM_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
266 [PIVOT_BORDER_DIM_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
267 [PIVOT_BORDER_CAT_ROW_HORZ] = BORDER(TABLE_STROKE_NONE),
268 [PIVOT_BORDER_CAT_ROW_VERT] = BORDER(TABLE_STROKE_NONE),
269 [PIVOT_BORDER_CAT_COL_HORZ] = BORDER(TABLE_STROKE_SOLID),
270 [PIVOT_BORDER_CAT_COL_VERT] = BORDER(TABLE_STROKE_SOLID),
274 return &look;
277 struct pivot_table_look *
278 pivot_table_look_new_builtin_default (void)
280 return pivot_table_look_unshare (
281 pivot_table_look_ref (pivot_table_look_builtin_default ()));
284 struct pivot_table_look *
285 pivot_table_look_ref (const struct pivot_table_look *look_)
287 assert (look_->ref_cnt > 0);
289 struct pivot_table_look *look = CONST_CAST (struct pivot_table_look *, look_);
290 look->ref_cnt++;
291 return look;
294 static char *
295 xstrdup_if_nonempty (const char *s)
297 return s && s[0] ? xstrdup (s) : NULL;
300 struct pivot_table_look *
301 pivot_table_look_unshare (struct pivot_table_look *old)
303 assert (old->ref_cnt > 0);
304 if (old->ref_cnt == 1)
305 return old;
307 pivot_table_look_unref (old);
309 struct pivot_table_look *new = xmemdup (old, sizeof *old);
310 new->ref_cnt = 1;
311 new->name = xstrdup_if_nonempty (old->name);
312 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
313 table_area_style_copy (NULL, &new->areas[i], &old->areas[i]);
314 new->continuation = xstrdup_if_nonempty (old->continuation);
316 return new;
319 void
320 pivot_table_look_unref (struct pivot_table_look *look)
322 if (look)
324 assert (look->ref_cnt > 0);
325 if (!--look->ref_cnt)
327 free (look->name);
328 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
329 table_area_style_uninit (&look->areas[i]);
330 free (look->continuation);
331 free (look);
336 /* Axes. */
338 /* Returns the name of AXIS_TYPE. */
339 const char *
340 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
342 switch (axis_type)
344 case PIVOT_AXIS_LAYER:
345 return "layer";
347 case PIVOT_AXIS_ROW:
348 return "row";
350 case PIVOT_AXIS_COLUMN:
351 return "column";
353 default:
354 return "<error>";
358 static enum pivot_axis_type
359 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
361 assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
362 return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
365 /* Implementation of PIVOT_AXIS_FOR_EACH. */
366 size_t *
367 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
369 if (!indexes)
371 if (axis->n_dimensions)
372 for (size_t i = 0; i < axis->n_dimensions; i++)
373 if (axis->dimensions[i]->n_leaves == 0)
374 return NULL;
376 size_t size = axis->n_dimensions * sizeof *indexes;
377 return xzalloc (MAX (size, 1));
380 for (size_t i = 0; i < axis->n_dimensions; i++)
382 const struct pivot_dimension *d = axis->dimensions[i];
383 if (++indexes[i] < d->n_leaves)
384 return indexes;
386 indexes[i] = 0;
389 free (indexes);
390 return NULL;
393 /* Dimensions. */
395 static void
396 pivot_category_set_rc (struct pivot_category *category, const char *s)
398 if (!s)
399 return;
401 pivot_table_use_rc (category->dimension->table, s,
402 &category->format, &category->honor_small);
404 /* Ensure that the category itself, in addition to the cells within it, takes
405 the format. (It's kind of rare for a category to have a numeric format
406 though.) */
407 struct pivot_value *name = category->name;
408 if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
409 pivot_table_use_rc (category->dimension->table, s,
410 &name->numeric.format, &name->numeric.honor_small);
413 static void
414 pivot_category_create_leaves_valist (struct pivot_category *parent,
415 va_list args)
417 const char *s;
418 while ((s = va_arg (args, const char *)))
420 if (!strncmp (s, "RC_", 3))
422 assert (parent->n_subs);
423 pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
425 else
426 pivot_category_create_leaf (parent, pivot_value_new_text (s));
430 /* Creates a new dimension with the given NAME in TABLE and returns it. The
431 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
432 that axis.
434 NAME should be a translatable name, but not actually translated yet,
435 e.g. enclosed in N_(). To use a different kind of value for a name, use
436 pivot_dimension_create__() instead.
438 The optional varargs parameters may be used to add an initial set of
439 categories to the dimension. Each string should be a translatable category
440 name, but not actually translated yet, e.g. enclosed in N_(). Each string
441 may optionally be followod by a PIVOT_RC_* string that specifies the default
442 numeric format for cells in this category. */
443 struct pivot_dimension * SENTINEL (0)
444 (pivot_dimension_create) (struct pivot_table *table,
445 enum pivot_axis_type axis_type,
446 const char *name, ...)
448 struct pivot_dimension *d = pivot_dimension_create__ (
449 table, axis_type, pivot_value_new_text (name));
451 va_list args;
452 va_start (args, name);
453 pivot_category_create_leaves_valist (d->root, args);
454 va_end (args);
456 return d;
459 /* Creates a new dimension with the given NAME in TABLE and returns it. The
460 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
461 that axis. */
462 struct pivot_dimension *
463 pivot_dimension_create__ (struct pivot_table *table,
464 enum pivot_axis_type axis_type,
465 struct pivot_value *name)
467 assert (pivot_table_is_empty (table));
469 struct pivot_dimension *d = xmalloc (sizeof *d);
470 *d = (struct pivot_dimension) {
471 .table = table,
472 .axis_type = axis_type,
473 .level = table->axes[axis_type].n_dimensions,
474 .top_index = table->n_dimensions,
475 .root = xmalloc (sizeof *d->root),
478 struct pivot_category *root = d->root;
479 *root = (struct pivot_category) {
480 .name = name,
481 .parent = NULL,
482 .dimension = d,
483 .show_label = false,
484 .data_index = SIZE_MAX,
485 .presentation_index = SIZE_MAX,
488 table->dimensions = xrealloc (
489 table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
490 table->dimensions[table->n_dimensions++] = d;
492 struct pivot_axis *axis = &table->axes[axis_type];
493 axis->dimensions = xrealloc (
494 axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
495 axis->dimensions[axis->n_dimensions++] = d;
497 if (axis_type == PIVOT_AXIS_LAYER)
499 free (table->current_layer);
500 table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
501 sizeof *table->current_layer);
504 /* axis->extent and axis->label_depth will be calculated later. */
506 return d;
509 void
510 pivot_dimension_destroy (struct pivot_dimension *d)
512 if (!d)
513 return;
515 pivot_category_destroy (d->root);
516 free (d->data_leaves);
517 free (d->presentation_leaves);
518 free (d);
521 /* Returns the first leaf node in an in-order traversal that is a child of
522 CAT. */
523 static const struct pivot_category * UNUSED
524 pivot_category_first_leaf (const struct pivot_category *cat)
526 if (pivot_category_is_leaf (cat))
527 return cat;
529 for (size_t i = 0; i < cat->n_subs; i++)
531 const struct pivot_category *first
532 = pivot_category_first_leaf (cat->subs[i]);
533 if (first)
534 return first;
537 return NULL;
540 /* Returns the next leaf node in an in-order traversal starting at CAT, which
541 must be a leaf. */
542 static const struct pivot_category * UNUSED
543 pivot_category_next_leaf (const struct pivot_category *cat)
545 assert (pivot_category_is_leaf (cat));
547 for (;;)
549 const struct pivot_category *parent = cat->parent;
550 if (!parent)
551 return NULL;
552 for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
554 const struct pivot_category *next
555 = pivot_category_first_leaf (parent->subs[i]);
556 if (next)
557 return next;
560 cat = cat->parent;
564 static void
565 pivot_category_add_child (struct pivot_category *child)
567 struct pivot_category *parent = child->parent;
569 assert (pivot_category_is_group (parent));
570 if (parent->n_subs >= parent->allocated_subs)
571 parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
572 sizeof *parent->subs);
573 parent->subs[parent->n_subs++] = child;
576 /* Adds leaf categories as a child of PARENT. To create top-level categories
577 within dimension 'd', pass 'd->root' for PARENT.
579 Each of the varargs parameters should be a string, each of which should be a
580 translatable category name, but not actually translated yet, e.g. enclosed
581 in N_(). Each string may optionally be followod by a PIVOT_RC_* string that
582 specifies the default numeric format for cells in this category.
584 Returns the category index, which is just a 0-based array index, for the
585 first new category.
587 Leaves have to be created in in-order, that is, don't create a group and add
588 some leaves, then add leaves outside the group and try to add more leaves
589 inside it. */
590 int SENTINEL (0)
591 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
593 int retval = parent->dimension->n_leaves;
595 va_list args;
596 va_start (args, parent);
597 pivot_category_create_leaves_valist (parent, args);
598 va_end (args);
600 return retval;
603 /* Creates a new leaf category with the given NAME as a child of PARENT. To
604 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
605 Returns the category index, which is just a 0-based array index, for the new
606 category.
608 Leaves have to be created in in-order, that is, don't create a group and add
609 some leaves, then add leaves outside the group and try to add more leaves
610 inside it. */
612 pivot_category_create_leaf (struct pivot_category *parent,
613 struct pivot_value *name)
615 return pivot_category_create_leaf_rc (parent, name, NULL);
618 /* Creates a new leaf category with the given NAME as a child of PARENT. To
619 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
620 Returns the category index, which is just a 0-based array index, for the new
621 category.
623 If RC is nonnull and the name of a result category, the category is assigned
624 that result category.
626 Leaves have to be created in in-order, that is, don't create a group and add
627 some leaves, then add leaves outside the group and try to add more leaves
628 inside it. */
630 pivot_category_create_leaf_rc (struct pivot_category *parent,
631 struct pivot_value *name, const char *rc)
633 struct pivot_dimension *d = parent->dimension;
635 struct pivot_category *leaf = xmalloc (sizeof *leaf);
636 *leaf = (struct pivot_category) {
637 .name = name,
638 .parent = parent,
639 .dimension = d,
640 .group_index = parent->n_subs,
641 .data_index = d->n_leaves,
642 .presentation_index = d->n_leaves,
645 if (d->n_leaves >= d->allocated_leaves)
647 d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
648 sizeof *d->data_leaves);
649 d->presentation_leaves = xrealloc (
650 d->presentation_leaves,
651 d->allocated_leaves * sizeof *d->presentation_leaves);
654 d->data_leaves[d->n_leaves] = leaf;
655 d->presentation_leaves[d->n_leaves] = leaf;
656 d->n_leaves++;
658 pivot_category_add_child (leaf);
660 /* Make sure that the new child is the last in in-order. */
661 assert (!pivot_category_next_leaf (leaf));
663 pivot_category_set_rc (leaf, rc);
665 return leaf->data_index;
668 /* Adds a new category group named NAME as a child of PARENT. To create a
669 top-level group within dimension 'd', pass 'd->root' for PARENT.
671 NAME should be a translatable name, but not actually translated yet,
672 e.g. enclosed in N_(). To use a different kind of value for a name, use
673 pivot_category_create_group__() instead.
675 The optional varargs parameters may be used to add an initial set of
676 categories to the group. Each string should be a translatable category
677 name, but not actually translated yet, e.g. enclosed in N_(). Each string
678 may optionally be followod by a PIVOT_RC_* string that specifies the default
679 numeric format for cells in this category.
681 Returns the new group. */
682 struct pivot_category * SENTINEL (0)
683 (pivot_category_create_group) (struct pivot_category *parent,
684 const char *name, ...)
686 struct pivot_category *group = pivot_category_create_group__ (
687 parent, pivot_value_new_text (name));
689 va_list args;
690 va_start (args, name);
691 pivot_category_create_leaves_valist (group, args);
692 va_end (args);
694 return group;
697 /* Adds a new category group named NAME as a child of PARENT. To create a
698 top-level group within dimension 'd', pass 'd->root' for PARENT. Returns
699 the new group. */
700 struct pivot_category *
701 pivot_category_create_group__ (struct pivot_category *parent,
702 struct pivot_value *name)
704 struct pivot_dimension *d = parent->dimension;
706 struct pivot_category *group = xmalloc (sizeof *group);
707 *group = (struct pivot_category) {
708 .name = name,
709 .parent = parent,
710 .dimension = d,
711 .show_label = true,
712 .group_index = parent->n_subs,
713 .data_index = SIZE_MAX,
714 .presentation_index = SIZE_MAX,
717 pivot_category_add_child (group);
719 return group;
722 void
723 pivot_category_destroy (struct pivot_category *c)
725 if (!c)
726 return;
728 pivot_value_destroy (c->name);
729 for (size_t i = 0; i < c->n_subs; i++)
730 pivot_category_destroy (c->subs[i]);
731 free (c->subs);
732 free (c);
735 /* Result classes.
737 These are usually the easiest way to control the formatting of numeric data
738 in a pivot table. See pivot_dimension_create() for an explanation of their
739 use. */
740 struct result_class
742 const char *name; /* "RC_*". */
743 struct fmt_spec format;
746 /* Formats for most of the result classes. */
747 static struct result_class result_classes[] =
749 { PIVOT_RC_INTEGER, { FMT_F, 40, 0 } },
750 { PIVOT_RC_PERCENT, { FMT_PCT, 40, 1 } },
751 { PIVOT_RC_CORRELATION, { FMT_F, 40, 3 } },
752 { PIVOT_RC_SIGNIFICANCE, { FMT_F, 40, 3 } },
753 { PIVOT_RC_RESIDUAL, { FMT_F, 40, 2 } },
754 { PIVOT_RC_COUNT, { 0, 0, 0 } },
755 { PIVOT_RC_OTHER, { 0, 0, 0 } },
758 /* Has PIVOT_RC_COUNT been overridden by the user? */
759 static bool overridden_count_format;
761 static struct result_class *
762 pivot_result_class_find (const char *s)
764 for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
765 if (!strcmp (s, result_classes[i].name))
766 return &result_classes[i];
767 return NULL;
770 static void
771 pivot_table_use_rc (const struct pivot_table *table, const char *s,
772 struct fmt_spec *format, bool *honor_small)
774 if (s)
776 if (!strcmp (s, PIVOT_RC_OTHER))
778 *format = *settings_get_format ();
779 *honor_small = true;
781 else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
783 *format = table->weight_format;
784 *honor_small = false;
786 else
788 const struct result_class *rc = pivot_result_class_find (s);
789 if (rc)
791 *format = rc->format;
792 *honor_small = false;
794 else
796 printf ("unknown class %s\n", s);
802 /* Sets the format specification for the result class named S (which should not
803 include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
804 does not name a known result class. */
805 bool
806 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
808 char *s = xasprintf ("RC_%s", s_);
809 struct result_class *rc = pivot_result_class_find (s);
810 if (rc)
812 rc->format = *format;
813 if (!strcmp (s, PIVOT_RC_COUNT))
814 overridden_count_format = true;
816 free (s);
818 return rc != NULL;
821 bool
822 is_pivot_result_class (const char *s)
824 return pivot_result_class_find (s) != NULL;
827 /* Pivot tables. */
829 static struct pivot_cell *pivot_table_insert_cell (struct pivot_table *,
830 const size_t *dindexes);
831 static void pivot_table_delete_cell (struct pivot_table *,
832 struct pivot_cell *);
834 /* Creates and returns a new pivot table with the given TITLE. TITLE should be
835 a text string marked for translation but not actually translated yet,
836 e.g. N_("Descriptive Statistics"). The un-translated text string is used as
837 the pivot table's subtype.
839 This function is a shortcut for pivot_table_create__() for the most common
840 case. Use pivot_table_create__() directly if the title should be some kind
841 of value other than an ordinary text string, or if the subtype should be
842 different from the title.
844 See the large comment at the top of pivot-table.h for general advice on
845 creating pivot tables. */
846 struct pivot_table *
847 pivot_table_create (const char *title)
849 return pivot_table_create__ (pivot_value_new_text (title), title);
852 /* Creates and returns a new pivot table with the given TITLE, and takes
853 ownership of TITLE. The new pivot table's subtype is SUBTYPE, which should
854 be an untranslated English string that describes the contents of the table
855 at a high level without being specific about the variables or other context
856 involved.
858 TITLE and SUBTYPE may be NULL, but in that case the client must add them
859 later because they are both mandatory for a pivot table.
861 See the large comment at the top of pivot-table.h for general advice on
862 creating pivot tables. */
863 struct pivot_table *
864 pivot_table_create__ (struct pivot_value *title, const char *subtype)
866 struct pivot_table *table = xzalloc (sizeof *table);
867 table->ref_cnt = 1;
868 table->show_title = true;
869 table->show_caption = true;
870 table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 };
871 table->title = title;
872 table->subtype = subtype ? pivot_value_new_text (subtype) : NULL;
873 table->command_c = output_get_command_name ();
874 table->look = pivot_table_look_ref (pivot_table_look_get_default ());
875 table->settings = fmt_settings_copy (settings_get_fmt_settings ());
876 table->small = settings_get_small ();
878 hmap_init (&table->cells);
880 return table;
883 /* Creates and returns a new pivot table with the given TITLE and a single cell
884 with the given CONTENT.
886 This is really just for error handling. */
887 struct pivot_table *
888 pivot_table_create_for_text (struct pivot_value *title,
889 struct pivot_value *content)
891 struct pivot_table *table = pivot_table_create__ (title, "Error");
893 struct pivot_dimension *d = pivot_dimension_create (
894 table, PIVOT_AXIS_ROW, N_("Error"));
895 d->hide_all_labels = true;
896 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
898 pivot_table_put1 (table, 0, content);
900 return table;
903 /* Increases TABLE's reference count, indicating that it has an additional
904 owner. A pivot table that is shared among multiple owners must not be
905 modified. */
906 struct pivot_table *
907 pivot_table_ref (const struct pivot_table *table_)
909 struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
910 table->ref_cnt++;
911 return table;
914 static char *
915 xstrdup_if_nonnull (const char *s)
917 return s ? xstrdup (s) : NULL;
920 static struct pivot_table_sizing
921 clone_sizing (const struct pivot_table_sizing *s)
923 return (struct pivot_table_sizing) {
924 .widths = (s->n_widths
925 ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
926 : NULL),
927 .n_widths = s->n_widths,
929 .breaks = (s->n_breaks
930 ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
931 : NULL),
932 .n_breaks = s->n_breaks,
934 .keeps = (s->n_keeps
935 ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
936 : NULL),
937 .n_keeps = s->n_keeps,
941 static struct pivot_footnote **
942 clone_footnotes (struct pivot_footnote **old, size_t n)
944 if (!n)
945 return NULL;
947 struct pivot_footnote **new = xmalloc (n * sizeof *new);
948 for (size_t i = 0; i < n; i++)
950 new[i] = xmalloc (sizeof *new[i]);
951 *new[i] = (struct pivot_footnote) {
952 .idx = old[i]->idx,
953 .content = pivot_value_clone (old[i]->content),
954 .marker = pivot_value_clone (old[i]->marker),
955 .show = old[i]->show,
958 return new;
961 static struct pivot_category *
962 clone_category (struct pivot_category *old,
963 struct pivot_dimension *new_dimension,
964 struct pivot_category *new_parent)
966 struct pivot_category *new = xmalloc (sizeof *new);
967 *new = (struct pivot_category) {
968 .name = pivot_value_clone (old->name),
969 .parent = new_parent,
970 .dimension = new_dimension,
971 .label_depth = old->label_depth,
972 .extra_depth = old->extra_depth,
974 .subs = (old->n_subs
975 ? xzalloc (old->n_subs * sizeof *new->subs)
976 : NULL),
977 .n_subs = old->n_subs,
978 .allocated_subs = old->n_subs,
980 .show_label = old->show_label,
981 .show_label_in_corner = old->show_label_in_corner,
983 .format = old->format,
984 .group_index = old->group_index,
985 .data_index = old->data_index,
986 .presentation_index = old->presentation_index,
989 if (pivot_category_is_leaf (old))
991 assert (new->data_index < new_dimension->n_leaves);
992 new->dimension->data_leaves[new->data_index] = new;
994 assert (new->presentation_index < new_dimension->n_leaves);
995 new->dimension->presentation_leaves[new->presentation_index] = new;
998 for (size_t i = 0; i < new->n_subs; i++)
999 new->subs[i] = clone_category (old->subs[i], new_dimension, new);
1001 return new;
1004 static struct pivot_dimension *
1005 clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
1007 struct pivot_dimension *new = xmalloc (sizeof *new);
1008 *new = (struct pivot_dimension) {
1009 .table = new_pt,
1010 .axis_type = old->axis_type,
1011 .level = old->level,
1012 .top_index = old->top_index,
1013 .data_leaves = xzalloc (old->n_leaves * sizeof *new->data_leaves),
1014 .presentation_leaves = xzalloc (old->n_leaves
1015 * sizeof *new->presentation_leaves),
1016 .n_leaves = old->n_leaves,
1017 .allocated_leaves = old->n_leaves,
1018 .hide_all_labels = old->hide_all_labels,
1019 .label_depth = old->label_depth,
1022 new->root = clone_category (old->root, new, NULL);
1024 return new;
1027 static struct pivot_dimension **
1028 clone_dimensions (struct pivot_dimension **old, size_t n,
1029 struct pivot_table *new_pt)
1031 if (!n)
1032 return NULL;
1034 struct pivot_dimension **new = xmalloc (n * sizeof *new);
1035 for (size_t i = 0; i < n; i++)
1036 new[i] = clone_dimension (old[i], new_pt);
1037 return new;
1040 struct pivot_table *
1041 pivot_table_unshare (struct pivot_table *old)
1043 assert (old->ref_cnt > 0);
1044 if (old->ref_cnt == 1)
1045 return old;
1047 pivot_table_unref (old);
1049 struct pivot_table *new = xmalloc (sizeof *new);
1050 *new = (struct pivot_table) {
1051 .ref_cnt = 1,
1053 .look = pivot_table_look_ref (old->look),
1055 .rotate_inner_column_labels = old->rotate_inner_column_labels,
1056 .rotate_outer_row_labels = old->rotate_outer_row_labels,
1057 .show_grid_lines = old->show_grid_lines,
1058 .show_title = old->show_title,
1059 .show_caption = old->show_caption,
1060 .current_layer = (old->current_layer
1061 ? xmemdup (old->current_layer,
1062 old->axes[PIVOT_AXIS_LAYER].n_dimensions
1063 * sizeof *new->current_layer)
1064 : NULL),
1065 .show_values = old->show_values,
1066 .show_variables = old->show_variables,
1067 .weight_format = old->weight_format,
1069 .sizing = {
1070 [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
1071 [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
1074 .settings = fmt_settings_copy (&old->settings),
1075 .grouping = old->grouping,
1076 .small = old->small,
1078 .command_local = xstrdup_if_nonnull (old->command_local),
1079 .command_c = xstrdup_if_nonnull (old->command_c),
1080 .language = xstrdup_if_nonnull (old->language),
1081 .locale = xstrdup_if_nonnull (old->locale),
1083 .dataset = xstrdup_if_nonnull (old->dataset),
1084 .datafile = xstrdup_if_nonnull (old->datafile),
1085 .date = old->date,
1087 .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
1088 .n_footnotes = old->n_footnotes,
1089 .allocated_footnotes = old->n_footnotes,
1091 .title = pivot_value_clone (old->title),
1092 .subtype = pivot_value_clone (old->subtype),
1093 .corner_text = pivot_value_clone (old->corner_text),
1094 .caption = pivot_value_clone (old->caption),
1095 .notes = xstrdup_if_nonnull (old->notes),
1097 .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
1098 .n_dimensions = old->n_dimensions,
1100 .cells = HMAP_INITIALIZER (new->cells),
1103 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1105 struct pivot_axis *new_axis = &new->axes[i];
1106 const struct pivot_axis *old_axis = &old->axes[i];
1108 *new_axis = (struct pivot_axis) {
1109 .dimensions = xmalloc (old_axis->n_dimensions
1110 * sizeof *new_axis->dimensions),
1111 .n_dimensions = old_axis->n_dimensions,
1112 .extent = old_axis->extent,
1113 .label_depth = old_axis->label_depth,
1116 for (size_t i = 0; i < new_axis->n_dimensions; i++)
1117 new_axis->dimensions[i] = new->dimensions[
1118 old_axis->dimensions[i]->top_index];
1121 const struct pivot_cell *old_cell;
1122 size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
1123 HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
1125 for (size_t i = 0; i < old->n_dimensions; i++)
1126 dindexes[i] = old_cell->idx[i];
1127 struct pivot_cell *new_cell
1128 = pivot_table_insert_cell (new, dindexes);
1129 new_cell->value = pivot_value_clone (old_cell->value);
1131 free (dindexes);
1133 return new;
1136 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
1137 If TABLE no longer has any owners, it is freed. */
1138 void
1139 pivot_table_unref (struct pivot_table *table)
1141 if (!table)
1142 return;
1143 assert (table->ref_cnt > 0);
1144 if (--table->ref_cnt)
1145 return;
1147 free (table->current_layer);
1148 pivot_table_look_unref (table->look);
1150 for (int i = 0; i < TABLE_N_AXES; i++)
1151 pivot_table_sizing_uninit (&table->sizing[i]);
1153 fmt_settings_uninit (&table->settings);
1155 free (table->command_local);
1156 free (table->command_c);
1157 free (table->language);
1158 free (table->locale);
1160 free (table->dataset);
1161 free (table->datafile);
1163 for (size_t i = 0; i < table->n_footnotes; i++)
1164 pivot_footnote_destroy (table->footnotes[i]);
1165 free (table->footnotes);
1167 pivot_value_destroy (table->title);
1168 pivot_value_destroy (table->subtype);
1169 pivot_value_destroy (table->corner_text);
1170 pivot_value_destroy (table->caption);
1171 free (table->notes);
1173 for (size_t i = 0; i < table->n_dimensions; i++)
1174 pivot_dimension_destroy (table->dimensions[i]);
1175 free (table->dimensions);
1177 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1178 free (table->axes[i].dimensions);
1180 struct pivot_cell *cell, *next_cell;
1181 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
1182 &table->cells)
1183 pivot_table_delete_cell (table, cell);
1185 hmap_destroy (&table->cells);
1187 free (table);
1190 /* Returns true if TABLE has more than one owner. A pivot table that is shared
1191 among multiple owners must not be modified. */
1192 bool
1193 pivot_table_is_shared (const struct pivot_table *table)
1195 return table->ref_cnt > 1;
1198 /* Swaps axes A and B in TABLE. */
1199 void
1200 pivot_table_swap_axes (struct pivot_table *table,
1201 enum pivot_axis_type a, enum pivot_axis_type b)
1203 if (a == b)
1204 return;
1206 struct pivot_axis tmp = table->axes[a];
1207 table->axes[a] = table->axes[b];
1208 table->axes[b] = tmp;
1210 for (int a = 0; a < PIVOT_N_AXES; a++)
1212 struct pivot_axis *axis = &table->axes[a];
1213 for (size_t d = 0; d < axis->n_dimensions; d++)
1214 axis->dimensions[d]->axis_type = a;
1217 if (a == PIVOT_AXIS_LAYER || b == PIVOT_AXIS_LAYER)
1219 free (table->current_layer);
1220 table->current_layer = xzalloc (
1221 table->axes[PIVOT_AXIS_LAYER].n_dimensions
1222 * sizeof *table->current_layer);
1226 /* Swaps the row and column axes in TABLE. */
1227 void
1228 pivot_table_transpose (struct pivot_table *table)
1230 pivot_table_swap_axes (table, PIVOT_AXIS_ROW, PIVOT_AXIS_COLUMN);
1233 static void
1234 pivot_table_update_axes (struct pivot_table *table)
1236 for (int a = 0; a < PIVOT_N_AXES; a++)
1238 struct pivot_axis *axis = &table->axes[a];
1240 for (size_t d = 0; d < axis->n_dimensions; d++)
1242 struct pivot_dimension *dim = axis->dimensions[d];
1243 dim->axis_type = a;
1244 dim->level = d;
1249 /* Moves DIM from its current location in TABLE to POS within AXIS. POS of 0
1250 is the innermost dimension, 1 is the next one out, and so on. */
1251 void
1252 pivot_table_move_dimension (struct pivot_table *table,
1253 struct pivot_dimension *dim,
1254 enum pivot_axis_type axis, size_t pos)
1256 assert (dim->table == table);
1258 struct pivot_axis *old_axis = &table->axes[dim->axis_type];
1259 struct pivot_axis *new_axis = &table->axes[axis];
1260 pos = MIN (pos, new_axis->n_dimensions);
1262 if (old_axis == new_axis && pos == dim->level)
1264 /* No change. */
1265 return;
1268 /* Update the current layer, if necessary. If we're moving within the layer
1269 axis, preserve the current layer. */
1270 if (dim->axis_type == PIVOT_AXIS_LAYER)
1272 if (axis == PIVOT_AXIS_LAYER)
1274 /* Rearranging the layer axis. */
1275 move_element (table->current_layer, old_axis->n_dimensions,
1276 sizeof *table->current_layer,
1277 dim->level, pos);
1279 else
1281 /* A layer is becoming a row or column. */
1282 remove_element (table->current_layer, old_axis->n_dimensions,
1283 sizeof *table->current_layer, dim->level);
1286 else if (axis == PIVOT_AXIS_LAYER)
1288 /* A row or column is becoming a layer. */
1289 table->current_layer = xrealloc (
1290 table->current_layer,
1291 (new_axis->n_dimensions + 1) * sizeof *table->current_layer);
1292 insert_element (table->current_layer, new_axis->n_dimensions,
1293 sizeof *table->current_layer, pos);
1294 table->current_layer[pos] = 0;
1297 /* Remove DIM from its current axis. */
1298 remove_element (old_axis->dimensions, old_axis->n_dimensions,
1299 sizeof *old_axis->dimensions, dim->level);
1300 old_axis->n_dimensions--;
1302 /* Insert DIM into its new axis. */
1303 new_axis->dimensions = xrealloc (
1304 new_axis->dimensions,
1305 (new_axis->n_dimensions + 1) * sizeof *new_axis->dimensions);
1306 insert_element (new_axis->dimensions, new_axis->n_dimensions,
1307 sizeof *new_axis->dimensions, pos);
1308 new_axis->dimensions[pos] = dim;
1309 new_axis->n_dimensions++;
1311 pivot_table_update_axes (table);
1315 const struct pivot_table_look *
1316 pivot_table_get_look (const struct pivot_table *table)
1318 return table->look;
1321 void
1322 pivot_table_set_look (struct pivot_table *table,
1323 const struct pivot_table_look *look)
1325 pivot_table_look_unref (table->look);
1326 table->look = pivot_table_look_ref (look);
1329 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
1330 WV, which should be the weight variable for the dictionary whose data or
1331 statistics are being put into TABLE.
1333 This has no effect if WV is NULL. */
1334 void
1335 pivot_table_set_weight_var (struct pivot_table *table,
1336 const struct variable *wv)
1338 if (wv)
1339 pivot_table_set_weight_format (table, var_get_print_format (wv));
1342 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
1343 format for the dictionary whose data or statistics are being put into TABLE.
1345 This has no effect if WFMT is NULL. */
1346 void
1347 pivot_table_set_weight_format (struct pivot_table *table,
1348 const struct fmt_spec *wfmt)
1350 if (wfmt)
1351 table->weight_format = *wfmt;
1354 /* Returns true if TABLE has no cells, false otherwise. */
1355 bool
1356 pivot_table_is_empty (const struct pivot_table *table)
1358 return hmap_is_empty (&table->cells);
1361 static unsigned int
1362 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
1364 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
1367 static bool
1368 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
1370 for (size_t i = 0; i < n; i++)
1371 if (a[i] != b[i])
1372 return false;
1374 return true;
1377 static struct pivot_cell *
1378 pivot_table_lookup_cell__ (const struct pivot_table *table,
1379 const size_t *dindexes, unsigned int hash)
1381 struct pivot_cell *cell;
1382 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
1383 &table->cells)
1384 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
1385 return cell;
1386 return false;
1389 static struct pivot_cell *
1390 pivot_cell_allocate (size_t n_idx)
1392 struct pivot_cell *cell UNUSED;
1393 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
1396 static struct pivot_cell *
1397 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
1399 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1400 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
1401 if (!cell)
1403 cell = pivot_cell_allocate (table->n_dimensions);
1404 for (size_t i = 0; i < table->n_dimensions; i++)
1405 cell->idx[i] = dindexes[i];
1406 cell->value = NULL;
1407 hmap_insert (&table->cells, &cell->hmap_node, hash);
1409 return cell;
1412 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
1413 DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of
1414 VALUE.
1416 If VALUE is a numeric value without a specified format, this function checks
1417 each of the categories designated by DINDEXES[] and takes the format from
1418 the first category with a result class. If none has a result class, uses
1419 the overall default numeric format. */
1420 void
1421 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
1422 struct pivot_value *value)
1424 assert (n == table->n_dimensions);
1425 for (size_t i = 0; i < n; i++)
1426 assert (dindexes[i] < table->dimensions[i]->n_leaves);
1428 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
1430 for (size_t i = 0; i < table->n_dimensions; i++)
1432 const struct pivot_dimension *d = table->dimensions[i];
1433 if (dindexes[i] < d->n_leaves)
1435 const struct pivot_category *c = d->data_leaves[dindexes[i]];
1436 if (c->format.w)
1438 value->numeric.format = c->format;
1439 value->numeric.honor_small = c->honor_small;
1440 goto done;
1444 value->numeric.format = *settings_get_format ();
1445 value->numeric.honor_small = true;
1447 done:;
1450 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1451 pivot_value_destroy (cell->value);
1452 cell->value = value;
1455 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
1456 dimension. Takes ownership of VALUE. */
1457 void
1458 pivot_table_put1 (struct pivot_table *table, size_t idx1,
1459 struct pivot_value *value)
1461 size_t dindexes[] = { idx1 };
1462 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1465 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
1466 dimensions. Takes ownership of VALUE. */
1467 void
1468 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
1469 struct pivot_value *value)
1471 size_t dindexes[] = { idx1, idx2 };
1472 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1475 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
1476 have 3 dimensions. Takes ownership of VALUE. */
1477 void
1478 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
1479 size_t idx3, struct pivot_value *value)
1481 size_t dindexes[] = { idx1, idx2, idx3 };
1482 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1485 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1486 must have 4 dimensions. Takes ownership of VALUE. */
1487 void
1488 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1489 size_t idx3, size_t idx4, struct pivot_value *value)
1491 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1492 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1495 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1496 automatically assigned marker.
1498 The footnote will only appear in output if it is referenced. Use
1499 pivot_value_add_footnote() to add a reference to the footnote. */
1500 struct pivot_footnote *
1501 pivot_table_create_footnote (struct pivot_table *table,
1502 struct pivot_value *content)
1504 return pivot_table_create_footnote__ (table, table->n_footnotes,
1505 NULL, content);
1508 void
1509 pivot_footnote_format_marker (const struct pivot_footnote *f,
1510 const struct pivot_table *pt,
1511 struct string *s)
1513 if (f->marker)
1514 pivot_value_format_body (f->marker, pt, s);
1515 else if (pt->look->show_numeric_markers)
1516 ds_put_format (s, "%zu", f->idx + 1);
1517 else
1519 char text[INT_BUFSIZE_BOUND (size_t)];
1520 str_format_26adic (f->idx + 1, false, text, sizeof text);
1521 ds_put_cstr (s, text);
1525 char *
1526 pivot_footnote_marker_string (const struct pivot_footnote *f,
1527 const struct pivot_table *pt)
1529 struct string s = DS_EMPTY_INITIALIZER;
1530 pivot_footnote_format_marker (f, pt, &s);
1531 return ds_steal_cstr (&s);
1534 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1535 all lower indexes as a side effect). If MARKER is nonnull, sets the
1536 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1537 struct pivot_footnote *
1538 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1539 struct pivot_value *marker,
1540 struct pivot_value *content)
1542 if (idx >= table->n_footnotes)
1544 while (idx >= table->allocated_footnotes)
1545 table->footnotes = x2nrealloc (table->footnotes,
1546 &table->allocated_footnotes,
1547 sizeof *table->footnotes);
1548 while (idx >= table->n_footnotes)
1550 struct pivot_footnote *f = xmalloc (sizeof *f);
1551 *f = (struct pivot_footnote) {
1552 .idx = table->n_footnotes,
1553 .show = true,
1555 table->footnotes[table->n_footnotes++] = f;
1559 struct pivot_footnote *f = table->footnotes[idx];
1560 if (marker)
1562 pivot_value_destroy (f->marker);
1563 f->marker = marker;
1565 if (content)
1567 pivot_value_destroy (f->content);
1568 f->content = content;
1570 return f;
1573 /* Frees the data owned by F. */
1574 void
1575 pivot_footnote_destroy (struct pivot_footnote *f)
1577 if (f)
1579 pivot_value_destroy (f->content);
1580 pivot_value_destroy (f->marker);
1581 free (f);
1585 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1586 indexes for each dimension in TABLE in DINDEXES[]. */
1587 void
1588 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1589 const size_t *pindexes[PIVOT_N_AXES],
1590 size_t dindexes[/* table->n_dimensions */])
1592 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1594 const struct pivot_axis *axis = &table->axes[i];
1596 for (size_t j = 0; j < axis->n_dimensions; j++)
1598 const struct pivot_dimension *d = axis->dimensions[j];
1599 size_t pindex = pindexes[i][j];
1600 dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
1605 size_t *
1606 pivot_table_enumerate_axis (const struct pivot_table *table,
1607 enum pivot_axis_type axis_type,
1608 const size_t *layer_indexes, bool omit_empty,
1609 size_t *n)
1611 const struct pivot_axis *axis = &table->axes[axis_type];
1612 if (!axis->n_dimensions)
1614 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1615 enumeration[0] = 0;
1616 enumeration[1] = SIZE_MAX;
1617 if (n)
1618 *n = 1;
1619 return enumeration;
1621 else if (!axis->extent)
1623 size_t *enumeration = xmalloc (sizeof *enumeration);
1624 *enumeration = SIZE_MAX;
1625 if (n)
1626 *n = 0;
1627 return enumeration;
1630 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1631 axis->n_dimensions), 1),
1632 sizeof *enumeration);
1633 size_t *p = enumeration;
1634 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1636 size_t *axis_indexes;
1637 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1639 if (omit_empty)
1641 enum pivot_axis_type axis2_type
1642 = pivot_axis_type_transpose (axis_type);
1644 size_t *axis2_indexes;
1645 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1647 const size_t *pindexes[PIVOT_N_AXES];
1648 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1649 pindexes[axis_type] = axis_indexes;
1650 pindexes[axis2_type] = axis2_indexes;
1651 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1652 if (pivot_table_get (table, dindexes))
1653 goto found;
1655 continue;
1657 found:
1658 free (axis2_indexes);
1661 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1662 p += axis->n_dimensions;
1664 if (omit_empty && p == enumeration)
1666 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1668 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1669 p += axis->n_dimensions;
1672 *p = SIZE_MAX;
1673 if (n)
1674 *n = (p - enumeration) / axis->n_dimensions;
1676 free (dindexes);
1677 return enumeration;
1680 static struct pivot_cell *
1681 pivot_table_lookup_cell (const struct pivot_table *table,
1682 const size_t *dindexes)
1684 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1685 return pivot_table_lookup_cell__ (table, dindexes, hash);
1688 const struct pivot_value *
1689 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1691 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1692 return cell ? cell->value : NULL;
1695 struct pivot_value *
1696 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1698 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1699 if (!cell->value)
1700 cell->value = pivot_value_new_user_text ("", -1);
1701 return cell->value;
1704 static void
1705 pivot_table_delete_cell (struct pivot_table *table, struct pivot_cell *cell)
1707 hmap_delete (&table->cells, &cell->hmap_node);
1708 pivot_value_destroy (cell->value);
1709 free (cell);
1712 bool
1713 pivot_table_delete (struct pivot_table *table, const size_t *dindexes)
1715 struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1716 if (cell)
1718 pivot_table_delete_cell (table, cell);
1719 return true;
1721 else
1722 return false;
1725 static void
1726 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1728 if (pivot_category_is_group (category) && category->n_subs)
1729 for (size_t i = 0; i < category->n_subs; i++)
1730 distribute_extra_depth (category->subs[i], extra_depth);
1731 else
1732 category->extra_depth += extra_depth;
1735 static void
1736 pivot_category_assign_label_depth (struct pivot_category *category,
1737 bool dimension_labels_in_corner)
1739 category->extra_depth = 0;
1741 if (pivot_category_is_group (category))
1743 size_t depth = 0;
1744 for (size_t i = 0; i < category->n_subs; i++)
1746 pivot_category_assign_label_depth (category->subs[i], false);
1747 depth = MAX (depth, category->subs[i]->label_depth);
1750 for (size_t i = 0; i < category->n_subs; i++)
1752 struct pivot_category *sub = category->subs[i];
1754 size_t extra_depth = depth - sub->label_depth;
1755 if (extra_depth)
1756 distribute_extra_depth (sub, extra_depth);
1758 sub->label_depth = depth;
1761 category->show_label_in_corner = (category->show_label
1762 && dimension_labels_in_corner);
1763 category->label_depth
1764 = (category->show_label && !category->show_label_in_corner
1765 ? depth + 1 : depth);
1767 else
1768 category->label_depth = 1;
1771 static bool
1772 pivot_axis_assign_label_depth (struct pivot_table *table,
1773 enum pivot_axis_type axis_type,
1774 bool dimension_labels_in_corner)
1776 struct pivot_axis *axis = &table->axes[axis_type];
1777 bool any_label_shown_in_corner = false;
1778 axis->label_depth = 0;
1779 axis->extent = 1;
1780 for (size_t i = 0; i < axis->n_dimensions; i++)
1782 struct pivot_dimension *d = axis->dimensions[i];
1783 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1784 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1785 axis->label_depth += d->label_depth;
1786 axis->extent *= d->n_leaves;
1788 if (d->root->show_label_in_corner)
1789 any_label_shown_in_corner = true;
1791 return any_label_shown_in_corner;
1794 void
1795 pivot_table_assign_label_depth (struct pivot_table *table)
1797 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1798 if (pivot_axis_assign_label_depth (
1799 table, PIVOT_AXIS_ROW, (table->look->row_labels_in_corner
1800 && !table->corner_text))
1801 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1802 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1803 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1806 static void
1807 indent (int indentation)
1809 for (int i = 0; i < indentation * 2; i++)
1810 putchar (' ');
1813 static void
1814 pivot_value_dump (const struct pivot_value *value,
1815 const struct pivot_table *pt)
1817 char *s = pivot_value_to_string (value, pt);
1818 fputs (s, stdout);
1819 free (s);
1822 static void
1823 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1824 const struct pivot_table *pt, int indentation)
1826 if (value)
1828 indent (indentation);
1829 printf ("%s: ", name);
1830 pivot_value_dump (value, pt);
1831 putchar ('\n');
1835 static void
1836 pivot_table_dump_string (const char *string, const char *name, int indentation)
1838 if (string)
1840 indent (indentation);
1841 printf ("%s: %s\n", name, string);
1845 static void
1846 pivot_category_dump (const struct pivot_category *c,
1847 const struct pivot_table *pt, int indentation)
1849 indent (indentation);
1850 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1851 pivot_value_dump (c->name, pt);
1852 printf ("\" ");
1854 if (pivot_category_is_leaf (c))
1855 printf ("data_index=%zu\n", c->data_index);
1856 else
1858 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1859 printf ("\n");
1861 for (size_t i = 0; i < c->n_subs; i++)
1862 pivot_category_dump (c->subs[i], pt, indentation + 1);
1866 void
1867 pivot_dimension_dump (const struct pivot_dimension *d,
1868 const struct pivot_table *pt, int indentation)
1870 indent (indentation);
1871 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1872 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1874 pivot_category_dump (d->root, pt, indentation + 1);
1877 static void
1878 table_area_style_dump (enum pivot_area area, const struct table_area_style *a,
1879 int indentation)
1881 indent (indentation);
1882 printf ("%s: ", pivot_area_to_string (area));
1883 font_style_dump (&a->font_style);
1884 putchar (' ');
1885 cell_style_dump (&a->cell_style);
1886 putchar ('\n');
1889 static void
1890 table_border_style_dump (enum pivot_border border,
1891 const struct table_border_style *b, int indentation)
1893 indent (indentation);
1894 printf ("%s: %s ", pivot_border_to_string (border),
1895 table_stroke_to_string (b->stroke));
1896 cell_color_dump (&b->color);
1897 putchar ('\n');
1900 static char ***
1901 compose_headings (const struct pivot_table *pt,
1902 const struct pivot_axis *axis,
1903 const size_t *column_enumeration)
1905 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1906 return NULL;
1908 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1909 for (size_t i = 0; i < axis->label_depth; i++)
1910 headings[i] = xcalloc (axis->extent, sizeof **headings);
1912 const size_t *indexes;
1913 size_t column = 0;
1914 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1916 int row = axis->label_depth - 1;
1917 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1919 const struct pivot_dimension *d = axis->dimensions[dim_index];
1920 if (d->hide_all_labels)
1921 continue;
1922 for (const struct pivot_category *c
1923 = d->presentation_leaves[indexes[dim_index]];
1925 c = c->parent)
1927 if (pivot_category_is_leaf (c) || (c->show_label
1928 && !c->show_label_in_corner))
1930 headings[row][column] = pivot_value_to_string (c->name, pt);
1931 if (!*headings[row][column])
1932 headings[row][column] = xstrdup ("<blank>");
1933 row--;
1937 column++;
1940 return headings;
1943 static void
1944 free_headings (const struct pivot_axis *axis, char ***headings)
1946 for (size_t i = 0; i < axis->label_depth; i++)
1948 for (size_t j = 0; j < axis->extent; j++)
1949 free (headings[i][j]);
1950 free (headings[i]);
1952 free (headings);
1955 static void
1956 pivot_table_sizing_dump (const char *name,
1957 const int width_ranges[2],
1958 const struct pivot_table_sizing *s,
1959 int indentation)
1961 indent (indentation);
1962 printf ("%ss: min=%d, max=%d\n", name, width_ranges[0], width_ranges[1]);
1963 if (s->n_widths)
1965 indent (indentation + 1);
1966 printf ("%s widths:", name);
1967 for (size_t i = 0; i < s->n_widths; i++)
1968 printf (" %d", s->widths[i]);
1969 printf ("\n");
1971 if (s->n_breaks)
1973 indent (indentation + 1);
1974 printf ("break after %ss:", name);
1975 for (size_t i = 0; i < s->n_breaks; i++)
1976 printf (" %zu", s->breaks[i]);
1977 printf ("\n");
1979 if (s->n_keeps)
1981 indent (indentation + 1);
1982 printf ("keep %ss together:", name);
1983 for (size_t i = 0; i < s->n_keeps; i++)
1984 printf (" [%zu,%zu]",
1985 s->keeps[i].ofs,
1986 s->keeps[i].ofs + s->keeps[i].n - 1);
1987 printf ("\n");
1991 void
1992 pivot_table_dump (const struct pivot_table *table, int indentation)
1994 if (!table)
1995 return;
1997 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, table));
1999 pivot_table_dump_value (table->title, "title", table, indentation);
2000 pivot_table_dump_value (table->subtype, "subtype", table, indentation);
2001 pivot_table_dump_string (table->command_c, "command", indentation);
2002 pivot_table_dump_string (table->dataset, "dataset", indentation);
2003 pivot_table_dump_string (table->datafile, "datafile", indentation);
2004 pivot_table_dump_string (table->notes, "notes", indentation);
2005 pivot_table_dump_string (table->look->name, "table-look", indentation);
2006 if (table->date)
2008 indent (indentation);
2010 struct tm *tm = localtime (&table->date);
2011 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
2012 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
2013 tm->tm_sec);
2016 indent (indentation);
2017 printf ("sizing:\n");
2018 pivot_table_sizing_dump ("column", table->look->width_ranges[TABLE_HORZ],
2019 &table->sizing[TABLE_HORZ], indentation + 1);
2020 pivot_table_sizing_dump ("row", table->look->width_ranges[TABLE_VERT],
2021 &table->sizing[TABLE_VERT], indentation + 1);
2023 indent (indentation);
2024 printf ("areas:\n");
2025 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
2026 table_area_style_dump (area, &table->look->areas[area], indentation + 1);
2028 indent (indentation);
2029 printf ("borders:\n");
2030 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
2031 table_border_style_dump (border, &table->look->borders[border],
2032 indentation + 1);
2034 for (size_t i = 0; i < table->n_dimensions; i++)
2035 pivot_dimension_dump (table->dimensions[i], table, indentation);
2037 /* Presentation and data indexes. */
2038 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
2040 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2041 if (layer_axis->n_dimensions)
2043 indent (indentation);
2044 printf ("current layer:");
2046 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2048 const struct pivot_dimension *d = layer_axis->dimensions[i];
2049 char *name = pivot_value_to_string (d->root->name, table);
2050 char *value = pivot_value_to_string (
2051 d->data_leaves[table->current_layer[i]]->name, table);
2052 printf (" %s=%s", name, value);
2053 free (value);
2054 free (name);
2057 putchar ('\n');
2060 size_t *layer_indexes;
2061 size_t layer_iteration = 0;
2062 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
2064 indent (indentation);
2065 printf ("layer %zu:", layer_iteration++);
2067 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
2068 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
2070 const struct pivot_dimension *d = layer_axis->dimensions[i];
2072 fputs (i == 0 ? " " : ", ", stdout);
2073 pivot_value_dump (d->root->name, table);
2074 fputs (" =", stdout);
2076 struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
2077 size_t n_names = 0;
2078 for (const struct pivot_category *c
2079 = d->presentation_leaves[layer_indexes[i]];
2081 c = c->parent)
2083 if (pivot_category_is_leaf (c) || c->show_label)
2084 names[n_names++] = c->name;
2087 for (size_t i = n_names; i-- > 0;)
2089 putchar (' ');
2090 pivot_value_dump (names[i], table);
2092 free (names);
2094 putchar ('\n');
2096 size_t *column_enumeration = pivot_table_enumerate_axis (
2097 table, PIVOT_AXIS_COLUMN, layer_indexes, table->look->omit_empty, NULL);
2098 size_t *row_enumeration = pivot_table_enumerate_axis (
2099 table, PIVOT_AXIS_ROW, layer_indexes, table->look->omit_empty, NULL);
2101 char ***column_headings = compose_headings (
2102 table, &table->axes[PIVOT_AXIS_COLUMN], column_enumeration);
2103 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
2105 indent (indentation + 1);
2106 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
2108 if (x)
2109 fputs ("; ", stdout);
2110 if (column_headings[y][x])
2111 fputs (column_headings[y][x], stdout);
2113 putchar ('\n');
2115 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
2117 indent (indentation + 1);
2118 printf ("-----------------------------------------------\n");
2120 char ***row_headings = compose_headings (
2121 table, &table->axes[PIVOT_AXIS_ROW], row_enumeration);
2123 size_t x = 0;
2124 const size_t *pindexes[PIVOT_N_AXES]
2125 = { [PIVOT_AXIS_LAYER] = layer_indexes };
2126 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
2127 &table->axes[PIVOT_AXIS_ROW])
2129 indent (indentation + 1);
2131 size_t i = 0;
2132 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
2134 if (i++)
2135 fputs ("; ", stdout);
2136 if (row_headings[y][x])
2137 fputs (row_headings[y][x], stdout);
2140 printf (" | ");
2142 i = 0;
2143 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
2144 column_enumeration,
2145 &table->axes[PIVOT_AXIS_COLUMN])
2147 if (i++)
2148 printf ("; ");
2150 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
2151 const struct pivot_value *value = pivot_table_get (
2152 table, dindexes);
2153 if (value)
2154 pivot_value_dump (value, table);
2156 printf ("\n");
2158 x++;
2161 free (column_enumeration);
2162 free (row_enumeration);
2163 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
2166 pivot_table_dump_value (table->caption, "caption", table, indentation);
2168 for (size_t i = 0; i < table->n_footnotes; i++)
2170 const struct pivot_footnote *f = table->footnotes[i];
2171 indent (indentation);
2172 putchar ('[');
2173 if (f->marker)
2174 pivot_value_dump (f->marker, table);
2175 else
2176 printf ("%zu", f->idx);
2177 putchar (']');
2178 pivot_value_dump (f->content, table);
2179 putchar ('\n');
2182 free (dindexes);
2185 static const char *
2186 consume_int (const char *p, size_t *n)
2188 *n = 0;
2189 while (c_isdigit (*p))
2190 *n = *n * 10 + (*p++ - '0');
2191 return p;
2194 static size_t
2195 pivot_format_inner_template (struct string *out, const char *template,
2196 char escape,
2197 struct pivot_value **values, size_t n_values,
2198 const struct pivot_table *pt)
2200 size_t args_consumed = 0;
2201 while (*template && *template != ':')
2203 if (*template == '\\' && template[1])
2205 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2206 template += 2;
2208 else if (*template == escape)
2210 size_t index;
2211 template = consume_int (template + 1, &index);
2212 if (index >= 1 && index <= n_values)
2214 pivot_value_format (values[index - 1], pt, out);
2215 args_consumed = MAX (args_consumed, index);
2218 else
2219 ds_put_byte (out, *template++);
2221 return args_consumed;
2224 static const char *
2225 pivot_extract_inner_template (const char *template, const char **p)
2227 *p = template;
2229 for (;;)
2231 if (*template == '\\' && template[1] != '\0')
2232 template += 2;
2233 else if (*template == ':')
2234 return template + 1;
2235 else if (*template == '\0')
2236 return template;
2237 else
2238 template++;
2242 static void
2243 pivot_format_template (struct string *out, const char *template,
2244 const struct pivot_argument *args, size_t n_args,
2245 const struct pivot_table *pt)
2247 while (*template)
2249 if (*template == '\\' && template[1] != '\0')
2251 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
2252 template += 2;
2254 else if (*template == '^')
2256 size_t index;
2257 template = consume_int (template + 1, &index);
2258 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
2259 pivot_value_format (args[index - 1].values[0], pt, out);
2261 else if (*template == '[')
2263 const char *tmpl[2];
2264 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
2265 template = pivot_extract_inner_template (template, &tmpl[1]);
2266 template += *template == ']';
2268 size_t index;
2269 template = consume_int (template, &index);
2270 if (index < 1 || index > n_args)
2271 continue;
2273 const struct pivot_argument *arg = &args[index - 1];
2274 size_t left = arg->n;
2275 while (left)
2277 struct pivot_value **values = arg->values + (arg->n - left);
2278 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
2279 char escape = "%^"[tmpl_idx];
2280 size_t used = pivot_format_inner_template (
2281 out, tmpl[tmpl_idx], escape, values, left, pt);
2282 if (!used || used > left)
2283 break;
2284 left -= used;
2287 else
2288 ds_put_byte (out, *template++);
2292 static enum settings_value_show
2293 interpret_show (enum settings_value_show global_show,
2294 enum settings_value_show table_show,
2295 enum settings_value_show value_show,
2296 bool has_label)
2298 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
2299 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
2300 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
2301 : global_show);
2304 /* Appends a text representation of the body of VALUE to OUT. Settings on
2305 PT control whether variable and value labels are included.
2307 The "body" omits subscripts and superscripts and footnotes.
2309 Returns true if OUT is a number (or a number plus a value label), false
2310 otherwise. */
2311 bool
2312 pivot_value_format_body (const struct pivot_value *value,
2313 const struct pivot_table *pt,
2314 struct string *out)
2316 enum settings_value_show show;
2317 bool numeric = false;
2319 switch (value->type)
2321 case PIVOT_VALUE_NUMERIC:
2322 show = interpret_show (settings_get_show_values (),
2323 pt->show_values,
2324 value->numeric.show,
2325 value->numeric.value_label != NULL);
2326 if (show & SETTINGS_VALUE_SHOW_VALUE)
2328 const struct fmt_spec *f = &value->numeric.format;
2329 const struct fmt_spec *format
2330 = (f->type == FMT_F
2331 && value->numeric.honor_small
2332 && value->numeric.x != 0
2333 && fabs (value->numeric.x) < pt->small
2334 ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
2335 : f);
2337 char *s = data_out (&(union value) { .f = value->numeric.x },
2338 "UTF-8", format, &pt->settings);
2339 ds_put_cstr (out, s + strspn (s, " "));
2340 free (s);
2342 if (show & SETTINGS_VALUE_SHOW_LABEL)
2344 if (show & SETTINGS_VALUE_SHOW_VALUE)
2345 ds_put_byte (out, ' ');
2346 ds_put_cstr (out, value->numeric.value_label);
2348 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
2349 break;
2351 case PIVOT_VALUE_STRING:
2352 show = interpret_show (settings_get_show_values (),
2353 pt->show_values,
2354 value->string.show,
2355 value->string.value_label != NULL);
2356 if (show & SETTINGS_VALUE_SHOW_VALUE)
2358 if (value->string.hex)
2360 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
2361 *p; p++)
2362 ds_put_format (out, "%02X", *p);
2364 else
2365 ds_put_cstr (out, value->string.s);
2367 if (show & SETTINGS_VALUE_SHOW_LABEL)
2369 if (show & SETTINGS_VALUE_SHOW_VALUE)
2370 ds_put_byte (out, ' ');
2371 ds_put_cstr (out, value->string.value_label);
2373 break;
2375 case PIVOT_VALUE_VARIABLE:
2376 show = interpret_show (settings_get_show_variables (),
2377 pt->show_variables,
2378 value->variable.show,
2379 value->variable.var_label != NULL);
2380 if (show & SETTINGS_VALUE_SHOW_VALUE)
2381 ds_put_cstr (out, value->variable.var_name);
2382 if (show & SETTINGS_VALUE_SHOW_LABEL)
2384 if (show & SETTINGS_VALUE_SHOW_VALUE)
2385 ds_put_byte (out, ' ');
2386 ds_put_cstr (out, value->variable.var_label);
2388 break;
2390 case PIVOT_VALUE_TEXT:
2391 ds_put_cstr (out, value->text.local);
2392 break;
2394 case PIVOT_VALUE_TEMPLATE:
2395 pivot_format_template (out, value->template.local, value->template.args,
2396 value->template.n_args, pt);
2397 break;
2400 return numeric;
2403 /* Appends a text representation of VALUE to OUT. Settings on
2404 PT control whether variable and value labels are included.
2406 Subscripts and footnotes are included.
2408 Returns true if OUT is a number (or a number plus a value label), false
2409 otherwise. */
2410 bool
2411 pivot_value_format (const struct pivot_value *value,
2412 const struct pivot_table *pt,
2413 struct string *out)
2415 bool numeric = pivot_value_format_body (value, pt, out);
2417 if (value->n_subscripts)
2419 for (size_t i = 0; i < value->n_subscripts; i++)
2420 ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
2423 for (size_t i = 0; i < value->n_footnotes; i++)
2425 ds_put_byte (out, '[');
2427 size_t idx = value->footnote_indexes[i];
2428 const struct pivot_footnote *f = pt->footnotes[idx];
2429 pivot_footnote_format_marker (f, pt, out);
2431 ds_put_byte (out, ']');
2434 return numeric;
2437 /* Returns a text representation of VALUE. The caller must free the string,
2438 with free(). */
2439 char *
2440 pivot_value_to_string (const struct pivot_value *value,
2441 const struct pivot_table *pt)
2443 struct string s = DS_EMPTY_INITIALIZER;
2444 pivot_value_format (value, pt, &s);
2445 return ds_steal_cstr (&s);
2448 char *
2449 pivot_value_to_string_defaults (const struct pivot_value *value)
2451 static const struct pivot_table pt = {
2452 .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
2453 .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
2455 return pivot_value_to_string (value, &pt);
2458 struct pivot_value *
2459 pivot_value_clone (const struct pivot_value *old)
2461 if (!old)
2462 return NULL;
2464 struct pivot_value *new = xmemdup (old, sizeof *new);
2465 if (old->font_style)
2467 new->font_style = xmalloc (sizeof *new->font_style);
2468 font_style_copy (NULL, new->font_style, old->font_style);
2470 if (old->cell_style)
2471 new->cell_style = xmemdup (old->cell_style, sizeof *new->cell_style);
2472 if (old->n_subscripts)
2474 new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
2475 for (size_t i = 0; i < old->n_subscripts; i++)
2476 new->subscripts[i] = xstrdup (old->subscripts[i]);
2478 if (old->n_footnotes)
2479 new->footnote_indexes = xmemdup (
2480 old->footnote_indexes, old->n_footnotes * sizeof *new->footnote_indexes);
2482 switch (new->type)
2484 case PIVOT_VALUE_NUMERIC:
2485 new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
2486 new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
2487 break;
2489 case PIVOT_VALUE_STRING:
2490 new->string.s = xstrdup (new->string.s);
2491 new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
2492 new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
2493 break;
2495 case PIVOT_VALUE_VARIABLE:
2496 new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
2497 new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
2498 break;
2500 case PIVOT_VALUE_TEXT:
2501 new->text.local = xstrdup (old->text.local);
2502 new->text.c = (old->text.c == old->text.local ? new->text.local
2503 : xstrdup_if_nonnull (old->text.c));
2504 new->text.id = (old->text.id == old->text.local ? new->text.local
2505 : old->text.id == old->text.c ? new->text.c
2506 : xstrdup_if_nonnull (old->text.id));
2507 break;
2509 case PIVOT_VALUE_TEMPLATE:
2510 new->template.local = xstrdup (old->template.local);
2511 new->template.id = (old->template.id == old->template.local
2512 ? new->template.local
2513 : xstrdup (old->template.id));
2514 new->template.args = xmalloc (new->template.n_args
2515 * sizeof *new->template.args);
2516 for (size_t i = 0; i < old->template.n_args; i++)
2517 pivot_argument_copy (&new->template.args[i],
2518 &old->template.args[i]);
2519 break;
2521 default:
2522 NOT_REACHED ();
2524 return new;
2527 /* Frees the data owned by V. */
2528 void
2529 pivot_value_destroy (struct pivot_value *value)
2531 if (value)
2533 font_style_uninit (value->font_style);
2534 free (value->font_style);
2535 free (value->cell_style);
2536 free (value->footnote_indexes);
2538 for (size_t i = 0; i < value->n_subscripts; i++)
2539 free (value->subscripts[i]);
2540 free (value->subscripts);
2542 switch (value->type)
2544 case PIVOT_VALUE_NUMERIC:
2545 free (value->numeric.var_name);
2546 free (value->numeric.value_label);
2547 break;
2549 case PIVOT_VALUE_STRING:
2550 free (value->string.s);
2551 free (value->string.var_name);
2552 free (value->string.value_label);
2553 break;
2555 case PIVOT_VALUE_VARIABLE:
2556 free (value->variable.var_name);
2557 free (value->variable.var_label);
2558 break;
2560 case PIVOT_VALUE_TEXT:
2561 free (value->text.local);
2562 if (value->text.c != value->text.local)
2563 free (value->text.c);
2564 if (value->text.id != value->text.local
2565 && value->text.id != value->text.c)
2566 free (value->text.id);
2567 break;
2569 case PIVOT_VALUE_TEMPLATE:
2570 free (value->template.local);
2571 if (value->template.id != value->template.local)
2572 free (value->template.id);
2573 for (size_t i = 0; i < value->template.n_args; i++)
2574 pivot_argument_uninit (&value->template.args[i]);
2575 free (value->template.args);
2576 break;
2578 default:
2579 NOT_REACHED ();
2581 free (value);
2585 /* Sets AREA to the style to use for VALUE, with defaults coming from
2586 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
2587 void
2588 pivot_value_get_style (struct pivot_value *value,
2589 const struct font_style *base_font_style,
2590 const struct cell_style *base_cell_style,
2591 struct table_area_style *area)
2593 font_style_copy (NULL, &area->font_style, (value->font_style
2594 ? value->font_style
2595 : base_font_style));
2596 area->cell_style = *(value->cell_style
2597 ? value->cell_style
2598 : base_cell_style);
2601 /* Copies AREA into VALUE's style. */
2602 void
2603 pivot_value_set_style (struct pivot_value *value,
2604 const struct table_area_style *area)
2606 pivot_value_set_font_style (value, &area->font_style);
2607 pivot_value_set_cell_style (value, &area->cell_style);
2610 void
2611 pivot_value_set_font_style (struct pivot_value *value,
2612 const struct font_style *font_style)
2614 if (value->font_style)
2615 font_style_uninit (value->font_style);
2616 else
2617 value->font_style = xmalloc (sizeof *value->font_style);
2618 font_style_copy (NULL, value->font_style, font_style);
2621 void
2622 pivot_value_set_cell_style (struct pivot_value *value,
2623 const struct cell_style *cell_style)
2625 if (!value->cell_style)
2626 value->cell_style = xmalloc (sizeof *value->cell_style);
2627 *value->cell_style = *cell_style;
2630 void
2631 pivot_argument_copy (struct pivot_argument *dst,
2632 const struct pivot_argument *src)
2634 *dst = (struct pivot_argument) {
2635 .n = src->n,
2636 .values = xmalloc (src->n * sizeof *dst->values),
2639 for (size_t i = 0; i < src->n; i++)
2640 dst->values[i] = pivot_value_clone (src->values[i]);
2643 /* Frees the data owned by ARG (but not ARG itself). */
2644 void
2645 pivot_argument_uninit (struct pivot_argument *arg)
2647 if (arg)
2649 for (size_t i = 0; i < arg->n; i++)
2650 pivot_value_destroy (arg->values[i]);
2651 free (arg->values);
2655 /* Creates and returns a new pivot_value whose contents is the null-terminated
2656 string TEXT. Takes ownership of TEXT.
2658 This function is for text strings provided by the user (with the exception
2659 that pivot_value_new_variable() should be used for variable names). For
2660 strings that are part of the PSPP user interface, such as names of
2661 procedures, statistics, annotations, error messages, etc., use
2662 pivot_value_new_text(). */
2663 struct pivot_value *
2664 pivot_value_new_user_text_nocopy (char *text)
2666 struct pivot_value *value = xmalloc (sizeof *value);
2667 *value = (struct pivot_value) {
2668 .type = PIVOT_VALUE_TEXT,
2669 .text = {
2670 .local = text,
2671 .c = text,
2672 .id = text,
2673 .user_provided = true,
2676 return value;
2679 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2680 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2681 in advance.
2683 This function is for text strings provided by the user (with the exception
2684 that pivot_value_new_variable() should be used for variable names). For
2685 strings that are part of the PSPP user interface, such as names of
2686 procedures, statistics, annotations, error messages, etc., use
2687 pivot_value_new_text().j
2689 The caller retains ownership of TEXT.*/
2690 struct pivot_value *
2691 pivot_value_new_user_text (const char *text, size_t length)
2693 return pivot_value_new_user_text_nocopy (
2694 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2697 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2698 a translatable string, but not actually translated yet, e.g. enclosed in
2699 N_(). This function is for text strings that are part of the PSPP user
2700 interface, such as names of procedures, statistics, annotations, error
2701 messages, etc. For strings that come from the user, use
2702 pivot_value_new_user_text(). */
2703 struct pivot_value *
2704 pivot_value_new_text (const char *text)
2706 char *c = xstrdup (text);
2707 char *local = xstrdup (gettext (c));
2709 struct pivot_value *value = xmalloc (sizeof *value);
2710 *value = (struct pivot_value) {
2711 .type = PIVOT_VALUE_TEXT,
2712 .text = {
2713 .local = local,
2714 .c = c,
2715 .id = c,
2716 .user_provided = false,
2719 return value;
2722 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2723 string. */
2724 struct pivot_value * PRINTF_FORMAT (1, 2)
2725 pivot_value_new_text_format (const char *format, ...)
2727 va_list args;
2728 va_start (args, format);
2729 char *c = xvasprintf (format, args);
2730 va_end (args);
2732 va_start (args, format);
2733 char *local = xvasprintf (gettext (format), args);
2734 va_end (args);
2736 struct pivot_value *value = xmalloc (sizeof *value);
2737 *value = (struct pivot_value) {
2738 .type = PIVOT_VALUE_TEXT,
2739 .text = {
2740 .local = local,
2741 .c = c,
2742 .id = xstrdup (c),
2743 .user_provided = false,
2746 return value;
2749 /* Returns a new pivot_value that represents X.
2751 The format to use for X is unspecified. Usually the easiest way to specify
2752 a format is through assigning a result class to one of the categories that
2753 the pivot_value will end up in. If that is not suitable, then the caller
2754 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2755 struct pivot_value *
2756 pivot_value_new_number (double x)
2758 struct pivot_value *value = xmalloc (sizeof *value);
2759 *value = (struct pivot_value) {
2760 .type = PIVOT_VALUE_NUMERIC,
2761 .numeric = { .x = x, },
2763 return value;
2766 /* Returns a new pivot_value that represents X, formatted as an integer. */
2767 struct pivot_value *
2768 pivot_value_new_integer (double x)
2770 struct pivot_value *value = pivot_value_new_number (x);
2771 value->numeric.format = (struct fmt_spec) { FMT_F, 40, 0 };
2772 return value;
2775 /* Returns a new pivot_value that represents VALUE, formatted as for
2776 VARIABLE. */
2777 struct pivot_value *
2778 pivot_value_new_var_value (const struct variable *variable,
2779 const union value *value)
2781 struct pivot_value *pv = pivot_value_new_value (
2782 value, var_get_width (variable), var_get_print_format (variable),
2783 var_get_encoding (variable));
2785 char *var_name = xstrdup (var_get_name (variable));
2786 if (var_is_alpha (variable))
2787 pv->string.var_name = var_name;
2788 else
2789 pv->numeric.var_name = var_name;
2791 const char *label = var_lookup_value_label (variable, value);
2792 if (label)
2794 if (var_is_alpha (variable))
2795 pv->string.value_label = xstrdup (label);
2796 else
2797 pv->numeric.value_label = xstrdup (label);
2800 return pv;
2803 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2804 formatted with FORMAT. For a string value, ENCODING must be its character
2805 encoding. */
2806 struct pivot_value *
2807 pivot_value_new_value (const union value *value, int width,
2808 const struct fmt_spec *format, const char *encoding)
2810 struct pivot_value *pv = xzalloc (sizeof *pv);
2811 if (width > 0)
2813 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2814 width);
2815 size_t n = strlen (s);
2816 while (n > 0 && s[n - 1] == ' ')
2817 s[--n] = '\0';
2819 pv->type = PIVOT_VALUE_STRING;
2820 pv->string.s = s;
2821 pv->string.hex = format->type == FMT_AHEX;
2823 else
2825 pv->type = PIVOT_VALUE_NUMERIC;
2826 pv->numeric.x = value->f;
2827 pv->numeric.format = *format;
2830 return pv;
2833 /* Returns a new pivot_value for VARIABLE. */
2834 struct pivot_value *
2835 pivot_value_new_variable (const struct variable *variable)
2837 struct pivot_value *value = xmalloc (sizeof *value);
2838 *value = (struct pivot_value) {
2839 .type = PIVOT_VALUE_VARIABLE,
2840 .variable = {
2841 .var_name = xstrdup (var_get_name (variable)),
2842 .var_label = xstrdup_if_nonempty (var_get_label (variable)),
2845 return value;
2848 /* Attaches a reference to FOOTNOTE to V. */
2849 void
2850 pivot_value_add_footnote (struct pivot_value *v,
2851 const struct pivot_footnote *footnote)
2853 /* Some legacy tables include numerous duplicate footnotes. Suppress
2854 them. */
2855 for (size_t i = 0; i < v->n_footnotes; i++)
2856 if (v->footnote_indexes[i] == footnote->idx)
2857 return;
2859 v->footnote_indexes = xrealloc (
2860 v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
2861 v->footnote_indexes[v->n_footnotes++] = footnote->idx;
2862 pivot_value_sort_footnotes (v);
2865 static int
2866 compare_footnote_indexes (const void *a_, const void *b_)
2868 const size_t *ap = a_;
2869 const size_t *bp = b_;
2870 size_t a = *ap;
2871 size_t b = *bp;
2872 return a < b ? -1 : a > b;
2875 /* Sorts the footnote references in V in the standard ascending order.
2877 This is only necessary if code adds (plural) footnotes to a pivot_value by
2878 itself, because pivot_value_add_footnote() does it automatically. */
2879 void
2880 pivot_value_sort_footnotes (struct pivot_value *v)
2882 if (v->n_footnotes > 1)
2883 qsort (v->footnote_indexes, v->n_footnotes, sizeof *v->footnote_indexes,
2884 compare_footnote_indexes);
2887 /* If VALUE is a numeric value, and RC is a result class such as
2888 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2889 void
2890 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2891 const char *rc)
2893 if (value->type == PIVOT_VALUE_NUMERIC)
2894 pivot_table_use_rc (table, rc,
2895 &value->numeric.format, &value->numeric.honor_small);