pivot-table: Always put footnote markers in sorted order.
[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 > 0; 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 >= table->dimensions[0]->n_leaves)
744     return xasprintf ("out of range cell data index %"PRIu64, in);
745
746   out[0] = remainder;
747   return NULL;
748 }
749
750 static char * WARN_UNUSED_RESULT
751 decode_spvlb_cells (struct spvlb_cell **in, size_t n_in,
752                     struct pivot_table *table, const char *encoding)
753 {
754   if (!table->n_dimensions)
755     return NULL;
756
757   size_t *dindexes = xnmalloc (table->n_dimensions, sizeof *dindexes);
758   for (size_t i = 0; i < n_in; i++)
759     {
760       struct pivot_value *value;
761       char *error = decode_data_index (in[i]->index, table, dindexes);
762       if (!error)
763         error = decode_spvlb_value (table, in[i]->value, encoding, &value);
764       if (error)
765         {
766           free (dindexes);
767           return error;
768         }
769       pivot_table_put (table, dindexes, table->n_dimensions, value);
770     }
771   free (dindexes);
772
773   return NULL;
774 }
775
776 static char * WARN_UNUSED_RESULT
777 decode_spvlb_footnote (const struct spvlb_footnote *in, const char *encoding,
778                        size_t idx, struct pivot_table *table)
779 {
780   struct pivot_value *content;
781   char *error = decode_spvlb_value (table, in->text, encoding, &content);
782   if (error)
783     return error;
784
785   struct pivot_value *marker = NULL;
786   if (in->marker)
787     {
788       error = decode_spvlb_value (table, in->marker, encoding, &marker);
789       if (error)
790         {
791           pivot_value_destroy (content);
792           return error;
793         }
794       if (marker->type == PIVOT_VALUE_TEXT)
795         marker->text.user_provided = false;
796     }
797
798   struct pivot_footnote *f = pivot_table_create_footnote__ (
799     table, idx, marker, content);
800   f->show = (int32_t) in->show > 0;
801   return NULL;
802 }
803
804 static char * WARN_UNUSED_RESULT
805 decode_current_layer (uint64_t current_layer, struct pivot_table *table)
806 {
807   const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
808   table->current_layer = xnmalloc (axis->n_dimensions,
809                                    sizeof *table->current_layer);
810
811   for (size_t i = 0; i < axis->n_dimensions; i++)
812     {
813       const struct pivot_dimension *d = axis->dimensions[i];
814       if (d->n_leaves)
815         {
816           table->current_layer[i] = current_layer % d->n_leaves;
817           current_layer /= d->n_leaves;
818         }
819       else
820         table->current_layer[i] = 0;
821     }
822   if (current_layer > 0)
823     return xasprintf ("out of range layer data index %"PRIu64, current_layer);
824   return NULL;
825 }
826
827 char * WARN_UNUSED_RESULT
828 decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
829 {
830   *outp = NULL;
831   if (in->header->version != 1 && in->header->version != 3)
832     return xasprintf ("unknown version %"PRIu32" (expected 1 or 3)",
833                       in->header->version);
834
835   char *error = NULL;
836   struct pivot_table *out = xzalloc (sizeof *out);
837   out->ref_cnt = 1;
838   hmap_init (&out->cells);
839   out->look = pivot_table_look_new_builtin_default ();
840   out->settings = (struct fmt_settings) FMT_SETTINGS_INIT;
841
842   const struct spvlb_y1 *y1 = (in->formats->x0 ? in->formats->x0->y1
843                                : in->formats->x3 ? in->formats->x3->y1
844                                : NULL);
845   const char *encoding = spvlb_table_get_encoding (in);
846
847   /* Display settings. */
848   out->look->show_numeric_markers = !in->ts->show_alphabetic_markers;
849   out->rotate_inner_column_labels = in->header->rotate_inner_column_labels;
850   out->rotate_outer_row_labels = in->header->rotate_outer_row_labels;
851   out->look->row_labels_in_corner = in->ts->show_row_labels_in_corner;
852   out->show_grid_lines = in->borders->show_grid_lines;
853   out->show_title = true;
854   out->show_caption = true;
855   out->look->footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
856   out->look->omit_empty = in->ts->omit_empty;
857
858   const struct spvlb_x1 *x1 = in->formats->x1;
859   if (x1)
860     {
861       error = decode_spvlb_value_show (x1->show_values, &out->show_values);
862       if (!error)
863         error = decode_spvlb_value_show (x1->show_variables,
864                                          &out->show_variables);
865       if (error)
866         goto error;
867
868       out->show_caption = x1->show_caption;
869       out->show_title = x1->show_title != 10;
870     }
871
872   /* Column and row display settings. */
873   out->look->width_ranges[TABLE_VERT][0] = in->header->min_row_height;
874   out->look->width_ranges[TABLE_VERT][1] = in->header->max_row_height;
875   out->look->width_ranges[TABLE_HORZ][0] = in->header->min_col_width;
876   out->look->width_ranges[TABLE_HORZ][1] = in->header->max_col_width;
877
878   convert_widths (in->formats->widths, in->formats->n_widths,
879                   &out->sizing[TABLE_HORZ].widths,
880                   &out->sizing[TABLE_HORZ].n_widths);
881
882   const struct spvlb_x2 *x2 = in->formats->x2;
883   if (x2)
884     convert_widths (x2->row_heights, x2->n_row_heights,
885                     &out->sizing[TABLE_VERT].widths,
886                     &out->sizing[TABLE_VERT].n_widths);
887
888   convert_breakpoints (in->ts->row_breaks,
889                        &out->sizing[TABLE_VERT].breaks,
890                        &out->sizing[TABLE_VERT].n_breaks);
891   convert_breakpoints (in->ts->col_breaks,
892                        &out->sizing[TABLE_HORZ].breaks,
893                        &out->sizing[TABLE_HORZ].n_breaks);
894
895   convert_keeps (in->ts->row_keeps,
896                  &out->sizing[TABLE_VERT].keeps,
897                  &out->sizing[TABLE_VERT].n_keeps);
898   convert_keeps (in->ts->col_keeps,
899                  &out->sizing[TABLE_HORZ].keeps,
900                  &out->sizing[TABLE_HORZ].n_keeps);
901
902   out->notes = to_utf8_if_nonempty (in->ts->notes, encoding);
903   out->look->name = to_utf8_if_nonempty (in->ts->table_look, encoding);
904
905   /* Print settings. */
906   out->look->print_all_layers = in->ps->all_layers;
907   out->look->paginate_layers = in->ps->paginate_layers;
908   out->look->shrink_to_fit[TABLE_HORZ] = in->ps->fit_width;
909   out->look->shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
910   out->look->top_continuation = in->ps->top_continuation;
911   out->look->bottom_continuation = in->ps->bottom_continuation;
912   out->look->continuation = to_utf8 (in->ps->continuation_string, encoding);
913   out->look->n_orphan_lines = in->ps->n_orphan_lines;
914
915   /* Format settings. */
916   int epoch = in->formats->y0->epoch;
917   if (epoch >= 1000 && epoch <= 9999)
918     out->settings.epoch = epoch;
919   char decimal = in->formats->y0->decimal;
920   if (decimal == '.' || decimal == '.')
921     out->settings.decimal = decimal;
922   else
923     {
924       /* XXX warn about bad decimal point */
925     }
926   out->grouping = in->formats->y0->grouping;
927   const struct spvlb_custom_currency *cc = in->formats->custom_currency;
928   for (int i = 0; i < 5; i++)
929     {
930       if (cc && i < cc->n_ccs)
931         {
932           out->settings.ccs[i] = fmt_number_style_from_string (cc->ccs[i]);
933           /* XXX warn if parsing fails */
934         }
935     }
936   out->small = in->formats->x3 ? in->formats->x3->small : 0;
937
938   /* Command information. */
939   if (y1)
940     {
941       out->command_local = to_utf8 (y1->command_local, encoding);
942       out->command_c = to_utf8 (y1->command, encoding);
943       out->language = to_utf8 (y1->language, encoding);
944       /* charset? */
945       out->locale = to_utf8 (y1->locale, encoding);
946     }
947
948   /* Source information. */
949   const struct spvlb_x3 *x3 = in->formats->x3;
950   if (x3)
951     {
952       if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
953         out->dataset = to_utf8 (x3->dataset, encoding);
954       out->datafile = to_utf8_if_nonempty (x3->datafile, encoding);
955       out->date = x3->date;
956     }
957
958   /* Footnotes.
959
960      Any pivot_value might refer to footnotes, so it's important to process the
961      footnotes early to ensure that those references can be resolved.  There is
962      a possible problem that a footnote might itself reference an
963      as-yet-unprocessed footnote, but that's OK because footnote references
964      don't actually look at the footnote contents but only resolve a pointer to
965      where the footnote will go later.
966
967      Before we really start, create all the footnotes we'll fill in.  This is
968      because sometimes footnotes refer to themselves or to each other and we
969      don't want to reject those references. */
970   const struct spvlb_footnotes *fn = in->footnotes;
971   if (fn->n_footnotes > 0)
972     {
973       pivot_table_create_footnote__ (out, fn->n_footnotes - 1, NULL, NULL);
974       for (size_t i = 0; i < fn->n_footnotes; i++)
975         {
976           error = decode_spvlb_footnote (in->footnotes->footnotes[i],
977                                          encoding, i, out);
978           if (error)
979             goto error;
980         }
981     }
982
983   /* Title and caption. */
984   error = decode_spvlb_value (out, in->titles->user_title, encoding,
985                               &out->title);
986   if (error)
987     goto error;
988
989   error = decode_spvlb_value (out, in->titles->subtype, encoding,
990                               &out->subtype);
991   if (error)
992     goto error;
993
994   if (in->titles->corner_text)
995     {
996       error = decode_spvlb_value (out, in->titles->corner_text,
997                                   encoding, &out->corner_text);
998       if (error)
999         goto error;
1000     }
1001
1002   if (in->titles->caption)
1003     {
1004       error = decode_spvlb_value (out, in->titles->caption, encoding,
1005                                   &out->caption);
1006       if (error)
1007         goto error;
1008     }
1009
1010
1011   /* Styles. */
1012   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1013     {
1014       error = decode_spvlb_area (in->areas->areas[i], &out->look->areas[i],
1015                                  encoding);
1016       if (error)
1017         goto error;
1018     }
1019   for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
1020     {
1021       error = decode_spvlb_border (in->borders->borders[i], out);
1022       if (error)
1023         goto error;
1024     }
1025
1026   /* Dimensions. */
1027   out->n_dimensions = in->dimensions->n_dims;
1028   out->dimensions = XCALLOC (out->n_dimensions, struct pivot_dimension *);
1029   for (size_t i = 0; i < out->n_dimensions; i++)
1030     {
1031       error = decode_spvlb_dimension (out, in->dimensions->dims[i],
1032                                       i, encoding, &out->dimensions[i]);
1033       if (error)
1034         goto error;
1035     }
1036
1037   /* Axes. */
1038   size_t a = in->axes->n_layers;
1039   size_t b = in->axes->n_rows;
1040   size_t c = in->axes->n_columns;
1041   if (size_overflow_p (xsum3 (a, b, c)) || a + b + c != out->n_dimensions)
1042     {
1043       error = xasprintf ("dimensions do not sum correctly "
1044                          "(%zu + %zu + %zu != %zu)",
1045                          a, b, c, out->n_dimensions);
1046       goto error;
1047     }
1048   error = decode_spvlb_axis (in->axes->layers, in->axes->n_layers,
1049                              PIVOT_AXIS_LAYER, out);
1050   if (error)
1051     goto error;
1052   error = decode_spvlb_axis (in->axes->rows, in->axes->n_rows,
1053                              PIVOT_AXIS_ROW, out);
1054   if (error)
1055     goto error;
1056   error = decode_spvlb_axis (in->axes->columns, in->axes->n_columns,
1057                              PIVOT_AXIS_COLUMN, out);
1058   if (error)
1059     goto error;
1060
1061   pivot_table_assign_label_depth (out);
1062
1063   error = decode_current_layer (in->ts->current_layer, out);
1064   if (error)
1065     goto error;
1066
1067   /* Data. */
1068   error = decode_spvlb_cells (in->cells->cells, in->cells->n_cells, out,
1069                               encoding);
1070
1071   *outp = out;
1072   return NULL;
1073
1074 error:
1075   pivot_table_unref (out);
1076   return error;
1077 }
1078 \f
1079 /* collect_spvlb_strings */
1080
1081 static void
1082 add_if_nonempty (struct string_array *strings, const char *s)
1083 {
1084   if (s && s[0])
1085     string_array_append (strings, s);
1086 }
1087
1088 static void
1089 collect_value_mod_strings (struct string_array *strings,
1090                            const struct spvlb_value_mod *vm)
1091 {
1092   if (vm->template_string)
1093     add_if_nonempty (strings, vm->template_string->id);
1094
1095   if (vm->style_pair && vm->style_pair->font_style)
1096     add_if_nonempty (strings, vm->style_pair->font_style->typeface);
1097 }
1098
1099 static void
1100 collect_value_strings (struct string_array *strings,
1101                        const struct spvlb_value *value)
1102 {
1103   if (!value)
1104     return;
1105
1106   switch (value->type)
1107     {
1108     case 1:
1109       collect_value_mod_strings (strings, value->type_01.value_mod);
1110       break;
1111
1112     case 2:
1113       collect_value_mod_strings (strings, value->type_02.value_mod);
1114       add_if_nonempty (strings, value->type_02.var_name);
1115       add_if_nonempty (strings, value->type_02.value_label);
1116       break;
1117
1118     case 3:
1119       collect_value_mod_strings (strings, value->type_03.value_mod);
1120       add_if_nonempty (strings, value->type_03.local);
1121       add_if_nonempty (strings, value->type_03.id);
1122       add_if_nonempty (strings, value->type_03.c);
1123       break;
1124
1125     case 4:
1126       collect_value_mod_strings (strings, value->type_04.value_mod);
1127       add_if_nonempty (strings, value->type_04.value_label);
1128       add_if_nonempty (strings, value->type_04.var_name);
1129       add_if_nonempty (strings, value->type_04.s);
1130       break;
1131
1132     case 5:
1133       collect_value_mod_strings (strings, value->type_05.value_mod);
1134       add_if_nonempty (strings, value->type_05.var_name);
1135       add_if_nonempty (strings, value->type_05.var_label);
1136       break;
1137
1138     case 6:
1139       collect_value_mod_strings (strings, value->type_06.value_mod);
1140       add_if_nonempty (strings, value->type_06.local);
1141       add_if_nonempty (strings, value->type_06.id);
1142       add_if_nonempty (strings, value->type_06.c);
1143       break;
1144
1145     case -1:
1146       collect_value_mod_strings (strings, value->type_else.value_mod);
1147       add_if_nonempty (strings, value->type_else.template);
1148       for (size_t i = 0; i < value->type_else.n_args; i++)
1149         {
1150           const struct spvlb_argument *a = value->type_else.args[i];
1151           collect_value_strings (strings, a->value);
1152           for (size_t j = 0; j < a->n_values; j++)
1153             collect_value_strings (strings, a->values[j]);
1154         }
1155       break;
1156     }
1157 }
1158
1159 static void
1160 collect_category_strings (struct string_array *strings,
1161                           const struct spvlb_category *cat)
1162 {
1163   collect_value_strings (strings, cat->name);
1164   if (cat->group)
1165     for (size_t i = 0; i < cat->group->n_subcategories; i++)
1166       collect_category_strings (strings, cat->group->subcategories[i]);
1167 }
1168
1169 /* Adds all of the characters strings in TABLE to STRINGS. */
1170 void
1171 collect_spvlb_strings (const struct spvlb_table *table,
1172                        struct string_array *strings)
1173 {
1174   add_if_nonempty (strings, table->ts->notes);
1175   add_if_nonempty (strings, table->ts->table_look);
1176   add_if_nonempty (strings, table->ps->continuation_string);
1177
1178   const struct spvlb_custom_currency *cc = table->formats->custom_currency;
1179   if (cc)
1180     for (int i = 0; i < cc->n_ccs; i++)
1181       add_if_nonempty (strings, cc->ccs[i]);
1182
1183   const struct spvlb_y1 *y1 = (table->formats->x0 ? table->formats->x0->y1
1184                                : table->formats->x3 ? table->formats->x3->y1
1185                                : NULL);
1186   if (y1)
1187     {
1188       add_if_nonempty (strings, y1->command_local);
1189       add_if_nonempty (strings, y1->command);
1190       add_if_nonempty (strings, y1->language);
1191       add_if_nonempty (strings, y1->charset);
1192       add_if_nonempty (strings, y1->locale);
1193     }
1194
1195   const struct spvlb_x3 *x3 = table->formats->x3;
1196   if (x3)
1197     {
1198       if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
1199         add_if_nonempty (strings, x3->dataset);
1200       add_if_nonempty (strings, x3->datafile);
1201     }
1202
1203   for (size_t i = 0; i < table->footnotes->n_footnotes; i++)
1204     {
1205       const struct spvlb_footnote *f = table->footnotes->footnotes[i];
1206       collect_value_strings (strings, f->text);
1207       collect_value_strings (strings, f->marker);
1208     }
1209
1210   collect_value_strings (strings, table->titles->user_title);
1211   collect_value_strings (strings, table->titles->subtype);
1212   collect_value_strings (strings, table->titles->corner_text);
1213   collect_value_strings (strings, table->titles->caption);
1214
1215   for (size_t i = 0; i < PIVOT_N_AREAS; i++)
1216     add_if_nonempty (strings, table->areas->areas[i]->typeface);
1217
1218   for (size_t i = 0; i < table->dimensions->n_dims; i++)
1219     {
1220       const struct spvlb_dimension *d = table->dimensions->dims[i];
1221       collect_value_strings (strings, d->name);
1222       for (size_t j = 0; j < d->n_categories; j++)
1223         collect_category_strings (strings, d->categories[j]);
1224     }
1225
1226   for (size_t i = 0; i < table->cells->n_cells; i++)
1227     collect_value_strings (strings, table->cells->cells[i]->value);
1228 }
1229 \f
1230 /* Returns the encoding that TABLE declares to be in use for its strings.
1231    (Watch out, it's not always correct.) */
1232 const char *
1233 spvlb_table_get_encoding (const struct spvlb_table *table)
1234 {
1235   const struct spvlb_y1 *y1 = (table->formats->x0 ? table->formats->x0->y1
1236                                : table->formats->x3 ? table->formats->x3->y1
1237                                : NULL);
1238   if (y1)
1239     return y1->charset;
1240   else
1241     {
1242       const char *dot = strchr (table->formats->locale, '.');
1243       return dot ? dot + 1 : "windows-1252";
1244     }
1245 }