MATCH FILES, UPDATE, ADD FILES: Improve error messages.
[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, 2020 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 #include "libpspp/misc.h"
30
31 #include "gl/xalloc.h"
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35
36 /* Commands that read and write system files share a great deal
37    of common syntactic structure for rearranging and dropping
38    variables.  This function parses this syntax and modifies DICT
39    appropriately.  If RELAX is true, then the modified dictionary
40    need not conform to the usual variable name rules.  Returns
41    true on success, false on failure. */
42 bool
43 parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax)
44 {
45   if (lex_match_id (lexer, "MAP"))
46     {
47       /* FIXME. */
48       return true;
49     }
50   else if (lex_match_id (lexer, "DROP"))
51     return parse_dict_drop (lexer, dict);
52   else if (lex_match_id (lexer, "KEEP"))
53     return parse_dict_keep (lexer, dict);
54   else if (lex_match_id (lexer, "RENAME"))
55     return parse_dict_rename (lexer, dict, relax);
56   else
57     {
58       lex_error_expecting (lexer, "MAP", "DROP", "KEEP", "RENAME");
59       return false;
60     }
61 }
62
63 /* Check that OLD_NAME can be renamed to NEW_NAME in DICT.  */
64 static bool
65 check_rename (struct lexer *lexer, int start_ofs, int end_ofs,
66               const struct dictionary *dict,
67               const char *old_name, const char *new_name)
68 {
69   if (dict_lookup_var (dict, new_name) != NULL)
70     {
71       lex_ofs_error (lexer, start_ofs, end_ofs,
72                      _("Cannot rename %s as %s because a variable named %s "
73                        "already exists."),
74                      old_name, new_name, new_name);
75       msg (SN, _("To rename variables with overlapping names, use a single "
76                  "RENAME subcommand such as `/RENAME (A=B)(B=C)(C=A)', or "
77                  "equivalently, `/RENAME (A B C=B C A)'."));
78       return false;
79     }
80   return true;
81 }
82
83 /* Parse a  "VarX TO VarY" sequence where X and Y are integers
84    such that X >= Y.
85    If successfull, returns a string to the prefix Var and sets FIRST
86    to X and LAST to Y.  Returns NULL on failure.
87    The caller must free the return value.  */
88 static char *
89 try_to_sequence (struct lexer *lexer, const struct dictionary *dict,
90                  int *first, int *last)
91 {
92   /* Check that the next 3 tokens are of the correct type.  */
93   if (lex_token (lexer) != T_ID
94       || lex_next_token (lexer, 1) != T_TO
95       || lex_next_token (lexer, 2) != T_ID)
96     return NULL;
97
98   /* Check that the first and last tokens are suitable as
99      variable names.  */
100   const char *s0 = lex_tokcstr (lexer);
101   char *error = id_is_valid__ (s0, dict_get_encoding (dict));
102   if (error)
103     {
104       lex_error (lexer, "%s", error);
105       free (error);
106       return NULL;
107     }
108
109   const char *s1 = lex_next_tokcstr (lexer, 2);
110   error = id_is_valid__ (s1, dict_get_encoding (dict));
111   if (error)
112     {
113       lex_next_error (lexer, 2, 2, "%s", error);
114       free (error);
115       return NULL;
116     }
117
118   int x0 = strcspn (s0, "0123456789");
119   int x1 = strcspn (s1, "0123456789");
120
121   /* The non-digit parts of s0 and s1 must be the same length.  */
122   if (x0 != x1)
123     return NULL;
124
125   /* Both s0 and s1 must have some digits.  */
126   if (strlen (s0) <= x0)
127     return NULL;
128
129   if (strlen (s1) <= x1)
130     return NULL;
131
132   /* The non-digit parts of s0 and s1 must be identical.  */
133   if (0 != strncmp (s0, s1, x0))
134     return NULL;
135
136   /* Both names must end with digits.  */
137   int len_s0_pfx = strspn (s0 + x0, "0123456789");
138   if (len_s0_pfx + x0 != strlen (s0))
139     return NULL;
140
141   int len_s1_pfx = strspn (s1 + x1, "0123456789");
142   if (len_s1_pfx + x1 != strlen (s1))
143     return NULL;
144
145   const char *n_start = s0 + x0;
146   const char *n_stop = s1 + x1;
147
148   /* The first may not be greater than the last.  */
149   if (atoi (n_start) > atoi (n_stop))
150     return NULL;
151
152   char *prefix = xstrndup (s0, x1);
153
154   *first = atoi (n_start);
155   *last = atoi (n_stop);
156
157   return prefix;
158 }
159
160
161 /* Parses and performs the RENAME subcommand of GET, SAVE, and
162    related commands.  If RELAX is true, then the new variable
163    names need  not conform to the normal dictionary rules.
164 */
165 bool
166 parse_dict_rename (struct lexer *lexer, struct dictionary *dict,
167                    bool relax)
168 {
169   struct variable **oldvars = NULL;
170   size_t n_newvars = 0;
171   int group = 0;
172   char **newnames = NULL;
173   lex_match (lexer, T_EQUALS);
174
175   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
176     {
177       size_t n_oldvars = 0;
178       oldvars = NULL;
179       n_newvars = 0;
180       n_oldvars = 0;
181       oldvars = NULL;
182
183       bool paren = lex_match (lexer, T_LPAREN);
184       group++;
185       if (!parse_variables (lexer, dict, &oldvars, &n_oldvars, PV_NO_DUPLICATE))
186         goto fail;
187
188       if (!lex_force_match (lexer, T_EQUALS))
189         goto fail;
190
191       newnames = xmalloc (sizeof *newnames * n_oldvars);
192
193       char *prefix = NULL;
194       int first, last;
195       /* First attempt to parse v1 TO v10 format.  */
196       if ((prefix = try_to_sequence (lexer, dict, &first, &last)))
197         {
198           /* These 3 tokens have already been checked in the
199              try_to_sequence function.  */
200           int start_ofs = lex_ofs (lexer);
201           lex_get (lexer);
202           lex_get (lexer);
203           lex_get (lexer);
204           int end_ofs = lex_ofs (lexer) - 1;
205
206           /* Make sure the new names are suitable.  */
207           for (int i = first; i <= last; ++i)
208             {
209               char *vn = xasprintf ("%s%d", prefix, i);
210
211               if (!check_rename (lexer, start_ofs, end_ofs,
212                                  dict, var_get_name (oldvars[n_newvars]), vn))
213                 {
214                   free (vn);
215                   free (prefix);
216                   goto fail;
217                 }
218
219               newnames[i - first] = vn;
220               n_newvars++;
221             }
222         }
223       else
224       while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING)
225         {
226           if (n_newvars >= n_oldvars)
227             break;
228           const char *new_name = lex_tokcstr (lexer);
229           if (!relax)
230             {
231               char *error = id_is_plausible__ (new_name);
232               if (error)
233                 {
234                   lex_error (lexer, "%s", error);
235                   free (error);
236                   goto fail;
237                 }
238             }
239
240           int ofs = lex_ofs (lexer);
241           if (!check_rename (lexer, ofs, ofs,
242                              dict, var_get_name (oldvars[n_newvars]), new_name))
243             goto fail;
244           newnames[n_newvars] = xstrdup (new_name);
245           lex_get (lexer);
246           n_newvars++;
247         }
248       free (prefix);
249
250       if (n_newvars != n_oldvars)
251         {
252           msg (SE, _("Number of variables on left side of `=' (%zu) does not "
253                      "match number of variables on right side (%zu), in "
254                      "parenthesized group %d of RENAME subcommand."),
255                n_oldvars, n_newvars, group);
256           goto fail;
257         }
258
259       if (paren)
260         if (!lex_force_match (lexer, T_RPAREN))
261           goto fail;
262
263       char *errname = 0;
264       if (!dict_rename_vars (dict, oldvars, newnames, n_newvars, &errname))
265         {
266           msg (SE,
267                _("Requested renaming duplicates variable name %s."),
268                errname);
269           goto fail;
270         }
271       free (oldvars);
272       for (int i = 0; i < n_newvars; ++i)
273         free (newnames[i]);
274       free (newnames);
275       newnames = NULL;
276     }
277
278   return true;
279
280  fail:
281   free (oldvars);
282   for (int i = 0; i < n_newvars; ++i)
283     free (newnames[i]);
284   free (newnames);
285   newnames = NULL;
286   return false;
287 }
288
289 /* Parses and performs the DROP subcommand of GET, SAVE, and
290    related commands.
291    Returns true if successful, false on failure.*/
292 bool
293 parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
294 {
295   int start_ofs = lex_ofs (lexer) - 1;
296   lex_match (lexer, T_EQUALS);
297
298   struct variable **v;
299   size_t nv;
300   if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
301     return false;
302   dict_delete_vars (dict, v, nv);
303   free (v);
304
305   if (dict_get_n_vars (dict) == 0)
306     {
307       lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
308                      _("Cannot DROP all variables from dictionary."));
309       return false;
310     }
311   return true;
312 }
313
314 /* Parses and performs the KEEP subcommand of GET, SAVE, and
315    related commands.
316    Returns true if successful, false on failure.*/
317 bool
318 parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
319 {
320   struct variable **v;
321   size_t nv;
322   size_t i;
323
324   lex_match (lexer, T_EQUALS);
325   if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
326     return false;
327
328   /* Move the specified variables to the beginning. */
329   dict_reorder_vars (dict, v, nv);
330
331   /* Delete the remaining variables. */
332   if (dict_get_n_vars (dict) == nv)
333     {
334       free (v);
335       return true;
336     }
337
338   v = xnrealloc (v, dict_get_n_vars (dict) - nv, sizeof *v);
339   for (i = nv; i < dict_get_n_vars (dict); i++)
340     v[i - nv] = dict_get_var (dict, i);
341   dict_delete_vars (dict, v, dict_get_n_vars (dict) - nv);
342   free (v);
343
344   return true;
345 }