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
23 #include "algorithm.h"
25 #include "bitvector.h"
35 /* FIXME: should change weighting variable, etc. */
36 /* These control the ordering produced by
37 compare_variables_given_ordering(). */
40 int forward; /* 1=FORWARD, 0=BACKWARD. */
41 int positional; /* 1=POSITIONAL, 0=ALPHA. */
44 /* Increasing order of variable index. */
45 static struct ordering forward_positional_ordering = {1, 1};
47 static int compare_variables_given_ordering (const void *, const void *,
50 /* Explains how to modify the variables in a dictionary. */
51 struct var_modification
53 /* New variable ordering. */
54 struct variable **reorder_vars;
57 /* DROP/KEEP information. */
58 struct variable **drop_vars;
61 /* New variable names. */
62 struct variable **rename_vars;
67 static int rearrange_dict (struct dictionary *d,
68 const struct var_modification *vm);
70 /* Performs MODIFY VARS command. */
72 cmd_modify_vars (void)
74 /* Bits indicated whether we've already encountered a subcommand of
76 unsigned already_encountered = 0;
78 /* What we're gonna do to the active file. */
79 struct var_modification vm;
82 int ret_code = CMD_FAILURE;
88 msg (SE, _("MODIFY VARS may not be used after TEMPORARY. "
89 "Temporary transformations will be made permanent."));
93 lex_match_id ("MODIFY");
94 lex_match_id ("VARS");
96 vm.reorder_vars = NULL;
98 vm.rename_vars = NULL;
104 /* Parse each subcommand. */
108 if (lex_match_id ("REORDER"))
110 struct variable **v = NULL;
113 if (already_encountered & 1)
115 msg (SE, _("REORDER subcommand may be given at most once."));
118 already_encountered |= 1;
123 struct ordering ordering;
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;
134 if (lex_match (T_ALL) || token == '/' || token == '.')
138 msg (SE, _("Cannot specify ALL after specifying a set "
142 dict_get_vars (default_dict, &v, &nv, 1u << DC_SYSTEM);
146 if (!lex_match ('('))
148 msg (SE, _("`(' expected on REORDER subcommand."));
152 if (!parse_variables (default_dict, &v, &nv,
153 PV_APPEND | PV_NO_DUPLICATE))
158 if (!lex_match (')'))
160 msg (SE, _("`)' expected following variable names on "
161 "REORDER subcommand."));
166 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
167 compare_variables_given_ordering, &ordering);
169 while (token != '/' && token != '.');
174 else if (lex_match_id ("RENAME"))
176 if (already_encountered & 2)
178 msg (SE, _("RENAME subcommand may be given at most once."));
181 already_encountered |= 2;
186 int prev_nv_1 = vm.rename_cnt;
187 int prev_nv_2 = vm.rename_cnt;
189 if (!lex_match ('('))
191 msg (SE, _("`(' expected on RENAME subcommand."));
194 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
195 PV_APPEND | PV_NO_DUPLICATE))
197 if (!lex_match ('='))
199 msg (SE, _("`=' expected between lists of new and old variable "
200 "names on RENAME subcommand."));
203 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
205 if (prev_nv_1 != vm.rename_cnt)
207 msg (SE, _("Differing number of variables in old name list "
208 "(%d) and in new name list (%d)."),
209 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
210 for (i = 0; i < prev_nv_1; i++)
211 free (vm.new_names[i]);
216 if (!lex_match (')'))
218 msg (SE, _("`)' expected after variable lists on RENAME "
223 while (token != '.' && token != '/');
225 else if (lex_match_id ("KEEP"))
227 struct variable **keep_vars, **all_vars, **drop_vars;
228 int keep_cnt, all_cnt, drop_cnt;
230 if (already_encountered & 4)
232 msg (SE, _("KEEP subcommand may be given at most once. It may not"
233 "be given in conjunction with the DROP subcommand."));
236 already_encountered |= 4;
239 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
242 /* Transform the list of variables to keep into a list of
243 variables to drop. First sort the keep list, then figure
244 out which variables are missing. */
245 sort (keep_vars, keep_cnt, sizeof *keep_vars,
246 compare_variables_given_ordering, &forward_positional_ordering);
248 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
250 drop_cnt = all_cnt - keep_cnt;
251 drop_vars = xmalloc (drop_cnt * sizeof *keep_vars);
252 if (set_difference (all_vars, all_cnt,
256 compare_variables_given_ordering,
257 &forward_positional_ordering)
264 vm.drop_vars = drop_vars;
265 vm.drop_cnt = drop_cnt;
267 else if (lex_match_id ("DROP"))
269 struct variable **drop_vars;
272 if (already_encountered & 4)
274 msg (SE, _("DROP subcommand may be given at most once. It may "
275 "not be given in conjunction with the KEEP "
279 already_encountered |= 4;
282 if (!parse_variables (default_dict, &drop_vars, &drop_cnt, PV_NONE))
284 vm.drop_vars = drop_vars;
285 vm.drop_cnt = drop_cnt;
287 else if (lex_match_id ("MAP"))
289 struct dictionary *temp = dict_clone (default_dict);
290 int success = rearrange_dict (temp, &vm);
293 /* FIXME: display new dictionary. */
300 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
302 msg (SE, _("Subcommand name expected."));
310 msg (SE, _("`/' or `.' expected."));
316 if (already_encountered & (1 | 4))
319 procedure (NULL, NULL);
322 if (!rearrange_dict (default_dict, &vm))
325 ret_code = CMD_SUCCESS;
328 free (vm.reorder_vars);
329 free (vm.rename_vars);
330 for (i = 0; i < vm.rename_cnt; i++)
331 free (vm.new_names[i]);
337 /* Compares A and B according to the settings in
338 ORDERING, returning a strcmp()-type result. */
340 compare_variables_given_ordering (const void *a_, const void *b_,
343 struct variable *const *pa = a_;
344 struct variable *const *pb = b_;
345 const struct variable *a = *pa;
346 const struct variable *b = *pb;
347 const struct ordering *ordering = ordering_;
350 if (ordering->positional)
351 result = a->index < b->index ? -1 : a->index > b->index;
353 result = strcmp (a->name, b->name);
354 if (!ordering->forward)
359 /* Pairs a variable with a new name. */
362 struct variable *var;
366 /* A algo_compare_func that compares new_name members in struct
367 var_renaming structures A and B. */
369 compare_var_renaming_by_new_name (const void *a_, const void *b_,
372 const struct var_renaming *a = a_;
373 const struct var_renaming *b = b_;
375 return strcmp (a->new_name, b->new_name);
378 /* Returns true if performing VM on dictionary D would not cause
379 problems such as duplicate variable names. Returns false
380 otherwise, and issues an error message. */
382 validate_var_modification (const struct dictionary *d,
383 const struct var_modification *vm)
385 /* Variable reordering can't be a problem, so we don't simulate
386 it. Variable renaming can cause duplicate names, but
387 dropping variables can eliminate them, so we simulate both
389 struct variable **all_vars;
390 struct variable **keep_vars;
391 struct variable **drop_vars;
392 size_t all_cnt, keep_cnt, drop_cnt;
394 struct var_renaming *var_renaming;
398 /* All variables, in index order. */
399 dict_get_vars (d, &all_vars, &all_cnt, 0);
401 /* Drop variables, in index order. */
402 drop_cnt = vm->drop_cnt;
403 drop_vars = xmalloc (drop_cnt * sizeof *drop_vars);
404 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
405 sort (drop_vars, drop_cnt, sizeof *drop_vars,
406 compare_variables_given_ordering, &forward_positional_ordering);
408 /* Keep variables, in index order. */
409 keep_cnt = all_cnt - drop_cnt;
410 keep_vars = xmalloc (keep_cnt * sizeof *keep_vars);
411 if (set_difference (all_vars, all_cnt,
415 compare_variables_given_ordering,
416 &forward_positional_ordering) != keep_cnt)
419 /* Copy variables into var_renaming array. */
420 var_renaming = xmalloc (keep_cnt * sizeof *var_renaming);
421 for (i = 0; i < keep_cnt; i++)
423 var_renaming[i].var = keep_vars[i];
424 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
427 /* Rename variables in var_renaming array. */
428 for (i = 0; i < vm->rename_cnt; i++)
430 struct variable *const *kv;
431 struct var_renaming *vr;
433 /* Get the var_renaming element. */
434 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
436 compare_variables_given_ordering,
437 &forward_positional_ordering);
440 vr = var_renaming + (kv - keep_vars);
442 strcpy (vr->new_name, vm->new_names[i]);
445 /* Sort var_renaming array by new names and check for
447 sort (var_renaming, keep_cnt, sizeof *var_renaming,
448 compare_var_renaming_by_new_name, NULL);
449 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
450 compare_var_renaming_by_new_name, NULL) == NULL;
461 /* Reoders, removes, and renames variables in dictionary D
462 according to VM. Returns nonzero if successful, zero if there
463 would have been duplicate variable names if the modifications
464 had been carried out. In the latter case, the dictionary is
467 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
469 char **rename_old_names;
471 struct variable **rename_vars;
472 char **rename_new_names;
477 /* Check whether the modifications will cause duplicate
479 if (!validate_var_modification (d, vm))
482 /* Record the old names of variables to rename. After
483 variables are deleted, we can't depend on the variables to
484 still exist, but we can still look them up by name. */
485 rename_old_names = xmalloc (vm->rename_cnt * sizeof *rename_old_names);
486 for (i = 0; i < vm->rename_cnt; i++)
487 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
489 /* Reorder and delete variables. */
490 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
491 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
493 /* Compose lists of variables to rename and their new names. */
494 rename_vars = xmalloc (vm->rename_cnt * sizeof *rename_vars);
495 rename_new_names = xmalloc (vm->rename_cnt * sizeof *rename_new_names);
497 for (i = 0; i < vm->rename_cnt; i++)
499 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
503 rename_vars[rename_cnt] = var;
504 rename_new_names[rename_cnt] = vm->new_names[i];
509 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
514 for (i = 0; i < vm->rename_cnt; i++)
515 free (rename_old_names[i]);
516 free (rename_old_names);
518 free (rename_new_names);