spv-light-decoder: Avoid GCC 11.x warning in decode_spvlb_halign().
[pspp] / src / output / spv / spv-light-decoder.c
index 990a847b8527920487b006c972486e47031f3fb5..f8c4f618b2edb0bb184af82940328e2512b67b6f 100644 (file)
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistr.h>
 
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
+#include "libpspp/string-array.h"
 #include "output/pivot-table.h"
 #include "output/spv/light-binary-parser.h"
 #include "output/spv/spv.h"
 #include "gl/xalloc.h"
 #include "gl/xsize.h"
 
+/* Returns a copy of S converted to UTF-8.  S might be in UTF-8 already or it
+   might be in ENCODING (yes, this makes no sense). */
 static char *
 to_utf8 (const char *s, const char *encoding)
 {
-  return recode_string ("UTF-8", encoding, s, strlen (s));
+  size_t length = strlen (s);
+  return (u8_check (CHAR_CAST (const uint8_t *, s), length)
+          ? recode_string ("UTF-8", encoding, s, length)
+          : xstrdup (s));
 }
 
 static char *
@@ -89,7 +96,7 @@ static char * WARN_UNUSED_RESULT
 decode_spvlb_color_string (const char *s, uint8_t def,
                            struct cell_color *colorp)
 {
-  int r, g, b;
+  unsigned int r, g, b;
   if (!*s)
     r = g = b = def;
   else if (sscanf (s, "#%2x%2x%2x", &r, &g, &b) != 3)
@@ -163,6 +170,7 @@ decode_spvlb_halign (uint32_t in, enum table_halign *halignp)
       return NULL;
 
     default:
+      *halignp = 0;
       return xasprintf ("bad cell style halign %"PRIu32, in);
     }
 }
