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