Clean up struct dictionary's value_cnt usage.
[pspp-builds.git] / src / dictionary.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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include "algorithm.h"
24 #include "alloc.h"
25 #include "hash.h"
26 #include "misc.h"
27 #include "str.h"
28 #include "value-labels.h"
29 #include "var.h"
30
31 /* A dictionary. */
32 struct dictionary
33   {
34     struct variable **var;      /* Variables. */
35     size_t var_cnt, var_cap;    /* Number of variables, capacity. */
36     struct hsh_table *name_tab; /* Variable index by name. */
37     int next_value_idx;         /* Index of next `union value' to allocate. */
38     struct variable **split;    /* SPLIT FILE vars. */
39     size_t split_cnt;           /* SPLIT FILE count. */
40     struct variable *weight;    /* WEIGHT variable. */
41     struct variable *filter;    /* FILTER variable. */
42     int case_limit;             /* Current case limit (N command). */
43     char *label;                /* File label. */
44     char *documents;            /* Documents, as a string. */
45     struct vector **vector;     /* Vectors of variables. */
46     size_t vector_cnt;          /* Number of vectors. */
47   };
48
49 /* Creates and returns a new dictionary. */
50 struct dictionary *
51 dict_create (void) 
52 {
53   struct dictionary *d = xmalloc (sizeof *d);
54   
55   d->var = NULL;
56   d->var_cnt = d->var_cap = 0;
57   d->name_tab = hsh_create (8, compare_variables, hash_variable, NULL, NULL);
58   d->next_value_idx = 0;
59   d->split = NULL;
60   d->split_cnt = 0;
61   d->weight = NULL;
62   d->filter = NULL;
63   d->case_limit = 0;
64   d->label = NULL;
65   d->documents = NULL;
66   d->vector = NULL;
67   d->vector_cnt = 0;
68
69   return d;
70 }
71
72 /* Creates and returns a (deep) copy of an existing
73    dictionary. */
74 struct dictionary *
75 dict_clone (const struct dictionary *s) 
76 {
77   struct dictionary *d;
78   size_t i;
79   
80   assert (s != NULL);
81   
82   d = dict_create ();
83   for (i = 0; i < s->var_cnt; i++)
84     dict_clone_var (d, s->var[i], s->var[i]->name);
85   d->next_value_idx = s->next_value_idx;
86
87   d->split_cnt = s->split_cnt;
88   if (d->split_cnt > 0) 
89     {
90       d->split = xmalloc (d->split_cnt * sizeof *d->split);
91       for (i = 0; i < d->split_cnt; i++) 
92         d->split[i] = dict_lookup_var_assert (d, s->split[i]->name);
93     }
94
95   if (s->weight != NULL) 
96     d->weight = dict_lookup_var_assert (d, s->weight->name);
97
98   if (s->filter != NULL) 
99     d->filter = dict_lookup_var_assert (d, s->filter->name);
100
101   d->case_limit = s->case_limit;
102   dict_set_label (d, dict_get_label (s));
103   dict_set_documents (d, dict_get_documents (s));
104
105   for (i = 0; i < s->vector_cnt; i++) 
106     dict_create_vector (d, s->vector[i]->name,
107                         s->vector[i]->var, s->vector[i]->cnt);
108
109   return d;
110 }
111
112 /* Clears the contents from a dictionary without destroying the
113    dictionary itself. */
114 void
115 dict_clear (struct dictionary *d) 
116 {
117   /* FIXME?  Should we really clear case_limit, label, documents?
118      Others are necessarily cleared by deleting all the variables.*/
119   int i;
120
121   assert (d != NULL);
122
123   for (i = 0; i < d->var_cnt; i++) 
124     {
125       struct variable *v = d->var[i];
126       val_labs_destroy (v->val_labs);
127       free (v->label);
128       free (v); 
129     }
130   free (d->var);
131   d->var = NULL;
132   d->var_cnt = d->var_cap = 0;
133   hsh_clear (d->name_tab);
134   d->next_value_idx = 0;
135   free (d->split);
136   d->split = NULL;
137   d->split_cnt = 0;
138   d->weight = NULL;
139   d->filter = NULL;
140   d->case_limit = 0;
141   free (d->label);
142   d->label = NULL;
143   free (d->documents);
144   d->documents = NULL;
145   dict_clear_vectors (d);
146 }
147
148 /* Clears a dictionary and destroys it. */
149 void
150 dict_destroy (struct dictionary *d)
151 {
152   if (d != NULL) 
153     {
154       dict_clear (d);
155       hsh_destroy (d->name_tab);
156       free (d);
157     }
158 }
159
160 /* Returns the number of variables in D. */
161 size_t
162 dict_get_var_cnt (const struct dictionary *d) 
163 {
164   assert (d != NULL);
165
166   return d->var_cnt;
167 }
168
169 /* Returns the variable in D with index IDX, which must be
170    between 0 and the count returned by dict_get_var_cnt(),
171    exclusive. */
172 struct variable *
173 dict_get_var (const struct dictionary *d, size_t idx) 
174 {
175   assert (d != NULL);
176   assert (idx < d->var_cnt);
177
178   return d->var[idx];
179 }
180
181 /* Sets *VARS to an array of pointers to variables in D and *CNT
182    to the number of variables in *D.  By default all variables
183    are returned, but bits may be set in EXCLUDE_CLASSES to
184    exclude ordinary, system, and/or scratch variables. */
185 void
186 dict_get_vars (const struct dictionary *d, struct variable ***vars,
187                size_t *cnt, unsigned exclude_classes)
188 {
189   size_t count;
190   size_t i;
191   
192   assert (d != NULL);
193   assert (vars != NULL);
194   assert (cnt != NULL);
195   assert ((exclude_classes & ~((1u << DC_ORDINARY)
196                                | (1u << DC_SYSTEM)
197                                | (1u << DC_SCRATCH))) == 0);
198   
199   count = 0;
200   for (i = 0; i < d->var_cnt; i++)
201     if (!(exclude_classes & (1u << dict_class_from_id (d->var[i]->name))))
202       count++;
203
204   *vars = xmalloc (count * sizeof **vars);
205   *cnt = 0;
206   for (i = 0; i < d->var_cnt; i++)
207     if (!(exclude_classes & (1u << dict_class_from_id (d->var[i]->name))))
208       (*vars)[(*cnt)++] = d->var[i];
209   assert (*cnt == count);
210 }
211
212 /* Creates and returns a new variable in D with the given NAME
213    and WIDTH.  Returns a null pointer if the given NAME would
214    duplicate that of an existing variable in the dictionary. */
215 struct variable *
216 dict_create_var (struct dictionary *d, const char *name, int width) 
217 {
218   struct variable *v;
219
220   assert (d != NULL);
221   assert (name != NULL);
222   assert (strlen (name) >= 1 && strlen (name) <= 8);
223   assert (width >= 0 && width < 256);
224
225   /* Make sure there's not already a variable by that name. */
226   if (dict_lookup_var (d, name) != NULL)
227     return NULL;
228
229   /* Allocate and initialize variable. */
230   v = xmalloc (sizeof *v);
231   strncpy (v->name, name, sizeof v->name);
232   v->name[8] = '\0';
233   v->index = d->var_cnt;
234   v->type = width == 0 ? NUMERIC : ALPHA;
235   v->width = width;
236   v->fv = d->next_value_idx;
237   v->nv = width == 0 ? 1 : DIV_RND_UP (width, 8);
238   v->init = 1;
239   v->reinit = name[0] != '#';
240   v->miss_type = MISSING_NONE;
241   if (v->type == NUMERIC)
242     {
243       v->print.type = FMT_F;
244       v->print.w = 8;
245       v->print.d = 2;
246     }
247   else
248     {
249       v->print.type = FMT_A;
250       v->print.w = v->width;
251       v->print.d = 0;
252     }
253   v->val_labs = val_labs_create (v->width);
254   v->label = NULL;
255
256   /* Update dictionary. */
257   if (d->var_cnt >= d->var_cap) 
258     {
259       d->var_cap = 8 + 2 * d->var_cap; 
260       d->var = xrealloc (d->var, d->var_cap * sizeof *d->var);
261     }
262   d->var[v->index] = v;
263   d->var_cnt++;
264   hsh_force_insert (d->name_tab, v);
265   d->next_value_idx += v->nv;
266
267   return v;
268 }
269
270 /* Creates and returns a new variable in D with the given NAME
271    and WIDTH.  Assert-fails if the given NAME would duplicate
272    that of an existing variable in the dictionary. */
273 struct variable *
274 dict_create_var_assert (struct dictionary *d, const char *name, int width) 
275 {
276   struct variable *v = dict_create_var (d, name, width);
277   assert (v != NULL);
278   return v;
279 }
280
281 /* Creates a new variable in D named NAME, as a copy of existing
282    variable OV, which need not be in D or in any dictionary. */
283 struct variable *
284 dict_clone_var (struct dictionary *d, const struct variable *ov,
285                 const char *name)
286 {
287   struct variable *nv;
288
289   assert (d != NULL);
290   assert (ov != NULL);
291   assert (name != NULL);
292   assert (strlen (name) >= 1 && strlen (name) <= 8);
293
294   nv = dict_create_var (d, name, ov->width);
295   if (nv == NULL)
296     return NULL;
297
298   nv->init = 1;
299   nv->reinit = ov->reinit;
300   nv->miss_type = ov->miss_type;
301   memcpy (nv->missing, ov->missing, sizeof nv->missing);
302   nv->print = ov->print;
303   nv->write = ov->write;
304   val_labs_destroy (nv->val_labs);
305   nv->val_labs = val_labs_copy (ov->val_labs);
306   if (ov->label != NULL)
307     nv->label = xstrdup (ov->label);
308
309   return nv;
310 }
311
312 /* Changes the name of V in D to name NEW_NAME.  Assert-fails if
313    a variable named NEW_NAME is already in D, except that
314    NEW_NAME may be the same as V's existing name. */
315 void 
316 dict_rename_var (struct dictionary *d, struct variable *v,
317                  const char *new_name) 
318 {
319   assert (d != NULL);
320   assert (v != NULL);
321   assert (new_name != NULL);
322   assert (strlen (new_name) >= 1 && strlen (new_name) <= 8);
323
324   if (!strcmp (v->name, new_name))
325     return;
326
327   assert (dict_lookup_var (d, new_name) == NULL);
328
329   hsh_force_delete (d->name_tab, v);
330   strncpy (v->name, new_name, sizeof v->name);
331   v->name[8] = '\0';
332   hsh_force_insert (d->name_tab, v);
333 }
334
335 /* Returns the variable named NAME in D, or a null pointer if no
336    variable has that name. */
337 struct variable *
338 dict_lookup_var (const struct dictionary *d, const char *name)
339 {
340   struct variable v;
341   
342   assert (d != NULL);
343   assert (name != NULL);
344   assert (strlen (name) >= 1 && strlen (name) <= 8);
345
346   strncpy (v.name, name, sizeof v.name);
347   v.name[8] = '\0';
348
349   return hsh_find (d->name_tab, &v);
350 }
351
352 /* Returns the variable named NAME in D.  Assert-fails if no
353    variable has that name. */
354 struct variable *
355 dict_lookup_var_assert (const struct dictionary *d, const char *name)
356 {
357   struct variable *v = dict_lookup_var (d, name);
358   assert (v != NULL);
359   return v;
360 }
361
362 /* Returns nonzero if variable V is in dictionary D. */
363 int
364 dict_contains_var (const struct dictionary *d, const struct variable *v)
365 {
366   assert (d != NULL);
367   assert (v != NULL);
368
369   return dict_lookup_var (d, v->name) == v;
370 }
371
372 /* Compares two double pointers to variables, which should point
373    to elements of a struct dictionary's `var' member array. */
374 static int
375 compare_variable_dblptrs (const void *a_, const void *b_, void *aux UNUSED) 
376 {
377   struct variable *const *a = a_;
378   struct variable *const *b = b_;
379
380   if (a > b)
381     return 1;
382   else if (a < b)
383     return -1;
384   else
385     return 0;
386 }
387
388 /* Deletes variable V from dictionary D and frees V.
389
390    This is a very bad idea if there might be any pointers to V
391    from outside D.  In general, no variable in default_dict
392    should be deleted when any transformations are active, because
393    those transformations might reference the deleted variable.
394    The safest time to delete a variable is just after a procedure
395    has been executed, as done by MODIFY VARS.
396
397    Pointers to V within D are not a problem, because
398    dict_delete_var() knows to remove V from split variables,
399    weights, filters, etc. */
400 void
401 dict_delete_var (struct dictionary *d, struct variable *v) 
402 {
403   size_t i;
404
405   assert (d != NULL);
406   assert (v != NULL);
407   assert (dict_contains_var (d, v));
408   assert (d->var[v->index] == v);
409
410   /* Remove v from splits, weight, filter variables. */
411   d->split_cnt = remove_equal (d->split, d->split_cnt, sizeof *d->split,
412                                &v,
413                                compare_variable_dblptrs, NULL);
414   if (d->weight == v)
415     d->weight = NULL;
416   if (d->filter == v)
417     d->filter = NULL;
418   dict_clear_vectors (d);
419
420   /* Remove v from var array. */
421   d->var_cnt--;
422   memmove (d->var + v->index, d->var + v->index + 1,
423            (d->var_cnt - v->index) * sizeof *d->var);
424
425   /* Update index. */
426   for (i = v->index; i < d->var_cnt; i++)
427     d->var[i]->index = i;
428
429   /* Update name hash. */
430   hsh_force_delete (d->name_tab, v);
431
432   /* Free memory. */
433   val_labs_destroy (v->val_labs);
434   free (v->label);
435   free (v);
436 }
437
438 /* Deletes the COUNT variables listed in VARS from D.  This is
439    unsafe; see the comment on dict_delete_var() for details. */
440 void 
441 dict_delete_vars (struct dictionary *d,
442                   struct variable *const *vars, size_t count) 
443 {
444   /* FIXME: this can be done in O(count) time, but this algorithm
445      is O(count**2). */
446   assert (d != NULL);
447   assert (count == 0 || vars != NULL);
448
449   while (count-- > 0)
450     dict_delete_var (d, *vars++);
451 }
452
453 /* Reorders the variables in D, placing the COUNT variables
454    listed in ORDER in that order at the beginning of D.  The
455    other variables in D, if any, retain their relative
456    positions. */
457 void 
458 dict_reorder_vars (struct dictionary *d,
459                    struct variable *const *order, size_t count) 
460 {
461   struct variable **new_var;
462   size_t i;
463   
464   assert (d != NULL);
465   assert (count == 0 || order != NULL);
466   assert (count <= d->var_cnt);
467
468   new_var = xmalloc (d->var_cnt * sizeof *new_var);
469   memcpy (new_var, order, count * sizeof *new_var);
470   for (i = 0; i < count; i++) 
471     {
472       assert (d->var[order[i]->index] != NULL);
473       d->var[order[i]->index] = NULL;
474       order[i]->index = i;
475     }
476   for (i = 0; i < d->var_cnt; i++)
477     if (d->var[i] != NULL)
478       {
479         assert (count < d->var_cnt);
480         new_var[count] = d->var[i];
481         new_var[count]->index = count;
482         count++;
483       }
484   free (d->var);
485   d->var = new_var;
486 }
487
488 /* Renames COUNT variables specified in VARS to the names given
489    in NEW_NAMES within dictionary D.  If the renaming would
490    result in a duplicate variable name, returns zero and stores a
491    name that would be duplicated into *ERR_NAME (if ERR_NAME is
492    non-null).  Otherwise, the renaming is successful, and nonzero
493    is returned. */
494 int
495 dict_rename_vars (struct dictionary *d,
496                   struct variable **vars, char **new_names,
497                   size_t count, char **err_name) 
498 {
499   char **old_names;
500   size_t i;
501   int success = 1;
502
503   assert (d != NULL);
504   assert (count == 0 || vars != NULL);
505   assert (count == 0 || new_names != NULL);
506
507   old_names = xmalloc (count * sizeof *old_names);
508   for (i = 0; i < count; i++) 
509     {
510       assert (d->var[vars[i]->index] == vars[i]);
511       hsh_force_delete (d->name_tab, vars[i]);
512       old_names[i] = xstrdup (vars[i]->name);
513     }
514   
515   for (i = 0; i < count; i++)
516     {
517       assert (new_names[i] != NULL);
518       assert (*new_names[i] != '\0');
519       assert (strlen (new_names[i]) < 9);
520       strcpy (vars[i]->name, new_names[i]);
521       if (hsh_insert (d->name_tab, vars[i]) != NULL) 
522         {
523           size_t fail_idx = i;
524           if (err_name != NULL) 
525             *err_name = new_names[i];
526
527           for (i = 0; i < fail_idx; i++)
528             hsh_force_delete (d->name_tab, vars[i]);
529           
530           for (i = 0; i < count; i++)
531             {
532               strcpy (vars[i]->name, old_names[i]);
533               hsh_force_insert (d->name_tab, vars[i]);
534             }
535
536           success = 0;
537           break;
538         }
539     }
540
541   for (i = 0; i < count; i++)
542     free (old_names[i]);
543   free (old_names);
544
545   return success;
546 }
547
548 /* Returns the weighting variable in dictionary D, or a null
549    pointer if the dictionary is unweighted. */
550 struct variable *
551 dict_get_weight (const struct dictionary *d) 
552 {
553   assert (d != NULL);
554   assert (d->weight == NULL || dict_contains_var (d, d->weight));
555   
556   return d->weight;
557 }
558
559 /* Returns the value of D's weighting variable in case C, except
560    that a negative weight is returned as 0.  Returns 1 if the
561    dictionary is unweighted. */
562 double
563 dict_get_case_weight (const struct dictionary *d, const struct ccase *c)
564 {
565   assert (d != NULL);
566   assert (c != NULL);
567
568   if (d->weight == NULL)
569     return 1.0;
570   else 
571     {
572       double w = c->data[d->weight->fv].f;
573       if (w < 0.0)
574         w = 0.0;
575       return w;
576     }
577 }
578
579 /* Sets the weighting variable of D to V, or turning off
580    weighting if V is a null pointer. */
581 void
582 dict_set_weight (struct dictionary *d, struct variable *v) 
583 {
584   assert (d != NULL);
585   assert (v == NULL || dict_contains_var (d, v));
586   assert (v == NULL || v->type == NUMERIC);
587
588   d->weight = v;
589 }
590
591 /* Returns the filter variable in dictionary D (see cmd_filter())
592    or a null pointer if the dictionary is unfiltered. */
593 struct variable *
594 dict_get_filter (const struct dictionary *d) 
595 {
596   assert (d != NULL);
597   assert (d->filter == NULL || dict_contains_var (d, d->filter));
598   
599   return d->filter;
600 }
601
602 /* Sets V as the filter variable for dictionary D.  Passing a
603    null pointer for V turn off filtering. */
604 void
605 dict_set_filter (struct dictionary *d, struct variable *v)
606 {
607   assert (d != NULL);
608   assert (v == NULL || dict_contains_var (d, v));
609
610   d->filter = v;
611 }
612
613 /* Returns the case limit for dictionary D, or zero if the number
614    of cases is unlimited (see cmd_n()). */
615 int
616 dict_get_case_limit (const struct dictionary *d) 
617 {
618   assert (d != NULL);
619
620   return d->case_limit;
621 }
622
623 /* Sets CASE_LIMIT as the case limit for dictionary D.  Zero for
624    CASE_LIMIT indicates no limit. */
625 void
626 dict_set_case_limit (struct dictionary *d, int case_limit) 
627 {
628   assert (d != NULL);
629   assert (case_limit >= 0);
630
631   d->case_limit = case_limit;
632 }
633
634 /* Returns the index of the next value to be added to D.  This
635    value is the number of `union value's that need to be
636    allocated to store a case for dictionary D. */
637 int
638 dict_get_next_value_idx (const struct dictionary *d) 
639 {
640   assert (d != NULL);
641
642   return d->next_value_idx;
643 }
644
645 /* Returns the number of bytes needed to store a case for
646    dictionary D. */
647 size_t
648 dict_get_case_size (const struct dictionary *d) 
649 {
650   assert (d != NULL);
651
652   return sizeof (union value) * dict_get_next_value_idx (d);
653 }
654
655 /* Reassigns values in dictionary D so that fragmentation is
656    eliminated. */
657 void
658 dict_compact_values (struct dictionary *d) 
659 {
660   size_t i;
661
662   d->next_value_idx = 0;
663   for (i = 0; i < d->var_cnt; i++)
664     {
665       struct variable *v = d->var[i];
666
667       v->fv = d->next_value_idx;
668       d->next_value_idx += v->nv;
669     }
670 }
671
672 /* Returns the SPLIT FILE vars (see cmd_split_file()).  Call
673    dict_get_split_cnt() to determine how many SPLIT FILE vars
674    there are.  Returns a null pointer if and only if there are no
675    SPLIT FILE vars. */
676 struct variable *const *
677 dict_get_split_vars (const struct dictionary *d) 
678 {
679   assert (d != NULL);
680   
681   return d->split;
682 }
683
684 /* Returns the number of SPLIT FILE vars. */
685 size_t
686 dict_get_split_cnt (const struct dictionary *d) 
687 {
688   assert (d != NULL);
689
690   return d->split_cnt;
691 }
692
693 /* Sets CNT split vars SPLIT in dictionary D. */
694 void
695 dict_set_split_vars (struct dictionary *d,
696                      struct variable *const *split, size_t cnt)
697 {
698   assert (d != NULL);
699   assert (cnt == 0 || split != NULL);
700
701   d->split_cnt = cnt;
702   d->split = xrealloc (d->split, cnt * sizeof *d->split);
703   memcpy (d->split, split, cnt * sizeof *d->split);
704 }
705
706 /* Returns the file label for D, or a null pointer if D is
707    unlabeled (see cmd_file_label()). */
708 const char *
709 dict_get_label (const struct dictionary *d) 
710 {
711   assert (d != NULL);
712
713   return d->label;
714 }
715
716 /* Sets D's file label to LABEL, truncating it to a maximum of 60
717    characters. */
718 void
719 dict_set_label (struct dictionary *d, const char *label) 
720 {
721   assert (d != NULL);
722
723   free (d->label);
724   if (label == NULL)
725     d->label = NULL;
726   else if (strlen (label) < 60)
727     d->label = xstrdup (label);
728   else 
729     {
730       d->label = xmalloc (61);
731       memcpy (d->label, label, 60);
732       d->label[60] = '\0';
733     }
734 }
735
736 /* Returns the documents for D, or a null pointer if D has no
737    documents (see cmd_document()).. */
738 const char *
739 dict_get_documents (const struct dictionary *d) 
740 {
741   assert (d != NULL);
742
743   return d->documents;
744 }
745
746 /* Sets the documents for D to DOCUMENTS, or removes D's
747    documents if DOCUMENT is a null pointer. */
748 void
749 dict_set_documents (struct dictionary *d, const char *documents)
750 {
751   assert (d != NULL);
752
753   free (d->documents);
754   if (documents == NULL)
755     d->documents = NULL;
756   else
757     d->documents = xstrdup (documents);
758 }
759
760 /* Creates in D a vector named NAME that contains CNT variables
761    VAR (see cmd_vector()).  Returns nonzero if successful, or
762    zero if a vector named NAME already exists in D. */
763 int
764 dict_create_vector (struct dictionary *d,
765                     const char *name,
766                     struct variable **var, size_t cnt) 
767 {
768   struct vector *vector;
769
770   assert (d != NULL);
771   assert (name != NULL);
772   assert (strlen (name) > 0 && strlen (name) < 9);
773   assert (var != NULL);
774   assert (cnt > 0);
775   
776   if (dict_lookup_vector (d, name) != NULL)
777     return 0;
778
779   d->vector = xrealloc (d->vector, (d->vector_cnt + 1) * sizeof *d->vector);
780   vector = d->vector[d->vector_cnt] = xmalloc (sizeof *vector);
781   vector->idx = d->vector_cnt++;
782   strncpy (vector->name, name, 8);
783   vector->name[8] = '\0';
784   vector->var = xmalloc (cnt * sizeof *var);
785   memcpy (vector->var, var, cnt * sizeof *var);
786   vector->cnt = cnt;
787   
788   return 1;
789 }
790
791 /* Returns the vector in D with index IDX, which must be less
792    than dict_get_vector_cnt (D). */
793 const struct vector *
794 dict_get_vector (const struct dictionary *d, size_t idx) 
795 {
796   assert (d != NULL);
797   assert (idx < d->vector_cnt);
798
799   return d->vector[idx];
800 }
801
802 /* Returns the number of vectors in D. */
803 size_t
804 dict_get_vector_cnt (const struct dictionary *d) 
805 {
806   assert (d != NULL);
807
808   return d->vector_cnt;
809 }
810
811 /* Looks up and returns the vector within D with the given
812    NAME. */
813 const struct vector *
814 dict_lookup_vector (const struct dictionary *d, const char *name) 
815 {
816   size_t i;
817
818   assert (d != NULL);
819   assert (name != NULL);
820
821   for (i = 0; i < d->vector_cnt; i++)
822     if (!strcmp (d->vector[i]->name, name))
823       return d->vector[i];
824   return NULL;
825 }
826
827 /* Deletes all vectors from D. */
828 void
829 dict_clear_vectors (struct dictionary *d) 
830 {
831   size_t i;
832   
833   assert (d != NULL);
834
835   for (i = 0; i < d->vector_cnt; i++) 
836     {
837       free (d->vector[i]->var);
838       free (d->vector[i]);
839     }
840   free (d->vector);
841   d->vector = NULL;
842   d->vector_cnt = 0;
843 }