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 (struct lexer *lexer, int start_ofs, int end_ofs,
66 const struct dictionary *dict,
67 const char *old_name, const char *new_name)
69 if (dict_lookup_var (dict, new_name) != NULL)
71 lex_ofs_error (lexer, start_ofs, end_ofs,
72 _("Cannot rename %s as %s because a variable named %s "
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)'."));
83 /* Parse a "VarX TO VarY" sequence where X and Y are integers
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. */
89 try_to_sequence (struct lexer *lexer, const struct dictionary *dict,
90 int *first, int *last)
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)
98 /* Check that the first and last tokens are suitable as
100 const char *s0 = lex_tokcstr (lexer);
101 char *error = id_is_valid__ (s0, dict_get_encoding (dict));
104 lex_error (lexer, "%s", error);
109 const char *s1 = lex_next_tokcstr (lexer, 2);
110 error = id_is_valid__ (s1, dict_get_encoding (dict));
113 lex_next_error (lexer, 2, 2, "%s", error);
118 int x0 = strcspn (s0, "0123456789");
119 int x1 = strcspn (s1, "0123456789");
121 /* The non-digit parts of s0 and s1 must be the same length. */
125 /* Both s0 and s1 must have some digits. */
126 if (strlen (s0) <= x0)
129 if (strlen (s1) <= x1)
132 /* The non-digit parts of s0 and s1 must be identical. */
133 if (0 != strncmp (s0, s1, x0))
136 /* Both names must end with digits. */
137 int len_s0_pfx = strspn (s0 + x0, "0123456789");
138 if (len_s0_pfx + x0 != strlen (s0))
141 int len_s1_pfx = strspn (s1 + x1, "0123456789");
142 if (len_s1_pfx + x1 != strlen (s1))
145 const char *n_start = s0 + x0;
146 const char *n_stop = s1 + x1;
148 /* The first may not be greater than the last. */
149 if (atoi (n_start) > atoi (n_stop))
152 char *prefix = xstrndup (s0, x1);
154 *first = atoi (n_start);
155 *last = atoi (n_stop);
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.
166 parse_dict_rename (struct lexer *lexer, struct dictionary *dict,
169 struct variable **oldvars = NULL;
170 size_t n_newvars = 0;
172 char **newnames = NULL;
173 lex_match (lexer, T_EQUALS);
175 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
177 size_t n_oldvars = 0;
183 bool paren = lex_match (lexer, T_LPAREN);
185 if (!parse_variables (lexer, dict, &oldvars, &n_oldvars, PV_NO_DUPLICATE))
188 if (!lex_force_match (lexer, T_EQUALS))
191 newnames = xmalloc (sizeof *newnames * n_oldvars);
195 /* First attempt to parse v1 TO v10 format. */
196 if ((prefix = try_to_sequence (lexer, dict, &first, &last)))
198 /* These 3 tokens have already been checked in the
199 try_to_sequence function. */
200 int start_ofs = lex_ofs (lexer);
204 int end_ofs = lex_ofs (lexer) - 1;
206 /* Make sure the new names are suitable. */
207 for (int i = first; i <= last; ++i)
209 char *vn = xasprintf ("%s%d", prefix, i);
211 if (!check_rename (lexer, start_ofs, end_ofs,
212 dict, var_get_name (oldvars[n_newvars]), vn))
219 newnames[i - first] = vn;
224 while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING)
226 if (n_newvars >= n_oldvars)
228 const char *new_name = lex_tokcstr (lexer);
231 char *error = id_is_plausible__ (new_name);
234 lex_error (lexer, "%s", error);
240 int ofs = lex_ofs (lexer);
241 if (!check_rename (lexer, ofs, ofs,
242 dict, var_get_name (oldvars[n_newvars]), new_name))
244 newnames[n_newvars] = xstrdup (new_name);
250 if (n_newvars != n_oldvars)
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);
260 if (!lex_force_match (lexer, T_RPAREN))
264 if (!dict_rename_vars (dict, oldvars, newnames, n_newvars, &errname))
267 _("Requested renaming duplicates variable name %s."),
272 for (int i = 0; i < n_newvars; ++i)
282 for (int i = 0; i < n_newvars; ++i)
289 /* Parses and performs the DROP subcommand of GET, SAVE, and
291 Returns true if successful, false on failure.*/
293 parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
295 int start_ofs = lex_ofs (lexer) - 1;
296 lex_match (lexer, T_EQUALS);
300 if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
302 dict_delete_vars (dict, v, nv);
305 if (dict_get_n_vars (dict) == 0)
307 lex_ofs_error (lexer, start_ofs, lex_ofs (lexer) - 1,
308 _("Cannot DROP all variables from dictionary."));
314 /* Parses and performs the KEEP subcommand of GET, SAVE, and
316 Returns true if successful, false on failure.*/
318 parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
324 lex_match (lexer, T_EQUALS);
325 if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
328 /* Move the specified variables to the beginning. */
329 dict_reorder_vars (dict, v, nv);
331 /* Delete the remaining variables. */
332 if (dict_get_n_vars (dict) == nv)
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);