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