Adopt use of gnulib for portability.
[pspp-builds.git] / src / vars-atr.c
index 9e7b541387f9d8ea1afd9d44fed2fef9de02cbbb..a854033a0831e5ea8dbc2bffab22835724a798da 100644 (file)
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
-#include <assert.h>
+#include "var.h"
+#include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
-#include "approx.h"
-#include "avl.h"
 #include "command.h"
+#include "dictionary.h"
 #include "do-ifP.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "file-handle.h"
-#include "inpt-pgm.h"
+#include "hash.h"
+#include "lexer.h"
 #include "misc.h"
 #include "str.h"
-#include "var.h"
-#include "vector.h"
+#include "value-labels.h"
 #include "vfm.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 #include "debug-print.h"
 
-#if DEBUGGING
-/* Dumps one variable to standard output. */
-void
-dump_one_var_node (void * pnode, void *param, int level)
+/* Assign auxiliary data AUX to variable V, which must not
+   already have auxiliary data.  Before V's auxiliary data is
+   cleared, AUX_DTOR(V) will be called. */
+void *
+var_attach_aux (struct variable *v,
+                void *aux, void (*aux_dtor) (struct variable *)) 
 {
-  struct variable *node = pnode;
-  int i;
-
-  for (i = 0; i < level - 1; i++)
-    printf ("   ");
-  if (node == NULL)
-    printf ("NULL_TREE\n");
-  else
-    printf ("%p=>%s\n", node, node->name ? node->name : "<null>");
+  assert (v->aux == NULL);
+  assert (aux != NULL);
+  v->aux = aux;
+  v->aux_dtor = aux_dtor;
+  return aux;
 }
 
-/* Dumps a tree of the variables to standard output. */
-void
-dump_var_tree (void)
+/* Remove auxiliary data, if any, from V, and returns it, without
+   calling any associated destructor. */
+void *
+var_detach_aux (struct variable *v) 
 {
-  printf (_("Vartree:\n"));
-/*
-  avl_walk_inorder (default_dict.var_by_name, dump_one_var_node, NULL);
-*/
+  void *aux = v->aux;
+  assert (aux != NULL);
+  v->aux = NULL;
+  return aux;
 }
-#endif
 
-/* Clear the default dictionary.  Note: This is probably not what you
-   want to do.  Use discard_variables() instead. */
+/* Clears auxiliary data, if any, from V, and calls any
+   associated destructor. */
 void
-clear_default_dict (void)
+var_clear_aux (struct variable *v) 
 {
-  int i;
-  
-  for (i = 0; i < default_dict.nvar; i++)
+  assert (v != NULL);
+  if (v->aux != NULL) 
     {
-      clear_variable (&default_dict, default_dict.var[i]);
-      free (default_dict.var[i]);
+      if (v->aux_dtor != NULL)
+        v->aux_dtor (v);
+      v->aux = NULL;
     }
-
-  assert (default_dict.splits == NULL);
-
-  default_dict.nvar = 0;
-  default_dict.N = 0;
-  default_dict.nval = 0;
-  default_handle = inline_file;
-  stop_weighting (&default_dict);
 }
 
-/* Discards all the current state in preparation for a data-input
-   command like DATA LIST or GET. */
+/* This function is appropriate for use an auxiliary data
+   destructor (passed as AUX_DTOR to var_attach_aux()) for the
+   case where the auxiliary data should be passed to free(). */
 void
