From cea3f4f0364025ca9f9dc7c10cb38b8f534688d9 Mon Sep 17 00:00:00 2001
From: Ben Pfaff <blp@cs.stanford.edu>
Date: Mon, 24 Dec 2018 21:46:11 -0800
Subject: [PATCH] missing-values: New function mv_to_string().

This seems like something that should be handled by the missing value code,
not by SYSFILE INFO.
---
 src/data/missing-values.c               | 40 +++++++++++++++++++++++++
 src/data/missing-values.h               |  3 ++
 src/language/dictionary/sys-file-info.c | 37 +++--------------------
 3 files changed, 47 insertions(+), 33 deletions(-)

diff --git a/src/data/missing-values.c b/src/data/missing-values.c
index 9aba57e0e0..742ddd2acf 100644
--- a/src/data/missing-values.c
+++ b/src/data/missing-values.c
@@ -24,8 +24,11 @@
 #include "data/variable.h"
 #include "libpspp/assertion.h"
 #include "libpspp/cast.h"
+#include "libpspp/i18n.h"
 #include "libpspp/str.h"
 
+#include "gl/minmax.h"
+
 /* Types of user-missing values.
    Invisible--use access functions defined below instead. */
 enum mv_type
@@ -470,3 +473,40 @@ mv_is_str_missing (const struct missing_values *mv, const uint8_t s[],
   assert (mv->width > 0);
   return class & MV_USER && is_str_user_missing (mv, s);
 }
+
+char *
+mv_to_string (const struct missing_values *mv, const char *encoding)
+{
+  struct string s = DS_EMPTY_INITIALIZER;
+  if (mv_has_range (mv))
+    {
+      double x, y;
+      mv_get_range (mv, &x, &y);
+      if (x == LOWEST)
+        ds_put_format (&s, "LOWEST THRU %.*g", DBL_DIG + 1, y);
+      else if (y == HIGHEST)
+        ds_put_format (&s, "%.*g THRU HIGHEST", DBL_DIG + 1, x);
+      else
+        ds_put_format (&s, "%.*g THRU %.*g",
+                       DBL_DIG + 1, x,
+                       DBL_DIG + 1, y);
+    }
+  for (size_t j = 0; j < mv_n_values (mv); j++)
+    {
+      const union value *value = mv_get_value (mv, j);
+      if (!ds_is_empty (&s))
+        ds_put_cstr (&s, "; ");
+      if (!mv->width)
+        ds_put_format (&s, "%.*g", DBL_DIG + 1, value->f);
+      else
+        {
+          char *mvs = recode_string (
+            "UTF-8", encoding,
+            CHAR_CAST (char *, value_str (value, mv->width)),
+            MIN (mv->width, MV_MAX_STRING));
+          ds_put_format (&s, "\"%s\"", mvs);
+          free (mvs);
+        }
+    }
+  return ds_is_empty (&s) ? NULL : ds_steal_cstr (&s);
+}
diff --git a/src/data/missing-values.h b/src/data/missing-values.h
index 511ebd7ddd..c4d5695074 100644
--- a/src/data/missing-values.h
+++ b/src/data/missing-values.h
@@ -103,4 +103,7 @@ bool mv_replace_value (struct missing_values *, const union value *, int idx);
 bool mv_add_range (struct missing_values *, double low, double high);
 void mv_pop_range (struct missing_values *, double *low, double *high);
 
+/* Formatting. */
+char *mv_to_string (const struct missing_values *, const char *encoding);
+
 #endif /* data/missing-values.h */
diff --git a/src/language/dictionary/sys-file-info.c b/src/language/dictionary/sys-file-info.c
index 8f0a4c2b69..baea6274af 100644
--- a/src/language/dictionary/sys-file-info.c
+++ b/src/language/dictionary/sys-file-info.c
@@ -497,41 +497,12 @@ display_variables (const struct variable **vl, size_t n, int flags)
         {
           const struct missing_values *mv = var_get_missing_values (v);
 
-          struct string s = DS_EMPTY_INITIALIZER;
-          if (mv_has_range (mv))
+          char *s = mv_to_string (mv, var_get_encoding (v));
+          if (s)
             {
-              double x, y;
-              mv_get_range (mv, &x, &y);
-              if (x == LOWEST)
-                ds_put_format (&s, "LOWEST THRU %.*g", DBL_DIG + 1, y);
-              else if (y == HIGHEST)
-                ds_put_format (&s, "%.*g THRU HIGHEST", DBL_DIG + 1, x);
-              else
-                ds_put_format (&s, "%.*g THRU %.*g",
-                               DBL_DIG + 1, x,
-                               DBL_DIG + 1, y);
+              tab_text (t, x, y, TAB_LEFT, s);
+              free (s);
             }
-          for (size_t j = 0; j < mv_n_values (mv); j++)
-            {
-              const union value *value = mv_get_value (mv, j);
-              if (!ds_is_empty (&s))
-                ds_put_cstr (&s, "; ");
-              if (var_is_numeric (v))
-                ds_put_format (&s, "%.*g", DBL_DIG + 1, value->f);
-              else
-                {
-                  int width = var_get_width (v);
-                  int mv_width = MIN (width, MV_MAX_STRING);
-
-                  ds_put_byte (&s, '"');
-                  memcpy (ds_put_uninit (&s, mv_width),
-                          value_str (value, width), mv_width);
-                  ds_put_byte (&s, '"');
-                }
-            }
-          if (!ds_is_empty (&s))
-            tab_text (t, x, y, TAB_LEFT, ds_cstr (&s));
-          ds_destroy (&s);
           x++;
 
           assert (x == nc);
-- 
2.30.2