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