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