1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2010, 2011, 2012 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "data/dataset.h"
22 #include "data/dictionary.h"
23 #include "data/variable.h"
24 #include "language/command.h"
25 #include "language/lexer/lexer.h"
26 #include "language/lexer/variable-parser.h"
27 #include "libpspp/array.h"
28 #include "libpspp/assertion.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/i18n.h"
31 #include "libpspp/message.h"
32 #include "libpspp/misc.h"
33 #include "libpspp/str.h"
35 #include "gl/xalloc.h"
38 #define _(msgid) gettext (msgid)
40 /* These control the ordering produced by
41 compare_variables_given_ordering(). */
44 bool forward; /* true=FORWARD, false=BACKWARD. */
45 bool positional; /* true=POSITIONAL, false=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 *,
52 const void *ordering);
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 bool rearrange_dict (struct dictionary *d,
72 const struct var_modification *vm);
74 /* Performs MODIFY VARS command. */
76 cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
78 if (proc_make_temporary_transformations_permanent (ds))
79 msg (SE, _("%s may not be used after %s. "
80 "Temporary transformations will be made permanent."),
81 "MODIFY VARS", "TEMPORARY");
83 /* Bits indicated whether we've already encountered a subcommand of this
85 unsigned int already_encountered = 0;
88 int ret_code = CMD_CASCADING_FAILURE;
90 /* What we are going to do to the active dataset. */
91 struct var_modification vm =
102 /* Parse each subcommand. */
103 lex_match (lexer, T_SLASH);
106 if (lex_match_id (lexer, "REORDER"))
108 if (already_encountered & 1)
110 lex_sbc_only_once ("REORDER");
113 already_encountered |= 1;
115 struct variable **v = NULL;
118 lex_match (lexer, T_EQUALS);
121 struct ordering ordering;
124 ordering.forward = ordering.positional = true;
127 if (lex_match_id (lexer, "FORWARD"))
128 ordering.forward = true;
129 else if (lex_match_id (lexer, "BACKWARD"))
130 ordering.forward = false;
131 else if (lex_match_id (lexer, "POSITIONAL"))
132 ordering.positional = true;
133 else if (lex_match_id (lexer, "ALPHA"))
134 ordering.positional = false;
139 if (lex_match (lexer, T_ALL)
140 || lex_token (lexer) == T_SLASH
141 || lex_token (lexer) == T_ENDCMD)
145 msg (SE, _("Cannot specify ALL after specifying a set "
149 dict_get_vars_mutable (dataset_dict (ds), &v, &nv,
154 if (!lex_match (lexer, T_LPAREN))
156 lex_error_expecting (lexer, "`('");
160 if (!parse_variables (lexer, dataset_dict (ds), &v, &nv,
161 PV_APPEND | PV_NO_DUPLICATE))
166 if (!lex_match (lexer, T_RPAREN))
168 lex_error_expecting (lexer, "`)'");
174 if (!ordering.positional)
175 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
176 compare_variables_given_ordering, &ordering);
177 else if (!ordering.forward)
178 reverse_array(&v[prev_nv], nv - prev_nv, sizeof *v);
180 while (lex_token (lexer) != T_SLASH
181 && lex_token (lexer) != T_ENDCMD);
186 else if (lex_match_id (lexer, "RENAME"))
188 if (already_encountered & 2)
190 lex_sbc_only_once ("RENAME");
193 already_encountered |= 2;
195 lex_match (lexer, T_EQUALS);
198 size_t prev_nv_1 = vm.n_rename;
199 size_t prev_nv_2 = vm.n_rename;
201 if (!lex_match (lexer, T_LPAREN))
203 lex_error_expecting (lexer, "`('");
206 if (!parse_variables (lexer, dataset_dict (ds),
207 &vm.rename_vars, &vm.n_rename,
208 PV_APPEND | PV_NO_DUPLICATE))
210 if (!lex_match (lexer, T_EQUALS))
212 lex_error_expecting (lexer, "`='");
216 if (!parse_DATA_LIST_vars (lexer, dataset_dict (ds),
217 &vm.new_names, &prev_nv_1, PV_APPEND))
219 if (prev_nv_1 != vm.n_rename)
221 msg (SE, _("Differing number of variables in old name list "
222 "(%zu) and in new name list (%zu)."),
223 vm.n_rename - prev_nv_2, prev_nv_1 - prev_nv_2);
224 for (size_t i = 0; i < prev_nv_1; i++)
225 free (vm.new_names[i]);
230 if (!lex_match (lexer, T_RPAREN))
232 lex_error_expecting (lexer, "`)'");
236 while (lex_token (lexer) != T_ENDCMD
237 && lex_token (lexer) != T_SLASH);
239 else if (lex_match_id (lexer, "KEEP"))
241 if (already_encountered & 4)
244 _("%s subcommand may be given at most once. It may "
245 "not be given in conjunction with the %s subcommand."),
249 already_encountered |= 4;
251 struct variable **keep_vars, **drop_vars;
252 size_t n_keep, n_drop;
253 lex_match (lexer, T_EQUALS);
254 if (!parse_variables (lexer, dataset_dict (ds),
255 &keep_vars, &n_keep, PV_NONE))
258 /* Transform the list of variables to keep into a list of
259 variables to drop. First sort the keep list, then figure
260 out which variables are missing. */
261 sort (keep_vars, n_keep, sizeof *keep_vars,
262 compare_variables_given_ordering,
263 &forward_positional_ordering);
265 struct variable **all_vars;
267 dict_get_vars_mutable (dataset_dict (ds), &all_vars, &n_all, 0);
268 assert (n_all >= n_keep);
270 n_drop = n_all - n_keep;
271 drop_vars = xnmalloc (n_drop, sizeof *keep_vars);
272 if (set_difference (all_vars, n_all,
276 compare_variables_given_ordering,
277 &forward_positional_ordering)
284 vm.drop_vars = drop_vars;
287 else if (lex_match_id (lexer, "DROP"))
289 struct variable **drop_vars;
292 if (already_encountered & 4)
294 msg (SE, _("%s subcommand may be given at most once. It may "
295 "not be given in conjunction with the %s "
301 already_encountered |= 4;
303 lex_match (lexer, T_EQUALS);
304 if (!parse_variables (lexer, dataset_dict (ds),
305 &drop_vars, &n_drop, PV_NONE))
307 vm.drop_vars = drop_vars;
310 if (n_drop == dict_get_var_cnt (dataset_dict (ds)))
312 msg (SE, _("%s may not be used to delete all variables "
313 "from the active dataset dictionary. "
314 "Use %s instead."), "MODIFY VARS", "NEW FILE");
318 else if (lex_match_id (lexer, "MAP"))
320 struct dictionary *temp = dict_clone (dataset_dict (ds));
321 int success = rearrange_dict (temp, &vm);
324 /* FIXME: display new dictionary. */
330 if (lex_token (lexer) == T_ID)
331 msg (SE, _("Unrecognized subcommand name `%s'."),
332 lex_tokcstr (lexer));
334 msg (SE, _("Subcommand name expected."));
338 if (lex_token (lexer) == T_ENDCMD)
340 if (lex_token (lexer) != T_SLASH)
342 lex_error_expecting (lexer, "`/'", "`.'");
348 if (already_encountered & (1 | 4))
351 if (!proc_execute (ds))
355 if (!rearrange_dict (dataset_dict (ds), &vm))
358 ret_code = CMD_SUCCESS;
361 free (vm.reorder_vars);
362 free (vm.rename_vars);
363 for (size_t i = 0; i < vm.n_rename; i++)
364 free (vm.new_names[i]);
370 /* Compares A and B according to the settings in ORDERING, returning a
371 strcmp()-type result. */
373 compare_variables_given_ordering (const void *a_, const void *b_,
374 const void *ordering_)
376 struct variable *const *pa = a_;
377 struct variable *const *pb = b_;
378 const struct variable *a = *pa;
379 const struct variable *b = *pb;
380 const struct ordering *ordering = ordering_;
383 if (ordering->positional)
385 size_t a_index = var_get_dict_index (a);
386 size_t b_index = var_get_dict_index (b);
387 result = a_index < b_index ? -1 : a_index > b_index;
390 result = utf8_strcasecmp (var_get_name (a), var_get_name (b));
391 if (!ordering->forward)
396 /* Pairs a variable with a new name. */
399 struct variable *var;
400 const char *new_name;
403 /* A algo_compare_func that compares new_name members in struct var_renaming
404 structures A and B. */
406 compare_var_renaming_by_new_name (const void *a_, const void *b_,
407 const void *aux UNUSED)
409 const struct var_renaming *a = a_;
410 const struct var_renaming *b = b_;
412 return utf8_strcasecmp (a->new_name, b->new_name);
415 /* Returns true if performing VM on dictionary D would not cause problems such
416 as duplicate variable names. Returns false otherwise, and issues an error
419 validate_var_modification (const struct dictionary *d,
420 const struct var_modification *vm)
422 /* Variable reordering can't be a problem, so we don't simulate
423 it. Variable renaming can cause duplicate names, but
424 dropping variables can eliminate them, so we simulate both
427 /* All variables, in index order. */
428 struct variable **all_vars;
430 dict_get_vars_mutable (d, &all_vars, &n_all, 0);
432 /* Drop variables, in index order. */
433 size_t n_drop = vm->n_drop;
434 struct variable **drop_vars = xnmalloc (n_drop, sizeof *drop_vars);
435 memcpy (drop_vars, vm->drop_vars, n_drop * sizeof *drop_vars);
436 sort (drop_vars, n_drop, sizeof *drop_vars,
437 compare_variables_given_ordering, &forward_positional_ordering);
439 /* Keep variables, in index order. */
440 assert (n_all >= n_drop);
441 size_t n_keep = n_all - n_drop;
442 struct variable **keep_vars = xnmalloc (n_keep, sizeof *keep_vars);
443 if (set_difference (all_vars, n_all,
447 compare_variables_given_ordering,
448 &forward_positional_ordering) != n_keep)
451 /* Copy variables into var_renaming array. */
452 struct var_renaming *var_renaming = xnmalloc (n_keep, sizeof *var_renaming);
453 for (size_t i = 0; i < n_keep; i++)
455 var_renaming[i].var = keep_vars[i];
456 var_renaming[i].new_name = var_get_name (keep_vars[i]);
459 /* Rename variables in var_renaming array. */
460 for (size_t i = 0; i < vm->n_rename; i++)
462 struct variable *const *kv;
463 struct var_renaming *vr;
465 /* Get the var_renaming element. */
466 kv = binary_search (keep_vars, n_keep, sizeof *keep_vars,
468 compare_variables_given_ordering,
469 &forward_positional_ordering);
472 vr = var_renaming + (kv - keep_vars);
474 vr->new_name = vm->new_names[i];
477 /* Sort var_renaming array by new names and check for duplicates. */
478 sort (var_renaming, n_keep, sizeof *var_renaming,
479 compare_var_renaming_by_new_name, NULL);
480 bool ok = !adjacent_find_equal (var_renaming, n_keep, sizeof *var_renaming,
481 compare_var_renaming_by_new_name, NULL);
492 /* Reorders, removes, and renames variables in dictionary D according to VM.
493 Returns true if successful, false if there would have been duplicate
494 variable names if the modifications had been carried out. In the latter
495 case, the dictionary is not modified. */
497 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
499 /* Check whether the modifications will cause duplicate names. */
500 if (!validate_var_modification (d, vm))
503 /* Record the old names of variables to rename. After variables are deleted,
504 we can't depend on the variables to still exist, but we can still look
506 char **rename_old_names = xnmalloc (vm->n_rename, sizeof *rename_old_names);
507 for (size_t i = 0; i < vm->n_rename; i++)
508 rename_old_names[i] = xstrdup (var_get_name (vm->rename_vars[i]));
510 /* Reorder and delete variables. */
511 dict_reorder_vars (d, vm->reorder_vars, vm->n_reorder);
512 dict_delete_vars (d, vm->drop_vars, vm->n_drop);
514 /* Compose lists of variables to rename and their new names. */
515 struct variable **rename_vars = xnmalloc (vm->n_rename, sizeof *rename_vars);
516 char **rename_new_names = xnmalloc (vm->n_rename, sizeof *rename_new_names);
518 for (size_t i = 0; i < vm->n_rename; i++)
520 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
524 rename_vars[n_rename] = var;
525 rename_new_names[n_rename] = vm->new_names[i];
530 if (dict_rename_vars (d, rename_vars, rename_new_names, n_rename,
535 for (size_t i = 0; i < vm->n_rename; i++)
536 free (rename_old_names[i]);
537 free (rename_old_names);
539 free (rename_new_names);