spvbin-helpers: Properly handle parsing strings with no destination.
[pspp] / src / output / pivot-table.c
index 671544ef06906a513e8c5099827fccc2b1b7f3a4..55559773c87217cbdec3dbb2060e732f10175e4b 100644 (file)
@@ -18,6 +18,8 @@
 
 #include "output/pivot-table.h"
 
+#include <libxml/parser.h>
+#include <libxml/tree.h>
 #include <math.h>
 #include <stdlib.h>
 
@@ -870,7 +872,7 @@ pivot_table_create__ (struct pivot_value *title, const char *subtype)
   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->command_c = xstrdup_if_nonempty (output_get_command_name ());
   table->look = pivot_table_look_ref (pivot_table_look_get_default ());
   table->settings = fmt_settings_copy (settings_get_fmt_settings ());
   table->small = settings_get_small ();
@@ -911,12 +913,6 @@ pivot_table_ref (const struct pivot_table *table_)
   return table;
 }
 
-static char *
-xstrdup_if_nonnull (const char *s)
-{
-  return s ? xstrdup (s) : NULL;
-}
-
 static struct pivot_table_sizing
 clone_sizing (const struct pivot_table_sizing *s)
 {
@@ -1505,15 +1501,30 @@ pivot_table_create_footnote (struct pivot_table *table,
                                         NULL, content);
 }
 
-static struct pivot_value *
-pivot_make_default_footnote_marker (int idx, bool show_numeric_markers)
-{
-  char text[INT_BUFSIZE_BOUND (size_t)];
-  if (show_numeric_markers)
-    snprintf (text, sizeof text, "%d", idx + 1);
+void
+pivot_footnote_format_marker (const struct pivot_footnote *f,
+                              const struct pivot_table *pt,
+                              struct string *s)
+{
+  if (f->marker)
+    pivot_value_format_body (f->marker, pt, s);
+  else if (pt->look->show_numeric_markers)
+    ds_put_format (s, "%zu", f->idx + 1);
   else
-    str_format_26adic (idx + 1, false, text, sizeof text);
-  return pivot_value_new_user_text (text, -1);
+    {
+      char text[INT_BUFSIZE_BOUND (size_t)];
+      str_format_26adic (f->idx + 1, false, text, sizeof text);
+      ds_put_cstr (s, text);
+    }
+}
+
+char *
+pivot_footnote_marker_string (const struct pivot_footnote *f,
+                              const struct pivot_table *pt)
+{
+  struct string s = DS_EMPTY_INITIALIZER;
+  pivot_footnote_format_marker (f, pt, &s);
+  return ds_steal_cstr (&s);
 }
 
 /* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
@@ -1533,12 +1544,10 @@ pivot_table_create_footnote__ (struct pivot_table *table, size_t idx,
       while (idx >= table->n_footnotes)
         {
           struct pivot_footnote *f = xmalloc (sizeof *f);
-          f->idx = table->n_footnotes;
-          f->marker = pivot_make_default_footnote_marker (
-            f->idx, table->look->show_numeric_markers);
-          f->content = NULL;
-          f->show = true;
-
+          *f = (struct pivot_footnote) {
+            .idx = table->n_footnotes,
+            .show = true,
+          };
           table->footnotes[table->n_footnotes++] = f;
         }
     }
@@ -2288,6 +2297,33 @@ interpret_show (enum settings_value_show global_show,
           : global_show);
 }
 
+/* Appends to OUT the actual text content from the given Pango MARKUP. */
+static void
+get_text_from_markup (const char *markup, struct string *out)
+{
+  xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
+  if (!parser)
+    {
+      ds_put_cstr (out, markup);
+      return;
+    }
+
+  xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
+  xmlParseChunk (parser, markup, strlen (markup), false);
+  xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
+
+  if (parser->wellFormed)
+    {
+      xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
+      ds_put_cstr (out, CHAR_CAST (char *, s));
+      xmlFree (s);
+    }
+  else
+    ds_put_cstr (out, markup);
+  xmlFreeDoc (parser->myDoc);
+  xmlFreeParserCtxt (parser);
+}
+
 /* Appends a text representation of the body of VALUE to OUT.  Settings on
    PT control whether variable and value labels are included.
 
@@ -2375,7 +2411,10 @@ pivot_value_format_body (const struct pivot_value *value,
       break;
 
     case PIVOT_VALUE_TEXT:
-      ds_put_cstr (out, value->text.local);
+      if (value->font_style && value->font_style->markup)
+        get_text_from_markup (value->text.local, out);
+      else
+        ds_put_cstr (out, value->text.local);
       break;
 
     case PIVOT_VALUE_TEMPLATE:
@@ -2390,13 +2429,16 @@ pivot_value_format_body (const struct pivot_value *value,
 /* Appends a text representation of VALUE to OUT.  Settings on
    PT control whether variable and value labels are included.
 
-   Subscripts and footnotes are included. */
-void
+   Subscripts and footnotes are included.
+
+   Returns true if OUT is a number (or a number plus a value label), false
+   otherwise.  */
+bool
 pivot_value_format (const struct pivot_value *value,
                     const struct pivot_table *pt,
                     struct string *out)
 {
-  pivot_value_format_body (value, pt, out);
+  bool numeric = pivot_value_format_body (value, pt, out);
 
   if (value->n_subscripts)
     {
@@ -2410,10 +2452,12 @@ pivot_value_format (const struct pivot_value *value,
 
       size_t idx = value->footnote_indexes[i];
       const struct pivot_footnote *f = pt->footnotes[idx];
-      pivot_value_format (f->marker, pt, out);
+      pivot_footnote_format_marker (f, pt, out);
 
       ds_put_byte (out, ']');
     }
+
+  return numeric;
 }
 
 /* Returns a text representation of VALUE.  The caller must free the string,
@@ -2433,6 +2477,7 @@ pivot_value_to_string_defaults (const struct pivot_value *value)
   static const struct pivot_table pt = {
     .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
     .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
+    .settings = FMT_SETTINGS_INIT,
   };
   return pivot_value_to_string (value, &pt);
 }
@@ -2841,6 +2886,29 @@ pivot_value_add_footnote (struct pivot_value *v,
   v->footnote_indexes = xrealloc (
     v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
   v->footnote_indexes[v->n_footnotes++] = footnote->idx;
+  pivot_value_sort_footnotes (v);
+}
+
+static int
+compare_footnote_indexes (const void *a_, const void *b_)
+{
+  const size_t *ap = a_;
+  const size_t *bp = b_;
+  size_t a = *ap;
+  size_t b = *bp;
+  return a < b ? -1 : a > b;
+}
+
+/* Sorts the footnote references in V in the standard ascending order.
+
+   This is only necessary if code adds (plural) footnotes to a pivot_value by
+   itself, because pivot_value_add_footnote() does it automatically. */
+void
+pivot_value_sort_footnotes (struct pivot_value *v)
+{
+  if (v->n_footnotes > 1)
+    qsort (v->footnote_indexes, v->n_footnotes, sizeof *v->footnote_indexes,
+           compare_footnote_indexes);
 }
 
 /* If VALUE is a numeric value, and RC is a result class such as