@@ -284,7 +292,7 @@ decode_spvlb_value (const struct pivot_table *table,
 {
   *outp = NULL;
 
-  struct pivot_value *out = xzalloc (sizeof *out);
+  struct pivot_value *out = XZALLOC (struct pivot_value);
   const struct spvlb_value_mod *vm;
 
   char *error;
@@ -295,6 +303,7 @@ decode_spvlb_value (const struct pivot_table *table,
       out->type = PIVOT_VALUE_NUMERIC;
       out->numeric.x = in->type_01.x;
       error = spv_decode_fmt_spec (in->type_01.format, &out->numeric.format);
+      out->numeric.honor_small = (in->type_01.format >> 16) == 40;
       if (error)
         return error;
       break;
@@ -330,6 +339,7 @@ decode_spvlb_value (const struct pivot_table *table,
       if (error)
         return NULL;
       out->string.s = to_utf8 (in->type_04.s, encoding);
+      out->string.hex = (in->type_04.format >> 16) == fmt_to_io (FMT_AHEX);
       out->string.var_name = to_utf8 (in->type_04.var_name, encoding);
       out->string.value_label = to_utf8_if_nonempty (in->type_04.value_label,
                                                      encoding);
@@ -384,16 +394,19 @@ decode_spvlb_value (const struct pivot_table *table,
     {
       if (vm->n_subscripts)
         {
-          out->n_subscripts = vm->n_subscripts;
-          out->subscripts = xnmalloc (vm->n_subscripts,
-                                      sizeof *out->subscripts);
+          struct pivot_value_ex *ex = pivot_value_ex_rw (out);
+          ex->n_subscripts = vm->n_subscripts;
+          ex->subscripts = xnmalloc (vm->n_subscripts, sizeof *ex->subscripts);
           for (size_t i = 0; i < vm->n_subscripts; i++)
-            out->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
+            ex->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
         }
 
       if (vm->n_refs)
         {
-          out->footnotes = xnmalloc (vm->n_refs, sizeof *out->footnotes);
+          struct pivot_value_ex *ex = pivot_value_ex_rw (out);
+          ex->footnote_indexes = xnmalloc (vm->n_refs,
+                                           sizeof *ex->footnote_indexes);
+
           for (size_t i = 0; i < vm->n_refs; i++)
             {
               uint16_t idx = vm->refs[i];
@@ -404,17 +417,19 @@ decode_spvlb_value (const struct pivot_table *table,
                                     idx, table->n_footnotes);
                 }
 
-              out->footnotes[out->n_footnotes++] = table->footnotes[idx];
+              ex->footnote_indexes[ex->n_footnotes++] = idx;
             }
+          pivot_value_sort_footnotes (out);
         }
 
       if (vm->style_pair)
         {
+          struct pivot_value_ex *ex = pivot_value_ex_rw (out);
           error = decode_spvlb_font_style (vm->style_pair->font_style,
-                                           encoding, &out->font_style);
+                                           encoding, &ex->font_style);
           if (!error)
             error = decode_spvlb_cell_style (vm->style_pair->cell_style,
-                                             &out->cell_style);
+                                             &ex->cell_style);
           if (error)
             {
               pivot_value_destroy (out);
@@ -467,6 +482,7 @@ decode_spvlb_area (const struct spvlb_area *in, struct table_area_style *out,
   if (error)
     return error;
 
+  table_area_style_uninit (out);
   *out = (struct table_area_style) {
     .font_style = {
       .bold = (in->style & 1) != 0,
@@ -525,7 +541,7 @@ decode_spvlb_categories (const struct pivot_table *table,
       if (error)
         return error;
 
-      struct pivot_category *out = xzalloc (sizeof *out);
+      struct pivot_category *out = XZALLOC (struct pivot_category);
       out->name = name;
       out->parent = parent;
       out->dimension = dimension;
@@ -614,7 +630,7 @@ decode_spvlb_dimension (const struct pivot_table *table,
   if (error)
     return error;
 
-  struct pivot_dimension *out = xzalloc (sizeof *out);
+  struct pivot_dimension *out = XZALLOC (struct pivot_dimension);
   out->level = UINT_MAX;
   out->top_index = idx;
   out->hide_all_labels = in->props->hide_all_labels;
@@ -718,7 +734,7 @@ decode_data_index (uint64_t in, const struct pivot_table *table,
                    size_t *out)
 {
   uint64_t remainder = in;
-  for (size_t i = table->n_dimensions - 1; i > 0; i--)
+  for (size_t i = table->n_dimensions - 1; i < table->n_dimensions; i--)
     {
       const struct pivot_dimension *d = table->dimensions[i];
       if (d->n_leaves)
@@ -729,10 +745,9 @@ decode_data_index (uint64_t in, const struct pivot_table *table,
       else
         out[i] = 0;
     }
-  if (remainder >= table->dimensions[0]->n_leaves)
+  if (remainder)
     return xasprintf ("out of range cell data index %"PRIu64, in);
 
-  out[0] = remainder;
   return NULL;
 }
 
@@ -797,19 +812,21 @@ decode_current_layer (uint64_t current_layer, struct pivot_table *table)
   table->current_layer = xnmalloc (axis->n_dimensions,
                                    sizeof *table->current_layer);
 
+  uint64_t remainder = current_layer;
   for (size_t i = 0; i < axis->n_dimensions; i++)
     {
       const struct pivot_dimension *d = axis->dimensions[i];
       if (d->n_leaves)
         {
-          table->current_layer[i] = current_layer % d->n_leaves;
-          current_layer /= d->n_leaves;
+          table->current_layer[i] = remainder % d->n_leaves;
+          remainder /= d->n_leaves;
         }
       else
         table->current_layer[i] = 0;
     }
-  if (current_layer > 0)
+  if (remainder > 0)
     return xasprintf ("out of range layer data index %"PRIu64, current_layer);
+
   return NULL;
 }
 
@@ -822,22 +839,16 @@ decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
                       in->header->version);
 
   char *error = NULL;
-  struct pivot_table *out = xzalloc (sizeof *out);
+  struct pivot_table *out = XZALLOC (struct pivot_table);
   out->ref_cnt = 1;
   hmap_init (&out->cells);
   out->look = pivot_table_look_new_builtin_default ();
+  out->settings = (struct fmt_settings) FMT_SETTINGS_INIT;
 
   const struct spvlb_y1 *y1 = (in->formats->x0 ? in->formats->x0->y1
                                : in->formats->x3 ? in->formats->x3->y1
                                : NULL);
-  const char *encoding;
-  if (y1)
-    encoding = y1->charset;
-  else
-    {
-      const char *dot = strchr (in->formats->locale, '.');
-      encoding = dot ? dot + 1 : "windows-1252";
-    }
+  const char *encoding = spvlb_table_get_encoding (in);
 
   /* Display settings. */
   out->look->show_numeric_markers = !in->ts->show_alphabetic_markers;
@@ -845,6 +856,7 @@ decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
   out->rotate_outer_row_labels = in->header->rotate_outer_row_labels;
   out->look->row_labels_in_corner = in->ts->show_row_labels_in_corner;
   out->show_grid_lines = in->borders->show_grid_lines;
+  out->show_title = true;
   out->show_caption = true;
   out->look->footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
   out->look->omit_empty = in->ts->omit_empty;
@@ -860,6 +872,7 @@ decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
         goto error;
 
       out->show_caption = x1->show_caption;
+      out->show_title = x1->show_title != 10;
     }
 
   /* Column and row display settings. */
@@ -902,17 +915,30 @@ decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
   out->look->shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
   out->look->top_continuation = in->ps->top_continuation;
   out->look->bottom_continuation = in->ps->bottom_continuation;
-  out->look->continuation = xstrdup (in->ps->continuation_string);
+  out->look->continuation = to_utf8 (in->ps->continuation_string, encoding);
   out->look->n_orphan_lines = in->ps->n_orphan_lines;
 
   /* Format settings. */
-  out->epoch = in->formats->y0->epoch;
-  out->decimal = in->formats->y0->decimal;
+  int epoch = in->formats->y0->epoch;
+  if (epoch >= 1000 && epoch <= 9999)
+    out->settings.epoch = epoch;
+  char decimal = in->formats->y0->decimal;
+  if (decimal == '.' || decimal == ',')
+    out->settings.decimal = decimal;
+  else
+    {
+      /* XXX warn about bad decimal point */
+    }
   out->grouping = in->formats->y0->grouping;
   const struct spvlb_custom_currency *cc = in->formats->custom_currency;
   for (int i = 0; i < 5; i++)
-    if (cc && i < cc->n_ccs)
-      out->ccs[i] = xstrdup (cc->ccs[i]);
+    {
+      if (cc && i < cc->n_ccs)
+        {
+          out->settings.ccs[i] = fmt_number_style_from_string (cc->ccs[i]);
+          /* XXX warn if parsing fails */
+        }
+    }
   out->small = in->formats->x3 ? in->formats->x3->small : 0;
 
   /* Command information. */
@@ -920,9 +946,9 @@ decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp)
     {
       out->command_local = to_utf8 (y1->command_local, encoding);
       out->command_c = to_utf8 (y1->command, encoding);
-      out->language = xstrdup (y1->language);
+      out->language = to_utf8 (y1->language, encoding);
       /* charset? */
-      out->locale = xstrdup (y1->locale);
+      out->locale = to_utf8 (y1->locale, encoding);
     }
 
   /* Source information. */
@@ -1055,3 +1081,171 @@ error:
   pivot_table_unref (out);
   return error;
 }
+\f
+/* collect_spvlb_strings */
+
+static void
+add_if_nonempty (struct string_array *strings, const char *s)
+{
+  if (s && s[0])
+    string_array_append (strings, s);
+}
+
+static void
+collect_value_mod_strings (struct string_array *strings,
+                           const struct spvlb_value_mod *vm)
+{
+  if (vm->template_string)
+    add_if_nonempty (strings, vm->template_string->id);
+
+  if (vm->style_pair && vm->style_pair->font_style)
+    add_if_nonempty (strings, vm->style_pair->font_style->typeface);
+}
+
+static void
+collect_value_strings (struct string_array *strings,
+                       const struct spvlb_value *value)
+{
+  if (!value)
+    return;
+
+  switch (value->type)
+    {
+    case 1:
+      collect_value_mod_strings (strings, value->type_01.value_mod);
+      break;
+
+    case 2:
+      collect_value_mod_strings (strings, value->type_02.value_mod);
+      add_if_nonempty (strings, value->type_02.var_name);
+      add_if_nonempty (strings, value->type_02.value_label);
+      break;
+
+    case 3:
+      collect_value_mod_strings (strings, value->type_03.value_mod);
+      add_if_nonempty (strings, value->type_03.local);
+      add_if_nonempty (strings, value->type_03.id);
+      add_if_nonempty (strings, value->type_03.c);
+      break;
+
+    case 4:
+      collect_value_mod_strings (strings, value->type_04.value_mod);
+      add_if_nonempty (strings, value->type_04.value_label);
+      add_if_nonempty (strings, value->type_04.var_name);
+      add_if_nonempty (strings, value->type_04.s);
+      break;
+
+    case 5:
+      collect_value_mod_strings (strings, value->type_05.value_mod);
+      add_if_nonempty (strings, value->type_05.var_name);
+      add_if_nonempty (strings, value->type_05.var_label);
+      break;
+
+    case 6:
+      collect_value_mod_strings (strings, value->type_06.value_mod);
+      add_if_nonempty (strings, value->type_06.local);
+      add_if_nonempty (strings, value->type_06.id);
+      add_if_nonempty (strings, value->type_06.c);
+      break;
+
+    case -1:
+      collect_value_mod_strings (strings, value->type_else.value_mod);
+      add_if_nonempty (strings, value->type_else.template);
+      for (size_t i = 0; i < value->type_else.n_args; i++)
+        {
+          const struct spvlb_argument *a = value->type_else.args[i];
+          collect_value_strings (strings, a->value);
+          for (size_t j = 0; j < a->n_values; j++)
+            collect_value_strings (strings, a->values[j]);
+        }
+      break;
+    }
+}
+
+static void
+collect_category_strings (struct string_array *strings,
+                          const struct spvlb_category *cat)
+{
+  collect_value_strings (strings, cat->name);
+  if (cat->group)
+    for (size_t i = 0; i < cat->group->n_subcategories; i++)
+      collect_category_strings (strings, cat->group->subcategories[i]);
+}
+
+/* Adds all of the characters strings in TABLE to STRINGS. */
+void
+collect_spvlb_strings (const struct spvlb_table *table,
+                       struct string_array *strings)
+{
+  add_if_nonempty (strings, table->ts->notes);
+  add_if_nonempty (strings, table->ts->table_look);
+  add_if_nonempty (strings, table->ps->continuation_string);
+
+  const struct spvlb_custom_currency *cc = table->formats->custom_currency;
+  if (cc)
+    for (int i = 0; i < cc->n_ccs; i++)
+      add_if_nonempty (strings, cc->ccs[i]);
+
+  const struct spvlb_y1 *y1 = (table->formats->x0 ? table->formats->x0->y1
+                               : table->formats->x3 ? table->formats->x3->y1
+                               : NULL);
+  if (y1)
+    {
+      add_if_nonempty (strings, y1->command_local);
+      add_if_nonempty (strings, y1->command);
+      add_if_nonempty (strings, y1->language);
+      add_if_nonempty (strings, y1->charset);
+      add_if_nonempty (strings, y1->locale);
+    }
+
+  const struct spvlb_x3 *x3 = table->formats->x3;
+  if (x3)
+    {
+      if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
+        add_if_nonempty (strings, x3->dataset);
+      add_if_nonempty (strings, x3->datafile);
+    }
+
+  for (size_t i = 0; i < table->footnotes->n_footnotes; i++)
+    {
+      const struct spvlb_footnote *f = table->footnotes->footnotes[i];
+      collect_value_strings (strings, f->text);
+      collect_value_strings (strings, f->marker);
+    }
+
+  collect_value_strings (strings, table->titles->user_title);
+  collect_value_strings (strings, table->titles->subtype);
+  collect_value_strings (strings, table->titles->corner_text);
+  collect_value_strings (strings, table->titles->caption);
+
+  for (size_t i = 0; i < PIVOT_N_AREAS; i++)
+    add_if_nonempty (strings, table->areas->areas[i]->typeface);
+
+  for (size_t i = 0; i < table->dimensions->n_dims; i++)
+    {
+      const struct spvlb_dimension *d = table->dimensions->dims[i];
+      collect_value_strings (strings, d->name);
+      for (size_t j = 0; j < d->n_categories; j++)
+        collect_category_strings (strings, d->categories[j]);
+    }
+
+  for (size_t i = 0; i < table->cells->n_cells; i++)
+    collect_value_strings (strings, table->cells->cells[i]->value);
+}
+\f
+/* Returns the encoding that TABLE declares to be in use for its strings.
+   (Watch out, it's not always correct.) */
+const char *
+spvlb_table_get_encoding (const struct spvlb_table *table)
+{
+  const struct spvlb_y1 *y1 = (table->formats->x0 ? table->formats->x0->y1
+                               : table->formats->x3 ? table->formats->x3->y1
+                               : NULL);
+  if (y1)
+    return y1->charset;
+  else
+    {
+      const char *dot = strchr (table->formats->locale, '.');
+      return dot ? dot + 1 : "windows-1252";
+    }
+}