Sat Dec 27 16:16:49 2003 Ben Pfaff <blp@gnu.org>
[pspp-builds.git] / src / vector.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 #include <config.h>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include "alloc.h"
24 #include "cases.h"
25 #include "command.h"
26 #include "error.h"
27 #include "lexer.h"
28 #include "misc.h"
29 #include "str.h"
30 #include "var.h"
31
32 int
33 cmd_vector (void)
34 {
35   /* Just to be different, points to a set of null terminated strings
36      containing the names of the vectors to be created.  The list
37      itself is terminated by a empty string.  So a list of three
38      elements, A B C, would look like this: "A\0B\0C\0\0". */
39   char *vecnames;
40
41   /* vecnames iterators. */
42   char *cp, *cp2;
43
44   /* Maximum allocated position for vecnames, plus one position. */
45   char *endp = NULL;
46
47   lex_match_id ("VECTOR");
48
49   cp = vecnames = xmalloc (256);
50   endp = &vecnames[256];
51   do
52     {
53       /* Get the name(s) of the new vector(s). */
54       if (!lex_force_id ())
55         return CMD_FAILURE;
56       while (token == T_ID)
57         {
58           if (cp + 16 > endp)
59             {
60               char *old_vecnames = vecnames;
61               vecnames = xrealloc (vecnames, endp - vecnames + 256);
62               cp = (cp - old_vecnames) + vecnames;
63               endp = (endp - old_vecnames) + vecnames + 256;
64             }
65
66           for (cp2 = cp; cp2 < cp; cp2 += strlen (cp))
67             if (!strcmp (cp2, tokid))
68               {
69                 msg (SE, _("Vector name %s is given twice."), tokid);
70                 goto fail;
71               }
72
73           if (dict_lookup_vector (default_dict, tokid))
74             {
75               msg (SE, _("There is already a vector with name %s."), tokid);
76               goto fail;
77             }
78
79           cp = stpcpy (cp, tokid) + 1;
80           lex_get ();
81           lex_match (',');
82         }
83       *cp++ = 0;
84
85       /* Now that we have the names it's time to check for the short
86          or long forms. */
87       if (lex_match ('='))
88         {
89           /* Long form. */
90           struct variable **v;
91           int nv;
92
93           if (strchr (vecnames, '\0')[1])
94             {
95               /* There's more than one vector name. */
96               msg (SE, _("A slash must be used to separate each vector "
97                    "specification when using the long form.  Commands "
98                    "such as VECTOR A,B=Q1 TO Q20 are not supported."));
99               goto fail;
100             }
101
102           if (!parse_variables (default_dict, &v, &nv,
103                                 PV_SAME_TYPE | PV_DUPLICATE))
104             goto fail;
105
106           dict_create_vector (default_dict, vecnames, v, nv);
107           free (v);
108         }
109       else if (lex_match ('('))
110         {
111           int i;
112
113           /* Maximum number of digits in a number to add to the base
114              vecname. */
115           int ndig;
116
117           /* Name of an individual variable to be created. */
118           char name[9];
119
120           /* Vector variables. */
121           struct variable **v;
122           int nv;
123
124           if (!lex_force_int ())
125             return CMD_FAILURE;
126           nv = lex_integer ();
127           lex_get ();
128           if (nv <= 0)
129             {
130               msg (SE, _("Vectors must have at least one element."));
131               goto fail;
132             }
133           if (!lex_force_match (')'))
134             goto fail;
135
136           /* First check that all the generated variable names are 8
137              characters or shorter. */
138           ndig = intlog10 (nv);
139           for (cp = vecnames; *cp;)
140             {
141               int len = strlen (cp);
142               if (len + ndig > 8)
143                 {
144                   msg (SE, _("%s%d is too long for a variable name."), cp, nv);
145                   goto fail;
146                 }
147               cp += len + 1;
148             }
149
150           /* Next check that none of the variables exist. */
151           for (cp = vecnames; *cp;)
152             {
153               for (i = 0; i < nv; i++)
154                 {
155                   sprintf (name, "%s%d", cp, i + 1);
156                   if (dict_lookup_var (default_dict, name))
157                     {
158                       msg (SE, _("There is already a variable named %s."), name);
159                       goto fail;
160                     }
161                 }
162               cp += strlen (cp) + 1;
163             }
164
165           /* Finally create the variables and vectors. */
166           v = xmalloc (nv * sizeof *v);
167           for (cp = vecnames; *cp;)
168             {
169               for (i = 0; i < nv; i++)
170                 {
171                   sprintf (name, "%s%d", cp, i + 1);
172                   v[i] = dict_create_var (default_dict, name, 0);
173                   assert (v[i] != NULL);
174                   envector (v[i]);
175                 }
176               if (!dict_create_vector (default_dict, cp, v, nv))
177                 assert (0);
178               cp += strlen (cp) + 1;
179             }
180           free (v);
181         }
182       else
183         {
184           msg (SE, _("The syntax for this command does not match "
185                "the expected syntax for either the long form "
186                "or the short form of VECTOR."));
187           goto fail;
188         }
189
190       free (vecnames);
191       vecnames = NULL;
192     }
193   while (lex_match ('/'));
194
195   if (token != '.')
196     {
197       lex_error (_("expecting end of command"));
198       goto fail;
199     }
200   return CMD_SUCCESS;
201
202 fail:
203   free (vecnames);
204   return CMD_PART_SUCCESS_MAYBE;
205 }