Rewrite and improve formatted output routines.
[pspp-builds.git] / src / data / value-labels.c
index 4638bd3fe6038387da8650acce2eff754e5e36c7..96d3dd754a3fab4369f3734735f329075183022b 100644 (file)
    02110-1301, USA. */
 
 #include <config.h>
+
 #include "value-labels.h"
-#include "message.h"
+
 #include <stdlib.h>
-#include "alloc.h"
-#include "hash.h"
-#include "str.h"
+
+#include <data/data-out.h>
+#include <data/variable.h>
+#include <libpspp/alloc.h>
+#include <libpspp/compiler.h>
+#include <libpspp/hash.h>
+#include <libpspp/message.h>
+#include <libpspp/str.h>
 
 static hsh_compare_func compare_int_val_lab;
 static hsh_hash_func hash_int_val_lab;
@@ -42,8 +48,8 @@ struct val_labs
   };
 
 /* Creates and returns a new, empty set of value labels with the
-   given WIDTH, which must designate a numeric (0) or short
-   string (1...MAX_SHORT_STRING inclusive) width. */
+   given WIDTH.  To actually add any value labels, WIDTH must be
+   a numeric or short string width. */
 struct val_labs *
 val_labs_create (int width) 
 {
@@ -75,16 +81,53 @@ val_labs_copy (const struct val_labs *vls)
   return copy;
 }
 
+/* Determines whether VLS's width can be changed to NEW_WIDTH.
+   Numeric widths cannot be changed at all.
+   Strings can be widened.  They can be shortened only if the
+   characters that will be truncated are spaces. */
+bool
+val_labs_can_set_width (const struct val_labs *vls, int new_width) 
+{
+  assert ((vls->width == 0) == (new_width == 0));
+
+  if (vls->width == 0)
+    return new_width == 0;
+  else if (new_width < vls->width)
+    {
+      struct val_labs_iterator *i;
+      struct val_lab *lab;
+
+      for (lab = val_labs_first (vls, &i); lab != NULL;
+           lab = val_labs_next (vls, &i))
+        {
+          int j;
+
+          /* We can shorten the value labels only if all the
+             truncated characters are blanks. */
+          for (j = vls->width; j < new_width; j++)
+            if (lab->value.s[j] != ' ') 
+              {
+                val_labs_done (&i);
+                return false;
+              }
+        }
+      return true;
+    }
+  else
+    return true;
+}
+
 /* Changes the width of VLS to NEW_WIDTH.  If VLS is numeric,
    NEW_WIDTH must be 0, otherwise it must be within the range
    1...MAX_SHORT_STRING inclusive. */
 void
 val_labs_set_width (struct val_labs *vls, int new_width) 
 {
-  assert (vls != NULL);
-  assert ((vls->width == 0) == (new_width == 0));
+  assert (val_labs_can_set_width (vls, new_width));
 
   vls->width = new_width;
+  if (new_width > MAX_SHORT_STRING)
+    val_labs_clear (vls);
 }
 
 /* Destroys VLS. */
@@ -93,8 +136,7 @@ val_labs_destroy (struct val_labs *vls)
 {
   if (vls != NULL) 
     {
-      if (vls->labels != NULL)
-        hsh_destroy (vls->labels);
+      hsh_destroy (vls->labels);
       free (vls);
     }
 }
@@ -148,10 +190,10 @@ create_int_val_lab (struct val_labs *vls, union value value, const char *label)
 }
 
 /* If VLS does not already contain a value label for VALUE, adds
-   LABEL for it and returns nonzero.  Otherwise, returns zero.
+   LABEL for it and returns true.  Otherwise, returns false.
    Behavior is undefined if VLS's width is greater than
    MAX_SHORT_STRING. */
-int
+bool
 val_labs_add (struct val_labs *vls, union value value, const char *label) 
 {
   struct int_val_lab *ivl;
@@ -170,20 +212,17 @@ val_labs_add (struct val_labs *vls, union value value, const char *label)
   if (*vlpp == NULL) 
     {
       *vlpp = ivl;
-      return 1; 
-    }
-  else 
-    {
-      free_int_val_lab (ivl, vls);
-      return 0;
+      return true; 
     }
+  free_int_val_lab (ivl, vls);
+  return false;
 }
 
