lexer: Use error helper functions in more situations.
[pspp] / src / language / dictionary / modify-variables.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 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 <stdlib.h>
20
21 #include "data/dataset.h"
22 #include "data/dictionary.h"
23 #include "data/variable.h"
24 #include "language/command.h"
25 #include "language/lexer/lexer.h"
26 #include "language/lexer/variable-parser.h"
27 #include "libpspp/array.h"
28 #include "libpspp/assertion.h"
29 #include "libpspp/bit-vector.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/message.h"
32 #include "libpspp/misc.h"
33 #include "libpspp/str.h"
34
35 #include "gl/xalloc.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 /* These control the ordering produced by
41    compare_variables_given_ordering(). */
42 struct ordering
43   {
44     int forward;                /* 1=FORWARD, 0=BACKWARD. */
45     int positional;             /* 1=POSITIONAL, 0=ALPHA. */
46   };
47
48 /* Increasing order of variable index. */
49 static struct ordering forward_positional_ordering = {1, 1};
50
51 static int compare_variables_given_ordering (const void *, const void *,
52                                              const void *ordering);
53
54 /* Explains how to modify the variables in a dictionary. */
55 struct var_modification
56   {
57     /* New variable ordering. */
58     struct variable **reorder_vars;
59     size_t reorder_cnt;
60
61     /* DROP/KEEP information. */
62     struct variable **drop_vars;
63     size_t drop_cnt;
64
65     /* New variable names. */
66     struct variable **rename_vars;
67     char **new_names;
68     size_t rename_cnt;
69   };
70
71 static bool rearrange_dict (struct dictionary *d,
72                            const struct var_modification *vm);
73
74 /* Performs MODIFY VARS command. */
75 int
76 cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
77 {
78   /* Bits indicated whether we've already encountered a subcommand of
79      this type. */
80   unsigned already_encountered = 0;
81
82   /* What we are going to do to the active dataset. */
83   struct var_modification vm;
84
85   /* Return code. */
86   int ret_code = CMD_CASCADING_FAILURE;
87
88   size_t i;
89
90   if (proc_make_temporary_transformations_permanent (ds))
91     msg (SE, _("MODIFY VARS may not be used after TEMPORARY.  "
92                "Temporary transformations will be made permanent."));
93
94   vm.reorder_vars = NULL;
95   vm.reorder_cnt = 0;
96   vm.rename_vars = NULL;
97   vm.new_names = NULL;
98   vm.rename_cnt = 0;
99   vm.drop_vars = NULL;
100   vm.drop_cnt = 0;
101
102   /* Parse each subcommand. */
103   lex_match (lexer, T_SLASH);
104   for (;;)
105     {
106       if (lex_match_id (lexer, "REORDER"))
107         {
108           struct variable **v = NULL;
109           size_t nv = 0;
110
111           if (already_encountered & 1)
112             {
113               lex_sbc_only_once ("REORDER");
114               goto done;
115             }
116           already_encountered |= 1;
117
118           lex_match (lexer, T_EQUALS);
119           do
120             {
121               struct ordering ordering;
122               size_t prev_nv = nv;
123
124               ordering.forward = ordering.positional = 1;
125               if (lex_match_id (lexer, "FORWARD"));
126               else if (lex_match_id (lexer, "BACKWARD"))
127                 ordering.forward = 0;
128               if (lex_match_id (lexer, "POSITIONAL"));
129               else if (lex_match_id (lexer, "ALPHA"))
130                 ordering.positional = 0;
131
132               if (lex_match (lexer, T_ALL) || lex_token (lexer) == T_SLASH || lex_token (lexer) == T_ENDCMD)
133                 {
134                   if (prev_nv != 0)
135                     {
136                       msg (SE, _("Cannot specify ALL after specifying a set "
137                            "of variables."));
138                       goto done;
139                     }
140                   dict_get_vars_mutable (dataset_dict (ds), &v, &nv, DC_SYSTEM);
141                 }
142               else
143                 {
144                   if (!lex_match (lexer, T_LPAREN))
145                     {
146                       lex_error_expecting (lexer, "`('", NULL_SENTINEL);
147                       free (v);
148                       goto done;
149                     }
150                   if (!parse_variables (lexer, dataset_dict (ds), &v, &nv,
151                                         PV_APPEND | PV_NO_DUPLICATE))
152                     {
153                       free (v);
154                       goto done;
155                     }
156                   if (!lex_match (lexer, T_RPAREN))
157                     {
158                       lex_error_expecting (lexer, "`)'", NULL_SENTINEL);
159                       free (v);
160                       goto done;
161                     }
162                 }
163               sort (&v[prev_nv], nv - prev_nv, sizeof *v,
164                     compare_variables_given_ordering, &ordering);
165             }
166           while (lex_token (lexer) != T_SLASH
167                  && lex_token (lexer) != T_ENDCMD);
168
169           vm.reorder_vars = v;
170           vm.reorder_cnt = nv;
171         }
172       else if (lex_match_id (lexer, "RENAME"))
173         {
174           if (already_encountered & 2)
175             {
176               lex_sbc_only_once ("RENAME");
177               goto done;
178             }
179           already_encountered |= 2;
180
181           lex_match (lexer, T_EQUALS);
182           do
183             {
184               size_t prev_nv_1 = vm.rename_cnt;
185               size_t prev_nv_2 = vm.rename_cnt;
186
187               if (!lex_match (lexer, T_LPAREN))
188                 {
189                   lex_error_expecting (lexer, "`('", NULL_SENTINEL);
190                   goto done;
191                 }
192               if (!parse_variables (lexer, dataset_dict (ds),
193                                     &vm.rename_vars, &vm.rename_cnt,
194                                     PV_APPEND | PV_NO_DUPLICATE))
195                 goto done;
196               if (!lex_match (lexer, T_EQUALS))
197                 {
198                   lex_error_expecting (lexer, "`='", NULL_SENTINEL);
199                   goto done;
200                 }
201               if (!parse_DATA_LIST_vars (lexer, dataset_dict (ds),
202                                          &vm.new_names, &prev_nv_1, PV_APPEND))
203                 goto done;
204               if (prev_nv_1 != vm.rename_cnt)
205                 {
206                   msg (SE, _("Differing number of variables in old name list "
207                              "(%zu) and in new name list (%zu)."),
208                        vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
209                   for (i = 0; i < prev_nv_1; i++)
210                     free (vm.new_names[i]);
211                   free (vm.new_names);
212                   vm.new_names = NULL;
213                   goto done;
214                 }
215               if (!lex_match (lexer, T_RPAREN))
216                 {
217                   lex_error_expecting (lexer, "`)'", NULL_SENTINEL);
218                   goto done;
219                 }
220             }
221           while (lex_token (lexer) != T_ENDCMD
222                  && lex_token (lexer) != T_SLASH);
223         }
224       else if (lex_match_id (lexer, "KEEP"))
225         {
226           struct variable **keep_vars, **all_vars, **drop_vars;
227           size_t keep_cnt, all_cnt, drop_cnt;
228
229           if (already_encountered & 4)
230             {
231               msg (SE, _("KEEP subcommand may be given at most once.  It may "
232                    "not be given in conjunction with the DROP subcommand."));
233               goto done;
234             }
235           already_encountered |= 4;
236
237           lex_match (lexer, T_EQUALS);
238           if (!parse_variables (lexer, dataset_dict (ds), &keep_vars, &keep_cnt, PV_NONE))
239             goto done;
240
241           /* Transform the list of variables to keep into a list of
242              variables to drop.  First sort the keep list, then figure
243              out which variables are missing. */
244           sort (keep_vars, keep_cnt, sizeof *keep_vars,
245                 compare_variables_given_ordering, &forward_positional_ordering);
246
247           dict_get_vars_mutable (dataset_dict (ds), &all_vars, &all_cnt, 0);
248           assert (all_cnt >= keep_cnt);
249
250           drop_cnt = all_cnt - keep_cnt;
251           drop_vars = xnmalloc (drop_cnt, sizeof *keep_vars);
252           if (set_difference (all_vars, all_cnt,
253                               keep_vars, keep_cnt,
254                               sizeof *all_vars,
255                               drop_vars,
256                               compare_variables_given_ordering,
257                               &forward_positional_ordering)
258               != drop_cnt)
259             NOT_REACHED ();
260
261           free (keep_vars);
262           free (all_vars);
263
264           vm.drop_vars = drop_vars;
265           vm.drop_cnt = drop_cnt;
266         }
267       else if (lex_match_id (lexer, "DROP"))
268         {
269           struct variable **drop_vars;
270           size_t drop_cnt;
271
272           if (already_encountered & 4)
273             {
274               msg (SE, _("DROP subcommand may be given at most once.  It may "
275                          "not be given in conjunction with the KEEP "
276                          "subcommand."));
277               goto done;
278             }
279           already_encountered |= 4;
280
281           lex_match (lexer, T_EQUALS);
282           if (!parse_variables (lexer, dataset_dict (ds), &drop_vars, &drop_cnt, PV_NONE))
283             goto done;
284           vm.drop_vars = drop_vars;
285           vm.drop_cnt = drop_cnt;
286         }
287       else if (lex_match_id (lexer, "MAP"))
288         {
289           struct dictionary *temp = dict_clone (dataset_dict (ds));
290           int success = rearrange_dict (temp, &vm);
291           if (success)
292             {
293               /* FIXME: display new dictionary. */
294             }
295           dict_destroy (temp);
296         }
297       else
298         {
299           if (lex_token (lexer) == T_ID)
300             msg (SE, _("Unrecognized subcommand name `%s'."), lex_tokcstr (lexer));
301           else
302             msg (SE, _("Subcommand name expected."));
303           goto done;
304         }
305
306       if (lex_token (lexer) == T_ENDCMD)
307         break;
308       if (lex_token (lexer) != T_SLASH)
309         {
310           lex_error_expecting (lexer, "`/'", "`.'", NULL_SENTINEL);
311           goto done;
312         }
313       lex_get (lexer);
314     }
315
316   if (already_encountered & (1 | 4))
317     {
318       /* Read the data. */
319       if (!proc_execute (ds))
320         goto done;
321     }
322
323   if (!rearrange_dict (dataset_dict (ds), &vm))
324     goto done;
325
326   ret_code = CMD_SUCCESS;
327
328 done:
329   free (vm.reorder_vars);
330   free (vm.rename_vars);
331   for (i = 0; i < vm.rename_cnt; i++)
332     free (vm.new_names[i]);
333   free (vm.new_names);
334   free (vm.drop_vars);
335   return ret_code;
336 }
337
338 /* Compares A and B according to the settings in
339    ORDERING, returning a strcmp()-type result. */
340 static int
341 compare_variables_given_ordering (const void *a_, const void *b_,
342                                   const void *ordering_)
343 {
344   struct variable *const *pa = a_;
345   struct variable *const *pb = b_;
346   const struct variable *a = *pa;
347   const struct variable *b = *pb;
348   const struct ordering *ordering = ordering_;
349
350   int result;
351   if (ordering->positional)
352     {
353       size_t a_index = var_get_dict_index (a);
354       size_t b_index = var_get_dict_index (b);
355       result = a_index < b_index ? -1 : a_index > b_index;
356     }
357   else
358     result = strcasecmp (var_get_name (a), var_get_name (b));
359   if (!ordering->forward)
360     result = -result;
361   return result;
362 }
363
364 /* Pairs a variable with a new name. */
365 struct var_renaming
366   {
367     struct variable *var;
368     const char *new_name;
369   };
370
371 /* A algo_compare_func that compares new_name members in struct
372    var_renaming structures A and B. */
373 static int
374 compare_var_renaming_by_new_name (const void *a_, const void *b_,
375                                   const void *aux UNUSED)
376 {
377   const struct var_renaming *a = a_;
378   const struct var_renaming *b = b_;
379
380   return strcasecmp (a->new_name, b->new_name);
381 }
382
383 /* Returns true if performing VM on dictionary D would not cause
384    problems such as duplicate variable names.  Returns false
385    otherwise, and issues an error message. */
386 static int
387 validate_var_modification (const struct dictionary *d,
388                            const struct var_modification *vm)
389 {
390   /* Variable reordering can't be a problem, so we don't simulate
391      it.  Variable renaming can cause duplicate names, but
392      dropping variables can eliminate them, so we simulate both
393      of those. */
394   struct variable **all_vars;
395   struct variable **keep_vars;
396   struct variable **drop_vars;
397   size_t keep_cnt, drop_cnt;
398   size_t all_cnt;
399
400   struct var_renaming *var_renaming;
401   int valid;
402   size_t i;
403
404   /* All variables, in index order. */
405   dict_get_vars_mutable (d, &all_vars, &all_cnt, 0);
406
407   /* Drop variables, in index order. */
408   drop_cnt = vm->drop_cnt;
409   drop_vars = xnmalloc (drop_cnt, sizeof *drop_vars);
410   memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
411   sort (drop_vars, drop_cnt, sizeof *drop_vars,
412         compare_variables_given_ordering, &forward_positional_ordering);
413
414   /* Keep variables, in index order. */
415   assert (all_cnt >= drop_cnt);
416   keep_cnt = all_cnt - drop_cnt;
417   keep_vars = xnmalloc (keep_cnt, sizeof *keep_vars);
418   if (set_difference (all_vars, all_cnt,
419                       drop_vars, drop_cnt,
420                       sizeof *all_vars,
421                       keep_vars,
422                       compare_variables_given_ordering,
423                       &forward_positional_ordering) != keep_cnt)
424     NOT_REACHED ();
425
426   /* Copy variables into var_renaming array. */
427   var_renaming = xnmalloc (keep_cnt, sizeof *var_renaming);
428   for (i = 0; i < keep_cnt; i++)
429     {
430       var_renaming[i].var = keep_vars[i];
431       var_renaming[i].new_name = var_get_name (keep_vars[i]);
432     }
433
434   /* Rename variables in var_renaming array. */
435   for (i = 0; i < vm->rename_cnt; i++)
436     {
437       struct variable *const *kv;
438       struct var_renaming *vr;
439
440       /* Get the var_renaming element. */
441       kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
442                           &vm->rename_vars[i],
443                           compare_variables_given_ordering,
444                           &forward_positional_ordering);
445       if (kv == NULL)
446         continue;
447       vr = var_renaming + (kv - keep_vars);
448
449       vr->new_name = vm->new_names[i];
450     }
451
452   /* Sort var_renaming array by new names and check for
453      duplicates. */
454   sort (var_renaming, keep_cnt, sizeof *var_renaming,
455         compare_var_renaming_by_new_name, NULL);
456   valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
457                                compare_var_renaming_by_new_name, NULL) == NULL;
458
459   /* Clean up. */
460   free (all_vars);
461   free (keep_vars);
462   free (drop_vars);
463   free (var_renaming);
464
465   return valid;
466 }
467
468 /* Reoders, removes, and renames variables in dictionary D
469    according to VM.  Returns true if successful, false if there
470    would have been duplicate variable names if the modifications
471    had been carried out.  In the latter case, the dictionary is
472    not modified. */
473 static bool
474 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
475 {
476   char **rename_old_names;
477
478   struct variable **rename_vars;
479   char **rename_new_names;
480   size_t rename_cnt;
481
482   size_t i;
483
484   /* Check whether the modifications will cause duplicate
485      names. */
486   if (!validate_var_modification (d, vm))
487     return false;
488
489   /* Record the old names of variables to rename.  After
490      variables are deleted, we can't depend on the variables to
491      still exist, but we can still look them up by name. */
492   rename_old_names = xnmalloc (vm->rename_cnt, sizeof *rename_old_names);
493   for (i = 0; i < vm->rename_cnt; i++)
494     rename_old_names[i] = xstrdup (var_get_name (vm->rename_vars[i]));
495
496   /* Reorder and delete variables. */
497   dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
498   dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
499
500   /* Compose lists of variables to rename and their new names. */
501   rename_vars = xnmalloc (vm->rename_cnt, sizeof *rename_vars);
502   rename_new_names = xnmalloc (vm->rename_cnt, sizeof *rename_new_names);
503   rename_cnt = 0;
504   for (i = 0; i < vm->rename_cnt; i++)
505     {
506       struct variable *var = dict_lookup_var (d, rename_old_names[i]);
507       if (var == NULL)
508         continue;
509
510       rename_vars[rename_cnt] = var;
511       rename_new_names[rename_cnt] = vm->new_names[i];
512       rename_cnt++;
513     }
514
515   /* Do renaming. */
516   if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
517                         NULL) == 0)
518     NOT_REACHED ();
519
520   /* Clean up. */
521   for (i = 0; i < vm->rename_cnt; i++)
522     free (rename_old_names[i]);
523   free (rename_old_names);
524   free (rename_vars);
525   free (rename_new_names);
526
527   return true;
528 }