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