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