X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fdictionary.c;h=427cae3669eb695ef282650965ebb5df0ca4cb23;hb=d9153beacc1308b37dbaf4f64155553360aa0173;hp=6dd1b99f876f52dec0204ab495f1ce801eaa518a;hpb=6e097c89af440da90b43ce90864394c4d0c843d5;p=pspp diff --git a/src/data/dictionary.c b/src/data/dictionary.c index 6dd1b99f87..427cae3669 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -55,6 +55,7 @@ /* A dictionary. */ struct dictionary { + int ref_cnt; struct vardict_info *var; /* Variables. */ size_t var_cnt, var_cap; /* Number of variables, capacity. */ struct caseproto *proto; /* Prototype for dictionary cases @@ -74,6 +75,11 @@ struct dictionary struct mrset **mrsets; /* Multiple response sets. */ size_t n_mrsets; /* Number of multiple response sets. */ + /* Whether variable names must be valid identifiers. Normally, this is + true, but sometimes a dictionary is prepared for external use + (e.g. output to a CSV file) where names don't have to be valid. */ + bool names_must_be_ids; + char *encoding; /* Character encoding of string data */ const struct dict_callbacks *callbacks; /* Callbacks on dictionary @@ -102,7 +108,8 @@ bool dict_id_is_valid (const struct dictionary *dict, const char *id, bool issue_error) { - return id_is_valid (id, dict->encoding, issue_error); + return (!dict->names_must_be_ids + || id_is_valid (id, dict->encoding, issue_error)); } void @@ -170,12 +177,21 @@ dict_create (const char *encoding) struct dictionary *d = xzalloc (sizeof *d); d->encoding = xstrdup (encoding); + d->names_must_be_ids = true; hmap_init (&d->name_map); attrset_init (&d->attributes); + d->ref_cnt = 1; return d; } +struct dictionary * +dict_ref (struct dictionary *s) +{ + s->ref_cnt++; + return s; +} + /* Creates and returns a (deep) copy of an existing dictionary. @@ -193,6 +209,7 @@ dict_clone (const struct dictionary *s) size_t i; d = dict_create (s->encoding); + dict_set_names_must_be_ids (d, dict_get_names_must_be_ids (s)); for (i = 0; i < s->var_cnt; i++) { @@ -211,7 +228,7 @@ dict_clone (const struct dictionary *s) d->split_cnt = s->split_cnt; if (d->split_cnt > 0) { - d->split = xnmalloc (d->split_cnt, sizeof *d->split); + d->split = xnmalloc (d->split_cnt, sizeof *d->split); for (i = 0; i < d->split_cnt; i++) d->split[i] = dict_lookup_var_assert (d, var_get_name (s->split[i])); } @@ -280,23 +297,31 @@ dict_clear (struct dictionary *d) } /* Clears a dictionary and destroys it. */ +static void +_dict_destroy (struct dictionary *d) +{ + /* In general, we don't want callbacks occurring, if the dictionary + is being destroyed */ + d->callbacks = NULL ; + + dict_clear (d); + string_array_destroy (&d->documents); + hmap_destroy (&d->name_map); + attrset_destroy (&d->attributes); + dict_clear_mrsets (d); + free (d->encoding); + free (d); +} + void -dict_destroy (struct dictionary *d) +dict_unref (struct dictionary *d) { - if (d != NULL) - { - /* In general, we don't want callbacks occuring, if the dictionary - is being destroyed */ - d->callbacks = NULL ; - - dict_clear (d); - string_array_destroy (&d->documents); - hmap_destroy (&d->name_map); - attrset_destroy (&d->attributes); - dict_clear_mrsets (d); - free (d->encoding); - free (d); - } + if (d == NULL) + return; + d->ref_cnt--; + assert (d->ref_cnt >= 0); + if (d->ref_cnt == 0) + _dict_destroy (d); } /* Returns the number of variables in D. */ @@ -756,17 +781,18 @@ rename_var (struct variable *v, const char *new_name) var_set_vardict (v, vardict); } -/* Changes the name of V in D to name NEW_NAME. Assert-fails if - a variable named NEW_NAME is already in D, except that - NEW_NAME may be the same as V's existing name. */ -void -dict_rename_var (struct dictionary *d, struct variable *v, - const char *new_name) +/* Tries to changes the name of V in D to name NEW_NAME. Returns true if + successful, false if a variable (other than V) with the given name already + exists in D. */ +bool +dict_try_rename_var (struct dictionary *d, struct variable *v, + const char *new_name) { - struct variable *old = var_clone (v); - assert (!utf8_strcasecmp (var_get_name (v), new_name) - || dict_lookup_var (d, new_name) == NULL); + struct variable *conflict = dict_lookup_var (d, new_name); + if (conflict && v != conflict) + return false; + struct variable *old = var_clone (v); unindex_var (d, var_get_vardict (v)); rename_var (v, new_name); reindex_var (d, var_get_vardict (v)); @@ -779,6 +805,19 @@ dict_rename_var (struct dictionary *d, struct variable *v, d->callbacks->var_changed (d, var_get_dict_index (v), VAR_TRAIT_NAME, old, d->cb_data); var_destroy (old); + + return true; +} + +/* Changes the name of V in D to name NEW_NAME. Assert-fails if + a variable named NEW_NAME is already in D, except that + NEW_NAME may be the same as V's existing name. */ +void +dict_rename_var (struct dictionary *d, struct variable *v, + const char *new_name) +{ + bool ok UNUSED = dict_try_rename_var (d, v, new_name); + assert (ok); } /* Renames COUNT variables specified in VARS to the names given @@ -980,6 +1019,27 @@ dict_make_unique_var_name (const struct dictionary *dict, const char *hint, return make_numeric_name (dict, num_start); } +/* Returns whether variable names must be valid identifiers. Normally, this is + true, but sometimes a dictionary is prepared for external use (e.g. output + to a CSV file) where names don't have to be valid. */ +bool +dict_get_names_must_be_ids (const struct dictionary *d) +{ + return d->names_must_be_ids; +} + +/* Sets whether variable names must be valid identifiers. Normally, this is + true, but sometimes a dictionary is prepared for external use (e.g. output + to a CSV file) where names don't have to be valid. + + Changing this setting from false to true doesn't make the dictionary check + all the existing variable names, so it can cause an invariant violation. */ +void +dict_set_names_must_be_ids (struct dictionary *d, bool names_must_be_ids) +{ + d->names_must_be_ids = names_must_be_ids; +} + /* Returns the weighting variable in dictionary D, or a null pointer if the dictionary is unweighted. */ struct variable * @@ -1012,6 +1072,13 @@ dict_get_case_weight (const struct dictionary *d, const struct ccase *c, } } +/* Returns the format to use for weights. */ +const struct fmt_spec * +dict_get_weight_format (const struct dictionary *d) +{ + return d->weight ? var_get_print_format (d->weight) : &F_8_0; +} + /* Sets the weighting variable of D to V, or turning off weighting if V is a null pointer. */ void @@ -1047,6 +1114,22 @@ dict_set_filter (struct dictionary *d, struct variable *v) assert (v == NULL || dict_contains_var (d, v)); assert (v == NULL || var_is_numeric (v)); + /* When a filter is set, we ref the dictionary. + This is because the GUI maintains a pointer + to the dict's variables, and the variables' + addresses change in the callback. */ + if (d->filter == NULL && v != NULL) + { + d = dict_ref (d); + } + + /* Deref the dict when a filter is removed. */ + if (d->filter != NULL && v == NULL) + { + assert (d->ref_cnt > 0); + dict_unref (d); + } + d->filter = v; if (d->changed) d->changed (d, d->changed_data); @@ -1660,7 +1743,7 @@ dict_destroy_internal_var (struct variable *var) valgrind --leak-check --show-reachable won't show internal_dict. */ if (dict_get_var_cnt (internal_dict) == 0) { - dict_destroy (internal_dict); + dict_unref (internal_dict); internal_dict = NULL; } }