X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fvalue-labels.c;h=9f6113b388576ee7c822380db69de9dc7fabc361;hb=85e74b3e36d5673205cb64345bca2587889f2113;hp=4638bd3fe6038387da8650acce2eff754e5e36c7;hpb=dcf9b154cbcaa35c3d8459a201b77eec8bcb30bd;p=pspp-builds.git diff --git a/src/data/value-labels.c b/src/data/value-labels.c index 4638bd3f..9f6113b3 100644 --- a/src/data/value-labels.c +++ b/src/data/value-labels.c @@ -1,29 +1,34 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009 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 + #include "value-labels.h" -#include "message.h" + #include -#include "alloc.h" -#include "hash.h" -#include "str.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "xalloc.h" static hsh_compare_func compare_int_val_lab; static hsh_hash_func hash_int_val_lab; @@ -35,17 +40,17 @@ static void atom_destroy (struct atom *); static char *atom_to_string (const struct atom *); /* A set of value labels. */ -struct val_labs +struct val_labs { int width; /* 0=numeric, otherwise string width. */ struct hsh_table *labels; /* Hash table of `struct int_val_lab's. */ }; /* Creates and returns a new, empty set of value labels with the - given WIDTH, which must designate a numeric (0) or short - string (1...MAX_SHORT_STRING inclusive) width. */ + given WIDTH. To actually add any value labels, WIDTH must be + a numeric or short string width. */ struct val_labs * -val_labs_create (int width) +val_labs_create (int width) { struct val_labs *vls; @@ -60,48 +65,69 @@ val_labs_create (int width) /* Creates and returns a new set of value labels identical to VLS. */ struct val_labs * -val_labs_copy (const struct val_labs *vls) +val_labs_clone (const struct val_labs *vls) { struct val_labs *copy; struct val_labs_iterator *i; struct val_lab *vl; - assert (vls != NULL); + if (vls == NULL) + return NULL; copy = val_labs_create (vls->width); for (vl = val_labs_first (vls, &i); vl != NULL; - vl = val_labs_next (vls, &i)) + vl = val_labs_next (vls, &i)) val_labs_add (copy, vl->value, vl->label); return copy; } -/* Changes the width of VLS to NEW_WIDTH. If VLS is numeric, - NEW_WIDTH must be 0, otherwise it must be within the range - 1...MAX_SHORT_STRING inclusive. */ +/* Determines whether VLS's width can be changed to NEW_WIDTH, + using the rules checked by value_is_resizable. */ +bool +val_labs_can_set_width (const struct val_labs *vls, int new_width) +{ + struct val_labs_iterator *i; + struct val_lab *lab; + + for (lab = val_labs_first (vls, &i); lab != NULL; + lab = val_labs_next (vls, &i)) + if (!value_is_resizable (&lab->value, vls->width, new_width)) + { + val_labs_done (&i); + return false; + } + + return true; +} + +/* Changes the width of VLS to NEW_WIDTH. The original and new + width must be both numeric or both string. If the new width + is a long string width, then any value labels in VLS are + deleted. */ void -val_labs_set_width (struct val_labs *vls, int new_width) +val_labs_set_width (struct val_labs *vls, int new_width) { - assert (vls != NULL); - assert ((vls->width == 0) == (new_width == 0)); + assert (val_labs_can_set_width (vls, new_width)); vls->width = new_width; + if (new_width > MAX_SHORT_STRING) + val_labs_clear (vls); } /* Destroys VLS. */ void -val_labs_destroy (struct val_labs *vls) +val_labs_destroy (struct val_labs *vls) { - if (vls != NULL) + if (vls != NULL) { - if (vls->labels != NULL) - hsh_destroy (vls->labels); + hsh_destroy (vls->labels); free (vls); } } /* Removes all the value labels from VLS. */ void -val_labs_clear (struct val_labs *vls) +val_labs_clear (struct val_labs *vls) { assert (vls != NULL); @@ -111,14 +137,9 @@ val_labs_clear (struct val_labs *vls) /* Returns the number of value labels in VLS. */ size_t -val_labs_count (const struct val_labs *vls) +val_labs_count (const struct val_labs *vls) { - assert (vls != NULL); - - if (vls->labels == NULL) - return 0; - else - return hsh_count (vls->labels); + return vls == NULL || vls->labels == NULL ? 0 : hsh_count (vls->labels); } /* One value label in internal format. */ @@ -131,13 +152,13 @@ struct int_val_lab /* Creates and returns an int_val_lab based on VALUE and LABEL. */ static struct int_val_lab * -create_int_val_lab (struct val_labs *vls, union value value, const char *label) +create_int_val_lab (struct val_labs *vls, union value value, const char *label) { struct int_val_lab *ivl; assert (label != NULL); assert (vls->width <= MAX_SHORT_STRING); - + ivl = xmalloc (sizeof *ivl); ivl->value = value; if (vls->width > 0) @@ -147,77 +168,61 @@ create_int_val_lab (struct val_labs *vls, union value value, const char *label) return ivl; } -/* If VLS does not already contain a value label for VALUE, adds - LABEL for it and returns nonzero. Otherwise, returns zero. - Behavior is undefined if VLS's width is greater than - MAX_SHORT_STRING. */ -int -val_labs_add (struct val_labs *vls, union value value, const char *label) +/* If VLS does not already contain a value label for VALUE (and + VLS represents a numeric or short string set of value labels), + adds LABEL for it and returns true. Otherwise, returns + false. */ +bool +val_labs_add (struct val_labs *vls, union value value, const char *label) { - struct int_val_lab *ivl; - void **vlpp; - - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); assert (label != NULL); - - if (vls->labels == NULL) - vls->labels = hsh_create (8, compare_int_val_lab, hash_int_val_lab, - free_int_val_lab, vls); - - ivl = create_int_val_lab (vls, value, label); - vlpp = hsh_probe (vls->labels, ivl); - if (*vlpp == NULL) - { - *vlpp = ivl; - return 1; - } - else + if (vls->width < MIN_LONG_STRING) { + struct int_val_lab *ivl; + void **vlpp; + + if (vls->labels == NULL) + vls->labels = hsh_create (8, compare_int_val_lab, hash_int_val_lab, + free_int_val_lab, vls); + + ivl = create_int_val_lab (vls, value, label); + vlpp = hsh_probe (vls->labels, ivl); + if (*vlpp == NULL) + { + *vlpp = ivl; + return true; + } free_int_val_lab (ivl, vls); - return 0; } + return false; } -/* Sets LABEL as the value label for VALUE in VLS. Returns zero - if there wasn't already a value label for VALUE, or nonzero if - there was. Behavior is undefined if VLS's width is greater - than MAX_SHORT_STRING. */ -int -val_labs_replace (struct val_labs *vls, union value value, const char *label) +/* Sets LABEL as the value label for VALUE in VLS, replacing any + existing label for VALUE. Has no effect if VLS has a long + string width. */ +void +val_labs_replace (struct val_labs *vls, union value value, const char *label) { - struct int_val_lab *ivl; - - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); - assert (label != NULL); - - if (vls->labels == NULL) - { - val_labs_add (vls, value, label); - return 0; - } - - ivl = hsh_replace (vls->labels, create_int_val_lab (vls, value, label)); - if (ivl == NULL) - return 0; - else + if (vls->width < MIN_LONG_STRING) { - free_int_val_lab (ivl, vls); - return 1; + if (vls->labels != NULL) + { + struct int_val_lab *new = create_int_val_lab (vls, value, label); + struct int_val_lab *old = hsh_replace (vls->labels, new); + if (old != NULL) + free_int_val_lab (old, vls); + } + else + val_labs_add (vls, value, label); } } -/* Removes any value label for VALUE within VLS. Returns nonzero - if a value label was removed. Behavior is undefined if VLS's - width is greater than MAX_SHORT_STRING. */ -int -val_labs_remove (struct val_labs *vls, union value value) +/* Removes any value label for VALUE within VLS. Returns true + if a value label was removed. */ +bool +val_labs_remove (struct val_labs *vls, union value value) { - assert (vls != NULL); - assert (vls->width <= MAX_SHORT_STRING); - - if (vls->labels != NULL) + if (vls->width < MIN_LONG_STRING && vls->labels != NULL) { struct int_val_lab *ivl = create_int_val_lab (vls, value, ""); int deleted = hsh_delete (vls->labels, ivl); @@ -225,7 +230,7 @@ val_labs_remove (struct val_labs *vls, union value value) return deleted; } else - return 0; + return false; } /* Searches VLS for a value label for VALUE. If successful, @@ -233,14 +238,11 @@ val_labs_remove (struct val_labs *vls, union value value) VLS's width is greater than MAX_SHORT_STRING, always returns a null pointer. */ char * -val_labs_find (const struct val_labs *vls, union value value) +val_labs_find (const struct val_labs *vls, union value value) { - assert (vls != NULL); - - if (vls->width > MAX_SHORT_STRING) - return NULL; - - if (vls->labels != NULL) + if (vls != NULL + && vls->width <= MAX_SHORT_STRING + && vls->labels != NULL) { struct int_val_lab ivl, *vlp; @@ -253,7 +255,7 @@ val_labs_find (const struct val_labs *vls, union value value) } /* A value labels iterator. */ -struct val_labs_iterator +struct val_labs_iterator { void **labels; /* The labels, in order. */ void **lp; /* Current label. */ @@ -267,7 +269,7 @@ struct val_labs_iterator val_labs_done() to free up the iterator. Otherwise, neither function may be called for *IP. */ struct val_lab * -val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip) +val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip) { struct val_labs_iterator *i; @@ -275,7 +277,10 @@ val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip) assert (ip != NULL); if (vls->labels == NULL || vls->width > MAX_SHORT_STRING) - return NULL; + { + *ip = NULL; + return NULL; + } i = *ip = xmalloc (sizeof *i); i->labels = hsh_data_copy (vls->labels); @@ -299,7 +304,10 @@ val_labs_first_sorted (const struct val_labs *vls, assert (ip != NULL); if (vls->labels == NULL || vls->width > MAX_SHORT_STRING) - return NULL; + { + *ip = NULL; + return NULL; + } i = *ip = xmalloc (sizeof *i); i->lp = i->labels = hsh_sort_copy (vls->labels); @@ -316,7 +324,7 @@ val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip) { struct val_labs_iterator *i; struct int_val_lab *ivl; - + assert (vls != NULL); assert (vls->width <= MAX_SHORT_STRING); assert (ip != NULL); @@ -324,13 +332,13 @@ val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip) i = *ip; ivl = *i->lp++; - if (ivl != NULL) + if (ivl != NULL) { i->vl.value = ivl->value; i->vl.label = atom_to_string (ivl->label); return &i->vl; } - else + else { free (i->labels); free (i); @@ -341,29 +349,27 @@ val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip) /* Discards the state for an incomplete iteration begun by val_labs_first() or val_labs_first_sorted(). */ -void -val_labs_done (struct val_labs_iterator **ip) +void +val_labs_done (struct val_labs_iterator **ip) { - struct val_labs_iterator *i; - - assert (ip != NULL); - assert (*ip != NULL); - - i = *ip; - free (i->labels); - free (i); - *ip = NULL; + if (*ip != NULL) + { + struct val_labs_iterator *i = *ip; + free (i->labels); + free (i); + *ip = NULL; + } } /* Compares two value labels and returns a strcmp()-type result. */ int -compare_int_val_lab (const void *a_, const void *b_, void *vls_) +compare_int_val_lab (const void *a_, const void *b_, const void *vls_) { const struct int_val_lab *a = a_; const struct int_val_lab *b = b_; const struct val_labs *vls = vls_; - if (vls->width == 0) + if (vls->width == 0) return a->value.f < b->value.f ? -1 : a->value.f > b->value.f; else return memcmp (a->value.s, b->value.s, vls->width); @@ -371,20 +377,20 @@ compare_int_val_lab (const void *a_, const void *b_, void *vls_) /* Hash a value label. */ unsigned -hash_int_val_lab (const void *vl_, void *vls_) +hash_int_val_lab (const void *vl_, const void *vls_) { const struct int_val_lab *vl = vl_; const struct val_labs *vls = vls_; if (vls->width == 0) - return hsh_hash_double (vl->value.f); + return hash_double (vl->value.f, 0); else - return hsh_hash_bytes (vl->value.s, sizeof vl->value.s); + return hash_bytes (vl->value.s, vls->width, 0); } /* Free a value label. */ void -free_int_val_lab (void *vl_, void *vls_ UNUSED) +free_int_val_lab (void *vl_, const void *vls_ UNUSED) { struct int_val_lab *vl = vl_; @@ -395,7 +401,7 @@ free_int_val_lab (void *vl_, void *vls_ UNUSED) /* Atoms. */ /* An atom. */ -struct atom +struct atom { char *string; /* String value. */ unsigned ref_count; /* Number of references. */ @@ -408,21 +414,30 @@ static hsh_free_func free_atom; /* Hash table of atoms. */ static struct hsh_table *atoms; +static void +destroy_atoms (void) +{ + hsh_destroy (atoms); +} + /* Creates and returns an atom for STRING. */ static struct atom * -atom_create (const char *string) +atom_create (const char *string) { struct atom a; void **app; - + assert (string != NULL); - - if (atoms == NULL) - atoms = hsh_create (8, compare_atoms, hash_atom, free_atom, NULL); + + if (atoms == NULL) + { + atoms = hsh_create (8, compare_atoms, hash_atom, free_atom, NULL); + atexit (destroy_atoms); + } a.string = (char *) string; app = hsh_probe (atoms, &a); - if (*app != NULL) + if (*app != NULL) { struct atom *ap = *app; ap->ref_count++; @@ -439,30 +454,30 @@ atom_create (const char *string) } /* Destroys ATOM. */ -static void +static void atom_destroy (struct atom *atom) { - if (atom != NULL) + if (atom != NULL) { assert (atom->ref_count > 0); atom->ref_count--; - if (atom->ref_count == 0) + if (atom->ref_count == 0) hsh_force_delete (atoms, atom); } } /* Returns the string associated with ATOM. */ static char * -atom_to_string (const struct atom *atom) +atom_to_string (const struct atom *atom) { assert (atom != NULL); - + return atom->string; } /* A hsh_compare_func that compares A and B. */ static int -compare_atoms (const void *a_, const void *b_, void *aux UNUSED) +compare_atoms (const void *a_, const void *b_, const void *aux UNUSED) { const struct atom *a = a_; const struct atom *b = b_; @@ -472,47 +487,19 @@ compare_atoms (const void *a_, const void *b_, void *aux UNUSED) /* A hsh_hash_func that hashes ATOM. */ static unsigned -hash_atom (const void *atom_, void *aux UNUSED) +hash_atom (const void *atom_, const void *aux UNUSED) { const struct atom *atom = atom_; - return hsh_hash_string (atom->string); + return hash_string (atom->string, 0); } /* A hsh_free_func that destroys ATOM. */ static void -free_atom (void *atom_, void *aux UNUSED) +free_atom (void *atom_, const void *aux UNUSED) { struct atom *atom = atom_; free (atom->string); free (atom); } - - -/* Get a string representing the value. - That is, if it has a label, then return that label, - otherwise, if the value is alpha, then return the string for it, - else format it and return the formatted string -*/ -const char * -value_to_string (const union value *val, const struct variable *var) -{ - char *s; - - assert (val != NULL); - assert (var != NULL); - - s = val_labs_find (var->val_labs, *val); - if (s == NULL) - { - static char buf[256]; - if (var->width != 0) - str_copy_buf_trunc (buf, sizeof buf, val->s, var->width); - else - snprintf(buf, 100, "%g", val->f); - s = buf; - } - - return s; -}