refactoring
[pspp] / src / language / data-io / save.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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/any-writer.h"
22 #include "data/case-map.h"
23 #include "data/case.h"
24 #include "data/casereader.h"
25 #include "data/casewriter.h"
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/mdd-writer.h"
29 #include "data/por-file-writer.h"
30 #include "data/sys-file-writer.h"
31 #include "data/transformations.h"
32 #include "data/variable.h"
33 #include "language/command.h"
34 #include "language/data-io/file-handle.h"
35 #include "language/data-io/trim.h"
36 #include "language/lexer/lexer.h"
37 #include "libpspp/assertion.h"
38 #include "libpspp/compiler.h"
39 #include "libpspp/message.h"
40
41 #include "gl/xalloc.h"
42
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45
46 /* Writing system and portable files. */
47
48 /* Type of output file. */
49 enum writer_type
50   {
51     SYSFILE_WRITER,     /* System file. */
52     PORFILE_WRITER      /* Portable file. */
53   };
54
55 /* Type of a command. */
56 enum command_type
57   {
58     XFORM_CMD,          /* Transformation. */
59     PROC_CMD            /* Procedure. */
60   };
61
62 static int parse_output_proc (struct lexer *, struct dataset *,
63                               enum writer_type);
64 static int parse_output_trns (struct lexer *, struct dataset *,
65                               enum writer_type);
66
67 int
68 cmd_save (struct lexer *lexer, struct dataset *ds)
69 {
70   return parse_output_proc (lexer, ds, SYSFILE_WRITER);
71 }
72
73 int
74 cmd_save_data_collection (struct lexer *lexer, struct dataset *ds)
75 {
76   return parse_output_proc (lexer, ds, SYSFILE_WRITER);
77 }
78
79 int
80 cmd_export (struct lexer *lexer, struct dataset *ds)
81 {
82   return parse_output_proc (lexer, ds, PORFILE_WRITER);
83 }
84
85 int
86 cmd_xsave (struct lexer *lexer, struct dataset *ds)
87 {
88   return parse_output_trns (lexer, ds, SYSFILE_WRITER);
89 }
90
91 int
92 cmd_xexport (struct lexer *lexer, struct dataset *ds)
93 {
94   return parse_output_trns (lexer, ds, PORFILE_WRITER);
95 }
96 \f
97 struct output_trns
98   {
99     struct casewriter *writer;          /* Writer. */
100   };
101
102 static const struct trns_class output_trns_class;
103 static struct casewriter *parse_write_command (struct lexer *,
104                                                struct dataset *,
105                                                enum writer_type,
106                                                enum command_type,
107                                                bool *retain_unselected);
108
109 /* Parses and performs the SAVE or EXPORT procedure. */
110 static int
111 parse_output_proc (struct lexer *lexer, struct dataset *ds,
112                    enum writer_type writer_type)
113 {
114   bool retain_unselected;
115   struct casewriter *output;
116   bool ok;
117
118   output = parse_write_command (lexer, ds, writer_type, PROC_CMD,
119                                 &retain_unselected);
120   if (output == NULL)
121     return CMD_CASCADING_FAILURE;
122
123   casereader_transfer (proc_open_filtering (ds, !retain_unselected), output);
124   ok = casewriter_destroy (output);
125   ok = proc_commit (ds) && ok;
126
127   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
128 }
129
130 /* Parses the XSAVE or XEXPORT transformation command. */
131 static int
132 parse_output_trns (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type)
133 {
134   struct output_trns *t = xmalloc (sizeof *t);
135   t->writer = parse_write_command (lexer, ds, writer_type, XFORM_CMD, NULL);
136   if (t->writer == NULL)
137     {
138       free (t);
139       return CMD_CASCADING_FAILURE;
140     }
141
142   add_transformation (ds, &output_trns_class, t);
143   return CMD_SUCCESS;
144 }
145
146 /* Parses SAVE or XSAVE or EXPORT or XEXPORT command.
147    WRITER_TYPE identifies the type of file to write,
148    and COMMAND_TYPE identifies the type of command.
149
150    On success, returns a writer.
151    For procedures only, sets *RETAIN_UNSELECTED to true if cases
152    that would otherwise be excluded by FILTER or USE should be
153    included.
154
155    On failure, returns a null pointer. */
156 static struct casewriter *
157 parse_write_command (struct lexer *lexer, struct dataset *ds,
158                      enum writer_type writer_type,
159                      enum command_type command_type,
160                      bool *retain_unselected)
161 {
162   /* Common data. */
163   struct file_handle *handle; /* Output file. */
164   struct file_handle *metadata; /* MDD output file. */
165   struct dictionary *dict;    /* Dictionary for output file. */
166   struct casewriter *writer;  /* Writer. */
167   struct case_map_stage *stage; /* Preparation for 'map'. */
168   struct case_map *map;       /* Map from input data to data for writer. */
169   const char *sav_name = "";
170
171   /* Common options. */
172   struct sfm_write_options sysfile_opts;
173   struct pfm_write_options porfile_opts;
174
175   assert (writer_type == SYSFILE_WRITER || writer_type == PORFILE_WRITER);
176   assert (command_type == XFORM_CMD || command_type == PROC_CMD);
177   assert ((retain_unselected != NULL) == (command_type == PROC_CMD));
178
179   if (command_type == PROC_CMD)
180     *retain_unselected = true;
181
182   handle = NULL;
183   metadata = NULL;
184   dict = dict_clone (dataset_dict (ds));
185   writer = NULL;
186   stage = NULL;
187   map = NULL;
188   sysfile_opts = sfm_writer_default_options ();
189   porfile_opts = pfm_writer_default_options ();
190
191   stage = case_map_stage_create (dict);
192   dict_delete_scratch_vars (dict);
193
194   lex_match (lexer, T_SLASH);
195   for (;;)
196     {
197       if (lex_match_id (lexer, "OUTFILE"))
198         {
199           if (handle != NULL)
200             {
201               lex_sbc_only_once ("OUTFILE");
202               goto error;
203             }
204
205           lex_match (lexer, T_EQUALS);
206
207           handle = fh_parse (lexer, FH_REF_FILE, NULL);
208           if (handle == NULL)
209             goto error;
210         }
211       else if (lex_match_id (lexer, "METADATA"))
212         {
213           if (metadata != NULL)
214             {
215               lex_sbc_only_once ("METADATA");
216               goto error;
217             }
218
219           lex_match (lexer, T_EQUALS);
220
221           metadata = fh_parse (lexer, FH_REF_FILE, NULL);
222           if (metadata == NULL)
223             goto error;
224         }
225       else if (lex_match_id (lexer, "NAMES"))
226         {
227           /* Not yet implemented. */
228         }
229       else if (lex_match_id (lexer, "PERMISSIONS"))
230         {
231           bool cw;
232
233           lex_match (lexer, T_EQUALS);
234           if (lex_match_id (lexer, "READONLY"))
235             cw = false;
236           else if (lex_match_id (lexer, "WRITEABLE"))
237             cw = true;
238           else
239             {
240               lex_error_expecting (lexer, "READONLY", "WRITEABLE");
241               goto error;
242             }
243           sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
244         }
245       else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED"))
246         {
247           lex_match (lexer, T_EQUALS);
248           if (lex_match_id (lexer, "RETAIN"))
249             *retain_unselected = true;
250           else if (lex_match_id (lexer, "DELETE"))
251             *retain_unselected = false;
252           else
253             {
254               lex_error_expecting (lexer, "RETAIN", "DELETE");
255               goto error;
256             }
257         }
258       else if (writer_type == SYSFILE_WRITER
259                && lex_match_id (lexer, "COMPRESSED"))
260         sysfile_opts.compression = ANY_COMP_SIMPLE;
261       else if (writer_type == SYSFILE_WRITER
262                && lex_match_id (lexer, "UNCOMPRESSED"))
263         sysfile_opts.compression = ANY_COMP_NONE;
264       else if (writer_type == SYSFILE_WRITER
265                && lex_match_id (lexer, "ZCOMPRESSED"))
266         sysfile_opts.compression = ANY_COMP_ZLIB;
267       else if (writer_type == SYSFILE_WRITER
268                && lex_match_id (lexer, "VERSION"))
269         {
270           lex_match (lexer, T_EQUALS);
271           if (!lex_force_int_range (lexer, "VERSION", 2, 3))
272             goto error;
273           sysfile_opts.version = lex_integer (lexer);
274           lex_get (lexer);
275         }
276       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
277         {
278           lex_match (lexer, T_EQUALS);
279           if (lex_match_id (lexer, "COMMUNICATIONS"))
280             porfile_opts.type = PFM_COMM;
281           else if (lex_match_id (lexer, "TAPE"))
282             porfile_opts.type = PFM_TAPE;
283           else
284             {
285               lex_error_expecting (lexer, "COMM", "TAPE");
286               goto error;
287             }
288         }
289       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
290         {
291           lex_match (lexer, T_EQUALS);
292           if (!lex_force_int_range (lexer, "DIGITS", 1, INT_MAX))
293             goto error;
294           porfile_opts.digits = lex_integer (lexer);
295           lex_get (lexer);
296         }
297       else if (!parse_dict_trim (lexer, dict, false))
298         goto error;
299
300       if (!lex_match (lexer, T_SLASH))
301         break;
302     }
303   if (lex_end_of_command (lexer) != CMD_SUCCESS)
304     goto error;
305
306   if (!handle && !metadata)
307     {
308       msg (SE, _("The OUTFILE or METADATA subcommand is required."));
309       goto error;
310     }
311
312   dict_delete_scratch_vars (dict);
313   dict_compact_values (dict);
314
315   if (handle)
316     {
317       if (metadata)
318         sav_name = (fh_get_referent (handle) == FH_REF_FILE
319                     ? fh_get_file_name (handle)
320                     : fh_get_name (handle));
321       if (fh_get_referent (handle) == FH_REF_FILE)
322         {
323           switch (writer_type)
324             {
325             case SYSFILE_WRITER:
326               writer = sfm_open_writer (handle, dict, sysfile_opts);
327               break;
328             case PORFILE_WRITER:
329               writer = pfm_open_writer (handle, dict, porfile_opts);
330               break;
331             }
332         }
333       else
334         writer = any_writer_open (handle, dict);
335       if (writer == NULL)
336         goto error;
337     }
338
339   if (metadata)
340     {
341       if (!mdd_write (metadata, dict, sav_name))
342         goto error;
343     }
344
345   map = case_map_stage_get_case_map (stage);
346   case_map_stage_destroy (stage);
347   if (map != NULL)
348     writer = case_map_create_output_translator (map, writer);
349   dict_unref (dict);
350
351   fh_unref (handle);
352   fh_unref (metadata);
353   return writer;
354
355  error:
356   case_map_stage_destroy (stage);
357   fh_unref (handle);
358   fh_unref (metadata);
359   casewriter_destroy (writer);
360   dict_unref (dict);
361   case_map_destroy (map);
362   return NULL;
363 }
364
365 /* Writes case *C to the system file specified on XSAVE or XEXPORT. */
366 static enum trns_result
367 output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
368 {
369   struct output_trns *t = trns_;
370   casewriter_write (t->writer, case_ref (*c));
371   return TRNS_CONTINUE;
372 }
373
374 /* Frees an XSAVE or XEXPORT transformation.
375    Returns true if successful, false if an I/O error occurred. */
376 static bool
377 output_trns_free (void *trns_)
378 {
379   struct output_trns *t = trns_;
380   bool ok = casewriter_destroy (t->writer);
381   free (t);
382   return ok;
383 }
384
385 static const struct trns_class output_trns_class = {
386   .name = "XSAVE/XEXPORT",
387   .execute = output_trns_proc,
388   .destroy = output_trns_free,
389 };