DELETE VARIABLES: Improve error messages.
[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       lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
51                      _("%s may not be used to delete all variables "
52                        "from the active dataset dictionary.  "
53                        "Use %s instead."), "DELETE VARIABLES", "NEW FILE");
54       goto error;
55     }
56
57   ok = casereader_destroy (proc_open_filtering (ds, false));
58   ok = proc_commit (ds) && ok;
59   if (!ok)
60     goto error;
61
62   dict_delete_vars (dataset_dict (ds), vars, n_vars);
63
64   /* XXX A bunch of bugs conspire to make executing transformations again here
65      necessary, even though it shouldn't be.
66
67      Consider the following (which is included in delete-variables.at):
68
69         DATA LIST NOTABLE /s1 TO s2 1-2(A).
70         BEGIN DATA
71         12
72         END DATA.
73         DELETE VARIABLES s1.
74         NUMERIC n1.
75         LIST.
76
77      The DATA LIST gives us a caseproto with widths 1,1.  DELETE VARIABLES
78      deletes the first variable so we now have -1,1.  This already is
79      technically a problem because proc_casereader_read() calls
80      case_unshare_and_resize() from the former to the latter caseproto, and
81      these caseprotos are not conformable (which is a requirement for
82      case_resize()).  It doesn't cause an assert by default because
83      case_resize() uses expensive_assert() to check for it though.  However, in
84      practice we don't see a problem yet because case_resize() only does work
85      if the number of widths in the source and dest caseproto are different.
86
87      Executing NUMERIC adds a third variable, though, so we have -1,1,0.  This
88      makes caseproto_resize() notice that there are fewer strings in the new
89      caseproto.  Therefore it destroys the second one (s2).  It should destroy
90      the first one (s1), but if the caseprotos were really conformable then it
91      would have destroyed the right one.  This mistake eventually causes a bad
92      memory reference.
93
94      Executing transformations a second time after DELETE VARIABLES, like we do
95      below, works around the problem because we can never run into a situation
96      where we've got both new variables (triggering a resize) and deleted
97      variables (triggering the bad free).
98
99      We should fix this in a better way.  Doing it cleanly seems hard.  This
100      seems to work for now. */
101   ok = casereader_destroy (proc_open_filtering (ds, false));
102   ok = proc_commit (ds) && ok;
103   if (!ok)
104     goto error;
105
106   free (vars);
107
108   return CMD_SUCCESS;
109
110  error:
111   free (vars);
112   return CMD_CASCADING_FAILURE;
113 }