value-labels: Interpret \n as new-line in value labels.
[pspp-builds.git] / src / data / value-labels.c
index 6d8f517510c59f62f7b29b852f4dd1107e01da45..6d7f19c17d7ee191862b29c454c7ef62540a8ad5 100644 (file)
@@ -58,7 +58,7 @@ val_labs_clone (const struct val_labs *vls)
 
   copy = val_labs_create (vls->width);
   HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
-    val_labs_add (copy, &label->value, label->label);
+    val_labs_add (copy, &label->value, label->escaped_label);
   return copy;
 }
 
@@ -114,6 +114,7 @@ val_labs_clear (struct val_labs *vls)
       hmap_delete (&vls->labels, &label->node);
       value_destroy (&label->value, vls->width);
       intern_unref (label->label);
+      intern_unref (label->escaped_label);
       free (label);
     }
 }
@@ -133,18 +134,48 @@ val_labs_count (const struct val_labs *vls)
   return vls == NULL ? 0 : hmap_count (&vls->labels);
 }
 \f
+static void
+set_label (struct val_lab *lab, const char *escaped_label)
+{
+  lab->escaped_label = intern_new (escaped_label);
+  if (strstr (escaped_label, "\\n") == NULL)
+    lab->label = intern_ref (lab->escaped_label);
+  else
+    {
+      struct string s;
+      const char *p;
+
+      ds_init_empty (&s);
+      ds_extend (&s, intern_strlen (lab->escaped_label));
+      for (p = escaped_label; *p != '\0'; p++)
+        {
+          char c = *p;
+          if (c == '\\' && p[1] == 'n')
+            {
+              c = '\n';
+              p++;
+            }
+          ds_put_byte (&s, c);
+        }
+      lab->label = intern_new (ds_cstr (&s));
+      ds_destroy (&s);
+    }
+}
+
 static void
 do_add_val_lab (struct val_labs *vls, const union value *value,
-                const char *label)
+                const char *escaped_label)
 {
   struct val_lab *lab = xmalloc (sizeof *lab);
   value_clone (&lab->value, value, vls->width);
-  lab->label = intern_new (label);
+  set_label (lab, escaped_label);
   hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
 }
 
-/* If VLS does not already contain a value label for VALUE, adds
-   LABEL for it and returns true.  Otherwise, returns false. */
+/* If VLS does not already contain a value label for VALUE, adds the UTF-8
+   encoded LABEL for it and returns true.  Otherwise, returns false.
+
+   In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
 bool
 val_labs_add (struct val_labs *vls, const union value *value,
               const char *label)
@@ -160,7 +191,9 @@ val_labs_add (struct val_labs *vls, const union value *value,
 }
 
 /* Sets LABEL as the value label for VALUE in VLS, replacing any
-   existing label for VALUE. */
+   existing label for VALUE.
+
+   In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
 void
 val_labs_replace (struct val_labs *vls, const union value *value,
                   const char *label)
@@ -169,7 +202,8 @@ val_labs_replace (struct val_labs *vls, const union value *value,
   if (vl != NULL)
     {
       intern_unref (vl->label);
-      vl->label = intern_new (label);
+      intern_unref (vl->escaped_label);
+      set_label (vl, label);
     }
   else
     do_add_val_lab (vls, value, label);
@@ -182,12 +216,14 @@ val_labs_remove (struct val_labs *vls, struct val_lab *label)
   hmap_delete (&vls->labels, &label->node);
   value_destroy (&label->value, vls->width);
   intern_unref (label->label);
+  intern_unref (label->escaped_label);
   free (label);
 }
 
-/* Searches VLS for a value label for VALUE.  If successful,
-   returns the string used as the label; otherwise, returns a
-   null pointer.  Returns a null pointer if VLS is null. */
+/* Searches VLS for a value label for VALUE.  If successful, returns the string
+   used as the label, as a UTF-8 encoded string in a format suitable for
+   output.  Otherwise, returns a null pointer.  Returns a null pointer if VLS
+   is null. */
 const char *
 val_labs_find (const struct val_labs *vls, const union value *value)
 {