-discard_variables (void)
-{
-  clear_default_dict ();
-  
-  n_lag = 0;
-  
-  if (vfm_source)
-    {
-      vfm_source->destroy_source ();
-      vfm_source = NULL;
-    }
-
-  cancel_transformations ();
-
-  ctl_stack = NULL;
-
-  free (vec);
-  vec = NULL;
-  nvec = 0;
-
-  expr_free (process_if_expr);
-  process_if_expr = NULL;
-
-  cancel_temporary ();
-
-  pgm_state = STATE_INIT;
-}
-
-/* Find and return the variable in default_dict having name NAME, or
-   NULL if no such variable exists in default_dict. */
-struct variable *
-find_variable (const char *name)
-{
-  return avl_find (default_dict.var_by_name, (struct variable *) name);
-}
-
-/* Find and return the variable in dictionary D having name NAME, or
-   NULL if no such variable exists in D. */
-struct variable *
-find_dict_variable (const struct dictionary *d, const char *name)
-{
-  return avl_find (d->var_by_name, (struct variable *) name);
-}
-
-/* Creates a variable named NAME in dictionary DICT having type TYPE
-   (ALPHA or NUMERIC) and, if type==ALPHA, width WIDTH.  Returns a
-   pointer to the newly created variable if successful.  On failure
-   (which indicates that a variable having the specified name already
-   exists), returns NULL.  */
-struct variable *
-create_variable (struct dictionary *dict, const char *name,
-                int type, int width)
-{
-  if (find_dict_variable (dict, name))
-    return NULL;
-  
-  {
-    struct variable *new_var;
-    
-    dict->var = xrealloc (dict->var, (dict->nvar + 1) * sizeof *dict->var);
-    new_var = dict->var[dict->nvar] = xmalloc (sizeof *new_var);
-    
-    new_var->index = dict->nvar;
-    dict->nvar++;
-    
-    init_variable (dict, new_var, name, type, width);
-    
-    return new_var;
-  }
-}
-
-#if GLOBAL_DEBUGGING
-/* For situations in which we know that there are no variables with an
-   identical name in the dictionary. */
-struct variable *
-force_create_variable (struct dictionary *dict, const char *name,
-                      int type, int width)
+var_dtor_free (struct variable *v) 
 {
-  struct variable *new_var = create_variable (dict, name, type, width);
-  assert (new_var != NULL);
-  return new_var;
+  free (v->aux);
 }
 
-/* For situations in which we know that there are no variables with an
-   identical name in the dictionary. */
-struct variable *
-force_dup_variable (struct dictionary *dict, const struct variable *src,
-                   const char *name)
-{
-  struct variable *new_var = dup_variable (dict, src, name);
-  assert (new_var != NULL);
-  return new_var;
-}
-#endif
-                                
-/* Delete variable V from DICT.  It should only be used when there are
-   guaranteed to be absolutely NO REFERENCES to it, for instance in
-   the very same function that created it. */
-void
-delete_variable (struct dictionary *dict, struct variable *v)
+/* Compares A and B, which both have the given WIDTH, and returns
+   a strcmp()-type result. */
+int
+compare_values (const union value *a, const union value *b, int width) 
 {
-  int i;
-
-  clear_variable (dict, v);
-  dict->nvar--;
-  for (i = v->index; i < dict->nvar; i++)
-    {
-      dict->var[i] = dict->var[i + 1];
-      dict->var[i]->index = i;
-    }
-  free (v);
+  if (width == 0) 
+    return a->f < b->f ? -1 : a->f > b->f;
+  else
+    return memcmp (a->s, b->s, min(MAX_SHORT_STRING, width));
 }
 
-/* Initialize fields in variable V inside dictionary D with name NAME,
-   type TYPE, and width WIDTH.  Initializes some other fields too. */
-static inline void
-common_init_stuff (struct dictionary *dict, struct variable *v,
-                  const char *name, int type, int width)
+/* Create a hash of v */
+unsigned 
+hash_value(const union value  *v, int width)
 {
-  if (v->name != name)
-    /* Avoid problems with overlap. */
-    strcpy (v->name, name);
+  unsigned id_hash;
 
-  avl_force_insert (dict->var_by_name, v);
-
-  v->type = type;
-  v->left = name[0] == '#';
-  v->width = type == NUMERIC ? 0 : width;
-  v->miss_type = MISSING_NONE;
-  if (v->type == NUMERIC)
-    {
-      v->print.type = FMT_F;
-      v->print.w = 8;
-      v->print.d = 2;
-    }
+  if ( 0 == width ) 
+    id_hash = hsh_hash_double (v->f);
   else
-    {
-      v->print.type = FMT_A;
-      v->print.w = v->width;
-      v->print.d = 0;
-    }
-  v->write = v->print;
-}
+    id_hash = hsh_hash_bytes (v->s, min(MAX_SHORT_STRING, width));
 
-/* Initialize (for the first time) a variable V in dictionary DICT
-   with name NAME, type TYPE, and width WIDTH.  */
-void
-init_variable (struct dictionary *dict, struct variable *v, const char *name,
-              int type, int width)
-{
-  common_init_stuff (dict, v, name, type, width);
-  v->nv = type == NUMERIC ? 1 : DIV_RND_UP (width, 8);
-  v->fv = dict->nval;
-  dict->nval += v->nv;
-  v->label = NULL;
-  v->val_lab = NULL;
-  v->get.fv = -1;
-
-  if (vfm_source == &input_program_source
-      || vfm_source == &file_type_source)
-    {
-      size_t nbytes = DIV_RND_UP (v->fv + 1, 4);
-      unsigned val = 0;
-
-      if (inp_init_size < nbytes)
-       {
-         inp_init = xrealloc (inp_init, nbytes);
-         memset (&inp_init[inp_init_size], 0, nbytes - inp_init_size);
-         inp_init_size = nbytes;
-       }
-
-      if (v->type == ALPHA)
-       val |= INP_STRING;
-      if (v->left)
-       val |= INP_LEFT;
-      inp_init[v->fv / 4] |= val << ((unsigned) (v->fv) % 4 * 2);
-    }
+  return id_hash;
 }
 
