d9eae5247c3dc833a5f105861e6179354567797d
[pspp-builds.git] / src / language / lexer / variable-parser.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
22 #include <language/lexer/variable-parser.h>
23
24 #include <ctype.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27
28 #include "lexer.h"
29 #include <data/dictionary.h>
30 #include <data/procedure.h>
31 #include <data/variable.h>
32 #include <libpspp/alloc.h>
33 #include <libpspp/assertion.h>
34 #include <libpspp/bit-vector.h>
35 #include <libpspp/hash.h>
36 #include <libpspp/message.h>
37 #include <libpspp/misc.h>
38 #include <libpspp/pool.h>
39 #include <libpspp/str.h>
40
41 #include "gettext.h"
42 #define _(msgid) gettext (msgid)
43
44 /* Parses a name as a variable within VS.  Sets *IDX to the
45    variable's index and returns true if successful.  On failure
46    emits an error message and returns false. */
47 static bool
48 parse_vs_variable_idx (struct lexer *lexer, const struct var_set *vs, 
49                 size_t *idx)
50 {
51   assert (idx != NULL);
52   
53   if (lex_token (lexer) != T_ID)
54     {
55       lex_error (lexer, _("expecting variable name"));
56       return false;
57     }
58   else if (var_set_lookup_var_idx (vs, lex_tokid (lexer), idx)) 
59     {
60       lex_get (lexer);
61       return true;
62     }
63   else 
64     {
65       msg (SE, _("%s is not a variable name."), lex_tokid (lexer));
66       return false;
67     }
68 }
69
70 /* Parses a name as a variable within VS and returns the variable
71    if successful.  On failure emits an error message and returns
72    a null pointer. */
73 static struct variable *
74 parse_vs_variable (struct lexer *lexer, const struct var_set *vs)
75 {
76   size_t idx;
77   return parse_vs_variable_idx (lexer, vs, &idx) ? var_set_get_var (vs, idx) : NULL;
78 }
79
80 /* Parses a variable name in dictionary D and returns the
81    variable if successful.  On failure emits an error message and
82    returns a null pointer. */
83 struct variable *
84 parse_variable (struct lexer *lexer, const struct dictionary *d) 
85 {
86   struct var_set *vs = var_set_create_from_dict (d);
87   struct variable *var = parse_vs_variable (lexer, vs);
88   var_set_destroy (vs);
89   return var;
90 }
91
92 /* Parses a set of variables from dictionary D given options
93    OPTS.  Resulting list of variables stored in *VAR and the
94    number of variables into *CNT.  Returns true only if
95    successful. */
96 bool
97 parse_variables (struct lexer *lexer, const struct dictionary *d, 
98                         struct variable ***var,
99                         size_t *cnt, int opts) 
100 {
101   struct var_set *vs;
102   int success;
103
104   assert (d != NULL);
105   assert (var != NULL);
106   assert (cnt != NULL);
107
108   vs = var_set_create_from_dict (d);
109   success = parse_var_set_vars (lexer, vs, var, cnt, opts);
110   if ( success == 0 )
111      free ( *var ) ;
112   var_set_destroy (vs);
113   return success;
114 }
115
116 /* Parses a set of variables from dictionary D given options
117    OPTS.  Resulting list of variables stored in *VARS and the
118    number of variables into *VAR_CNT.  Returns true only if
119    successful.  Same behavior as parse_variables, except that all
120    allocations are taken from the given POOL. */
121 bool
122 parse_variables_pool (struct lexer *lexer, struct pool *pool, 
123                 const struct dictionary *dict,
124                 struct variable ***vars, size_t *var_cnt, int opts) 
125 {
126   int retval;
127
128   /* PV_APPEND is unsafe because parse_variables would free the
129      existing names on failure, but those names are presumably
130      already in the pool, which would attempt to re-free it
131      later. */
132   assert (!(opts & PV_APPEND));
133   
134   retval = parse_variables (lexer, dict, vars, var_cnt, opts);
135   if (retval)
136     pool_register (pool, free, *vars);
137   return retval;
138 }
139
140 /* Parses a variable name from VS.  If successful, sets *IDX to
141    the variable's index in VS, *CLASS to the variable's
142    dictionary class, and returns true.  Returns false on
143    failure. */
144 static bool
145 parse_var_idx_class (struct lexer *lexer, const struct var_set *vs, 
146                         size_t *idx,
147                         enum dict_class *class)
148 {
149   if (!parse_vs_variable_idx (lexer, vs, idx))
150     return false;
151
152   *class = dict_class_from_id (var_get_name (var_set_get_var (vs, *idx)));
153   return true;
154 }
155
156 /* Add the variable from VS with index IDX to the list of
157    variables V that has *NV elements and room for *MV.
158    Uses and updates INCLUDED to avoid duplicates if indicated by
159    PV_OPTS, which also affects what variables are allowed in
160    appropriate ways. */
161 static void
162 add_variable (struct variable ***v, size_t *nv, size_t *mv,
163               char *included, int pv_opts,
164               const struct var_set *vs, size_t idx)
165 {
166   struct variable *add = var_set_get_var (vs, idx);
167   const char *add_name = var_get_name (add);
168
169   if ((pv_opts & PV_NUMERIC) && !var_is_numeric (add)) 
170     msg (SW, _("%s is not a numeric variable.  It will not be "
171                "included in the variable list."), add_name);
172   else if ((pv_opts & PV_STRING) && !var_is_alpha (add)) 
173     msg (SE, _("%s is not a string variable.  It will not be "
174                "included in the variable list."), add_name);
175   else if ((pv_opts & PV_NO_SCRATCH)
176            && dict_class_from_id (add_name) == DC_SCRATCH)
177     msg (SE, _("Scratch variables (such as %s) are not allowed "
178                "here."), add_name);
179   else if ((pv_opts & (PV_SAME_TYPE | PV_SAME_WIDTH)) && *nv
180            && var_get_type (add) != var_get_type ((*v)[0])) 
181     msg (SE, _("%s and %s are not the same type.  All variables in "
182                "this variable list must be of the same type.  %s "
183                "will be omitted from the list."),
184          var_get_name ((*v)[0]), add_name, add_name);
185   else if ((pv_opts & PV_SAME_WIDTH) && *nv
186            && var_get_width (add) != var_get_width ((*v)[0]))
187     msg (SE, _("%s and %s are string variables with different widths.  "
188                "All variables in this variable list must have the "
189                "same width.  %s will be omttied from the list."),
190          var_get_name ((*v)[0]), add_name, add_name);
191   else if ((pv_opts & PV_NO_DUPLICATE) && included[idx]) 
192     msg (SE, _("Variable %s appears twice in variable list."), add_name);
193   else if ((pv_opts & PV_DUPLICATE) || !included[idx])
194     {
195       if (*nv >= *mv)
196         {
197           *mv = 2 * (*nv + 1);
198           *v = xnrealloc (*v, *mv, sizeof **v);
199         }
200       (*v)[(*nv)++] = add;
201       if (included != NULL)
202         included[idx] = 1;
203     }
204 }
205
206 /* Adds the variables in VS with indexes FIRST_IDX through
207    LAST_IDX, inclusive, to the list of variables V that has *NV
208    elements and room for *MV.  Uses and updates INCLUDED to avoid
209    duplicates if indicated by PV_OPTS, which also affects what
210    variables are allowed in appropriate ways. */
211 static void
212 add_variables (struct variable ***v, size_t *nv, size_t *mv, char *included,
213                int pv_opts,
214                const struct var_set *vs, int first_idx, int last_idx,
215                enum dict_class class) 
216 {
217   size_t i;
218   
219   for (i = first_idx; i <= last_idx; i++)
220     if (dict_class_from_id (var_get_name (var_set_get_var (vs, i))) == class)
221       add_variable (v, nv, mv, included, pv_opts, vs, i);
222 }
223
224 /* Note that if parse_variables() returns false, *v is free()'d.
225    Conversely, if parse_variables() returns true, then *nv is
226    nonzero and *v is non-NULL. */
227 bool
228 parse_var_set_vars (struct lexer *lexer, const struct var_set *vs, 
229                     struct variable ***v, size_t *nv,
230                     int pv_opts)
231 {
232   size_t mv;
233   char *included;
234
235   assert (vs != NULL);
236   assert (v != NULL);
237   assert (nv != NULL);
238
239   /* At most one of PV_NUMERIC, PV_STRING, PV_SAME_TYPE,
240      PV_SAME_WIDTH may be specified. */
241   assert (((pv_opts & PV_NUMERIC) != 0)
242           + ((pv_opts & PV_STRING) != 0)
243           + ((pv_opts & PV_SAME_TYPE) != 0)
244           + ((pv_opts & PV_SAME_WIDTH) != 0) <= 1);
245
246   /* PV_DUPLICATE and PV_NO_DUPLICATE are incompatible. */
247   assert (!(pv_opts & PV_DUPLICATE) || !(pv_opts & PV_NO_DUPLICATE));
248
249   if (!(pv_opts & PV_APPEND))
250     {
251       *v = NULL;
252       *nv = 0;
253       mv = 0;
254     }
255   else
256     mv = *nv;
257
258   if (!(pv_opts & PV_DUPLICATE))
259     {
260       size_t i;
261       
262       included = xcalloc (var_set_get_cnt (vs), sizeof *included);
263       for (i = 0; i < *nv; i++) 
264         {
265           size_t index;
266           if (!var_set_lookup_var_idx (vs, var_get_name ((*v)[i]), &index))
267             NOT_REACHED ();
268           included[index] = 1; 
269         }
270     }
271   else
272     included = NULL;
273
274   do
275     {
276       if (lex_match (lexer, T_ALL))
277         add_variables (v, nv, &mv, included, pv_opts,
278                        vs, 0, var_set_get_cnt (vs) - 1, DC_ORDINARY);
279       else 
280         {
281           enum dict_class class;
282           size_t first_idx;
283
284           if (!parse_var_idx_class (lexer, vs, &first_idx, &class))
285             goto fail;
286
287           if (!lex_match (lexer, T_TO))
288             add_variable (v, nv, &mv, included, pv_opts, vs, first_idx);
289           else 
290             {
291               size_t last_idx;
292               enum dict_class last_class;
293               struct variable *first_var, *last_var;
294
295               if (!parse_var_idx_class (lexer, vs, &last_idx, &last_class))
296                 goto fail;
297
298               first_var = var_set_get_var (vs, first_idx);
299               last_var = var_set_get_var (vs, last_idx);
300
301               if (last_idx < first_idx)
302                 {
303                   const char *first_name = var_get_name (first_var);
304                   const char *last_name = var_get_name (last_var);
305                   msg (SE, _("%s TO %s is not valid syntax since %s "
306                              "precedes %s in the dictionary."),
307                        first_name, last_name, first_name, last_name);
308                   goto fail;
309                 }
310
311               if (class != last_class)
312                 {
313                   msg (SE, _("When using the TO keyword to specify several "
314                              "variables, both variables must be from "
315                              "the same variable dictionaries, of either "
316                              "ordinary, scratch, or system variables.  "
317                              "%s is a %s variable, whereas %s is %s."),
318                        var_get_name (first_var), dict_class_to_name (class),
319                        var_get_name (last_var),
320                        dict_class_to_name (last_class));
321                   goto fail;
322                 }
323
324               add_variables (v, nv, &mv, included, pv_opts,
325                              vs, first_idx, last_idx, class);
326             } 
327         }
328
329       if (pv_opts & PV_SINGLE)
330         break;
331       lex_match (lexer, ',');
332     }
333   while (lex_token (lexer) == T_ALL
334          || (lex_token (lexer) == T_ID && var_set_lookup_var (vs, lex_tokid (lexer)) != NULL));
335   
336   if (*nv == 0)
337     goto fail;
338
339   free (included);
340   return 1;
341
342 fail:
343   free (included);
344   free (*v);
345   *v = NULL;
346   *nv = 0;
347   return 0;
348 }
349
350 /* Extracts a numeric suffix from variable name S, copying it
351    into string R.  Sets *D to the length of R and *N to its
352    value. */
353 static int
354 extract_num (char *s, char *r, int *n, int *d)
355 {
356   char *cp;
357
358   /* Find first digit. */
359   cp = s + strlen (s) - 1;
360   while (isdigit ((unsigned char) *cp) && cp > s)
361     cp--;
362   cp++;
363
364   /* Extract root. */
365   strncpy (r, s, cp - s);
366   r[cp - s] = 0;
367
368   /* Count initial zeros. */
369   *n = *d = 0;
370   while (*cp == '0')
371     {
372       (*d)++;
373       cp++;
374     }
375
376   /* Extract value. */
377   while (isdigit ((unsigned char) *cp))
378     {
379       (*d)++;
380       *n = (*n * 10) + (*cp - '0');
381       cp++;
382     }
383
384   /* Sanity check. */
385   if (*n == 0 && *d == 0)
386     {
387       msg (SE, _("incorrect use of TO convention"));
388       return 0;
389     }
390   return 1;
391 }
392
393 /* Parses a list of variable names according to the DATA LIST version
394    of the TO convention.  */
395 bool
396 parse_DATA_LIST_vars (struct lexer *lexer, char ***names, size_t *nnames, int pv_opts)
397 {
398   int n1, n2;
399   int d1, d2;
400   int n;
401   size_t nvar, mvar;
402   char name1[LONG_NAME_LEN + 1], name2[LONG_NAME_LEN + 1];
403   char root1[LONG_NAME_LEN + 1], root2[LONG_NAME_LEN + 1];
404   int success = 0;
405
406   assert (names != NULL);
407   assert (nnames != NULL);
408   assert ((pv_opts & ~(PV_APPEND | PV_SINGLE
409                        | PV_NO_SCRATCH | PV_NO_DUPLICATE)) == 0);
410   /* FIXME: PV_NO_DUPLICATE is not implemented. */
411
412   if (pv_opts & PV_APPEND)
413     nvar = mvar = *nnames;
414   else
415     {
416       nvar = mvar = 0;
417       *names = NULL;
418     }
419
420   do
421     {
422       if (lex_token (lexer) != T_ID)
423         {
424           lex_error (lexer, "expecting variable name");
425           goto fail;
426         }
427       if (dict_class_from_id (lex_tokid (lexer)) == DC_SCRATCH
428           && (pv_opts & PV_NO_SCRATCH))
429         {
430           msg (SE, _("Scratch variables not allowed here."));
431           goto fail;
432         }
433       strcpy (name1, lex_tokid (lexer));
434       lex_get (lexer);
435       if (lex_token (lexer) == T_TO)
436         {
437           lex_get (lexer);
438           if (lex_token (lexer) != T_ID)
439             {
440               lex_error (lexer, "expecting variable name");
441               goto fail;
442             }
443           strcpy (name2, lex_tokid (lexer));
444           lex_get (lexer);
445
446           if (!extract_num (name1, root1, &n1, &d1)
447               || !extract_num (name2, root2, &n2, &d2))
448             goto fail;
449
450           if (strcasecmp (root1, root2))
451             {
452               msg (SE, _("Prefixes don't match in use of TO convention."));
453               goto fail;
454             }
455           if (n1 > n2)
456             {
457               msg (SE, _("Bad bounds in use of TO convention."));
458               goto fail;
459             }
460           if (d2 > d1)
461             d2 = d1;
462
463           if (mvar < nvar + (n2 - n1 + 1))
464             {
465               mvar += ROUND_UP (n2 - n1 + 1, 16);
466               *names = xnrealloc (*names, mvar, sizeof **names);
467             }
468
469           for (n = n1; n <= n2; n++)
470             {
471               char name[LONG_NAME_LEN + 1];
472               sprintf (name, "%s%0*d", root1, d1, n);
473               (*names)[nvar] = xstrdup (name);
474               nvar++;
475             }
476         }
477       else
478         {
479           if (nvar >= mvar)
480             {
481               mvar += 16;
482               *names = xnrealloc (*names, mvar, sizeof **names);
483             }
484           (*names)[nvar++] = xstrdup (name1);
485         }
486
487       lex_match (lexer, ',');
488
489       if (pv_opts & PV_SINGLE)
490         break;
491     }
492   while (lex_token (lexer) == T_ID);
493   success = 1;
494
495 fail:
496   *nnames = nvar;
497   if (!success)
498     {
499       int i;
500       for (i = 0; i < nvar; i++)
501         free ((*names)[i]);
502       free (*names);
503       *names = NULL;
504       *nnames = 0;
505     }
506   return success;
507 }
508
509 /* Registers each of the NAMES[0...NNAMES - 1] in POOL, as well
510    as NAMES itself. */
511 static void
512 register_vars_pool (struct pool *pool, char **names, size_t nnames)
513 {
514   size_t i;
515
516   for (i = 0; i < nnames; i++)
517     pool_register (pool, free, names[i]);
518   pool_register (pool, free, names);
519 }
520
521 /* Parses a list of variable names according to the DATA LIST
522    version of the TO convention.  Same args as
523    parse_DATA_LIST_vars(), except that all allocations are taken
524    from the given POOL. */
525 bool
526 parse_DATA_LIST_vars_pool (struct lexer *lexer, struct pool *pool,
527                            char ***names, size_t *nnames, int pv_opts)
528 {
529   int retval;
530
531   /* PV_APPEND is unsafe because parse_DATA_LIST_vars would free
532      the existing names on failure, but those names are
533      presumably already in the pool, which would attempt to
534      re-free it later. */
535   assert (!(pv_opts & PV_APPEND));
536   
537   retval = parse_DATA_LIST_vars (lexer, names, nnames, pv_opts);
538   if (retval)
539     register_vars_pool (pool, *names, *nnames);
540   return retval;
541 }
542
543 /* Parses a list of variables where some of the variables may be
544    existing and the rest are to be created.  Same args as
545    parse_DATA_LIST_vars(). */
546 bool
547 parse_mixed_vars (struct lexer *lexer, const struct dictionary *dict, 
548                   char ***names, size_t *nnames, int pv_opts)
549 {
550   size_t i;
551
552   assert (names != NULL);
553   assert (nnames != NULL);
554   assert ((pv_opts & ~PV_APPEND) == 0);
555
556   if (!(pv_opts & PV_APPEND))
557     {
558       *names = NULL;
559       *nnames = 0;
560     }
561   while (lex_token (lexer) == T_ID || lex_token (lexer) == T_ALL)
562     {
563       if (lex_token (lexer) == T_ALL || dict_lookup_var (dict, lex_tokid (lexer)) != NULL)
564         {
565           struct variable **v;
566           size_t nv;
567
568           if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
569             goto fail;
570           *names = xnrealloc (*names, *nnames + nv, sizeof **names);
571           for (i = 0; i < nv; i++)
572             (*names)[*nnames + i] = xstrdup (var_get_name (v[i]));
573           free (v);
574           *nnames += nv;
575         }
576       else if (!parse_DATA_LIST_vars (lexer, names, nnames, PV_APPEND))
577         goto fail;
578     }
579   return 1;
580
581 fail:
582   for (i = 0; i < *nnames; i++)
583     free ((*names)[i]);
584   free (*names);
585   *names = NULL;
586   *nnames = 0;
587   return 0;
588 }
589
590 /* Parses a list of variables where some of the variables may be
591    existing and the rest are to be created.  Same args as
592    parse_mixed_vars(), except that all allocations are taken
593    from the given POOL. */
594 bool
595 parse_mixed_vars_pool (struct lexer *lexer, const struct dictionary *dict, struct pool *pool,
596                        char ***names, size_t *nnames, int pv_opts)
597 {
598   int retval;
599
600   /* PV_APPEND is unsafe because parse_mixed_vars_pool would free
601      the existing names on failure, but those names are
602      presumably already in the pool, which would attempt to
603      re-free it later. */
604   assert (!(pv_opts & PV_APPEND));
605
606   retval = parse_mixed_vars (lexer, dict, names, nnames, pv_opts);
607   if (retval)
608     register_vars_pool (pool, *names, *nnames);
609   return retval;
610 }
611 \f
612 /* A set of variables. */
613 struct var_set 
614   {
615     size_t (*get_cnt) (const struct var_set *);
616     struct variable *(*get_var) (const struct var_set *, size_t idx);
617     bool (*lookup_var_idx) (const struct var_set *, const char *, size_t *);
618     void (*destroy) (struct var_set *);
619     void *aux;
620   };
621
622 /* Returns the number of variables in VS. */
623 size_t
624 var_set_get_cnt (const struct var_set *vs) 
625 {
626   assert (vs != NULL);
627
628   return vs->get_cnt (vs);
629 }
630
631 /* Return variable with index IDX in VS.
632    IDX must be less than the number of variables in VS. */
633 struct variable *
634 var_set_get_var (const struct var_set *vs, size_t idx) 
635 {
636   assert (vs != NULL);
637   assert (idx < var_set_get_cnt (vs));
638
639   return vs->get_var (vs, idx);
640 }
641
642 /* Returns the variable in VS named NAME, or a null pointer if VS
643    contains no variable with that name. */
644 struct variable *
645 var_set_lookup_var (const struct var_set *vs, const char *name) 
646 {
647   size_t idx;
648   return (var_set_lookup_var_idx (vs, name, &idx)
649           ? var_set_get_var (vs, idx)
650           : NULL);
651 }
652
653 /* If VS contains a variable named NAME, sets *IDX to its index
654    and returns true.  Otherwise, returns false. */
655 bool
656 var_set_lookup_var_idx (const struct var_set *vs, const char *name,
657                         size_t *idx)
658 {
659   assert (vs != NULL);
660   assert (name != NULL);
661   assert (strlen (name) <= LONG_NAME_LEN);
662
663   return vs->lookup_var_idx (vs, name, idx);
664 }
665
666 /* Destroys VS. */
667 void
668 var_set_destroy (struct var_set *vs) 
669 {
670   if (vs != NULL)
671     vs->destroy (vs);
672 }
673 \f
674 /* Returns the number of variables in VS. */
675 static size_t
676 dict_var_set_get_cnt (const struct var_set *vs) 
677 {
678   struct dictionary *d = vs->aux;
679
680   return dict_get_var_cnt (d);
681 }
682
683 /* Return variable with index IDX in VS.
684    IDX must be less than the number of variables in VS. */
685 static struct variable *
686 dict_var_set_get_var (const struct var_set *vs, size_t idx) 
687 {
688   struct dictionary *d = vs->aux;
689
690   return dict_get_var (d, idx);
691 }
692
693 /* If VS contains a variable named NAME, sets *IDX to its index
694    and returns true.  Otherwise, returns false. */
695 static bool
696 dict_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
697                              size_t *idx) 
698 {
699   struct dictionary *d = vs->aux;
700   struct variable *v = dict_lookup_var (d, name);
701   if (v != NULL) 
702     {
703       *idx = var_get_dict_index (v);
704       return true;
705     }
706   else
707     return false;
708 }
709
710 /* Destroys VS. */
711 static void
712 dict_var_set_destroy (struct var_set *vs) 
713 {
714   free (vs);
715 }
716
717 /* Returns a variable set based on D. */
718 struct var_set *
719 var_set_create_from_dict (const struct dictionary *d) 
720 {
721   struct var_set *vs = xmalloc (sizeof *vs);
722   vs->get_cnt = dict_var_set_get_cnt;
723   vs->get_var = dict_var_set_get_var;
724   vs->lookup_var_idx = dict_var_set_lookup_var_idx;
725   vs->destroy = dict_var_set_destroy;
726   vs->aux = (void *) d;
727   return vs;
728 }
729 \f
730 /* A variable set based on an array. */
731 struct array_var_set 
732   {
733     struct variable *const *var;/* Array of variables. */
734     size_t var_cnt;             /* Number of elements in var. */
735     struct hsh_table *name_tab; /* Hash from variable names to variables. */
736   };
737
738 /* Returns the number of variables in VS. */
739 static size_t
740 array_var_set_get_cnt (const struct var_set *vs) 
741 {
742   struct array_var_set *avs = vs->aux;
743
744   return avs->var_cnt;
745 }
746
747 /* Return variable with index IDX in VS.
748    IDX must be less than the number of variables in VS. */
749 static struct variable *
750 array_var_set_get_var (const struct var_set *vs, size_t idx) 
751 {
752   struct array_var_set *avs = vs->aux;
753
754   return (struct variable *) avs->var[idx];
755 }
756
757 /* If VS contains a variable named NAME, sets *IDX to its index
758    and returns true.  Otherwise, returns false. */
759 static bool
760 array_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
761                               size_t *idx) 
762 {
763   struct array_var_set *avs = vs->aux;
764   struct variable *v, *const *vpp;
765
766   v = var_create (name, 0);
767   vpp = hsh_find (avs->name_tab, &v);
768   var_destroy (v);
769   
770   if (vpp != NULL) 
771     {
772       *idx = vpp - avs->var;
773       return true;
774     }
775   else
776     return false;
777 }
778
779 /* Destroys VS. */
780 static void
781 array_var_set_destroy (struct var_set *vs) 
782 {
783   struct array_var_set *avs = vs->aux;
784
785   hsh_destroy (avs->name_tab);
786   free (avs);
787   free (vs);
788 }
789
790 /* Returns a variable set based on the VAR_CNT variables in
791    VAR. */
792 struct var_set *
793 var_set_create_from_array (struct variable *const *var, size_t var_cnt) 
794 {
795   struct var_set *vs;
796   struct array_var_set *avs;
797   size_t i;
798
799   vs = xmalloc (sizeof *vs);
800   vs->get_cnt = array_var_set_get_cnt;
801   vs->get_var = array_var_set_get_var;
802   vs->lookup_var_idx = array_var_set_lookup_var_idx;
803   vs->destroy = array_var_set_destroy;
804   vs->aux = avs = xmalloc (sizeof *avs);
805   avs->var = var;
806   avs->var_cnt = var_cnt;
807   avs->name_tab = hsh_create (2 * var_cnt,
808                               compare_var_ptrs_by_name, hash_var_ptr_by_name,
809                               NULL, NULL);
810   for (i = 0; i < var_cnt; i++)
811     if (hsh_insert (avs->name_tab, (void *) &var[i]) != NULL) 
812       {
813         var_set_destroy (vs);
814         return NULL;
815       }
816   
817   return vs;
818 }