1063b1652e6abe0b549162f5ad40f31b1f28d5e4
[pspp] / src / language / dictionary / delete-variables.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2006, 2007, 2010, 2011, 2013 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20
21 #include "data/casereader.h"
22 #include "data/dataset.h"
23 #include "data/dictionary.h"
24 #include "language/command.h"
25 #include "language/lexer/lexer.h"
26 #include "language/lexer/variable-parser.h"
27 #include "libpspp/message.h"
28
29 #include "gettext.h"
30 #define _(msgid) gettext (msgid)
31
32 /* Performs DELETE VARIABLES command. */
33 int
34 cmd_delete_variables (struct lexer *lexer, struct dataset *ds)
35 {
36   struct variable **vars;
37   size_t n_vars;
38   bool ok;
39
40   if (proc_make_temporary_transformations_permanent (ds))
41     lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
42                    _("%s may not be used after %s.  "
43                      "Temporary transformations will be made permanent."),
44                    "DELETE VARIABLES", "TEMPORARY");
45
46   if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars, PV_NONE))
47     goto error;
48   if (n_vars == dict_get_n_vars (dataset_dict (ds)))
49     {
50       msg (SE, _("%s may not be used to delete all variables "
51                  "from the active dataset dictionary.  "
52                  "Use %s instead."), "DELETE VARIABLES", "NEW FILE");
53       goto error;
54     }
55
56   ok = casereader_destroy (proc_open_filtering (ds, false));
57   ok = proc_commit (ds) && ok;
58   if (!ok)
59     goto error;
60
61   dict_delete_vars (dataset_dict (ds), vars, n_vars);
62
63   /* XXX A bunch of bugs conspire to make executing transformations again here
64      necessary, even though it shouldn't be.
65
66      Consider the following (which is included in delete-variables.at):
67
68         DATA LIST NOTABLE /s1 TO s2 1-2(A).
69         BEGIN DATA
70         12
71         END DATA.
72         DELETE VARIABLES s1.
73         NUMERIC n1.
74         LIST.
75
76      The DATA LIST gives us a caseproto with widths 1,1.  DELETE VARIABLES
77      deletes the first variable so we now have -1,1.  This already is
78      technically a problem because proc_casereader_read() calls
79      case_unshare_and_resize() from the former to the latter caseproto, and
80      these caseprotos are not conformable (which is a requirement for
81      case_resize()).  It doesn't cause an assert by default because
82      case_resize() uses expensive_assert() to check for it though.  However, in
83      practice we don't see a problem yet because case_resize() only does work
84      if the number of widths in the source and dest caseproto are different.
85
86      Executing NUMERIC adds a third variable, though, so we have -1,1,0.  This
87      makes caseproto_resize() notice that there are fewer strings in the new
88      caseproto.  Therefore it destroys the second one (s2).  It should destroy
89      the first one (s1), but if the caseprotos were really conformable then it
90      would have destroyed the right one.  This mistake eventually causes a bad
91      memory reference.
92
93      Executing transformations a second time after DELETE VARIABLES, like we do
94      below, works around the problem because we can never run into a situation
95      where we've got both new variables (triggering a resize) and deleted
96      variables (triggering the bad free).
97
98      We should fix this in a better way.  Doing it cleanly seems hard.  This
99      seems to work for now. */
100   ok = casereader_destroy (proc_open_filtering (ds, false));
101   ok = proc_commit (ds) && ok;
102   if (!ok)
103     goto error;
104
105   free (vars);
106
107   return CMD_SUCCESS;
108
109  error:
110   free (vars);
111   return CMD_CASCADING_FAILURE;
112 }