-/* Replace variable V in default_dict with a different variable having
-   name NAME, type TYPE, and width WIDTH. */
-void
-replace_variable (struct variable *v, const char *name, int type, int width)
-{
-  int nv;
-
-  assert (v && name && (type == NUMERIC || type == ALPHA) && width >= 0
-         && (type == ALPHA || width == 0));
-  clear_variable (&default_dict, v);
-  common_init_stuff (&default_dict, v, name, type, width);
 
-  nv = (type == NUMERIC) ? 1 : DIV_RND_UP (width, 8);
-  if (nv > v->nv)
-    {
-      v->fv = v->nv = 0;
-      v->fv = default_dict.nval;
-      default_dict.nval += nv;
-    }
-  v->nv = nv;
-}
 
-/* Changes the name of variable V in dictionary DICT to name NEW_NAME.
-   NEW_NAME must be known not to already exist in dictionary DICT. */
+/* Discards all the current state in preparation for a data-input
+   command like DATA LIST or GET. */
 void
-rename_variable (struct dictionary * dict, struct variable *v,
-                const char *new_name)
+discard_variables (void)
 {
-  assert (dict && dict->var_by_name && v && new_name);
-  avl_delete (dict->var_by_name, v);
-  strncpy (v->name, new_name, 9);
-  avl_force_insert (dict->var_by_name, v);
-}
+  dict_clear (default_dict);
+  default_handle = NULL;
 
-/* Delete the contents of variable V within dictionary DICT.  Does not
-   remove the variable from the vector of variables in the dictionary.
-   Use with caution. */
-void
-clear_variable (struct dictionary *dict, struct variable *v)
-{
-  assert (dict && v);
-  
-#if DEBUGGING
-  printf (_("clearing variable %d:%s %s\n"), v->index, v->name,
-         (dict == &default_dict ? _("in default dictionary")
-          : _("in auxiliary dictionary")));
-  if (dict->var_by_name != NULL)
-    dump_var_tree ();
-#endif
-  
-  if (dict->var_by_name != NULL)
-    avl_force_delete (dict->var_by_name, v);
-  
-  if (v->val_lab)
-    {
-      avl_destroy (v->val_lab, free_val_lab);
-      v->val_lab = NULL;
-    }
+  n_lag = 0;
   
-  if (v->label)
+  if (vfm_source != NULL)
     {
-      free (v->label);
-      v->label = NULL;
-    }
-
-  if (dict->splits)
-    {
-      struct variable **iter, **trailer;
-
-      for (trailer = iter = dict->splits; *iter; iter++)
-       if (*iter != v)
-         *trailer++ = *iter;
-       else
-         dict->n_splits--;
-
-      *trailer = NULL;
-      
-      if (dict->n_splits == 0)
-       {
-         free (dict->splits);
-         dict->splits = NULL;
-       }
+      free_case_source (vfm_source);
+      vfm_source = NULL;
     }
-         
-#if DEBUGGING
-  if (dict->var_by_name != NULL)
-    dump_var_tree ();
-#endif
-}
 
-/* Creates a new variable in dictionary DICT, whose properties are
-   copied from variable SRC, and returns a pointer to the new variable
-   of name NAME, if successful.  If unsuccessful (which only happens
-   if a variable of the same name NAME exists in DICT), returns
-   NULL. */
-struct variable *
-dup_variable (struct dictionary *dict, const struct variable *src,
-             const char *name)
-{
-  if (find_dict_variable (dict, name))
-    return NULL;
-  
-  {
-    struct variable *new_var;
-    
-    dict->var = xrealloc (dict->var, (dict->nvar + 1) * sizeof *dict->var);
-    new_var = dict->var[dict->nvar] = xmalloc (sizeof *new_var);
-
-    new_var->index = dict->nvar;
-    new_var->foo = -1;
-    new_var->get.fv = -1;
-    new_var->get.nv = -1;
-    dict->nvar++;
-    
-    copy_variable (new_var, src);
-
-    assert (new_var->nv >= 0);
-    new_var->fv = dict->nval;
-    dict->nval += new_var->nv;
-
-    strcpy (new_var->name, name);
-    avl_force_insert (dict->var_by_name, new_var);
-
-    return new_var;
-  }
-}
+  cancel_transformations ();
 
