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;
86 lex_match_id ("MODIFY");
87 lex_match_id ("VARS");
89 vm.reorder_vars = NULL;
91 vm.rename_vars = NULL;
97 /* Parse each subcommand. */
101 if (lex_match_id ("REORDER"))
103 struct variable **v = NULL;
106 if (already_encountered & 1)
108 msg (SE, _("REORDER subcommand may be given at most once."));
111 already_encountered |= 1;
116 struct ordering ordering;
119 ordering.forward = ordering.positional = 1;
120 if (lex_match_id ("FORWARD"));
121 else if (lex_match_id ("BACKWARD"))
122 ordering.forward = 0;
123 if (lex_match_id ("POSITIONAL"));
124 else if (lex_match_id ("ALPHA"))
125 ordering.positional = 0;
127 if (lex_match (T_ALL) || token == '/' || token == '.')
131 msg (SE, _("Cannot specify ALL after specifying a set "
135 dict_get_vars (default_dict, &v, &nv, 1u << DC_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 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
160 compare_variables_given_ordering, &ordering);
162 while (token != '/' && token != '.');
167 else if (lex_match_id ("RENAME"))
169 if (already_encountered & 2)
171 msg (SE, _("RENAME subcommand may be given at most once."));
174 already_encountered |= 2;
179 int prev_nv_1 = vm.rename_cnt;
180 int prev_nv_2 = vm.rename_cnt;
182 if (!lex_match ('('))
184 msg (SE, _("`(' expected on RENAME subcommand."));
187 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
188 PV_APPEND | PV_NO_DUPLICATE))
190 if (!lex_match ('='))
192 msg (SE, _("`=' expected between lists of new and old variable "
193 "names on RENAME subcommand."));
196 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
198 if (prev_nv_1 != vm.rename_cnt)
200 msg (SE, _("Differing number of variables in old name list "
201 "(%d) and in new name list (%d)."),
202 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
203 for (i = 0; i < prev_nv_1; i++)
204 free (vm.new_names[i]);
209 if (!lex_match (')'))
211 msg (SE, _("`)' expected after variable lists on RENAME "
216 while (token != '.' && token != '/');
218 else if (lex_match_id ("KEEP"))
220 struct variable **keep_vars, **all_vars, **drop_vars;
221 int keep_cnt, all_cnt, drop_cnt;
223 if (already_encountered & 4)
225 msg (SE, _("KEEP subcommand may be given at most once. It may not"
226 "be given in conjunction with the DROP subcommand."));
229 already_encountered |= 4;
232 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
235 /* Transform the list of variables to keep into a list of
236 variables to drop. First sort the keep list, then figure
237 out which variables are missing. */
238 sort (keep_vars, keep_cnt, sizeof *keep_vars,
239 compare_variables_given_ordering, &forward_positional_ordering);
241 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
243 drop_cnt = all_cnt - keep_cnt;
244 drop_vars = xmalloc (drop_cnt * sizeof *keep_vars);
245 if (set_difference (all_vars, all_cnt,
249 compare_variables_given_ordering,
250 &forward_positional_ordering)
257 vm.drop_vars = drop_vars;
258 vm.drop_cnt = drop_cnt;
260 else if (lex_match_id ("DROP"))
262 struct variable **drop_vars;
265 if (already_encountered & 4)
267 msg (SE, _("DROP subcommand may be given at most once. It may "
268 "not be given in conjunction with the KEEP "
272 already_encountered |= 4;
275 if (!parse_variables (default_dict, &drop_vars, &drop_cnt, PV_NONE))
277 vm.drop_vars = drop_vars;
278 vm.drop_cnt = drop_cnt;
280 else if (lex_match_id ("MAP"))
282 struct dictionary *temp = dict_clone (default_dict);
283 int success = rearrange_dict (temp, &vm);
286 /* FIXME: display new dictionary. */
293 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
295 msg (SE, _("Subcommand name expected."));
303 msg (SE, _("`/' or `.' expected."));
309 if (already_encountered & (1 | 4))
312 procedure (NULL, NULL, NULL);
315 if (!rearrange_dict (default_dict, &vm))
318 ret_code = CMD_SUCCESS;
321 free (vm.reorder_vars);
322 free (vm.rename_vars);
323 for (i = 0; i < vm.rename_cnt; i++)
324 free (vm.new_names[i]);
330 /* Compares A and B according to the settings in
331 ORDERING, returning a strcmp()-type result. */
333 compare_variables_given_ordering (const void *a_, const void *b_,
336 struct variable *const *pa = a_;
337 struct variable *const *pb = b_;
338 const struct variable *a = *pa;
339 const struct variable *b = *pb;
340 const struct ordering *ordering = ordering_;
343 if (ordering->positional)
344 result = a->index < b->index ? -1 : a->index > b->index;
346 result = strcmp (a->name, b->name);
347 if (!ordering->forward)
352 /* Pairs a variable with a new name. */
355 struct variable *var;
359 /* A algo_compare_func that compares new_name members in struct
360 var_renaming structures A and B. */
362 compare_var_renaming_by_new_name (const void *a_, const void *b_,
365 const struct var_renaming *a = a_;
366 const struct var_renaming *b = b_;
368 return strcmp (a->new_name, b->new_name);
371 /* Returns true if performing VM on dictionary D would not cause
372 problems such as duplicate variable names. Returns false
373 otherwise, and issues an error message. */
375 validate_var_modification (const struct dictionary *d,
376 const struct var_modification *vm)
378 /* Variable reordering can't be a problem, so we don't simulate
379 it. Variable renaming can cause duplicate names, but
380 dropping variables can eliminate them, so we simulate both
382 struct variable **all_vars;
383 struct variable **keep_vars;
384 struct variable **drop_vars;
385 size_t all_cnt, keep_cnt, drop_cnt;
387 struct var_renaming *var_renaming;
391 /* All variables, in index order. */
392 dict_get_vars (d, &all_vars, &all_cnt, 0);
394 /* Drop variables, in index order. */
395 drop_cnt = vm->drop_cnt;
396 drop_vars = xmalloc (drop_cnt * sizeof *drop_vars);
397 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
398 sort (drop_vars, drop_cnt, sizeof *drop_vars,
399 compare_variables_given_ordering, &forward_positional_ordering);
401 /* Keep variables, in index order. */
402 keep_cnt = all_cnt - drop_cnt;
403 keep_vars = xmalloc (keep_cnt * sizeof *keep_vars);
404 if (set_difference (all_vars, all_cnt,
408 compare_variables_given_ordering,
409 &forward_positional_ordering) != keep_cnt)
412 /* Copy variables into var_renaming array. */
413 var_renaming = xmalloc (keep_cnt * sizeof *var_renaming);
414 for (i = 0; i < keep_cnt; i++)
416 var_renaming[i].var = keep_vars[i];
417 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
420 /* Rename variables in var_renaming array. */
421 for (i = 0; i < vm->rename_cnt; i++)
423 struct variable *const *kv;
424 struct var_renaming *vr;
426 /* Get the var_renaming element. */
427 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
429 compare_variables_given_ordering,
430 &forward_positional_ordering);
433 vr = var_renaming + (kv - keep_vars);
435 strcpy (vr->new_name, vm->new_names[i]);
438 /* Sort var_renaming array by new names and check for
440 sort (var_renaming, keep_cnt, sizeof *var_renaming,
441 compare_var_renaming_by_new_name, NULL);
442 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
443 compare_var_renaming_by_new_name, NULL) == NULL;
454 /* Reoders, removes, and renames variables in dictionary D
455 according to VM. Returns nonzero if successful, zero if there
456 would have been duplicate variable names if the modifications
457 had been carried out. In the latter case, the dictionary is
460 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
462 char **rename_old_names;
464 struct variable **rename_vars;
465 char **rename_new_names;
470 /* Check whether the modifications will cause duplicate
472 if (!validate_var_modification (d, vm))
475 /* Record the old names of variables to rename. After
476 variables are deleted, we can't depend on the variables to
477 still exist, but we can still look them up by name. */
478 rename_old_names = xmalloc (vm->rename_cnt * sizeof *rename_old_names);
479 for (i = 0; i < vm->rename_cnt; i++)
480 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
482 /* Reorder and delete variables. */
483 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
484 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
486 /* Compose lists of variables to rename and their new names. */
487 rename_vars = xmalloc (vm->rename_cnt * sizeof *rename_vars);
488 rename_new_names = xmalloc (vm->rename_cnt * sizeof *rename_new_names);
490 for (i = 0; i < vm->rename_cnt; i++)
492 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
496 rename_vars[rename_cnt] = var;
497 rename_new_names[rename_cnt] = vm->new_names[i];
502 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
507 for (i = 0; i < vm->rename_cnt; i++)
508 free (rename_old_names[i]);
509 free (rename_old_names);
511 free (rename_new_names);