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