-/* Sets LABEL as the value label for VALUE in VLS.  Returns zero
-   if there wasn't already a value label for VALUE, or nonzero if
+/* Sets LABEL as the value label for VALUE in VLS.  Returns false
+   if there wasn't already a value label for VALUE, or true if
    there was.  Behavior is undefined if VLS's width is greater
    than MAX_SHORT_STRING. */
-int
+bool
 val_labs_replace (struct val_labs *vls, union value value, const char *label) 
 {
   struct int_val_lab *ivl;
@@ -195,23 +234,23 @@ val_labs_replace (struct val_labs *vls, union value value, const char *label)
   if (vls->labels == NULL)
     {
       val_labs_add (vls, value, label);
-      return 0;
+      return false;
     }
 
   ivl = hsh_replace (vls->labels, create_int_val_lab (vls, value, label));
   if (ivl == NULL) 
-    return 0;
+    return false;
   else 
     {
       free_int_val_lab (ivl, vls);
-      return 1;
+      return true;
     }
 }
 
-/* Removes any value label for VALUE within VLS.  Returns nonzero
+/* Removes any value label for VALUE within VLS.  Returns true
    if a value label was removed. Behavior is undefined if VLS's
    width is greater than MAX_SHORT_STRING. */
-int 
+bool
 val_labs_remove (struct val_labs *vls, union value value) 
 {
   assert (vls != NULL);
@@ -225,7 +264,7 @@ val_labs_remove (struct val_labs *vls, union value value)
       return deleted;
     }
   else
-    return 0;
+    return false;
 }
 
 /* Searches VLS for a value label for VALUE.  If successful,
@@ -357,7 +396,7 @@ val_labs_done (struct val_labs_iterator **ip)
 \f
 /* Compares two value labels and returns a strcmp()-type result. */
 int
-compare_int_val_lab (const void *a_, const void *b_, void *vls_)
+compare_int_val_lab (const void *a_, const void *b_, const void *vls_)
 {
   const struct int_val_lab *a = a_;
   const struct int_val_lab *b = b_;
@@ -371,7 +410,7 @@ compare_int_val_lab (const void *a_, const void *b_, void *vls_)
 
 /* Hash a value label. */
 unsigned
-hash_int_val_lab (const void *vl_, void *vls_)
+hash_int_val_lab (const void *vl_, const void *vls_)
 {
   const struct int_val_lab *vl = vl_;
   const struct val_labs *vls = vls_;
@@ -384,7 +423,7 @@ hash_int_val_lab (const void *vl_, void *vls_)
 
 /* Free a value label. */
 void
-free_int_val_lab (void *vl_, void *vls_ UNUSED) 
+free_int_val_lab (void *vl_, const void *vls_ UNUSED) 
 {
   struct int_val_lab *vl = vl_;
 
@@ -462,7 +501,7 @@ atom_to_string (const struct atom *atom)
 
 /* A hsh_compare_func that compares A and B. */
 static int
-compare_atoms (const void *a_, const void *b_, void *aux UNUSED) 
+compare_atoms (const void *a_, const void *b_, const void *aux UNUSED) 
 {
   const struct atom *a = a_;
   const struct atom *b = b_;
@@ -472,7 +511,7 @@ compare_atoms (const void *a_, const void *b_, void *aux UNUSED)
 
 /* A hsh_hash_func that hashes ATOM. */
 static unsigned
-hash_atom (const void *atom_, void *aux UNUSED) 
+hash_atom (const void *atom_, const void *aux UNUSED) 
 {
   const struct atom *atom = atom_;
 
@@ -481,7 +520,7 @@ hash_atom (const void *atom_, void *aux UNUSED)
 
 /* A hsh_free_func that destroys ATOM. */
 static void
-free_atom (void *atom_, void *aux UNUSED) 
+free_atom (void *atom_, const void *aux UNUSED) 
 {
   struct atom *atom = atom_;
 
@@ -506,11 +545,9 @@ value_to_string (const union value *val, const struct variable *var)
   s = val_labs_find (var->val_labs, *val);
   if (s == NULL) 
     {
-      static char buf[256];
-      if (var->width != 0) 
-        str_copy_buf_trunc (buf, sizeof buf, val->s, var->width);
-      else
-        snprintf(buf, 100, "%g", val->f);
+      static char buf[MAX_STRING + 1];
+      data_out (val, &var->print, buf);
+      buf[var->print.w] = '\0';
       s = buf;
     }