output view: make items selectable and use system colours
[pspp] / src / output / spv / spv-light-decoder.c
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/spv/spv-light-decoder.h"
20
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "libpspp/i18n.h"
27 #include "libpspp/message.h"
28 #include "output/pivot-table.h"
29 #include "output/spv/light-binary-parser.h"
30 #include "output/spv/spv.h"
31
32 #include "gl/xalloc.h"
33 #include "gl/xsize.h"
34
35 static char *
36 to_utf8 (const char *s, const char *encoding)
37 {
38   return recode_string ("UTF-8", encoding, s, strlen (s));
39 }
40
41 static char *
42 to_utf8_if_nonempty (const char *s, const char *encoding)
43 {
44   return s && s[0] ? to_utf8 (s, encoding) : NULL;
45 }
46
47 static void
48 convert_widths (const uint32_t *in, uint32_t n, int **out, size_t *n_out)
49 {
50   if (n)
51     {
52       *n_out = n;
53       *out = xnmalloc (n, sizeof **out);
54       for (size_t i = 0; i < n; i++)
55         (*out)[i] = in[i];
56     }
57 }
58
59 static void
60 convert_breakpoints (const struct spvlb_breakpoints *in,
61                      size_t **out, size_t *n_out)
62 {
63   if (in && in->n_breaks)
64     {
65       *n_out = in->n_breaks;
66       *out = xnmalloc (in->n_breaks, sizeof *out);
67       for (size_t i = 0; i < in->n_breaks; i++)
68         (*out)[i] = in->breaks[i];
69     }
70 }
71
72 static void
73 convert_keeps (const struct spvlb_keeps *in,
74                struct pivot_keep **out, size_t *n_out)
75 {
76   if (in && in->n_keeps)
77     {
78       *n_out = in->n_keeps;
79       *out = xnmalloc (*n_out, sizeof **out);
80       for (size_t i = 0; i < *n_out; i++)
81         {
82           (*out)[i].ofs = in->keeps[i]->offset;
83           (*out)[i].n = in->keeps[i]->n;
84         }
85     }
86 }
87
88 static char * WARN_UNUSED_RESULT
89 decode_spvlb_color_string (const char *s, uint8_t def,
90                            struct cell_color *colorp)
91 {
92   int r, g, b;
93   if (!*s)
94     r = g = b = def;
95   else if (sscanf (s, "#%2x%2x%2x", &r, &g, &b) != 3)
96     return xasprintf ("bad color %s", s);
97
98   *colorp = (struct cell_color) CELL_COLOR (r, g, b);
99   return NULL;
100 }
101
102 static struct cell_color
103 decode_spvlb_color_u32 (uint32_t x)
104 {
105   return (struct cell_color) { x >> 24, x >> 16, x >> 8, x };
106 }
107
108 static char * WARN_UNUSED_RESULT
109 decode_spvlb_font_style (const struct spvlb_font_style *in,
110                          const char *encoding, struct font_style **outp)
111 {
112   if (!in)
113     {
114       *outp = NULL;
115       return NULL;
116     }
117
118   struct cell_color fg, bg;
119   char *error = decode_spvlb_color_string (in->fg_color, 0x00, &fg);
120   if (!error)
121     error = decode_spvlb_color_string (in->bg_color, 0xff, &bg);
122   if (error)
123     return error;
124
125   *outp = xmalloc (sizeof **outp);
126   **outp = (struct font_style) {
127     .bold = in->bold,
128     .italic = in->italic,
129     .underline = in->underline,
130     .fg = { fg, fg },
131     .bg = { bg, bg },
132     .typeface = to_utf8 (in->typeface, encoding),
133     .size = in->size / 1.33,
134   };
135   return NULL;
136 }
137
138 static char * WARN_UNUSED_RESULT
139 decode_spvlb_halign (uint32_t in, enum table_halign *halignp)
140 {
141   switch (in)
142     {
143     case 0:
144       *halignp = TABLE_HALIGN_CENTER;
145       return NULL;
146
147     case 2:
148       *halignp = TABLE_HALIGN_LEFT;
149       return NULL;
150
151     case 4:
152       *halignp = TABLE_HALIGN_RIGHT;
153       return NULL;
154
155     case 6:
156     case 61453:
157       *halignp = TABLE_HALIGN_DECIMAL;
158       return NULL;
159
160     case 0xffffffad:
161     case 64173:
162       *halignp = TABLE_HALIGN_MIXED;
163       return NULL;
164
165     default:
166       return xasprintf ("bad cell style halign %"PRIu32, in);
167     }
168 }
169
170 static char * WARN_UNUSED_RESULT
171 decode_spvlb_valign (uint32_t in, enum table_valign *valignp)
172 {
173   switch (in)
174     {
175     case 0:
176       *valignp = TABLE_VALIGN_CENTER;
177       return NULL;
178
179     case 1:
180       *valignp = TABLE_VALIGN_TOP;
181       return NULL;
182
183     case 3:
184       *valignp = TABLE_VALIGN_BOTTOM;
185       return NULL;
186
187     default:
188       *valignp = 0;
189       return xasprintf ("bad cell style valign %"PRIu32, in);
190     }
191 }
192
193 static char * WARN_UNUSED_RESULT
194 decode_spvlb_cell_style (const struct spvlb_cell_style *in,
195                          struct cell_style **outp)
196 {
197   if (!in)
198     {
199       *outp = NULL;
200       return NULL;
201     }
202
203   enum table_halign halign;
204   char *error = decode_spvlb_halign (in->halign, &halign);
205   if (error)
206     return error;
207
208   enum table_valign valign;
209   error = decode_spvlb_valign (in->valign, &valign);
210   if (error)
211     return error;
212
213   *outp = xzalloc (sizeof **outp);
214   **outp = (struct cell_style) {
215     .halign = halign,
216     .valign = valign,
217     .decimal_offset = in->decimal_offset,
218     .margin = {
219       [TABLE_HORZ] = { in->left_margin, in->right_margin },
220       [TABLE_VERT] = { in->top_margin, in->bottom_margin },
221     },
222   };
223   return NULL;
224 }
225
226 static char *decode_spvlb_value (
227   const struct pivot_table *, const struct spvlb_value *,
228   const char *encoding, struct pivot_value **) WARN_UNUSED_RESULT;
229
230 static char * WARN_UNUSED_RESULT
231 decode_spvlb_argument (const struct pivot_table *table,
232                        const struct spvlb_argument *in,
233                        const char *encoding, struct pivot_argument *out)
234 {
235   if (in->value)
236     {
237       struct pivot_value *value;
238       char *error = decode_spvlb_value (table, in->value, encoding, &value);
239       if (error)
240         return error;
241
242       out->n = 1;
243       out->values = xmalloc (sizeof *out->values);
244       out->values[0] = value;
245     }
246   else
247     {
248       out->n = 0;
249       out->values = xnmalloc (in->n_values, sizeof *out->values);
250       for (size_t i = 0; i < in->n_values; i++)
251         {
252           char *error = decode_spvlb_value (table, in->values[i], encoding,
253                                             &out->values[i]);
254           if (error)
255             {
256               pivot_argument_uninit (out);
257               return error;
258             }
259           out->n++;
260         }
261     }
262
263   return NULL;
264 }
265
266 static char * WARN_UNUSED_RESULT
267 decode_spvlb_value_show (uint8_t in, enum settings_value_show *out)
268 {
269   switch (in)
270     {
271     case 0: *out = SETTINGS_VALUE_SHOW_DEFAULT; return NULL;
272     case 1: *out = SETTINGS_VALUE_SHOW_VALUE; return NULL;
273     case 2: *out = SETTINGS_VALUE_SHOW_LABEL; return NULL;
274     case 3: *out = SETTINGS_VALUE_SHOW_BOTH; return NULL;
275     default:
276       return xasprintf ("bad value show %"PRIu8, in);
277     }
278 }
279
280 static char * WARN_UNUSED_RESULT
281 decode_spvlb_value (const struct pivot_table *table,
282                     const struct spvlb_value *in,
283                     const char *encoding, struct pivot_value **outp)
284 {
285   *outp = NULL;
286
287   struct pivot_value *out = xzalloc (sizeof *out);
288   const struct spvlb_value_mod *vm;
289
290   char *error;
291   switch (in->type)
292     {
293     case 1:
294       vm = in->type_01.value_mod;
295       out->type = PIVOT_VALUE_NUMERIC;
296       out->numeric.x = in->type_01.x;
297       error = spv_decode_fmt_spec (in->type_01.format, &out->numeric.format);
298       if (error)
299         return error;
300       break;
301
302     case 2:
303       vm = in->type_02.value_mod;
304       out->type = PIVOT_VALUE_NUMERIC;
305       out->numeric.x = in->type_02.x;
306       error = spv_decode_fmt_spec (in->type_02.format, &out->numeric.format);
307       if (!error)
308         error = decode_spvlb_value_show (in->type_02.show, &out->numeric.show);
309       if (error)
310         return NULL;
311       out->numeric.var_name = to_utf8_if_nonempty (in->type_02.var_name,
312                                                    encoding);
313       out->numeric.value_label = to_utf8_if_nonempty (in->type_02.value_label,
314                                                       encoding);
315       break;
316
317     case 3:
318       vm = in->type_03.value_mod;
319       out->type = PIVOT_VALUE_TEXT;
320       out->text.local = to_utf8 (in->type_03.local, encoding);
321       out->text.c = to_utf8 (in->type_03.c, encoding);
322       out->text.id = to_utf8 (in->type_03.id, encoding);
323       out->text.user_provided = !in->type_03.fixed;
324       break;
325
326     case 4:
327       vm = in->type_04.value_mod;
328       out->type = PIVOT_VALUE_STRING;
329       error = decode_spvlb_value_show (in->type_04.show, &out->string.show);
330       if (error)
331         return NULL;
332       out->string.s = to_utf8 (in->type_04.s, encoding);
333       out->string.var_name = to_utf8 (in->type_04.var_name, encoding);
334       out->string.value_label = to_utf8_if_nonempty (in->type_04.value_label,
335                                                      encoding);
336       break;
337
338     case 5:
339       vm = in->type_05.value_mod;
340       out->type = PIVOT_VALUE_VARIABLE;
341       error = decode_spvlb_value_show (in->type_05.show, &out->variable.show);
342       if (error)
343         return error;
344       out->variable.var_name = to_utf8 (in->type_05.var_name, encoding);
345       out->variable.var_label = to_utf8_if_nonempty (in->type_05.var_label,
346                                                      encoding);
347       break;
348
349     case 6:
350       vm = in->type_06.value_mod;
351       out->type = PIVOT_VALUE_TEXT;
352       out->text.local = to_utf8 (in->type_06.local, encoding);
353       out->text.c = to_utf8 (in->type_06.c, encoding);
354       out->text.id = to_utf8 (in->type_06.id, encoding);
355       out->text.user_provided = false;
356       break;
357
358     case -1:
359       vm = in->type_else.value_mod;
360       out->type = PIVOT_VALUE_TEMPLATE;
361       out->template.local = to_utf8 (in->type_else.template, encoding);
362       out->template.id = out->template.local;
363       out->template.n_args = 0;
364       out->template.args = xnmalloc (in->type_else.n_args,
365                                      sizeof *out->template.args);
366       for (size_t i = 0; i < in->type_else.n_args; i++)
367         {
368           error = decode_spvlb_argument (table, in->type_else.args[i],
369                                          encoding, &out->template.args[i]);
370           if (error)
371             {
372               pivot_value_destroy (out);
373               return error;
374             }
375           out->template.n_args++;
376         }
377       break;
378
379     default:
380       assert (0);
381     }
382
383   if (vm)
384     {
385       if (vm->n_subscripts)
386         {
387           out->n_subscripts = vm->n_subscripts;
388           out->subscripts = xnmalloc (vm->n_subscripts,
389                                       sizeof *out->subscripts);
390           for (size_t i = 0; i < vm->n_subscripts; i++)
391             out->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
392         }
393
394       if (vm->n_refs)
395         {
396           out->footnotes = xnmalloc (vm->n_refs, sizeof *out->footnotes);
397           for (size_t i = 0; i < vm->n_refs; i++)
398             {
399               uint16_t idx = vm->refs[i];
400               if (idx >= table->n_footnotes)
401                 {
402                   pivot_value_destroy (out);
403                   return xasprintf ("bad footnote index: %"PRIu16" >= %zu",
404                                     idx, table->n_footnotes);
405                 }
406
407               out->footnotes[out->n_footnotes++] = table->footnotes[idx];
408             }
409         }
410
411       if (vm->style_pair)
412         {
413           error = decode_spvlb_font_style (vm->style_pair->font_style,
414                                            encoding, &out->font_style);
415           if (!error)
416             error = decode_spvlb_cell_style (vm->style_pair->cell_style,
417                                              &out->cell_style);
418           if (error)
419             {
420               pivot_value_destroy (out);
421               return error;
422             }
423         }
424
425       if (vm->template_string
426           && vm->template_string->id
427           && vm->template_string->id[0]
428           && out->type == PIVOT_VALUE_TEMPLATE)
429         out->template.id = to_utf8 (vm->template_string->id, encoding);
430     }
431
432   *outp = out;
433   return NULL;
434 }
435
436 static char * WARN_UNUSED_RESULT
437 decode_spvlb_area (const struct spvlb_area *in, struct area_style *out,
438                    const char *encoding)
439 {
440   char *error;
441
442   struct cell_color fg0, fg1, bg0, bg1;
443   error = decode_spvlb_color_string (in->fg_color, 0x00, &fg0);
444   if (!error)
445     error = decode_spvlb_color_string (in->bg_color, 0xff, &bg0);
446   if (!error && in->alternate)
447     error = decode_spvlb_color_string (in->alt_fg_color, 0x00, &fg1);
448   if (!error && in->alternate)
449     error = decode_spvlb_color_string (in->alt_bg_color, 0xff, &bg1);
450
451   enum table_halign halign;
452   if (!error)
453     {
454       error = decode_spvlb_halign (in->halign, &halign);
455
456       /* TABLE_HALIGN_DECIMAL doesn't seem to be a real halign for areas, which
457          is good because there's no way to indicate the decimal offset.  Just
458          in case: */
459       if (!error && halign == TABLE_HALIGN_DECIMAL)
460         halign = TABLE_HALIGN_MIXED;
461     }
462
463   enum table_valign valign;
464   if (!error)
465     error = decode_spvlb_valign (in->valign, &valign);
466
467   if (error)
468     return error;
469
470   *out = (struct area_style) {
471     .font_style = {
472       .bold = (in->style & 1) != 0,
473       .italic = (in->style & 2) != 0,
474       .underline = in->underline,
475       .fg = { fg0, in->alternate ? fg1 : fg0 },
476       .bg = { bg0, in->alternate ? bg1 : bg0 },
477       .typeface = to_utf8 (in->typeface, encoding),
478       .size = in->size / 1.33,
479     },
480     .cell_style = {
481       .halign = halign,
482       .valign = valign,
483       .margin = {
484         [TABLE_HORZ] = { in->left_margin, in->right_margin },
485         [TABLE_VERT] = { in->top_margin, in->bottom_margin },
486       },
487     },
488   };
489   return NULL;
490 }
491
492 static char * WARN_UNUSED_RESULT
493 decode_spvlb_group (const struct pivot_table *,
494                     struct spvlb_category **,
495                     size_t n_categories,
496                     bool show_label,
497                     struct pivot_category *parent,
498                     struct pivot_dimension *,
499                     const char *encoding);
500
501 static char * WARN_UNUSED_RESULT
502 decode_spvlb_categories (const struct pivot_table *table,
503                          struct spvlb_category **categories,
504                          size_t n_categories,
505                          struct pivot_category *parent,
506                          struct pivot_dimension *dimension,
507                          const char *encoding)
508 {
509   for (size_t i = 0; i < n_categories; i++)
510     {
511       const struct spvlb_category *in = categories[i];
512       if (in->group && in->group->merge)
513         {
514           char *error = decode_spvlb_categories (
515             table, in->group->subcategories, in->group->n_subcategories,
516             parent, dimension, encoding);
517           if (error)
518             return error;
519
520           continue;
521         }
522
523       struct pivot_value *name;
524       char *error = decode_spvlb_value (table, in->name, encoding, &name);
525       if (error)
526         return error;
527
528       struct pivot_category *out = xzalloc (sizeof *out);
529       out->name = name;
530       out->parent = parent;
531       out->dimension = dimension;
532       if (in->group)
533         {
534           char *error = decode_spvlb_group (table, in->group->subcategories,
535                                             in->group->n_subcategories,
536                                             true, out, dimension, encoding);
537           if (error)
538             {
539               pivot_category_destroy (out);
540               return error;
541             }
542
543           out->data_index = SIZE_MAX;
544           out->presentation_index = SIZE_MAX;
545         }
546       else
547         {
548           out->data_index = in->leaf->leaf_index;
549           out->presentation_index = dimension->n_leaves;
550           dimension->n_leaves++;
551         }
552
553       if (parent->n_subs >= parent->allocated_subs)
554         parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
555                                    sizeof *parent->subs);
556       parent->subs[parent->n_subs++] = out;
557     }
558   return NULL;
559 }
560
561 static char * WARN_UNUSED_RESULT
562 decode_spvlb_group (const struct pivot_table *table,
563                     struct spvlb_category **categories,
564                     size_t n_categories, bool show_label,
565                     struct pivot_category *category,
566                     struct pivot_dimension *dimension,
567                     const char *encoding)
568 {
569   category->subs = XCALLOC (n_categories, struct pivot_category *);
570   category->n_subs = 0;
571   category->allocated_subs = 0;
572   category->show_label = show_label;
573
574   return decode_spvlb_categories (table, categories, n_categories, category,
575                                   dimension, encoding);
576 }
577
578 static char * WARN_UNUSED_RESULT
579 fill_leaves (struct pivot_category *category,
580              struct pivot_dimension *dimension)
581 {
582   if (pivot_category_is_group (category))
583     {
584       for (size_t i = 0; i < category->n_subs; i++)
585         {
586           char *error = fill_leaves (category->subs[i], dimension);
587           if (error)
588             return error;
589         }
590     }
591   else
592     {
593       if (category->data_index >= dimension->n_leaves)
594         return xasprintf ("leaf_index %zu >= n_leaves %zu",
595                           category->data_index, dimension->n_leaves);
596       if (dimension->data_leaves[category->data_index])
597         return xasprintf ("two leaves with data_index %zu",
598                           category->data_index);
599       dimension->data_leaves[category->data_index] = category;
600       dimension->presentation_leaves[category->presentation_index] = category;
601     }
602   return NULL;
603 }
604
605 static char * WARN_UNUSED_RESULT
606 decode_spvlb_dimension (const struct pivot_table *table,
607                         const struct spvlb_dimension *in,
608                         size_t idx, const char *encoding,
609                         struct pivot_dimension **outp)
610 {
611   /* Convert most of the dimension. */
612   struct pivot_value *name;
613   char *error = decode_spvlb_value (table, in->name, encoding, &name);
614   if (error)
615     return error;
616
617   struct pivot_dimension *out = xzalloc (sizeof *out);
618   out->level = UINT_MAX;
619   out->top_index = idx;
620   out->hide_all_labels = in->props->hide_all_labels;
621
622   out->root = xzalloc (sizeof *out->root);
623   *out->root = (struct pivot_category) {
624     .name = name,
625     .dimension = out,
626     .data_index = SIZE_MAX,
627     .presentation_index = SIZE_MAX,
628   };
629   error = decode_spvlb_group (table, in->categories, in->n_categories,
630                               !in->props->hide_dim_label, out->root,
631                               out, encoding);
632   if (error)
633     goto error;
634
635   /* Allocate and fill the array of leaves now that we know how many there
636      are. */
637   out->data_leaves = XCALLOC (out->n_leaves, struct pivot_category *);
638   out->presentation_leaves = XCALLOC (out->n_leaves, struct pivot_category *);
639   out->allocated_leaves = out->n_leaves;
640   error = fill_leaves (out->root, out);
641   if (error)
642     goto error;
643   for (size_t i = 0; i < out->n_leaves; i++)
644     {
645       assert (out->data_leaves[i] != NULL);
646       assert (out->presentation_leaves[i] != NULL);
647     }
648   *outp = out;
649   return NULL;
650
651 error:
652   pivot_dimension_destroy (out);
653   return error;
654 }
655
656 static char * WARN_UNUSED_RESULT
657 decode_spvlb_stroke (uint32_t stroke_type, enum table_stroke *strokep)
658 {
659   enum table_stroke strokes[] = {
660     TABLE_STROKE_NONE,
661     TABLE_STROKE_SOLID,
662     TABLE_STROKE_DASHED,
663     TABLE_STROKE_THICK,
664     TABLE_STROKE_THIN,
665     TABLE_STROKE_DOUBLE,
666   };
667
668   if (stroke_type >= sizeof strokes / sizeof *strokes)
669     return xasprintf ("bad stroke %"PRIu32, stroke_type);
670
671   *strokep = strokes[stroke_type];
672   return NULL;
673 }
674
675 static char * WARN_UNUSED_RESULT
676 decode_spvlb_border (const struct spvlb_border *in, struct pivot_table *table)
677
678 {
679   if (in->border_type >= PIVOT_N_BORDERS)
680     return xasprintf ("bad border type %"PRIu32, in->border_type);
681
682   struct table_border_style *out = &table->borders[in->border_type];
683   out->color = decode_spvlb_color_u32 (in->color);
684   return decode_spvlb_stroke (in->stroke_type, &out->stroke);
685 }
686
687 static char * WARN_UNUSED_RESULT
688 decode_spvlb_axis (const uint32_t *dimension_indexes, size_t n_dimensions,
689                    enum pivot_axis_type axis_type, struct pivot_table *table)
690 {
691   struct pivot_axis *axis = &table->axes[axis_type];
692   axis->dimensions = XCALLOC (n_dimensions, struct pivot_dimension *);
693   axis->n_dimensions = n_dimensions;
694   axis->extent = 1;
695   for (size_t i = 0; i < n_dimensions; i++)
696     {
697       uint32_t idx = dimension_indexes[i];
698       if (idx >= table->n_dimensions)
699         return xasprintf ("bad dimension index %"PRIu32" >= %zu",
700                           idx, table->n_dimensions);
701
702       struct pivot_dimension *d = table->dimensions[idx];
703       if (d->level != UINT_MAX)
704         return xasprintf ("duplicate dimension %"PRIu32, idx);
705
706       axis->dimensions[i] = d;
707       d->axis_type = axis_type;
708       d->level = i;
709
710       axis->extent *= d->n_leaves;
711     }
712
713   return NULL;
714 }
715
716 static char *
717 decode_data_index (uint64_t in, const struct pivot_table *table,
718                    size_t *out)
719 {
720   uint64_t remainder = in;
721   for (size_t i = table->n_dimensions - 1; i > 0; i--)
722     {
723       const struct pivot_dimension *d = table->dimensions[i];
724       if (d->n_leaves)
725         {
726           out[i] = remainder % d->n_leaves;
727           remainder /= d->n_leaves;
728         }
729       else
730         out[i] = 0;
731     }
732   if (remainder >= table->dimensions[0]->n_leaves)
733     return xasprintf ("out of range cell data index %"PRIu64, in);
734
735   out[0] = remainder;
736   return NULL;
737 }
738
739 static char * WARN_UNUSED_RESULT
740 decode_spvlb_cells (struct spvlb_cell **in, size_t n_in,
741                     struct pivot_table *table, const char *encoding)
742 {
743   if (!table->n_dimensions)
744     return NULL;
745
746   size_t *dindexes = xnmalloc (table->n_dimensions, sizeof *dindexes);
747   for (size_t i = 0; i < n_in; i++)
748     {
749       struct pivot_value *value;
750       char *error = decode_data_index (in[i]->index, table, dindexes);
751       if (!error)
752         error = decode_spvlb_value (table, in[i]->value, encoding, &value);
753       if (error)
754         {
755           free (dindexes);
756           return error;
757         }
758       pivot_table_put (table, dindexes, table->n_dimensions, value);
759     }
760   free (dindexes);
761
762   return NULL;
763 }
764
765 static char * WARN_UNUSED_RESULT
766 decode_spvlb_footnote (const struct spvlb_footnote *in, const char *encoding,
767                        size_t idx, struct pivot_table *table)
768 {
769   struct pivot_value *content;
770   char *error = decode_spvlb_value (table, in->text, encoding, &content);
771   if (error)
772     return error;
773
774   struct pivot_value *marker = NULL;
775   if (in->marker)
776     {
777       error = decode_spvlb_value (table, in->marker, encoding, &marker);
778       if (error)
779         {
780           pivot_value_destroy (content);
781           return error;
782         }
783       if (marker->type == PIVOT_VALUE_TEXT)
784         marker->text.user_provided = false;
785     }
786
787   struct pivot_footnote *f = pivot_table_create_footnote__ (
788     table, idx, marker, content);
789   f->show = (int32_t) in->show > 0;
790   return NULL;
791 }
792
793 static char * WARN_UNUSED_RESULT
794 decode_current_layer (uint64_t current_layer, struct pivot_table *table)
795 {
796   const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
797   table->current_layer = xnmalloc (axis->n_dimensions,
798                                    sizeof *table->current_layer);
799
800   for (size_t i = 0; i < axis->n_dimensions; i++)
801     {
802       const struct pivot_dimension *d = axis->dimensions[i];
803       if (d->n_leaves)
804         {
805           table->current_layer[i] = current_layer % d->n_leaves;
806           current_layer /= d->n_leaves;
807         }
808       else
809         table->current_layer[i] = 0;
810     }
811   if (current_layer > 0)
812     return xasprintf ("out of range layer data index %"PRIu64, current_layer);
813   return NULL;
814 }
815
816 char * WARN_UNUSED_RESULT
817 decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
818 {
819   *outp = NULL;
820   if (in->header->version != 1 && in->header->version != 3)
821     return xasprintf ("unknown version %"PRIu32" (expected 1 or 3)",
822                       in->header->version);
823
824   char *error = NULL;
825   struct pivot_table *out = xzalloc (sizeof *out);
826   out->ref_cnt = 1;
827   hmap_init (&out->cells);
828
829   const struct spvlb_y1 *y1 = (in->formats->x0 ? in->formats->x0->y1
830                                : in->formats->x3 ? in->formats->x3->y1
831                                : NULL);
832   const char *encoding;
833   if (y1)
834     encoding = y1->charset;
835   else
836     {
837       const char *dot = strchr (in->formats->locale, '.');
838       encoding = dot ? dot + 1 : "windows-1252";
839     }
840
841   /* Display settings. */
842   out->show_numeric_markers = !in->ts->show_alphabetic_markers;
843   out->rotate_inner_column_labels = in->header->rotate_inner_column_labels;
844   out->rotate_outer_row_labels = in->header->rotate_outer_row_labels;
845   out->row_labels_in_corner = in->ts->show_row_labels_in_corner;
846   out->show_grid_lines = in->borders->show_grid_lines;
847   out->show_caption = true;
848   out->footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
849   out->omit_empty = in->ts->omit_empty;
850
851   const struct spvlb_x1 *x1 = in->formats->x1;
852   if (x1)
853     {
854       error = decode_spvlb_value_show (x1->show_values, &out->show_values);
855       if (!error)
856         error = decode_spvlb_value_show (x1->show_variables,
857                                          &out->show_variables);
858       if (error)
859         goto error;
860
861       out->show_caption = x1->show_caption;
862     }
863
864   /* Column and row display settings. */
865   out->sizing[TABLE_VERT].range[0] = in->header->min_row_height;
866   out->sizing[TABLE_VERT].range[1] = in->header->max_row_height;
867   out->sizing[TABLE_HORZ].range[0] = in->header->min_col_width;
868   out->sizing[TABLE_HORZ].range[1] = in->header->max_col_width;
869
870   convert_widths (in->formats->widths, in->formats->n_widths,
871                   &out->sizing[TABLE_HORZ].widths,
872                   &out->sizing[TABLE_HORZ].n_widths);
873
874   const struct spvlb_x2 *x2 = in->formats->x2;
875   if (x2)
876     convert_widths (x2->row_heights, x2->n_row_heights,
877                     &out->sizing[TABLE_VERT].widths,
878                     &out->sizing[TABLE_VERT].n_widths);
879
880   convert_breakpoints (in->ts->row_breaks,
881                        &out->sizing[TABLE_VERT].breaks,
882                        &out->sizing[TABLE_VERT].n_breaks);
883   convert_breakpoints (in->ts->col_breaks,
884                        &out->sizing[TABLE_HORZ].breaks,
885                        &out->sizing[TABLE_HORZ].n_breaks);
886
887   convert_keeps (in->ts->row_keeps,
888                  &out->sizing[TABLE_VERT].keeps,
889                  &out->sizing[TABLE_VERT].n_keeps);
890   convert_keeps (in->ts->col_keeps,
891                  &out->sizing[TABLE_HORZ].keeps,
892                  &out->sizing[TABLE_HORZ].n_keeps);
893
894   out->notes = to_utf8_if_nonempty (in->ts->notes, encoding);
895   out->table_look = to_utf8_if_nonempty (in->ts->table_look, encoding);
896
897   /* Print settings. */
898   out->print_all_layers = in->ps->all_layers;
899   out->paginate_layers = in->ps->paginate_layers;
900   out->shrink_to_fit[TABLE_HORZ] = in->ps->fit_width;
901   out->shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
902   out->top_continuation = in->ps->top_continuation;
903   out->bottom_continuation = in->ps->bottom_continuation;
904   out->continuation = xstrdup (in->ps->continuation_string);
905   out->n_orphan_lines = in->ps->n_orphan_lines;
906
907   /* Format settings. */
908   out->epoch = in->formats->y0->epoch;
909   out->decimal = in->formats->y0->decimal;
910   out->grouping = in->formats->y0->grouping;
911   const struct spvlb_custom_currency *cc = in->formats->custom_currency;
912   for (int i = 0; i < 5; i++)
913     if (cc && i < cc->n_ccs)
914       out->ccs[i] = xstrdup (cc->ccs[i]);
915   out->small = in->formats->x3 ? in->formats->x3->small : 0;
916
917   /* Command information. */
918   if (y1)
919     {
920       out->command_local = to_utf8 (y1->command_local, encoding);
921       out->command_c = to_utf8 (y1->command, encoding);
922       out->language = xstrdup (y1->language);
923       /* charset? */
924       out->locale = xstrdup (y1->locale);
925     }
926
927   /* Source information. */
928   const struct spvlb_x3 *x3 = in->formats->x3;
929   if (x3)
930     {
931       if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
932         out->dataset = to_utf8 (x3->dataset, encoding);
933       out->datafile = to_utf8_if_nonempty (x3->datafile, encoding);
934       out->date = x3->date;
935     }
936
937   /* Footnotes.
938
939      Any pivot_value might refer to footnotes, so it's important to process the
940      footnotes early to ensure that those references can be resolved.  There is
941      a possible problem that a footnote might itself reference an
942      as-yet-unprocessed footnote, but that's OK because footnote references
943      don't actually look at the footnote contents but only resolve a pointer to
944      where the footnote will go later.
945
946      Before we really start, create all the footnotes we'll fill in.  This is
947      because sometimes footnotes refer to themselves or to each other and we
948      don't want to reject those references. */
949   const struct spvlb_footnotes *fn = in->footnotes;
950   if (fn->n_footnotes > 0)
951     {
952       pivot_table_create_footnote__ (out, fn->n_footnotes - 1, NULL, NULL);
953       for (size_t i = 0; i < fn->n_footnotes; i++)
954         {
955           error = decode_spvlb_footnote (in->footnotes->footnotes[i],
956                                          encoding, i, out);
957           if (error)
958             goto error;
959         }
960     }
961
962   /* Title and caption. */
963   error = decode_spvlb_value (out, in->titles->user_title, encoding,
964                               &out->title);
965   if (error)
966     goto error;
967
968   error = decode_spvlb_value (out, in->titles->subtype, encoding,
969                               &out->subtype);
970   if (error)
971     goto error;
972
973   if (in->titles->corner_text)
974     {
975       error = decode_spvlb_value (out, in->titles->corner_text,
976                                   encoding, &out->corner_text);
977       if (error)
978         goto error;
979     }
980
981   if (in->titles->caption)
982     {
983       error = decode_spvlb_value (out, in->titles->caption, encoding,
984                                   &out->caption);
985       if (error)
986         goto error;
987     }
988
989
990   /* Styles. */
991   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
992     {
993       error = decode_spvlb_area (in->areas->areas[i], &out->areas[i],
994                                  encoding);
995       if (error)
996         goto error;
997     }
998   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
999     {
1000       error = decode_spvlb_border (in->borders->borders[i], out);
1001       if (error)
1002         goto error;
1003     }
1004
1005   /* Dimensions. */
1006   out->n_dimensions = in->dimensions->n_dims;
1007   out->dimensions = XCALLOC (out->n_dimensions, struct pivot_dimension *);
1008   for (size_t i = 0; i < out->n_dimensions; i++)
1009     {
1010       error = decode_spvlb_dimension (out, in->dimensions->dims[i],
1011                                       i, encoding, &out->dimensions[i]);
1012       if (error)
1013         goto error;
1014     }
1015
1016   /* Axes. */
1017   size_t a = in->axes->n_layers;
1018   size_t b = in->axes->n_rows;
1019   size_t c = in->axes->n_columns;
1020   if (size_overflow_p (xsum3 (a, b, c)) || a + b + c != out->n_dimensions)
1021     {
1022       error = xasprintf ("dimensions do not sum correctly "
1023                          "(%zu + %zu + %zu != %zu)",
1024                          a, b, c, out->n_dimensions);
1025       goto error;
1026     }
1027   error = decode_spvlb_axis (in->axes->layers, in->axes->n_layers,
1028                              PIVOT_AXIS_LAYER, out);
1029   if (error)
1030     goto error;
1031   error = decode_spvlb_axis (in->axes->rows, in->axes->n_rows,
1032                              PIVOT_AXIS_ROW, out);
1033   if (error)
1034     goto error;
1035   error = decode_spvlb_axis (in->axes->columns, in->axes->n_columns,
1036                              PIVOT_AXIS_COLUMN, out);
1037   if (error)
1038     goto error;
1039
1040   pivot_table_assign_label_depth (out);
1041
1042   error = decode_current_layer (in->ts->current_layer, out);
1043   if (error)
1044     goto error;
1045
1046   /* Data. */
1047   error = decode_spvlb_cells (in->cells->cells, in->cells->n_cells, out,
1048                               encoding);
1049
1050   *outp = out;
1051   return NULL;
1052
1053 error:
1054   pivot_table_unref (out);
1055   return error;
1056 }