sys-file-reader: Refactor to clean up character encoding support.
[pspp-builds.git] / src / data / dictionary.c
index 03548c44eb628aef66341b1c3bd53da05ae9aca2..467f347efd9f5a5e290435265cbd1077032bfb0b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include "data/dictionary.h"
 
+#include <stdint.h>
 #include <stdlib.h>
 #include <ctype.h>
 
 #include "data/attributes.h"
 #include "data/case.h"
-#include "data/category.h"
 #include "data/identifier.h"
+#include "data/mrset.h"
 #include "data/settings.h"
 #include "data/value-labels.h"
 #include "data/vardict.h"
@@ -66,6 +67,8 @@ struct dictionary
     struct vector **vector;     /* Vectors of variables. */
     size_t vector_cnt;          /* Number of vectors. */
     struct attrset attributes;  /* Custom attributes. */
+    struct mrset **mrsets;      /* Multiple response sets. */
+    size_t n_mrsets;            /* Number of multiple response sets. */
 
     char *encoding;             /* Character encoding of string data */
 
@@ -77,6 +80,8 @@ struct dictionary
     void *changed_data;
   };
 
+static void dict_unset_split_var (struct dictionary *, struct variable *);
+static void dict_unset_mrset_var (struct dictionary *, struct variable *);
 
 void
 dict_set_encoding (struct dictionary *d, const char *enc)
@@ -222,6 +227,20 @@ dict_clone (const struct dictionary *s)
 
   dict_set_attributes (d, dict_get_attributes (s));
 
+  for (i = 0; i < s->n_mrsets; i++)
+    {
+      const struct mrset *old = s->mrsets[i];
+      struct mrset *new;
+      size_t j;
+
+      /* Clone old mrset, then replace vars from D by vars from S. */
+      new = mrset_clone (old);
+      for (j = 0; j < new->n_vars; j++)
+        new->vars[j] = dict_lookup_var_assert (d, var_get_name (new->vars[j]));
+
+      dict_add_mrset (d, new);
+    }
+
   return d;
 }
 
@@ -278,6 +297,8 @@ dict_destroy (struct dictionary *d)
       dict_clear (d);
       hmap_destroy (&d->name_map);
       attrset_destroy (&d->attributes);
+      dict_clear_mrsets (d);
+      free (d->encoding);
       free (d);
     }
 }
@@ -577,6 +598,7 @@ dict_delete_var (struct dictionary *d, struct variable *v)
   var_clear_aux (v);
 
   dict_unset_split_var (d, v);
+  dict_unset_mrset_var (d, v);
 
   if (d->weight == v)
     dict_set_weight (d, NULL);
@@ -820,10 +842,10 @@ var_name_is_insertable (const struct dictionary *dict, const char *name)
           && lex_id_to_token (ss_cstr (name)) == T_ID);
 }
 
