1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017, 2018 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "output/pivot-table.h"
20
21 #include <stdlib.h>
22
23 #include "data/data-out.h"
24 #include "data/settings.h"
25 #include "data/value.h"
26 #include "data/variable.h"
27 #include "libpspp/hash-functions.h"
28 #include "libpspp/i18n.h"
29 #include "output/driver.h"
30
31 #include "gl/c-ctype.h"
32 #include "gl/intprops.h"
33 #include "gl/minmax.h"
34 #include "gl/xalloc.h"
35 #include "gl/xmemdup0.h"
36 #include "gl/xsize.h"
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40 #define N_(msgid) msgid
41
42 static const struct fmt_spec *pivot_table_get_format (
43 const struct pivot_table *, const char *s);
44
45 /* Pivot table display styling. */
46
47 /* Returns the name of AREA. */
48 const char *
pivot_area_to_string(enum pivot_area area)49 pivot_area_to_string (enum pivot_area area)
50 {
51 switch (area)
52 {
53 case PIVOT_AREA_TITLE: return "title";
54 case PIVOT_AREA_CAPTION: return "caption";
55 case PIVOT_AREA_FOOTER: return "footer";
56 case PIVOT_AREA_CORNER: return "corner";
57 case PIVOT_AREA_COLUMN_LABELS: return "column labels";
58 case PIVOT_AREA_ROW_LABELS: return "row labels";
59 case PIVOT_AREA_DATA: return "data";
60 case PIVOT_AREA_LAYERS: return "layers";
61 case PIVOT_N_AREAS: default: return "**error**";
62 }
63 }
64
65 const struct area_style *
pivot_area_get_default_style(enum pivot_area area)66 pivot_area_get_default_style (enum pivot_area area)
67 {
68 #define STYLE(BOLD, H, V, L, R, T, B) { \
69 .cell_style = { \
70 .halign = TABLE_HALIGN_##H, \
71 .valign = TABLE_VALIGN_##V, \
72 .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \
73 [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \
74 }, \
75 .font_style = { \
76 .bold = BOLD, \
77 .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \
78 .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \
79 .size = 9, \
80 .typeface = (char *) "Sans Serif", \
81 }, \
82 }
83 static const struct area_style default_area_styles[PIVOT_N_AREAS] = {
84 [PIVOT_AREA_TITLE] = STYLE(true, CENTER, CENTER, 8,11,1,8),
85 [PIVOT_AREA_CAPTION] = STYLE(false, LEFT, TOP, 8,11,1,1),
86 [PIVOT_AREA_FOOTER] = STYLE(false, LEFT, TOP, 11, 8,2,3),
87 [PIVOT_AREA_CORNER] = STYLE(false, LEFT, BOTTOM, 8,11,1,1),
88 [PIVOT_AREA_COLUMN_LABELS] = STYLE(false, CENTER, BOTTOM, 8,11,1,3),
89 [PIVOT_AREA_ROW_LABELS] = STYLE(false, LEFT, TOP, 8,11,1,3),
90 [PIVOT_AREA_DATA] = STYLE(false, MIXED, TOP, 8,11,1,1),
91 [PIVOT_AREA_LAYERS] = STYLE(false, LEFT, BOTTOM, 8,11,1,3),
92 };
93 #undef STYLE
94
95 return &default_area_styles[area];
96 }
97
98 void
pivot_border_get_default_style(enum pivot_border border,struct table_border_style * style)99 pivot_border_get_default_style (enum pivot_border border,
100 struct table_border_style *style)
101 {
102 static const enum table_stroke default_strokes[PIVOT_N_BORDERS] = {
103 [PIVOT_BORDER_TITLE] = TABLE_STROKE_NONE,
104 [PIVOT_BORDER_OUTER_LEFT] = TABLE_STROKE_NONE,
105 [PIVOT_BORDER_OUTER_TOP] = TABLE_STROKE_NONE,
106 [PIVOT_BORDER_OUTER_RIGHT] = TABLE_STROKE_NONE,
107 [PIVOT_BORDER_OUTER_BOTTOM] = TABLE_STROKE_NONE,
108 [PIVOT_BORDER_INNER_LEFT] = TABLE_STROKE_THICK,
109 [PIVOT_BORDER_INNER_TOP] = TABLE_STROKE_THICK,
110 [PIVOT_BORDER_INNER_RIGHT] = TABLE_STROKE_THICK,
111 [PIVOT_BORDER_INNER_BOTTOM] = TABLE_STROKE_THICK,
112 [PIVOT_BORDER_DATA_LEFT] = TABLE_STROKE_THICK,
113 [PIVOT_BORDER_DATA_TOP] = TABLE_STROKE_THICK,
114 [PIVOT_BORDER_DIM_ROW_HORZ] = TABLE_STROKE_SOLID,
115 [PIVOT_BORDER_DIM_ROW_VERT] = TABLE_STROKE_NONE,
116 [PIVOT_BORDER_DIM_COL_HORZ] = TABLE_STROKE_SOLID,
117 [PIVOT_BORDER_DIM_COL_VERT] = TABLE_STROKE_SOLID,
118 [PIVOT_BORDER_CAT_ROW_HORZ] = TABLE_STROKE_NONE,
119 [PIVOT_BORDER_CAT_ROW_VERT] = TABLE_STROKE_NONE,
120 [PIVOT_BORDER_CAT_COL_HORZ] = TABLE_STROKE_SOLID,
121 [PIVOT_BORDER_CAT_COL_VERT] = TABLE_STROKE_SOLID,
122 };
123 *style = (struct table_border_style) {
124 .stroke = default_strokes[border],
125 .color = CELL_COLOR_BLACK,
126 };
127 }
128
129 /* Returns the name of BORDER. */
130 const char *
pivot_border_to_string(enum pivot_border border)131 pivot_border_to_string (enum pivot_border border)
132 {
133 switch (border)
134 {
135 case PIVOT_BORDER_TITLE:
136 return "title";
137
138 case PIVOT_BORDER_OUTER_LEFT:
139 return "left outer frame";
140 case PIVOT_BORDER_OUTER_TOP:
141 return "top outer frame";
142 case PIVOT_BORDER_OUTER_RIGHT:
143 return "right outer frame";
144 case PIVOT_BORDER_OUTER_BOTTOM:
145 return "bottom outer frame";
146
147 case PIVOT_BORDER_INNER_LEFT:
148 return "left inner frame";
149 case PIVOT_BORDER_INNER_TOP:
150 return "top inner frame";
151 case PIVOT_BORDER_INNER_RIGHT:
152 return "right inner frame";
153 case PIVOT_BORDER_INNER_BOTTOM:
154 return "bottom inner frame";
155
156 case PIVOT_BORDER_DATA_LEFT:
157 return "data area left";
158 case PIVOT_BORDER_DATA_TOP:
159 return "data area top";
160
161 case PIVOT_BORDER_DIM_ROW_HORZ:
162 return "row label horizontal dimension border";
163 case PIVOT_BORDER_DIM_ROW_VERT:
164 return "row label vertical dimension border";
165 case PIVOT_BORDER_DIM_COL_HORZ:
166 return "column label horizontal dimension border";
167 case PIVOT_BORDER_DIM_COL_VERT:
168 return "column label vertical dimension border";
169
170 case PIVOT_BORDER_CAT_ROW_HORZ:
171 return "row label horizontal category border";
172 case PIVOT_BORDER_CAT_ROW_VERT:
173 return "row label vertical category border";
174 case PIVOT_BORDER_CAT_COL_HORZ:
175 return "column label horizontal category border";
176 case PIVOT_BORDER_CAT_COL_VERT:
177 return "column label vertical category border";
178
179 case PIVOT_N_BORDERS:
180 default:
181 return "**error**";
182 }
183 }
184
185 void
pivot_table_sizing_uninit(struct pivot_table_sizing * sizing)186 pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
187 {
188 if (sizing)
189 {
190 free (sizing->widths);
191 free (sizing->breaks);
192 free (sizing->keeps);
193 }
194 }
195
196 /* Axes. */
197
198 /* Returns the name of AXIS_TYPE. */
199 const char *
pivot_axis_type_to_string(enum pivot_axis_type axis_type)200 pivot_axis_type_to_string (enum pivot_axis_type axis_type)
201 {
202 switch (axis_type)
203 {
204 case PIVOT_AXIS_LAYER:
205 return "layer";
206
207 case PIVOT_AXIS_ROW:
208 return "row";
209
210 case PIVOT_AXIS_COLUMN:
211 return "column";
212
213 default:
214 return "<error>";
215 }
216 }
217
218 static enum pivot_axis_type
pivot_axis_type_transpose(enum pivot_axis_type axis_type)219 pivot_axis_type_transpose (enum pivot_axis_type axis_type)
220 {
221 assert (axis_type == PIVOT_AXIS_ROW || axis_type == PIVOT_AXIS_COLUMN);
222 return (axis_type == PIVOT_AXIS_ROW ? PIVOT_AXIS_COLUMN : PIVOT_AXIS_ROW);
223 }
224
225 /* Implementation of PIVOT_AXIS_FOR_EACH. */
226 size_t *
pivot_axis_iterator_next(size_t * indexes,const struct pivot_axis * axis)227 pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
228 {
229 if (!indexes)
230 {
231 if (axis->n_dimensions)
232 for (size_t i = 0; i < axis->n_dimensions; i++)
233 if (axis->dimensions[i]->n_leaves == 0)
234 return NULL;
235
236 return xcalloc (axis->n_dimensions, sizeof *indexes);
237 }
238
239 for (size_t i = 0; i < axis->n_dimensions; i++)
240 {
241 const struct pivot_dimension *d = axis->dimensions[i];
242 if (++indexes[i] < d->n_leaves)
243 return indexes;
244
245 indexes[i] = 0;
246 }
247
248 free (indexes);
249 return NULL;
250 }
251
252 /* Dimensions. */
253
254 static void
pivot_category_set_rc(struct pivot_category * category,const char * s)255 pivot_category_set_rc (struct pivot_category *category, const char *s)
256 {
257 const struct fmt_spec *format = pivot_table_get_format (
258 category->dimension->table, s);
259 if (format)
260 category->format = *format;
261 }
262
263 static void
pivot_category_create_leaves_valist(struct pivot_category * parent,va_list args)264 pivot_category_create_leaves_valist (struct pivot_category *parent,
265 va_list args)
266 {
267 const char *s;
268 while ((s = va_arg (args, const char *)))
269 {
270 if (!strncmp (s, "RC_", 3))
271 {
272 assert (parent->n_subs);
273 pivot_category_set_rc (parent->subs[parent->n_subs - 1], s);
274 }
275 else
276 pivot_category_create_leaf (parent, pivot_value_new_text (s));
277 }
278 }
279
280 /* Creates a new dimension with the given NAME in TABLE and returns it. The
281 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
282 that axis.
283
284 NAME should be a translatable name, but not actually translated yet,
285 e.g. enclosed in N_(). To use a different kind of value for a name, use
286 pivot_dimension_create__() instead.
287
288 The optional varargs parameters may be used to add an initial set of
289 categories to the dimension. Each string should be a translatable category
290 name, but not actually translated yet, e.g. enclosed in N_(). Each string
291 may optionally be followod by a PIVOT_RC_* string that specifies the default
292 numeric format for cells in this category. */
293 struct pivot_dimension * SENTINEL (0)
294 (pivot_dimension_create) (struct pivot_table *table,
295 enum pivot_axis_type axis_type,
296 const char *name, ...)
297 {
298 struct pivot_dimension *d = pivot_dimension_create__ (
299 table, axis_type, pivot_value_new_text (name));
300
301 va_list args;
302 va_start (args, name);
303 pivot_category_create_leaves_valist (d->root, args);
304 va_end (args);
305
306 return d;
307 }
308
309 /* Creates a new dimension with the given NAME in TABLE and returns it. The
310 dimension is added to axis AXIS_TYPE, becoming the outermost dimension on
311 that axis. */
312 struct pivot_dimension *
pivot_dimension_create__(struct pivot_table * table,enum pivot_axis_type axis_type,struct pivot_value * name)313 pivot_dimension_create__ (struct pivot_table *table,
314 enum pivot_axis_type axis_type,
315 struct pivot_value *name)
316 {
317 assert (pivot_table_is_empty (table));
318
319 struct pivot_dimension *d = xmalloc (sizeof *d);
320 *d = (struct pivot_dimension) {
321 .table = table,
322 .axis_type = axis_type,
323 .level = table->axes[axis_type].n_dimensions,
324 .top_index = table->n_dimensions,
325 .root = xmalloc (sizeof *d->root),
326 };
327
328 struct pivot_category *root = d->root;
329 *root = (struct pivot_category) {
330 .name = name,
331 .parent = NULL,
332 .dimension = d,
333 .show_label = false,
334 .data_index = SIZE_MAX,
335 .presentation_index = SIZE_MAX,
336 };
337
338 table->dimensions = xrealloc (
339 table->dimensions, (table->n_dimensions + 1) * sizeof *table->dimensions);
340 table->dimensions[table->n_dimensions++] = d;
341
342 struct pivot_axis *axis = &table->axes[axis_type];
343 axis->dimensions = xrealloc (
344 axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions);
345 axis->dimensions[axis->n_dimensions++] = d;
346
347 if (axis_type == PIVOT_AXIS_LAYER)
348 {
349 free (table->current_layer);
350 table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions,
351 sizeof *table->current_layer);
352 }
353
354 /* axis->extent and axis->label_depth will be calculated later. */
355
356 return d;
357 }
358
359 void
pivot_dimension_destroy(struct pivot_dimension * d)360 pivot_dimension_destroy (struct pivot_dimension *d)
361 {
362 if (!d)
363 return;
364
365 pivot_category_destroy (d->root);
366 free (d->data_leaves);
367 free (d->presentation_leaves);
368 free (d);
369 }
370
371 /* Returns the first leaf node in an in-order traversal that is a child of
372 CAT. */
373 static const struct pivot_category * UNUSED
pivot_category_first_leaf(const struct pivot_category * cat)374 pivot_category_first_leaf (const struct pivot_category *cat)
375 {
376 if (pivot_category_is_leaf (cat))
377 return cat;
378
379 for (size_t i = 0; i < cat->n_subs; i++)
380 {
381 const struct pivot_category *first
382 = pivot_category_first_leaf (cat->subs[i]);
383 if (first)
384 return first;
385 }
386
387 return NULL;
388 }
389
390 /* Returns the next leaf node in an in-order traversal starting at CAT, which
391 must be a leaf. */
392 static const struct pivot_category * UNUSED
pivot_category_next_leaf(const struct pivot_category * cat)393 pivot_category_next_leaf (const struct pivot_category *cat)
394 {
395 assert (pivot_category_is_leaf (cat));
396
397 for (;;)
398 {
399 const struct pivot_category *parent = cat->parent;
400 if (!parent)
401 return NULL;
402 for (size_t i = cat->group_index + 1; i < parent->n_subs; i++)
403 {
404 const struct pivot_category *next
405 = pivot_category_first_leaf (parent->subs[i]);
406 if (next)
407 return next;
408 }
409
410 cat = cat->parent;
411 }
412 }
413
414 static void
pivot_category_add_child(struct pivot_category * child)415 pivot_category_add_child (struct pivot_category *child)
416 {
417 struct pivot_category *parent = child->parent;
418
419 assert (pivot_category_is_group (parent));
420 if (parent->n_subs >= parent->allocated_subs)
421 parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
422 sizeof *parent->subs);
423 parent->subs[parent->n_subs++] = child;
424 }
425
426 /* Adds leaf categories as a child of PARENT. To create top-level categories
427 within dimension 'd', pass 'd->root' for PARENT.
428
429 Each of the varargs parameters should be a string, each of which should be a
430 translatable category name, but not actually translated yet, e.g. enclosed
431 in N_(). Each string may optionally be followod by a PIVOT_RC_* string that
432 specifies the default numeric format for cells in this category.
433
434 Returns the category index, which is just a 0-based array index, for the
435 first new category.
436
437 Leaves have to be created in in-order, that is, don't create a group and add
438 some leaves, then add leaves outside the group and try to add more leaves
439 inside it. */
440 int SENTINEL (0)
441 (pivot_category_create_leaves) (struct pivot_category *parent, ...)
442 {
443 int retval = parent->dimension->n_leaves;
444
445 va_list args;
446 va_start (args, parent);
447 pivot_category_create_leaves_valist (parent, args);
448 va_end (args);
449
450 return retval;
451 }
452
453 /* Creates a new leaf category with the given NAME as a child of PARENT. To
454 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
455 Returns the category index, which is just a 0-based array index, for the new
456 category.
457
458 Leaves have to be created in in-order, that is, don't create a group and add
459 some leaves, then add leaves outside the group and try to add more leaves
460 inside it. */
461 int
pivot_category_create_leaf(struct pivot_category * parent,struct pivot_value * name)462 pivot_category_create_leaf (struct pivot_category *parent,
463 struct pivot_value *name)
464 {
465 return pivot_category_create_leaf_rc (parent, name, NULL);
466 }
467
468 /* Creates a new leaf category with the given NAME as a child of PARENT. To
469 create a top-level category within dimension 'd', pass 'd->root' for PARENT.
470 Returns the category index, which is just a 0-based array index, for the new
471 category.
472
473 If RC is nonnull and the name of a result category, the category is assigned
474 that result category.
475
476 Leaves have to be created in in-order, that is, don't create a group and add
477 some leaves, then add leaves outside the group and try to add more leaves
478 inside it. */
479 int
pivot_category_create_leaf_rc(struct pivot_category * parent,struct pivot_value * name,const char * rc)480 pivot_category_create_leaf_rc (struct pivot_category *parent,
481 struct pivot_value *name, const char *rc)
482 {
483 struct pivot_dimension *d = parent->dimension;
484
485 struct pivot_category *leaf = xmalloc (sizeof *leaf);
486 *leaf = (struct pivot_category) {
487 .name = name,
488 .parent = parent,
489 .dimension = d,
490 .group_index = parent->n_subs,
491 .data_index = d->n_leaves,
492 .presentation_index = d->n_leaves,
493 };
494
495 if (d->n_leaves >= d->allocated_leaves)
496 {
497 d->data_leaves = x2nrealloc (d->data_leaves, &d->allocated_leaves,
498 sizeof *d->data_leaves);
499 d->presentation_leaves = xrealloc (
500 d->presentation_leaves,
501 d->allocated_leaves * sizeof *d->presentation_leaves);
502 }
503
504 d->data_leaves[d->n_leaves] = leaf;
505 d->presentation_leaves[d->n_leaves] = leaf;
506 d->n_leaves++;
507
508 pivot_category_add_child (leaf);
509
510 /* Make sure that the new child is the last in in-order. */
511 assert (!pivot_category_next_leaf (leaf));
512
513 pivot_category_set_rc (leaf, rc);
514
515 return leaf->data_index;
516 }
517
518 /* Adds a new category group named NAME as a child of PARENT. To create a
519 top-level group within dimension 'd', pass 'd->root' for PARENT.
520
521 NAME should be a translatable name, but not actually translated yet,
522 e.g. enclosed in N_(). To use a different kind of value for a name, use
523 pivot_category_create_group__() instead.
524
525 The optional varargs parameters may be used to add an initial set of
526 categories to the group. Each string should be a translatable category
527 name, but not actually translated yet, e.g. enclosed in N_(). Each string
528 may optionally be followod by a PIVOT_RC_* string that specifies the default
529 numeric format for cells in this category.
530
531 Returns the new group. */
532 struct pivot_category * SENTINEL (0)
533 (pivot_category_create_group) (struct pivot_category *parent,
534 const char *name, ...)
535 {
536 struct pivot_category *group = pivot_category_create_group__ (
537 parent, pivot_value_new_text (name));
538
539 va_list args;
540 va_start (args, name);
541 pivot_category_create_leaves_valist (group, args);
542 va_end (args);
543
544 return group;
545 }
546
547 /* Adds a new category group named NAME as a child of PARENT. To create a
548 top-level group within dimension 'd', pass 'd->root' for PARENT. Returns
549 the new group. */
550 struct pivot_category *
pivot_category_create_group__(struct pivot_category * parent,struct pivot_value * name)551 pivot_category_create_group__ (struct pivot_category *parent,
552 struct pivot_value *name)
553 {
554 struct pivot_dimension *d = parent->dimension;
555
556 struct pivot_category *group = xmalloc (sizeof *group);
557 *group = (struct pivot_category) {
558 .name = name,
559 .parent = parent,
560 .dimension = d,
561 .show_label = true,
562 .group_index = parent->n_subs,
563 .data_index = SIZE_MAX,
564 .presentation_index = SIZE_MAX,
565 };
566
567 pivot_category_add_child (group);
568
569 return group;
570 }
571
572 void
pivot_category_destroy(struct pivot_category * c)573 pivot_category_destroy (struct pivot_category *c)
574 {
575 if (!c)
576 return;
577
578 pivot_value_destroy (c->name);
579 for (size_t i = 0; i < c->n_subs; i++)
580 pivot_category_destroy (c->subs[i]);
581 free (c->subs);
582 free (c);
583 }
584
585 /* Result classes.
586
587 These are usually the easiest way to control the formatting of numeric data
588 in a pivot table. See pivot_dimension_create() for an explanation of their
589 use. */
590 struct result_class
591 {
592 const char *name; /* "RC_*". */
593 struct fmt_spec format;
594 };
595
596 /* Formats for most of the result classes. */
597 static struct result_class result_classes[] =
598 {
599 { PIVOT_RC_INTEGER, { FMT_F, 40, 0 } },
600 { PIVOT_RC_PERCENT, { FMT_PCT, 40, 1 } },
601 { PIVOT_RC_CORRELATION, { FMT_F, 40, 3 } },
602 { PIVOT_RC_SIGNIFICANCE, { FMT_F, 40, 3 } },
603 { PIVOT_RC_RESIDUAL, { FMT_F, 40, 2 } },
604 { PIVOT_RC_COUNT, { 0, 0, 0 } },
605 { PIVOT_RC_OTHER, { 0, 0, 0 } },
606 };
607
608 /* Has PIVOT_RC_COUNT been overridden by the user? */
609 static bool overridden_count_format;
610
611 static struct result_class *
pivot_result_class_find(const char * s)612 pivot_result_class_find (const char *s)
613 {
614 for (size_t i = 0; i < sizeof result_classes / sizeof *result_classes; i++)
615 if (!strcmp (s, result_classes[i].name))
616 return &result_classes[i];
617 return NULL;
618 }
619
620 static const struct fmt_spec *
pivot_table_get_format(const struct pivot_table * table,const char * s)621 pivot_table_get_format (const struct pivot_table *table, const char *s)
622 {
623 if (!s)
624 return NULL;
625 else if (!strcmp (s, PIVOT_RC_OTHER))
626 return settings_get_format ();
627 else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
628 return &table->weight_format;
629 else
630 {
631 const struct result_class *rc = pivot_result_class_find (s);
632 return rc ? &rc->format : NULL;
633 }
634 }
635
636 /* Sets the format specification for the result class named S (which should not
637 include the RC_ prefix) to *FORMAT. Returns true if successful, false if S
638 does not name a known result class. */
639 bool
pivot_result_class_change(const char * s_,const struct fmt_spec * format)640 pivot_result_class_change (const char *s_, const struct fmt_spec *format)
641 {
642 char *s = xasprintf ("RC_%s", s_);
643 struct result_class *rc = pivot_result_class_find (s);
644 if (rc)
645 {
646 rc->format = *format;
647 if (!strcmp (s, PIVOT_RC_COUNT))
648 overridden_count_format = true;
649 }
650 free (s);
651
652 return rc != NULL;
653 }
654
655 /* Pivot tables. */
656
657 /* Creates and returns a new pivot table with the given TITLE. TITLE should be
658 a text string marked for translation but not actually translated yet,
659 e.g. N_("Descriptive Statistics"). The un-translated text string is used as
660 the pivot table's subtype.
661
662 Operations commonly performed on the new pivot_table:
663
664 - If empty rows or columns should not be displayed, set ->omit_empty to
665 true.
666
667 - Set the format to use for "count" values with pivot_table_set_weight_var()
668 or pivot_table_set_weight_format().
669
670 This function is a shortcut for pivot_table_create__() for the most common
671 case. Use pivot_table_create__() directly if the title should be some kind
672 of value other than an ordinary text string, or if the subtype should be
673 different from the title.
674
675 See the large comment at the top of pivot-table.h for general advice on
676 creating pivot tables. */
677 struct pivot_table *
pivot_table_create(const char * title)678 pivot_table_create (const char *title)
679 {
680 return pivot_table_create__ (pivot_value_new_text (title), title);
681 }
682
683 /* Creates and returns a new pivot table with the given TITLE, and takes
684 ownership of TITLE. The new pivot table's subtype is SUBTYPE, which
685 should be an untranslated English string that describes the contents of
686 the table at a high level without being specific about the variables or
687 other context involved.
688
689 Operations commonly performed on the new pivot_table:
690
691 - If empty rows or columns should not be displayed, set ->omit_empty to
692 true.
693
694 - Set the format to use for "count" values with pivot_table_set_weight_var()
695 or pivot_table_set_weight_format().
696
697 See the large comment at the top of pivot-table.h for general advice on
698 creating pivot tables. */
699 struct pivot_table *
pivot_table_create__(struct pivot_value * title,const char * subtype)700 pivot_table_create__ (struct pivot_value *title, const char *subtype)
701 {
702 struct pivot_table *table = xzalloc (sizeof *table);
703 table->ref_cnt = 1;
704 table->show_caption = true;
705 table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 };
706 table->title = title;
707 table->subtype = subtype ? pivot_value_new_text (subtype) : NULL;
708 table->command_c = output_get_command_name ();
709
710 table->sizing[TABLE_HORZ].range[0] = 50;
711 table->sizing[TABLE_HORZ].range[1] = 72;
712 table->sizing[TABLE_VERT].range[0] = 36;
713 table->sizing[TABLE_VERT].range[1] = 120;
714
715 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
716 area_style_copy (NULL, &table->areas[i], pivot_area_get_default_style (i));
717
718 /* Set default border styles. */
719 static const enum table_stroke default_strokes[PIVOT_N_BORDERS] = {
720 [PIVOT_BORDER_TITLE] = TABLE_STROKE_NONE,
721 [PIVOT_BORDER_OUTER_LEFT] = TABLE_STROKE_NONE,
722 [PIVOT_BORDER_OUTER_TOP] = TABLE_STROKE_NONE,
723 [PIVOT_BORDER_OUTER_RIGHT] = TABLE_STROKE_NONE,
724 [PIVOT_BORDER_OUTER_BOTTOM] = TABLE_STROKE_NONE,
725 [PIVOT_BORDER_INNER_LEFT] = TABLE_STROKE_THICK,
726 [PIVOT_BORDER_INNER_TOP] = TABLE_STROKE_THICK,
727 [PIVOT_BORDER_INNER_RIGHT] = TABLE_STROKE_THICK,
728 [PIVOT_BORDER_INNER_BOTTOM] = TABLE_STROKE_THICK,
729 [PIVOT_BORDER_DATA_LEFT] = TABLE_STROKE_THICK,
730 [PIVOT_BORDER_DATA_TOP] = TABLE_STROKE_THICK,
731 [PIVOT_BORDER_DIM_ROW_HORZ] = TABLE_STROKE_SOLID,
732 [PIVOT_BORDER_DIM_ROW_VERT] = TABLE_STROKE_NONE,
733 [PIVOT_BORDER_DIM_COL_HORZ] = TABLE_STROKE_SOLID,
734 [PIVOT_BORDER_DIM_COL_VERT] = TABLE_STROKE_SOLID,
735 [PIVOT_BORDER_CAT_ROW_HORZ] = TABLE_STROKE_NONE,
736 [PIVOT_BORDER_CAT_ROW_VERT] = TABLE_STROKE_NONE,
737 [PIVOT_BORDER_CAT_COL_HORZ] = TABLE_STROKE_SOLID,
738 [PIVOT_BORDER_CAT_COL_VERT] = TABLE_STROKE_SOLID,
739 };
740 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
741 table->borders[i] = (struct table_border_style) {
742 .stroke = default_strokes[i],
743 .color = CELL_COLOR_BLACK,
744 };
745
746 table->row_labels_in_corner = true;
747 hmap_init (&table->cells);
748
749 return table;
750 }
751
752 /* Creates and returns a new pivot table with the given TITLE and a single cell
753 with the given CONTENT.
754
755 This is really just for error handling. */
756 struct pivot_table *
pivot_table_create_for_text(struct pivot_value * title,struct pivot_value * content)757 pivot_table_create_for_text (struct pivot_value *title,
758 struct pivot_value *content)
759 {
760 struct pivot_table *table = pivot_table_create__ (title, "Error");
761
762 struct pivot_dimension *d = pivot_dimension_create (
763 table, PIVOT_AXIS_ROW, N_("Error"));
764 d->hide_all_labels = true;
765 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
766
767 pivot_table_put1 (table, 0, content);
768
769 return table;
770 }
771
772 /* Increases TABLE's reference count, indicating that it has an additional
773 owner. A pivot table that is shared among multiple owners must not be
774 modified. */
775 struct pivot_table *
pivot_table_ref(const struct pivot_table * table_)776 pivot_table_ref (const struct pivot_table *table_)
777 {
778 struct pivot_table *table = CONST_CAST (struct pivot_table *, table_);
779 table->ref_cnt++;
780 return table;
781 }
782
783 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
784 If TABLE no longer has any owners, it is freed. */
785 void
pivot_table_unref(struct pivot_table * table)786 pivot_table_unref (struct pivot_table *table)
787 {
788 if (!table)
789 return;
790 assert (table->ref_cnt > 0);
791 if (--table->ref_cnt)
792 return;
793
794 free (table->current_layer);
795 free (table->table_look);
796
797 for (int i = 0; i < TABLE_N_AXES; i++)
798 pivot_table_sizing_uninit (&table->sizing[i]);
799
800 free (table->continuation);
801
802 for (int i = 0; i < sizeof table->ccs / sizeof *table->ccs; i++)
803 free (table->ccs[i]);
804
805 free (table->command_local);
806 free (table->command_c);
807 free (table->language);
808 free (table->locale);
809
810 free (table->dataset);
811 free (table->datafile);
812
813 for (size_t i = 0; i < table->n_footnotes; i++)
814 pivot_footnote_destroy (table->footnotes[i]);
815 free (table->footnotes);
816
817 pivot_value_destroy (table->title);
818 pivot_value_destroy (table->subtype);
819 pivot_value_destroy (table->corner_text);
820 pivot_value_destroy (table->caption);
821
822 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
823 area_style_uninit (&table->areas[i]);
824
825 for (size_t i = 0; i < table->n_dimensions; i++)
826 pivot_dimension_destroy (table->dimensions[i]);
827 free (table->dimensions);
828
829 for (size_t i = 0; i < PIVOT_N_AXES; i++)
830 free (table->axes[i].dimensions);
831
832 struct pivot_cell *cell, *next_cell;
833 HMAP_FOR_EACH_SAFE (cell, next_cell, struct pivot_cell, hmap_node,
834 &table->cells)
835 {
836 hmap_delete (&table->cells, &cell->hmap_node);
837 pivot_value_destroy (cell->value);
838 free (cell);
839 }
840 hmap_destroy (&table->cells);
841
842 free (table);
843 }
844
845 /* Returns true if TABLE has more than one owner. A pivot table that is shared
846 among multiple owners must not be modified. */
847 bool
pivot_table_is_shared(const struct pivot_table * table)848 pivot_table_is_shared (const struct pivot_table *table)
849 {
850 return table->ref_cnt > 1;
851 }
852
853 /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable
854 WV, which should be the weight variable for the dictionary whose data or
855 statistics are being put into TABLE.
856
857 This has no effect if WV is NULL. */
858 void
pivot_table_set_weight_var(struct pivot_table * table,const struct variable * wv)859 pivot_table_set_weight_var (struct pivot_table *table,
860 const struct variable *wv)
861 {
862 if (wv)
863 pivot_table_set_weight_format (table, var_get_print_format (wv));
864 }
865
866 /* Sets the format used for PIVOT_RC_COUNT cells to WFMT, which should be the
867 format for the dictionary whose data or statistics are being put into TABLE.
868
869 This has no effect if WFMT is NULL. */
870 void
pivot_table_set_weight_format(struct pivot_table * table,const struct fmt_spec * wfmt)871 pivot_table_set_weight_format (struct pivot_table *table,
872 const struct fmt_spec *wfmt)
873 {
874 if (wfmt)
875 table->weight_format = *wfmt;
876 }
877
878 /* Returns true if TABLE has no cells, false otherwise. */
879 bool
pivot_table_is_empty(const struct pivot_table * table)880 pivot_table_is_empty (const struct pivot_table *table)
881 {
882 return hmap_is_empty (&table->cells);
883 }
884
885 static unsigned int
pivot_cell_hash_indexes(const size_t * indexes,size_t n_idx)886 pivot_cell_hash_indexes (const size_t *indexes, size_t n_idx)
887 {
888 return hash_bytes (indexes, n_idx * sizeof *indexes, 0);
889 }
890
891 static bool
equal_indexes(const size_t * a,const unsigned int * b,size_t n)892 equal_indexes (const size_t *a, const unsigned int *b, size_t n)
893 {
894 for (size_t i = 0; i < n; i++)
895 if (a[i] != b[i])
896 return false;
897
898 return true;
899 }
900
901 static struct pivot_cell *
pivot_table_lookup_cell__(const struct pivot_table * table,const size_t * dindexes,unsigned int hash)902 pivot_table_lookup_cell__ (const struct pivot_table *table,
903 const size_t *dindexes, unsigned int hash)
904 {
905 struct pivot_cell *cell;
906 HMAP_FOR_EACH_WITH_HASH (cell, struct pivot_cell, hmap_node, hash,
907 &table->cells)
908 if (equal_indexes (dindexes, cell->idx, table->n_dimensions))
909 return cell;
910 return false;
911 }
912
913 static struct pivot_cell *
pivot_cell_allocate(size_t n_idx)914 pivot_cell_allocate (size_t n_idx)
915 {
916 struct pivot_cell *cell UNUSED;
917 return xmalloc (sizeof *cell + n_idx * sizeof *cell->idx);
918 }
919
920 static struct pivot_cell *
pivot_table_insert_cell(struct pivot_table * table,const size_t * dindexes)921 pivot_table_insert_cell (struct pivot_table *table, const size_t *dindexes)
922 {
923 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
924 struct pivot_cell *cell = pivot_table_lookup_cell__ (table, dindexes, hash);
925 if (!cell)
926 {
927 cell = pivot_cell_allocate (table->n_dimensions);
928 for (size_t i = 0; i < table->n_dimensions; i++)
929 cell->idx[i] = dindexes[i];
930 cell->value = NULL;
931 hmap_insert (&table->cells, &cell->hmap_node, hash);
932 }
933 return cell;
934 }
935
936 /* Puts VALUE in the cell in TABLE whose indexes are given by the N indexes in
937 DINDEXES. N must be the number of dimensions in TABLE. Takes ownership of
938 VALUE.
939
940 If VALUE is a numeric value without a specified format, this function checks
941 each of the categories designated by DINDEXES[] and takes the format from
942 the first category with a result class. If none has a result class, uses
943 the overall default numeric format. */
944 void
pivot_table_put(struct pivot_table * table,const size_t * dindexes,size_t n,struct pivot_value * value)945 pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
946 struct pivot_value *value)
947 {
948 assert (n == table->n_dimensions);
949
950 if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
951 {
952 for (size_t i = 0; i < table->n_dimensions; i++)
953 {
954 const struct pivot_dimension *d = table->dimensions[i];
955 if (dindexes[i] < d->n_leaves)
956 {
957 const struct pivot_category *c = d->data_leaves[dindexes[i]];
958 if (c->format.w)
959 {
960 value->numeric.format = c->format;
961 goto done;
962 }
963 }
964 }
965 value->numeric.format = *settings_get_format ();
966
967 done:;
968 }
969
970 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
971 pivot_value_destroy (cell->value);
972 cell->value = value;
973 }
974
975 /* Puts VALUE in the cell in TABLE with index IDX1. TABLE must have 1
976 dimension. Takes ownership of VALUE. */
977 void
pivot_table_put1(struct pivot_table * table,size_t idx1,struct pivot_value * value)978 pivot_table_put1 (struct pivot_table *table, size_t idx1,
979 struct pivot_value *value)
980 {
981 size_t dindexes[] = { idx1 };
982 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
983 }
984
985 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2). TABLE must have 2
986 dimensions. Takes ownership of VALUE. */
987 void
pivot_table_put2(struct pivot_table * table,size_t idx1,size_t idx2,struct pivot_value * value)988 pivot_table_put2 (struct pivot_table *table, size_t idx1, size_t idx2,
989 struct pivot_value *value)
990 {
991 size_t dindexes[] = { idx1, idx2 };
992 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
993 }
994
995 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3). TABLE must
996 have 3 dimensions. Takes ownership of VALUE. */
997 void
pivot_table_put3(struct pivot_table * table,size_t idx1,size_t idx2,size_t idx3,struct pivot_value * value)998 pivot_table_put3 (struct pivot_table *table, size_t idx1, size_t idx2,
999 size_t idx3, struct pivot_value *value)
1000 {
1001 size_t dindexes[] = { idx1, idx2, idx3 };
1002 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1003 }
1004
1005 /* Puts VALUE in the cell in TABLE with index (IDX1, IDX2, IDX3, IDX4). TABLE
1006 must have 4 dimensions. Takes ownership of VALUE. */
1007 void
pivot_table_put4(struct pivot_table * table,size_t idx1,size_t idx2,size_t idx3,size_t idx4,struct pivot_value * value)1008 pivot_table_put4 (struct pivot_table *table, size_t idx1, size_t idx2,
1009 size_t idx3, size_t idx4, struct pivot_value *value)
1010 {
1011 size_t dindexes[] = { idx1, idx2, idx3, idx4 };
1012 pivot_table_put (table, dindexes, sizeof dindexes / sizeof *dindexes, value);
1013 }
1014
1015 /* Creates and returns a new footnote in TABLE with the given CONTENT and an
1016 automatically assigned marker.
1017
1018 The footnote will only appear in output if it is referenced. Use
1019 pivot_value_add_footnote() to add a reference to the footnote. */
1020 struct pivot_footnote *
pivot_table_create_footnote(struct pivot_table * table,struct pivot_value * content)1021 pivot_table_create_footnote (struct pivot_table *table,
1022 struct pivot_value *content)
1023 {
1024 return pivot_table_create_footnote__ (table, table->n_footnotes,
1025 NULL, content);
1026 }
1027
1028 static struct pivot_value *
pivot_make_default_footnote_marker(int idx,bool show_numeric_markers)1029 pivot_make_default_footnote_marker (int idx, bool show_numeric_markers)
1030 {
1031 char text[INT_BUFSIZE_BOUND (size_t)];
1032 if (show_numeric_markers)
1033 snprintf (text, sizeof text, "%d", idx + 1);
1034 else
1035 str_format_26adic (idx + 1, false, text, sizeof text);
1036 return pivot_value_new_user_text (text, -1);
1037 }
1038
1039 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
1040 all lower indexes as a side effect). If MARKER is nonnull, sets the
1041 footnote's marker; if CONTENT is nonnull, sets the footnote's content. */
1042 struct pivot_footnote *
pivot_table_create_footnote__(struct pivot_table * table,size_t idx,struct pivot_value * marker,struct pivot_value * content)1043 pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
1044 struct pivot_value *marker,
1045 struct pivot_value *content)
1046 {
1047 if (idx >= table->n_footnotes)
1048 {
1049 while (idx >= table->allocated_footnotes)
1050 table->footnotes = x2nrealloc (table->footnotes,
1051 &table->allocated_footnotes,
1052 sizeof *table->footnotes);
1053 while (idx >= table->n_footnotes)
1054 {
1055 struct pivot_footnote *f = xmalloc (sizeof *f);
1056 f->idx = table->n_footnotes;
1057 f->marker = pivot_make_default_footnote_marker (
1058 f->idx, table->show_numeric_markers);
1059 f->content = NULL;
1060 f->show = true;
1061
1062 table->footnotes[table->n_footnotes++] = f;
1063 }
1064 }
1065
1066 struct pivot_footnote *f = table->footnotes[idx];
1067 if (marker)
1068 {
1069 pivot_value_destroy (f->marker);
1070 f->marker = marker;
1071 }
1072 if (content)
1073 {
1074 pivot_value_destroy (f->content);
1075 f->content = content;
1076 }
1077 return f;
1078 }
1079
1080 /* Frees the data owned by F. */
1081 void
pivot_footnote_destroy(struct pivot_footnote * f)1082 pivot_footnote_destroy (struct pivot_footnote *f)
1083 {
1084 if (f)
1085 {
1086 pivot_value_destroy (f->content);
1087 pivot_value_destroy (f->marker);
1088 free (f);
1089 }
1090 }
1091
1092 /* Converts per-axis presentation-order indexes, given in PINDEXES, into data
1093 indexes for each dimension in TABLE in DINDEXES[]. */
1094 void
pivot_table_convert_indexes_ptod(const struct pivot_table * table,const size_t * pindexes[PIVOT_N_AXES],size_t dindexes[])1095 pivot_table_convert_indexes_ptod (const struct pivot_table *table,
1096 const size_t *pindexes[PIVOT_N_AXES],
1097 size_t dindexes[/* table->n_dimensions */])
1098 {
1099 for (size_t i = 0; i < PIVOT_N_AXES; i++)
1100 {
1101 const struct pivot_axis *axis = &table->axes[i];
1102
1103 for (size_t j = 0; j < axis->n_dimensions; j++)
1104 {
1105 const struct pivot_dimension *d = axis->dimensions[j];
1106 dindexes[d->top_index]
1107 = d->presentation_leaves[pindexes[i][j]]->data_index;
1108 }
1109 }
1110 }
1111
1112 size_t *
pivot_table_enumerate_axis(const struct pivot_table * table,enum pivot_axis_type axis_type,const size_t * layer_indexes,bool omit_empty,size_t * n)1113 pivot_table_enumerate_axis (const struct pivot_table *table,
1114 enum pivot_axis_type axis_type,
1115 const size_t *layer_indexes, bool omit_empty,
1116 size_t *n)
1117 {
1118 const struct pivot_axis *axis = &table->axes[axis_type];
1119 if (!axis->n_dimensions)
1120 {
1121 size_t *enumeration = xnmalloc (2, sizeof *enumeration);
1122 enumeration[0] = 0;
1123 enumeration[1] = SIZE_MAX;
1124 if (n)
1125 *n = 1;
1126 return enumeration;
1127 }
1128 else if (!axis->extent)
1129 {
1130 size_t *enumeration = xmalloc (sizeof *enumeration);
1131 *enumeration = SIZE_MAX;
1132 if (n)
1133 *n = 0;
1134 return enumeration;
1135 }
1136
1137 size_t *enumeration = xnmalloc (xsum (xtimes (axis->extent,
1138 axis->n_dimensions), 1),
1139 sizeof *enumeration);
1140 size_t *p = enumeration;
1141 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1142
1143 size_t *axis_indexes;
1144 PIVOT_AXIS_FOR_EACH (axis_indexes, axis)
1145 {
1146 if (omit_empty)
1147 {
1148 enum pivot_axis_type axis2_type
1149 = pivot_axis_type_transpose (axis_type);
1150
1151 size_t *axis2_indexes;
1152 PIVOT_AXIS_FOR_EACH (axis2_indexes, &table->axes[axis2_type])
1153 {
1154 const size_t *pindexes[PIVOT_N_AXES];
1155 pindexes[PIVOT_AXIS_LAYER] = layer_indexes;
1156 pindexes[axis_type] = axis_indexes;
1157 pindexes[axis2_type] = axis2_indexes;
1158 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1159 if (pivot_table_get (table, dindexes))
1160 goto found;
1161 }
1162 continue;
1163
1164 found:
1165 free (axis2_indexes);
1166 }
1167
1168 memcpy (p, axis_indexes, axis->n_dimensions * sizeof *p);
1169 p += axis->n_dimensions;
1170 }
1171 *p = SIZE_MAX;
1172 if (n)
1173 *n = (p - enumeration) / axis->n_dimensions;
1174
1175 free (dindexes);
1176 return enumeration;
1177 }
1178
1179 static const struct pivot_cell *
pivot_table_lookup_cell(const struct pivot_table * table,const size_t * dindexes)1180 pivot_table_lookup_cell (const struct pivot_table *table,
1181 const size_t *dindexes)
1182 {
1183 unsigned int hash = pivot_cell_hash_indexes (dindexes, table->n_dimensions);
1184 return pivot_table_lookup_cell__ (table, dindexes, hash);
1185 }
1186
1187 const struct pivot_value *
pivot_table_get(const struct pivot_table * table,const size_t * dindexes)1188 pivot_table_get (const struct pivot_table *table, const size_t *dindexes)
1189 {
1190 const struct pivot_cell *cell = pivot_table_lookup_cell (table, dindexes);
1191 return cell ? cell->value : NULL;
1192 }
1193
1194 struct pivot_value *
pivot_table_get_rw(struct pivot_table * table,const size_t * dindexes)1195 pivot_table_get_rw (struct pivot_table *table, const size_t *dindexes)
1196 {
1197 struct pivot_cell *cell = pivot_table_insert_cell (table, dindexes);
1198 if (!cell->value)
1199 cell->value = pivot_value_new_user_text ("", -1);
1200 return cell->value;
1201 }
1202
1203 static void
distribute_extra_depth(struct pivot_category * category,size_t extra_depth)1204 distribute_extra_depth (struct pivot_category *category, size_t extra_depth)
1205 {
1206 if (pivot_category_is_group (category) && category->n_subs)
1207 for (size_t i = 0; i < category->n_subs; i++)
1208 distribute_extra_depth (category->subs[i], extra_depth);
1209 else
1210 category->extra_depth += extra_depth;
1211 }
1212
1213 static void
pivot_category_assign_label_depth(struct pivot_category * category,bool dimension_labels_in_corner)1214 pivot_category_assign_label_depth (struct pivot_category *category,
1215 bool dimension_labels_in_corner)
1216 {
1217 category->extra_depth = 0;
1218
1219 if (pivot_category_is_group (category))
1220 {
1221 size_t depth = 0;
1222 for (size_t i = 0; i < category->n_subs; i++)
1223 {
1224 pivot_category_assign_label_depth (category->subs[i], false);
1225 depth = MAX (depth, category->subs[i]->label_depth);
1226 }
1227
1228 for (size_t i = 0; i < category->n_subs; i++)
1229 {
1230 struct pivot_category *sub = category->subs[i];
1231
1232 size_t extra_depth = depth - sub->label_depth;
1233 if (extra_depth)
1234 distribute_extra_depth (sub, extra_depth);
1235
1236 sub->label_depth = depth;
1237 }
1238
1239 category->show_label_in_corner = (category->show_label
1240 && dimension_labels_in_corner);
1241 category->label_depth
1242 = (category->show_label && !category->show_label_in_corner
1243 ? depth + 1 : depth);
1244 }
1245 else
1246 category->label_depth = 1;
1247 }
1248
1249 static bool
pivot_axis_assign_label_depth(struct pivot_table * table,enum pivot_axis_type axis_type,bool dimension_labels_in_corner)1250 pivot_axis_assign_label_depth (struct pivot_table *table,
1251 enum pivot_axis_type axis_type,
1252 bool dimension_labels_in_corner)
1253 {
1254 struct pivot_axis *axis = &table->axes[axis_type];
1255 bool any_label_shown_in_corner = false;
1256 axis->label_depth = 0;
1257 axis->extent = 1;
1258 for (size_t i = 0; i < axis->n_dimensions; i++)
1259 {
1260 struct pivot_dimension *d = axis->dimensions[i];
1261 pivot_category_assign_label_depth (d->root, dimension_labels_in_corner);
1262 d->label_depth = d->hide_all_labels ? 0 : d->root->label_depth;
1263 axis->label_depth += d->label_depth;
1264 axis->extent *= d->n_leaves;
1265
1266 if (d->root->show_label_in_corner)
1267 any_label_shown_in_corner = true;
1268 }
1269 return any_label_shown_in_corner;
1270 }
1271
1272 void
pivot_table_assign_label_depth(struct pivot_table * table)1273 pivot_table_assign_label_depth (struct pivot_table *table)
1274 {
1275 pivot_axis_assign_label_depth (table, PIVOT_AXIS_COLUMN, false);
1276 if (pivot_axis_assign_label_depth (
1277 table, PIVOT_AXIS_ROW, (table->row_labels_in_corner
1278 && !table->corner_text))
1279 && table->axes[PIVOT_AXIS_COLUMN].label_depth == 0)
1280 table->axes[PIVOT_AXIS_COLUMN].label_depth = 1;
1281 pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
1282 }
1283
1284 /* Footnotes. */
1285
1286
1287
1288 static void
indent(int indentation)1289 indent (int indentation)
1290 {
1291 for (int i = 0; i < indentation * 2; i++)
1292 putchar (' ');
1293 }
1294
1295 static void
pivot_value_dump(const struct pivot_value * value)1296 pivot_value_dump (const struct pivot_value *value)
1297 {
1298 char *s = pivot_value_to_string (value, SETTINGS_VALUE_SHOW_DEFAULT,
1299 SETTINGS_VALUE_SHOW_DEFAULT);
1300 fputs (s, stdout);
1301 free (s);
1302 }
1303
1304 static void
pivot_table_dump_value(const struct pivot_value * value,const char * name,int indentation)1305 pivot_table_dump_value (const struct pivot_value *value, const char *name,
1306 int indentation)
1307 {
1308 if (value)
1309 {
1310 indent (indentation);
1311 printf ("%s: ", name);
1312 pivot_value_dump (value);
1313 putchar ('\n');
1314 }
1315 }
1316
1317 static void
pivot_table_dump_string(const char * string,const char * name,int indentation)1318 pivot_table_dump_string (const char *string, const char *name, int indentation)
1319 {
1320 if (string)
1321 {
1322 indent (indentation);
1323 printf ("%s: %s\n", name, string);
1324 }
1325 }
1326
1327 static void
pivot_category_dump(const struct pivot_category * c,int indentation)1328 pivot_category_dump (const struct pivot_category *c, int indentation)
1329 {
1330 indent (indentation);
1331 printf ("%s \"", pivot_category_is_leaf (c) ? "leaf" : "group");
1332 pivot_value_dump (c->name);
1333 printf ("\" ");
1334
1335 if (pivot_category_is_leaf (c))
1336 printf ("data_index=%zu\n", c->data_index);
1337 else
1338 {
1339 printf (" (label %s)", c->show_label ? "shown" : "hidden");
1340 printf ("\n");
1341
1342 for (size_t i = 0; i < c->n_subs; i++)
1343 pivot_category_dump (c->subs[i], indentation + 1);
1344 }
1345 }
1346
1347 void
pivot_dimension_dump(const struct pivot_dimension * d,int indentation)1348 pivot_dimension_dump (const struct pivot_dimension *d, int indentation)
1349 {
1350 indent (indentation);
1351 printf ("%s dimension %zu (where 0=innermost), label_depth=%d:\n",
1352 pivot_axis_type_to_string (d->axis_type), d->level, d->label_depth);
1353
1354 pivot_category_dump (d->root, indentation + 1);
1355 }
1356
1357 static void
area_style_dump(enum pivot_area area,const struct area_style * a,int indentation)1358 area_style_dump (enum pivot_area area, const struct area_style *a,
1359 int indentation)
1360 {
1361 indent (indentation);
1362 printf ("%s: ", pivot_area_to_string (area));
1363 font_style_dump (&a->font_style);
1364 putchar (' ');
1365 cell_style_dump (&a->cell_style);
1366 putchar ('\n');
1367 }
1368
1369 static void
table_border_style_dump(enum pivot_border border,const struct table_border_style * b,int indentation)1370 table_border_style_dump (enum pivot_border border,
1371 const struct table_border_style *b, int indentation)
1372 {
1373 indent (indentation);
1374 printf ("%s: %s ", pivot_border_to_string (border),
1375 table_stroke_to_string (b->stroke));
1376 cell_color_dump (&b->color);
1377 putchar ('\n');
1378 }
1379
1380 static char ***
compose_headings(const struct pivot_axis * axis,const size_t * column_enumeration,enum settings_value_show show_values,enum settings_value_show show_variables)1381 compose_headings (const struct pivot_axis *axis,
1382 const size_t *column_enumeration,
1383 enum settings_value_show show_values,
1384 enum settings_value_show show_variables)
1385 {
1386 if (!axis->n_dimensions || !axis->extent || !axis->label_depth)
1387 return NULL;
1388
1389 char ***headings = xnmalloc (axis->label_depth, sizeof *headings);
1390 for (size_t i = 0; i < axis->label_depth; i++)
1391 headings[i] = xcalloc (axis->extent, sizeof **headings);
1392
1393 const size_t *indexes;
1394 size_t column = 0;
1395 PIVOT_ENUMERATION_FOR_EACH (indexes, column_enumeration, axis)
1396 {
1397 int row = axis->label_depth - 1;
1398 for (int dim_index = 0; dim_index < axis->n_dimensions; dim_index++)
1399 {
1400 const struct pivot_dimension *d = axis->dimensions[dim_index];
1401 if (d->hide_all_labels)
1402 continue;
1403 for (const struct pivot_category *c
1404 = d->presentation_leaves[indexes[dim_index]];
1405 c;
1406 c = c->parent)
1407 {
1408 if (pivot_category_is_leaf (c) || (c->show_label
1409 && !c->show_label_in_corner))
1410 {
1411 headings[row][column] = pivot_value_to_string (
1412 c->name, show_values, show_variables);
1413 if (!*headings[row][column])
1414 headings[row][column] = xstrdup ("<blank>");
1415 row--;
1416 }
1417 }
1418 }
1419 column++;
1420 }
1421
1422 return headings;
1423 }
1424
1425 static void
free_headings(const struct pivot_axis * axis,char *** headings)1426 free_headings (const struct pivot_axis *axis, char ***headings)
1427 {
1428 for (size_t i = 0; i < axis->label_depth; i++)
1429 {
1430 for (size_t j = 0; j < axis->extent; j++)
1431 free (headings[i][j]);
1432 free (headings[i]);
1433 }
1434 free (headings);
1435 }
1436
1437 static void
pivot_table_sizing_dump(const char * name,const struct pivot_table_sizing * s,int indentation)1438 pivot_table_sizing_dump (const char *name, const struct pivot_table_sizing *s,
1439 int indentation)
1440 {
1441 indent (indentation);
1442 printf ("%ss: min=%d, max=%d\n", name, s->range[0], s->range[1]);
1443 if (s->n_widths)
1444 {
1445 indent (indentation + 1);
1446 printf ("%s widths:", name);
1447 for (size_t i = 0; i < s->n_widths; i++)
1448 printf (" %d", s->widths[i]);
1449 printf ("\n");
1450 }
1451 if (s->n_breaks)
1452 {
1453 indent (indentation + 1);
1454 printf ("break after %ss:", name);
1455 for (size_t i = 0; i < s->n_breaks; i++)
1456 printf (" %zu", s->breaks[i]);
1457 printf ("\n");
1458 }
1459 if (s->n_keeps)
1460 {
1461 indent (indentation + 1);
1462 printf ("keep %ss together:", name);
1463 for (size_t i = 0; i < s->n_keeps; i++)
1464 printf (" [%zu,%zu]",
1465 s->keeps[i].ofs,
1466 s->keeps[i].ofs + s->keeps[i].n - 1);
1467 printf ("\n");
1468 }
1469 }
1470
1471 void
pivot_table_dump(const struct pivot_table * table,int indentation)1472 pivot_table_dump (const struct pivot_table *table, int indentation)
1473 {
1474 if (!table)
1475 return;
1476
1477 int old_decimal = settings_get_decimal_char (FMT_COMMA);
1478 if (table->decimal == '.' || table->decimal == ',')
1479 settings_set_decimal_char (table->decimal);
1480
1481 pivot_table_dump_value (table->title, "title", indentation);
1482 pivot_table_dump_string (table->command_c, "command", indentation);
1483 pivot_table_dump_string (table->dataset, "dataset", indentation);
1484 pivot_table_dump_string (table->datafile, "datafile", indentation);
1485 pivot_table_dump_string (table->notes, "notes", indentation);
1486 pivot_table_dump_string (table->table_look, "table-look", indentation);
1487 if (table->date)
1488 {
1489 indent (indentation);
1490
1491 struct tm *tm = localtime (&table->date);
1492 printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900,
1493 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
1494 tm->tm_sec);
1495 }
1496
1497 indent (indentation);
1498 printf ("sizing:\n");
1499 pivot_table_sizing_dump ("column", &table->sizing[TABLE_HORZ],
1500 indentation + 1);
1501 pivot_table_sizing_dump ("row", &table->sizing[TABLE_VERT],
1502 indentation + 1);
1503
1504 indent (indentation);
1505 printf ("areas:\n");
1506 for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++)
1507 area_style_dump (area, &table->areas[area], indentation + 1);
1508
1509 indent (indentation);
1510 printf ("borders:\n");
1511 for (enum pivot_border border = 0; border < PIVOT_N_BORDERS; border++)
1512 table_border_style_dump (border, &table->borders[border], indentation + 1);
1513
1514 for (size_t i = 0; i < table->n_dimensions; i++)
1515 pivot_dimension_dump (table->dimensions[i], indentation);
1516
1517 /* Presentation and data indexes. */
1518 size_t *dindexes = XCALLOC (table->n_dimensions, size_t);
1519
1520 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
1521 if (layer_axis->n_dimensions)
1522 {
1523 indent (indentation);
1524 printf ("current layer:");
1525
1526 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
1527 {
1528 const struct pivot_dimension *d = layer_axis->dimensions[i];
1529 char *name = pivot_value_to_string (d->root->name,
1530 table->show_values,
1531 table->show_variables);
1532 char *value = pivot_value_to_string (
1533 d->data_leaves[table->current_layer[i]]->name,
1534 table->show_values, table->show_variables);
1535 printf (" %s=%s", name, value);
1536 free (value);
1537 free (name);
1538 }
1539
1540 putchar ('\n');
1541 }
1542
1543 size_t *layer_indexes;
1544 size_t layer_iteration = 0;
1545 PIVOT_AXIS_FOR_EACH (layer_indexes, &table->axes[PIVOT_AXIS_LAYER])
1546 {
1547 indent (indentation);
1548 printf ("layer %zu:", layer_iteration++);
1549
1550 const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
1551 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
1552 {
1553 const struct pivot_dimension *d = layer_axis->dimensions[i];
1554
1555 fputs (i == 0 ? " " : ", ", stdout);
1556 pivot_value_dump (d->root->name);
1557 fputs (" =", stdout);
1558
1559 struct pivot_value **names = xnmalloc (layer_axis->label_depth,
1560 sizeof *names);
1561 size_t n_names = 0;
1562 for (const struct pivot_category *c
1563 = d->presentation_leaves[layer_indexes[i]];
1564 c;
1565 c = c->parent)
1566 {
1567 if (pivot_category_is_leaf (c) || c->show_label)
1568 names[n_names++] = c->name;
1569 }
1570
1571 for (size_t i = n_names; i-- > 0;)
1572 {
1573 putchar (' ');
1574 pivot_value_dump (names[i]);
1575 }
1576 free (names);
1577 }
1578 putchar ('\n');
1579
1580 size_t *column_enumeration = pivot_table_enumerate_axis (
1581 table, PIVOT_AXIS_COLUMN, layer_indexes, table->omit_empty, NULL);
1582 size_t *row_enumeration = pivot_table_enumerate_axis (
1583 table, PIVOT_AXIS_ROW, layer_indexes, table->omit_empty, NULL);
1584
1585 char ***column_headings = compose_headings (
1586 &table->axes[PIVOT_AXIS_COLUMN], column_enumeration,
1587 table->show_values, table->show_variables);
1588 for (size_t y = 0; y < table->axes[PIVOT_AXIS_COLUMN].label_depth; y++)
1589 {
1590 indent (indentation + 1);
1591 for (size_t x = 0; x < table->axes[PIVOT_AXIS_COLUMN].extent; x++)
1592 {
1593 if (x)
1594 fputs ("; ", stdout);
1595 if (column_headings[y][x])
1596 fputs (column_headings[y][x], stdout);
1597 }
1598 putchar ('\n');
1599 }
1600 free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings);
1601
1602 indent (indentation + 1);
1603 printf ("-----------------------------------------------\n");
1604
1605 char ***row_headings = compose_headings (
1606 &table->axes[PIVOT_AXIS_ROW], row_enumeration,
1607 table->show_values, table->show_variables);
1608
1609 size_t x = 0;
1610 const size_t *pindexes[PIVOT_N_AXES]
1611 = { [PIVOT_AXIS_LAYER] = layer_indexes };
1612 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
1613 &table->axes[PIVOT_AXIS_ROW])
1614 {
1615 indent (indentation + 1);
1616
1617 size_t i = 0;
1618 for (size_t y = 0; y < table->axes[PIVOT_AXIS_ROW].label_depth; y++)
1619 {
1620 if (i++)
1621 fputs ("; ", stdout);
1622 if (row_headings[y][x])
1623 fputs (row_headings[y][x], stdout);
1624 }
1625
1626 printf (" | ");
1627
1628 i = 0;
1629 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
1630 column_enumeration,
1631 &table->axes[PIVOT_AXIS_COLUMN])
1632 {
1633 if (i++)
1634 printf ("; ");
1635
1636 pivot_table_convert_indexes_ptod (table, pindexes, dindexes);
1637 const struct pivot_value *value = pivot_table_get (
1638 table, dindexes);
1639 if (value)
1640 pivot_value_dump (value);
1641 }
1642 printf ("\n");
1643
1644 x++;
1645 }
1646
1647 free (column_enumeration);
1648 free (row_enumeration);
1649 free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings);
1650 }
1651
1652 pivot_table_dump_value (table->caption, "caption", indentation);
1653
1654 for (size_t i = 0; i < table->n_footnotes; i++)
1655 {
1656 const struct pivot_footnote *f = table->footnotes[i];
1657 indent (indentation);
1658 putchar ('[');
1659 if (f->marker)
1660 pivot_value_dump (f->marker);
1661 else
1662 printf ("%zu", f->idx);
1663 putchar (']');
1664 pivot_value_dump (f->content);
1665 putchar ('\n');
1666 }
1667
1668 free (dindexes);
1669 settings_set_decimal_char (old_decimal);
1670 }
1671
1672 static const char *
consume_int(const char * p,size_t * n)1673 consume_int (const char *p, size_t *n)
1674 {
1675 *n = 0;
1676 while (c_isdigit (*p))
1677 *n = *n * 10 + (*p++ - '0');
1678 return p;
1679 }
1680
1681 static size_t
pivot_format_inner_template(struct string * out,const char * template,char escape,struct pivot_value ** values,size_t n_values,enum settings_value_show show_values,enum settings_value_show show_variables)1682 pivot_format_inner_template (struct string *out, const char *template,
1683 char escape,
1684 struct pivot_value **values, size_t n_values,
1685 enum settings_value_show show_values,
1686 enum settings_value_show show_variables)
1687 {
1688 size_t args_consumed = 0;
1689 while (*template && *template != ':')
1690 {
1691 if (*template == '\\' && template[1])
1692 {
1693 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
1694 template += 2;
1695 }
1696 else if (*template == escape)
1697 {
1698 size_t index;
1699 template = consume_int (template + 1, &index);
1700 if (index >= 1 && index <= n_values)
1701 {
1702 pivot_value_format (values[index - 1], show_values,
1703 show_variables, out);
1704 args_consumed = MAX (args_consumed, index);
1705 }
1706 }
1707 else
1708 ds_put_byte (out, *template++);
1709 }
1710 return args_consumed;
1711 }
1712
1713 static const char *
pivot_extract_inner_template(const char * template,const char ** p)1714 pivot_extract_inner_template (const char *template, const char **p)
1715 {
1716 *p = template;
1717
1718 for (;;)
1719 {
1720 if (*template == '\\' && template[1] != '\0')
1721 template += 2;
1722 else if (*template == ':')
1723 return template + 1;
1724 else if (*template == '\0')
1725 return template;
1726 else
1727 template++;
1728 }
1729 }
1730
1731 static void
pivot_format_template(struct string * out,const char * template,const struct pivot_argument * args,size_t n_args,enum settings_value_show show_values,enum settings_value_show show_variables)1732 pivot_format_template (struct string *out, const char *template,
1733 const struct pivot_argument *args, size_t n_args,
1734 enum settings_value_show show_values,
1735 enum settings_value_show show_variables)
1736 {
1737 while (*template)
1738 {
1739 if (*template == '\\' && template[1] != '\0')
1740 {
1741 ds_put_byte (out, template[1] == 'n' ? '\n' : template[1]);
1742 template += 2;
1743 }
1744 else if (*template == '^')
1745 {
1746 size_t index;
1747 template = consume_int (template + 1, &index);
1748 if (index >= 1 && index <= n_args && args[index - 1].n > 0)
1749 pivot_value_format (args[index - 1].values[0],
1750 show_values, show_variables, out);
1751 }
1752 else if (*template == '[')
1753 {
1754 const char *tmpl[2];
1755 template = pivot_extract_inner_template (template + 1, &tmpl[0]);
1756 template = pivot_extract_inner_template (template, &tmpl[1]);
1757 template += *template == ']';
1758
1759 size_t index;
1760 template = consume_int (template, &index);
1761 if (index < 1 || index > n_args)
1762 continue;
1763
1764 const struct pivot_argument *arg = &args[index - 1];
1765 size_t left = arg->n;
1766 while (left)
1767 {
1768 struct pivot_value **values = arg->values + (arg->n - left);
1769 int tmpl_idx = left == arg->n && *tmpl[0] != ':' ? 0 : 1;
1770 char escape = "%^"[tmpl_idx];
1771 size_t used = pivot_format_inner_template (
1772 out, tmpl[tmpl_idx], escape, values, left,
1773 show_values, show_variables);
1774 if (!used || used > left)
1775 break;
1776 left -= used;
1777 }
1778 }
1779 else
1780 ds_put_byte (out, *template++);
1781 }
1782 }
1783
1784 static enum settings_value_show
interpret_show(enum settings_value_show global_show,enum settings_value_show table_show,enum settings_value_show value_show,bool has_label)1785 interpret_show (enum settings_value_show global_show,
1786 enum settings_value_show table_show,
1787 enum settings_value_show value_show,
1788 bool has_label)
1789 {
1790 return (!has_label ? SETTINGS_VALUE_SHOW_VALUE
1791 : value_show != SETTINGS_VALUE_SHOW_DEFAULT ? value_show
1792 : table_show != SETTINGS_VALUE_SHOW_DEFAULT ? table_show
1793 : global_show);
1794 }
1795
1796 /* Appends a text representation of the body of VALUE to OUT. SHOW_VALUES and
1797 SHOW_VARIABLES control whether variable and value labels are included.
1798
1799 The "body" omits subscripts and superscripts and footnotes. */
1800 bool
pivot_value_format_body(const struct pivot_value * value,enum settings_value_show show_values,enum settings_value_show show_variables,struct string * out)1801 pivot_value_format_body (const struct pivot_value *value,
1802 enum settings_value_show show_values,
1803 enum settings_value_show show_variables,
1804 struct string *out)
1805 {
1806 enum settings_value_show show;
1807 bool numeric = false;
1808
1809 switch (value->type)
1810 {
1811 case PIVOT_VALUE_NUMERIC:
1812 show = interpret_show (settings_get_show_values (),
1813 show_values,
1814 value->numeric.show,
1815 value->numeric.value_label != NULL);
1816 if (show & SETTINGS_VALUE_SHOW_VALUE)
1817 {
1818 char *s = data_out (&(union value) { .f = value->numeric.x },
1819 "UTF-8", &value->numeric.format);
1820 ds_put_cstr (out, s + strspn (s, " "));
1821 free (s);
1822 }
1823 if (show & SETTINGS_VALUE_SHOW_LABEL)
1824 {
1825 if (show & SETTINGS_VALUE_SHOW_VALUE)
1826 ds_put_byte (out, ' ');
1827 ds_put_cstr (out, value->numeric.value_label);
1828 }
1829 numeric = !(show & SETTINGS_VALUE_SHOW_LABEL);
1830 break;
1831
1832 case PIVOT_VALUE_STRING:
1833 show = interpret_show (settings_get_show_values (),
1834 show_values,
1835 value->string.show,
1836 value->string.value_label != NULL);
1837 if (show & SETTINGS_VALUE_SHOW_VALUE)
1838 {
1839 if (value->string.hex)
1840 {
1841 for (const uint8_t *p = CHAR_CAST (uint8_t *, value->string.s);
1842 *p; p++)
1843 ds_put_format (out, "%02X", *p);
1844 }
1845 else
1846 ds_put_cstr (out, value->string.s);
1847 }
1848 if (show & SETTINGS_VALUE_SHOW_LABEL)
1849 {
1850 if (show & SETTINGS_VALUE_SHOW_VALUE)
1851 ds_put_byte (out, ' ');
1852 ds_put_cstr (out, value->string.value_label);
1853 }
1854 break;
1855
1856 case PIVOT_VALUE_VARIABLE:
1857 show = interpret_show (settings_get_show_variables (),
1858 show_variables,
1859 value->variable.show,
1860 value->variable.var_label != NULL);
1861 if (show & SETTINGS_VALUE_SHOW_VALUE)
1862 ds_put_cstr (out, value->variable.var_name);
1863 if (show & SETTINGS_VALUE_SHOW_LABEL)
1864 {
1865 if (show & SETTINGS_VALUE_SHOW_VALUE)
1866 ds_put_byte (out, ' ');
1867 ds_put_cstr (out, value->variable.var_label);
1868 }
1869 break;
1870
1871 case PIVOT_VALUE_TEXT:
1872 ds_put_cstr (out, value->text.local);
1873 break;
1874
1875 case PIVOT_VALUE_TEMPLATE:
1876 pivot_format_template (out, value->template.local, value->template.args,
1877 value->template.n_args, show_values,
1878 show_variables);
1879 break;
1880 }
1881
1882 return numeric;
1883 }
1884
1885 /* Appends a text representation of VALUE to OUT. SHOW_VALUES and
1886 SHOW_VARIABLES control whether variable and value labels are included.
1887
1888 Subscripts and superscripts and footnotes are included. */
1889 void
pivot_value_format(const struct pivot_value * value,enum settings_value_show show_values,enum settings_value_show show_variables,struct string * out)1890 pivot_value_format (const struct pivot_value *value,
1891 enum settings_value_show show_values,
1892 enum settings_value_show show_variables,
1893 struct string *out)
1894 {
1895 pivot_value_format_body (value, show_values, show_variables, out);
1896
1897 if (value->n_subscripts)
1898 {
1899 for (size_t i = 0; i < value->n_subscripts; i++)
1900 ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
1901 }
1902
1903 if (value->superscript)
1904 ds_put_format (out, "^%s", value->superscript);
1905
1906 for (size_t i = 0; i < value->n_footnotes; i++)
1907 {
1908 ds_put_byte (out, '^');
1909 pivot_value_format (value->footnotes[i]->marker,
1910 show_values, show_variables, out);
1911 }
1912 }
1913
1914 /* Returns a text representation of VALUE. The caller must free the string,
1915 with free(). */
1916 char *
pivot_value_to_string(const struct pivot_value * value,enum settings_value_show show_values,enum settings_value_show show_variables)1917 pivot_value_to_string (const struct pivot_value *value,
1918 enum settings_value_show show_values,
1919 enum settings_value_show show_variables)
1920 {
1921 struct string s = DS_EMPTY_INITIALIZER;
1922 pivot_value_format (value, show_values, show_variables, &s);
1923 return ds_steal_cstr (&s);
1924 }
1925
1926 /* Frees the data owned by V. */
1927 void
pivot_value_destroy(struct pivot_value * value)1928 pivot_value_destroy (struct pivot_value *value)
1929 {
1930 if (value)
1931 {
1932 font_style_uninit (value->font_style);
1933 free (value->font_style);
1934 free (value->cell_style);
1935 /* Do not free the elements of footnotes because VALUE does not own
1936 them. */
1937 free (value->footnotes);
1938
1939 for (size_t i = 0; i < value->n_subscripts; i++)
1940 free (value->subscripts[i]);
1941 free (value->subscripts);
1942
1943 free (value->superscript);
1944
1945 switch (value->type)
1946 {
1947 case PIVOT_VALUE_NUMERIC:
1948 free (value->numeric.var_name);
1949 free (value->numeric.value_label);
1950 break;
1951
1952 case PIVOT_VALUE_STRING:
1953 free (value->string.s);
1954 free (value->string.var_name);
1955 free (value->string.value_label);
1956 break;
1957
1958 case PIVOT_VALUE_VARIABLE:
1959 free (value->variable.var_name);
1960 free (value->variable.var_label);
1961 break;
1962
1963 case PIVOT_VALUE_TEXT:
1964 free (value->text.local);
1965 if (value->text.c != value->text.local)
1966 free (value->text.c);
1967 if (value->text.id != value->text.local
1968 && value->text.id != value->text.c)
1969 free (value->text.id);
1970 break;
1971
1972 case PIVOT_VALUE_TEMPLATE:
1973 free (value->template.local);
1974 if (value->template.id != value->template.local)
1975 free (value->template.id);
1976 for (size_t i = 0; i < value->template.n_args; i++)
1977 pivot_argument_uninit (&value->template.args[i]);
1978 free (value->template.args);
1979 break;
1980 }
1981 free (value);
1982 }
1983 }
1984
1985 /* Sets AREA to the style to use for VALUE, with defaults coming from
1986 DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */
1987 void
pivot_value_get_style(struct pivot_value * value,const struct font_style * base_font_style,const struct cell_style * base_cell_style,struct area_style * area)1988 pivot_value_get_style (struct pivot_value *value,
1989 const struct font_style *base_font_style,
1990 const struct cell_style *base_cell_style,
1991 struct area_style *area)
1992 {
1993 font_style_copy (NULL, &area->font_style, (value->font_style
1994 ? value->font_style
1995 : base_font_style));
1996 area->cell_style = *(value->cell_style
1997 ? value->cell_style
1998 : base_cell_style);
1999 }
2000
2001 /* Copies AREA into VALUE's style. */
2002 void
pivot_value_set_style(struct pivot_value * value,const struct area_style * area)2003 pivot_value_set_style (struct pivot_value *value,
2004 const struct area_style *area)
2005 {
2006 if (value->font_style)
2007 font_style_uninit (value->font_style);
2008 else
2009 value->font_style = xmalloc (sizeof *value->font_style);
2010 font_style_copy (NULL, value->font_style, &area->font_style);
2011
2012 if (!value->cell_style)
2013 value->cell_style = xmalloc (sizeof *value->cell_style);
2014 *value->cell_style = area->cell_style;
2015 }
2016
2017 /* Frees the data owned by ARG (but not ARG itself). */
2018 void
pivot_argument_uninit(struct pivot_argument * arg)2019 pivot_argument_uninit (struct pivot_argument *arg)
2020 {
2021 if (arg)
2022 {
2023 for (size_t i = 0; i < arg->n; i++)
2024 pivot_value_destroy (arg->values[i]);
2025 free (arg->values);
2026 }
2027 }
2028
2029 /* Creates and returns a new pivot_value whose contents is the null-terminated
2030 string TEXT. Takes ownership of TEXT.
2031
2032 This function is for text strings provided by the user (with the exception
2033 that pivot_value_new_variable() should be used for variable names). For
2034 strings that are part of the PSPP user interface, such as names of
2035 procedures, statistics, annotations, error messages, etc., use
2036 pivot_value_new_text(). */
2037 struct pivot_value *
pivot_value_new_user_text_nocopy(char * text)2038 pivot_value_new_user_text_nocopy (char *text)
2039 {
2040 struct pivot_value *value = xmalloc (sizeof *value);
2041 *value = (struct pivot_value) {
2042 .type = PIVOT_VALUE_TEXT,
2043 .text = {
2044 .local = text,
2045 .c = text,
2046 .id = text,
2047 .user_provided = true,
2048 }
2049 };
2050 return value;
2051 }
2052
2053 /* Creates and returns a new pivot_value whose contents is the LENGTH bytes of
2054 TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known
2055 in advance.
2056
2057 This function is for text strings provided by the user (with the exception
2058 that pivot_value_new_variable() should be used for variable names). For
2059 strings that are part of the PSPP user interface, such as names of
2060 procedures, statistics, annotations, error messages, etc., use
2061 pivot_value_new_text().j
2062
2063 The caller retains ownership of TEXT.*/
2064 struct pivot_value *
pivot_value_new_user_text(const char * text,size_t length)2065 pivot_value_new_user_text (const char *text, size_t length)
2066 {
2067 return pivot_value_new_user_text_nocopy (
2068 xmemdup0 (text, length != SIZE_MAX ? length : strlen (text)));
2069 }
2070
2071 /* Creates and returns new pivot_value whose contents is TEXT, which should be
2072 a translatable string, but not actually translated yet, e.g. enclosed in
2073 N_(). This function is for text strings that are part of the PSPP user
2074 interface, such as names of procedures, statistics, annotations, error
2075 messages, etc. For strings that come from the user, use
2076 pivot_value_new_user_text(). */
2077 struct pivot_value *
pivot_value_new_text(const char * text)2078 pivot_value_new_text (const char *text)
2079 {
2080 char *c = xstrdup (text);
2081 char *local = xstrdup (gettext (c));
2082
2083 struct pivot_value *value = xmalloc (sizeof *value);
2084 *value = (struct pivot_value) {
2085 .type = PIVOT_VALUE_TEXT,
2086 .text = {
2087 .local = local,
2088 .c = c,
2089 .id = c,
2090 .user_provided = false,
2091 }
2092 };
2093 return value;
2094 }
2095
2096 /* Same as pivot_value_new_text() but its argument is a printf()-like format
2097 string. */
2098 struct pivot_value * PRINTF_FORMAT (1, 2)
pivot_value_new_text_format(const char * format,...)2099 pivot_value_new_text_format (const char *format, ...)
2100 {
2101 va_list args;
2102 va_start (args, format);
2103 char *c = xvasprintf (format, args);
2104 va_end (args);
2105
2106 va_start (args, format);
2107 char *local = xvasprintf (gettext (format), args);
2108 va_end (args);
2109
2110 struct pivot_value *value = xmalloc (sizeof *value);
2111 *value = (struct pivot_value) {
2112 .type = PIVOT_VALUE_TEXT,
2113 .text = {
2114 .local = local,
2115 .c = c,
2116 .id = xstrdup (c),
2117 .user_provided = false,
2118 }
2119 };
2120 return value;
2121 }
2122
2123 static char *
xstrdup_if_nonempty(const char * s)2124 xstrdup_if_nonempty (const char *s)
2125 {
2126 return s && s[0] ? xstrdup (s) : NULL;
2127 }
2128
2129 /* Returns a new pivot_value that represents X.
2130
2131 The format to use for X is unspecified. Usually the easiest way to specify
2132 a format is through assigning a result class to one of the categories that
2133 the pivot_value will end up in. If that is not suitable, then the caller
2134 can use pivot_value_set_rc() or assign directly to value->numeric.format. */
2135 struct pivot_value *
pivot_value_new_number(double x)2136 pivot_value_new_number (double x)
2137 {
2138 struct pivot_value *value = xmalloc (sizeof *value);
2139 *value = (struct pivot_value) {
2140 .type = PIVOT_VALUE_NUMERIC,
2141 .numeric = { .x = x, },
2142 };
2143 return value;
2144 }
2145
2146 /* Returns a new pivot_value that represents X, formatted as an integer. */
2147 struct pivot_value *
pivot_value_new_integer(double x)2148 pivot_value_new_integer (double x)
2149 {
2150 struct pivot_value *value = pivot_value_new_number (x);
2151 value->numeric.format = (struct fmt_spec) { FMT_F, 40, 0 };
2152 return value;
2153 }
2154
2155 /* Returns a new pivot_value that represents VALUE, formatted as for
2156 VARIABLE. */
2157 struct pivot_value *
pivot_value_new_var_value(const struct variable * variable,const union value * value)2158 pivot_value_new_var_value (const struct variable *variable,
2159 const union value *value)
2160 {
2161 struct pivot_value *pv = pivot_value_new_value (
2162 value, var_get_width (variable), var_get_print_format (variable),
2163 var_get_encoding (variable));
2164
2165 char *var_name = xstrdup (var_get_name (variable));
2166 if (var_is_alpha (variable))
2167 pv->string.var_name = var_name;
2168 else
2169 pv->numeric.var_name = var_name;
2170
2171 const char *label = var_lookup_value_label (variable, value);
2172 if (label)
2173 {
2174 if (var_is_alpha (variable))
2175 pv->string.value_label = xstrdup (label);
2176 else
2177 pv->numeric.value_label = xstrdup (label);
2178 }
2179
2180 return pv;
2181 }
2182
2183 /* Returns a new pivot_value that represents VALUE, with the given WIDTH,
2184 formatted with FORMAT. For a string value, ENCODING must be its character
2185 encoding. */
2186 struct pivot_value *
pivot_value_new_value(const union value * value,int width,const struct fmt_spec * format,const char * encoding)2187 pivot_value_new_value (const union value *value, int width,
2188 const struct fmt_spec *format, const char *encoding)
2189 {
2190 struct pivot_value *pv = xzalloc (sizeof *pv);
2191 if (width > 0)
2192 {
2193 char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s),
2194 width);
2195 size_t n = strlen (s);
2196 while (n > 0 && s[n - 1] == ' ')
2197 s[--n] = '\0';
2198
2199 pv->type = PIVOT_VALUE_STRING;
2200 pv->string.s = s;
2201 pv->string.hex = format->type == FMT_AHEX;
2202 }
2203 else
2204 {
2205 pv->type = PIVOT_VALUE_NUMERIC;
2206 pv->numeric.x = value->f;
2207 pv->numeric.format = *format;
2208 }
2209
2210 return pv;
2211 }
2212
2213 /* Returns a new pivot_value for VARIABLE. */
2214 struct pivot_value *
pivot_value_new_variable(const struct variable * variable)2215 pivot_value_new_variable (const struct variable *variable)
2216 {
2217 struct pivot_value *value = xmalloc (sizeof *value);
2218 *value = (struct pivot_value) {
2219 .type = PIVOT_VALUE_VARIABLE,
2220 .variable = {
2221 .var_name = xstrdup (var_get_name (variable)),
2222 .var_label = xstrdup_if_nonempty (var_get_label (variable)),
2223 },
2224 };
2225 return value;
2226 }
2227
2228 /* Attaches a reference to FOOTNOTE to V. */
2229 void
pivot_value_add_footnote(struct pivot_value * v,const struct pivot_footnote * footnote)2230 pivot_value_add_footnote (struct pivot_value *v,
2231 const struct pivot_footnote *footnote)
2232 {
2233 /* Some legacy tables include numerous duplicate footnotes. Suppress
2234 them. */
2235 for (size_t i = 0; i < v->n_footnotes; i++)
2236 if (v->footnotes[i] == footnote)
2237 return;
2238
2239 v->footnotes = xrealloc (v->footnotes,
2240 (v->n_footnotes + 1) * sizeof *v->footnotes);
2241 v->footnotes[v->n_footnotes++] = footnote;
2242 }
2243
2244 /* If VALUE is a numeric value, and RC is a result class such as
2245 PIVOT_RC_COUNT, changes VALUE's format to the result class's. */
2246 void
pivot_value_set_rc(const struct pivot_table * table,struct pivot_value * value,const char * rc)2247 pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value,
2248 const char *rc)
2249 {
2250 if (value->type == PIVOT_VALUE_NUMERIC)
2251 {
2252 const struct fmt_spec *f = pivot_table_get_format (table, rc);
2253 if (f)
2254 value->numeric.format = *f;
2255 }
2256 }
2257
2258