variable-parser: Fix null deref on error path in parse_DATA_LIST_vars().
[pspp] / src / language / lexer / variable-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU 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, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/lexer/variable-parser.h"
20
21 #include <ctype.h>
22 #include <limits.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/variable.h"
29 #include "language/lexer/lexer.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/cast.h"
32 #include "libpspp/hash-functions.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/hmapx.h"
35 #include "libpspp/message.h"
36 #include "libpspp/misc.h"
37 #include "libpspp/pool.h"
38 #include "libpspp/str.h"
39 #include "libpspp/stringi-set.h"
40
41 #include "math/interaction.h"
42
43 #include "gl/c-ctype.h"
44 #include "gl/xalloc.h"
45
46 #include "gettext.h"
47 #define _(msgid) gettext (msgid)
48
49 static struct variable *var_set_get_var (const struct var_set *, size_t);
50 static struct variable *var_set_lookup_var (const struct var_set *,
51                                             const char *);
52 static bool var_set_lookup_var_idx (const struct var_set *, const char *,
53                                     size_t *);
54 static bool var_set_get_names_must_be_ids (const struct var_set *);
55
56 static bool
57 is_name_token (const struct lexer *lexer, bool names_must_be_ids)
58 {
59   return (lex_token (lexer) == T_ID
60           || (!names_must_be_ids && lex_token (lexer) == T_STRING));
61 }
62
63 static bool
64 is_vs_name_token (const struct lexer *lexer, const struct var_set *vs)
65 {
66   return is_name_token (lexer, var_set_get_names_must_be_ids (vs));
67 }
68
69 static bool
70 is_dict_name_token (const struct lexer *lexer, const struct dictionary *d)
71 {
72   return is_name_token (lexer, dict_get_names_must_be_ids (d));
73 }
74
75 /* Parses a name as a variable within VS.  Sets *IDX to the
76    variable's index and returns true if successful.  On failure
77    emits an error message and returns false. */
78 static bool
79 parse_vs_variable_idx (struct lexer *lexer, const struct var_set *vs,
80                        size_t *idx)
81 {
82   assert (idx != NULL);
83
84   if (!is_vs_name_token (lexer, vs))
85     {
86       lex_error (lexer, _("expecting variable name"));
87       return false;
88     }
89   else if (var_set_lookup_var_idx (vs, lex_tokcstr (lexer), idx))
90     {
91       lex_get (lexer);
92       return true;
93     }
94   else
95     {
96       msg (SE, _("%s is not a variable name."), lex_tokcstr (lexer));
97       return false;
98     }
99 }
100
101 /* Parses a name as a variable within VS and returns the variable
102    if successful.  On failure emits an error message and returns
103    a null pointer. */
104 static struct variable *
105 parse_vs_variable (struct lexer *lexer, const struct var_set *vs)
106 {
107   size_t idx;
108   return parse_vs_variable_idx (lexer, vs, &idx) ? var_set_get_var (vs, idx) : NULL;
109 }
110
111 /* Parses a variable name in dictionary D and returns the
112    variable if successful.  On failure emits an error message and
113    returns a null pointer. */
114 struct variable *
115 parse_variable (struct lexer *lexer, const struct dictionary *d)
116 {
117   struct var_set *vs = var_set_create_from_dict (d);
118   struct variable *var = parse_vs_variable (lexer, vs);
119   var_set_destroy (vs);
120   return var;
121 }
122
123 /* Parses a set of variables from dictionary D given options
124    OPTS.  Resulting list of variables stored in *VAR and the
125    number of variables into *CNT.  Returns true only if
126    successful. */
127 bool
128 parse_variables (struct lexer *lexer, const struct dictionary *d,
129                         struct variable ***var,
130                         size_t *cnt, int opts)
131 {
132   struct var_set *vs;
133   int success;
134
135   assert (d != NULL);
136   assert (var != NULL);
137   assert (cnt != NULL);
138
139   vs = var_set_create_from_dict (d);
140   success = parse_var_set_vars (lexer, vs, var, cnt, opts);
141   var_set_destroy (vs);
142   return success;
143 }
144
145 /* Parses a set of variables from dictionary D given options
146    OPTS.  Resulting list of variables stored in *VARS and the
147    number of variables into *VAR_CNT.  Returns true only if
148    successful.  Same behavior as parse_variables, except that all
149    allocations are taken from the given POOL. */
150 bool
151 parse_variables_pool (struct lexer *lexer, struct pool *pool,
152                 const struct dictionary *dict,
153                 struct variable ***vars, size_t *var_cnt, int opts)
154 {
155   int retval;
156
157   /* PV_APPEND is unsafe because parse_variables would free the
158      existing names on failure, but those names are presumably
159      already in the pool, which would attempt to re-free it
160      later. */
161   assert (!(opts & PV_APPEND));
162
163   retval = parse_variables (lexer, dict, vars, var_cnt, opts);
164   if (retval)
165     pool_register (pool, free, *vars);
166   return retval;
167 }
168
169 /* Parses a variable name from VS.  If successful, sets *IDX to
170    the variable's index in VS, *CLASS to the variable's
171    dictionary class, and returns true.  Returns false on
172    failure. */
173 static bool
174 parse_var_idx_class (struct lexer *lexer, const struct var_set *vs,
175                         size_t *idx,
176                         enum dict_class *class)
177 {
178   if (!parse_vs_variable_idx (lexer, vs, idx))
179     return false;
180
181   *class = dict_class_from_id (var_get_name (var_set_get_var (vs, *idx)));
182   return true;
183 }
184
185 /* Add the variable from VS with index IDX to the list of
186    variables V that has *NV elements and room for *MV.
187    Uses and updates INCLUDED to avoid duplicates if indicated by
188    PV_OPTS, which also affects what variables are allowed in
189    appropriate ways. */
190 static void
191 add_variable (struct variable ***v, size_t *nv, size_t *mv,
192               char *included, int pv_opts,
193               const struct var_set *vs, size_t idx)
194 {
195   struct variable *add = var_set_get_var (vs, idx);
196   const char *add_name = var_get_name (add);
197
198   if ((pv_opts & PV_NUMERIC) && !var_is_numeric (add))
199     msg (SW, _("%s is not a numeric variable.  It will not be "
200                "included in the variable list."), add_name);
201   else if ((pv_opts & PV_STRING) && !var_is_alpha (add))
202     msg (SE, _("%s is not a string variable.  It will not be "
203                "included in the variable list."), add_name);
204   else if ((pv_opts & PV_NO_SCRATCH)
205            && dict_class_from_id (add_name) == DC_SCRATCH)
206     msg (SE, _("Scratch variables (such as %s) are not allowed "
207                "here."), add_name);
208   else if ((pv_opts & (PV_SAME_TYPE | PV_SAME_WIDTH)) && *nv
209            && var_get_type (add) != var_get_type ((*v)[0]))
210     msg (SE, _("%s and %s are not the same type.  All variables in "
211                "this variable list must be of the same type.  %s "
212                "will be omitted from the list."),
213          var_get_name ((*v)[0]), add_name, add_name);
214   else if ((pv_opts & PV_SAME_WIDTH) && *nv
215            && var_get_width (add) != var_get_width ((*v)[0]))
216     msg (SE, _("%s and %s are string variables with different widths.  "
217                "All variables in this variable list must have the "
218                "same width.  %s will be omitted from the list."),
219          var_get_name ((*v)[0]), add_name, add_name);
220   else if ((pv_opts & PV_NO_DUPLICATE) && included && included[idx])
221     msg (SE, _("Variable %s appears twice in variable list."), add_name);
222   else if ((pv_opts & PV_DUPLICATE) || !included || !included[idx])
223     {
224       if (*nv >= *mv)
225         {
226           *mv = 2 * (*nv + 1);
227           *v = xnrealloc (*v, *mv, sizeof **v);
228         }
229       (*v)[(*nv)++] = add;
230       if (included != NULL)
231         included[idx] = 1;
232     }
233 }
234
235 /* Adds the variables in VS with indexes FIRST_IDX through
236    LAST_IDX, inclusive, to the list of variables V that has *NV
237    elements and room for *MV.  Uses and updates INCLUDED to avoid
238    duplicates if indicated by PV_OPTS, which also affects what
239    variables are allowed in appropriate ways. */
240 static void
241 add_variables (struct variable ***v, size_t *nv, size_t *mv, char *included,
242                int pv_opts,
243                const struct var_set *vs, int first_idx, int last_idx,
244                enum dict_class class)
245 {
246   size_t i;
247
248   for (i = first_idx; i <= last_idx; i++)
249     if (dict_class_from_id (var_get_name (var_set_get_var (vs, i))) == class)
250       add_variable (v, nv, mv, included, pv_opts, vs, i);
251 }
252
253 /* Note that if parse_variables() returns false, *v is free()'d.
254    Conversely, if parse_variables() returns true, then *nv is
255    nonzero and *v is non-NULL. */
256 bool
257 parse_var_set_vars (struct lexer *lexer, const struct var_set *vs,
258                     struct variable ***v, size_t *nv,
259                     int pv_opts)
260 {
261   size_t mv;
262   char *included;
263
264   assert (vs != NULL);
265   assert (v != NULL);
266   assert (nv != NULL);
267
268   /* At most one of PV_NUMERIC, PV_STRING, PV_SAME_TYPE,
269      PV_SAME_WIDTH may be specified. */
270   assert (((pv_opts & PV_NUMERIC) != 0)
271           + ((pv_opts & PV_STRING) != 0)
272           + ((pv_opts & PV_SAME_TYPE) != 0)
273           + ((pv_opts & PV_SAME_WIDTH) != 0) <= 1);
274
275   /* PV_DUPLICATE and PV_NO_DUPLICATE are incompatible. */
276   assert (!(pv_opts & PV_DUPLICATE) || !(pv_opts & PV_NO_DUPLICATE));
277
278   if (!(pv_opts & PV_APPEND))
279     {
280       *v = NULL;
281       *nv = 0;
282       mv = 0;
283     }
284   else
285     mv = *nv;
286
287   if (!(pv_opts & PV_DUPLICATE))
288     {
289       size_t i;
290
291       included = xcalloc (var_set_get_cnt (vs), sizeof *included);
292       for (i = 0; i < *nv; i++)
293         {
294           size_t index;
295           if (!var_set_lookup_var_idx (vs, var_get_name ((*v)[i]), &index))
296             NOT_REACHED ();
297           included[index] = 1;
298         }
299     }
300   else
301     included = NULL;
302
303   do
304     {
305       if (lex_match (lexer, T_ALL))
306         add_variables (v, nv, &mv, included, pv_opts,
307                        vs, 0, var_set_get_cnt (vs) - 1, DC_ORDINARY);
308       else
309         {
310           enum dict_class class;
311           size_t first_idx;
312
313           if (!parse_var_idx_class (lexer, vs, &first_idx, &class))
314             goto fail;
315
316           if (!lex_match (lexer, T_TO))
317             add_variable (v, nv, &mv, included, pv_opts, vs, first_idx);
318           else
319             {
320               size_t last_idx;
321               enum dict_class last_class;
322               struct variable *first_var, *last_var;
323
324               if (!parse_var_idx_class (lexer, vs, &last_idx, &last_class))
325                 goto fail;
326
327               first_var = var_set_get_var (vs, first_idx);
328               last_var = var_set_get_var (vs, last_idx);
329
330               if (last_idx < first_idx)
331                 {
332                   const char *first_name = var_get_name (first_var);
333                   const char *last_name = var_get_name (last_var);
334                   msg (SE, _("%s TO %s is not valid syntax since %s "
335                              "precedes %s in the dictionary."),
336                        first_name, last_name, first_name, last_name);
337                   goto fail;
338                 }
339
340               if (class != last_class)
341                 {
342                   msg (SE, _("When using the TO keyword to specify several "
343                              "variables, both variables must be from "
344                              "the same variable dictionaries, of either "
345                              "ordinary, scratch, or system variables.  "
346                              "%s is a %s variable, whereas %s is %s."),
347                        var_get_name (first_var), dict_class_to_name (class),
348                        var_get_name (last_var),
349                        dict_class_to_name (last_class));
350                   goto fail;
351                 }
352
353               add_variables (v, nv, &mv, included, pv_opts,
354                              vs, first_idx, last_idx, class);
355             }
356         }
357
358       if (pv_opts & PV_SINGLE)
359         break;
360       lex_match (lexer, T_COMMA);
361     }
362   while (lex_token (lexer) == T_ALL
363          || (is_vs_name_token (lexer, vs)
364              && var_set_lookup_var (vs, lex_tokcstr (lexer)) != NULL));
365
366   if (*nv == 0)
367     goto fail;
368
369   free (included);
370   return 1;
371
372 fail:
373   free (included);
374   free (*v);
375   *v = NULL;
376   *nv = 0;
377   return 0;
378 }
379
380 char *
381 parse_DATA_LIST_var (struct lexer *lexer, const struct dictionary *d)
382 {
383   if (!is_dict_name_token (lexer, d))
384     {
385       lex_error (lexer, "expecting variable name");
386       return NULL;
387     }
388   if (!dict_id_is_valid (d, lex_tokcstr (lexer), true))
389     return NULL;
390
391   char *name = xstrdup (lex_tokcstr (lexer));
392   lex_get (lexer);
393   return name;
394 }
395
396 /* Attempts to break UTF-8 encoded NAME into a root (whose contents are
397    arbitrary except that it does not end in a digit) followed by an integer
398    numeric suffix.  On success, stores the value of the suffix into *NUMBERP,
399    the number of digits in the suffix into *N_DIGITSP, and returns the number
400    of bytes in the root.  On failure, returns 0. */
401 static int
402 extract_numeric_suffix (const char *name,
403                         unsigned long int *numberp, int *n_digitsp)
404 {
405   size_t root_len, n_digits;
406   size_t i;
407
408   /* Count length of root. */
409   root_len = 1;                 /* Valid identifier never starts with digit. */
410   for (i = 1; name[i] != '\0'; i++)
411     if (!c_isdigit (name[i]))
412       root_len = i + 1;
413   n_digits = i - root_len;
414
415   if (n_digits == 0)
416     {
417       msg (SE, _("`%s' cannot be used with TO because it does not end in "
418                  "a digit."), name);
419       return 0;
420     }
421
422   *numberp = strtoull (name + root_len, NULL, 10);
423   if (*numberp == ULONG_MAX)
424     {
425       msg (SE, _("Numeric suffix on `%s' is larger than supported with TO."),
426            name);
427       return 0;
428     }
429   *n_digitsp = n_digits;
430   return root_len;
431 }
432
433 static bool
434 add_var_name (char *name,
435               char ***names, size_t *n_vars, size_t *allocated_vars,
436               struct stringi_set *set, int pv_opts)
437 {
438   if (pv_opts & PV_NO_DUPLICATE && !stringi_set_insert (set, name))
439     {
440       msg (SE, _("Variable %s appears twice in variable list."),
441            name);
442       return false;
443     }
444
445   if (*n_vars >= *allocated_vars)
446     *names = x2nrealloc (*names, allocated_vars, sizeof **names);
447   (*names)[(*n_vars)++] = name;
448   return true;
449 }
450
451 /* Parses a list of variable names according to the DATA LIST version
452    of the TO convention.  */
453 bool
454 parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
455                       char ***namesp, size_t *n_varsp, int pv_opts)
456 {
457   char **names;
458   size_t n_vars;
459   size_t allocated_vars;
460
461   struct stringi_set set;
462
463   char *name1 = NULL;
464   char *name2 = NULL;
465
466   bool ok = false;
467
468   assert ((pv_opts & ~(PV_APPEND | PV_SINGLE
469                        | PV_NO_SCRATCH | PV_NO_DUPLICATE)) == 0);
470   stringi_set_init (&set);
471
472   if (pv_opts & PV_APPEND)
473     {
474       n_vars = allocated_vars = *n_varsp;
475       names = *namesp;
476
477       if (pv_opts & PV_NO_DUPLICATE)
478         {
479           size_t i;
480
481           for (i = 0; i < n_vars; i++)
482             stringi_set_insert (&set, names[i]);
483         }
484     }
485   else
486     {
487       n_vars = allocated_vars = 0;
488       names = NULL;
489     }
490
491   do
492     {
493       name1 = parse_DATA_LIST_var (lexer, dict);
494       if (!name1)
495         goto exit;
496       if (dict_class_from_id (name1) == DC_SCRATCH && pv_opts & PV_NO_SCRATCH)
497         {
498           msg (SE, _("Scratch variables not allowed here."));
499           goto exit;
500         }
501       if (lex_match (lexer, T_TO))
502         {
503           unsigned long int num1, num2;
504           int n_digits1, n_digits2;
505           int root_len1, root_len2;
506           unsigned long int number;
507
508           name2 = parse_DATA_LIST_var (lexer, dict);
509           if (!name2)
510             goto exit;
511
512           root_len1 = extract_numeric_suffix (name1, &num1, &n_digits1);
513           if (root_len1 == 0)
514             goto exit;
515
516           root_len2 = extract_numeric_suffix (name2, &num2, &n_digits2);
517           if (root_len2 == 0)
518             goto exit;
519
520           if (root_len1 != root_len2 || memcasecmp (name1, name2, root_len1))
521             {
522               msg (SE, _("Prefixes don't match in use of TO convention."));
523               goto exit;
524             }
525           if (num1 > num2)
526             {
527               msg (SE, _("Bad bounds in use of TO convention."));
528               goto exit;
529             }
530
531           for (number = num1; number <= num2; number++)
532             {
533               char *name = xasprintf ("%.*s%0*lu",
534                                       root_len1, name1,
535                                       n_digits1, number);
536               if (!add_var_name (name, &names, &n_vars, &allocated_vars,
537                                  &set, pv_opts))
538                 {
539                   free (name);
540                   goto exit;
541                 }
542             }
543
544           free (name1);
545           name1 = NULL;
546           free (name2);
547           name2 = NULL;
548         }
549       else
550         {
551           if (!add_var_name (name1, &names, &n_vars, &allocated_vars,
552                              &set, pv_opts))
553             goto exit;
554           name1 = NULL;
555         }
556
557       lex_match (lexer, T_COMMA);
558
559       if (pv_opts & PV_SINGLE)
560         break;
561     }
562   while (lex_token (lexer) == T_ID);
563   ok = true;
564
565 exit:
566   stringi_set_destroy (&set);
567   if (ok)
568     {
569       *namesp = names;
570       *n_varsp = n_vars;
571     }
572   else
573     {
574       int i;
575       for (i = 0; i < n_vars; i++)
576         free (names[i]);
577       free (names);
578       *namesp = NULL;
579       *n_varsp = 0;
580
581       free (name1);
582       free (name2);
583     }
584   return ok;
585 }
586
587 /* Registers each of the NAMES[0...NNAMES - 1] in POOL, as well
588    as NAMES itself. */
589 static void
590 register_vars_pool (struct pool *pool, char **names, size_t nnames)
591 {
592   size_t i;
593
594   for (i = 0; i < nnames; i++)
595     pool_register (pool, free, names[i]);
596   pool_register (pool, free, names);
597 }
598
599 /* Parses a list of variable names according to the DATA LIST
600    version of the TO convention.  Same args as
601    parse_DATA_LIST_vars(), except that all allocations are taken
602    from the given POOL. */
603 bool
604 parse_DATA_LIST_vars_pool (struct lexer *lexer, const struct dictionary *dict,
605                            struct pool *pool,
606                            char ***names, size_t *nnames, int pv_opts)
607 {
608   int retval;
609
610   /* PV_APPEND is unsafe because parse_DATA_LIST_vars would free
611      the existing names on failure, but those names are
612      presumably already in the pool, which would attempt to
613      re-free it later. */
614   assert (!(pv_opts & PV_APPEND));
615
616   retval = parse_DATA_LIST_vars (lexer, dict, names, nnames, pv_opts);
617   if (retval)
618     register_vars_pool (pool, *names, *nnames);
619   return retval;
620 }
621
622 /* Parses a list of variables where some of the variables may be
623    existing and the rest are to be created.  Same args as
624    parse_DATA_LIST_vars(). */
625 bool
626 parse_mixed_vars (struct lexer *lexer, const struct dictionary *dict,
627                   char ***names, size_t *nnames, int pv_opts)
628 {
629   size_t i;
630
631   assert (names != NULL);
632   assert (nnames != NULL);
633
634   if (!(pv_opts & PV_APPEND))
635     {
636       *names = NULL;
637       *nnames = 0;
638     }
639   while (is_dict_name_token (lexer, dict) || lex_token (lexer) == T_ALL)
640     {
641       if (lex_token (lexer) == T_ALL || dict_lookup_var (dict, lex_tokcstr (lexer)) != NULL)
642         {
643           struct variable **v;
644           size_t nv;
645
646           if (!parse_variables (lexer, dict, &v, &nv, pv_opts))
647             goto fail;
648           *names = xnrealloc (*names, *nnames + nv, sizeof **names);
649           for (i = 0; i < nv; i++)
650             (*names)[*nnames + i] = xstrdup (var_get_name (v[i]));
651           free (v);
652           *nnames += nv;
653         }
654       else if (!parse_DATA_LIST_vars (lexer, dict, names, nnames, PV_APPEND | pv_opts))
655         goto fail;
656     }
657   if (*nnames == 0)
658     goto fail;
659
660   return true;
661
662 fail:
663   for (i = 0; i < *nnames; i++)
664     free ((*names)[i]);
665   free (*names);
666   *names = NULL;
667   *nnames = 0;
668   return false;
669 }
670
671 /* Parses a list of variables where some of the variables may be
672    existing and the rest are to be created.  Same args as
673    parse_mixed_vars(), except that all allocations are taken
674    from the given POOL. */
675 bool
676 parse_mixed_vars_pool (struct lexer *lexer, const struct dictionary *dict, struct pool *pool,
677                        char ***names, size_t *nnames, int pv_opts)
678 {
679   int retval;
680
681   /* PV_APPEND is unsafe because parse_mixed_vars_pool would free
682      the existing names on failure, but those names are
683      presumably already in the pool, which would attempt to
684      re-free it later. */
685   assert (!(pv_opts & PV_APPEND));
686
687   retval = parse_mixed_vars (lexer, dict, names, nnames, pv_opts);
688   if (retval)
689     register_vars_pool (pool, *names, *nnames);
690   return retval;
691 }
692 \f
693 /* A set of variables. */
694 struct var_set
695   {
696     bool names_must_be_ids;
697     size_t (*get_cnt) (const struct var_set *);
698     struct variable *(*get_var) (const struct var_set *, size_t idx);
699     bool (*lookup_var_idx) (const struct var_set *, const char *, size_t *);
700     void (*destroy) (struct var_set *);
701     void *aux;
702   };
703
704 /* Returns the number of variables in VS. */
705 size_t
706 var_set_get_cnt (const struct var_set *vs)
707 {
708   assert (vs != NULL);
709
710   return vs->get_cnt (vs);
711 }
712
713 /* Return variable with index IDX in VS.
714    IDX must be less than the number of variables in VS. */
715 static struct variable *
716 var_set_get_var (const struct var_set *vs, size_t idx)
717 {
718   assert (vs != NULL);
719   assert (idx < var_set_get_cnt (vs));
720
721   return vs->get_var (vs, idx);
722 }
723
724 /* Returns the variable in VS named NAME, or a null pointer if VS
725    contains no variable with that name. */
726 struct variable *
727 var_set_lookup_var (const struct var_set *vs, const char *name)
728 {
729   size_t idx;
730   return (var_set_lookup_var_idx (vs, name, &idx)
731           ? var_set_get_var (vs, idx)
732           : NULL);
733 }
734
735 /* If VS contains a variable named NAME, sets *IDX to its index
736    and returns true.  Otherwise, returns false. */
737 bool
738 var_set_lookup_var_idx (const struct var_set *vs, const char *name,
739                         size_t *idx)
740 {
741   assert (vs != NULL);
742   assert (name != NULL);
743
744   return vs->lookup_var_idx (vs, name, idx);
745 }
746
747 /* Destroys VS. */
748 void
749 var_set_destroy (struct var_set *vs)
750 {
751   if (vs != NULL)
752     vs->destroy (vs);
753 }
754
755 static bool
756 var_set_get_names_must_be_ids (const struct var_set *vs)
757 {
758   return vs->names_must_be_ids;
759 }
760 \f
761 /* Returns the number of variables in VS. */
762 static size_t
763 dict_var_set_get_cnt (const struct var_set *vs)
764 {
765   struct dictionary *d = vs->aux;
766
767   return dict_get_var_cnt (d);
768 }
769
770 /* Return variable with index IDX in VS.
771    IDX must be less than the number of variables in VS. */
772 static struct variable *
773 dict_var_set_get_var (const struct var_set *vs, size_t idx)
774 {
775   struct dictionary *d = vs->aux;
776
777   return dict_get_var (d, idx);
778 }
779
780 /* If VS contains a variable named NAME, sets *IDX to its index
781    and returns true.  Otherwise, returns false. */
782 static bool
783 dict_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
784                              size_t *idx)
785 {
786   struct dictionary *d = vs->aux;
787   struct variable *v = dict_lookup_var (d, name);
788   if (v != NULL)
789     {
790       *idx = var_get_dict_index (v);
791       return true;
792     }
793   else
794     return false;
795 }
796
797 /* Destroys VS. */
798 static void
799 dict_var_set_destroy (struct var_set *vs)
800 {
801   free (vs);
802 }
803
804 /* Returns a variable set based on D. */
805 struct var_set *
806 var_set_create_from_dict (const struct dictionary *d)
807 {
808   struct var_set *vs = xmalloc (sizeof *vs);
809   vs->names_must_be_ids = dict_get_names_must_be_ids (d);
810   vs->get_cnt = dict_var_set_get_cnt;
811   vs->get_var = dict_var_set_get_var;
812   vs->lookup_var_idx = dict_var_set_lookup_var_idx;
813   vs->destroy = dict_var_set_destroy;
814   vs->aux = (void *) d;
815   return vs;
816 }
817 \f
818 /* A variable set based on an array. */
819 struct array_var_set
820   {
821     struct variable *const *var;/* Array of variables. */
822     size_t var_cnt;             /* Number of elements in var. */
823     struct hmapx vars_by_name;  /* Variables hashed by name. */
824   };
825
826 /* Returns the number of variables in VS. */
827 static size_t
828 array_var_set_get_cnt (const struct var_set *vs)
829 {
830   struct array_var_set *avs = vs->aux;
831
832   return avs->var_cnt;
833 }
834
835 /* Return variable with index IDX in VS.
836    IDX must be less than the number of variables in VS. */
837 static struct variable *
838 array_var_set_get_var (const struct var_set *vs, size_t idx)
839 {
840   struct array_var_set *avs = vs->aux;
841
842   return CONST_CAST (struct variable *, avs->var[idx]);
843 }
844
845 /* If VS contains a variable named NAME, sets *IDX to its index
846    and returns true.  Otherwise, returns false. */
847 static bool
848 array_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
849                               size_t *idx)
850 {
851   struct array_var_set *avs = vs->aux;
852   struct hmapx_node *node;
853   struct variable **varp;
854
855   HMAPX_FOR_EACH_WITH_HASH (varp, node, utf8_hash_case_string (name, 0),
856                             &avs->vars_by_name)
857     if (!utf8_strcasecmp (name, var_get_name (*varp)))
858       {
859         *idx = varp - avs->var;
860         return true;
861       }
862
863   return false;
864 }
865
866 /* Destroys VS. */
867 static void
868 array_var_set_destroy (struct var_set *vs)
869 {
870   struct array_var_set *avs = vs->aux;
871
872   hmapx_destroy (&avs->vars_by_name);
873   free (avs);
874   free (vs);
875 }
876
877 /* Returns a variable set based on the VAR_CNT variables in VAR. */
878 struct var_set *
879 var_set_create_from_array (struct variable *const *var, size_t var_cnt)
880 {
881   struct var_set *vs;
882   struct array_var_set *avs;
883   size_t i;
884
885   vs = xmalloc (sizeof *vs);
886   vs->names_must_be_ids = true;
887   vs->get_cnt = array_var_set_get_cnt;
888   vs->get_var = array_var_set_get_var;
889   vs->lookup_var_idx = array_var_set_lookup_var_idx;
890   vs->destroy = array_var_set_destroy;
891   vs->aux = avs = xmalloc (sizeof *avs);
892   avs->var = var;
893   avs->var_cnt = var_cnt;
894   hmapx_init (&avs->vars_by_name);
895   for (i = 0; i < var_cnt; i++)
896     {
897       const char *name = var_get_name (var[i]);
898       size_t idx;
899
900       if (array_var_set_lookup_var_idx (vs, name, &idx))
901         {
902           var_set_destroy (vs);
903           return NULL;
904         }
905       hmapx_insert (&avs->vars_by_name, CONST_CAST (void *, &avs->var[i]),
906                     utf8_hash_case_string (name, 0));
907     }
908
909   return vs;
910 }
911
912
913 /* Match a variable.
914    If the match succeeds, the variable will be placed in VAR.
915    Returns true if successful */
916 bool
917 lex_match_variable (struct lexer *lexer, const struct dictionary *dict, const struct variable **var)
918 {
919   if (lex_token (lexer) !=  T_ID)
920     return false;
921
922   *var = parse_variable_const  (lexer, dict);
923
924   if ( *var == NULL)
925     return false;
926   return true;
927 }
928
929 /* An interaction is a variable followed by {*, BY} followed by an interaction */
930 static bool
931 parse_internal_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact, struct interaction **it)
932 {
933   const struct variable *v = NULL;
934   assert (iact);
935
936   switch  (lex_next_token (lexer, 1))
937     {
938     case T_ENDCMD:
939     case T_SLASH:
940     case T_COMMA:
941     case T_ID:
942     case T_BY:
943     case T_ASTERISK:
944       break;
945     default:
946       return false;
947       break;
948     }
949
950   if (! lex_match_variable (lexer, dict, &v))
951     {
952       if (it)
953         interaction_destroy (*it);
954       *iact = NULL;
955       return false;
956     }
957
958   assert (v);
959
960   if ( *iact == NULL)
961     *iact = interaction_create (v);
962   else
963     interaction_add_variable (*iact, v);
964
965   if ( lex_match (lexer, T_ASTERISK) || lex_match (lexer, T_BY))
966     {
967       return parse_internal_interaction (lexer, dict, iact, iact);
968     }
969
970   return true;
971 }
972
973 bool
974 parse_design_interaction (struct lexer *lexer, const struct dictionary *dict, struct interaction **iact)
975 {
976   return parse_internal_interaction (lexer, dict, iact, NULL);
977 }
978