-static bool
-make_hinted_name (const struct dictionary *dict, const char *hint,
-                  char name[VAR_NAME_LEN + 1])
+static char *
+make_hinted_name (const struct dictionary *dict, const char *hint)
 {
+  char name[VAR_NAME_LEN + 1];
   bool dropped = false;
   char *cp;
 
@@ -852,7 +874,7 @@ make_hinted_name (const struct dictionary *dict, const char *hint,
       unsigned long int i;
 
       if (var_name_is_insertable (dict, name))
-        return true;
+        return xstrdup (name);
 
       for (i = 0; i < ULONG_MAX; i++)
         {
@@ -867,16 +889,15 @@ make_hinted_name (const struct dictionary *dict, const char *hint,
           strcpy (&name[ofs], suffix);
 
           if (var_name_is_insertable (dict, name))
-            return true;
+            return xstrdup (name);
         }
     }
 
-  return false;
+  return NULL;
 }
 
-static bool
-make_numeric_name (const struct dictionary *dict, unsigned long int *num_start,
-                   char name[VAR_NAME_LEN + 1])
+static char *
+make_numeric_name (const struct dictionary *dict, unsigned long int *num_start)
 {
   unsigned long int number;
 
@@ -884,27 +905,24 @@ make_numeric_name (const struct dictionary *dict, unsigned long int *num_start,
        number < ULONG_MAX;
        number++)
     {
+      char name[3 + INT_STRLEN_BOUND (number) + 1];
+
       sprintf (name, "VAR%03lu", number);
       if (dict_lookup_var (dict, name) == NULL)
         {
           if (num_start != NULL)
             *num_start = number + 1;
-          return true;
+          return xstrdup (name);
         }
     }
 
-  if (num_start != NULL)
-    *num_start = ULONG_MAX;
-  return false;
+  NOT_REACHED ();
 }
 
 
-/* Attempts to devise a variable name unique within DICT.
-   Returns true if successful, in which case the new variable
-   name is stored into NAME.  Returns false if all names that can
-   be generated have already been taken.  (Returning false is
-   quite unlikely: at least ULONG_MAX unique names can be
-   generated.)
+/* Devises and returns a variable name unique within DICT.  The variable name
+   is owned by the caller, which must free it with free() when it is no longer
+   needed.
 
    HINT, if it is non-null, is used as a suggestion that will be
    modified for suitability as a variable name and for
@@ -915,14 +933,18 @@ make_numeric_name (const struct dictionary *dict, unsigned long int *num_start,
    value is used.  If NUM_START is non-null, then its value is
    used as the minimum numeric value to check, and it is updated
    to the next value to be checked.
-   */
-bool
+*/
+char *
 dict_make_unique_var_name (const struct dictionary *dict, const char *hint,
-                           unsigned long int *num_start,
-                           char name[VAR_NAME_LEN + 1])
+                           unsigned long int *num_start)
 {
-  return ((hint != NULL && make_hinted_name (dict, hint, name))
-          || make_numeric_name (dict, num_start, name));
+  if (hint != NULL)
+    {
+      char *hinted_name = make_hinted_name (dict, hint);
+      if (hinted_name != NULL)
+        return hinted_name;
+    }
+  return make_numeric_name (dict, num_start);
 }
 
 /* Returns the weighting variable in dictionary D, or a null
@@ -1153,7 +1175,7 @@ dict_get_split_cnt (const struct dictionary *d)
 
 /* Removes variable V, which must be in D, from D's set of split
    variables. */
-void
+static void
 dict_unset_split_var (struct dictionary *d, struct variable *v)
 {
   int orig_count;
@@ -1206,12 +1228,14 @@ dict_get_label (const struct dictionary *d)
 }
 
 /* Sets D's file label to LABEL, truncating it to a maximum of 60
-   characters. */
+   characters.
+
+   Removes D's label if LABEL is null or the empty string. */
 void
 dict_set_label (struct dictionary *d, const char *label)
 {
   free (d->label);
-  d->label = label != NULL ? xstrndup (label, 60) : NULL;
+  d->label = label != NULL && label[0] != '\0' ? xstrndup (label, 60) : NULL;
 }
 
 /* Returns the documents for D, or a null pointer if D has no
@@ -1240,7 +1264,7 @@ dict_set_documents (struct dictionary *d, const char *documents)
      final line with spaces. */
   remainder = ds_length (&d->documents) % DOC_LINE_LENGTH;
   if (remainder != 0)
-    ds_put_char_multiple (&d->documents, ' ', DOC_LINE_LENGTH - remainder);
+    ds_put_byte_multiple (&d->documents, ' ', DOC_LINE_LENGTH - remainder);
 }
 
 /* Drops the documents from dictionary D. */
@@ -1362,7 +1386,138 @@ dict_clear_vectors (struct dictionary *d)
   d->vector = NULL;
   d->vector_cnt = 0;
 }
