1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
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.
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.
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
20 /* AIX requires this to be the first thing in the file. */
23 #define alloca __builtin_alloca
31 #ifndef alloca /* predefined by HP cc +Olibcalls */
42 #include "bitvector.h"
51 /* FIXME: should change weighting variable, etc. */
52 /* These control the way that compare_variables() does its work. */
53 static int forward; /* 1=FORWARD, 0=BACKWARD. */
54 static int positional; /* 1=POSITIONAL, 0=ALPHA. */
56 static int compare_variables (const void *pa, const void *pb);
58 /* Explains how to modify the variables in a dictionary in conjunction
59 with the p.mfv field of `variable'. */
60 struct var_modification
62 /* REORDER information. */
63 struct variable **reorder_list;
65 /* RENAME information. */
66 struct variable **old_names;
70 /* DROP/KEEP information. */
71 int n_drop; /* Number of variables being dropped. */
74 static struct dictionary *rearrange_dict (struct dictionary *d,
75 struct var_modification *vm,
78 /* Performs MODIFY VARS command. */
80 cmd_modify_vars (void)
82 /* Bits indicated whether we've already encountered a subcommand of
84 unsigned already_encountered = 0;
86 /* What we're gonna do to the active file. */
87 struct var_modification vm;
89 lex_match_id ("MODIFY");
90 lex_match_id ("VARS");
92 vm.reorder_list = NULL;
98 /* Parse each subcommand. */
102 if (lex_match_id ("REORDER"))
104 struct variable **v = NULL;
107 if (already_encountered & 1)
109 msg (SE, _("REORDER subcommand may be given at most once."));
112 already_encountered |= 1;
119 forward = positional = 1;
120 if (lex_match_id ("FORWARD"));
121 else if (lex_match_id ("BACKWARD"))
123 if (lex_match_id ("POSITIONAL"));
124 else if (lex_match_id ("ALPHA"))
127 if (lex_match (T_ALL) || token == '/' || token == '.')
131 msg (SE, _("Cannot specify ALL after specifying a set "
135 fill_all_vars (&v, &nv, FV_NO_SYSTEM);
139 if (!lex_match ('('))
141 msg (SE, _("`(' expected on REORDER subcommand."));
145 if (!parse_variables (&default_dict, &v, &nv,
146 PV_APPEND | PV_NO_DUPLICATE))
151 if (!lex_match (')'))
153 msg (SE, _("`)' expected following variable names on "
154 "REORDER subcommand."));
159 qsort (&v[prev_nv], nv - prev_nv, sizeof *v, compare_variables);
161 while (token != '/' && token != '.');
163 if (nv != default_dict.nvar)
165 size_t nbytes = DIV_RND_UP (default_dict.nvar, 8);
166 unsigned char *bits = local_alloc (nbytes);
169 memset (bits, 0, nbytes);
170 for (i = 0; i < nv; i++)
171 SET_BIT (bits, v[i]->index);
172 v = xrealloc (v, sizeof *v * default_dict.nvar);
173 for (i = 0; i < default_dict.nvar; i++)
174 if (!TEST_BIT (bits, i))
175 v[nv++] = default_dict.var[i];
181 else if (lex_match_id ("RENAME"))
183 if (already_encountered & 2)
185 msg (SE, _("RENAME subcommand may be given at most once."));
188 already_encountered |= 2;
193 int prev_nv_1 = vm.n_rename;
194 int prev_nv_2 = vm.n_rename;
196 if (!lex_match ('('))
198 msg (SE, _("`(' expected on RENAME subcommand."));
201 if (!parse_variables (&default_dict, &vm.old_names, &vm.n_rename,
202 PV_APPEND | PV_NO_DUPLICATE))
204 if (!lex_match ('='))
206 msg (SE, _("`=' expected between lists of new and old variable "
207 "names on RENAME subcommand."));
210 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
212 if (prev_nv_1 != vm.n_rename)
216 msg (SE, _("Differing number of variables in old name list "
217 "(%d) and in new name list (%d)."),
218 vm.n_rename - prev_nv_2, prev_nv_1 - prev_nv_2);
219 for (i = 0; i < prev_nv_1; i++)
220 free (&vm.new_names[i]);
221 free (&vm.new_names);
225 if (!lex_match (')'))
227 msg (SE, _("`)' expected after variable lists on RENAME "
232 while (token != '.' && token != '/');
234 else if (lex_match_id ("KEEP"))
236 struct variable **keep_vars;
241 if (already_encountered & 4)
243 msg (SE, _("KEEP subcommand may be given at most once. It may not"
244 "be given in conjunction with the DROP subcommand."));
247 already_encountered |= 4;
250 if (!parse_variables (&default_dict, &keep_vars, &nv, PV_NONE))
253 /* Transform the list of variables to keep into a list of
254 variables to drop. First sort the keep list, then figure
255 out which variables are missing. */
256 forward = positional = 1;
257 qsort (keep_vars, nv, sizeof *keep_vars, compare_variables);
259 vm.n_drop = default_dict.nvar - nv;
262 for (i = 0; i < nv; i++)
264 while (counter < keep_vars[i]->index)
265 default_dict.var[counter++]->p.mfv.drop_this_var = 1;
266 default_dict.var[counter++]->p.mfv.drop_this_var = 0;
269 default_dict.var[counter++]->p.mfv.drop_this_var = 1;
273 else if (lex_match_id ("DROP"))
275 struct variable **drop_vars;
279 if (already_encountered & 4)
281 msg (SE, _("DROP subcommand may be given at most once. It may not"
282 "be given in conjunction with the KEEP subcommand."));
285 already_encountered |= 4;
288 if (!parse_variables (&default_dict, &drop_vars, &nv, PV_NONE))
290 for (i = 0; i < default_dict.nvar; i++)
291 default_dict.var[i]->p.mfv.drop_this_var = 0;
292 for (i = 0; i < nv; i++)
293 drop_vars[i]->p.mfv.drop_this_var = 1;
297 else if (lex_match_id ("MAP"))
299 struct dictionary *new_dict = rearrange_dict (&default_dict, &vm, 0);
302 /* FIXME: display new dictionary. */
307 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
309 msg (SE, _("Subcommand name expected."));
317 msg (SE, _("`/' or `.' expected."));
326 if (already_encountered & (1 | 4))
329 procedure (NULL, NULL, NULL);
332 if (NULL == rearrange_dict (&default_dict, &vm, 1))
335 free (vm.reorder_list);
337 for (i = 0; i < vm.n_rename; i++)
338 free (vm.new_names[i]);
348 free (vm.reorder_list);
350 for (i = 0; i < vm.n_rename; i++)
351 free (vm.new_names[i]);
357 /* Compares a pair of variables according to the settings in `forward'
358 and `positional', returning a strcmp()-type result. */
360 compare_variables (const void *pa, const void *pb)
362 const struct variable *a = *(const struct variable **) pa;
363 const struct variable *b = *(const struct variable **) pb;
365 int result = positional ? a->index - b->index : strcmp (a->name, b->name);
366 return forward ? result : -result;
369 /* (Possibly) rearranges variables and (possibly) removes some
370 variables and (possibly) renames some more variables in dictionary
371 D. There are two modes of operation, distinguished by the value of
374 If PERMANENT is nonzero, then the dictionary is modified in place.
375 Returns the new dictionary on success or NULL if there would have
376 been duplicate variable names in the resultant dictionary (in this
377 case the dictionary has not been modified).
379 If PERMANENT is zero, then the dictionary is copied to a new
380 dictionary structure that retains most of the same deep structure
381 as D. The p.mfv.new_name field of each variable is set to what
382 would become the variable's new name if PERMANENT were nonzero.
383 Returns the new dictionary. */
384 static struct dictionary *
385 rearrange_dict (struct dictionary * d, struct var_modification * vm, int permanent)
387 struct dictionary *n;
389 struct variable **save_var;
391 /* Linked list of variables for deletion. */
392 struct variable *head, *tail;
396 /* First decide what dictionary to modify. */
399 n = xmalloc (sizeof *n);
406 /* Perform first half of renaming. */
409 for (i = 0; i < d->nvar; i++)
410 d->var[i]->p.mfv.new_name[0] = 0;
411 d->var = xmalloc (sizeof *d->var * d->nvar);
414 for (i = 0; i < d->nvar; i++)
415 strcpy (d->var[i]->p.mfv.new_name, d->var[i]->name);
416 for (i = 0; i < vm->n_rename; i++)
417 strcpy (vm->old_names[i]->p.mfv.new_name, vm->new_names[i]);
419 /* Copy the variable list, reordering if appropriate. */
420 if (vm->reorder_list)
421 memcpy (n->var, vm->reorder_list, sizeof *n->var * d->nvar);
423 for (i = 0; i < d->nvar; i++)
424 n->var[i] = d->var[i];
426 /* Drop all the unwanted variables. */
432 n->nvar = d->nvar - vm->n_drop;
433 for (i = j = 0; i < n->nvar; i++)
435 while (n->var[j]->p.mfv.drop_this_var != 0)
439 /* If this is permanent, then we have to keep a list
440 of all the dropped variables because they must be
441 free()'d, but can't be until we know that there
442 aren't any duplicate variable names. */
444 tail = tail->p.mfv.next = n->var[j];
446 head = tail = n->var[j];
450 n->var[i] = n->var[j++];
453 tail->p.mfv.next = NULL;
456 /* Check for duplicate variable names if appropriate. */
457 if (permanent && vm->n_rename)
461 if (vm->reorder_list)
462 v = vm->reorder_list; /* Reuse old buffer if possible. */
464 v = xmalloc (sizeof *v * n->nvar);
465 memcpy (v, n->var, sizeof *v * n->nvar);
466 forward = 1, positional = 0;
467 qsort (v, n->nvar, sizeof *v, compare_variables);
468 for (i = 1; i < n->nvar; i++)
469 if (!strcmp (n->var[i]->name, n->var[i - 1]->name))
471 msg (SE, _("Duplicate variable name `%s' after renaming."),
473 if (vm->reorder_list == NULL)
478 if (vm->reorder_list == NULL)
482 /* Delete unwanted variables and finalize renaming if
486 /* Delete dropped variables for good. */
487 for (; head; head = tail)
489 tail = head->p.mfv.next;
490 clear_variable (n, head);
494 /* Remove names from all renamed variables. */
496 for (i = 0; i < n->nvar; i++)
497 if (n->var[i]->p.mfv.new_name[0])
499 avl_force_delete (n->var_by_name, n->var[i]);
501 tail = tail->p.mfv.next = n->var[i];
503 head = tail = n->var[i];
506 tail->p.mfv.next = NULL;
508 /* Put names onto renamed variables. */
509 for (; head; head = head->p.mfv.next)
511 strcpy (head->name, head->p.mfv.new_name);
512 avl_force_insert (n->var_by_name, head);
516 /* As a final step the index fields must be redone. */
517 for (i = 0; i < n->nvar; i++)
518 n->var[i]->index = i;