-   
-/* Decrements the reference count for value label V.  Destroys the
-   value label if the reference count reaches zero. */
-void
-free_value_label (struct value_label * v)
-{
-  assert (v->ref_count >= 1);
-  if (--v->ref_count == 0)
-    {
-      free (v->s);
-      free (v);
-    }
-}
+  ctl_stack = NULL;
 
-/* Frees value label P.  PARAM is ignored.  Used as a callback with
-   avl_destroy(). */
-void
-free_val_lab (void *p, void *param unused)
-{
-  free_value_label ((struct value_label *) p);
-}
+  expr_free (process_if_expr);
+  process_if_expr = NULL;
 
-/* Returns a value label corresponding to VAL in variable V padded to
-   length N.  If N==0 then no padding is performed, and NULL is
-   returned if no label exists.  (Normally a string of spaces is
-   returned in this case.) */
-char *
-get_val_lab (const struct variable *v, union value val, int n)
-{
-  static char *buf;
-  static int bufsize;
-  struct value_label template, *find;
+  cancel_temporary ();
 
-  if (bufsize < n)
-    {
-      buf = xrealloc (buf, n + 1);
-      bufsize = n;
-    }
-  if (n)
-    buf[0] = 0;
-  template.v = val;
-  find = NULL;
-  if (v->val_lab)
-    find = avl_find (v->val_lab, &template);
-  if (find)
-    {
-      if (n)
-       {
-         st_pad_copy (buf, find->s, n + 1);
-         return buf;
-       }
-      else
-       return find->s;
-    }
-  else
-    {
-      if (n)
-       {
-         memset (buf, ' ', n);
-         buf[n] = '\0';
-         return buf;
-       }
-      else
-       return NULL;
-    }
+  pgm_state = STATE_INIT;
 }
 
 /* Return nonzero only if X is a user-missing value for numeric
@@ -471,31 +152,25 @@ is_num_user_missing (double x, const struct variable *v)
     case MISSING_NONE:
       return 0;
     case MISSING_1:
-      return approx_eq (x, v->missing[0].f);
+      return x == v->missing[0].f;
     case MISSING_2:
-      return (approx_eq (x, v->missing[0].f)
-             || approx_eq (x, v->missing[1].f));
+      return x == v->missing[0].f || x == v->missing[1].f;
     case MISSING_3:
-      return (approx_eq (x, v->missing[0].f)
-             || approx_eq (x, v->missing[1].f)
-             || approx_eq (x, v->missing[2].f));
+      return (x == v->missing[0].f || x == v->missing[1].f
+              || x == v->missing[2].f);
     case MISSING_RANGE:
-      return (approx_ge (x, v->missing[0].f)
-             && approx_le (x, v->missing[1].f));
+      return x >= v->missing[0].f && x <= v->missing[1].f;
     case MISSING_LOW:
-      return approx_le (x, v->missing[0].f);
+      return x <= v->missing[0].f;
     case MISSING_HIGH:
-      return approx_ge (x, v->missing[0].f);
+      return x >= v->missing[0].f;
     case MISSING_RANGE_1:
-      return ((approx_ge (x, v->missing[0].f)
-              && approx_le (x, v->missing[1].f))
-             || approx_eq (x, v->missing[2].f));
+      return ((x >= v->missing[0].f && x <= v->missing[1].f)
+             || x == v->missing[2].f);
     case MISSING_LOW_1:
-      return (approx_le (x, v->missing[0].f)
-             || approx_eq (x, v->missing[1].f));
+      return x <= v->missing[0].f || x == v->missing[1].f;
     case MISSING_HIGH_1:
-      return (approx_ge (x, v->missing[0].f)
-             || approx_eq (x, v->missing[1].f));
+      return x >= v->missing[0].f || x == v->missing[1].f;
     default:
       assert (0);
     }
@@ -507,6 +182,7 @@ is_num_user_missing (double x, const struct variable *v)
 inline int
 is_str_user_missing (const unsigned char s[], const struct variable *v)
 {
+  /* FIXME: should these be memcmp()? */
   switch (v->miss_type)
     {
     case MISSING_NONE:
@@ -568,3 +244,160 @@ is_user_missing (const union value *val, const struct variable *v)
     }
   abort ();
 }
