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