X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fvars-atr.c;h=970ce5673f45c0154ae08640b28bcb92171cc99b;hb=dcf9b154cbcaa35c3d8459a201b77eec8bcb30bd;hp=9e7b541387f9d8ea1afd9d44fed2fef9de02cbbb;hpb=fcb9e49b2a2d57af7c001ae5d2eda9ac443ba36b;p=pspp diff --git a/src/vars-atr.c b/src/vars-atr.c index 9e7b541387..970ce5673f 100644 --- a/src/vars-atr.c +++ b/src/vars-atr.c @@ -14,557 +14,306 @@ 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 -#include +#include "var.h" +#include "error.h" #include #include "alloc.h" -#include "approx.h" -#include "avl.h" -#include "command.h" -#include "do-ifP.h" -#include "expr.h" -#include "file-handle.h" -#include "inpt-pgm.h" +#include "dictionary.h" +#include "hash.h" +#include "lex-def.h" #include "misc.h" #include "str.h" -#include "var.h" -#include "vector.h" -#include "vfm.h" +#include "value-labels.h" -#include "debug-print.h" +#include "gettext.h" +#define _(msgid) gettext (msgid) -#if DEBUGGING -/* Dumps one variable to standard output. */ -void -dump_one_var_node (void * pnode, void *param, int level) +/* Returns an adjective describing the given variable TYPE, + suitable for use in phrases like "numeric variable". */ +const char * +var_type_adj (enum var_type type) { - 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 : ""); + return type == NUMERIC ? _("numeric") : _("string"); } -/* Dumps a tree of the variables to standard output. */ -void -dump_var_tree (void) +/* Returns a noun describing a value of the given variable TYPE, + suitable for use in phrases like "a number". */ +const char * +var_type_noun (enum var_type type) { - printf (_("Vartree:\n")); -/* - avl_walk_inorder (default_dict.var_by_name, dump_one_var_node, NULL); -*/ + return type == NUMERIC ? _("number") : _("string"); } -#endif -/* 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) +/* 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) { - return avl_find (default_dict.var_by_name, (struct variable *) name); + free (v->aux); } -/* 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) +/* 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) { - return avl_find (d->var_by_name, (struct variable *) name); + 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)); } -/* 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) +/* Create a hash of v */ +unsigned +hash_value(const union value *v, 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; - } -} + unsigned id_hash; -#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) -{ - struct variable *new_var = create_variable (dict, name, type, width); - assert (new_var != NULL); - return new_var; -} + if ( 0 == width ) + id_hash = hsh_hash_double (v->f); + else + id_hash = hsh_hash_bytes (v->s, min(MAX_SHORT_STRING, width)); -/* 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; + return id_hash; } -#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) -{ - 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); -} -/* 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) -{ - if (v->name != name) - /* Avoid problems with overlap. */ - strcpy (v->name, name); - avl_force_insert (dict->var_by_name, v); + +/* 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); - v->type = type; - v->left = name[0] == '#'; - v->width = type == NUMERIC ? 0 : width; - v->miss_type = MISSING_NONE; - if (v->type == NUMERIC) + length = strlen (name); + if (length < 1) { - v->print.type = FMT_F; - v->print.w = 8; - v->print.d = 2; + if (issue_error) + msg (SE, _("Variable name cannot be empty string.")); + return false; } - else + else if (length > LONG_NAME_LEN) { - v->print.type = FMT_A; - v->print.w = v->width; - v->print.d = 0; + if (issue_error) + msg (SE, _("Variable name %s exceeds %d-character limit."), + name, (int) LONG_NAME_LEN); + return false; } - v->write = v->print; -} -/* 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) + 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])) { - 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); + 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; } -} - -/* 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) + if (lex_id_to_token (name, strlen (name)) != T_ID) { - v->fv = v->nv = 0; - v->fv = default_dict.nval; - default_dict.nval += nv; + if (issue_error) + msg (SE, _("`%s' may not be used as a variable name because it " + "is a reserved word."), name); + return false; } - v->nv = nv; + + return true; } -/* 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. */ -void -rename_variable (struct dictionary * dict, struct variable *v, - const char *new_name) +/* 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) { - 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); + const struct variable *a = a_; + const struct variable *b = b_; + + return strcasecmp (a->name, b->name); } -/* 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) +/* A hsh_hash_func that hashes variable V based on its name. */ +unsigned +hash_var_name (const void *v_, void *foo UNUSED) { - 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; - } - - if (v->label) - { - free (v->label); - v->label = NULL; - } + const struct variable *v = v_; - 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; - } - } - -#if DEBUGGING - if (dict->var_by_name != NULL) - dump_var_tree (); -#endif + return hsh_hash_case_string (v->name); } -/* 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) +/* 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) { - 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; - } + struct variable *const *a = a_; + struct variable *const *b = b_; + + return strcasecmp ((*a)->name, (*b)->name); } - -/* 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) +/* 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) { - assert (v->ref_count >= 1); - if (--v->ref_count == 0) - { - free (v->s); - free (v); - } -} + struct variable *const *v = v_; -/* Frees value label P. PARAM is ignored. Used as a callback with - avl_destroy(). */ + return hsh_hash_case_string ((*v)->name); +} + +/* Sets V's short_name to SHORT_NAME, truncating it to + SHORT_NAME_LEN characters and converting it to uppercase in + the process. */ void -free_val_lab (void *p, void *param unused) +var_set_short_name (struct variable *v, const char *short_name) { - free_value_label ((struct value_label *) p); + 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); } -/* 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) +/* Clears V's short name. */ +void +var_clear_short_name (struct variable *v) { - static char *buf; - static int bufsize; - struct value_label template, *find; + assert (v != NULL); - 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; - } + v->short_name[0] = '\0'; } -/* Return nonzero only if X is a user-missing value for numeric - variable V. */ -inline int -is_num_user_missing (double x, const struct variable *v) +/* 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) { - switch (v->miss_type) - { - case MISSING_NONE: - return 0; - case MISSING_1: - return approx_eq (x, v->missing[0].f); - case MISSING_2: - return (approx_eq (x, v->missing[0].f) - || approx_eq (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)); - case MISSING_RANGE: - return (approx_ge (x, v->missing[0].f) - && approx_le (x, v->missing[1].f)); - case MISSING_LOW: - return approx_le (x, v->missing[0].f); - case MISSING_HIGH: - return approx_ge (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)); - case MISSING_LOW_1: - return (approx_le (x, v->missing[0].f) - || approx_eq (x, v->missing[1].f)); - case MISSING_HIGH_1: - return (approx_ge (x, v->missing[0].f) - || approx_eq (x, v->missing[1].f)); - default: - assert (0); - } - abort (); -} + char string[SHORT_NAME_LEN + 1]; + char *start, *end; + int len, ofs; -/* Return nonzero only if string S is a user-missing variable for - string variable V. */ -inline int -is_str_user_missing (const unsigned char s[], const struct variable *v) -{ - switch (v->miss_type) + 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 { - case MISSING_NONE: - return 0; - case MISSING_1: - return !strncmp (s, v->missing[0].s, v->width); - case MISSING_2: - return (!strncmp (s, v->missing[0].s, v->width) - || !strncmp (s, v->missing[1].s, v->width)); - case MISSING_3: - return (!strncmp (s, v->missing[0].s, v->width) - || !strncmp (s, v->missing[1].s, v->width) - || !strncmp (s, v->missing[2].s, v->width)); - default: - assert (0); + *--start = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[suffix % 26]; + if (start <= string + 1) + msg (SE, _("Variable suffix too large.")); + suffix /= 26; } - abort (); -} + while (suffix > 0); + *--start = '_'; -/* Return nonzero only if value VAL is system-missing for variable - V. */ -int -is_system_missing (const union value *val, const struct variable *v) -{ - return v->type == NUMERIC && val->f == SYSMIS; + /* 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); } -/* Return nonzero only if value VAL is system- or user-missing for - variable V. */ -int -is_missing (const union value *val, const struct variable *v) + +/* Returns the dictionary class corresponding to a variable named + NAME. */ +enum dict_class +dict_class_from_id (const char *name) { - switch (v->type) + assert (name != NULL); + + switch (name[0]) { - case NUMERIC: - if (val->f == SYSMIS) - return 1; - return is_num_user_missing (val->f, v); - case ALPHA: - return is_str_user_missing (val->s, v); default: - assert (0); + return DC_ORDINARY; + case '$': + return DC_SYSTEM; + case '#': + return DC_SCRATCH; } - abort (); } -/* Return nonzero only if value VAL is user-missing for variable V. */ -int -is_user_missing (const union value *val, const struct variable *v) +/* Returns the name of dictionary class DICT_CLASS. */ +const char * +dict_class_to_name (enum dict_class dict_class) { - switch (v->type) + switch (dict_class) { - case NUMERIC: - return is_num_user_missing (val->f, v); - case ALPHA: - return is_str_user_missing (val->s, v); + case DC_ORDINARY: + return _("ordinary"); + case DC_SYSTEM: + return _("system"); + case DC_SCRATCH: + return _("scratch"); default: assert (0); + abort (); } - abort (); }