spv-file-format: Document "show-title" option.
[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       abort ();
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 table_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   table_area_style_uninit (out);
471   *out = (struct table_area_style) {
472     .font_style = {
473       .bold = (in->style & 1) != 0,
474       .italic = (in->style & 2) != 0,
475       .underline = in->underline,
476       .fg = { fg0, in->alternate ? fg1 : fg0 },
477       .bg = { bg0, in->alternate ? bg1 : bg0 },
478       .typeface = to_utf8 (in->typeface, encoding),
479       .size = in->size / 1.33,
480     },
481     .cell_style = {
482       .halign = halign,
483       .valign = valign,
484       .margin = {
485         [TABLE_HORZ] = { in->left_margin, in->right_margin },
486         [TABLE_VERT] = { in->top_margin, in->bottom_margin },
487       },
488     },
489   };
490   return NULL;
491 }
492
493 static char * WARN_UNUSED_RESULT
494 decode_spvlb_group (const struct pivot_table *,
495                     struct spvlb_category **,
496                     size_t n_categories,
497                     bool show_label,
498                     struct pivot_category *parent,
499                     struct pivot_dimension *,
500                     const char *encoding);
501
502 static char * WARN_UNUSED_RESULT
503 decode_spvlb_categories (const struct pivot_table *table,
504                          struct spvlb_category **categories,
505                          size_t n_categories,
506                          struct pivot_category *parent,
507                          struct pivot_dimension *dimension,
508                          const char *encoding)
509 {
510   for (size_t i = 0; i < n_categories; i++)
511     {
512       const struct spvlb_category *in = categories[i];
513       if (in->group && in->group->merge)
514         {
515           char *error = decode_spvlb_categories (
516             table, in->group->subcategories, in->group->n_subcategories,
517             parent, dimension, encoding);
518           if (error)
519             return error;
520
521           continue;
522         }
523
524       struct pivot_value *name;
525       char *error = decode_spvlb_value (table, in->name, encoding, &name);
526       if (error)
527         return error;
528
529       struct pivot_category *out = xzalloc (sizeof *out);
530       out->name = name;
531       out->parent = parent;
532       out->dimension = dimension;
533       if (in->group)
534         {
535           char *error = decode_spvlb_group (table, in->group->subcategories,
536                                             in->group->n_subcategories,
537                                             true, out, dimension, encoding);
538           if (error)
539             {
540               pivot_category_destroy (out);
541               return error;
542             }
543
544           out->data_index = SIZE_MAX;
545           out->presentation_index = SIZE_MAX;
546         }
547       else
548         {
549           out->data_index = in->leaf->leaf_index;
550           out->presentation_index = dimension->n_leaves;
551           dimension->n_leaves++;
552         }
553
554       if (parent->n_subs >= parent->allocated_subs)
555         parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
556                                    sizeof *parent->subs);
557       parent->subs[parent->n_subs++] = out;
558     }
559   return NULL;
560 }
561
562 static char * WARN_UNUSED_RESULT
563 decode_spvlb_group (const struct pivot_table *table,
564                     struct spvlb_category **categories,
565                     size_t n_categories, bool show_label,
566                     struct pivot_category *category,
567                     struct pivot_dimension *dimension,
568                     const char *encoding)
569 {
570   category->subs = XCALLOC (n_categories, struct pivot_category *);
571   category->n_subs = 0;
572   category->allocated_subs = 0;
573   category->show_label = show_label;
574
575   return decode_spvlb_categories (table, categories, n_categories, category,
576                                   dimension, encoding);
577 }
578
579 static char * WARN_UNUSED_RESULT
580 fill_leaves (struct pivot_category *category,
581              struct pivot_dimension *dimension)
582 {
583   if (pivot_category_is_group (category))
584     {
585       for (size_t i = 0; i < category->n_subs; i++)
586         {
587           char *error = fill_leaves (category->subs[i], dimension);
588           if (error)
589             return error;
590         }
591     }
592   else
593     {
594       if (category->data_index >= dimension->n_leaves)
595         return xasprintf ("leaf_index %zu >= n_leaves %zu",
596                           category->data_index, dimension->n_leaves);
597       if (dimension->data_leaves[category->data_index])
598         return xasprintf ("two leaves with data_index %zu",
599                           category->data_index);
600       dimension->data_leaves[category->data_index] = category;
601       dimension->presentation_leaves[category->presentation_index] = category;
602     }
603   return NULL;
604 }
605
606 static char * WARN_UNUSED_RESULT
607 decode_spvlb_dimension (const struct pivot_table *table,
608                         const struct spvlb_dimension *in,
609                         size_t idx, const char *encoding,
610                         struct pivot_dimension **outp)
611 {
612   /* Convert most of the dimension. */
613   struct pivot_value *name;
614   char *error = decode_spvlb_value (table, in->name, encoding, &name);
615   if (error)
616     return error;
617
618   struct pivot_dimension *out = xzalloc (sizeof *out);
619   out->level = UINT_MAX;
620   out->top_index = idx;
621   out->hide_all_labels = in->props->hide_all_labels;
622
623   out->root = xzalloc (sizeof *out->root);
624   *out->root = (struct pivot_category) {
625     .name = name,
626     .dimension = out,
627     .data_index = SIZE_MAX,
628     .presentation_index = SIZE_MAX,
629   };
630   error = decode_spvlb_group (table, in->categories, in->n_categories,
631                               !in->props->hide_dim_label, out->root,
632                               out, encoding);
633   if (error)
634     goto error;
635
636   /* Allocate and fill the array of leaves now that we know how many there
637      are. */
638   out->data_leaves = XCALLOC (out->n_leaves, struct pivot_category *);
639   out->presentation_leaves = XCALLOC (out->n_leaves, struct pivot_category *);
640   out->allocated_leaves = out->n_leaves;
641   error = fill_leaves (out->root, out);
642   if (error)
643     goto error;
644   for (size_t i = 0; i < out->n_leaves; i++)
645     {
646       assert (out->data_leaves[i] != NULL);
647       assert (out->presentation_leaves[i] != NULL);
648     }
649   *outp = out;
650   return NULL;
651
652 error:
653   pivot_dimension_destroy (out);
654   return error;
655 }
656
657 static char * WARN_UNUSED_RESULT
658 decode_spvlb_stroke (uint32_t stroke_type, enum table_stroke *strokep)
659 {
660   enum table_stroke strokes[] = {
661     TABLE_STROKE_NONE,
662     TABLE_STROKE_SOLID,
663     TABLE_STROKE_DASHED,
664     TABLE_STROKE_THICK,
665     TABLE_STROKE_THIN,
666     TABLE_STROKE_DOUBLE,
667   };
668
669   if (stroke_type >= sizeof strokes / sizeof *strokes)
670     return xasprintf ("bad stroke %"PRIu32, stroke_type);
671
672   *strokep = strokes[stroke_type];
673   return NULL;
674 }
675
676 static char * WARN_UNUSED_RESULT
677 decode_spvlb_border (const struct spvlb_border *in, struct pivot_table *table)
678
679 {
680   if (in->border_type >= PIVOT_N_BORDERS)
681     return xasprintf ("bad border type %"PRIu32, in->border_type);
682
683   struct table_border_style *out = &table->look->borders[in->border_type];
684   out->color = decode_spvlb_color_u32 (in->color);
685   return decode_spvlb_stroke (in->stroke_type, &out->stroke);
686 }
687
688 static char * WARN_UNUSED_RESULT
689 decode_spvlb_axis (const uint32_t *dimension_indexes, size_t n_dimensions,
690                    enum pivot_axis_type axis_type, struct pivot_table *table)
691 {
692   struct pivot_axis *axis = &table->axes[axis_type];
693   axis->dimensions = XCALLOC (n_dimensions, struct pivot_dimension *);
694   axis->n_dimensions = n_dimensions;
695   axis->extent = 1;
696   for (size_t i = 0; i < n_dimensions; i++)
697     {
698       uint32_t idx = dimension_indexes[i];
699       if (idx >= table->n_dimensions)
700         return xasprintf ("bad dimension index %"PRIu32" >= %zu",
701                           idx, table->n_dimensions);
702
703       struct pivot_dimension *d = table->dimensions[idx];
704       if (d->level != UINT_MAX)
705         return xasprintf ("duplicate dimension %"PRIu32, idx);
706
707       axis->dimensions[i] = d;
708       d->axis_type = axis_type;
709       d->level = i;
710
711       axis->extent *= d->n_leaves;
712     }
713
714   return NULL;
715 }
716
717 static char *
718 decode_data_index (uint64_t in, const struct pivot_table *table,
719                    size_t *out)
720 {
721   uint64_t remainder = in;
722   for (size_t i = table->n_dimensions - 1; i > 0; i--)
723     {
724       const struct pivot_dimension *d = table->dimensions[i];
725       if (d->n_leaves)
726         {
727           out[i] = remainder % d->n_leaves;
728           remainder /= d->n_leaves;
729         }
730       else
731         out[i] = 0;
732     }
733   if (remainder >= table->dimensions[0]->n_leaves)
734     return xasprintf ("out of range cell data index %"PRIu64, in);
735
736   out[0] = remainder;
737   return NULL;
738 }
739
740 static char * WARN_UNUSED_RESULT
741 decode_spvlb_cells (struct spvlb_cell **in, size_t n_in,
742                     struct pivot_table *table, const char *encoding)
743 {
744   if (!table->n_dimensions)
745     return NULL;
746
747   size_t *dindexes = xnmalloc (table->n_dimensions, sizeof *dindexes);
748   for (size_t i = 0; i < n_in; i++)
749     {
750       struct pivot_value *value;
751       char *error = decode_data_index (in[i]->index, table, dindexes);
752       if (!error)
753         error = decode_spvlb_value (table, in[i]->value, encoding, &value);
754       if (error)
755         {
756           free (dindexes);
757           return error;
758         }
759       pivot_table_put (table, dindexes, table->n_dimensions, value);
760     }
761   free (dindexes);
762
763   return NULL;
764 }
765
766 static char * WARN_UNUSED_RESULT
767 decode_spvlb_footnote (const struct spvlb_footnote *in, const char *encoding,
768                        size_t idx, struct pivot_table *table)
769 {
770   struct pivot_value *content;
771   char *error = decode_spvlb_value (table, in->text, encoding, &content);
772   if (error)
773     return error;
774
775   struct pivot_value *marker = NULL;
776   if (in->marker)
777     {
778       error = decode_spvlb_value (table, in->marker, encoding, &marker);
779       if (error)
780         {
781           pivot_value_destroy (content);
782           return error;
783         }
784       if (marker->type == PIVOT_VALUE_TEXT)
785         marker->text.user_provided = false;
786     }
787
788   struct pivot_footnote *f = pivot_table_create_footnote__ (
789     table, idx, marker, content);
790   f->show = (int32_t) in->show > 0;
791   return NULL;
792 }
793
794 static char * WARN_UNUSED_RESULT
795 decode_current_layer (uint64_t current_layer, struct pivot_table *table)
796 {
797   const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
798   table->current_layer = xnmalloc (axis->n_dimensions,
799                                    sizeof *table->current_layer);
800
801   for (size_t i = 0; i < axis->n_dimensions; i++)
802     {
803       const struct pivot_dimension *d = axis->dimensions[i];
804       if (d->n_leaves)
805         {
806           table->current_layer[i] = current_layer % d->n_leaves;
807           current_layer /= d->n_leaves;
808         }
809       else
810         table->current_layer[i] = 0;
811     }
812   if (current_layer > 0)
813     return xasprintf ("out of range layer data index %"PRIu64, current_layer);
814   return NULL;
815 }
816
817 char * WARN_UNUSED_RESULT
818 decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
819 {
820   *outp = NULL;
821   if (in->header->version != 1 && in->header->version != 3)
822     return xasprintf ("unknown version %"PRIu32" (expected 1 or 3)",
823                       in->header->version);
824
825   char *error = NULL;
826   struct pivot_table *out = xzalloc (sizeof *out);
827   out->ref_cnt = 1;
828   hmap_init (&out->cells);
829   out->look = pivot_table_look_new_builtin_default ();
830
831   const struct spvlb_y1 *y1 = (in->formats->x0 ? in->formats->x0->y1
832                                : in->formats->x3 ? in->formats->x3->y1
833                                : NULL);
834   const char *encoding;
835   if (y1)
836     encoding = y1->charset;
837   else
838     {
839       const char *dot = strchr (in->formats->locale, '.');
840       encoding = dot ? dot + 1 : "windows-1252";
841     }
842
843   /* Display settings. */
844   out->look->show_numeric_markers = !in->ts->show_alphabetic_markers;
845   out->rotate_inner_column_labels = in->header->rotate_inner_column_labels;
846   out->rotate_outer_row_labels = in->header->rotate_outer_row_labels;
847   out->look->row_labels_in_corner = in->ts->show_row_labels_in_corner;
848   out->show_grid_lines = in->borders->show_grid_lines;
849   out->show_title = true;
850   out->show_caption = true;
851   out->look->footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
852   out->look->omit_empty = in->ts->omit_empty;
853
854   const struct spvlb_x1 *x1 = in->formats->x1;
855   if (x1)
856     {
857       error = decode_spvlb_value_show (x1->show_values, &out->show_values);
858       if (!error)
859         error = decode_spvlb_value_show (x1->show_variables,
860                                          &out->show_variables);
861       if (error)
862         goto error;
863
864       out->show_caption = x1->show_caption;
865       out->show_title = x1->show_title != 10;
866     }
867
868   /* Column and row display settings. */
869   out->look->width_ranges[TABLE_VERT][0] = in->header->min_row_height;
870   out->look->width_ranges[TABLE_VERT][1] = in->header->max_row_height;
871   out->look->width_ranges[TABLE_HORZ][0] = in->header->min_col_width;
872   out->look->width_ranges[TABLE_HORZ][1] = in->header->max_col_width;
873
874   convert_widths (in->formats->widths, in->formats->n_widths,
875                   &out->sizing[TABLE_HORZ].widths,
876                   &out->sizing[TABLE_HORZ].n_widths);
877
878   const struct spvlb_x2 *x2 = in->formats->x2;
879   if (x2)
880     convert_widths (x2->row_heights, x2->n_row_heights,
881                     &out->sizing[TABLE_VERT].widths,
882                     &out->sizing[TABLE_VERT].n_widths);
883
884   convert_breakpoints (in->ts->row_breaks,
885                        &out->sizing[TABLE_VERT].breaks,
886                        &out->sizing[TABLE_VERT].n_breaks);
887   convert_breakpoints (in->ts->col_breaks,
888                        &out->sizing[TABLE_HORZ].breaks,
889                        &out->sizing[TABLE_HORZ].n_breaks);
890
891   convert_keeps (in->ts->row_keeps,
892                  &out->sizing[TABLE_VERT].keeps,
893                  &out->sizing[TABLE_VERT].n_keeps);
894   convert_keeps (in->ts->col_keeps,
895                  &out->sizing[TABLE_HORZ].keeps,
896                  &out->sizing[TABLE_HORZ].n_keeps);
897
898   out->notes = to_utf8_if_nonempty (in->ts->notes, encoding);
899   out->look->name = to_utf8_if_nonempty (in->ts->table_look, encoding);
900
901   /* Print settings. */
902   out->look->print_all_layers = in->ps->all_layers;
903   out->look->paginate_layers = in->ps->paginate_layers;
904   out->look->shrink_to_fit[TABLE_HORZ] = in->ps->fit_width;
905   out->look->shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
906   out->look->top_continuation = in->ps->top_continuation;
907   out->look->bottom_continuation = in->ps->bottom_continuation;
908   out->look->continuation = xstrdup (in->ps->continuation_string);
909   out->look->n_orphan_lines = in->ps->n_orphan_lines;
910
911   /* Format settings. */
912   out->epoch = in->formats->y0->epoch;
913   out->decimal = in->formats->y0->decimal;
914   out->grouping = in->formats->y0->grouping;
915   const struct spvlb_custom_currency *cc = in->formats->custom_currency;
916   for (int i = 0; i < 5; i++)
917     if (cc && i < cc->n_ccs)
918       out->ccs[i] = xstrdup (cc->ccs[i]);
919   out->small = in->formats->x3 ? in->formats->x3->small : 0;
920
921   /* Command information. */
922   if (y1)
923     {
924       out->command_local = to_utf8 (y1->command_local, encoding);
925       out->command_c = to_utf8 (y1->command, encoding);
926       out->language = xstrdup (y1->language);
927       /* charset? */
928       out->locale = xstrdup (y1->locale);
929     }
930
931   /* Source information. */
932   const struct spvlb_x3 *x3 = in->formats->x3;
933   if (x3)
934     {
935       if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
936         out->dataset = to_utf8 (x3->dataset, encoding);
937       out->datafile = to_utf8_if_nonempty (x3->datafile, encoding);
938       out->date = x3->date;
939     }
940
941   /* Footnotes.
942
943      Any pivot_value might refer to footnotes, so it's important to process the
944      footnotes early to ensure that those references can be resolved.  There is
945      a possible problem that a footnote might itself reference an
946      as-yet-unprocessed footnote, but that's OK because footnote references
947      don't actually look at the footnote contents but only resolve a pointer to
948      where the footnote will go later.
949
950      Before we really start, create all the footnotes we'll fill in.  This is
951      because sometimes footnotes refer to themselves or to each other and we
952      don't want to reject those references. */
953   const struct spvlb_footnotes *fn = in->footnotes;
954   if (fn->n_footnotes > 0)
955     {
956       pivot_table_create_footnote__ (out, fn->n_footnotes - 1, NULL, NULL);
957       for (size_t i = 0; i < fn->n_footnotes; i++)
958         {
959           error = decode_spvlb_footnote (in->footnotes->footnotes[i],
960                                          encoding, i, out);
961           if (error)
962             goto error;
963         }
964     }
965
966   /* Title and caption. */
967   error = decode_spvlb_value (out, in->titles->user_title, encoding,
968                               &out->title);
969   if (error)
970     goto error;
971
972   error = decode_spvlb_value (out, in->titles->subtype, encoding,
973                               &out->subtype);
974   if (error)
975     goto error;
976
977   if (in->titles->corner_text)
978     {
979       error = decode_spvlb_value (out, in->titles->corner_text,
980                                   encoding, &out->corner_text);
981       if (error)
982         goto error;
983     }
984
985   if (in->titles->caption)
986     {
987       error = decode_spvlb_value (out, in->titles->caption, encoding,
988                                   &out->caption);
989       if (error)
990         goto error;
991     }
992
993
994   /* Styles. */
995   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
996     {
997       error = decode_spvlb_area (in->areas->areas[i], &out->look->areas[i],
998                                  encoding);
999       if (error)
1000         goto error;
1001     }
1002   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
1003     {
1004       error = decode_spvlb_border (in->borders->borders[i], out);
1005       if (error)
1006         goto error;
1007     }
1008
1009   /* Dimensions. */
1010   out->n_dimensions = in->dimensions->n_dims;
1011   out->dimensions = XCALLOC (out->n_dimensions, struct pivot_dimension *);
1012   for (size_t i = 0; i < out->n_dimensions; i++)
1013     {
1014       error = decode_spvlb_dimension (out, in->dimensions->dims[i],
1015                                       i, encoding, &out->dimensions[i]);
1016       if (error)
1017         goto error;
1018     }
1019
1020   /* Axes. */
1021   size_t a = in->axes->n_layers;
1022   size_t b = in->axes->n_rows;
1023   size_t c = in->axes->n_columns;
1024   if (size_overflow_p (xsum3 (a, b, c)) || a + b + c != out->n_dimensions)
1025     {
1026       error = xasprintf ("dimensions do not sum correctly "
1027                          "(%zu + %zu + %zu != %zu)",
1028                          a, b, c, out->n_dimensions);
1029       goto error;
1030     }
1031   error = decode_spvlb_axis (in->axes->layers, in->axes->n_layers,
1032                              PIVOT_AXIS_LAYER, out);
1033   if (error)
1034     goto error;
1035   error = decode_spvlb_axis (in->axes->rows, in->axes->n_rows,
1036                              PIVOT_AXIS_ROW, out);
1037   if (error)
1038     goto error;
1039   error = decode_spvlb_axis (in->axes->columns, in->axes->n_columns,
1040                              PIVOT_AXIS_COLUMN, out);
1041   if (error)
1042     goto error;
1043
1044   pivot_table_assign_label_depth (out);
1045
1046   error = decode_current_layer (in->ts->current_layer, out);
1047   if (error)
1048     goto error;
1049
1050   /* Data. */
1051   error = decode_spvlb_cells (in->cells->cells, in->cells->n_cells, out,
1052                               encoding);
1053
1054   *outp = out;
1055   return NULL;
1056
1057 error:
1058   pivot_table_unref (out);
1059   return error;
1060 }