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.
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.
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.
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/>. */
20 #include "language/data-io/trim.h"
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"
31 #include "gl/xalloc.h"
34 #define _(msgid) gettext (msgid)
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. */
43 parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax)
45 if (lex_match_id (lexer, "MAP"))
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);
58 lex_error_expecting (lexer, "MAP", "DROP", "KEEP", "RENAME");
63 /* Check that OLD_NAME can be renamed to NEW_NAME in DICT. */
65 check_rename (const struct dictionary *dict, const char *old_name, const char *new_name)
67 if (dict_lookup_var (dict, new_name) != NULL)
69 msg (SE, _("Cannot rename %s as %s because there already exists "
70 "a variable named %s. To rename variables with "
71 "overlapping names, use a single RENAME subcommand "
72 "such as `/RENAME (A=B)(B=C)(C=A)', or equivalently, "
73 "`/RENAME (A B C=B C A)'."),
74 old_name, new_name, new_name);
80 /* Parse a "VarX TO VarY" sequence where X and Y are integers
82 If successfull, returns a string to the prefix Var and sets FIRST
83 to X and LAST to Y. Returns NULL on failure.
84 The caller must free the return value. */
86 try_to_sequence (struct lexer *lexer, const struct dictionary *dict,
87 int *first, int *last)
89 /* Check that the next 3 tokens are of the correct type. */
90 if (lex_token (lexer) != T_ID
91 || lex_next_token (lexer, 1) != T_TO
92 || lex_next_token (lexer, 2) != T_ID)
95 /* Check that the first and last tokens are suitable as
97 const char *s0 = lex_tokcstr (lexer);
98 char *error = id_is_valid__ (s0, dict_get_encoding (dict));
101 lex_error (lexer, "%s", error);
106 const char *s1 = lex_next_tokcstr (lexer, 2);
107 error = id_is_valid__ (s1, dict_get_encoding (dict));
110 lex_next_error (lexer, 2, 2, "%s", error);
115 int x0 = strcspn (s0, "0123456789");
116 int x1 = strcspn (s1, "0123456789");
118 /* The non-digit parts of s0 and s1 must be the same length. */
122 /* Both s0 and s1 must have some digits. */
123 if (strlen (s0) <= x0)
126 if (strlen (s1) <= x1)
129 /* The non-digit parts of s0 and s1 must be identical. */
130 if (0 != strncmp (s0, s1, x0))
133 /* Both names must end with digits. */
134 int len_s0_pfx = strspn (s0 + x0, "0123456789");
135 if (len_s0_pfx + x0 != strlen (s0))
138 int len_s1_pfx = strspn (s1 + x1, "0123456789");
139 if (len_s1_pfx + x1 != strlen (s1))
142 const char *n_start = s0 + x0;
143 const char *n_stop = s1 + x1;
145 /* The first may not be greater than the last. */
146 if (atoi (n_start) > atoi (n_stop))
149 char *prefix = xstrndup (s0, x1);
151 *first = atoi (n_start);
152 *last = atoi (n_stop);
158 /* Parses and performs the RENAME subcommand of GET, SAVE, and
159 related commands. If RELAX is true, then the new variable
160 names need not conform to the normal dictionary rules.
163 parse_dict_rename (struct lexer *lexer, struct dictionary *dict,
166 struct variable **oldvars = NULL;
167 size_t n_newvars = 0;
169 char **newnames = NULL;
170 lex_match (lexer, T_EQUALS);
172 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
174 size_t n_oldvars = 0;
180 bool paren = lex_match (lexer, T_LPAREN);
182 if (!parse_variables (lexer, dict, &oldvars, &n_oldvars, PV_NO_DUPLICATE))
185 if (!lex_force_match (lexer, T_EQUALS))
188 newnames = xmalloc (sizeof *newnames * n_oldvars);
192 /* First attempt to parse v1 TO v10 format. */
193 if ((prefix = try_to_sequence (lexer, dict, &first, &last)))
195 /* These 3 tokens have already been checked in the
196 try_to_sequence function. */
201 /* Make sure the new names are suitable. */
202 for (int i = first; i <= last; ++i)
204 int sz = strlen (prefix) + intlog10 (last) + 1;
205 char *vn = malloc (sz);
206 snprintf (vn, sz, "%s%d", prefix, i);
208 if (!check_rename (dict, var_get_name (oldvars[n_newvars]), vn))
214 newnames[i - first] = vn;
219 while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING)
221 if (n_newvars >= n_oldvars)
223 const char *new_name = lex_tokcstr (lexer);
226 char *error = id_is_plausible__ (new_name);
229 lex_error (lexer, "%s", error);
235 if (!check_rename (dict, var_get_name (oldvars[n_newvars]), new_name))
237 newnames[n_newvars] = strdup (new_name);
243 if (n_newvars != n_oldvars)
245 msg (SE, _("Number of variables on left side of `=' (%zu) does not "
246 "match number of variables on right side (%zu), in "
247 "parenthesized group %d of RENAME subcommand."),
248 n_oldvars, n_newvars, group);
253 if (!lex_force_match (lexer, T_RPAREN))
257 if (!dict_rename_vars (dict, oldvars, newnames, n_newvars, &errname))
260 _("Requested renaming duplicates variable name %s."),
265 for (int i = 0; i < n_newvars; ++i)
275 for (int i = 0; i < n_newvars; ++i)
282 /* Parses and performs the DROP subcommand of GET, SAVE, and
284 Returns true if successful, false on failure.*/
286 parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
291 lex_match (lexer, T_EQUALS);
292 if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
294 dict_delete_vars (dict, v, nv);
297 if (dict_get_n_vars (dict) == 0)
299 msg (SE, _("Cannot DROP all variables from dictionary."));
305 /* Parses and performs the KEEP subcommand of GET, SAVE, and
307 Returns true if successful, false on failure.*/
309 parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
315 lex_match (lexer, T_EQUALS);
316 if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
319 /* Move the specified variables to the beginning. */
320 dict_reorder_vars (dict, v, nv);
322 /* Delete the remaining variables. */
323 if (dict_get_n_vars (dict) == nv)
329 v = xnrealloc (v, dict_get_n_vars (dict) - nv, sizeof *v);
330 for (i = nv; i < dict_get_n_vars (dict); i++)
331 v[i - nv] = dict_get_var (dict, i);
332 dict_delete_vars (dict, v, dict_get_n_vars (dict) - nv);