spv-light-decoder: Support special format 40 in decode_spvlb_value().
[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       out->numeric.honor_small = (in->type_01.format >> 16) == 40;
306       if (error)
307         return error;
308       break;
309
310     case 2:
311       vm = in->type_02.value_mod;
312       out->type = PIVOT_VALUE_NUMERIC;
313       out->numeric.x = in->type_02.x;
314       error = spv_decode_fmt_spec (in->type_02.format, &out->numeric.format);
315       if (!error)
316         error = decode_spvlb_value_show (in->type_02.show, &out->numeric.show);
317       if (error)
318         return NULL;
319       out->numeric.var_name = to_utf8_if_nonempty (in->type_02.var_name,
320                                                    encoding);
321       out->numeric.value_label = to_utf8_if_nonempty (in->type_02.value_label,
322                                                       encoding);
323       break;
324
325     case 3:
326       vm = in->type_03.value_mod;
327       out->type = PIVOT_VALUE_TEXT;
328       out->text.local = to_utf8 (in->type_03.local, encoding);
329       out->text.c = to_utf8 (in->type_03.c, encoding);
330       out->text.id = to_utf8 (in->type_03.id, encoding);
331       out->text.user_provided = !in->type_03.fixed;
332       break;
333
334     case 4:
335       vm = in->type_04.value_mod;
336       out->type = PIVOT_VALUE_STRING;
337       error = decode_spvlb_value_show (in->type_04.show, &out->string.show);
338       if (error)
339         return NULL;
340       out->string.s = to_utf8 (in->type_04.s, encoding);
341       out->string.hex = (in->type_04.format >> 16) == fmt_to_io (FMT_AHEX);
342       out->string.var_name = to_utf8 (in->type_04.var_name, encoding);
343       out->string.value_label = to_utf8_if_nonempty (in->type_04.value_label,
344                                                      encoding);
345       break;
346
347     case 5:
348       vm = in->type_05.value_mod;
349       out->type = PIVOT_VALUE_VARIABLE;
350       error = decode_spvlb_value_show (in->type_05.show, &out->variable.show);
351       if (error)
352         return error;
353       out->variable.var_name = to_utf8 (in->type_05.var_name, encoding);
354       out->variable.var_label = to_utf8_if_nonempty (in->type_05.var_label,
355                                                      encoding);
356       break;
357
358     case 6:
359       vm = in->type_06.value_mod;
360       out->type = PIVOT_VALUE_TEXT;
361       out->text.local = to_utf8 (in->type_06.local, encoding);
362       out->text.c = to_utf8 (in->type_06.c, encoding);
363       out->text.id = to_utf8 (in->type_06.id, encoding);
364       out->text.user_provided = false;
365       break;
366
367     case -1:
368       vm = in->type_else.value_mod;
369       out->type = PIVOT_VALUE_TEMPLATE;
370       out->template.local = to_utf8 (in->type_else.template, encoding);
371       out->template.id = out->template.local;
372       out->template.n_args = 0;
373       out->template.args = xnmalloc (in->type_else.n_args,
374                                      sizeof *out->template.args);
375       for (size_t i = 0; i < in->type_else.n_args; i++)
376         {
377           error = decode_spvlb_argument (table, in->type_else.args[i],
378                                          encoding, &out->template.args[i]);
379           if (error)
380             {
381               pivot_value_destroy (out);
382               return error;
383             }
384           out->template.n_args++;
385         }
386       break;
387
388     default:
389       abort ();
390     }
391
392   if (vm)
393     {
394       if (vm->n_subscripts)
395         {
396           out->n_subscripts = vm->n_subscripts;
397           out->subscripts = xnmalloc (vm->n_subscripts,
398                                       sizeof *out->subscripts);
399           for (size_t i = 0; i < vm->n_subscripts; i++)
400             out->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
401         }
402
403       if (vm->n_refs)
404         {
405           out->footnote_indexes = xnmalloc (vm->n_refs,
406                                             sizeof *out->footnote_indexes);
407           for (size_t i = 0; i < vm->n_refs; i++)
408             {
409               uint16_t idx = vm->refs[i];
410               if (idx >= table->n_footnotes)
411                 {
412                   pivot_value_destroy (out);
413                   return xasprintf ("bad footnote index: %"PRIu16" >= %zu",
414                                     idx, table->n_footnotes);
415                 }
416
417               out->footnote_indexes[out->n_footnotes++] = idx;
418             }
419           pivot_value_sort_footnotes (out);
420         }
421
422       if (vm->style_pair)
423         {
424           error = decode_spvlb_font_style (vm->style_pair->font_style,
425                                            encoding, &out->font_style);
426           if (!error)
427             error = decode_spvlb_cell_style (vm->style_pair->cell_style,
428                                              &out->cell_style);
429           if (error)
430             {
431               pivot_value_destroy (out);
432               return error;
433             }
434         }
435
436       if (vm->template_string
437           && vm->template_string->id
438           && vm->template_string->id[0]
439           && out->type == PIVOT_VALUE_TEMPLATE)
440         out->template.id = to_utf8 (vm->template_string->id, encoding);
441     }
442
443   *outp = out;
444   return NULL;
445 }
446
447 static char * WARN_UNUSED_RESULT
448 decode_spvlb_area (const struct spvlb_area *in, struct table_area_style *out,
449                    const char *encoding)
450 {
451   char *error;
452
453   struct cell_color fg0, fg1, bg0, bg1;
454   error = decode_spvlb_color_string (in->fg_color, 0x00, &fg0);
455   if (!error)
456     error = decode_spvlb_color_string (in->bg_color, 0xff, &bg0);
457   if (!error && in->alternate)
458     error = decode_spvlb_color_string (in->alt_fg_color, 0x00, &fg1);
459   if (!error && in->alternate)
460     error = decode_spvlb_color_string (in->alt_bg_color, 0xff, &bg1);
461
462   enum table_halign halign;
463   if (!error)
464     {
465       error = decode_spvlb_halign (in->halign, &halign);
466
467       /* TABLE_HALIGN_DECIMAL doesn't seem to be a real halign for areas, which
468          is good because there's no way to indicate the decimal offset.  Just
469          in case: */
470       if (!error && halign == TABLE_HALIGN_DECIMAL)
471         halign = TABLE_HALIGN_MIXED;
472     }
473
474   enum table_valign valign;
475   if (!error)
476     error = decode_spvlb_valign (in->valign, &valign);
477
478   if (error)
479     return error;
480
481   table_area_style_uninit (out);
482   *out = (struct table_area_style) {
483     .font_style = {
484       .bold = (in->style & 1) != 0,
485       .italic = (in->style & 2) != 0,
486       .underline = in->underline,
487       .fg = { fg0, in->alternate ? fg1 : fg0 },
488       .bg = { bg0, in->alternate ? bg1 : bg0 },
489       .typeface = to_utf8 (in->typeface, encoding),
490       .size = in->size / 1.33,
491     },
492     .cell_style = {
493       .halign = halign,
494       .valign = valign,
495       .margin = {
496         [TABLE_HORZ] = { in->left_margin, in->right_margin },
497         [TABLE_VERT] = { in->top_margin, in->bottom_margin },
498       },
499     },
500   };
501   return NULL;
502 }
503
504 static char * WARN_UNUSED_RESULT
505 decode_spvlb_group (const struct pivot_table *,
506                     struct spvlb_category **,
507                     size_t n_categories,
508                     bool show_label,
509                     struct pivot_category *parent,
510                     struct pivot_dimension *,
511                     const char *encoding);
512
513 static char * WARN_UNUSED_RESULT
514 decode_spvlb_categories (const struct pivot_table *table,
515                          struct spvlb_category **categories,
516                          size_t n_categories,
517                          struct pivot_category *parent,
518                          struct pivot_dimension *dimension,
519                          const char *encoding)
520 {
521   for (size_t i = 0; i < n_categories; i++)
522     {
523       const struct spvlb_category *in = categories[i];
524       if (in->group && in->group->merge)
525         {
526           char *error = decode_spvlb_categories (
527             table, in->group->subcategories, in->group->n_subcategories,
528             parent, dimension, encoding);
529           if (error)
530             return error;
531
532           continue;
533         }
534
535       struct pivot_value *name;
536       char *error = decode_spvlb_value (table, in->name, encoding, &name);
537       if (error)
538         return error;
539
540       struct pivot_category *out = xzalloc (sizeof *out);
541       out->name = name;
542       out->parent = parent;
543       out->dimension = dimension;
544       if (in->group)
545         {
546           char *error = decode_spvlb_group (table, in->group->subcategories,
547                                             in->group->n_subcategories,
548                                             true, out, dimension, encoding);
549           if (error)
550             {
551               pivot_category_destroy (out);
552               return error;
553             }
554
555           out->data_index = SIZE_MAX;
556           out->presentation_index = SIZE_MAX;
557         }
558       else
559         {
560           out->data_index = in->leaf->leaf_index;
561           out->presentation_index = dimension->n_leaves;
562           dimension->n_leaves++;
563         }
564
565       if (parent->n_subs >= parent->allocated_subs)
566         parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs,
567                                    sizeof *parent->subs);
568       parent->subs[parent->n_subs++] = out;
569     }
570   return NULL;
571 }
572
573 static char * WARN_UNUSED_RESULT
574 decode_spvlb_group (const struct pivot_table *table,
575                     struct spvlb_category **categories,
576                     size_t n_categories, bool show_label,
577                     struct pivot_category *category,
578                     struct pivot_dimension *dimension,
579                     const char *encoding)
580 {
581   category->subs = XCALLOC (n_categories, struct pivot_category *);
582   category->n_subs = 0;
583   category->allocated_subs = 0;
584   category->show_label = show_label;
585
586   return decode_spvlb_categories (table, categories, n_categories, category,
587                                   dimension, encoding);
588 }
589
590 static char * WARN_UNUSED_RESULT
591 fill_leaves (struct pivot_category *category,
592              struct pivot_dimension *dimension)
593 {
594   if (pivot_category_is_group (category))
595     {
596       for (size_t i = 0; i < category->n_subs; i++)
597         {
598           char *error = fill_leaves (category->subs[i], dimension);
599           if (error)
600             return error;
601         }
602     }
603   else
604     {
605       if (category->data_index >= dimension->n_leaves)
606         return xasprintf ("leaf_index %zu >= n_leaves %zu",
607                           category->data_index, dimension->n_leaves);
608       if (dimension->data_leaves[category->data_index])
609         return xasprintf ("two leaves with data_index %zu",
610                           category->data_index);
611       dimension->data_leaves[category->data_index] = category;
612       dimension->presentation_leaves[category->presentation_index] = category;
613     }
614   return NULL;
615 }
616
617 static char * WARN_UNUSED_RESULT
618 decode_spvlb_dimension (const struct pivot_table *table,
619                         const struct spvlb_dimension *in,
620                         size_t idx, const char *encoding,
621                         struct pivot_dimension **outp)
622 {
623   /* Convert most of the dimension. */
624   struct pivot_value *name;
625   char *error = decode_spvlb_value (table, in->name, encoding, &name);
626   if (error)
627     return error;
628
629   struct pivot_dimension *out = xzalloc (sizeof *out);
630   out->level = UINT_MAX;
631   out->top_index = idx;
632   out->hide_all_labels = in->props->hide_all_labels;
633
634   out->root = xzalloc (sizeof *out->root);
635   *out->root = (struct pivot_category) {
636     .name = name,
637     .dimension = out,
638     .data_index = SIZE_MAX,
639     .presentation_index = SIZE_MAX,
640   };
641   error = decode_spvlb_group (table, in->categories, in->n_categories,
642                               !in->props->hide_dim_label, out->root,
643                               out, encoding);
644   if (error)
645     goto error;
646
647   /* Allocate and fill the array of leaves now that we know how many there
648      are. */
649   out->data_leaves = XCALLOC (out->n_leaves, struct pivot_category *);
650   out->presentation_leaves = XCALLOC (out->n_leaves, struct pivot_category *);
651   out->allocated_leaves = out->n_leaves;
652   error = fill_leaves (out->root, out);
653   if (error)
654     goto error;
655   for (size_t i = 0; i < out->n_leaves; i++)
656     {
657       assert (out->data_leaves[i] != NULL);
658       assert (out->presentation_leaves[i] != NULL);
659     }
660   *outp = out;
661   return NULL;
662
663 error:
664   pivot_dimension_destroy (out);
665   return error;
666 }
667
668 static char * WARN_UNUSED_RESULT
669 decode_spvlb_stroke (uint32_t stroke_type, enum table_stroke *strokep)
670 {
671   enum table_stroke strokes[] = {
672     TABLE_STROKE_NONE,
673     TABLE_STROKE_SOLID,
674     TABLE_STROKE_DASHED,
675     TABLE_STROKE_THICK,
676     TABLE_STROKE_THIN,
677     TABLE_STROKE_DOUBLE,
678   };
679
680   if (stroke_type >= sizeof strokes / sizeof *strokes)
681     return xasprintf ("bad stroke %"PRIu32, stroke_type);
682
683   *strokep = strokes[stroke_type];
684   return NULL;
685 }
686
687 static char * WARN_UNUSED_RESULT
688 decode_spvlb_border (const struct spvlb_border *in, struct pivot_table *table)
689
690 {
691   if (in->border_type >= PIVOT_N_BORDERS)
692     return xasprintf ("bad border type %"PRIu32, in->border_type);
693
694   struct table_border_style *out = &table->look->borders[in->border_type];
695   out->color = decode_spvlb_color_u32 (in->color);
696   return decode_spvlb_stroke (in->stroke_type, &out->stroke);
697 }
698
699 static char * WARN_UNUSED_RESULT
700 decode_spvlb_axis (const uint32_t *dimension_indexes, size_t n_dimensions,
701                    enum pivot_axis_type axis_type, struct pivot_table *table)
702 {
703   struct pivot_axis *axis = &table->axes[axis_type];
704   axis->dimensions = XCALLOC (n_dimensions, struct pivot_dimension *);
705   axis->n_dimensions = n_dimensions;
706   axis->extent = 1;
707   for (size_t i = 0; i < n_dimensions; i++)
708     {
709       uint32_t idx = dimension_indexes[i];
710       if (idx >= table->n_dimensions)
711         return xasprintf ("bad dimension index %"PRIu32" >= %zu",
712                           idx, table->n_dimensions);
713
714       struct pivot_dimension *d = table->dimensions[idx];
715       if (d->level != UINT_MAX)
716         return xasprintf ("duplicate dimension %"PRIu32, idx);
717
718       axis->dimensions[i] = d;
719       d->axis_type = axis_type;
720       d->level = i;
721
722       axis->extent *= d->n_leaves;
723     }
724
725   return NULL;
726 }
727
728 static char *
729 decode_data_index (uint64_t in, const struct pivot_table *table,
730                    size_t *out)
731 {
732   uint64_t remainder = in;
733   for (size_t i = table->n_dimensions - 1; i < table->n_dimensions; i--)
734     {
735       const struct pivot_dimension *d = table->dimensions[i];
736       if (d->n_leaves)
737         {
738           out[i] = remainder % d->n_leaves;
739           remainder /= d->n_leaves;
740         }
741       else
742         out[i] = 0;
743     }
744   if (remainder)
745     return xasprintf ("out of range cell data index %"PRIu64, in);
746
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 }