X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fvariable.c;h=2e584fe86fa5dd7e9867c27d257186e3ecd4a528;hb=899ca177810116bc0ff1c4bcce9368d79cd71d42;hp=349ea21c7cca1e76e4a0793c75f3b8bfbba8879e;hpb=d678d355282239870b9033040dade610c1d4f8f0;p=pspp diff --git a/src/data/variable.c b/src/data/variable.c index 349ea21c7c..2e584fe86f 100644 --- a/src/data/variable.c +++ b/src/data/variable.c @@ -1,5 +1,6 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, + 2014, 2016, 2020 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 @@ -62,6 +63,7 @@ const GEnumValue align[] = const GEnumValue measure[] = { + {MEASURE_UNKNOWN, "unknown", N_("Unknown")}, {MEASURE_NOMINAL, "nominal", N_("Nominal")}, {MEASURE_ORDINAL, "ordinal", N_("Ordinal")}, {MEASURE_SCALE, "scale", N_("Scale")}, @@ -82,6 +84,7 @@ const GEnumValue role[] = /* A variable. */ struct variable { + int ref_cnt; /* Dictionary information. */ char *name; /* Variable name. Mixed case. */ int width; /* 0 for numeric, otherwise string width. */ @@ -107,7 +110,7 @@ struct variable /* Used only for system and portable file input and output. See short-names.h. */ char **short_names; - size_t short_name_cnt; + size_t n_short_names; /* Custom attributes. */ struct attrset attributes; @@ -126,47 +129,61 @@ static void var_set_name_quiet (struct variable *v, const char *name); struct variable * var_create (const char *name, int width) { - struct variable *v; enum val_type type; assert (width >= 0 && width <= MAX_STRING); - v = xzalloc (sizeof *v); + struct variable *v = XZALLOC (struct variable); var_set_name_quiet (v, name); v->width = width; mv_init (&v->miss, width); v->leave = var_must_leave (v); type = val_type_from_width (width); v->alignment = var_default_alignment (type); - v->measure = var_default_measure (type); + v->measure = var_default_measure_for_type (type); v->role = ROLE_INPUT; v->display_width = var_default_display_width (width); v->print = v->write = var_default_formats (width); attrset_init (&v->attributes); ds_init_empty (&v->name_and_label); + v->ref_cnt = 1; + return v; } /* Destroys variable V. V must not belong to a dictionary. If it does, use dict_delete_var instead. */ +static void +var_destroy__ (struct variable *v) +{ + assert (!var_has_vardict (v)); + mv_destroy (&v->miss); + var_clear_short_names (v); + val_labs_destroy (v->val_labs); + var_set_label_quiet (v, NULL); + attrset_destroy (var_get_attributes (v)); + free (v->name); + ds_destroy (&v->name_and_label); + free (v); +} + +struct variable * +var_ref (struct variable *v) +{ + v->ref_cnt++; + return v; +} + void -var_destroy (struct variable *v) +var_unref (struct variable *v) { - if (v != NULL) - { - assert (!var_has_vardict (v)); - mv_destroy (&v->miss); - var_clear_short_names (v); - val_labs_destroy (v->val_labs); - var_set_label_quiet (v, NULL); - attrset_destroy (var_get_attributes (v)); - free (v->name); - ds_destroy (&v->name_and_label); - free (v); - } + if (--v->ref_cnt == 0) + var_destroy__ (v); } + + /* Variable names. */ @@ -186,7 +203,6 @@ static void var_set_name_quiet (struct variable *v, const char *name) { assert (!var_has_vardict (v)); - assert (id_is_plausible (name, false)); free (v->name); v->name = xstrdup (name); @@ -291,17 +307,15 @@ var_set_width_and_formats (struct variable *v, int new_width, ov = var_clone (v); - if (var_has_missing_values (v)) + if (mv_is_resizable (&v->miss, new_width)) + mv_resize (&v->miss, new_width); + else { - if (mv_is_resizable (&v->miss, new_width)) - mv_resize (&v->miss, new_width); - else - { - mv_destroy (&v->miss); - mv_init (&v->miss, new_width); - } - traits |= VAR_TRAIT_MISSING_VALUES; + mv_destroy (&v->miss); + mv_init (&v->miss, new_width); } + if (new_width != var_get_width (v)) + traits |= VAR_TRAIT_MISSING_VALUES; if (v->val_labs != NULL) { @@ -426,32 +440,28 @@ var_has_missing_values (const struct variable *v) return !mv_is_empty (&v->miss); } -/* Returns true if VALUE is in the given CLASS of missing values - in V, false otherwise. */ -bool -var_is_value_missing (const struct variable *v, const union value *value, - enum mv_class class) +/* Returns MV_SYSTEM if VALUE is system-missing, MV_USER if VALUE is + user-missing for V, and otherwise 0. */ +enum mv_class +var_is_value_missing (const struct variable *v, const union value *value) { - return mv_is_value_missing (&v->miss, value, class); + return mv_is_value_missing (&v->miss, value); } -/* Returns true if D is in the given CLASS of missing values in - V, false otherwise. - V must be a numeric variable. */ -bool -var_is_num_missing (const struct variable *v, double d, enum mv_class class) +/* Returns MV_SYSTEM if VALUE is system-missing, MV_USER if VALUE is + user-missing for V, and otherwise 0. V must be a numeric variable. */ +enum mv_class +var_is_num_missing (const struct variable *v, double d) { - return mv_is_num_missing (&v->miss, d, class); + return mv_is_num_missing (&v->miss, d); } -/* Returns true if S[] is a missing value for V, false otherwise. - S[] must contain exactly as many characters as V's width. - V must be a string variable. */ -bool -var_is_str_missing (const struct variable *v, const uint8_t s[], - enum mv_class class) +/* Returns MV_USER if VALUE is user-missing for V and otherwise 0. V must be + a string variable. */ +enum mv_class +var_is_str_missing (const struct variable *v, const uint8_t s[]) { - return mv_is_str_missing (&v->miss, s, class); + return mv_is_str_missing (&v->miss, s); } /* Returns variable V's value labels, @@ -497,7 +507,7 @@ var_set_value_labels (struct variable *v, const struct val_labs *vls) { struct variable *ov = var_clone (v); var_set_value_labels_quiet (v, vls); - dict_var_changed (v, VAR_TRAIT_LABEL, ov); + dict_var_changed (v, VAR_TRAIT_LABEL, ov); } @@ -558,47 +568,52 @@ static void append_value (const struct variable *v, const union value *value, struct string *str) { - char *s = data_out (value, var_get_encoding (v), &v->print); - ds_put_cstr (str, s); + char *s = data_out (value, var_get_encoding (v), &v->print, + settings_get_fmt_settings ()); + struct substring ss = ss_cstr (s); + ss_rtrim (&ss, ss_cstr (" ")); + ds_put_substring (str, ss); free (s); } -/* Append STR with a string representing VALUE for variable V. - That is, if VALUE has a label, append that label, - otherwise format VALUE and append the formatted string. - STR must be a pointer to an initialised struct string. -*/ void -var_append_value_name (const struct variable *v, const union value *value, - struct string *str) +var_append_value_name__ (const struct variable *v, const union value *value, + enum settings_value_show show, struct string *str) { - enum settings_value_style style = settings_get_value_style (); - const char *name = var_lookup_value_label (v, value); + const char *label = var_lookup_value_label (v, value); - switch (style) + switch (show) { - case SETTINGS_VAL_STYLE_VALUES: + case SETTINGS_VALUE_SHOW_VALUE: append_value (v, value, str); break; - - case SETTINGS_VAL_STYLE_LABELS: - if (name == NULL) - append_value (v, value, str); + + default: + case SETTINGS_VALUE_SHOW_LABEL: + if (label) + ds_put_cstr (str, label); else - ds_put_cstr (str, name); + append_value (v, value, str); break; - case SETTINGS_VAL_STYLE_BOTH: - default: + case SETTINGS_VALUE_SHOW_BOTH: append_value (v, value, str); - if (name != NULL) - { - ds_put_cstr (str, " ("); - ds_put_cstr (str, name); - ds_put_cstr (str, ")"); - } + if (label != NULL) + ds_put_format (str, " %s", label); break; - }; + } +} + +/* Append STR with a string representing VALUE for variable V. + That is, if VALUE has a label, append that label, + otherwise format VALUE and append the formatted string. + STR must be a pointer to an initialised struct string. +*/ +void +var_append_value_name (const struct variable *v, const union value *value, + struct string *str) +{ + var_append_value_name__ (v, value, settings_get_show_values (), str); } /* Print and write formats. */ @@ -721,24 +736,19 @@ update_vl_string (const struct variable *v) const char * var_to_string (const struct variable *v) { - enum settings_var_style style = settings_get_var_style (); - - switch (style) - { - case SETTINGS_VAR_STYLE_NAMES: + switch (settings_get_show_variables ()) + { + case SETTINGS_VALUE_SHOW_VALUE: return v->name; - break; - case SETTINGS_VAR_STYLE_LABELS: + + case SETTINGS_VALUE_SHOW_LABEL: + default: return v->label != NULL ? v->label : v->name; - break; - case SETTINGS_VAR_STYLE_BOTH: + + case SETTINGS_VALUE_SHOW_BOTH: update_vl_string (v); return ds_cstr (&v->name_and_label); - break; - default: - NOT_REACHED (); - break; - }; + } } /* Returns V's variable label, or a null pointer if it has none. */ @@ -800,15 +810,17 @@ var_has_label (const struct variable *v) bool measure_is_valid (enum measure m) { - return m == MEASURE_NOMINAL || m == MEASURE_ORDINAL || m == MEASURE_SCALE; + return (m == MEASURE_UNKNOWN || m == MEASURE_NOMINAL + || m == MEASURE_ORDINAL || m == MEASURE_SCALE); } -/* Returns a string version of measurement level M, for display to a user. */ +/* Returns a string version of measurement level M, for display to a user. + The caller may translate the string by passing it to gettext(). */ const char * measure_to_string (enum measure m) { assert (m == measure[m].value); - return gettext (measure[m].label); + return measure[m].label; } /* Returns a string version of measurement level M, for use in PSPP command @@ -863,9 +875,38 @@ var_set_measure (struct variable *v, enum measure measure) used to reset a variable's measurement level to the default. */ enum measure -var_default_measure (enum val_type type) +var_default_measure_for_type (enum val_type type) +{ + return type == VAL_NUMERIC ? MEASURE_UNKNOWN : MEASURE_NOMINAL; +} + +/* Returns the default measurement level for a variable with the given + FORMAT, or MEASURE_UNKNOWN if there is no good default. */ +enum measure +var_default_measure_for_format (enum fmt_type format) { - return type == VAL_NUMERIC ? MEASURE_SCALE : MEASURE_NOMINAL; + if (format == FMT_DOLLAR) + return MEASURE_SCALE; + + switch (fmt_get_category (format)) + { + case FMT_CAT_BASIC: + case FMT_CAT_LEGACY: + case FMT_CAT_BINARY: + case FMT_CAT_HEXADECIMAL: + return MEASURE_UNKNOWN; + + case FMT_CAT_CUSTOM: + case FMT_CAT_DATE: + case FMT_CAT_TIME: + return MEASURE_SCALE; + + case FMT_CAT_DATE_COMPONENT: + case FMT_CAT_STRING: + return MEASURE_NOMINAL; + } + + NOT_REACHED (); } /* Returns true if M is a valid variable role, @@ -888,12 +929,13 @@ var_role_is_valid (enum var_role role) } } -/* Returns a string version of ROLE, for display to a user. */ +/* Returns a string version of ROLE, for display to a user. + The caller may translate the string by passing it to gettext(). */ const char * var_role_to_string (enum var_role r) { assert (r == role[r].value); - return gettext (role[r].label); + return role[r].label; } /* Returns a string version of ROLE, for use in PSPP comamnd syntax. */ @@ -995,12 +1037,13 @@ alignment_is_valid (enum alignment a) return a == ALIGN_LEFT || a == ALIGN_RIGHT || a == ALIGN_CENTRE; } -/* Returns a string version of alignment A, for display to a user. */ +/* Returns a string version of alignment A, for display to a user. + The caller may translate the string by passing it to gettext(). */ const char * alignment_to_string (enum alignment a) { assert (a == align[a].value); - return gettext (align[a].label); + return align[a].label; } /* Returns a string version of alignment A, for use in PSPP command syntax. */ @@ -1106,9 +1149,9 @@ var_must_leave (const struct variable *v) all if it hasn't been saved to or read from a system or portable file. */ size_t -var_get_short_name_cnt (const struct variable *var) +var_get_n_short_names (const struct variable *var) { - return var->short_name_cnt; + return var->n_short_names; } /* Returns VAR's short name with the given IDX, if it has one @@ -1118,7 +1161,7 @@ var_get_short_name_cnt (const struct variable *var) const char * var_get_short_name (const struct variable *var, size_t idx) { - return idx < var->short_name_cnt ? var->short_names[idx] : NULL; + return idx < var->n_short_names ? var->short_names[idx] : NULL; } /* Sets VAR's short name with the given IDX to the UTF-8 string SHORT_NAME. @@ -1132,26 +1175,24 @@ var_set_short_name (struct variable *var, size_t idx, const char *short_name) { struct variable *ov = var_clone (var); - assert (short_name == NULL || id_is_plausible (short_name, false)); - /* Clear old short name numbered IDX, if any. */ - if (idx < var->short_name_cnt) + if (idx < var->n_short_names) { free (var->short_names[idx]); - var->short_names[idx] = NULL; + var->short_names[idx] = NULL; } /* Install new short name for IDX. */ - if (short_name != NULL) + if (short_name != NULL) { - if (idx >= var->short_name_cnt) + if (idx >= var->n_short_names) { - size_t old_cnt = var->short_name_cnt; + size_t n_old = var->n_short_names; size_t i; - var->short_name_cnt = MAX (idx * 2, 1); - var->short_names = xnrealloc (var->short_names, var->short_name_cnt, + var->n_short_names = MAX (idx * 2, 1); + var->short_names = xnrealloc (var->short_names, var->n_short_names, sizeof *var->short_names); - for (i = old_cnt; i < var->short_name_cnt; i++) + for (i = n_old; i < var->n_short_names; i++) var->short_names[i] = NULL; } var->short_names[idx] = utf8_to_upper (short_name); @@ -1166,11 +1207,11 @@ var_clear_short_names (struct variable *v) { size_t i; - for (i = 0; i < v->short_name_cnt; i++) + for (i = 0; i < v->n_short_names; i++) free (v->short_names[i]); free (v->short_names); v->short_names = NULL; - v->short_name_cnt = 0; + v->n_short_names = 0; } /* Relationship with dictionary. */ @@ -1201,14 +1242,14 @@ var_get_case_index (const struct variable *v) V, or calling var_set_attributes() on V, will also destroy its attribute set. */ struct attrset * -var_get_attributes (const struct variable *v) +var_get_attributes (const struct variable *v) { return CONST_CAST (struct attrset *, &v->attributes); } /* Replaces variable V's attributes set by a copy of ATTRS. */ static void -var_set_attributes_quiet (struct variable *v, const struct attrset *attrs) +var_set_attributes_quiet (struct variable *v, const struct attrset *attrs) { attrset_destroy (&v->attributes); attrset_clone (&v->attributes, attrs); @@ -1216,7 +1257,7 @@ var_set_attributes_quiet (struct variable *v, const struct attrset *attrs) /* Replaces variable V's attributes set by a copy of ATTRS. */ void -var_set_attributes (struct variable *v, const struct attrset *attrs) +var_set_attributes (struct variable *v, const struct attrset *attrs) { struct variable *ov = var_clone (v); var_set_attributes_quiet (v, attrs); @@ -1303,3 +1344,29 @@ var_clear_vardict (struct variable *v) { v->vardict = NULL; } + + +/* + Returns zero, if W is a missing value for WV or if it is less than zero. + Typically used to force a numerical value into a valid weight. + + As a side effect, this function will emit a warning if the value + WARN_ON_INVALID points to a bool which is TRUE. That bool will be then + set to FALSE. + */ +double +var_force_valid_weight (const struct variable *wv, double w, bool *warn_on_invalid) +{ + if (w < 0.0 || (wv && var_is_num_missing (wv, w))) + w = 0.0; + + 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 " + "negative. These case(s) were ignored.")); + } + + return w; +}