Sat Dec 27 16:16:49 2003 Ben Pfaff <blp@gnu.org>
[pspp-builds.git] / src / vars-prs.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 /* AIX requires this to be the first thing in the file.  */
21 #include <config.h>
22 #if __GNUC__
23 #define alloca __builtin_alloca
24 #else
25 #if HAVE_ALLOCA_H
26 #include <alloca.h>
27 #else
28 #ifdef _AIX
29 #pragma alloca
30 #else
31 #ifndef alloca                  /* predefined by HP cc +Olibcalls */
32 char *alloca ();
33 #endif
34 #endif
35 #endif
36 #endif
37
38 #include "var.h"
39 #include <assert.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42 #include "alloc.h"
43 #include "bitvector.h"
44 #include "error.h"
45 #include "hash.h"
46 #include "lexer.h"
47 #include "misc.h"
48 #include "str.h"
49
50 static struct variable *
51 parse_vs_variable (struct var_set *vs)
52 {
53   struct variable *vp;
54
55   if (token != T_ID)
56     {
57       lex_error ("expecting variable name");
58       return NULL;
59     }
60
61   vp = var_set_lookup_var (vs, tokid);
62   if (vp == NULL)
63     msg (SE, _("%s is not a variable name."), tokid);
64   lex_get ();
65
66   return vp;
67 }
68
69 struct variable *
70 parse_dict_variable (struct dictionary *d) 
71 {
72   struct var_set *vs = var_set_create_from_dict (d);
73   struct variable *var = parse_vs_variable (vs);
74   var_set_destroy (vs);
75   return var;
76 }
77
78 struct variable *
79 parse_variable (void)
80 {
81   return parse_dict_variable (default_dict);
82 }
83
84
85 enum dict_class
86 dict_class_from_id (const char *name) 
87 {
88   assert (name != NULL);
89
90   switch (name[0]) 
91     {
92     default:
93       return DC_ORDINARY;
94     case '$':
95       return DC_SYSTEM;
96     case '#':
97       return DC_SCRATCH;
98     }
99 }
100
101 const char *
102 dict_class_to_name (enum dict_class dict_class) 
103 {
104   switch (dict_class) 
105     {
106     case DC_ORDINARY:
107       return "ordinary";
108     case DC_SYSTEM:
109       return "system";
110     case DC_SCRATCH:
111       return "scratch";
112     default:
113       assert (0);
114     }
115 }
116
117 int
118 parse_variables (struct dictionary *d, struct variable ***var, int *cnt,
119                  int opts) 
120 {
121   struct var_set *vs;
122   int success;
123
124   assert (d != NULL);
125   assert (var != NULL);
126   assert (cnt != NULL);
127
128   vs = var_set_create_from_dict (d);
129   success = parse_var_set_vars (vs, var, cnt, opts);
130   var_set_destroy (vs);
131   return success;
132 }
133
134 /* Note that if parse_variables() returns 0, *v is free()'d.
135    Conversely, if parse_variables() returns non-zero, then *nv is
136    nonzero and *v is non-NULL. */
137 int
138 parse_var_set_vars (struct var_set *vs, 
139                     struct variable ***v, int *nv,
140                     int pv_opts)
141 {
142   size_t vs_var_cnt;
143   int i;
144   char *included;
145
146   struct variable *v1, *v2;
147   int count, mv;
148   enum dict_class dict_class;
149
150   assert (vs != NULL);
151   assert (v != NULL);
152   assert (nv != NULL);
153
154   /* At most one of PV_NUMERIC, PV_STRING, PV_SAME_TYPE may be
155      specified. */
156   assert ((((pv_opts & PV_NUMERIC) != 0)
157            + ((pv_opts & PV_STRING) != 0)
158            + ((pv_opts & PV_SAME_TYPE) != 0)) <= 1);
159
160   /* PV_DUPLICATE and PV_NO_DUPLICATE are incompatible. */
161   assert (!(pv_opts & PV_DUPLICATE) || !(pv_opts & PV_NO_DUPLICATE));
162
163   vs_var_cnt = var_set_get_cnt (vs);
164
165   if (!(pv_opts & PV_APPEND))
166     {
167       *v = NULL;
168       *nv = 0;
169       mv = 0;
170     }
171   else
172     mv = *nv;
173
174   if (!(pv_opts & PV_DUPLICATE))
175     {
176       included = xmalloc (vs_var_cnt);
177       memset (included, 0, vs_var_cnt);
178       for (i = 0; i < *nv; i++)
179         included[(*v)[i]->index] = 1;
180     }
181
182   do
183     {
184       if (lex_match (T_ALL))
185         {
186           v1 = var_set_get_var (vs, 0);
187           v2 = var_set_get_var (vs, vs_var_cnt - 1);
188           count = vs_var_cnt;
189           dict_class = DC_ORDINARY;
190         }
191       else
192         {
193           v1 = parse_vs_variable (vs);
194           if (!v1)
195             goto fail;
196
197           if (lex_match (T_TO))
198             {
199               enum dict_class dict_class_2;
200
201               v2 = parse_vs_variable (vs);
202               if (!v2)
203                 {
204                   lex_error ("expecting variable name");
205                   goto fail;
206                 }
207
208               count = v2->index - v1->index + 1;
209               if (count < 1)
210                 {
211                   msg (SE, _("%s TO %s is not valid syntax since %s "
212                        "precedes %s in the dictionary."),
213                        v1->name, v2->name, v2->name, v1->name);
214                   goto fail;
215                 }
216
217               dict_class = dict_class_from_id (v1->name);
218               dict_class_2 = dict_class_from_id (v2->name);
219               if (dict_class != dict_class_2)
220                 {
221                   msg (SE, _("When using the TO keyword to specify several "
222                              "variables, both variables must be from "
223                              "the same variable dictionaries, of either "
224                              "ordinary, scratch, or system variables.  "
225                              "%s is a %s variable, whereas %s is %s."),
226                        v1->name, dict_class_to_name (dict_class),
227                        v2->name, dict_class_to_name (dict_class_2));
228                   goto fail;
229                 }
230             }
231           else
232             {
233               v2 = v1;
234               count = 1;
235               dict_class = dict_class_from_id (v1->name);
236             }
237           if (dict_class == DC_SCRATCH && (pv_opts & PV_NO_SCRATCH))
238             {
239               msg (SE, _("Scratch variables (such as %s) are not allowed "
240                          "here."), v1->name);
241               goto fail;
242             }
243         }
244
245       if (*nv + count > mv)
246         {
247           mv += ROUND_UP (count, 16);
248           *v = xrealloc (*v, mv * sizeof **v);
249         }
250
251       /* Add v1...v2 to the list. */
252       for (i = v1->index; i <= v2->index; i++)
253         {
254           struct variable *add = var_set_get_var (vs, i);
255
256           /* Skip over other dictionaries. */
257           if (dict_class != dict_class_from_id (add->name))
258             continue;
259
260           /* Different kinds of errors. */
261           if ((pv_opts & PV_NUMERIC) && add->type != NUMERIC)
262             msg (SW, _("%s is not a numeric variable.  It will not be "
263                        "included in the variable list."), add->name);
264           else if ((pv_opts & PV_STRING) && add->type != ALPHA)
265             msg (SE, _("%s is not a string variable.  It will not be "
266                        "included in the variable list."), add->name);
267           else if ((pv_opts & PV_SAME_TYPE) && *nv
268                    && add->type != (*v)[0]->type)
269             msg (SE, _("%s and %s are not the same type.  All variables in "
270                        "this variable list must be of the same type.  %s "
271                        "will be omitted from list."),
272                  (*v)[0]->name, add->name, add->name);
273           else if ((pv_opts & PV_NO_DUPLICATE) && included[add->index])
274             msg (SE, _("Variable %s appears twice in variable list."),
275                  add->name);
276           else {
277             /* Success--add the variable to the list. */
278             if ((pv_opts & PV_DUPLICATE) || !included[add->index])
279               {
280                 (*v)[(*nv)++] = var_set_get_var (vs, i);
281                 if (!(pv_opts & PV_DUPLICATE))
282                   included[add->index] = 1;
283               }
284
285             /* Next. */
286             continue;
287           }
288
289           /* Arrive here only on failure. */
290           if (pv_opts & PV_SINGLE)
291             goto fail;
292         }
293
294       /* We finished adding v1...v2 to the list. */
295       if (pv_opts & PV_SINGLE)
296         return 1;
297       lex_match (',');
298     }
299   while ((token == T_ID && var_set_lookup_var (vs, tokid) != NULL)
300          || token == T_ALL);
301
302   if (!(pv_opts & PV_DUPLICATE))
303     free (included);
304   if (!*nv)
305     goto fail;
306   return 1;
307
308 fail:
309   free (*v);
310   *v = NULL;
311   *nv = 0;
312   if (!(pv_opts & PV_DUPLICATE))
313     free (included);
314   return 0;
315 }
316
317 static int
318 extract_num (char *s, char *r, int *n, int *d)
319 {
320   char *cp;
321
322   /* Find first digit. */
323   cp = s + strlen (s) - 1;
324   while (isdigit ((unsigned char) *cp) && cp > s)
325     cp--;
326   cp++;
327
328   /* Extract root. */
329   strncpy (r, s, cp - s);
330   r[cp - s] = 0;
331
332   /* Count initial zeros. */
333   *n = *d = 0;
334   while (*cp == '0')
335     {
336       (*d)++;
337       cp++;
338     }
339
340   /* Extract value. */
341   while (isdigit ((unsigned char) *cp))
342     {
343       (*d)++;
344       *n = (*n * 10) + (*cp - '0');
345       cp++;
346     }
347
348   /* Sanity check. */
349   if (*n == 0 && *d == 0)
350     {
351       msg (SE, _("incorrect use of TO convention"));
352       return 0;
353     }
354   return 1;
355 }
356
357 /* Parses a list of variable names according to the DATA LIST version
358    of the TO convention.  */
359 int
360 parse_DATA_LIST_vars (char ***names, int *nnames, int pv_opts)
361 {
362   int n1, n2;
363   int d1, d2;
364   int n;
365   int nvar, mvar;
366   char *name1, *name2;
367   char *root1, *root2;
368   int success = 0;
369
370   assert (names != NULL);
371   assert (nnames != NULL);
372   assert ((pv_opts & ~(PV_APPEND | PV_SINGLE
373                        | PV_NO_SCRATCH | PV_NO_DUPLICATE)) == 0);
374   /* FIXME: PV_NO_DUPLICATE is not implemented. */
375
376   if (pv_opts & PV_APPEND)
377     nvar = mvar = *nnames;
378   else
379     {
380       nvar = mvar = 0;
381       *names = NULL;
382     }
383
384   name1 = xmalloc (36);
385   name2 = &name1[1 * 9];
386   root1 = &name1[2 * 9];
387   root2 = &name1[3 * 9];
388   do
389     {
390       if (token != T_ID)
391         {
392           lex_error ("expecting variable name");
393           goto fail;
394         }
395       if (tokid[0] == '#' && (pv_opts & PV_NO_SCRATCH))
396         {
397           msg (SE, _("Scratch variables not allowed here."));
398           goto fail;
399         }
400       strcpy (name1, tokid);
401       lex_get ();
402       if (token == T_TO)
403         {
404           lex_get ();
405           if (token != T_ID)
406             {
407               lex_error ("expecting variable name");
408               goto fail;
409             }
410           strcpy (name2, tokid);
411           lex_get ();
412
413           if (!extract_num (name1, root1, &n1, &d1)
414               || !extract_num (name2, root2, &n2, &d2))
415             goto fail;
416
417           if (strcmp (root1, root2))
418             {
419               msg (SE, _("Prefixes don't match in use of TO convention."));
420               goto fail;
421             }
422           if (n1 > n2)
423             {
424               msg (SE, _("Bad bounds in use of TO convention."));
425               goto fail;
426             }
427           if (d2 > d1)
428             d2 = d1;
429
430           if (mvar < nvar + (n2 - n1 + 1))
431             {
432               mvar += ROUND_UP (n2 - n1 + 1, 16);
433               *names = xrealloc (*names, mvar * sizeof **names);
434             }
435
436           for (n = n1; n <= n2; n++)
437             {
438               (*names)[nvar] = xmalloc (9);
439               sprintf ((*names)[nvar], "%s%0*d", root1, d1, n);
440               nvar++;
441             }
442         }
443       else
444         {
445           if (nvar >= mvar)
446             {
447               mvar += 16;
448               *names = xrealloc (*names, mvar * sizeof **names);
449             }
450           (*names)[nvar++] = xstrdup (name1);
451         }
452
453       lex_match (',');
454
455       if (pv_opts & PV_SINGLE)
456         break;
457     }
458   while (token == T_ID);
459   success = 1;
460
461 fail:
462   *nnames = nvar;
463   free (name1);
464   if (!success)
465     {
466       int i;
467       for (i = 0; i < nvar; i++)
468         free ((*names)[i]);
469       free (*names);
470       *names = NULL;
471       *nnames = 0;
472     }
473   return success;
474 }
475
476 /* Parses a list of variables where some of the variables may be
477    existing and the rest are to be created.  Same args as
478    parse_DATA_LIST_vars(). */
479 int
480 parse_mixed_vars (char ***names, int *nnames, int pv_opts)
481 {
482   int i;
483
484   assert (names != NULL);
485   assert (nnames != NULL);
486   assert ((pv_opts & ~PV_APPEND) == 0);
487
488   if (!(pv_opts & PV_APPEND))
489     {
490       *names = NULL;
491       *nnames = 0;
492     }
493   while (token == T_ID || token == T_ALL)
494     {
495       if (token == T_ALL || dict_lookup_var (default_dict, tokid) != NULL)
496         {
497           struct variable **v;
498           int nv;
499
500           if (!parse_variables (default_dict, &v, &nv, PV_NONE))
501             goto fail;
502           *names = xrealloc (*names, (*nnames + nv) * sizeof **names);
503           for (i = 0; i < nv; i++)
504             (*names)[*nnames + i] = xstrdup (v[i]->name);
505           free (v);
506           *nnames += nv;
507         }
508       else if (!parse_DATA_LIST_vars (names, nnames, PV_APPEND))
509         goto fail;
510     }
511   return 1;
512
513 fail:
514   for (i = 0; i < *nnames; i++)
515     free ((*names)[*nnames]);
516   free (names);
517   *names = NULL;
518   *nnames = 0;
519   return 0;
520 }
521 \f
522 struct var_set 
523   {
524     size_t (*get_cnt) (struct var_set *);
525     struct variable *(*get_var) (struct var_set *, size_t idx);
526     struct variable *(*lookup_var) (struct var_set *, const char *);
527     void (*destroy) (struct var_set *);
528     void *aux;
529   };
530
531 size_t
532 var_set_get_cnt (struct var_set *vs) 
533 {
534   assert (vs != NULL);
535
536   return vs->get_cnt (vs);
537 }
538
539 struct variable *
540 var_set_get_var (struct var_set *vs, size_t idx) 
541 {
542   assert (vs != NULL);
543   assert (idx < var_set_get_cnt (vs));
544
545   return vs->get_var (vs, idx);
546 }
547
548 struct variable *
549 var_set_lookup_var (struct var_set *vs, const char *name) 
550 {
551   assert (vs != NULL);
552   assert (name != NULL);
553   assert (strlen (name) < 9);
554
555   return vs->lookup_var (vs, name);
556 }
557
558 void
559 var_set_destroy (struct var_set *vs) 
560 {
561   if (vs != NULL)
562     vs->destroy (vs);
563 }
564 \f
565 static size_t
566 dict_var_set_get_cnt (struct var_set *vs) 
567 {
568   struct dictionary *d = vs->aux;
569
570   return dict_get_var_cnt (d);
571 }
572
573 static struct variable *
574 dict_var_set_get_var (struct var_set *vs, size_t idx) 
575 {
576   struct dictionary *d = vs->aux;
577
578   return dict_get_var (d, idx);
579 }
580
581 static struct variable *
582 dict_var_set_lookup_var (struct var_set *vs, const char *name) 
583 {
584   struct dictionary *d = vs->aux;
585
586   return dict_lookup_var (d, name);
587 }
588
589 static void
590 dict_var_set_destroy (struct var_set *vs) 
591 {
592   free (vs);
593 }
594
595 struct var_set *
596 var_set_create_from_dict (struct dictionary *d) 
597 {
598   struct var_set *vs = xmalloc (sizeof *vs);
599   vs->get_cnt = dict_var_set_get_cnt;
600   vs->get_var = dict_var_set_get_var;
601   vs->lookup_var = dict_var_set_lookup_var;
602   vs->destroy = dict_var_set_destroy;
603   vs->aux = d;
604   return vs;
605 }
606 \f
607 struct array_var_set 
608   {
609     struct variable **var;
610     size_t var_cnt;
611     struct hsh_table *name_tab;
612   };
613
614 static size_t
615 array_var_set_get_cnt (struct var_set *vs) 
616 {
617   struct array_var_set *avs = vs->aux;
618
619   return avs->var_cnt;
620 }
621
622 static struct variable *
623 array_var_set_get_var (struct var_set *vs, size_t idx) 
624 {
625   struct array_var_set *avs = vs->aux;
626
627   return avs->var[idx];
628 }
629
630 static struct variable *
631 array_var_set_lookup_var (struct var_set *vs, const char *name) 
632 {
633   struct array_var_set *avs = vs->aux;
634   struct variable v;
635
636   strcpy (v.name, name);
637
638   return hsh_find (avs->name_tab, &v);
639 }
640
641 static void
642 array_var_set_destroy (struct var_set *vs) 
643 {
644   struct array_var_set *avs = vs->aux;
645
646   hsh_destroy (avs->name_tab);
647   free (avs);
648   free (vs);
649 }
650
651 struct var_set *
652 var_set_create_from_array (struct variable **var, size_t var_cnt) 
653 {
654   struct var_set *vs;
655   struct array_var_set *avs;
656   size_t i;
657
658   vs = xmalloc (sizeof *vs);
659   vs->get_cnt = array_var_set_get_cnt;
660   vs->get_var = array_var_set_get_var;
661   vs->lookup_var = array_var_set_lookup_var;
662   vs->destroy = array_var_set_destroy;
663   vs->aux = avs = xmalloc (sizeof *avs);
664   avs->var = var;
665   avs->var_cnt = var_cnt;
666   avs->name_tab = hsh_create (2 * var_cnt,
667                               compare_variables, hash_variable, NULL,
668                               NULL);
669   for (i = 0; i < var_cnt; i++)
670     if (hsh_insert (avs->name_tab, var[i]) != NULL) 
671       {
672         var_set_destroy (vs);
673         return NULL;
674       }
675   
676   return vs;
677 }