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 lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
80 _("%s may not be used after %s. "
81 "Temporary transformations will be made permanent."),
82 "MODIFY VARS", "TEMPORARY");
84 /* Bits indicated whether we've already encountered a subcommand of this
86 unsigned int already_encountered = 0;
89 int ret_code = CMD_CASCADING_FAILURE;
91 /* What we are going to do to the active dataset. */
92 struct var_modification vm =
103 /* Parse each subcommand. */
104 lex_match (lexer, T_SLASH);
107 if (lex_match_id (lexer, "REORDER"))
109 if (already_encountered & 1)
111 lex_sbc_only_once (lexer, "REORDER");
114 already_encountered |= 1;
116 struct variable **v = NULL;
119 lex_match (lexer, T_EQUALS);
122 struct ordering ordering;
125 ordering.forward = ordering.positional = true;
128 if (lex_match_id (lexer, "FORWARD"))
129 ordering.forward = true;
130 else if (lex_match_id (lexer, "BACKWARD"))
131 ordering.forward = false;
132 else if (lex_match_id (lexer, "POSITIONAL"))
133 ordering.positional = true;
134 else if (lex_match_id (lexer, "ALPHA"))
135 ordering.positional = false;
140 if (lex_match (lexer, T_ALL)
141 || lex_token (lexer) == T_SLASH
142 || lex_token (lexer) == T_ENDCMD)
146 msg (SE, _("Cannot specify ALL after specifying a set "
150 dict_get_vars_mutable (dataset_dict (ds), &v, &nv,
155 if (!lex_match (lexer, T_LPAREN))
157 lex_error_expecting (lexer, "`('");
161 if (!parse_variables (lexer, dataset_dict (ds), &v, &nv,
162 PV_APPEND | PV_NO_DUPLICATE))
167 if (!lex_match (lexer, T_RPAREN))
169 lex_error_expecting (lexer, "`)'");
175 if (!ordering.positional)
176 sort (&v[prev_nv], nv - prev_nv, sizeof *v,
177 compare_variables_given_ordering, &ordering);
178 else if (!ordering.forward)
179 reverse_array(&v[prev_nv], nv - prev_nv, sizeof *v);
181 while (lex_token (lexer) != T_SLASH
182 && lex_token (lexer) != T_ENDCMD);
187 else if (lex_match_id (lexer, "RENAME"))
189 if (already_encountered & 2)
191 lex_sbc_only_once (lexer, "RENAME");
194 already_encountered |= 2;
196 lex_match (lexer, T_EQUALS);
199 size_t prev_nv_1 = vm.n_rename;
200 size_t prev_nv_2 = vm.n_rename;
202 if (!lex_match (lexer, T_LPAREN))
204 lex_error_expecting (lexer, "`('");
207 if (!parse_variables (lexer, dataset_dict (ds),
208 &vm.rename_vars, &vm.n_rename,
209 PV_APPEND | PV_NO_DUPLICATE))
211 if (!lex_match (lexer, T_EQUALS))
213 lex_error_expecting (lexer, "`='");
217 if (!parse_DATA_LIST_vars (lexer, dataset_dict (ds),
218 &vm.new_names, &prev_nv_1, PV_APPEND))
220 if (prev_nv_1 != vm.n_rename)
222 msg (SE, _("Differing number of variables in old name list "
223 "(%zu) and in new name list (%zu)."),
224 vm.n_rename - prev_nv_2, prev_nv_1 - prev_nv_2);
225 for (size_t i = 0; i < prev_nv_1; i++)
226 free (vm.new_names[i]);
231 if (!lex_match (lexer, T_RPAREN))
233 lex_error_expecting (lexer, "`)'");
237 while (lex_token (lexer) != T_ENDCMD
238 && lex_token (lexer) != T_SLASH);
240 else if (lex_match_id (lexer, "KEEP"))
242 if (already_encountered & 4)
244 lex_next_error (lexer, -1, -1,
245 _("%s subcommand may be given at most once. "
246 "It may not be given in conjunction with the "
251 already_encountered |= 4;
253 struct variable **keep_vars, **drop_vars;
254 size_t n_keep, n_drop;
255 lex_match (lexer, T_EQUALS);
256 if (!parse_variables (lexer, dataset_dict (ds),
257 &keep_vars, &n_keep, PV_NONE))
260 /* Transform the list of variables to keep into a list of
261 variables to drop. First sort the keep list, then figure
262 out which variables are missing. */
263 sort (keep_vars, n_keep, sizeof *keep_vars,
264 compare_variables_given_ordering,
265 &forward_positional_ordering);
267 struct variable **all_vars;
269 dict_get_vars_mutable (dataset_dict (ds), &all_vars, &n_all, 0);
270 assert (n_all >= n_keep);
272 n_drop = n_all - n_keep;
273 drop_vars = xnmalloc (n_drop, sizeof *keep_vars);
274 if (set_difference (all_vars, n_all,
278 compare_variables_given_ordering,
279 &forward_positional_ordering)
286 vm.drop_vars = drop_vars;
289 else if (lex_match_id (lexer, "DROP"))
291 struct variable **drop_vars;
294 if (already_encountered & 4)
296 lex_next_error (lexer, -1, -1,
297 _("%s subcommand may be given at most once. "
298 "It may not be given in conjunction with the "
304 already_encountered |= 4;
306 int start_ofs = lex_ofs (lexer) - 1;
307 lex_match (lexer, T_EQUALS);
308 if (!parse_variables (lexer, dataset_dict (ds),
309 &drop_vars, &n_drop, PV_NONE))
311 int end_ofs = lex_ofs (lexer) - 1;
312 vm.drop_vars = drop_vars;
315 if (n_drop == dict_get_n_vars (dataset_dict (ds)))
317 lex_ofs_error (lexer, start_ofs, end_ofs,
318 _("%s may not be used to delete all variables "
319 "from the active dataset dictionary. "
320 "Use %s instead."), "MODIFY VARS", "NEW FILE");
324 else if (lex_match_id (lexer, "MAP"))
326 struct dictionary *temp = dict_clone (dataset_dict (ds));
327 int success = rearrange_dict (temp, &vm);
330 /* FIXME: display new dictionary. */
336 lex_error_expecting (lexer, "REORDER", "RENAME", "KEEP",
341 if (lex_token (lexer) == T_ENDCMD)
343 if (lex_token (lexer) != T_SLASH)
345 lex_error_expecting (lexer, "`/'", "`.'");
351 if (already_encountered & (1 | 4))
354 if (!proc_execute (ds))
358 if (!rearrange_dict (dataset_dict (ds), &vm))
361 ret_code = CMD_SUCCESS;
364 free (vm.reorder_vars);
365 free (vm.rename_vars);
367 for (size_t i = 0; i < vm.n_rename; i++)
368 free (vm.new_names[i]);
374 /* Compares A and B according to the settings in ORDERING, returning a
375 strcmp()-type result. */
377 compare_variables_given_ordering (const void *a_, const void *b_,
378 const void *ordering_)
380 struct variable *const *pa = a_;
381 struct variable *const *pb = b_;
382 const struct variable *a = *pa;
383 const struct variable *b = *pb;
384 const struct ordering *ordering = ordering_;
387 if (ordering->positional)
389 size_t a_index = var_get_dict_index (a);
390 size_t b_index = var_get_dict_index (b);
391 result = a_index < b_index ? -1 : a_index > b_index;
394 result = utf8_strcasecmp (var_get_name (a), var_get_name (b));
395 if (!ordering->forward)
400 /* Pairs a variable with a new name. */
403 struct variable *var;
404 const char *new_name;
407 /* A algo_compare_func that compares new_name members in struct var_renaming
408 structures A and B. */
410 compare_var_renaming_by_new_name (const void *a_, const void *b_,
411 const void *aux UNUSED)
413 const struct var_renaming *a = a_;
414 const struct var_renaming *b = b_;
416 return utf8_strcasecmp (a->new_name, b->new_name);
419 /* Returns true if performing VM on dictionary D would not cause problems such
420 as duplicate variable names. Returns false otherwise, and issues an error
423 validate_var_modification (const struct dictionary *d,
424 const struct var_modification *vm)
426 /* Variable reordering can't be a problem, so we don't simulate
427 it. Variable renaming can cause duplicate names, but
428 dropping variables can eliminate them, so we simulate both
431 /* All variables, in index order. */
432 struct variable **all_vars;
434 dict_get_vars_mutable (d, &all_vars, &n_all, 0);
436 /* Drop variables, in index order. */
437 size_t n_drop = vm->n_drop;
438 struct variable **drop_vars = xnmalloc (n_drop, sizeof *drop_vars);
439 memcpy (drop_vars, vm->drop_vars, n_drop * sizeof *drop_vars);
440 sort (drop_vars, n_drop, sizeof *drop_vars,
441 compare_variables_given_ordering, &forward_positional_ordering);
443 /* Keep variables, in index order. */
444 assert (n_all >= n_drop);
445 size_t n_keep = n_all - n_drop;
446 struct variable **keep_vars = xnmalloc (n_keep, sizeof *keep_vars);
447 if (set_difference (all_vars, n_all,
451 compare_variables_given_ordering,
452 &forward_positional_ordering) != n_keep)
455 /* Copy variables into var_renaming array. */
456 struct var_renaming *var_renaming = xnmalloc (n_keep, sizeof *var_renaming);
457 for (size_t i = 0; i < n_keep; i++)
459 var_renaming[i].var = keep_vars[i];
460 var_renaming[i].new_name = var_get_name (keep_vars[i]);
463 /* Rename variables in var_renaming array. */
464 for (size_t i = 0; i < vm->n_rename; i++)
466 struct variable *const *kv;
467 struct var_renaming *vr;
469 /* Get the var_renaming element. */
470 kv = binary_search (keep_vars, n_keep, sizeof *keep_vars,
472 compare_variables_given_ordering,
473 &forward_positional_ordering);
476 vr = var_renaming + (kv - keep_vars);
478 vr->new_name = vm->new_names[i];
481 /* Sort var_renaming array by new names and check for duplicates. */
482 sort (var_renaming, n_keep, sizeof *var_renaming,
483 compare_var_renaming_by_new_name, NULL);
484 bool ok = !adjacent_find_equal (var_renaming, n_keep, sizeof *var_renaming,
485 compare_var_renaming_by_new_name, NULL);
496 /* Reorders, removes, and renames variables in dictionary D according to VM.
497 Returns true if successful, false if there would have been duplicate
498 variable names if the modifications had been carried out. In the latter
499 case, the dictionary is not modified. */
501 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
503 /* Check whether the modifications will cause duplicate names. */
504 if (!validate_var_modification (d, vm))
507 /* Record the old names of variables to rename. After variables are deleted,
508 we can't depend on the variables to still exist, but we can still look
510 char **rename_old_names = xnmalloc (vm->n_rename, sizeof *rename_old_names);
511 for (size_t i = 0; i < vm->n_rename; i++)
512 rename_old_names[i] = xstrdup (var_get_name (vm->rename_vars[i]));
514 /* Reorder and delete variables. */
515 dict_reorder_vars (d, vm->reorder_vars, vm->n_reorder);
516 dict_delete_vars (d, vm->drop_vars, vm->n_drop);
518 /* Compose lists of variables to rename and their new names. */
519 struct variable **rename_vars = xnmalloc (vm->n_rename, sizeof *rename_vars);
520 char **rename_new_names = xnmalloc (vm->n_rename, sizeof *rename_new_names);
522 for (size_t i = 0; i < vm->n_rename; i++)
524 struct variable *var = dict_lookup_var (d, rename_old_names[i]);
528 rename_vars[n_rename] = var;
529 rename_new_names[n_rename] = vm->new_names[i];
534 if (dict_rename_vars (d, rename_vars, rename_new_names, n_rename,
539 for (size_t i = 0; i < vm->n_rename; i++)
540 free (rename_old_names[i]);
541 free (rename_old_names);
543 free (rename_new_names);