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