Adopt use of gnulib for portability.
[pspp-builds.git] / src / vars-atr.c
index 9f2baf0ea1e6f4593decbfb9dd2da769de2b5873..a854033a0831e5ea8dbc2bffab22835724a798da 100644 (file)
@@ -14,8 +14,8 @@
 
    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 "var.h"
 #include "expressions/public.h"
 #include "file-handle.h"
 #include "hash.h"
+#include "lexer.h"
 #include "misc.h"
 #include "str.h"
 #include "value-labels.h"
 #include "vfm.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 #include "debug-print.h"
 
+/* 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 *)) 
@@ -46,6 +53,8 @@ var_attach_aux (struct variable *v,
   return aux;
 }
 
+/* Remove auxiliary data, if any, from V, and returns it, without
+   calling any associated destructor. */
 void *
 var_detach_aux (struct variable *v) 
 {
@@ -55,6 +64,8 @@ var_detach_aux (struct variable *v)
   return aux;
 }
 
+/* Clears auxiliary data, if any, from V, and calls any
+   associated destructor. */
 void
 var_clear_aux (struct variable *v) 
 {
@@ -67,6 +78,9 @@ var_clear_aux (struct variable *v)
     }
 }
 
+/* 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
 var_dtor_free (struct variable *v) 
 {
@@ -168,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:
@@ -230,6 +245,61 @@ 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
@@ -238,7 +308,7 @@ compare_var_names (const void *a_, const void *b_, void *foo UNUSED)
   const struct variable *a = a_;
   const struct variable *b = b_;
 
-  return strcmp (a->name, b->name);
+  return strcasecmp (a->name, b->name);
 }
 
 /* A hsh_hash_func that hashes variable V based on its name. */
@@ -247,7 +317,7 @@ hash_var_name (const void *v_, void *foo UNUSED)
 {
   const struct variable *v = v_;
 
-  return hsh_hash_string (v->name);
+  return hsh_hash_case_string (v->name);
 }
 
 /* A hsh_compare_func that orders pointers to variables A and B
@@ -258,7 +328,7 @@ compare_var_ptr_names (const void *a_, const void *b_, void *foo UNUSED)
   struct variable *const *a = a_;
   struct variable *const *b = b_;
 
-  return strcmp ((*a)->name, (*b)->name);
+  return strcasecmp ((*a)->name, (*b)->name);
 }
 
 /* A hsh_hash_func that hashes pointer to variable V based on its
@@ -268,5 +338,66 @@ hash_var_ptr_name (const void *v_, void *foo UNUSED)
 {
   struct variable *const *v = v_;
 
-  return hsh_hash_string ((*v)->name);
+  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);
 }