SAVE TRANSLATE: Improve error message.
[pspp.git] / src / language / data-io / save-translate.c
blob26941329f35ad378426ad441654031e9ab586791
1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010, 2011, 2013, 2016 Free Software Foundation, Inc.
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.
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.
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/>. */
17 #include <config.h>
19 #include <stdlib.h>
21 #include "data/case-map.h"
22 #include "data/casereader.h"
23 #include "data/casewriter.h"
24 #include "data/csv-file-writer.h"
25 #include "data/dataset.h"
26 #include "data/dictionary.h"
27 #include "data/file-name.h"
28 #include "data/format.h"
29 #include "data/settings.h"
30 #include "language/command.h"
31 #include "language/data-io/file-handle.h"
32 #include "language/data-io/trim.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/message.h"
36 #include "xalloc.h"
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40 #define N_(msgid) (msgid)
42 int
43 cmd_save_translate (struct lexer *lexer, struct dataset *ds)
45 enum { CSV_FILE = 1, TAB_FILE } type;
47 struct dictionary *dict;
48 struct case_map_stage *stage;
49 struct case_map *map;
50 struct casewriter *writer;
51 struct file_handle *handle;
53 struct csv_writer_options csv_opts;
55 bool replace;
57 bool retain_unselected;
58 bool recode_user_missing;
59 bool include_var_names;
60 bool use_value_labels;
61 bool use_print_formats;
62 char decimal;
63 char delimiter;
64 char qualifier;
66 bool ok;
68 type = 0;
70 dict = dict_clone (dataset_dict (ds));
71 stage = NULL;
72 map = NULL;
74 handle = NULL;
75 replace = false;
77 retain_unselected = true;
78 recode_user_missing = false;
79 include_var_names = false;
80 use_value_labels = false;
81 use_print_formats = false;
82 decimal = settings_get_decimal_char (FMT_F);
83 delimiter = 0;
84 qualifier = '"';
86 stage = case_map_stage_create (dict);
87 dict_delete_scratch_vars (dict);
89 while (lex_token (lexer) != T_ENDCMD)
91 if (!lex_force_match (lexer, T_SLASH))
92 goto error;
94 if (lex_match_id (lexer, "OUTFILE"))
96 if (handle != NULL)
98 lex_sbc_only_once ("OUTFILE");
99 goto error;
102 lex_match (lexer, T_EQUALS);
104 handle = fh_parse (lexer, FH_REF_FILE, NULL);
105 if (handle == NULL)
106 goto error;
108 else if (lex_match_id (lexer, "TYPE"))
110 if (type != 0)
112 lex_sbc_only_once ("TYPE");
113 goto error;
116 lex_match (lexer, T_EQUALS);
117 if (lex_match_id (lexer, "CSV"))
118 type = CSV_FILE;
119 else if (lex_match_id (lexer, "TAB"))
120 type = TAB_FILE;
121 else
123 lex_error_expecting (lexer, "CSV", "TAB", NULL_SENTINEL);
124 goto error;
127 else if (lex_match_id (lexer, "REPLACE"))
128 replace = true;
129 else if (lex_match_id (lexer, "FIELDNAMES"))
130 include_var_names = true;
131 else if (lex_match_id (lexer, "MISSING"))
133 lex_match (lexer, T_EQUALS);
134 if (lex_match_id (lexer, "IGNORE"))
135 recode_user_missing = false;
136 else if (lex_match_id (lexer, "RECODE"))
137 recode_user_missing = true;
138 else
140 lex_error_expecting (lexer, "IGNORE", "RECODE", NULL_SENTINEL);
141 goto error;
144 else if (lex_match_id (lexer, "CELLS"))
146 lex_match (lexer, T_EQUALS);
147 if (lex_match_id (lexer, "VALUES"))
148 use_value_labels = false;
149 else if (lex_match_id (lexer, "LABELS"))
150 use_value_labels = true;
151 else
153 lex_error_expecting (lexer, "VALUES", "LABELS", NULL_SENTINEL);
154 goto error;
157 else if (lex_match_id (lexer, "TEXTOPTIONS"))
159 lex_match (lexer, T_EQUALS);
160 for (;;)
162 if (lex_match_id (lexer, "DELIMITER"))
164 lex_match (lexer, T_EQUALS);
165 if (!lex_force_string (lexer))
166 goto error;
167 /* XXX should support multibyte UTF-8 delimiters */
168 if (ss_length (lex_tokss (lexer)) != 1)
170 msg (SE, _("The %s string must contain exactly one "
171 "character."), "DELIMITER");
172 goto error;
174 delimiter = ss_first (lex_tokss (lexer));
175 lex_get (lexer);
177 else if (lex_match_id (lexer, "QUALIFIER"))
179 lex_match (lexer, T_EQUALS);
180 if (!lex_force_string (lexer))
181 goto error;
182 /* XXX should support multibyte UTF-8 qualifiers */
183 if (ss_length (lex_tokss (lexer)) != 1)
185 msg (SE, _("The %s string must contain exactly one "
186 "character."), "QUALIFIER");
187 goto error;
189 qualifier = ss_first (lex_tokss (lexer));
190 lex_get (lexer);
192 else if (lex_match_id (lexer, "DECIMAL"))
194 lex_match (lexer, T_EQUALS);
195 if (lex_match_id (lexer, "DOT"))
196 decimal = '.';
197 else if (lex_match_id (lexer, "COMMA"))
198 decimal = ',';
199 else
201 lex_error_expecting (lexer, "DOT", "COMMA",
202 NULL_SENTINEL);
203 goto error;
206 else if (lex_match_id (lexer, "FORMAT"))
208 lex_match (lexer, T_EQUALS);
209 if (lex_match_id (lexer, "PLAIN"))
210 use_print_formats = false;
211 else if (lex_match_id (lexer, "VARIABLE"))
212 use_print_formats = true;
213 else
215 lex_error_expecting (lexer, "PLAIN", "VARIABLE",
216 NULL_SENTINEL);
217 goto error;
220 else
221 break;
224 else if (lex_match_id (lexer, "UNSELECTED"))
226 lex_match (lexer, T_EQUALS);
227 if (lex_match_id (lexer, "RETAIN"))
228 retain_unselected = true;
229 else if (lex_match_id (lexer, "DELETE"))
230 retain_unselected = false;
231 else
233 lex_error_expecting (lexer, "RETAIN", "DELETE", NULL_SENTINEL);
234 goto error;
237 else if (!parse_dict_trim (lexer, dict))
238 goto error;
241 if (type == 0)
243 lex_sbc_missing ("TYPE");
244 goto error;
246 else if (handle == NULL)
248 lex_sbc_missing ("OUTFILE");
249 goto error;
251 else if (!replace && fn_exists (handle))
253 msg (SE, _("Output file `%s' exists but %s was not specified."),
254 fh_get_file_name (handle), "REPLACE");
255 goto error;
258 dict_delete_scratch_vars (dict);
259 dict_compact_values (dict);
261 csv_opts.recode_user_missing = recode_user_missing;
262 csv_opts.include_var_names = include_var_names;
263 csv_opts.use_value_labels = use_value_labels;
264 csv_opts.use_print_formats = use_print_formats;
265 csv_opts.decimal = decimal;
266 csv_opts.delimiter = (delimiter ? delimiter
267 : type == TAB_FILE ? '\t'
268 : decimal == '.' ? ','
269 : ';');
270 csv_opts.qualifier = qualifier;
272 writer = csv_writer_open (handle, dict, &csv_opts);
273 if (writer == NULL)
274 goto error;
275 fh_unref (handle);
277 map = case_map_stage_get_case_map (stage);
278 case_map_stage_destroy (stage);
279 if (map != NULL)
280 writer = case_map_create_output_translator (map, writer);
281 dict_destroy (dict);
283 casereader_transfer (proc_open_filtering (ds, !retain_unselected), writer);
284 ok = casewriter_destroy (writer);
285 ok = proc_commit (ds) && ok;
287 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
289 error:
290 case_map_stage_destroy (stage);
291 fh_unref (handle);
292 dict_destroy (dict);
293 case_map_destroy (map);
294 return CMD_FAILURE;