947a7524a84eaa9d00773d9ff8af440b49910351
[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 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/data-io/trim.h"
20
21 #include <stdlib.h>
22
23 #include "data/dictionary.h"
24 #include "data/variable.h"
25 #include "language/lexer/lexer.h"
26 #include "language/lexer/variable-parser.h"
27 #include "libpspp/message.h"
28
29 #include "gl/xalloc.h"
30
31 #include "gettext.h"
32 #define _(msgid) gettext (msgid)
33
34 /* Commands that read and write system files share a great deal
35    of common syntactic structure for rearranging and dropping
36    variables.  This function parses this syntax and modifies DICT
37    appropriately.  Returns true on success, false on failure. */
38 bool
39 parse_dict_trim (struct lexer *lexer, struct dictionary *dict)
40 {
41   if (lex_match_id (lexer, "MAP"))
42     {
43       /* FIXME. */
44       return true;
45     }
46   else if (lex_match_id (lexer, "DROP"))
47     return parse_dict_drop (lexer, dict);
48   else if (lex_match_id (lexer, "KEEP"))
49     return parse_dict_keep (lexer, dict);
50   else if (lex_match_id (lexer, "RENAME"))
51     return parse_dict_rename (lexer, dict);
52   else
53     {
54       lex_error (lexer, _("expecting a valid subcommand"));
55       return false;
56     }
57 }
58
59 /* Parses and performs the RENAME subcommand of GET, SAVE, and
60    related commands. */
61 bool
62 parse_dict_rename (struct lexer *lexer, struct dictionary *dict)
63 {
64   size_t i;
65
66   int success = 0;
67
68   struct variable **v;
69   char **new_names;
70   size_t nv, nn;
71   char *err_name;
72
73   int group;
74
75   lex_match (lexer, T_EQUALS);
76   if (lex_token (lexer) != T_LPAREN)
77     {
78       struct variable *v;
79
80       v = parse_variable (lexer, dict);
81       if (v == NULL)
82         return 0;
83       if (!lex_force_match (lexer, T_EQUALS)
84           || !lex_force_id (lexer)
85           || !dict_id_is_valid (dict, lex_tokcstr (lexer), true))
86         return 0;
87       if (dict_lookup_var (dict, lex_tokcstr (lexer)) != NULL)
88         {
89           msg (SE, _("Cannot rename %s as %s because there already exists "
90                      "a variable named %s.  To rename variables with "
91                      "overlapping names, use a single RENAME subcommand "
92                      "such as `/RENAME (A=B)(B=C)(C=A)', or equivalently, "
93                      "`/RENAME (A B C=B C A)'."),
94                var_get_name (v), lex_tokcstr (lexer), lex_tokcstr (lexer));
95           return 0;
96         }
97
98       dict_rename_var (dict, v, lex_tokcstr (lexer));
99       lex_get (lexer);
100       return 1;
101     }
102
103   nv = nn = 0;
104   v = NULL;
105   new_names = 0;
106   group = 1;
107   while (lex_match (lexer, T_LPAREN))
108     {
109       size_t old_nv = nv;
110
111       if (!parse_variables (lexer, dict, &v, &nv, PV_NO_DUPLICATE | PV_APPEND))
112         goto done;
113       if (!lex_match (lexer, T_EQUALS))
114         {
115           lex_error_expecting (lexer, "`='", NULL_SENTINEL);
116           goto done;
117         }
118       if (!parse_DATA_LIST_vars (lexer, dict, &new_names, &nn,
119                                  PV_APPEND | PV_NO_SCRATCH | PV_NO_DUPLICATE))
120         goto done;
121       if (nn != nv)
122         {
123           msg (SE, _("Number of variables on left side of `=' (%zu) does not "
124                      "match number of variables on right side (%zu), in "
125                      "parenthesized group %d of RENAME subcommand."),
126                nv - old_nv, nn - old_nv, group);
127           goto done;
128         }
129       if (!lex_force_match (lexer, T_RPAREN))
130         goto done;
131       group++;
132     }
133
134   if (!dict_rename_vars (dict, v, new_names, nv, &err_name))
135     {
136       msg (SE, _("Requested renaming duplicates variable name %s."), err_name);
137       goto done;
138     }
139   success = 1;
140
141  done:
142   for (i = 0; i < nn; i++)
143     free (new_names[i]);
144   free (new_names);
145   free (v);
146
147   return success;
148 }
149
150 /* Parses and performs the DROP subcommand of GET, SAVE, and
151    related commands.
152    Returns true if successful, false on failure.*/
153 bool
154 parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
155 {
156   struct variable **v;
157   size_t nv;
158
159   lex_match (lexer, T_EQUALS);
160   if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
161     return false;
162   dict_delete_vars (dict, v, nv);
163   free (v);
164
165   if (dict_get_var_cnt (dict) == 0)
166     {
167       msg (SE, _("Cannot DROP all variables from dictionary."));
168       return false;
169     }
170   return true;
171 }
172
173 /* Parses and performs the KEEP subcommand of GET, SAVE, and
174    related commands.
175    Returns true if successful, false on failure.*/
176 bool
177 parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
178 {
179   struct variable **v;
180   size_t nv;
181   size_t i;
182
183   lex_match (lexer, T_EQUALS);
184   if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
185     return false;
186
187   /* Move the specified variables to the beginning. */
188   dict_reorder_vars (dict, v, nv);
189
190   /* Delete the remaining variables. */
191   v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v);
192   for (i = nv; i < dict_get_var_cnt (dict); i++)
193     v[i - nv] = dict_get_var (dict, i);
194   dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv);
195   free (v);
196
197   return true;
198 }