pivot-table: Don't rely on xcalloc (0, x) returning nonnull.
[pspp] / src / output / pivot-table.c
index 7eeee46ce621855e16baecbccf9bdb330e817493..c255ea83b0b6a4880f26c9abb854f66b9a859d96 100644 (file)
 #include "data/settings.h"
 #include "data/value.h"
 #include "data/variable.h"
+#include "data/file-name.h"
 #include "libpspp/hash-functions.h"
 #include "libpspp/i18n.h"
 #include "output/driver.h"
+#include "output/spv/spv-table-look.h"
 
 #include "gl/c-ctype.h"
+#include "gl/configmake.h"
 #include "gl/intprops.h"
 #include "gl/minmax.h"
+#include "gl/relocatable.h"
 #include "gl/xalloc.h"
 #include "gl/xmemdup0.h"
 #include "gl/xsize.h"
@@ -131,6 +135,75 @@ pivot_table_sizing_uninit (struct pivot_table_sizing *sizing)
 \f
 /* Pivot table looks. */
 
+static const struct pivot_table_look *
+default_look (const struct pivot_table_look *new)
+{
+  static struct pivot_table_look *look;
+  if (new)
+    {
+      pivot_table_look_unref (look);
+      look = pivot_table_look_ref (new);
+    }
+  else if (!look)
+    {
+      char *error = pivot_table_look_read ("default.stt", &look);
+      if (error)
+        {
+          free (error);
+          look = pivot_table_look_ref (pivot_table_look_builtin_default ());
+        }
+    }
+  return look;
+}
+
+const struct pivot_table_look *
+pivot_table_look_get_default (void)
+{
+  return default_look (NULL);
+}
+
+void
+pivot_table_look_set_default (const struct pivot_table_look *look)
+{
+  default_look (look);
+}
+
+char * WARN_UNUSED_RESULT
+pivot_table_look_read (const char *name, struct pivot_table_look **lookp)
+{
+  *lookp = NULL;
+
+  /* Construct search path. */
+  const char *path[4];
+  size_t n = 0;
+  path[n++] = ".";
+  const char *home = getenv ("HOME");
+  char *allocated = NULL;
+  if (home != NULL)
+    path[n++] = allocated = xasprintf ("%s/.pspp/looks", home);
+  char *allocated2;
+  path[n++] = relocate2 (PKGDATADIR "/looks", &allocated2);
+  path[n++] = NULL;
+
+  /* Search path. */
+  char *file = fn_search_path (name, (char **) path);
+  if (!file)
+    {
+      char *name2 = xasprintf ("%s.stt", name);
+      file = fn_search_path (name2, (char **) path);
+      free (name2);
+    }
+  free (allocated);
+  free (allocated2);
+  if (!file)
+    return xasprintf ("%s: not found", name);
+
+  /* Read file. */
+  char *error = spv_table_look_read (file, lookp);
+  free (file);
+  return error;
+}
+
 const struct pivot_table_look *
 pivot_table_look_builtin_default (void)
 {
@@ -297,7 +370,8 @@ pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
           if (axis->dimensions[i]->n_leaves == 0)
             return NULL;
 
-      return xcalloc (axis->n_dimensions, sizeof *indexes);
+      size_t size = axis->n_dimensions * sizeof *indexes;
+      return xzalloc (MAX (size, 1));
     }
 
   for (size_t i = 0; i < axis->n_dimensions; i++)
@@ -723,18 +797,10 @@ pivot_result_class_change (const char *s_, const struct fmt_spec *format)
    e.g. N_("Descriptive Statistics").  The un-translated text string is used as
    the pivot table's subtype.
 
-   Operations commonly performed on the new pivot_table:
-
-   - If empty rows or columns should not be displayed, set ->omit_empty to
-     true.
-
-   - Set the format to use for "count" values with pivot_table_set_weight_var()
-     or pivot_table_set_weight_format().
-
    This function is a shortcut for pivot_table_create__() for the most common
    case.  Use pivot_table_create__() directly if the title should be some kind
    of value other than an ordinary text string, or if the subtype should be
-different from the title.
+   different from the title.
 
    See the large comment at the top of pivot-table.h for general advice on
    creating pivot tables. */
