2ef156a0387d93764565a12fbdee734dced62bb7
[pspp-builds.git] / src / modify-vars.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 <stdlib.h>
39 #include <assert.h>
40 #include "algorithm.h"
41 #include "alloc.h"
42 #include "bitvector.h"
43 #include "command.h"
44 #include "error.h"
45 #include "hash.h"
46 #include "lexer.h"
47 #include "misc.h"
48 #include "str.h"
49 #include "var.h"
50 #include "vfm.h"
51
52 /* FIXME: should change weighting variable, etc. */
53 /* These control the ordering produced by
54    compare_variables_given_ordering(). */
55 struct ordering
56   {
57     int forward;                /* 1=FORWARD, 0=BACKWARD. */
58     int positional;             /* 1=POSITIONAL, 0=ALPHA. */
59   };
60
61 static int compare_variables_given_ordering (const void *, const void *,
62                                              void *ordering);
63
64 /* Explains how to modify the variables in a dictionary in conjunction
65    with the p.mfv field of `variable'. */
66 struct var_modification
67   {
68     /* REORDER information. */
69     struct variable **reorder_list;
70
71     /* RENAME information. */
72     struct variable **old_names;
73     char **new_names;
74     int n_rename;
75
76     /* DROP/KEEP information. */
77     int n_drop;                 /* Number of variables being dropped. */
78   };
79
80 static struct dictionary *rearrange_dict (struct dictionary *d,
81                                           struct var_modification *vm,
82                                           int permanent);
83
84 /* Performs MODIFY VARS command. */
85 int
86 cmd_modify_vars (void)
87 {
88   /* Bits indicated whether we've already encountered a subcommand of
89      this type. */
90   unsigned already_encountered = 0;
91
92   /* What we're gonna do to the active file. */
93   struct var_modification vm;
94
95   lex_match_id ("MODIFY");
96   lex_match_id ("VARS");
97
98   vm.reorder_list = NULL;
99   vm.old_names = NULL;
100   vm.new_names = NULL;
101   vm.n_rename = 0;
102   vm.n_drop = 0;
103
104   /* Parse each subcommand. */
105   lex_match ('/');
106   for (;;)
107     {
108       if (lex_match_id ("REORDER"))
109         {
110           struct variable **v = NULL;
111           int nv = 0;
112
113           if (already_encountered & 1)
114             {
115               msg (SE, _("REORDER subcommand may be given at most once."));
116               goto lossage;
117             }
118           already_encountered |= 1;
119
120           lex_match ('=');
121           do
122             {
123               struct ordering ordering;
124               int prev_nv = nv;
125
126               ordering.forward = ordering.positional = 1;
127               if (lex_match_id ("FORWARD"));
128               else if (lex_match_id ("BACKWARD"))
129                 ordering.forward = 0;
130               if (lex_match_id ("POSITIONAL"));
131               else if (lex_match_id ("ALPHA"))
132                 ordering.positional = 0;
133
134               if (lex_match (T_ALL) || token == '/' || token == '.')
135                 {
136                   if (prev_nv != 0)
137                     {
138                       msg (SE, _("Cannot specify ALL after specifying a set "
139                            "of variables."));
140                       goto lossage;
141                     }
142                   fill_all_vars (&v, &nv, FV_NO_SYSTEM);
143                 }
144               else
145                 {
146                   if (!lex_match ('('))
147                     {
148                       msg (SE, _("`(' expected on REORDER subcommand."));
149                       free (v);
150                       goto lossage;
151                     }
152                   if (!parse_variables (&default_dict, &v, &nv,
153                                         PV_APPEND | PV_NO_DUPLICATE))
154                     {
155                       free (v);
156                       goto lossage;
157                     }
158                   if (!lex_match (')'))
159                     {
160                       msg (SE, _("`)' expected following variable names on "
161                            "REORDER subcommand."));
162                       free (v);
163                       goto lossage;
164                     }
165                 }
166               sort (&v[prev_nv], nv - prev_nv, sizeof *v,
167                     compare_variables_given_ordering, &ordering);
168             }
169           while (token != '/' && token != '.');
170
171           if (nv != default_dict.nvar)
172             {
173               size_t nbytes = DIV_RND_UP (default_dict.nvar, 8);
174               unsigned char *bits = local_alloc (nbytes);
175               int i;
176
177               memset (bits, 0, nbytes);
178               for (i = 0; i < nv; i++)
179                 SET_BIT (bits, v[i]->index);
180               v = xrealloc (v, sizeof *v * default_dict.nvar);
181               for (i = 0; i < default_dict.nvar; i++)
182                 if (!TEST_BIT (bits, i))
183                   v[nv++] = default_dict.var[i];
184               local_free (bits);
185             }
186
187           vm.reorder_list = v;
188         }
189       else if (lex_match_id ("RENAME"))
190         {
191           if (already_encountered & 2)
192             {
193               msg (SE, _("RENAME subcommand may be given at most once."));
194               goto lossage;
195             }
196           already_encountered |= 2;
197
198           lex_match ('=');
199           do
200             {
201               int prev_nv_1 = vm.n_rename;
202               int prev_nv_2 = vm.n_rename;
203
204               if (!lex_match ('('))
205                 {
206                   msg (SE, _("`(' expected on RENAME subcommand."));
207                   goto lossage;
208                 }
209               if (!parse_variables (&default_dict, &vm.old_names, &vm.n_rename,
210                                     PV_APPEND | PV_NO_DUPLICATE))
211                 goto lossage;
212               if (!lex_match ('='))
213                 {
214                   msg (SE, _("`=' expected between lists of new and old variable "
215                        "names on RENAME subcommand."));
216                   goto lossage;
217                 }
218               if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
219                 goto lossage;
220               if (prev_nv_1 != vm.n_rename)
221                 {
222                   int i;
223
224                   msg (SE, _("Differing number of variables in old name list "
225                        "(%d) and in new name list (%d)."),
226                        vm.n_rename - prev_nv_2, prev_nv_1 - prev_nv_2);
227                   for (i = 0; i < prev_nv_1; i++)
228                     free (&vm.new_names[i]);
229                   free (&vm.new_names);
230                   vm.new_names = NULL;
231                   goto lossage;
232                 }
233               if (!lex_match (')'))
234                 {
235                   msg (SE, _("`)' expected after variable lists on RENAME "
236                        "subcommand."));
237                   goto lossage;
238                 }
239             }
240           while (token != '.' && token != '/');
241         }
242       else if (lex_match_id ("KEEP"))
243         {
244           struct ordering ordering;
245           struct variable **keep_vars;
246           int nv;
247           int counter;
248           int i;
249
250           if (already_encountered & 4)
251             {
252               msg (SE, _("KEEP subcommand may be given at most once.  It may not"
253                    "be given in conjunction with the DROP subcommand."));
254               goto lossage;
255             }
256           already_encountered |= 4;
257
258           lex_match ('=');
259           if (!parse_variables (&default_dict, &keep_vars, &nv, PV_NONE))
260             goto lossage;
261
262           /* Transform the list of variables to keep into a list of
263              variables to drop.  First sort the keep list, then figure
264              out which variables are missing. */
265           ordering.forward = ordering.positional = 1;
266           sort (keep_vars, nv, sizeof *keep_vars,
267                  compare_variables_given_ordering, &ordering);
268
269           vm.n_drop = default_dict.nvar - nv;
270
271           counter = 0;
272           for (i = 0; i < nv; i++)
273             {
274               while (counter < keep_vars[i]->index)
275                 default_dict.var[counter++]->p.mfv.drop_this_var = 1;
276               default_dict.var[counter++]->p.mfv.drop_this_var = 0;
277             }
278           while (counter < nv)
279             default_dict.var[counter++]->p.mfv.drop_this_var = 1;
280
281           free (keep_vars);
282         }
283       else if (lex_match_id ("DROP"))
284         {
285           struct variable **drop_vars;
286           int nv;
287           int i;
288
289           if (already_encountered & 4)
290             {
291               msg (SE, _("DROP subcommand may be given at most once.  It may not"
292                    "be given in conjunction with the KEEP subcommand."));
293               goto lossage;
294             }
295           already_encountered |= 4;
296
297           lex_match ('=');
298           if (!parse_variables (&default_dict, &drop_vars, &nv, PV_NONE))
299             goto lossage;
300           for (i = 0; i < default_dict.nvar; i++)
301             default_dict.var[i]->p.mfv.drop_this_var = 0;
302           for (i = 0; i < nv; i++)
303             drop_vars[i]->p.mfv.drop_this_var = 1;
304           vm.n_drop = nv;
305           free (drop_vars);
306         }
307       else if (lex_match_id ("MAP"))
308         {
309           struct dictionary *new_dict = rearrange_dict (&default_dict, &vm, 0);
310           if (!new_dict)
311             goto lossage;
312           /* FIXME: display new dictionary. */
313         }
314       else
315         {
316           if (token == T_ID)
317             msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
318           else
319             msg (SE, _("Subcommand name expected."));
320           goto lossage;
321         }
322
323       if (token == '.')
324         break;
325       if (token != '/')
326         {
327           msg (SE, _("`/' or `.' expected."));
328           goto lossage;
329         }
330       lex_get ();
331     }
332
333   {
334     int i;
335
336     if (already_encountered & (1 | 4))
337       {
338         /* Read the data. */
339         procedure (NULL, NULL, NULL);
340       }
341
342     if (NULL == rearrange_dict (&default_dict, &vm, 1))
343       goto lossage;
344
345     free (vm.reorder_list);
346     free (vm.old_names);
347     for (i = 0; i < vm.n_rename; i++)
348       free (vm.new_names[i]);
349     free (vm.new_names);
350
351     return CMD_SUCCESS;
352   }
353
354 lossage:
355   {
356     int i;
357
358     free (vm.reorder_list);
359     free (vm.old_names);
360     for (i = 0; i < vm.n_rename; i++)
361       free (vm.new_names[i]);
362     free (vm.new_names);
363     return CMD_FAILURE;
364   }
365 }
366
367 /* Compares A and B according to the settings in
368    ORDERING, returning a strcmp()-type result. */
369 static int
370 compare_variables_given_ordering (const void *a_, const void *b_,
371                                   void *ordering_)
372 {
373   struct variable *const *pa = a_;
374   struct variable *const *pb = b_;
375   const struct variable *a = *pa;
376   const struct variable *b = *pb;
377   const struct ordering *ordering = ordering_;
378
379   int result;
380   if (ordering->positional)
381     result = a->index < b->index ? -1 : a->index > b->index;
382   else
383     result = strcmp (a->name, b->name);
384   if (!ordering->forward)
385     result = -result;
386   return result;
387 }
388
389 /* (Possibly) rearranges variables and (possibly) removes some
390    variables and (possibly) renames some more variables in dictionary
391    D.  There are two modes of operation, distinguished by the value of
392    PERMANENT:
393
394    If PERMANENT is nonzero, then the dictionary is modified in place.
395    Returns the new dictionary on success or NULL if there would have
396    been duplicate variable names in the resultant dictionary (in this
397    case the dictionary has not been modified).
398
399    If PERMANENT is zero, then the dictionary is copied to a new
400    dictionary structure that retains most of the same deep structure
401    as D.  The p.mfv.new_name field of each variable is set to what
402    would become the variable's new name if PERMANENT were nonzero.
403    Returns the new dictionary. */
404 static struct dictionary *
405 rearrange_dict (struct dictionary * d, struct var_modification * vm, int permanent)
406 {
407   struct dictionary *n;
408
409   struct variable **save_var;
410
411   /* Linked list of variables for deletion. */
412   struct variable *head, *tail;
413
414   int i;
415
416   /* First decide what dictionary to modify. */
417   if (permanent == 0)
418     {
419       n = xmalloc (sizeof *n);
420       *n = *d;
421     }
422   else
423     n = d;
424   save_var = n->var;
425
426   /* Perform first half of renaming. */
427   if (permanent)
428     {
429       for (i = 0; i < d->nvar; i++)
430         d->var[i]->p.mfv.new_name[0] = 0;
431       d->var = xmalloc (sizeof *d->var * d->nvar);
432     }
433   else
434     for (i = 0; i < d->nvar; i++)
435       strcpy (d->var[i]->p.mfv.new_name, d->var[i]->name);
436   for (i = 0; i < vm->n_rename; i++)
437     strcpy (vm->old_names[i]->p.mfv.new_name, vm->new_names[i]);
438
439   /* Copy the variable list, reordering if appropriate. */
440   if (vm->reorder_list)
441     memcpy (n->var, vm->reorder_list, sizeof *n->var * d->nvar);
442   else if (!permanent)
443     for (i = 0; i < d->nvar; i++)
444       n->var[i] = d->var[i];
445
446   /* Drop all the unwanted variables. */
447   head = NULL;
448   if (vm->n_drop)
449     {
450       int j;
451
452       n->nvar = d->nvar - vm->n_drop;
453       for (i = j = 0; i < n->nvar; i++)
454         {
455           while (n->var[j]->p.mfv.drop_this_var != 0)
456             {
457               if (permanent)
458                 {
459                   /* If this is permanent, then we have to keep a list
460                      of all the dropped variables because they must be
461                      free()'d, but can't be until we know that there
462                      aren't any duplicate variable names. */
463                   if (head)
464                     tail = tail->p.mfv.next = n->var[j];
465                   else
466                     head = tail = n->var[j];
467                 }
468               j++;
469             }
470           n->var[i] = n->var[j++];
471         }
472       if (permanent)
473         tail->p.mfv.next = NULL;
474     }
475
476   /* Check for duplicate variable names if appropriate. */
477   if (permanent && vm->n_rename)
478     {
479       struct ordering ordering;
480       struct variable **v;
481
482       if (vm->reorder_list)
483         v = vm->reorder_list;   /* Reuse old buffer if possible. */
484       else
485         v = xmalloc (sizeof *v * n->nvar);
486       memcpy (v, n->var, sizeof *v * n->nvar);
487       ordering.forward = 1;
488       ordering.positional = 0;
489       sort (v, n->nvar, sizeof *v,
490             compare_variables_given_ordering, &ordering);
491       for (i = 1; i < n->nvar; i++)
492         if (!strcmp (n->var[i]->name, n->var[i - 1]->name))
493           {
494             msg (SE, _("Duplicate variable name `%s' after renaming."),
495                  n->var[i]->name);
496             if (vm->reorder_list == NULL)
497               free (v);
498             n->var = save_var;
499             return NULL;
500           }
501       if (vm->reorder_list == NULL)
502         free (v);
503     }
504
505   /* Delete unwanted variables and finalize renaming if
506      appropriate. */
507   if (permanent)
508     {
509       /* Delete dropped variables for good. */
510       for (; head; head = tail)
511         {
512           tail = head->p.mfv.next;
513           clear_variable (n, head);
514           free (head);
515         }
516
517       /* Remove names from all renamed variables. */
518       head = NULL;
519       for (i = 0; i < n->nvar; i++)
520         if (n->var[i]->p.mfv.new_name[0])
521           {
522             hsh_force_delete (n->name_tab, n->var[i]);
523             if (head)
524               tail = tail->p.mfv.next = n->var[i];
525             else
526               head = tail = n->var[i];
527           }
528       if (head)
529         tail->p.mfv.next = NULL;
530
531       /* Put names onto renamed variables. */
532       for (; head; head = head->p.mfv.next)
533         {
534           strcpy (head->name, head->p.mfv.new_name);
535           hsh_force_insert (n->name_tab, head);
536         }
537       free (save_var);
538
539       /* As a final step the index fields must be redone. */
540       for (i = 0; i < n->nvar; i++)
541         n->var[i]->index = i;
542     }
543
544   return n;
545 }