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