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
25 #include "bit-vector.h"
27 #include "dictionary.h"
34 #include "procedure.h"
37 #define _(msgid) gettext (msgid)
39 /* FIXME: should change weighting variable, etc. */
40 /* These control the ordering produced by
41 compare_variables_given_ordering(). */
44 int forward; /* 1=FORWARD, 0=BACKWARD. */
45 int positional; /* 1=POSITIONAL, 0=ALPHA. */
48 /* Increasing order of variable index. */
49 static struct ordering forward_positional_ordering = {1, 1};
51 static int compare_variables_given_ordering (const void *, const void *,
54 /* Explains how to modify the variables in a dictionary. */
55 struct var_modification
57 /* New variable ordering. */
58 struct variable **reorder_vars;
61 /* DROP/KEEP information. */
62 struct variable **drop_vars;
65 /* New variable names. */
66 struct variable **rename_vars;
71 static int rearrange_dict (struct dictionary *d,
72 const struct var_modification *vm);
74 /* Performs MODIFY VARS command. */
76 cmd_modify_vars (void)
78 /* Bits indicated whether we've already encountered a subcommand of
80 unsigned already_encountered = 0;
82 /* What we're gonna do to the active file. */
83 struct var_modification vm;
86 int ret_code = CMD_CASCADING_FAILURE;
92 msg (SE, _("MODIFY VARS may not be used after TEMPORARY. "
93 "Temporary transformations will be made permanent."));
97 vm.reorder_vars = NULL;
99 vm.rename_vars = NULL;
105 /* Parse each subcommand. */
109 if (lex_match_id ("REORDER"))
111 struct variable **v = NULL;
114 if (already_encountered & 1)
116 msg (SE, _("REORDER subcommand may be given at most once."));
119 already_encountered |= 1;
124 struct ordering ordering;
127 ordering.forward = ordering.positional = 1;
128 if (lex_match_id ("FORWARD"));
129 else if (lex_match_id ("BACKWARD"))
130 ordering.forward = 0;
131 if (lex_match_id ("POSITIONAL"));
132 else if (lex_match_id ("ALPHA"))
133 ordering.positional = 0;
135 if (lex_match (T_ALL) || token == '/' || token == '.')
139 msg (SE, _("Cannot specify ALL after specifying a set "
143 dict_get_vars (default_dict, &v, &nv, 1u << DC_SYSTEM);
147 if (!lex_match ('('))
149 msg (SE, _("`(' expected on REORDER subcommand."));
153 if (!parse_variables (default_dict, &v, &nv,
154 PV_APPEND | PV_NO_DUPLICATE))
159 if (!lex_match (')'))
161 msg (SE, _("`)' expected following variable names on "
162 "REORDER subcommand."));
167 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
168 compare_variables_given_ordering, &ordering);
170 while (token != '/' && token != '.');
175 else if (lex_match_id ("RENAME"))
177 if (already_encountered & 2)
179 msg (SE, _("RENAME subcommand may be given at most once."));
182 already_encountered |= 2;
187 size_t prev_nv_1 = vm.rename_cnt;
188 size_t prev_nv_2 = vm.rename_cnt;
190 if (!lex_match ('('))
192 msg (SE, _("`(' expected on RENAME subcommand."));
195 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
196 PV_APPEND | PV_NO_DUPLICATE))
198 if (!lex_match ('='))
200 msg (SE, _("`=' expected between lists of new and old variable "
201 "names on RENAME subcommand."));
204 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
206 if (prev_nv_1 != vm.rename_cnt)
208 msg (SE, _("Differing number of variables in old name list "
209 "(%d) and in new name list (%d)."),
210 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
211 for (i = 0; i < prev_nv_1; i++)
212 free (vm.new_names[i]);
217 if (!lex_match (')'))
219 msg (SE, _("`)' expected after variable lists on RENAME "
224 while (token != '.' && token != '/');
226 else if (lex_match_id ("KEEP"))
228 struct variable **keep_vars, **all_vars, **drop_vars;
229 size_t keep_cnt, all_cnt, drop_cnt;
231 if (already_encountered & 4)
233 msg (SE, _("KEEP subcommand may be given at most once. It may not"
234 "be given in conjunction with the DROP subcommand."));
237 already_encountered |= 4;
240 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
243 /* Transform the list of variables to keep into a list of
244 variables to drop. First sort the keep list, then figure
245 out which variables are missing. */
246 sort (keep_vars, keep_cnt, sizeof *keep_vars,
247 compare_variables_given_ordering, &forward_positional_ordering);
249 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
250 assert (all_cnt >= keep_cnt);
252 drop_cnt = all_cnt - keep_cnt;
253 drop_vars = xnmalloc (drop_cnt, sizeof *keep_vars);
254 if (set_difference (all_vars, all_cnt,
258 compare_variables_given_ordering,
259 &forward_positional_ordering)
266 vm.drop_vars = drop_vars;
267 vm.drop_cnt = drop_cnt;
269 else if (lex_match_id ("DROP"))
271 struct variable **drop_vars;
274 if (already_encountered & 4)
276 msg (SE, _("DROP subcommand may be given at most once. It may "
277 "not be given in conjunction with the KEEP "
281 already_encountered |= 4;
284 if (!parse_variables (default_dict, &drop_vars, &drop_cnt, PV_NONE))
286 vm.drop_vars = drop_vars;
287 vm.drop_cnt = drop_cnt;
289 else if (lex_match_id ("MAP"))
291 struct dictionary *temp = dict_clone (default_dict);
292 int success = rearrange_dict (temp, &vm);
295 /* FIXME: display new dictionary. */
302 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
304 msg (SE, _("Subcommand name expected."));
312 msg (SE, _("`/' or `.' expected."));
318 if (already_encountered & (1 | 4))
321 if (!procedure (NULL, NULL))
325 if (!rearrange_dict (default_dict, &vm))
328 ret_code = CMD_SUCCESS;
331 free (vm.reorder_vars);
332 free (vm.rename_vars);
333 for (i = 0; i < vm.rename_cnt; i++)
334 free (vm.new_names[i]);
340 /* Compares A and B according to the settings in
341 ORDERING, returning a strcmp()-type result. */
343 compare_variables_given_ordering (const void *a_, const void *b_,
346 struct variable *const *pa = a_;
347 struct variable *const *pb = b_;
348 const struct variable *a = *pa;
349 const struct variable *b = *pb;
350 const struct ordering *ordering = ordering_;
353 if (ordering->positional)
354 result = a->index < b->index ? -1 : a->index > b->index;
356 result = strcasecmp (a->name, b->name);
357 if (!ordering->forward)
362 /* Pairs a variable with a new name. */
365 struct variable *var;
366 char new_name[LONG_NAME_LEN + 1];
369 /* A algo_compare_func that compares new_name members in struct
370 var_renaming structures A and B. */
372 compare_var_renaming_by_new_name (const void *a_, const void *b_,
375 const struct var_renaming *a = a_;
376 const struct var_renaming *b = b_;
378 return strcasecmp (a->new_name, b->new_name);
381 /* Returns true if performing VM on dictionary D would not cause
382 problems such as duplicate variable names. Returns false
383 otherwise, and issues an error message. */
385 validate_var_modification (const struct dictionary *d,
386 const struct var_modification *vm)
388 /* Variable reordering can't be a problem, so we don't simulate
389 it. Variable renaming can cause duplicate names, but
390 dropping variables can eliminate them, so we simulate both
392 struct variable **all_vars;
393 struct variable **keep_vars;
394 struct variable **drop_vars;
395 size_t keep_cnt, drop_cnt;
398 struct var_renaming *var_renaming;
402 /* All variables, in index order. */
403 dict_get_vars (d, &all_vars, &all_cnt, 0);
405 /* Drop variables, in index order. */
406 drop_cnt = vm->drop_cnt;
407 drop_vars = xnmalloc (drop_cnt, sizeof *drop_vars);
408 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
409 sort (drop_vars, drop_cnt, sizeof *drop_vars,
410 compare_variables_given_ordering, &forward_positional_ordering);
412 /* Keep variables, in index order. */
413 assert (all_cnt >= drop_cnt);
414 keep_cnt = all_cnt - drop_cnt;
415 keep_vars = xnmalloc (keep_cnt, sizeof *keep_vars);
416 if (set_difference (all_vars, all_cnt,
420 compare_variables_given_ordering,
421 &forward_positional_ordering) != keep_cnt)
424 /* Copy variables into var_renaming array. */
425 var_renaming = xnmalloc (keep_cnt, sizeof *var_renaming);
426 for (i = 0; i < keep_cnt; i++)
428 var_renaming[i].var = keep_vars[i];
429 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
432 /* Rename variables in var_renaming array. */
433 for (i = 0; i < vm->rename_cnt; i++)
435 struct variable *const *kv;
436 struct var_renaming *vr;
438 /* Get the var_renaming element. */
439 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
441 compare_variables_given_ordering,
442 &forward_positional_ordering);
445 vr = var_renaming + (kv - keep_vars);
447 strcpy (vr->new_name, vm->new_names[i]);
450 /* Sort var_renaming array by new names and check for
452 sort (var_renaming, keep_cnt, sizeof *var_renaming,
453 compare_var_renaming_by_new_name, NULL);
454 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
455 compare_var_renaming_by_new_name, NULL) == NULL;
466 /* Reoders, removes, and renames variables in dictionary D
467 according to VM. Returns nonzero if successful, zero if there
468 would have been duplicate variable names if the modifications
469 had been carried out. In the latter case, the dictionary is
472 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
474 char **rename_old_names;
476 struct variable **rename_vars;
477 char **rename_new_names;
482 /* Check whether the modifications will cause duplicate
484 if (!validate_var_modification (d, vm))
487 /* Record the old names of variables to rename. After
488 variables are deleted, we can't depend on the variables to
489 still exist, but we can still look them up by name. */
490 rename_old_names = xnmalloc (vm->rename_cnt, sizeof *rename_old_names);
491 for (i = 0; i < vm->rename_cnt; i++)
492 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
494 /* Reorder and delete variables. */
495 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
496 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
498 /* Compose lists of variables to rename and their new names. */
499 rename_vars = xnmalloc (vm->rename_cnt, sizeof *rename_vars);
500 rename_new_names = xnmalloc (vm->rename_cnt, sizeof *rename_new_names);
502 for (i = 0; i < vm->rename_cnt; i++)
504 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
508 rename_vars[rename_cnt] = var;
509 rename_new_names[rename_cnt] = vm->new_names[i];
514 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
519 for (i = 0; i < vm->rename_cnt; i++)
520 free (rename_old_names[i]);
521 free (rename_old_names);
523 free (rename_new_names);