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 "command.h"
+#include "dictionary.h"
#include "do-ifP.h"
-#include "expr.h"
+#include "expressions/public.h"
#include "file-handle.h"
#include "hash.h"
-#include "inpt-pgm.h"
+#include "lexer.h"
#include "misc.h"
#include "str.h"
-#include "var.h"
-#include "vector.h"
#include "value-labels.h"
#include "vfm.h"
#include "debug-print.h"
-/* Clear the default dictionary. Note: This is probably not what you
- want to do. Use discard_variables() instead. */
-void
-clear_default_dict (void)
+/* 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 *))
{
- int i;
-
- for (i = 0; i < default_dict.nvar; i++)
- {
- clear_variable (&default_dict, default_dict.var[i]);
- free (default_dict.var[i]);
- }
-
- assert (default_dict.splits == NULL);
+ assert (v->aux == NULL);
+ assert (aux != NULL);
+ v->aux = aux;
+ v->aux_dtor = aux_dtor;
+ return aux;
+}
- default_dict.nvar = 0;
- default_dict.N = 0;
- default_dict.nval = 0;
- default_handle = inline_file;
- stop_weighting (&default_dict);
+/* Remove auxiliary data, if any, from V, and returns it, without
+ calling any associated destructor. */
+void *
+var_detach_aux (struct variable *v)
+{
+ void *aux = v->aux;
+ assert (aux != NULL);
+ v->aux = NULL;
+ return aux;
}
-/* Discards all the current state in preparation for a data-input
- command like DATA LIST or GET. */
+/* Clears auxiliary data, if any, from V, and calls any
+ associated destructor. */
void
-discard_variables (void)
+var_clear_aux (struct variable *v)
{
- clear_default_dict ();
-
- n_lag = 0;
-
- if (vfm_source)
+ assert (v != NULL);
+ if (v->aux != NULL)
{
- vfm_source->destroy_source ();
- vfm_source = NULL;
+ if (v->aux_dtor != NULL)
+ v->aux_dtor (v);
+ v->aux = 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 hsh_find (default_dict.name_tab, 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 hsh_find (d->name_tab, 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)
+/* 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)
{
- 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);
-
- hsh_force_insert (dict->name_tab, v);
+ unsigned id_hash;
- 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_labs = val_labs_create (width);
- 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->name_tab && v && new_name);
- hsh_delete (dict->name_tab, v);
- strncpy (v->name, new_name, 9);
- hsh_force_insert (dict->name_tab, 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 != NULL);
- assert (v != NULL);
-
- if (dict->name_tab != NULL)
- hsh_force_delete (dict->name_tab, v);
-
- val_labs_clear (v->val_labs);
+ n_lag = 0;
- if (v->label)
+ if (vfm_source != NULL)
{
- free (v->label);
- v->label = NULL;
+ free_case_source (vfm_source);
+ vfm_source = 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;
- }
- }
-}
+ cancel_transformations ();
-/* 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);
- hsh_force_insert (dict->name_tab, new_var);
-
- return new_var;
- }
+ ctl_stack = NULL;
+
+ expr_free (process_if_expr);
+ process_if_expr = NULL;
+
+ cancel_temporary ();
+
+ pgm_state = STATE_INIT;
}
-
/* Return nonzero only if X is a user-missing value for numeric
variable V. */
inline int
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);
}
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:
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."),
+ (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);
+ 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);
+ 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_variables (const void *a_, const void *b_, void *foo unused)
+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. */
unsigned
-hash_variable (const void *v_, void *foo unused)
+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
+ 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));
+
+ st_trim_copy (v->short_name, short_name, sizeof v->short_name);
+ st_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);
}