Add support for reading and writing SPV files.
[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       return xasprintf ("bad cell style valign %"PRIu32, in);
189     }
190 }
191
192 static char * WARN_UNUSED_RESULT
193 decode_spvlb_cell_style (const struct spvlb_cell_style *in,
194                          struct cell_style **outp)
195 {
196   if (!in)
197     {
198       *outp = NULL;
199       return NULL;
200     }
201
202   enum table_halign halign;
203   char *error = decode_spvlb_halign (in->halign, &halign);
204   if (error)
205     return error;
206
207   enum table_valign valign;
208   error = decode_spvlb_valign (in->valign, &valign);
209   if (error)
210     return error;
211
212   *outp = xzalloc (sizeof **outp);
213   **outp = (struct cell_style) {
214     .halign = halign,
215     .valign = valign,
216     .decimal_offset = in->decimal_offset,
217     .margin = {
218       [TABLE_HORZ] = { in->left_margin, in->right_margin },
219       [TABLE_VERT] = { in->top_margin, in->bottom_margin },
220     },
221   };
222   return NULL;
223 }
224
225 static char *decode_spvlb_value (
226   const struct pivot_table *, const struct spvlb_value *,
227   const char *encoding, struct pivot_value **) WARN_UNUSED_RESULT;
228
229 static char * WARN_UNUSED_RESULT
230 decode_spvlb_argument (const struct pivot_table *table,
231                        const struct spvlb_argument *in,
232                        const char *encoding, struct pivot_argument *out)
233 {
234   if (in->value)
235     {
236       struct pivot_value *value;
237       char *error = decode_spvlb_value (table, in->value, encoding, &value);
238       if (error)
239         return error;
240
241       out->n = 1;
242       out->values = xmalloc (sizeof *out->values);
243       out->values[0] = value;
244     }
245   else
246     {
247       out->n = 0;
248       out->values = xnmalloc (in->n_values, sizeof *out->values);
249       for (size_t i = 0; i < in->n_values; i++)
250         {
251           char *error = decode_spvlb_value (table, in->values[i], encoding,
252                                             &out->values[i]);
253           if (error)
254             {
255               pivot_argument_uninit (out);
256               return error;
257             }
258           out->n++;
259         }
260     }
261
262   return NULL;
263 }
264
265 static char * WARN_UNUSED_RESULT
266 decode_spvlb_value_show (uint8_t in, enum settings_value_show *out)
267 {
268   switch (in)
269     {
270     case 0: *out = SETTINGS_VALUE_SHOW_DEFAULT; return NULL;
271     case 1: *out = SETTINGS_VALUE_SHOW_VALUE; return NULL;
272     case 2: *out = SETTINGS_VALUE_SHOW_LABEL; return NULL;
273     case 3: *out = SETTINGS_VALUE_SHOW_BOTH; return NULL;
274     default:
275       return xasprintf ("bad value show %"PRIu8, in);
276     }
277 }
278
279 static char * WARN_UNUSED_RESULT
280 decode_spvlb_value (const struct pivot_table *table,
281                     const struct spvlb_value *in,
282                     const char *encoding, struct pivot_value **outp)
283 {
284   *outp = NULL;
285
286   struct pivot_value *out = xzalloc (sizeof *out);
287   const struct spvlb_value_mod *vm;
288
289   char *error;
290   switch (in->type)
291     {
292     case 1:
293       vm = in->type_01.value_mod;
294       out->type = PIVOT_VALUE_NUMERIC;
295       out->numeric.x = in->type_01.x;
296       error = spv_decode_fmt_spec (in->type_01.format, &out->numeric.format);
297       if (error)
298         return error;
299       break;
300
301     case 2:
302       vm = in->type_02.value_mod;
303       out->type = PIVOT_VALUE_NUMERIC;
304       out->numeric.x = in->type_02.x;
305       error = spv_decode_fmt_spec (in->type_02.format, &out->numeric.format);
306       if (!error)
307         error = decode_spvlb_value_show (in->type_02.show, &out->numeric.show);
308       if (error)
309         return NULL;
310       out->numeric.var_name = to_utf8_if_nonempty (in->type_02.var_name,
311                                                    encoding);
312       out->numeric.value_label = to_utf8_if_nonempty (in->type_02.value_label,
313                                                       encoding);
314       break;
315
316     case 3:
317       vm = in->type_03.value_mod;
318       out->type = PIVOT_VALUE_TEXT;
319       out->text.local = to_utf8 (in->type_03.local, encoding);
320       out->text.c = to_utf8 (in->type_03.c, encoding);
321       out->text.id = to_utf8 (in->type_03.id, encoding);
322       out->text.user_provided = !in->type_03.fixed;
323       break;
324
325     case 4:
326       vm = in->type_04.value_mod;
327       out->type = PIVOT_VALUE_STRING;
328       error = decode_spvlb_value_show (in->type_04.show, &out->string.show);
329       if (error)
330         return NULL;
331       out->string.s = to_utf8 (in->type_04.s, encoding);
332       out->string.var_name = to_utf8 (in->type_04.var_name, encoding);
333       out->string.value_label = to_utf8_if_nonempty (in->type_04.value_label,
334                                                      encoding);
335       break;
336
337     case 5:
338       vm = in->type_05.value_mod;
339       out->type = PIVOT_VALUE_VARIABLE;
340       error = decode_spvlb_value_show (in->type_05.show, &out->variable.show);
341       if (error)
342         return error;
343       out->variable.var_name = to_utf8 (in->type_05.var_name, encoding);
344       out->variable.var_label = to_utf8_if_nonempty (in->type_05.var_label,
345                                                      encoding);
346       break;
347
348     case 6:
349       vm = in->type_06.value_mod;
350       out->type = PIVOT_VALUE_TEXT;
351       out->text.local = to_utf8 (in->type_06.local, encoding);
352       out->text.c = to_utf8 (in->type_06.c, encoding);
353       out->text.id = to_utf8 (in->type_06.id, encoding);
354       out->text.user_provided = false;
355       break;
356
357     case -1:
358       vm = in->type_else.value_mod;
359       out->type = PIVOT_VALUE_TEMPLATE;
360       out->template.local = to_utf8 (in->type_else.template, encoding);
361       out->template.id = out->template.local;
362       out->template.n_args = 0;
363       out->template.args = xnmalloc (in->type_else.n_args,
364                                      sizeof *out->template.args);
365       for (size_t i = 0; i < in->type_else.n_args; i++)
366         {
367           error = decode_spvlb_argument (table, in->type_else.args[i],
368                                          encoding, &out->template.args[i]);
369           if (error)
370             {
371               pivot_value_destroy (out);
372               return error;
373             }
374           out->template.n_args++;
375         }
376       break;
377
378     default:
379       assert (0);
380     }
381
382   if (vm)
383     {
384       if (vm->n_subscripts)
385         {
386           out->n_subscripts = vm->n_subscripts;
387           out->subscripts = xnmalloc (vm->n_subscripts,
388                                       sizeof *out->subscripts);
389           for (size_t i = 0; i < vm->n_subscripts; i++)
390             out->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
391         }
392
393       if (vm->n_refs)
394         {
395           out->footnotes = xnmalloc (vm->n_refs, sizeof *out->footnotes);
396           for (size_t i = 0; i < vm->n_refs; i++)
397             {
398               uint16_t idx = vm->refs[i];
399               if (idx >= table->n_footnotes)
400                 {
401                   pivot_value_destroy (out);
402                   return xasprintf ("bad footnote index: %"PRIu16" >= %zu",
403                                     idx, table->n_footnotes);
404                 }
405
406               out->footnotes[out->n_footnotes++] = table->footnotes[idx];
407             }
408         }
409
410       if (vm->style_pair)
411         {
412           error = decode_spvlb_font_style (vm->style_pair->font_style,
413                                            encoding, &out->font_style);
414           if (!error)
415             error = decode_spvlb_cell_style (vm->style_pair->cell_style,
416                                              &out->cell_style);
417           if (error)
418             {
419               pivot_value_destroy (out);
420               return error;
421             }
422         }
423
424       if (vm->template_string
425           && vm->template_string->id
426           && vm->template_string->id[0]
427           && out->type == PIVOT_VALUE_TEMPLATE)
428         out->template.id = to_utf8 (vm->template_string->id, encoding);
429     }
430
431   *outp = out;
432   return NULL;
433 }
434
435 static char * WARN_UNUSED_RESULT
436 decode_spvlb_area (const struct spvlb_area *in, struct area_style *out,
437                    const char *encoding)
438 {
439   char *error;
440
441   struct cell_color fg0, fg1, bg0, bg1;
442   error = decode_spvlb_color_string (in->fg_color, 0x00, &fg0);
443   if (!error)
444     error = decode_spvlb_color_string (in->bg_color, 0xff, &bg0);
445   if (!error && in->alternate)
446     error = decode_spvlb_color_string (in->alt_fg_color, 0x00, &fg1);
447   if (!error && in->alternate)
448     error = decode_spvlb_color_string (in->alt_bg_color, 0xff, &bg1);
449
450   enum table_halign halign;
451   if (!error)
452     {
453       error = decode_spvlb_halign (in->halign, &halign);
454
455       /* TABLE_HALIGN_DECIMAL doesn't seem to be a real halign for areas, which
456          is good because there's no way to indicate the decimal offset.  Just
457          in case: */
458       if (!error && halign == TABLE_HALIGN_DECIMAL)
459         halign = TABLE_HALIGN_MIXED;
460     }
461
462   enum table_valign valign;
463   if (!error)
464     error = decode_spvlb_valign (in->valign, &valign);
465
466   if (error)
467     return error;
468
469   *out = (struct area_style) {
470     .font_style = {
471       .bold = (in->style & 1) != 0,
472       .italic = (in->style & 2) != 0,
473       .underline = in->underline,
474       .fg = { fg0, in->alternate ? fg1 : fg0 },
475       .bg = { bg0, in->alternate ? bg1 : bg0 },
476       .typeface = to_utf8 (in->typeface, encoding),
477       .size = in->size / 1.33,
478     },
479     .cell_style = {
480       .halign = halign,
481       .valign = valign,
482       .margin = {
483         [TABLE_HORZ] = { in->left_margin, in->right_margin },
484         [TABLE_VERT] = { in->top_margin, in->bottom_margin },
485       },
486     },
487   };
488   return NULL;
489 }
490
491 static char * WARN_UNUSED_RESULT
492 decode_spvlb_group (const struct pivot_table *,
493                     struct spvlb_category **,
494                     size_t n_categories,
495                     bool show_label,
496                     struct pivot_category *parent,
497                     struct pivot_dimension *,
498                     const char *encoding);
499
500 static char * WARN_UNUSED_RESULT
501 decode_spvlb_categories (const struct pivot_table *table,
502                          struct spvlb_category **categories,
503                          size_t n_categories,
504                          struct pivot_category *parent,
505                          struct pivot_dimension *dimension,
506                          const char *encoding)
507 {
508   for (size_t i = 0; i < n_categories; i++)
509     {
510       const struct spvlb_category *in = categories[i];
511       if (in->group && in->group->merge)
512         {
513           char *error = decode_spvlb_categories (
514             table, in->group->subcategories, in->group->n_subcategories,
515             parent, dimension, encoding);
516           if (error)
517             return error;
518
519           continue;
520         }
521
522       struct pivot_value *name;
523       char *error = decode_spvlb_value (table, in->name, encoding, &name);
524       if (error)
525         return error;
526
527       struct pivot_category *out = xzalloc (sizeof *out);
528       out->name = name;
529       out->parent = parent;
530       out->dimension = dimension;
531       if (in->group)
532         {
533           char *error = decode_spvlb_group (table, in->group->subcategories,
534                                             in->group->n_subcategories,
535                                             true, out, dimension, encoding);
536           if (error)
537             {
538               pivot_category_destroy (out);
539               return error;
540             }
541
542           out->data_index = SIZE_MAX;
543           out->presentation_index = SIZE_MAX;
544         }
545       else
546         {
547           out->data_index = in->leaf->leaf_index;
548           out->presentation_index = dimension->n_leaves;
549           dimension->n_leaves++;
550         }
551
552       if (parent->n_subs >= parent->allocated_subs)
553         parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
554                                    sizeof *parent->subs);
555       parent->subs[parent->n_subs++] = out;
556     }
557   return NULL;
558 }
559
560 static char * WARN_UNUSED_RESULT
561 decode_spvlb_group (const struct pivot_table *table,
562                     struct spvlb_category **categories,
563                     size_t n_categories, bool show_label,
564                     struct pivot_category *category,
565                     struct pivot_dimension *dimension,
566                     const char *encoding)
567 {
568   category->subs = xcalloc (n_categories, sizeof *category->subs);
569   category->n_subs = 0;
570   category->allocated_subs = 0;
571   category->show_label = show_label;
572
573   return decode_spvlb_categories (table, categories, n_categories, category,
574                                   dimension, encoding);
575 }
576
577 static char * WARN_UNUSED_RESULT
578 fill_leaves (struct pivot_category *category,
579              struct pivot_dimension *dimension)
580 {
581   if (pivot_category_is_group (category))
582     {
583       for (size_t i = 0; i < category->n_subs; i++)
584         {
585           char *error = fill_leaves (category->subs[i], dimension);
586           if (error)
587             return error;
588         }
589     }
590   else
591     {
592       if (category->data_index >= dimension->n_leaves)
593         return xasprintf ("leaf_index %zu >= n_leaves %zu",
594                           category->data_index, dimension->n_leaves);
595       if (dimension->data_leaves[category->data_index])
596         return xasprintf ("two leaves with data_index %zu",
597                           category->data_index);
598       dimension->data_leaves[category->data_index] = category;
599       dimension->presentation_leaves[category->presentation_index] = category;
600     }
601   return NULL;
602 }
603
604 static char * WARN_UNUSED_RESULT
605 decode_spvlb_dimension (const struct pivot_table *table,
606                         const struct spvlb_dimension *in,
607                         size_t idx, const char *encoding,
608                         struct pivot_dimension **outp)
609 {
610   /* Convert most of the dimension. */
611   struct pivot_value *name;
612   char *error = decode_spvlb_value (table, in->name, encoding, &name);
613   if (error)
614     return error;
615
616   struct pivot_dimension *out = xzalloc (sizeof *out);
617   out->level = UINT_MAX;
618   out->top_index = idx;
619   out->hide_all_labels = in->props->hide_all_labels;
620
621   out->root = xzalloc (sizeof *out->root);
622   *out->root = (struct pivot_category) {
623     .name = name,
624     .dimension = out,
625     .data_index = SIZE_MAX,
626     .presentation_index = SIZE_MAX,
627   };
628   error = decode_spvlb_group (table, in->categories, in->n_categories,
629                               !in->props->hide_dim_label, out->root,
630                               out, encoding);
631   if (error)
632     goto error;
633
634   /* Allocate and fill the array of leaves now that we know how many there
635      are. */
636   out->data_leaves = xcalloc (out->n_leaves, sizeof *out->data_leaves);
637   out->presentation_leaves = xcalloc (out->n_leaves,
638                                       sizeof *out->presentation_leaves);
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, sizeof *axis->dimensions);
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, sizeof *out->dimensions);
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 }