-/* PSPP - computes sample statistics.
+/* PSPP - a program for statistical analysis.
Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
- 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 <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdlib.h>
#include <data/data-out.h>
-#include <data/format.h>
#include <data/value.h>
#include <data/variable.h>
-#include <libpspp/alloc.h>
#include <libpspp/compiler.h>
#include <libpspp/hash.h>
#include <libpspp/message.h>
#include <libpspp/str.h>
+#include "xalloc.h"
+
static hsh_compare_func compare_int_val_lab;
static hsh_hash_func hash_int_val_lab;
static hsh_free_func free_int_val_lab;
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. */
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;
/* 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;
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;
}
-/* Determines whether VLS's width can be changed to NEW_WIDTH.
- Numeric widths cannot be changed at all.
- Strings can be widened. They can be shortened only if the
- characters that will be truncated are spaces. */
+/* 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)
+val_labs_can_set_width (const struct val_labs *vls, int new_width)
{
- assert ((vls->width == 0) == (new_width == 0));
+ struct val_labs_iterator *i;
+ struct val_lab *lab;
- if (vls->width == 0)
- return new_width == 0;
- else if (new_width < vls->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;
+ }
- for (lab = val_labs_first (vls, &i); lab != NULL;
- lab = val_labs_next (vls, &i))
- {
- int j;
-
- /* We can shorten the value labels only if all the
- truncated characters are blanks. */
- for (j = vls->width; j < new_width; j++)
- if (lab->value.s[j] != ' ')
- {
- val_labs_done (&i);
- return false;
- }
- }
- return true;
- }
- else
- return true;
+ return true;
}
-/* 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. */
+/* 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 (val_labs_can_set_width (vls, new_width));
/* Destroys VLS. */
void
-val_labs_destroy (struct val_labs *vls)
+val_labs_destroy (struct val_labs *vls)
{
- if (vls != NULL)
+ if (vls != NULL)
{
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);
/* 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)
{
return vls == NULL || vls->labels == NULL ? 0 : hsh_count (vls->labels);
}
/* 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)
return ivl;
}
-/* If VLS does not already contain a value label for VALUE, adds
- LABEL for it and returns true. Otherwise, returns false.
- Behavior is undefined if VLS's width is greater than
- MAX_SHORT_STRING. */
+/* 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)
+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->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);
+ 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;
+ 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);
}
- free_int_val_lab (ivl, vls);
return false;
}
-/* Sets LABEL as the value label for VALUE in VLS. Returns false
- if there wasn't already a value label for VALUE, or true if
- there was. Behavior is undefined if VLS's width is greater
- than MAX_SHORT_STRING. */
+/* 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)
+val_labs_replace (struct val_labs *vls, union value value, const char *label)
{
- assert (vls->width <= MAX_SHORT_STRING);
- if (vls->labels != NULL)
+ if (vls->width < MIN_LONG_STRING)
{
- 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);
+ 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);
}
- else
- val_labs_add (vls, value, label);
}
/* Removes any value label for VALUE within VLS. Returns true
- if a value label was removed. Behavior is undefined if VLS's
- width is greater than MAX_SHORT_STRING. */
+ if a value label was removed. */
bool
-val_labs_remove (struct val_labs *vls, union value value)
+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);
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)
{
if (vls != NULL
&& vls->width <= MAX_SHORT_STRING
}
\f
/* A value labels iterator. */
-struct val_labs_iterator
+struct val_labs_iterator
{
void **labels; /* The labels, in order. */
void **lp; /* Current label. */
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;
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);
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);
{
struct val_labs_iterator *i;
struct int_val_lab *ivl;
-
+
assert (vls != NULL);
assert (vls->width <= MAX_SHORT_STRING);
assert (ip != NULL);
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);
/* 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;
+ }
}
\f
/* Compares two value labels and returns a strcmp()-type result. */
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);
if (vls->width == 0)
return hsh_hash_double (vl->value.f);
else
- return hsh_hash_bytes (vl->value.s, sizeof vl->value.s);
+ return hsh_hash_bytes (vl->value.s, vls->width);
}
/* Free a value label. */
void
-free_int_val_lab (void *vl_, const void *vls_ UNUSED)
+free_int_val_lab (void *vl_, const void *vls_ UNUSED)
{
struct int_val_lab *vl = vl_;
/* Atoms. */
/* An atom. */
-struct atom
+struct atom
{
char *string; /* String value. */
unsigned ref_count; /* Number of references. */
/* 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)
+
+ 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++;
}
/* 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_, const 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_;
/* A hsh_hash_func that hashes ATOM. */
static unsigned
-hash_atom (const void *atom_, const void *aux UNUSED)
+hash_atom (const void *atom_, const void *aux UNUSED)
{
const struct atom *atom = atom_;
/* A hsh_free_func that destroys ATOM. */
static void
-free_atom (void *atom_, const void *aux UNUSED)
+free_atom (void *atom_, const void *aux UNUSED)
{
struct atom *atom = atom_;