/RENAME subcommand: Allow quoted strings in destination variables.
[pspp] / src / language / data-io / trim.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2010, 2011,
3    2019 Free Software Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 #include "language/data-io/trim.h"
21
22 #include <stdlib.h>
23
24 #include "data/dictionary.h"
25 #include "data/variable.h"
26 #include "language/lexer/lexer.h"
27 #include "language/lexer/variable-parser.h"
28 #include "libpspp/message.h"
29
30 #include "gl/xalloc.h"
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34
35 /* Commands that read and write system files share a great deal
36    of common syntactic structure for rearranging and dropping
37    variables.  This function parses this syntax and modifies DICT
38    appropriately.  If RELAX is true, then the modified dictionary
39    need not conform to the usual variable name rules.  Returns
40    true on success, false on failure. */
41 bool
42 parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax)
43 {
44   if (lex_match_id (lexer, "MAP"))
45     {
46       /* FIXME. */
47       return true;
48     }
49   else if (lex_match_id (lexer, "DROP"))
50     return parse_dict_drop (lexer, dict);
51   else if (lex_match_id (lexer, "KEEP"))
52     return parse_dict_keep (lexer, dict);
53   else if (lex_match_id (lexer, "RENAME"))
54     return parse_dict_rename (lexer, dict, relax);
55   else
56     {
57       lex_error (lexer, _("expecting a valid subcommand"));
58       return false;
59     }
60 }
61
62 /* Parses and performs the RENAME subcommand of GET, SAVE, and
63    related commands.  If RELAX is true, then the new variable
64    names need  not conform to the normal dictionary rules.
65 */
66 bool
67 parse_dict_rename (struct lexer *lexer, struct dictionary *dict,
68                    bool relax)
69 {
70   struct variable **oldvars = NULL;
71   size_t n_newvars = 0;
72   int group = 0;
73   char **newnames = NULL;
74   lex_match (lexer, T_EQUALS);
75
76   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
77     {
78       size_t n_oldvars = 0;
79       oldvars = NULL;
80       n_newvars = 0;
81       n_oldvars = 0;
82       oldvars = NULL;
83
84       bool paren = lex_match (lexer, T_LPAREN);
85       group++;
86       if (!parse_variables (lexer, dict, &oldvars, &n_oldvars, PV_NO_DUPLICATE))
87         goto fail;
88
89       if (!lex_force_match (lexer, T_EQUALS))
90         goto fail;
91
92       newnames = xmalloc (sizeof *newnames * n_oldvars);
93       while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING)
94         {
95           if (n_newvars >= n_oldvars)
96             break;
97           const char *new_name = lex_tokcstr (lexer);
98           if (!relax && ! id_is_plausible (new_name, true))
99             goto fail;
100
101           if (dict_lookup_var (dict, new_name) != NULL)
102             {
103               msg (SE, _("Cannot rename %s as %s because there already exists "
104                          "a variable named %s.  To rename variables with "
105                          "overlapping names, use a single RENAME subcommand "
106                          "such as `/RENAME (A=B)(B=C)(C=A)', or equivalently, "
107                          "`/RENAME (A B C=B C A)'."),
108                    var_get_name (oldvars[n_newvars]), new_name, new_name);
109               goto fail;
110             }
111           newnames[n_newvars] = strdup (new_name);
112           lex_get (lexer);
113           n_newvars++;
114         }
115       if (n_newvars != n_oldvars)
116         {
117           msg (SE, _("Number of variables on left side of `=' (%zu) does not "
118                      "match number of variables on right side (%zu), in "
119                      "parenthesized group %d of RENAME subcommand."),
120                n_newvars, n_oldvars, group);
121           goto fail;
122         }
123
124       if (paren)
125         if (!lex_force_match (lexer, T_RPAREN))
126           goto fail;
127
128       char *errname = 0;
129       if (!dict_rename_vars (dict, oldvars, newnames, n_newvars, &errname))
130         {
131           msg (SE,
132                _("Requested renaming duplicates variable name %s."),
133                errname);
134           goto fail;
135         }
136       free (oldvars);
137       for (int i = 0; i < n_newvars; ++i)
138         free (newnames[i]);
139       free (newnames);
140       newnames = NULL;
141     }
142
143   return true;
144
145  fail:
146   free (oldvars);
147   for (int i = 0; i < n_newvars; ++i)
148     free (newnames[i]);
149   free (newnames);
150   newnames = NULL;
151   return false;
152 }
153
154 /* Parses and performs the DROP subcommand of GET, SAVE, and
155    related commands.
156    Returns true if successful, false on failure.*/
157 bool
158 parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
159 {
160   struct variable **v;
161   size_t nv;
162
163   lex_match (lexer, T_EQUALS);
164   if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
165     return false;
166   dict_delete_vars (dict, v, nv);
167   free (v);
168
169   if (dict_get_var_cnt (dict) == 0)
170     {
171       msg (SE, _("Cannot DROP all variables from dictionary."));
172       return false;
173     }
174   return true;
175 }
176
177 /* Parses and performs the KEEP subcommand of GET, SAVE, and
178    related commands.
179    Returns true if successful, false on failure.*/
180 bool
181 parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
182 {
183   struct variable **v;
184   size_t nv;
185   size_t i;
186
187   lex_match (lexer, T_EQUALS);
188   if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
189     return false;
190
191   /* Move the specified variables to the beginning. */
192   dict_reorder_vars (dict, v, nv);
193
194   /* Delete the remaining variables. */
195   v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v);
196   for (i = nv; i < dict_get_var_cnt (dict); i++)
197     v[i - nv] = dict_get_var (dict, i);
198   dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv);
199   free (v);
200
201   return true;
202 }