X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fdictionary.c;h=d69f1c29017c61c7d4842de8e11cce765f757c9e;hb=2a64e944e842caf6df8476f3bc7c815a332dc975;hp=66cb956f1dc6d1dfa6284fe5bcc9d601e110d2b3;hpb=5f90c3d6b3d73d535c730df350310107fd942532;p=pspp diff --git a/src/data/dictionary.c b/src/data/dictionary.c index 66cb956f1d..d69f1c2901 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -32,6 +32,7 @@ #include "data/value-labels.h" #include "data/vardict.h" #include "data/variable.h" +#include "data/varset.h" #include "data/vector.h" #include "libpspp/array.h" #include "libpspp/assertion.h" @@ -78,6 +79,8 @@ struct dictionary struct attrset attributes; /* Custom attributes. */ struct mrset **mrsets; /* Multiple response sets. */ size_t n_mrsets; /* Number of multiple response sets. */ + struct varset **varsets; /* Variable sets. */ + size_t n_varsets; /* Number of variable sets. */ /* Whether variable names must be valid identifiers. Normally, this is true, but sometimes a dictionary is prepared for external use @@ -96,6 +99,7 @@ struct dictionary static void dict_unset_split_var (struct dictionary *, struct variable *, bool); static void dict_unset_mrset_var (struct dictionary *, struct variable *); +static void dict_unset_varset_var (struct dictionary *, struct variable *); /* Compares two double pointers to variables, which should point to elements of a struct dictionary's `var' member array. */ @@ -179,15 +183,35 @@ dict_get_encoding (const struct dictionary *d) return d->encoding ; } +/* Checks whether UTF-8 string ID is an acceptable identifier in DICT's + encoding. Returns true if it is, otherwise an error message that the caller + must free(). */ +char * WARN_UNUSED_RESULT +dict_id_is_valid__ (const struct dictionary *dict, const char *id) +{ + if (!dict->names_must_be_ids) + return NULL; + return id_is_valid__ (id, dict->encoding); +} + +static bool +error_to_bool (char *error) +{ + if (error) + { + free (error); + return false; + } + else + return true; +} + /* Returns true if UTF-8 string ID is an acceptable identifier in DICT's - encoding, false otherwise. If ISSUE_ERROR is true, issues an explanatory - error message on failure. */ + encoding, false otherwise. */ bool -dict_id_is_valid (const struct dictionary *dict, const char *id, - bool issue_error) +dict_id_is_valid (const struct dictionary *dict, const char *id) { - return (!dict->names_must_be_ids - || id_is_valid (id, dict->encoding, issue_error)); + return error_to_bool (dict_id_is_valid__ (dict, id)); } void @@ -279,20 +303,16 @@ dict_create (const char *encoding) struct dictionary * dict_clone (const struct dictionary *s) { - struct dictionary *d; - size_t i; - - d = dict_create (s->encoding); + struct dictionary *d = dict_create (s->encoding); dict_set_names_must_be_ids (d, dict_get_names_must_be_ids (s)); - for (i = 0; i < s->n_vars; i++) + for (size_t i = 0; i < s->n_vars; i++) { struct variable *sv = s->vars[i].var; struct variable *dv = dict_clone_var_assert (d, sv); - size_t i; - for (i = 0; i < var_get_n_short_names (sv); i++) - var_set_short_name (dv, i, var_get_short_name (sv, i)); + for (size_t j = 0; j < var_get_n_short_names (sv); j++) + var_set_short_name (dv, j, var_get_short_name (sv, j)); var_get_vardict (dv)->case_index = var_get_vardict (sv)->case_index; } @@ -303,7 +323,7 @@ dict_clone (const struct dictionary *s) if (d->n_splits > 0) { d->split = xnmalloc (d->n_splits, sizeof *d->split); - for (i = 0; i < d->n_splits; i++) + for (size_t i = 0; i < d->n_splits; i++) d->split[i] = dict_lookup_var_assert (d, var_get_name (s->split[i])); } d->split_type = s->split_type; @@ -320,12 +340,12 @@ dict_clone (const struct dictionary *s) d->n_vectors = s->n_vectors; d->vector = xnmalloc (d->n_vectors, sizeof *d->vector); - for (i = 0; i < s->n_vectors; i++) + for (size_t i = 0; i < s->n_vectors; i++) d->vector[i] = vector_clone (s->vector[i], s, d); dict_set_attributes (d, dict_get_attributes (s)); - for (i = 0; i < s->n_mrsets; i++) + for (size_t i = 0; i < s->n_mrsets; i++) { const struct mrset *old = s->mrsets[i]; struct mrset *new; @@ -339,10 +359,20 @@ dict_clone (const struct dictionary *s) dict_add_mrset (d, new); } - return d; -} + for (size_t i = 0; i < s->n_varsets; i++) + { + const struct varset *old = s->varsets[i]; + /* Clone old varset, then replace vars from D by vars from S. */ + struct varset *new = varset_clone (old); + for (size_t j = 0; j < new->n_vars; j++) + new->vars[j] = dict_lookup_var_assert (d, var_get_name (new->vars[j])); + + dict_add_varset (d, new); + } + return d; +} /* Returns the SPLIT FILE vars (see cmd_split_file()). Call dict_get_n_splits() to determine how many SPLIT FILE vars @@ -384,28 +414,33 @@ dict_unset_split_var (struct dictionary *d, struct variable *v, bool skip_callba } -/* Sets N split vars SPLIT in dictionary D. */ +/* Sets N split vars SPLIT in dictionary D. N is silently capped to a maximum + of MAX_SPLITS. */ static void dict_set_split_vars__ (struct dictionary *d, struct variable *const *split, size_t n, enum split_type type, bool skip_callbacks) { + if (n > MAX_SPLITS) + n = MAX_SPLITS; assert (n == 0 || split != NULL); d->n_splits = n; - d->split_type = type == SPLIT_NONE ? SPLIT_LAYERED : type; + d->split_type = (n == 0 ? SPLIT_NONE + : type == SPLIT_NONE ? SPLIT_LAYERED + : type); if (n > 0) - { - d->split = xnrealloc (d->split, n, sizeof *d->split) ; - memcpy (d->split, split, n * sizeof *d->split); - } + { + d->split = xnrealloc (d->split, n, sizeof *d->split) ; + memcpy (d->split, split, n * sizeof *d->split); + } else - { - free (d->split); - d->split = NULL; - } + { + free (d->split); + d->split = NULL; + } - if (!skip_callbacks) + if (!skip_callbacks) { if (d->changed) d->changed (d, d->changed_data); if (d->callbacks && d->callbacks->split_changed) @@ -458,6 +493,7 @@ dict_delete_var__ (struct dictionary *d, struct variable *v, bool skip_callbacks dict_unset_split_var (d, v, skip_callbacks); dict_unset_mrset_var (d, v); + dict_unset_varset_var (d, v); if (d->weight == v) dict_set_weight (d, NULL); @@ -554,6 +590,7 @@ dict_delete_consecutive_vars (struct dictionary *d, size_t idx, size_t count) dict_unset_split_var (d, v, false); dict_unset_mrset_var (d, v); + dict_unset_varset_var (d, v); if (d->weight == v) dict_set_weight (d, NULL); @@ -670,6 +707,7 @@ _dict_destroy (struct dictionary *d) hmap_destroy (&d->name_map); attrset_destroy (&d->attributes); dict_clear_mrsets (d); + dict_clear_varsets (d); free (d->encoding); free (d); } @@ -1290,10 +1328,10 @@ dict_get_rounded_case_weight (const struct dictionary *d, } /* Returns the format to use for weights. */ -const struct fmt_spec * +struct fmt_spec dict_get_weight_format (const struct dictionary *d) { - return d->weight ? var_get_print_format (d->weight) : &F_8_0; + return d->weight ? var_get_print_format (d->weight) : F_8_0; } /* Sets the weighting variable of D to V, or turning off @@ -1410,9 +1448,8 @@ dict_compact_values (struct dictionary *d) /* Returns the number of values occupied by the variables in dictionary D. All variables are considered if EXCLUDE_CLASSES - is 0, or it may contain one or more of (1u << DC_ORDINARY), - (1u << DC_SYSTEM), or (1u << DC_SCRATCH) to exclude the - corresponding type of variable. + is 0, or it may contain one or more of DC_ORDINARY, DC_SYSTEM, + or DC_SCRATCH to exclude the corresponding type of variable. The return value may be less than the number of values in one of dictionary D's cases (as returned by @@ -1421,15 +1458,13 @@ dict_compact_values (struct dictionary *d) size_t dict_count_values (const struct dictionary *d, unsigned int exclude_classes) { - assert ((exclude_classes & ~((1u << DC_ORDINARY) - | (1u << DC_SYSTEM) - | (1u << DC_SCRATCH))) == 0); + assert (!(exclude_classes & ~DC_ALL)); size_t n = 0; for (size_t i = 0; i < d->n_vars; i++) { enum dict_class class = var_get_dict_class (d->vars[i].var); - if (!(exclude_classes & (1u << class))) + if (!(exclude_classes & class)) n++; } return n; @@ -1449,15 +1484,13 @@ dict_get_compacted_proto (const struct dictionary *d, struct caseproto *proto; size_t i; - assert ((exclude_classes & ~((1u << DC_ORDINARY) - | (1u << DC_SYSTEM) - | (1u << DC_SCRATCH))) == 0); + assert (!(exclude_classes & ~DC_ALL)); proto = caseproto_create (); for (i = 0; i < d->n_vars; i++) { struct variable *v = d->vars[i].var; - if (!(exclude_classes & (1u << var_get_dict_class (v)))) + if (!(exclude_classes & var_get_dict_class (v))) proto = caseproto_add_width (proto, var_get_width (v)); } return proto; @@ -1785,6 +1818,97 @@ dict_unset_mrset_var (struct dictionary *dict, struct variable *var) } } + +/* Returns the variable set in DICT with index IDX, which must be between 0 and + the count returned by dict_get_n_varsets(), exclusive. */ +const struct varset * +dict_get_varset (const struct dictionary *dict, size_t idx) +{ + assert (idx < dict->n_varsets); + return dict->varsets[idx]; +} + +/* Returns the number of variable sets in DICT. */ +size_t +dict_get_n_varsets (const struct dictionary *dict) +{ + return dict->n_varsets; +} + +/* Looks for a variable set named NAME in DICT. If it finds one, returns its + index; otherwise, returns SIZE_MAX. */ +static size_t +dict_lookup_varset_idx (const struct dictionary *dict, const char *name) +{ + for (size_t i = 0; i < dict->n_varsets; i++) + if (!utf8_strcasecmp (name, dict->varsets[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 varset * +dict_lookup_varset (const struct dictionary *dict, const char *name) +{ + size_t idx = dict_lookup_varset_idx (dict, name); + return idx != SIZE_MAX ? dict->varsets[idx] : NULL; +} + +/* Adds VARSET 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 VARSET is transferred to DICT. */ +bool +dict_add_varset (struct dictionary *dict, struct varset *varset) +{ + size_t idx = dict_lookup_varset_idx (dict, varset->name); + if (idx == SIZE_MAX) + { + dict->varsets = xrealloc (dict->varsets, + (dict->n_varsets + 1) * sizeof *dict->varsets); + dict->varsets[dict->n_varsets++] = varset; + return true; + } + else + { + varset_destroy (dict->varsets[idx]); + dict->varsets[idx] = varset; + return false; + } +} + +/* Deletes all variable sets from DICT. */ +void +dict_clear_varsets (struct dictionary *dict) +{ + for (size_t i = 0; i < dict->n_varsets; i++) + varset_destroy (dict->varsets[i]); + free (dict->varsets); + dict->varsets = NULL; + dict->n_varsets = 0; +} + +/* Removes VAR, which must be in DICT, from DICT's multiple response sets. */ +static void +dict_unset_varset_var (struct dictionary *dict, struct variable *var) +{ + assert (dict_contains_var (dict, var)); + + for (size_t i = 0; i < dict->n_varsets; i++) + { + struct varset *varset = dict->varsets[i]; + + for (size_t j = 0; j < varset->n_vars;) + if (varset->vars[j] == var) + remove_element (varset->vars, varset->n_vars--, + sizeof *varset->vars, j); + else + j++; + } +} + /* 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