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