Improve error messages by citing syntax in more of them.
[pspp] / src / language / data-io / dataset.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 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/command.h"
20
21 #include "data/dataset.h"
22 #include "data/session.h"
23 #include "language/lexer/lexer.h"
24 #include "libpspp/message.h"
25 #include "output/pivot-table.h"
26
27 #include "gettext.h"
28 #define N_(msgid) msgid
29 #define _(msgid) gettext (msgid)
30 \f
31 static int
32 parse_window (struct lexer *lexer, unsigned int allowed,
33               enum dataset_display def)
34 {
35   if (!lex_match_id (lexer, "WINDOW"))
36     return def;
37   lex_match (lexer, T_EQUALS);
38
39   if (allowed & (1 << DATASET_MINIMIZED) && lex_match_id (lexer, "MINIMIZED"))
40     return DATASET_MINIMIZED;
41   else if (allowed & (1 << DATASET_ASIS) && lex_match_id (lexer, "ASIS"))
42     return DATASET_ASIS;
43   else if (allowed & (1 << DATASET_FRONT) && lex_match_id (lexer, "FRONT"))
44     return DATASET_FRONT;
45   else if (allowed & (1 << DATASET_HIDDEN) && lex_match_id (lexer, "HIDDEN"))
46     return DATASET_HIDDEN;
47
48   lex_error (lexer, NULL);
49   return -1;
50 }
51
52 static struct dataset *
53 parse_dataset_name (struct lexer *lexer, struct session *session)
54 {
55   struct dataset *ds;
56
57   if (!lex_force_id (lexer))
58     return NULL;
59
60   ds = session_lookup_dataset (session, lex_tokcstr (lexer));
61   if (ds != NULL)
62     lex_get (lexer);
63   else
64     lex_error (lexer, _("There is no dataset named %s."), lex_tokcstr (lexer));
65   return ds;
66 }
67
68 int
69 cmd_dataset_name (struct lexer *lexer, struct dataset *active)
70 {
71   int display;
72
73   if (!lex_force_id (lexer))
74     return CMD_FAILURE;
75   dataset_set_name (active, lex_tokcstr (lexer));
76   lex_get (lexer);
77
78   display = parse_window (lexer, (1 << DATASET_ASIS) | (1 << DATASET_FRONT),
79                           DATASET_ASIS);
80   if (display < 0)
81     return CMD_FAILURE;
82   else if (display != DATASET_ASIS)
83     dataset_set_display (active, display);
84
85   return CMD_SUCCESS;
86 }
87
88 int
89 cmd_dataset_activate (struct lexer *lexer, struct dataset *active)
90 {
91   struct session *session = dataset_session (active);
92   struct dataset *ds;
93   int display;
94
95   ds = parse_dataset_name (lexer, session);
96   if (ds == NULL)
97     return CMD_FAILURE;
98
99   if (ds != active)
100     {
101       proc_execute (active);
102       session_set_active_dataset (session, ds);
103       if (dataset_name (active)[0] == '\0')
104         dataset_destroy (active);
105       return CMD_SUCCESS;
106     }
107
108   display = parse_window (lexer, (1 << DATASET_ASIS) | (1 << DATASET_FRONT),
109                           DATASET_ASIS);
110   if (display < 0)
111     return CMD_FAILURE;
112   else if (display != DATASET_ASIS)
113     dataset_set_display (ds, display);
114
115   return CMD_SUCCESS;
116 }
117
118 int
119 cmd_dataset_copy (struct lexer *lexer, struct dataset *old)
120 {
121   struct session *session = dataset_session (old);
122   struct dataset *new;
123   int display;
124   char *name;
125
126   /* Parse the entire command first.  proc_execute() can attempt to parse
127      BEGIN DATA...END DATA and it will fail confusingly if we are in the
128      middle of the command at the point.  */
129   if (!lex_force_id (lexer))
130     return CMD_FAILURE;
131   name = xstrdup (lex_tokcstr (lexer));
132   lex_get (lexer);
133
134   display = parse_window (lexer, ((1 << DATASET_MINIMIZED)
135                                   | (1 << DATASET_HIDDEN)
136                                   | (1 << DATASET_FRONT)),
137                           DATASET_MINIMIZED);
138   if (display < 0)
139     {
140       free (name);
141       return CMD_FAILURE;
142     }
143
144   if (session_lookup_dataset (session, name) == old)
145     {
146       new = old;
147       dataset_set_name (old, "");
148     }
149   else
150     {
151       proc_execute (old);
152       new = dataset_clone (old, name);
153     }
154   dataset_set_display (new, display);
155
156   free (name);
157   return CMD_SUCCESS;
158 }
159
160 int
161 cmd_dataset_declare (struct lexer *lexer, struct dataset *ds)
162 {
163   struct session *session = dataset_session (ds);
164   struct dataset *new;
165   int display;
166
167   if (!lex_force_id (lexer))
168     return CMD_FAILURE;
169
170   new = session_lookup_dataset (session, lex_tokcstr (lexer));
171   if (new == NULL)
172     new = dataset_create (session, lex_tokcstr (lexer));
173   lex_get (lexer);
174
175   display = parse_window (lexer, ((1 << DATASET_MINIMIZED)
176                                   | (1 << DATASET_HIDDEN)
177                                   | (1 << DATASET_FRONT)),
178                           DATASET_MINIMIZED);
179   if (display < 0)
180     return CMD_FAILURE;
181   dataset_set_display (new, display);
182
183   return CMD_SUCCESS;
184 }
185
186 static void
187 dataset_close_cb (struct dataset *ds, void *session_)
188 {
189   struct session *session = session_;
190
191   if (ds != session_active_dataset (session))
192     dataset_destroy (ds);
193 }
194
195 int
196 cmd_dataset_close (struct lexer *lexer, struct dataset *ds)
197 {
198   struct session *session = dataset_session (ds);
199
200   if (lex_match (lexer, T_ALL))
201     {
202       session_for_each_dataset (session, dataset_close_cb, session);
203       dataset_set_name (session_active_dataset (session), "");
204     }
205   else
206     {
207       if (!lex_match (lexer, T_ASTERISK))
208         {
209           ds = parse_dataset_name (lexer, session);
210           if (ds == NULL)
211             return CMD_FAILURE;
212         }
213
214       if (ds == session_active_dataset (session))
215         dataset_set_name (ds, "");
216       else
217         dataset_destroy (ds);
218     }
219
220   return CMD_SUCCESS;
221 }
222
223 static void
224 dataset_display_cb (struct dataset *ds, void *p_)
225 {
226   struct dataset ***p = p_;
227   **p = ds;
228   (*p)++;
229 }
230
231 static int
232 sort_datasets (const void *a_, const void *b_)
233 {
234   struct dataset *const *a = a_;
235   struct dataset *const *b = b_;
236
237   return strcmp (dataset_name (*a), dataset_name (*b));
238 }
239
240 int
241 cmd_dataset_display (struct lexer *lexer UNUSED, struct dataset *ds)
242 {
243   struct session *session = dataset_session (ds);
244   size_t n = session_n_datasets (session);
245   struct dataset **datasets = xmalloc (n * sizeof *datasets);
246   struct dataset **p = datasets;
247   session_for_each_dataset (session, dataset_display_cb, &p);
248   qsort (datasets, n, sizeof *datasets, sort_datasets);
249
250   struct pivot_table *table = pivot_table_create (N_("Datasets"));
251
252   struct pivot_dimension *datasets_dim = pivot_dimension_create (
253     table, PIVOT_AXIS_ROW, N_("Dataset"));
254   datasets_dim->hide_all_labels = true;
255
256   for (size_t i = 0; i < n; i++)
257     {
258       struct dataset *ds = datasets[i];
259       const char *name;
260
261       name = dataset_name (ds);
262       if (name[0] == '\0')
263         name = _("unnamed dataset");
264
265       char *text = (ds == session_active_dataset (session)
266                     ? xasprintf ("%s (%s)", name, _("active dataset"))
267                     : xstrdup (name));
268
269       int dataset_idx = pivot_category_create_leaf (
270         datasets_dim->root, pivot_value_new_integer (i));
271
272       pivot_table_put1 (table, dataset_idx,
273                         pivot_value_new_user_text_nocopy (text));
274     }
275
276   free (datasets);
277
278   pivot_table_submit (table);
279
280   return CMD_SUCCESS;
281 }