X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fdictionary.c;h=44504ed96525ec5c4af0eaffd82f5de2fe3634c1;hb=235aec793b51987574741a6b8346980b9396cfde;hp=0dadb959d371d5d0624f4dca21ff5d7c54d0a4ed;hpb=a10cebe053263d7e936b6533a3dbf5ac2f0586a1;p=pspp-builds.git diff --git a/src/data/dictionary.c b/src/data/dictionary.c index 0dadb959..44504ed9 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -1,20 +1,18 @@ -/* PSPP - computes sample statistics. +/* PSPP - a program for statistical analysis. Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. 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., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include @@ -24,7 +22,6 @@ #include #include "case.h" -#include "cat-routines.h" #include "category.h" #include "settings.h" #include "value-labels.h" @@ -52,13 +49,13 @@ struct dictionary size_t var_cnt, var_cap; /* Number of variables, capacity. */ struct hsh_table *name_tab; /* Variable index by name. */ int next_value_idx; /* Index of next `union value' to allocate. */ - struct variable **split; /* SPLIT FILE vars. */ + const struct variable **split; /* SPLIT FILE vars. */ size_t split_cnt; /* SPLIT FILE count. */ struct variable *weight; /* WEIGHT variable. */ struct variable *filter; /* FILTER variable. */ size_t case_limit; /* Current case limit (N command). */ char *label; /* File label. */ - char *documents; /* Documents, as a string. */ + struct string documents; /* Documents, as a string. */ struct vector **vector; /* Vectors of variables. */ size_t vector_cnt; /* Number of vectors. */ const struct dict_callbacks *callbacks; /* Callbacks on dictionary @@ -159,14 +156,22 @@ dict_clear (struct dictionary *d) while (d->var_cnt > 0 ) { - var_clear_vardict (d->var[d->var_cnt - 1]); - var_destroy (d->var[d->var_cnt -1]); + struct variable *v = d->var[d->var_cnt - 1]; + int dict_index = var_get_dict_index (v); + int case_index = var_get_case_index (v); + int val_cnt = var_get_value_cnt (v); + + var_clear_vardict (v); + var_destroy (v); d->var_cnt--; if (d->callbacks && d->callbacks->var_deleted ) - d->callbacks->var_deleted (d, d->var_cnt, d->cb_data); + d->callbacks->var_deleted (d, + dict_index, case_index, val_cnt, + d->cb_data); } + free (d->var); d->var = NULL; d->var_cnt = d->var_cap = 0; @@ -178,8 +183,7 @@ dict_clear (struct dictionary *d) d->case_limit = 0; free (d->label); d->label = NULL; - free (d->documents); - d->documents = NULL; + ds_destroy (&d->documents); dict_clear_vectors (d); } @@ -233,13 +237,20 @@ dict_get_var (const struct dictionary *d, size_t idx) return d->var[idx]; } +inline void +dict_get_vars (const struct dictionary *d, const struct variable ***vars, + size_t *cnt, unsigned exclude_classes) +{ + dict_get_vars_mutable (d, (struct variable ***) vars, cnt, exclude_classes); +} + /* Sets *VARS to an array of pointers to variables in D and *CNT to the number of variables in *D. All variables are returned 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. */ void -dict_get_vars (const struct dictionary *d, struct variable ***vars, +dict_get_vars_mutable (const struct dictionary *d, struct variable ***vars, size_t *cnt, unsigned exclude_classes) { size_t count; @@ -415,7 +426,7 @@ set_var_dict_index (struct variable *v, int dict_index) d->callbacks->var_changed (d, dict_index, d->cb_data); } -/* Sets the case_index in V's vardict to DICT_INDEX. */ +/* Sets the case_index in V's vardict to CASE_INDEX. */ static void set_var_case_index (struct variable *v, int case_index) { @@ -438,11 +449,12 @@ reindex_vars (struct dictionary *d, size_t from, size_t to) /* Deletes variable V from dictionary D and frees V. This is a very bad idea if there might be any pointers to V - from outside D. In general, no variable in should be deleted when - any transformations are active on the dictionary's dataset, because - those transformations might reference the deleted variable. - The safest time to delete a variable is just after a procedure - has been executed, as done by MODIFY VARS. + from outside D. In general, no variable in the active file's + dictionary should be deleted when any transformations are + active on the dictionary's dataset, because those + transformations might reference the deleted variable. The + safest time to delete a variable is just after a procedure has + been executed, as done by MODIFY VARS. Pointers to V within D are not a problem, because dict_delete_var() knows to remove V from split variables, @@ -451,6 +463,8 @@ void dict_delete_var (struct dictionary *d, struct variable *v) { int dict_index = var_get_dict_index (v); + const int case_index = var_get_case_index (v); + const int val_cnt = var_get_value_cnt (v); assert (dict_contains_var (d, v)); @@ -483,7 +497,7 @@ dict_delete_var (struct dictionary *d, struct variable *v) var_destroy (v); if (d->callbacks && d->callbacks->var_deleted ) - d->callbacks->var_deleted (d, dict_index, d->cb_data); + d->callbacks->var_deleted (d, dict_index, case_index, val_cnt, d->cb_data); } /* Deletes the COUNT variables listed in VARS from D. This is @@ -712,7 +726,7 @@ dict_get_case_weight (const struct dictionary *d, const struct ccase *c, double w = case_num (c, d->weight); if (w < 0.0 || var_is_num_missing (d->weight, w, MV_ANY)) w = 0.0; - if ( w == 0.0 && *warn_on_invalid ) { + if ( w == 0.0 && warn_on_invalid != NULL && *warn_on_invalid ) { *warn_on_invalid = false; msg (SW, _("At least one case in the data file had a weight value " "that was user-missing, system-missing, zero, or " @@ -830,6 +844,33 @@ dict_compact_values (struct dictionary *d) } } + +/* + Reassigns case indices for D, increasing each index above START by + the value PADDING. +*/ +static void +dict_pad_values (struct dictionary *d, int start, int padding) +{ + size_t i; + + if ( padding <= 0 ) + return; + + for (i = 0; i < d->var_cnt; ++i) + { + struct variable *v = d->var[i]; + + int index = var_get_case_index (v); + + if ( index >= start) + set_var_case_index (v, index + padding); + } + + d->next_value_idx += padding; +} + + /* Returns the number of values that would be used by a case if dict_compact_values() were called. */ size_t @@ -1009,7 +1050,7 @@ dict_compactor_destroy (struct dict_compactor *compactor) dict_get_split_cnt() to determine how many SPLIT FILE vars there are. Returns a null pointer if and only if there are no SPLIT FILE vars. */ -struct variable *const * +const struct variable *const * dict_get_split_vars (const struct dictionary *d) { assert (d != NULL); @@ -1051,7 +1092,7 @@ dict_set_split_vars (struct dictionary *d, assert (cnt == 0 || split != NULL); d->split_cnt = cnt; - d->split = xnrealloc (d->split, cnt, sizeof *d->split); + d->split = cnt > 0 ? xnrealloc (d->split, cnt, sizeof *d->split) : NULL; memcpy (d->split, split, cnt * sizeof *d->split); if ( d->callbacks && d->callbacks->split_changed ) @@ -1089,27 +1130,73 @@ dict_set_label (struct dictionary *d, const char *label) } /* Returns the documents for D, or a null pointer if D has no - documents (see cmd_document()).. */ + documents. If the return value is nonnull, then the string + will be an exact multiple of DOC_LINE_LENGTH bytes in length, + with each segment corresponding to one line. */ const char * dict_get_documents (const struct dictionary *d) { - assert (d != NULL); - - return d->documents; + return ds_is_empty (&d->documents) ? NULL : ds_cstr (&d->documents); } /* Sets the documents for D to DOCUMENTS, or removes D's - documents if DOCUMENT is a null pointer. */ + documents if DOCUMENT is a null pointer. If DOCUMENTS is + nonnull, then it should be an exact multiple of + DOC_LINE_LENGTH bytes in length, with each segment + corresponding to one line. */ void dict_set_documents (struct dictionary *d, const char *documents) { - assert (d != NULL); + size_t remainder; - free (d->documents); - if (documents == NULL) - d->documents = NULL; - else - d->documents = xstrdup (documents); + ds_assign_cstr (&d->documents, documents != NULL ? documents : ""); + + /* In case the caller didn't get it quite right, pad out the + final line with spaces. */ + remainder = ds_length (&d->documents) % DOC_LINE_LENGTH; + if (remainder != 0) + ds_put_char_multiple (&d->documents, ' ', DOC_LINE_LENGTH - remainder); +} + +/* Drops the documents from dictionary D. */ +void +dict_clear_documents (struct dictionary *d) +{ + ds_clear (&d->documents); +} + +/* Appends LINE to the documents in D. LINE will be truncated or + padded on the right with spaces to make it exactly + DOC_LINE_LENGTH bytes long. */ +void +dict_add_document_line (struct dictionary *d, const char *line) +{ + if (strlen (line) > DOC_LINE_LENGTH) + { + /* Note to translators: "bytes" is correct, not characters */ + msg (SW, _("Truncating document line to %d bytes."), DOC_LINE_LENGTH); + } + buf_copy_str_rpad (ds_put_uninit (&d->documents, DOC_LINE_LENGTH), + DOC_LINE_LENGTH, line); +} + +/* Returns the number of document lines in dictionary D. */ +size_t +dict_get_document_line_cnt (const struct dictionary *d) +{ + return ds_length (&d->documents) / DOC_LINE_LENGTH; +} + +/* Copies document line number IDX from dictionary D into + LINE, trimming off any trailing white space. */ +void +dict_get_document_line (const struct dictionary *d, + size_t idx, struct string *line) +{ + assert (idx < dict_get_document_line_cnt (d)); + ds_assign_substring (line, ds_substr (&d->documents, idx * DOC_LINE_LENGTH, + DOC_LINE_LENGTH)); + ds_rtrim (line, ss_cstr (CC_SPACES)); } /* Creates in D a vector named NAME that contains the CNT @@ -1137,6 +1224,18 @@ dict_create_vector (struct dictionary *d, return false; } +/* Creates in D a vector named NAME that contains the CNT + variables in VAR. A vector named NAME must not already exist + in D. */ +void +dict_create_vector_assert (struct dictionary *d, + const char *name, + struct variable **var, size_t cnt) +{ + assert (dict_lookup_vector (d, name) == NULL); + dict_create_vector (d, name, var, cnt); +} + /* Returns the vector in D with index IDX, which must be less than dict_get_vector_cnt (D). */ const struct vector * @@ -1326,3 +1425,23 @@ dict_var_changed (const struct variable *v) d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data); } } + + +/* Called from variable.c to notify the dictionary that the variable's width + has changed */ +void +dict_var_resized (const struct variable *v, int delta) +{ + if ( var_has_vardict (v)) + { + const struct vardict_info *vdi = var_get_vardict (v); + struct dictionary *d; + + d = vdi->dict; + + dict_pad_values (d, var_get_case_index(v) + 1, delta); + + if ( d->callbacks && d->callbacks->var_resized ) + d->callbacks->var_resized (d, var_get_dict_index (v), delta, d->cb_data); + } +}