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 (lexer, _("expecting a valid subcommand"));
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 if (!id_is_valid (s0, dict_get_encoding (dict), true))
101 const char *s1 = lex_next_tokcstr (lexer, 2);
102 if (!id_is_valid (s1, dict_get_encoding (dict), true))
105 int x0 = strcspn (s0, "0123456789");
106 int x1 = strcspn (s1, "0123456789");
108 /* The non-digit parts of s0 and s1 must be the same length. */
112 /* Both s0 and s1 must have some digits. */
113 if (strlen (s0) <= x0)
116 if (strlen (s1) <= x1)
119 /* The non-digit parts of s0 and s1 must be identical. */
120 if (0 != strncmp (s0, s1, x0))
123 /* Both names must end with digits. */
124 int len_s0_pfx = strspn (s0 + x0, "0123456789");
125 if (len_s0_pfx + x0 != strlen (s0))
128 int len_s1_pfx = strspn (s1 + x1, "0123456789");
129 if (len_s1_pfx + x1 != strlen (s1))
132 const char *n_start = s0 + x0;
133 const char *n_stop = s1 + x1;
135 /* The first may not be greater than the last. */
136 if (atoi (n_start) > atoi (n_stop))
139 char *prefix = xstrndup (s0, x1);
141 *first = atoi (n_start);
142 *last = atoi (n_stop);
148 /* Parses and performs the RENAME subcommand of GET, SAVE, and
149 related commands. If RELAX is true, then the new variable
150 names need not conform to the normal dictionary rules.
153 parse_dict_rename (struct lexer *lexer, struct dictionary *dict,
156 struct variable **oldvars = NULL;
157 size_t n_newvars = 0;
159 char **newnames = NULL;
160 lex_match (lexer, T_EQUALS);
162 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
164 size_t n_oldvars = 0;
170 bool paren = lex_match (lexer, T_LPAREN);
172 if (!parse_variables (lexer, dict, &oldvars, &n_oldvars, PV_NO_DUPLICATE))
175 if (!lex_force_match (lexer, T_EQUALS))
178 newnames = xmalloc (sizeof *newnames * n_oldvars);
182 /* First attempt to parse v1 TO v10 format. */
183 if ((prefix = try_to_sequence (lexer, dict, &first, &last)))
185 /* These 3 tokens have already been checked in the
186 try_to_sequence function. */
191 /* Make sure the new names are suitable. */
192 for (int i = first; i <= last; ++i)
194 int sz = strlen (prefix) + intlog10 (last) + 1;
195 char *vn = malloc (sz);
196 snprintf (vn, sz, "%s%d", prefix, i);
198 if (!check_rename (dict, var_get_name (oldvars[n_newvars]), vn))
204 newnames[i - first] = vn;
209 while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING)
211 if (n_newvars >= n_oldvars)
213 const char *new_name = lex_tokcstr (lexer);
214 if (!relax && ! id_is_plausible (new_name, true))
217 if (!check_rename (dict, var_get_name (oldvars[n_newvars]), new_name))
219 newnames[n_newvars] = strdup (new_name);
225 if (n_newvars != n_oldvars)
227 msg (SE, _("Number of variables on left side of `=' (%zu) does not "
228 "match number of variables on right side (%zu), in "
229 "parenthesized group %d of RENAME subcommand."),
230 n_oldvars, n_newvars, group);
235 if (!lex_force_match (lexer, T_RPAREN))
239 if (!dict_rename_vars (dict, oldvars, newnames, n_newvars, &errname))
242 _("Requested renaming duplicates variable name %s."),
247 for (int i = 0; i < n_newvars; ++i)
257 for (int i = 0; i < n_newvars; ++i)
264 /* Parses and performs the DROP subcommand of GET, SAVE, and
266 Returns true if successful, false on failure.*/
268 parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
273 lex_match (lexer, T_EQUALS);
274 if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
276 dict_delete_vars (dict, v, nv);
279 if (dict_get_var_cnt (dict) == 0)
281 msg (SE, _("Cannot DROP all variables from dictionary."));
287 /* Parses and performs the KEEP subcommand of GET, SAVE, and
289 Returns true if successful, false on failure.*/
291 parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
297 lex_match (lexer, T_EQUALS);
298 if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
301 /* Move the specified variables to the beginning. */
302 dict_reorder_vars (dict, v, nv);
304 /* Delete the remaining variables. */
305 if (dict_get_var_cnt (dict) == nv)
311 v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v);
312 for (i = nv; i < dict_get_var_cnt (dict); i++)
313 v[i - nv] = dict_get_var (dict, i);
314 dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv);