Added new files resulting from directory restructuring.
[pspp-builds.git] / src / data / value-labels.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "value-labels.h"
22 #include "message.h"
23 #include <stdlib.h>
24 #include "alloc.h"
25 #include "hash.h"
26 #include "str.h"
27
28 static hsh_compare_func compare_int_val_lab;
29 static hsh_hash_func hash_int_val_lab;
30 static hsh_free_func free_int_val_lab;
31
32 struct atom;
33 static struct atom *atom_create (const char *string);
34 static void atom_destroy (struct atom *);
35 static char *atom_to_string (const struct atom *);
36
37 /* A set of value labels. */
38 struct val_labs 
39   {
40     int width;                  /* 0=numeric, otherwise string width. */
41     struct hsh_table *labels;   /* Hash table of `struct int_val_lab's. */
42   };
43
44 /* Creates and returns a new, empty set of value labels with the
45    given WIDTH, which must designate a numeric (0) or short
46    string (1...MAX_SHORT_STRING inclusive) width. */
47 struct val_labs *
48 val_labs_create (int width) 
49 {
50   struct val_labs *vls;
51
52   assert (width >= 0);
53
54   vls = xmalloc (sizeof *vls);
55   vls->width = width;
56   vls->labels = NULL;
57   return vls;
58 }
59
60 /* Creates and returns a new set of value labels identical to
61    VLS. */
62 struct val_labs *
63 val_labs_copy (const struct val_labs *vls) 
64 {
65   struct val_labs *copy;
66   struct val_labs_iterator *i;
67   struct val_lab *vl;
68
69   assert (vls != NULL);
70
71   copy = val_labs_create (vls->width);
72   for (vl = val_labs_first (vls, &i); vl != NULL;
73        vl = val_labs_next (vls, &i)) 
74     val_labs_add (copy, vl->value, vl->label);
75   return copy;
76 }
77
78 /* Changes the width of VLS to NEW_WIDTH.  If VLS is numeric,
79    NEW_WIDTH must be 0, otherwise it must be within the range
80    1...MAX_SHORT_STRING inclusive. */
81 void
82 val_labs_set_width (struct val_labs *vls, int new_width) 
83 {
84   assert (vls != NULL);
85   assert ((vls->width == 0) == (new_width == 0));
86
87   vls->width = new_width;
88 }
89
90 /* Destroys VLS. */
91 void
92 val_labs_destroy (struct val_labs *vls) 
93 {
94   if (vls != NULL) 
95     {
96       if (vls->labels != NULL)
97         hsh_destroy (vls->labels);
98       free (vls);
99     }
100 }
101
102 /* Removes all the value labels from VLS. */
103 void
104 val_labs_clear (struct val_labs *vls) 
105 {
106   assert (vls != NULL);
107
108   hsh_destroy (vls->labels);
109   vls->labels = NULL;
110 }
111
112 /* Returns the number of value labels in VLS. */
113 size_t
114 val_labs_count (const struct val_labs *vls) 
115 {
116   assert (vls != NULL);
117
118   if (vls->labels == NULL)
119     return 0;
120   else
121     return hsh_count (vls->labels);
122 }
123 \f
124 /* One value label in internal format. */
125 struct int_val_lab
126   {
127     union value value;          /* The value being labeled. */
128     struct atom *label;         /* A ref-counted string. */
129   };
130
131 /* Creates and returns an int_val_lab based on VALUE and
132    LABEL. */
133 static struct int_val_lab *
134 create_int_val_lab (struct val_labs *vls, union value value, const char *label) 
135 {
136   struct int_val_lab *ivl;
137
138   assert (label != NULL);
139   assert (vls->width <= MAX_SHORT_STRING);
140   
141   ivl = xmalloc (sizeof *ivl);
142   ivl->value = value;
143   if (vls->width > 0)
144     memset (ivl->value.s + vls->width, ' ', MAX_SHORT_STRING - vls->width);
145   ivl->label = atom_create (label);
146
147   return ivl;
148 }
149
150 /* If VLS does not already contain a value label for VALUE, adds
151    LABEL for it and returns nonzero.  Otherwise, returns zero.
152    Behavior is undefined if VLS's width is greater than
153    MAX_SHORT_STRING. */
154 int
155 val_labs_add (struct val_labs *vls, union value value, const char *label) 
156 {
157   struct int_val_lab *ivl;
158   void **vlpp;
159
160   assert (vls != NULL);
161   assert (vls->width <= MAX_SHORT_STRING);
162   assert (label != NULL);
163
164   if (vls->labels == NULL) 
165     vls->labels = hsh_create (8, compare_int_val_lab, hash_int_val_lab,
166                               free_int_val_lab, vls);
167
168   ivl = create_int_val_lab (vls, value, label);
169   vlpp = hsh_probe (vls->labels, ivl);
170   if (*vlpp == NULL) 
171     {
172       *vlpp = ivl;
173       return 1; 
174     }
175   else 
176     {
177       free_int_val_lab (ivl, vls);
178       return 0;
179     }
180 }
181
182 /* Sets LABEL as the value label for VALUE in VLS.  Returns zero
183    if there wasn't already a value label for VALUE, or nonzero if
184    there was.  Behavior is undefined if VLS's width is greater
185    than MAX_SHORT_STRING. */
186 int
187 val_labs_replace (struct val_labs *vls, union value value, const char *label) 
188 {
189   struct int_val_lab *ivl;
190
191   assert (vls != NULL);
192   assert (vls->width <= MAX_SHORT_STRING);
193   assert (label != NULL);
194
195   if (vls->labels == NULL)
196     {
197       val_labs_add (vls, value, label);
198       return 0;
199     }
200
201   ivl = hsh_replace (vls->labels, create_int_val_lab (vls, value, label));
202   if (ivl == NULL) 
203     return 0;
204   else 
205     {
206       free_int_val_lab (ivl, vls);
207       return 1;
208     }
209 }
210
211 /* Removes any value label for VALUE within VLS.  Returns nonzero
212    if a value label was removed. Behavior is undefined if VLS's
213    width is greater than MAX_SHORT_STRING. */
214 int 
215 val_labs_remove (struct val_labs *vls, union value value) 
216 {
217   assert (vls != NULL);
218   assert (vls->width <= MAX_SHORT_STRING);
219
220   if (vls->labels != NULL) 
221     {
222       struct int_val_lab *ivl = create_int_val_lab (vls, value, "");
223       int deleted = hsh_delete (vls->labels, ivl);
224       free (ivl);
225       return deleted;
226     }
227   else
228     return 0;
229 }
230
231 /* Searches VLS for a value label for VALUE.  If successful,
232    returns the label; otherwise, returns a null pointer.  If
233    VLS's width is greater than MAX_SHORT_STRING, always returns a
234    null pointer. */
235 char *
236 val_labs_find (const struct val_labs *vls, union value value) 
237 {
238   assert (vls != NULL);
239
240   if (vls->width > MAX_SHORT_STRING)
241     return NULL;
242
243   if (vls->labels != NULL) 
244     {
245       struct int_val_lab ivl, *vlp;
246
247       ivl.value = value;
248       vlp = hsh_find (vls->labels, &ivl);
249       if (vlp != NULL)
250         return atom_to_string (vlp->label);
251     }
252   return NULL;
253 }
254 \f
255 /* A value labels iterator. */
256 struct val_labs_iterator 
257   {
258     void **labels;              /* The labels, in order. */
259     void **lp;                  /* Current label. */
260     struct val_lab vl;          /* Structure presented to caller. */
261   };
262
263 /* Sets up *IP for iterating through the value labels in VLS in
264    no particular order.  Returns the first value label or a null
265    pointer if VLS is empty.  If the return value is non-null,
266    then val_labs_next() may be used to continue iterating or
267    val_labs_done() to free up the iterator.  Otherwise, neither
268    function may be called for *IP. */
269 struct val_lab *
270 val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip) 
271 {
272   struct val_labs_iterator *i;
273
274   assert (vls != NULL);
275   assert (ip != NULL);
276
277   if (vls->labels == NULL || vls->width > MAX_SHORT_STRING)
278     return NULL;
279
280   i = *ip = xmalloc (sizeof *i);
281   i->labels = hsh_data_copy (vls->labels);
282   i->lp = i->labels;
283   return val_labs_next (vls, ip);
284 }
285
286 /* Sets up *IP for iterating through the value labels in VLS in
287    sorted order of values.  Returns the first value label or a
288    null pointer if VLS is empty.  If the return value is
289    non-null, then val_labs_next() may be used to continue
290    iterating or val_labs_done() to free up the iterator.
291    Otherwise, neither function may be called for *IP. */
292 struct val_lab *
293 val_labs_first_sorted (const struct val_labs *vls,
294                        struct val_labs_iterator **ip)
295 {
296   struct val_labs_iterator *i;
297
298   assert (vls != NULL);
299   assert (ip != NULL);
300
301   if (vls->labels == NULL || vls->width > MAX_SHORT_STRING)
302     return NULL;
303
304   i = *ip = xmalloc (sizeof *i);
305   i->lp = i->labels = hsh_sort_copy (vls->labels);
306   return val_labs_next (vls, ip);
307 }
308
309 /* Returns the next value label in an iteration begun by
310    val_labs_first() or val_labs_first_sorted().  If the return
311    value is non-null, then val_labs_next() may be used to
312    continue iterating or val_labs_done() to free up the iterator.
313    Otherwise, neither function may be called for *IP. */
314 struct val_lab *
315 val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip)
316 {
317   struct val_labs_iterator *i;
318   struct int_val_lab *ivl;
319   
320   assert (vls != NULL);
321   assert (vls->width <= MAX_SHORT_STRING);
322   assert (ip != NULL);
323   assert (*ip != NULL);
324
325   i = *ip;
326   ivl = *i->lp++;
327   if (ivl != NULL) 
328     {
329       i->vl.value = ivl->value;
330       i->vl.label = atom_to_string (ivl->label);
331       return &i->vl;
332     }
333   else 
334     {
335       free (i->labels);
336       free (i);
337       *ip = NULL;
338       return NULL;
339     }
340 }
341
342 /* Discards the state for an incomplete iteration begun by
343    val_labs_first() or val_labs_first_sorted(). */
344 void 
345 val_labs_done (struct val_labs_iterator **ip) 
346 {
347   struct val_labs_iterator *i;
348
349   assert (ip != NULL);
350   assert (*ip != NULL);
351   
352   i = *ip;
353   free (i->labels);
354   free (i);
355   *ip = NULL;
356 }
357 \f
358 /* Compares two value labels and returns a strcmp()-type result. */
359 int
360 compare_int_val_lab (const void *a_, const void *b_, void *vls_)
361 {
362   const struct int_val_lab *a = a_;
363   const struct int_val_lab *b = b_;
364   const struct val_labs *vls = vls_;
365
366   if (vls->width == 0) 
367     return a->value.f < b->value.f ? -1 : a->value.f > b->value.f;
368   else
369     return memcmp (a->value.s, b->value.s, vls->width);
370 }
371
372 /* Hash a value label. */
373 unsigned
374 hash_int_val_lab (const void *vl_, void *vls_)
375 {
376   const struct int_val_lab *vl = vl_;
377   const struct val_labs *vls = vls_;
378
379   if (vls->width == 0)
380     return hsh_hash_double (vl->value.f);
381   else
382     return hsh_hash_bytes (vl->value.s, sizeof vl->value.s);
383 }
384
385 /* Free a value label. */
386 void
387 free_int_val_lab (void *vl_, void *vls_ UNUSED) 
388 {
389   struct int_val_lab *vl = vl_;
390
391   atom_destroy (vl->label);
392   free (vl);
393 }
394 \f
395 /* Atoms. */
396
397 /* An atom. */
398 struct atom 
399   {
400     char *string;               /* String value. */
401     unsigned ref_count;         /* Number of references. */
402   };
403
404 static hsh_compare_func compare_atoms;
405 static hsh_hash_func hash_atom;
406 static hsh_free_func free_atom;
407
408 /* Hash table of atoms. */
409 static struct hsh_table *atoms;
410
411 /* Creates and returns an atom for STRING. */
412 static struct atom *
413 atom_create (const char *string) 
414 {
415   struct atom a;
416   void **app;
417   
418   assert (string != NULL);
419           
420   if (atoms == NULL) 
421     atoms = hsh_create (8, compare_atoms, hash_atom, free_atom, NULL);
422
423   a.string = (char *) string;
424   app = hsh_probe (atoms, &a);
425   if (*app != NULL) 
426     {
427       struct atom *ap = *app;
428       ap->ref_count++;
429       return ap;
430     }
431   else
432     {
433       struct atom *ap = xmalloc (sizeof *ap);
434       ap->string = xstrdup (string);
435       ap->ref_count = 1;
436       *app = ap;
437       return ap;
438     }
439 }
440
441 /* Destroys ATOM. */
442 static void 
443 atom_destroy (struct atom *atom)
444 {
445   if (atom != NULL) 
446     {
447       assert (atom->ref_count > 0);
448       atom->ref_count--;
449       if (atom->ref_count == 0) 
450         hsh_force_delete (atoms, atom);
451     }
452 }
453
454 /* Returns the string associated with ATOM. */
455 static  char *
456 atom_to_string (const struct atom *atom) 
457 {
458   assert (atom != NULL);
459   
460   return atom->string;
461 }
462
463 /* A hsh_compare_func that compares A and B. */
464 static int
465 compare_atoms (const void *a_, const void *b_, void *aux UNUSED) 
466 {
467   const struct atom *a = a_;
468   const struct atom *b = b_;
469
470   return strcmp (a->string, b->string);
471 }
472
473 /* A hsh_hash_func that hashes ATOM. */
474 static unsigned
475 hash_atom (const void *atom_, void *aux UNUSED) 
476 {
477   const struct atom *atom = atom_;
478
479   return hsh_hash_string (atom->string);
480 }
481
482 /* A hsh_free_func that destroys ATOM. */
483 static void
484 free_atom (void *atom_, void *aux UNUSED) 
485 {
486   struct atom *atom = atom_;
487
488   free (atom->string);
489   free (atom);
490 }
491
492
493 /* Get a string representing the value.
494    That is, if it has a label, then return that label,
495    otherwise, if the value is alpha, then return the string for it,
496    else format it and return the formatted string
497 */
498 const char *
499 value_to_string (const union value *val, const struct variable *var)
500 {
501   char *s;
502   
503   assert (val != NULL);
504   assert (var != NULL);
505
506   s = val_labs_find (var->val_labs, *val);
507   if (s == NULL) 
508     {
509       static char buf[256];
510       if (var->width != 0) 
511         str_copy_buf_trunc (buf, sizeof buf, val->s, var->width);
512       else
513         snprintf(buf, 100, "%g", val->f);
514       s = buf;
515     }
516   
517   return s;
518 }