/* PSPP - a program for statistical analysis. Copyright (C) 2008, 2009, 2011, 2012, 2016 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 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "data/attributes.h" #include #include #include "libpspp/array.h" #include "libpspp/compiler.h" #include "libpspp/hash-functions.h" #include "libpspp/i18n.h" #include "gl/xalloc.h" /* A custom attribute of the sort maintained by the DATAFILE ATTRIBUTE and VARIABLE ATTRIBUTE commands. Attributes have a name (the rules for which are the same as those for PSPP variable names) and one or more values, each of which is a string. An attribute may be part of one attribute set. */ struct attribute { struct hmap_node node; /* Used by attrset. */ char *name; /* Name. */ char **values; /* Each value. */ size_t n_values; /* Number of values. */ size_t allocated_values; /* Amount of allocated space for values. */ }; /* Creates and returns a new attribute with the given NAME. The attribute initially has no values. (Attributes with no values cannot be saved to system files, so at least one value should be added before the attribute is made available to the PSPP user.) */ struct attribute * attribute_create (const char *name) { struct attribute *attr = xmalloc (sizeof *attr); attr->name = xstrdup (name); attr->values = NULL; attr->n_values = 0; attr->allocated_values = 0; return attr; } /* Creates and returns a new attribute with the same name and values as ORIG. */ struct attribute * attribute_clone (const struct attribute *orig) { struct attribute *attr; size_t i; attr = attribute_create (orig->name); for (i = 0; i < orig->n_values; i++) attribute_add_value (attr, orig->values[i]); return attr; } /* Destroys ATTR and frees all associated memory. This function must not be called if ATTR is part of an attribute set. Use attrset_delete() instead. */ void attribute_destroy (struct attribute *attr) { if (attr != NULL) { size_t i; for (i = 0; i < attr->n_values; i++) free (attr->values[i]); free (attr->values); free (attr->name); free (attr); } } /* Returns the name of ATTR. The caller must not free or modify the returned string. */ const char * attribute_get_name (const struct attribute *attr) { return attr->name; } /* Returns ATTR's value with the given INDEX, or a null pointer if INDEX is greater than or equal to the number of values in ATTR (that is, attributes are numbered starting from 0). The caller must not free or modify the returned string. */ const char * attribute_get_value (const struct attribute *attr, size_t index) { return index < attr->n_values ? attr->values[index] : NULL; } /* Returns ATTR's number of values. */ size_t attribute_get_n_values (const struct attribute *attrs) { return attrs->n_values; } /* Adds a copy of VALUE as a new value to ATTR. The caller retains ownership of VALUE. */ void attribute_add_value (struct attribute *attr, const char *value) { if (attr->n_values>= attr->allocated_values) attr->values = x2nrealloc (attr->values, &attr->allocated_values, sizeof *attr->values); attr->values[attr->n_values++] = xstrdup (value); } /* Adds or replaces the value with the given INDEX in ATTR by a copy of VALUE. The caller retains ownership of VALUE. If INDEX is an existing value index, that value is replaced. If no value index numbered INDEX exists in ATTR, then it is added, and any values intermediate between the last maximum index and INDEX are set to the empty string. */ void attribute_set_value (struct attribute *attr, size_t index, const char *value) { if (index < attr->n_values) { /* Replace existing value. */ free (attr->values[index]); attr->values[index] = xstrdup (value); } else { /* Add new value. */ while (index> attr->n_values) attribute_add_value (attr, ""); attribute_add_value (attr, value); } } /* Deletes the value with the given INDEX from ATTR. Any values with higher-numbered indexes are shifted down into the gap that this creates. If INDEX is greater than the maximum index, this has no effect.*/ void attribute_del_value (struct attribute *attr, size_t index) { if (index < attr->n_values) { free (attr->values[index]); remove_element (attr->values, attr->n_values, sizeof *attr->values, index); attr->n_values--; } } /* Initializes SET as a new, initially empty attibute set. */ void attrset_init (struct attrset *set) { hmap_init (&set->map); } /* Initializes NEW_SET as a new attribute set whose contents are initially the same as that of OLD_SET. */ void attrset_clone (struct attrset *new_set, const struct attrset *old_set) { struct attribute *old_attr; attrset_init (new_set); HMAP_FOR_EACH (old_attr, struct attribute, node, &old_set->map) { struct attribute *new_attr = attribute_clone (old_attr); hmap_insert (&new_set->map, &new_attr->node, hmap_node_hash (&old_attr->node)); } } /* Frees the storage associated with SET, if SET is nonnull. (Does not free SET itself.) */ void attrset_destroy (struct attrset *set) { if (set != NULL) { struct attribute *attr, *next; HMAP_FOR_EACH_SAFE (attr, next, struct attribute, node, &set->map) attribute_destroy (attr); hmap_destroy (&set->map); } } /* Returns the number of attributes in SET. */ size_t attrset_count (const struct attrset *set) { return hmap_count (&set->map); } /* Returns the attribute in SET whose name matches NAME case-insensitively, or a null pointer if SET does not contain an attribute with that name. */ struct attribute * attrset_lookup (const struct attrset *set, const char *name) { const struct attribute *attr; HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node, utf8_hash_case_string (name, 0), &set->map) if (!utf8_strcasecmp (attribute_get_name (attr), name)) break; return CONST_CAST (struct attribute *, attr); } /* Adds ATTR to SET. Succeeds and returns true if SET does not already contain an attribute with the same name (matched case insensitively); otherwise fails and returns false. On success only, ownership of ATTR is transferred to SET. */ bool attrset_try_add (struct attrset *set, struct attribute *attr) { const char *name = attribute_get_name (attr); if (attrset_lookup (set, name)) return false; hmap_insert (&set->map, &attr->node, utf8_hash_case_string (name, 0)); return true; } /* Adds ATTR to SET, which must not already contain an attribute with the same name (matched case insensitively). Ownership of ATTR is transferred to SET. */ void attrset_add (struct attrset *set, struct attribute *attr) { bool ok UNUSED = attrset_try_add (set, attr); assert (ok); } /* Deletes any attribute from SET that matches NAME (case-insensitively). */ void attrset_delete (struct attrset *set, const char *name) { struct attribute *attr = attrset_lookup (set, name); if (attr != NULL) { hmap_delete (&set->map, &attr->node); attribute_destroy (attr); } } /* Deletes all attributes from SET. */ void attrset_clear (struct attrset *set) { attrset_destroy (set); attrset_init (set); } static struct attribute *iterator_data (struct attrset_iterator *iterator) { return HMAP_NULLABLE_DATA (iterator->node, struct attribute, node); } /* Returns the first attribute in SET and initializes ITERATOR. If SET is empty, returns a null pointer. The caller must not destroy the returned attribute, but it may add or remove values. Attributes are visited in no particular order. Calling attrset_add() during iteration can cause some attributes to be visited more than once and others not at all. */ struct attribute * attrset_first (const struct attrset *set, struct attrset_iterator *iterator) { iterator->node = hmap_first (&set->map); return iterator_data (iterator); } /* Returns the next attribute in SET and advances ITERATOR, which should have been initialized by calling attrset_first(). If all the attributes in SET have already been visited, returns a null pointer. The caller must not destroy the returned attribute, but it may add or remove values. Attributes are visited in no particular order. Calling attrset_add() during iteration can cause some attributes to be visited more than once and others not at all. */ struct attribute * attrset_next (const struct attrset *set, struct attrset_iterator *iterator) { iterator->node = hmap_next (&set->map, iterator->node); return iterator_data (iterator); } static int compare_attribute_by_name (const void *a_, const void *b_) { const struct attribute *const *a = a_; const struct attribute *const *b = b_; return strcmp ((*a)->name, (*b)->name); } /* Allocates and returns an array of pointers to attributes that is sorted by attribute name. The array has 'attrset_count (SET)' elements. The caller is responsible for freeing the array. */ struct attribute ** attrset_sorted (const struct attrset *set) { if (set != NULL && attrset_count (set)> 0) { struct attribute **attrs; struct attribute *attr; size_t i; attrs = xmalloc (attrset_count (set) * sizeof *attrs); i = 0; HMAP_FOR_EACH (attr, struct attribute, node, &set->map) attrs[i++] = attr; assert (i == attrset_count (set)); qsort (attrs, attrset_count (set), sizeof *attrs, compare_attribute_by_name); return attrs; } else return NULL; }

AltStyle によって変換されたページ (->オリジナル) /