+\f
+/* Multiple response sets. */
+
+/* Returns the multiple response set in DICT with index IDX, which must be
+   between 0 and the count returned by dict_get_n_mrsets(), exclusive. */
+const struct mrset *
+dict_get_mrset (const struct dictionary *dict, size_t idx)
+{
+  assert (idx < dict->n_mrsets);
+  return dict->mrsets[idx];
+}
+
+/* Returns the number of multiple response sets in DICT. */
+size_t
+dict_get_n_mrsets (const struct dictionary *dict)
+{
+  return dict->n_mrsets;
+}
+
+/* Looks for a multiple response set named NAME in DICT.  If it finds one,
+   returns its index; otherwise, returns SIZE_MAX. */
+static size_t
+dict_lookup_mrset_idx (const struct dictionary *dict, const char *name)
+{
+  size_t i;
+
+  for (i = 0; i < dict->n_mrsets; i++)
+    if (!strcasecmp (name, dict->mrsets[i]->name))
+      return i;
+
+  return SIZE_MAX;
+}
+
+/* Looks for a multiple response set named NAME in DICT.  If it finds one,
+   returns it; otherwise, returns NULL. */
+const struct mrset *
+dict_lookup_mrset (const struct dictionary *dict, const char *name)
+{
+  size_t idx = dict_lookup_mrset_idx (dict, name);
+  return idx != SIZE_MAX ? dict->mrsets[idx] : NULL;
+}
+
+/* Adds MRSET to DICT, replacing any existing set with the same name.  Returns
+   true if a set was replaced, false if none existed with the specified name.
+
+   Ownership of MRSET is transferred to DICT. */
+bool
+dict_add_mrset (struct dictionary *dict, struct mrset *mrset)
+{
+  size_t idx;
+
+  assert (mrset_ok (mrset, dict));
+
+  idx = dict_lookup_mrset_idx (dict, mrset->name);
+  if (idx == SIZE_MAX)
+    {
+      dict->mrsets = xrealloc (dict->mrsets,
+                               (dict->n_mrsets + 1) * sizeof *dict->mrsets);
+      dict->mrsets[dict->n_mrsets++] = mrset;
+      return true;
+    }
+  else
+    {
+      mrset_destroy (dict->mrsets[idx]);
+      dict->mrsets[idx] = mrset;
+      return false;
+    }
+}
+
+/* Looks for a multiple response set in DICT named NAME.  If found, removes it
+   from DICT and returns true.  If none is found, returns false without
+   modifying DICT.
+
+   Deleting one multiple response set causes the indexes of other sets within
+   DICT to change. */
+bool
+dict_delete_mrset (struct dictionary *dict, const char *name)
+{
+  size_t idx = dict_lookup_mrset_idx (dict, name);
+  if (idx != SIZE_MAX)
+    {
+      mrset_destroy (dict->mrsets[idx]);
+      dict->mrsets[idx] = dict->mrsets[--dict->n_mrsets];
+      return true;
+    }
+  else
+    return false;
+}
 
+/* Deletes all multiple response sets from DICT. */
+void
+dict_clear_mrsets (struct dictionary *dict)
+{
+  size_t i;
+
+  for (i = 0; i < dict->n_mrsets; i++)
+    mrset_destroy (dict->mrsets[i]);
+  free (dict->mrsets);
+  dict->mrsets = NULL;
+  dict->n_mrsets = 0;
+}
+
+/* Removes VAR, which must be in DICT, from DICT's multiple response sets. */
+static void
+dict_unset_mrset_var (struct dictionary *dict, struct variable *var)
+{
+  size_t i;
+
+  assert (dict_contains_var (dict, var));
+
+  for (i = 0; i < dict->n_mrsets; )
+    {
+      struct mrset *mrset = dict->mrsets[i];
+      size_t j;
+
+      for (j = 0; j < mrset->n_vars; )
+        if (mrset->vars[j] == var)
+          remove_element (mrset->vars, mrset->n_vars--,
+                          sizeof *mrset->vars, j);
+        else
+          j++;
+
+      if (mrset->n_vars < 2)
+        {
+          mrset_destroy (mrset);
+          dict->mrsets[i] = dict->mrsets[--dict->n_mrsets];
+        }
+      else
+        i++;
+    }
+}
+\f
 /* Returns D's attribute set.  The caller may examine or modify
    the attribute set, but must not destroy it.  Destroying D or
    calling dict_set_attributes for D will also destroy D's