@@ -745,18 +811,13 @@ pivot_table_create (const char *title)
 }
 
 /* Creates and returns a new pivot table with the given TITLE, and takes
-   ownership of TITLE.  The new pivot table's subtype is SUBTYPE, which
-   should be an untranslated English string that describes the contents of
-   the table at a high level without being specific about the variables or
-   other context involved.
+   ownership of TITLE.  The new pivot table's subtype is SUBTYPE, which should
+   be an untranslated English string that describes the contents of the table
+   at a high level without being specific about the variables or other context
+   involved.
 
-   Operations commonly performed on the new pivot_table:
-
-   - If empty rows or columns should not be displayed, set ->omit_empty to
-     true.
-
-   - Set the format to use for "count" values with pivot_table_set_weight_var()
-     or pivot_table_set_weight_format().
+   TITLE and SUBTYPE may be NULL, but in that case the client must add them
+   later because they are both mandatory for a pivot table.
 
    See the large comment at the top of pivot-table.h for general advice on
    creating pivot tables. */
@@ -765,12 +826,13 @@ pivot_table_create__ (struct pivot_value *title, const char *subtype)
 {
   struct pivot_table *table = xzalloc (sizeof *table);
   table->ref_cnt = 1;
+  table->show_title = true;
   table->show_caption = true;
   table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 };
   table->title = title;
   table->subtype = subtype ? pivot_value_new_text (subtype) : NULL;
   table->command_c = output_get_command_name ();
-  table->look = pivot_table_look_ref (pivot_table_look_builtin_default ());
+  table->look = pivot_table_look_ref (pivot_table_look_get_default ());
 
   hmap_init (&table->cells);
 
@@ -844,6 +906,7 @@ pivot_table_unref (struct pivot_table *table)
   pivot_value_destroy (table->subtype);
   pivot_value_destroy (table->corner_text);
   pivot_value_destroy (table->caption);
+  free (table->notes);
 
   for (size_t i = 0; i < table->n_dimensions; i++)
     pivot_dimension_destroy (table->dimensions[i]);
@@ -983,6 +1046,8 @@ pivot_table_put (struct pivot_table *table, const size_t *dindexes, size_t n,
                  struct pivot_value *value)
 {
   assert (n == table->n_dimensions);
+  for (size_t i = 0; i < n; i++)
+    assert (dindexes[i] < table->dimensions[i]->n_leaves);
 
   if (value->type == PIVOT_VALUE_NUMERIC && !value->numeric.format.w)
     {
@@ -1326,10 +1391,6 @@ pivot_table_assign_label_depth (struct pivot_table *table)
   pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
 }
 \f
-/* Footnotes. */
-
-\f
-\f
 static void
 indent (int indentation)
 {
@@ -1526,6 +1587,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation)
     settings_set_decimal_char (table->decimal);
 
   pivot_table_dump_value (table->title, "title", indentation);
+  pivot_table_dump_value (table->subtype, "subtype", indentation);
   pivot_table_dump_string (table->command_c, "command", indentation);
   pivot_table_dump_string (table->dataset, "dataset", indentation);
   pivot_table_dump_string (table->datafile, "datafile", indentation);
@@ -1604,8 +1666,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation)
           pivot_value_dump (d->root->name);
           fputs (" =", stdout);
 
-          struct pivot_value **names = xnmalloc (layer_axis->label_depth,
-                                                 sizeof *names);
+          struct pivot_value **names = xnmalloc (d->n_leaves, sizeof *names);
           size_t n_names = 0;
           for (const struct pivot_category *c
                  = d->presentation_leaves[layer_indexes[i]];
@@ -1844,7 +1905,10 @@ interpret_show (enum settings_value_show global_show,
 /* Appends a text representation of the body of VALUE to OUT.  SHOW_VALUES and
    SHOW_VARIABLES control whether variable and value labels are included.
 
-   The "body" omits subscripts and superscripts and footnotes. */
+   The "body" omits subscripts and superscripts and footnotes.
+
+   Returns true if OUT is a number (or a number plus a value label), false
+   otherwise.  */
 bool
 pivot_value_format_body (const struct pivot_value *value,
                          enum settings_value_show show_values,
@@ -1933,7 +1997,7 @@ pivot_value_format_body (const struct pivot_value *value,
 /* Appends a text representation of VALUE to OUT.  SHOW_VALUES and
    SHOW_VARIABLES control whether variable and value labels are included.
 
-   Subscripts and superscripts and footnotes are included. */
+   Subscripts and footnotes are included. */
 void
 pivot_value_format (const struct pivot_value *value,
                     enum settings_value_show show_values,
@@ -1948,9 +2012,6 @@ pivot_value_format (const struct pivot_value *value,
         ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
     }
 
-  if (value->superscript)
-    ds_put_format (out, "^%s", value->superscript);
-
   for (size_t i = 0; i < value->n_footnotes; i++)
     {
       ds_put_byte (out, '^');
@@ -1988,8 +2049,6 @@ pivot_value_destroy (struct pivot_value *value)
         free (value->subscripts[i]);
       free (value->subscripts);
 
-      free (value->superscript);
-
       switch (value->type)
         {
         case PIVOT_VALUE_NUMERIC: