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., 51 Franklin Street, Fifth Floor, Boston, MA
23 #include "algorithm.h"
25 #include "bitvector.h"
27 #include "dictionary.h"
36 /* FIXME: should change weighting variable, etc. */
37 /* These control the ordering produced by
38 compare_variables_given_ordering(). */
41 int forward; /* 1=FORWARD, 0=BACKWARD. */
42 int positional; /* 1=POSITIONAL, 0=ALPHA. */
45 /* Increasing order of variable index. */
46 static struct ordering forward_positional_ordering = {1, 1};
48 static int compare_variables_given_ordering (const void *, const void *,
51 /* Explains how to modify the variables in a dictionary. */
52 struct var_modification
54 /* New variable ordering. */
55 struct variable **reorder_vars;
58 /* DROP/KEEP information. */
59 struct variable **drop_vars;
62 /* New variable names. */
63 struct variable **rename_vars;
68 static int rearrange_dict (struct dictionary *d,
69 const struct var_modification *vm);
71 /* Performs MODIFY VARS command. */
73 cmd_modify_vars (void)
75 /* Bits indicated whether we've already encountered a subcommand of
77 unsigned already_encountered = 0;
79 /* What we're gonna do to the active file. */
80 struct var_modification vm;
83 int ret_code = CMD_FAILURE;
89 msg (SE, _("MODIFY VARS may not be used after TEMPORARY. "
90 "Temporary transformations will be made permanent."));
94 vm.reorder_vars = NULL;
96 vm.rename_vars = NULL;
102 /* Parse each subcommand. */
106 if (lex_match_id ("REORDER"))
108 struct variable **v = NULL;
111 if (already_encountered & 1)
113 msg (SE, _("REORDER subcommand may be given at most once."));
116 already_encountered |= 1;
121 struct ordering ordering;
124 ordering.forward = ordering.positional = 1;
125 if (lex_match_id ("FORWARD"));
126 else if (lex_match_id ("BACKWARD"))
127 ordering.forward = 0;
128 if (lex_match_id ("POSITIONAL"));
129 else if (lex_match_id ("ALPHA"))
130 ordering.positional = 0;
132 if (lex_match (T_ALL) || token == '/' || token == '.')
136 msg (SE, _("Cannot specify ALL after specifying a set "
140 dict_get_vars (default_dict, &v, &nv, 1u << DC_SYSTEM);
144 if (!lex_match ('('))
146 msg (SE, _("`(' expected on REORDER subcommand."));
150 if (!parse_variables (default_dict, &v, &nv,
151 PV_APPEND | PV_NO_DUPLICATE))
156 if (!lex_match (')'))
158 msg (SE, _("`)' expected following variable names on "
159 "REORDER subcommand."));
164 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
165 compare_variables_given_ordering, &ordering);
167 while (token != '/' && token != '.');
172 else if (lex_match_id ("RENAME"))
174 if (already_encountered & 2)
176 msg (SE, _("RENAME subcommand may be given at most once."));
179 already_encountered |= 2;
184 int prev_nv_1 = vm.rename_cnt;
185 int prev_nv_2 = vm.rename_cnt;
187 if (!lex_match ('('))
189 msg (SE, _("`(' expected on RENAME subcommand."));
192 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
193 PV_APPEND | PV_NO_DUPLICATE))
195 if (!lex_match ('='))
197 msg (SE, _("`=' expected between lists of new and old variable "
198 "names on RENAME subcommand."));
201 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
203 if (prev_nv_1 != vm.rename_cnt)
205 msg (SE, _("Differing number of variables in old name list "
206 "(%d) and in new name list (%d)."),
207 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
208 for (i = 0; i < prev_nv_1; i++)
209 free (vm.new_names[i]);
214 if (!lex_match (')'))
216 msg (SE, _("`)' expected after variable lists on RENAME "
221 while (token != '.' && token != '/');
223 else if (lex_match_id ("KEEP"))
225 struct variable **keep_vars, **all_vars, **drop_vars;
226 int keep_cnt, all_cnt, drop_cnt;
228 if (already_encountered & 4)
230 msg (SE, _("KEEP subcommand may be given at most once. It may not"
231 "be given in conjunction with the DROP subcommand."));
234 already_encountered |= 4;
237 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
240 /* Transform the list of variables to keep into a list of
241 variables to drop. First sort the keep list, then figure
242 out which variables are missing. */
243 sort (keep_vars, keep_cnt, sizeof *keep_vars,
244 compare_variables_given_ordering, &forward_positional_ordering);
246 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
248 drop_cnt = all_cnt - keep_cnt;
249 drop_vars = xmalloc (drop_cnt * sizeof *keep_vars);
250 if (set_difference (all_vars, all_cnt,
254 compare_variables_given_ordering,
255 &forward_positional_ordering)
262 vm.drop_vars = drop_vars;
263 vm.drop_cnt = drop_cnt;
265 else if (lex_match_id ("DROP"))
267 struct variable **drop_vars;
270 if (already_encountered & 4)
272 msg (SE, _("DROP subcommand may be given at most once. It may "
273 "not be given in conjunction with the KEEP "
277 already_encountered |= 4;
280 if (!parse_variables (default_dict, &drop_vars, &drop_cnt, PV_NONE))
282 vm.drop_vars = drop_vars;
283 vm.drop_cnt = drop_cnt;
285 else if (lex_match_id ("MAP"))
287 struct dictionary *temp = dict_clone (default_dict);
288 int success = rearrange_dict (temp, &vm);
291 /* FIXME: display new dictionary. */
298 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
300 msg (SE, _("Subcommand name expected."));
308 msg (SE, _("`/' or `.' expected."));
314 if (already_encountered & (1 | 4))
317 procedure (NULL, NULL);
320 if (!rearrange_dict (default_dict, &vm))
323 ret_code = CMD_SUCCESS;
326 free (vm.reorder_vars);
327 free (vm.rename_vars);
328 for (i = 0; i < vm.rename_cnt; i++)
329 free (vm.new_names[i]);
335 /* Compares A and B according to the settings in
336 ORDERING, returning a strcmp()-type result. */
338 compare_variables_given_ordering (const void *a_, const void *b_,
341 struct variable *const *pa = a_;
342 struct variable *const *pb = b_;
343 const struct variable *a = *pa;
344 const struct variable *b = *pb;
345 const struct ordering *ordering = ordering_;
348 if (ordering->positional)
349 result = a->index < b->index ? -1 : a->index > b->index;
351 result = strcasecmp (a->name, b->name);
352 if (!ordering->forward)
357 /* Pairs a variable with a new name. */
360 struct variable *var;
361 char new_name[LONG_NAME_LEN + 1];
364 /* A algo_compare_func that compares new_name members in struct
365 var_renaming structures A and B. */
367 compare_var_renaming_by_new_name (const void *a_, const void *b_,
370 const struct var_renaming *a = a_;
371 const struct var_renaming *b = b_;
373 return strcasecmp (a->new_name, b->new_name);
376 /* Returns true if performing VM on dictionary D would not cause
377 problems such as duplicate variable names. Returns false
378 otherwise, and issues an error message. */
380 validate_var_modification (const struct dictionary *d,
381 const struct var_modification *vm)
383 /* Variable reordering can't be a problem, so we don't simulate
384 it. Variable renaming can cause duplicate names, but
385 dropping variables can eliminate them, so we simulate both
387 struct variable **all_vars;
388 struct variable **keep_vars;
389 struct variable **drop_vars;
390 size_t all_cnt, keep_cnt, drop_cnt;
392 struct var_renaming *var_renaming;
396 /* All variables, in index order. */
397 dict_get_vars (d, &all_vars, &all_cnt, 0);
399 /* Drop variables, in index order. */
400 drop_cnt = vm->drop_cnt;
401 drop_vars = xmalloc (drop_cnt * sizeof *drop_vars);
402 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
403 sort (drop_vars, drop_cnt, sizeof *drop_vars,
404 compare_variables_given_ordering, &forward_positional_ordering);
406 /* Keep variables, in index order. */
407 keep_cnt = all_cnt - drop_cnt;
408 keep_vars = xmalloc (keep_cnt * sizeof *keep_vars);
409 if (set_difference (all_vars, all_cnt,
413 compare_variables_given_ordering,
414 &forward_positional_ordering) != keep_cnt)
417 /* Copy variables into var_renaming array. */
418 var_renaming = xmalloc (keep_cnt * sizeof *var_renaming);
419 for (i = 0; i < keep_cnt; i++)
421 var_renaming[i].var = keep_vars[i];
422 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
425 /* Rename variables in var_renaming array. */
426 for (i = 0; i < vm->rename_cnt; i++)
428 struct variable *const *kv;
429 struct var_renaming *vr;
431 /* Get the var_renaming element. */
432 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
434 compare_variables_given_ordering,
435 &forward_positional_ordering);
438 vr = var_renaming + (kv - keep_vars);
440 strcpy (vr->new_name, vm->new_names[i]);
443 /* Sort var_renaming array by new names and check for
445 sort (var_renaming, keep_cnt, sizeof *var_renaming,
446 compare_var_renaming_by_new_name, NULL);
447 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
448 compare_var_renaming_by_new_name, NULL) == NULL;
459 /* Reoders, removes, and renames variables in dictionary D
460 according to VM. Returns nonzero if successful, zero if there
461 would have been duplicate variable names if the modifications
462 had been carried out. In the latter case, the dictionary is
465 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
467 char **rename_old_names;
469 struct variable **rename_vars;
470 char **rename_new_names;
475 /* Check whether the modifications will cause duplicate
477 if (!validate_var_modification (d, vm))
480 /* Record the old names of variables to rename. After
481 variables are deleted, we can't depend on the variables to
482 still exist, but we can still look them up by name. */
483 rename_old_names = xmalloc (vm->rename_cnt * sizeof *rename_old_names);
484 for (i = 0; i < vm->rename_cnt; i++)
485 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
487 /* Reorder and delete variables. */
488 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
489 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
491 /* Compose lists of variables to rename and their new names. */
492 rename_vars = xmalloc (vm->rename_cnt * sizeof *rename_vars);
493 rename_new_names = xmalloc (vm->rename_cnt * sizeof *rename_new_names);
495 for (i = 0; i < vm->rename_cnt; i++)
497 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
501 rename_vars[rename_cnt] = var;
502 rename_new_names[rename_cnt] = vm->new_names[i];
507 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
512 for (i = 0; i < vm->rename_cnt; i++)
513 free (rename_old_names[i]);
514 free (rename_old_names);
516 free (rename_new_names);