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
22 #include <libpspp/message.h>
23 #include <libpspp/array.h>
24 #include <libpspp/alloc.h>
25 #include <libpspp/bit-vector.h>
26 #include <language/command.h>
27 #include <libpspp/compiler.h>
28 #include <data/dictionary.h>
29 #include <libpspp/message.h>
30 #include <libpspp/hash.h>
31 #include <language/lexer/lexer.h>
32 #include <libpspp/misc.h>
33 #include <libpspp/str.h>
34 #include <data/variable.h>
35 #include <procedure.h>
38 #define _(msgid) gettext (msgid)
40 /* FIXME: should change weighting variable, etc. */
41 /* These control the ordering produced by
42 compare_variables_given_ordering(). */
45 int forward; /* 1=FORWARD, 0=BACKWARD. */
46 int positional; /* 1=POSITIONAL, 0=ALPHA. */
49 /* Increasing order of variable index. */
50 static struct ordering forward_positional_ordering = {1, 1};
52 static int compare_variables_given_ordering (const void *, const void *,
55 /* Explains how to modify the variables in a dictionary. */
56 struct var_modification
58 /* New variable ordering. */
59 struct variable **reorder_vars;
62 /* DROP/KEEP information. */
63 struct variable **drop_vars;
66 /* New variable names. */
67 struct variable **rename_vars;
72 static int rearrange_dict (struct dictionary *d,
73 const struct var_modification *vm);
75 /* Performs MODIFY VARS command. */
77 cmd_modify_vars (void)
79 /* Bits indicated whether we've already encountered a subcommand of
81 unsigned already_encountered = 0;
83 /* What we're gonna do to the active file. */
84 struct var_modification vm;
87 int ret_code = CMD_CASCADING_FAILURE;
93 msg (SE, _("MODIFY VARS may not be used after TEMPORARY. "
94 "Temporary transformations will be made permanent."));
98 vm.reorder_vars = NULL;
100 vm.rename_vars = NULL;
106 /* Parse each subcommand. */
110 if (lex_match_id ("REORDER"))
112 struct variable **v = NULL;
115 if (already_encountered & 1)
117 msg (SE, _("REORDER subcommand may be given at most once."));
120 already_encountered |= 1;
125 struct ordering ordering;
128 ordering.forward = ordering.positional = 1;
129 if (lex_match_id ("FORWARD"));
130 else if (lex_match_id ("BACKWARD"))
131 ordering.forward = 0;
132 if (lex_match_id ("POSITIONAL"));
133 else if (lex_match_id ("ALPHA"))
134 ordering.positional = 0;
136 if (lex_match (T_ALL) || token == '/' || token == '.')
140 msg (SE, _("Cannot specify ALL after specifying a set "
144 dict_get_vars (default_dict, &v, &nv, 1u << DC_SYSTEM);
148 if (!lex_match ('('))
150 msg (SE, _("`(' expected on REORDER subcommand."));
154 if (!parse_variables (default_dict, &v, &nv,
155 PV_APPEND | PV_NO_DUPLICATE))
160 if (!lex_match (')'))
162 msg (SE, _("`)' expected following variable names on "
163 "REORDER subcommand."));
168 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
169 compare_variables_given_ordering, &ordering);
171 while (token != '/' && token != '.');
176 else if (lex_match_id ("RENAME"))
178 if (already_encountered & 2)
180 msg (SE, _("RENAME subcommand may be given at most once."));
183 already_encountered |= 2;
188 size_t prev_nv_1 = vm.rename_cnt;
189 size_t prev_nv_2 = vm.rename_cnt;
191 if (!lex_match ('('))
193 msg (SE, _("`(' expected on RENAME subcommand."));
196 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
197 PV_APPEND | PV_NO_DUPLICATE))
199 if (!lex_match ('='))
201 msg (SE, _("`=' expected between lists of new and old variable "
202 "names on RENAME subcommand."));
205 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
207 if (prev_nv_1 != vm.rename_cnt)
209 msg (SE, _("Differing number of variables in old name list "
210 "(%d) and in new name list (%d)."),
211 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
212 for (i = 0; i < prev_nv_1; i++)
213 free (vm.new_names[i]);
218 if (!lex_match (')'))
220 msg (SE, _("`)' expected after variable lists on RENAME "
225 while (token != '.' && token != '/');
227 else if (lex_match_id ("KEEP"))
229 struct variable **keep_vars, **all_vars, **drop_vars;
230 size_t keep_cnt, all_cnt, drop_cnt;
232 if (already_encountered & 4)
234 msg (SE, _("KEEP subcommand may be given at most once. It may not"
235 "be given in conjunction with the DROP subcommand."));
238 already_encountered |= 4;
241 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
244 /* Transform the list of variables to keep into a list of
245 variables to drop. First sort the keep list, then figure
246 out which variables are missing. */
247 sort (keep_vars, keep_cnt, sizeof *keep_vars,
248 compare_variables_given_ordering, &forward_positional_ordering);
250 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
251 assert (all_cnt >= keep_cnt);
253 drop_cnt = all_cnt - keep_cnt;
254 drop_vars = xnmalloc (drop_cnt, sizeof *keep_vars);
255 if (set_difference (all_vars, all_cnt,
259 compare_variables_given_ordering,
260 &forward_positional_ordering)
267 vm.drop_vars = drop_vars;
268 vm.drop_cnt = drop_cnt;
270 else if (lex_match_id ("DROP"))
272 struct variable **drop_vars;
275 if (already_encountered & 4)
277 msg (SE, _("DROP subcommand may be given at most once. It may "
278 "not be given in conjunction with the KEEP "
282 already_encountered |= 4;
285 if (!parse_variables (default_dict, &drop_vars, &drop_cnt, PV_NONE))
287 vm.drop_vars = drop_vars;
288 vm.drop_cnt = drop_cnt;
290 else if (lex_match_id ("MAP"))
292 struct dictionary *temp = dict_clone (default_dict);
293 int success = rearrange_dict (temp, &vm);
296 /* FIXME: display new dictionary. */
303 msg (SE, _("Unrecognized subcommand name `%s'."), tokid);
305 msg (SE, _("Subcommand name expected."));
313 msg (SE, _("`/' or `.' expected."));
319 if (already_encountered & (1 | 4))
322 if (!procedure (NULL, NULL))
326 if (!rearrange_dict (default_dict, &vm))
329 ret_code = CMD_SUCCESS;
332 free (vm.reorder_vars);
333 free (vm.rename_vars);
334 for (i = 0; i < vm.rename_cnt; i++)
335 free (vm.new_names[i]);
341 /* Compares A and B according to the settings in
342 ORDERING, returning a strcmp()-type result. */
344 compare_variables_given_ordering (const void *a_, const void *b_,
347 struct variable *const *pa = a_;
348 struct variable *const *pb = b_;
349 const struct variable *a = *pa;
350 const struct variable *b = *pb;
351 const struct ordering *ordering = ordering_;
354 if (ordering->positional)
355 result = a->index < b->index ? -1 : a->index > b->index;
357 result = strcasecmp (a->name, b->name);
358 if (!ordering->forward)
363 /* Pairs a variable with a new name. */
366 struct variable *var;
367 char new_name[LONG_NAME_LEN + 1];
370 /* A algo_compare_func that compares new_name members in struct
371 var_renaming structures A and B. */
373 compare_var_renaming_by_new_name (const void *a_, const void *b_,
376 const struct var_renaming *a = a_;
377 const struct var_renaming *b = b_;
379 return strcasecmp (a->new_name, b->new_name);
382 /* Returns true if performing VM on dictionary D would not cause
383 problems such as duplicate variable names. Returns false
384 otherwise, and issues an error message. */
386 validate_var_modification (const struct dictionary *d,
387 const struct var_modification *vm)
389 /* Variable reordering can't be a problem, so we don't simulate
390 it. Variable renaming can cause duplicate names, but
391 dropping variables can eliminate them, so we simulate both
393 struct variable **all_vars;
394 struct variable **keep_vars;
395 struct variable **drop_vars;
396 size_t keep_cnt, drop_cnt;
399 struct var_renaming *var_renaming;
403 /* All variables, in index order. */
404 dict_get_vars (d, &all_vars, &all_cnt, 0);
406 /* Drop variables, in index order. */
407 drop_cnt = vm->drop_cnt;
408 drop_vars = xnmalloc (drop_cnt, sizeof *drop_vars);
409 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
410 sort (drop_vars, drop_cnt, sizeof *drop_vars,
411 compare_variables_given_ordering, &forward_positional_ordering);
413 /* Keep variables, in index order. */
414 assert (all_cnt >= drop_cnt);
415 keep_cnt = all_cnt - drop_cnt;
416 keep_vars = xnmalloc (keep_cnt, sizeof *keep_vars);
417 if (set_difference (all_vars, all_cnt,
421 compare_variables_given_ordering,
422 &forward_positional_ordering) != keep_cnt)
425 /* Copy variables into var_renaming array. */
426 var_renaming = xnmalloc (keep_cnt, sizeof *var_renaming);
427 for (i = 0; i < keep_cnt; i++)
429 var_renaming[i].var = keep_vars[i];
430 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
433 /* Rename variables in var_renaming array. */
434 for (i = 0; i < vm->rename_cnt; i++)
436 struct variable *const *kv;
437 struct var_renaming *vr;
439 /* Get the var_renaming element. */
440 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
442 compare_variables_given_ordering,
443 &forward_positional_ordering);
446 vr = var_renaming + (kv - keep_vars);
448 strcpy (vr->new_name, vm->new_names[i]);
451 /* Sort var_renaming array by new names and check for
453 sort (var_renaming, keep_cnt, sizeof *var_renaming,
454 compare_var_renaming_by_new_name, NULL);
455 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
456 compare_var_renaming_by_new_name, NULL) == NULL;
467 /* Reoders, removes, and renames variables in dictionary D
468 according to VM. Returns nonzero if successful, zero if there
469 would have been duplicate variable names if the modifications
470 had been carried out. In the latter case, the dictionary is
473 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
475 char **rename_old_names;
477 struct variable **rename_vars;
478 char **rename_new_names;
483 /* Check whether the modifications will cause duplicate
485 if (!validate_var_modification (d, vm))
488 /* Record the old names of variables to rename. After
489 variables are deleted, we can't depend on the variables to
490 still exist, but we can still look them up by name. */
491 rename_old_names = xnmalloc (vm->rename_cnt, sizeof *rename_old_names);
492 for (i = 0; i < vm->rename_cnt; i++)
493 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
495 /* Reorder and delete variables. */
496 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
497 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
499 /* Compose lists of variables to rename and their new names. */
500 rename_vars = xnmalloc (vm->rename_cnt, sizeof *rename_vars);
501 rename_new_names = xnmalloc (vm->rename_cnt, sizeof *rename_new_names);
503 for (i = 0; i < vm->rename_cnt; i++)
505 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
509 rename_vars[rename_cnt] = var;
510 rename_new_names[rename_cnt] = vm->new_names[i];
515 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
520 for (i = 0; i < vm->rename_cnt; i++)
521 free (rename_old_names[i]);
522 free (rename_old_names);
524 free (rename_new_names);