+\f
+/* Returns true if NAME is an acceptable name for a variable,
+   false otherwise.  If ISSUE_ERROR is true, issues an
+   explanatory error message on failure. */
+bool
+var_is_valid_name (const char *name, bool issue_error) 
+{
+  size_t length, i;
+  
+  assert (name != NULL);
+
+  length = strlen (name);
+  if (length < 1) 
+    {
+      if (issue_error)
+        msg (SE, _("Variable name cannot be empty string."));
+      return false;
+    }
+  else if (length > LONG_NAME_LEN) 
+    {
+      if (issue_error)
+        msg (SE, _("Variable name %s exceeds %d-character limit."),
+             name, (int) LONG_NAME_LEN);
+      return false;
+    }
+
+  for (i = 0; i < length; i++)
+    if (!CHAR_IS_IDN (name[i])) 
+      {
+        if (issue_error)
+          msg (SE, _("Character `%c' (in %s) may not appear in "
+                     "a variable name."),
+               name[i], name);
+        return false;
+      }
+        
+  if (!CHAR_IS_ID1 (name[0]))
+    {
+      if (issue_error)
+        msg (SE, _("Character `%c' (in %s), may not appear "
+                   "as the first character in a variable name."),
+             name[0], name);
+      return false;
+    }
+
+  if (lex_id_to_token (name, strlen (name)) != T_ID) 
+    {
+      if (issue_error)
+        msg (SE, _("%s may not be used as a variable name because it "
+                   "is a reserved word."), name);
+      return false;
+    }
+
+  return true;
+}
+
+/* A hsh_compare_func that orders variables A and B by their
+   names. */
+int
+compare_var_names (const void *a_, const void *b_, void *foo UNUSED) 
+{
+  const struct variable *a = a_;
+  const struct variable *b = b_;
+
+  return strcasecmp (a->name, b->name);
+}
+
+/* A hsh_hash_func that hashes variable V based on its name. */
+unsigned
+hash_var_name (const void *v_, void *foo UNUSED) 
+{
+  const struct variable *v = v_;
+
+  return hsh_hash_case_string (v->name);
+}
+
+/* A hsh_compare_func that orders pointers to variables A and B
+   by their names. */
+int
+compare_var_ptr_names (const void *a_, const void *b_, void *foo UNUSED) 
+{
+  struct variable *const *a = a_;
+  struct variable *const *b = b_;
+
+  return strcasecmp ((*a)->name, (*b)->name);
+}
+
+/* A hsh_hash_func that hashes pointer to variable V based on its
+   name. */
+unsigned
+hash_var_ptr_name (const void *v_, void *foo UNUSED) 
+{
+  struct variable *const *v = v_;
+
+  return hsh_hash_case_string ((*v)->name);
+}
+\f
+/* Sets V's short_name to SHORT_NAME, truncating it to
+   SHORT_NAME_LEN characters and converting it to uppercase in
+   the process. */
+void
+var_set_short_name (struct variable *v, const char *short_name) 
+{
+  assert (v != NULL);
+  assert (short_name[0] == '\0' || var_is_valid_name (short_name, false));
+  
+  str_copy_trunc (v->short_name, sizeof v->short_name, short_name);
+  str_uppercase (v->short_name);
+}
+
+/* Clears V's short name. */
+void
+var_clear_short_name (struct variable *v) 
+{
+  assert (v != NULL);
+
+  v->short_name[0] = '\0';
+}
+
+/* Sets V's short name to BASE, followed by a suffix of the form
+   _A, _B, _C, ..., _AA, _AB, etc. according to the value of
+   SUFFIX.  Truncates BASE as necessary to fit. */
+void
+var_set_short_name_suffix (struct variable *v, const char *base, int suffix)
+{
+  char string[SHORT_NAME_LEN + 1];
+  char *start, *end;
+  int len, ofs;
+
+  assert (v != NULL);
+  assert (suffix >= 0);
+  assert (strlen (v->short_name) > 0);
+
+  /* Set base name. */
+  var_set_short_name (v, base);
+
+  /* Compose suffix_string. */
+  start = end = string + sizeof string - 1;
+  *end = '\0';
+  do 
+    {
+      *--start = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[suffix % 26];
+      if (start <= string + 1)
+        msg (SE, _("Variable suffix too large."));
+      suffix /= 26;
+    }
+  while (suffix > 0);
+  *--start = '_';
+
+  /* Append suffix_string to V's short name. */
+  len = end - start;
+  if (len + strlen (v->short_name) > SHORT_NAME_LEN)
+    ofs = SHORT_NAME_LEN - len;
+  else
+    ofs = strlen (v->short_name);
+  strcpy (v->short_name + ofs, start);
+}