X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fdata-io%2Ftrim.c;h=4616b431d22cb1303810e8679899514d0589f5bb;hb=f5711a3a6d3f28e53513269e774bfa1e8274c1c5;hp=657d23aa0bfa464664f927a3887795debfc39700;hpb=53d339111a9f51561cfccc65764874cdf54e501a;p=pspp diff --git a/src/language/data-io/trim.c b/src/language/data-io/trim.c index 657d23aa0b..4616b431d2 100644 --- a/src/language/data-io/trim.c +++ b/src/language/data-io/trim.c @@ -1,6 +1,6 @@ /* PSPP - a program for statistical analysis. Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2010, 2011, - 2019 Free Software Foundation, Inc. + 2019, 2020 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include "language/lexer/lexer.h" #include "language/lexer/variable-parser.h" #include "libpspp/message.h" +#include "libpspp/misc.h" #include "gl/xalloc.h" @@ -59,6 +60,91 @@ parse_dict_trim (struct lexer *lexer, struct dictionary *dict, bool relax) } } +/* Check that OLD_NAME can be renamed to NEW_NAME in DICT. */ +static bool +check_rename (const struct dictionary *dict, const char *old_name, const char *new_name) +{ + if (dict_lookup_var (dict, new_name) != NULL) + { + msg (SE, _("Cannot rename %s as %s because there already exists " + "a variable named %s. To rename variables with " + "overlapping names, use a single RENAME subcommand " + "such as `/RENAME (A=B)(B=C)(C=A)', or equivalently, " + "`/RENAME (A B C=B C A)'."), + old_name, new_name, new_name); + return false; + } + return true; +} + +/* Parse a "VarX TO VarY" sequence where X and Y are integers + such that X >= Y. + If successfull, returns a string to the prefix Var and sets FIRST + to X and LAST to Y. Returns NULL on failure. + The caller must free the return value. */ +static char * +try_to_sequence (struct lexer *lexer, const struct dictionary *dict, + int *first, int *last) +{ + /* Check that the next 3 tokens are of the correct type. */ + if (lex_token (lexer) != T_ID + || lex_next_token (lexer, 1) != T_TO + || lex_next_token (lexer, 2) != T_ID) + return NULL; + + /* Check that the first and last tokens are suitable as + variable names. */ + const char *s0 = lex_tokcstr (lexer); + if (!id_is_valid (s0, dict_get_encoding (dict), true)) + return NULL; + + const char *s1 = lex_next_tokcstr (lexer, 2); + if (!id_is_valid (s1, dict_get_encoding (dict), true)) + return NULL; + + int x0 = strcspn (s0, "0123456789"); + int x1 = strcspn (s1, "0123456789"); + + /* The non-digit parts of s0 and s1 must be the same length. */ + if (x0 != x1) + return NULL; + + /* Both s0 and s1 must have some digits. */ + if (strlen (s0) <= x0) + return NULL; + + if (strlen (s1) <= x1) + return NULL; + + /* The non-digit parts of s0 and s1 must be identical. */ + if (0 != strncmp (s0, s1, x0)) + return NULL; + + /* Both names must end with digits. */ + int len_s0_pfx = strspn (s0 + x0, "0123456789"); + if (len_s0_pfx + x0 != strlen (s0)) + return NULL; + + int len_s1_pfx = strspn (s1 + x1, "0123456789"); + if (len_s1_pfx + x1 != strlen (s1)) + return NULL; + + const char *n_start = s0 + x0; + const char *n_stop = s1 + x1; + + /* The first may not be greater than the last. */ + if (atoi (n_start) > atoi (n_stop)) + return NULL; + + char *prefix = xstrndup (s0, x1); + + *first = atoi (n_start); + *last = atoi (n_stop); + + return prefix; +} + + /* Parses and performs the RENAME subcommand of GET, SAVE, and related commands. If RELAX is true, then the new variable names need not conform to the normal dictionary rules. @@ -90,34 +176,58 @@ parse_dict_rename (struct lexer *lexer, struct dictionary *dict, goto fail; newnames = xmalloc (sizeof *newnames * n_oldvars); + + char *prefix = NULL; + int first, last; + /* First attempt to parse v1 TO v10 format. */ + if ((prefix = try_to_sequence (lexer, dict, &first, &last))) + { + /* These 3 tokens have already been checked in the + try_to_sequence function. */ + lex_get (lexer); + lex_get (lexer); + lex_get (lexer); + + /* Make sure the new names are suitable. */ + for (int i = first; i <= last; ++i) + { + int sz = strlen (prefix) + intlog10 (last) + 1; + char *vn = malloc (sz); + snprintf (vn, sz, "%s%d", prefix, i); + + if (!check_rename (dict, var_get_name (oldvars[n_newvars]), vn)) + { + free (prefix); + goto fail; + } + + newnames[i - first] = vn; + n_newvars++; + } + } + else while (lex_token (lexer) == T_ID || lex_token (lexer) == T_STRING) - { - if (n_newvars >= n_oldvars) - break; - const char *new_name = lex_tokcstr (lexer); - if (!relax && ! id_is_plausible (new_name, true)) - goto fail; - - if (dict_lookup_var (dict, new_name) != NULL) - { - msg (SE, _("Cannot rename %s as %s because there already exists " - "a variable named %s. To rename variables with " - "overlapping names, use a single RENAME subcommand " - "such as `/RENAME (A=B)(B=C)(C=A)', or equivalently, " - "`/RENAME (A B C=B C A)'."), - var_get_name (oldvars[n_newvars]), new_name, new_name); - goto fail; - } - newnames[n_newvars] = strdup (new_name); - lex_get (lexer); - n_newvars++; - } + { + if (n_newvars >= n_oldvars) + break; + const char *new_name = lex_tokcstr (lexer); + if (!relax && ! id_is_plausible (new_name, true)) + goto fail; + + if (!check_rename (dict, var_get_name (oldvars[n_newvars]), new_name)) + goto fail; + newnames[n_newvars] = strdup (new_name); + lex_get (lexer); + n_newvars++; + } + free (prefix); + if (n_newvars != n_oldvars) { msg (SE, _("Number of variables on left side of `=' (%zu) does not " "match number of variables on right side (%zu), in " "parenthesized group %d of RENAME subcommand."), - n_newvars, n_oldvars, group); + n_oldvars, n_newvars, group); goto fail; } @@ -166,7 +276,7 @@ parse_dict_drop (struct lexer *lexer, struct dictionary *dict) dict_delete_vars (dict, v, nv); free (v); - if (dict_get_var_cnt (dict) == 0) + if (dict_get_n_vars (dict) == 0) { msg (SE, _("Cannot DROP all variables from dictionary.")); return false; @@ -192,10 +302,16 @@ parse_dict_keep (struct lexer *lexer, struct dictionary *dict) dict_reorder_vars (dict, v, nv); /* Delete the remaining variables. */ - v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v); - for (i = nv; i < dict_get_var_cnt (dict); i++) + if (dict_get_n_vars (dict) == nv) + { + free (v); + return true; + } + + v = xnrealloc (v, dict_get_n_vars (dict) - nv, sizeof *v); + for (i = nv; i < dict_get_n_vars (dict); i++) v[i - nv] = dict_get_var (dict, i); - dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv); + dict_delete_vars (dict, v, dict_get_n_vars (dict) - nv); free (v); return true;