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/procedure.h>
35 #include <data/variable.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;
91 if (proc_make_temporary_transformations_permanent ())
92 msg (SE, _("MODIFY VARS may not be used after TEMPORARY. "
93 "Temporary transformations will be made permanent."));
95 vm.reorder_vars = NULL;
97 vm.rename_vars = NULL;
103 /* Parse each subcommand. */
107 if (lex_match_id ("REORDER"))
109 struct variable **v = NULL;
112 if (already_encountered & 1)
114 msg (SE, _("REORDER subcommand may be given at most once."));
117 already_encountered |= 1;
122 struct ordering ordering;
125 ordering.forward = ordering.positional = 1;
126 if (lex_match_id ("FORWARD"));
127 else if (lex_match_id ("BACKWARD"))
128 ordering.forward = 0;
129 if (lex_match_id ("POSITIONAL"));
130 else if (lex_match_id ("ALPHA"))
131 ordering.positional = 0;
133 if (lex_match (T_ALL) || token == '/' || token == '.')
137 msg (SE, _("Cannot specify ALL after specifying a set "
141 dict_get_vars (default_dict, &v, &nv, 1u << DC_SYSTEM);
145 if (!lex_match ('('))
147 msg (SE, _("`(' expected on REORDER subcommand."));
151 if (!parse_variables (default_dict, &v, &nv,
152 PV_APPEND | PV_NO_DUPLICATE))
157 if (!lex_match (')'))
159 msg (SE, _("`)' expected following variable names on "
160 "REORDER subcommand."));
165 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
166 compare_variables_given_ordering, &ordering);
168 while (token != '/' && token != '.');
173 else if (lex_match_id ("RENAME"))
175 if (already_encountered & 2)
177 msg (SE, _("RENAME subcommand may be given at most once."));
180 already_encountered |= 2;
185 size_t prev_nv_1 = vm.rename_cnt;
186 size_t prev_nv_2 = vm.rename_cnt;
188 if (!lex_match ('('))
190 msg (SE, _("`(' expected on RENAME subcommand."));
193 if (!parse_variables (default_dict, &vm.rename_vars, &vm.rename_cnt,
194 PV_APPEND | PV_NO_DUPLICATE))
196 if (!lex_match ('='))
198 msg (SE, _("`=' expected between lists of new and old variable "
199 "names on RENAME subcommand."));
202 if (!parse_DATA_LIST_vars (&vm.new_names, &prev_nv_1, PV_APPEND))
204 if (prev_nv_1 != vm.rename_cnt)
206 msg (SE, _("Differing number of variables in old name list "
207 "(%d) and in new name list (%d)."),
208 vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
209 for (i = 0; i < prev_nv_1; i++)
210 free (vm.new_names[i]);
215 if (!lex_match (')'))
217 msg (SE, _("`)' expected after variable lists on RENAME "
222 while (token != '.' && token != '/');
224 else if (lex_match_id ("KEEP"))
226 struct variable **keep_vars, **all_vars, **drop_vars;
227 size_t keep_cnt, all_cnt, drop_cnt;
229 if (already_encountered & 4)
231 msg (SE, _("KEEP subcommand may be given at most once. It may not"
232 "be given in conjunction with the DROP subcommand."));
235 already_encountered |= 4;
238 if (!parse_variables (default_dict, &keep_vars, &keep_cnt, PV_NONE))
241 /* Transform the list of variables to keep into a list of
242 variables to drop. First sort the keep list, then figure
243 out which variables are missing. */
244 sort (keep_vars, keep_cnt, sizeof *keep_vars,
245 compare_variables_given_ordering, &forward_positional_ordering);
247 dict_get_vars (default_dict, &all_vars, &all_cnt, 0);
248 assert (all_cnt >= keep_cnt);
250 drop_cnt = all_cnt - keep_cnt;
251 drop_vars = xnmalloc (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 if (!procedure (NULL, NULL))
323 if (!rearrange_dict (default_dict, &vm))
326 ret_code = CMD_SUCCESS;
329 free (vm.reorder_vars);
330 free (vm.rename_vars);
331 for (i = 0; i < vm.rename_cnt; i++)
332 free (vm.new_names[i]);
338 /* Compares A and B according to the settings in
339 ORDERING, returning a strcmp()-type result. */
341 compare_variables_given_ordering (const void *a_, const void *b_,
344 struct variable *const *pa = a_;
345 struct variable *const *pb = b_;
346 const struct variable *a = *pa;
347 const struct variable *b = *pb;
348 const struct ordering *ordering = ordering_;
351 if (ordering->positional)
352 result = a->index < b->index ? -1 : a->index > b->index;
354 result = strcasecmp (a->name, b->name);
355 if (!ordering->forward)
360 /* Pairs a variable with a new name. */
363 struct variable *var;
364 char new_name[LONG_NAME_LEN + 1];
367 /* A algo_compare_func that compares new_name members in struct
368 var_renaming structures A and B. */
370 compare_var_renaming_by_new_name (const void *a_, const void *b_,
373 const struct var_renaming *a = a_;
374 const struct var_renaming *b = b_;
376 return strcasecmp (a->new_name, b->new_name);
379 /* Returns true if performing VM on dictionary D would not cause
380 problems such as duplicate variable names. Returns false
381 otherwise, and issues an error message. */
383 validate_var_modification (const struct dictionary *d,
384 const struct var_modification *vm)
386 /* Variable reordering can't be a problem, so we don't simulate
387 it. Variable renaming can cause duplicate names, but
388 dropping variables can eliminate them, so we simulate both
390 struct variable **all_vars;
391 struct variable **keep_vars;
392 struct variable **drop_vars;
393 size_t keep_cnt, drop_cnt;
396 struct var_renaming *var_renaming;
400 /* All variables, in index order. */
401 dict_get_vars (d, &all_vars, &all_cnt, 0);
403 /* Drop variables, in index order. */
404 drop_cnt = vm->drop_cnt;
405 drop_vars = xnmalloc (drop_cnt, sizeof *drop_vars);
406 memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
407 sort (drop_vars, drop_cnt, sizeof *drop_vars,
408 compare_variables_given_ordering, &forward_positional_ordering);
410 /* Keep variables, in index order. */
411 assert (all_cnt >= drop_cnt);
412 keep_cnt = all_cnt - drop_cnt;
413 keep_vars = xnmalloc (keep_cnt, sizeof *keep_vars);
414 if (set_difference (all_vars, all_cnt,
418 compare_variables_given_ordering,
419 &forward_positional_ordering) != keep_cnt)
422 /* Copy variables into var_renaming array. */
423 var_renaming = xnmalloc (keep_cnt, sizeof *var_renaming);
424 for (i = 0; i < keep_cnt; i++)
426 var_renaming[i].var = keep_vars[i];
427 strcpy (var_renaming[i].new_name, keep_vars[i]->name);
430 /* Rename variables in var_renaming array. */
431 for (i = 0; i < vm->rename_cnt; i++)
433 struct variable *const *kv;
434 struct var_renaming *vr;
436 /* Get the var_renaming element. */
437 kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
439 compare_variables_given_ordering,
440 &forward_positional_ordering);
443 vr = var_renaming + (kv - keep_vars);
445 strcpy (vr->new_name, vm->new_names[i]);
448 /* Sort var_renaming array by new names and check for
450 sort (var_renaming, keep_cnt, sizeof *var_renaming,
451 compare_var_renaming_by_new_name, NULL);
452 valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
453 compare_var_renaming_by_new_name, NULL) == NULL;
464 /* Reoders, removes, and renames variables in dictionary D
465 according to VM. Returns nonzero if successful, zero if there
466 would have been duplicate variable names if the modifications
467 had been carried out. In the latter case, the dictionary is
470 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
472 char **rename_old_names;
474 struct variable **rename_vars;
475 char **rename_new_names;
480 /* Check whether the modifications will cause duplicate
482 if (!validate_var_modification (d, vm))
485 /* Record the old names of variables to rename. After
486 variables are deleted, we can't depend on the variables to
487 still exist, but we can still look them up by name. */
488 rename_old_names = xnmalloc (vm->rename_cnt, sizeof *rename_old_names);
489 for (i = 0; i < vm->rename_cnt; i++)
490 rename_old_names[i] = xstrdup (vm->rename_vars[i]->name);
492 /* Reorder and delete variables. */
493 dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
494 dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
496 /* Compose lists of variables to rename and their new names. */
497 rename_vars = xnmalloc (vm->rename_cnt, sizeof *rename_vars);
498 rename_new_names = xnmalloc (vm->rename_cnt, sizeof *rename_new_names);
500 for (i = 0; i < vm->rename_cnt; i++)
502 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
506 rename_vars[rename_cnt] = var;
507 rename_new_names[rename_cnt] = vm->new_names[i];
512 if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
517 for (i = 0; i < vm->rename_cnt; i++)
518 free (rename_old_names[i]);
519 free (rename_old_names);
521 free (rename_new_names);