Improve MDD writer support.
[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 trns_proc_func output_trns_proc;
103 static trns_free_func output_trns_free;
104 static struct casewriter *parse_write_command (struct lexer *,
105                                                struct dataset *,
106                                                enum writer_type,
107                                                enum command_type,
108                                                bool *retain_unselected);
109
110 /* Parses and performs the SAVE or EXPORT procedure. */
111 static int
112 parse_output_proc (struct lexer *lexer, struct dataset *ds,
113                    enum writer_type writer_type)
114 {
115   bool retain_unselected;
116   struct casewriter *output;
117   bool ok;
118
119   output = parse_write_command (lexer, ds, writer_type, PROC_CMD,
120                                 &retain_unselected);
121   if (output == NULL)
122     return CMD_CASCADING_FAILURE;
123
124   casereader_transfer (proc_open_filtering (ds, !retain_unselected), output);
125   ok = casewriter_destroy (output);
126   ok = proc_commit (ds) && ok;
127
128   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
129 }
130
131 /* Parses the XSAVE or XEXPORT transformation command. */
132 static int
133 parse_output_trns (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type)
134 {
135   struct output_trns *t = xmalloc (sizeof *t);
136   t->writer = parse_write_command (lexer, ds, writer_type, XFORM_CMD, NULL);
137   if (t->writer == NULL)
138     {
139       free (t);
140       return CMD_CASCADING_FAILURE;
141     }
142
143   add_transformation (ds, output_trns_proc, output_trns_free, t);
144   return CMD_SUCCESS;
145 }
146
147 /* Parses SAVE or XSAVE or EXPORT or XEXPORT command.
148    WRITER_TYPE identifies the type of file to write,
149    and COMMAND_TYPE identifies the type of command.
150
151    On success, returns a writer.
152    For procedures only, sets *RETAIN_UNSELECTED to true if cases
153    that would otherwise be excluded by FILTER or USE should be
154    included.
155
156    On failure, returns a null pointer. */
157 static struct casewriter *
158 parse_write_command (struct lexer *lexer, struct dataset *ds,
159                      enum writer_type writer_type,
160                      enum command_type command_type,
161                      bool *retain_unselected)
162 {
163   /* Common data. */
164   struct file_handle *handle; /* Output file. */
165   struct file_handle *metadata; /* MDD output file. */
166   struct dictionary *dict;    /* Dictionary for output file. */
167   struct casewriter *writer;  /* Writer. */
168   struct case_map_stage *stage; /* Preparation for 'map'. */
169   struct case_map *map;       /* Map from input data to data for writer. */
170   const char *sav_name = "";
171
172   /* Common options. */
173   struct sfm_write_options sysfile_opts;
174   struct pfm_write_options porfile_opts;
175
176   assert (writer_type == SYSFILE_WRITER || writer_type == PORFILE_WRITER);
177   assert (command_type == XFORM_CMD || command_type == PROC_CMD);
178   assert ((retain_unselected != NULL) == (command_type == PROC_CMD));
179
180   if (command_type == PROC_CMD)
181     *retain_unselected = true;
182
183   handle = NULL;
184   metadata = NULL;
185   dict = dict_clone (dataset_dict (ds));
186   writer = NULL;
187   stage = NULL;
188   map = NULL;
189   sysfile_opts = sfm_writer_default_options ();
190   porfile_opts = pfm_writer_default_options ();
191
192   stage = case_map_stage_create (dict);
193   dict_delete_scratch_vars (dict);
194
195   lex_match (lexer, T_SLASH);
196   for (;;)
197     {
198       if (lex_match_id (lexer, "OUTFILE"))
199         {
200           if (handle != NULL)
201             {
202               lex_sbc_only_once ("OUTFILE");
203               goto error;
204             }
205
206           lex_match (lexer, T_EQUALS);
207
208           handle = fh_parse (lexer, FH_REF_FILE, NULL);
209           if (handle == NULL)
210             goto error;
211         }
212       else if (lex_match_id (lexer, "METADATA"))
213         {
214           if (metadata != NULL)
215             {
216               lex_sbc_only_once ("METADATA");
217               goto error;
218             }
219
220           lex_match (lexer, T_EQUALS);
221
222           metadata = fh_parse (lexer, FH_REF_FILE, NULL);
223           if (metadata == NULL)
224             goto error;
225         }
226       else if (lex_match_id (lexer, "NAMES"))
227         {
228           /* Not yet implemented. */
229         }
230       else if (lex_match_id (lexer, "PERMISSIONS"))
231         {
232           bool cw;
233
234           lex_match (lexer, T_EQUALS);
235           if (lex_match_id (lexer, "READONLY"))
236             cw = false;
237           else if (lex_match_id (lexer, "WRITEABLE"))
238             cw = true;
239           else
240             {
241               lex_error_expecting (lexer, "READONLY", "WRITEABLE",
242                                    NULL_SENTINEL);
243               goto error;
244             }
245           sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
246         }
247       else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED"))
248         {
249           lex_match (lexer, T_EQUALS);
250           if (lex_match_id (lexer, "RETAIN"))
251             *retain_unselected = true;
252           else if (lex_match_id (lexer, "DELETE"))
253             *retain_unselected = false;
254           else
255             {
256               lex_error_expecting (lexer, "RETAIN", "DELETE", NULL_SENTINEL);
257               goto error;
258             }
259         }
260       else if (writer_type == SYSFILE_WRITER
261                && lex_match_id (lexer, "COMPRESSED"))
262         sysfile_opts.compression = ANY_COMP_SIMPLE;
263       else if (writer_type == SYSFILE_WRITER
264                && lex_match_id (lexer, "UNCOMPRESSED"))
265         sysfile_opts.compression = ANY_COMP_NONE;
266       else if (writer_type == SYSFILE_WRITER
267                && lex_match_id (lexer, "ZCOMPRESSED"))
268         sysfile_opts.compression = ANY_COMP_ZLIB;
269       else if (writer_type == SYSFILE_WRITER
270                && lex_match_id (lexer, "VERSION"))
271         {
272           lex_match (lexer, T_EQUALS);
273           if (!lex_force_int (lexer))
274             goto error;
275           sysfile_opts.version = lex_integer (lexer);
276           lex_get (lexer);
277         }
278       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
279         {
280           lex_match (lexer, T_EQUALS);
281           if (lex_match_id (lexer, "COMMUNICATIONS"))
282             porfile_opts.type = PFM_COMM;
283           else if (lex_match_id (lexer, "TAPE"))
284             porfile_opts.type = PFM_TAPE;
285           else
286             {
287               lex_error_expecting (lexer, "COMM", "TAPE", NULL_SENTINEL);
288               goto error;
289             }
290         }
291       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
292         {
293           lex_match (lexer, T_EQUALS);
294           if (!lex_force_int (lexer))
295             goto error;
296           porfile_opts.digits = lex_integer (lexer);
297           lex_get (lexer);
298         }
299       else if (!parse_dict_trim (lexer, dict))
300         goto error;
301
302       if (!lex_match (lexer, T_SLASH))
303         break;
304     }
305   if (lex_end_of_command (lexer) != CMD_SUCCESS)
306     goto error;
307
308   if (!handle && !metadata)
309     {
310       msg (SE, _("The OUTFILE or METADATA subcommand is required."));
311       goto error;
312     }
313
314   dict_delete_scratch_vars (dict);
315   dict_compact_values (dict);
316
317   if (handle)
318     {
319       if (metadata)
320         sav_name = (fh_get_referent (handle) == FH_REF_FILE
321                     ? fh_get_file_name (handle)
322                     : fh_get_name (handle));
323       if (fh_get_referent (handle) == FH_REF_FILE)
324         {
325           switch (writer_type)
326             {
327             case SYSFILE_WRITER:
328               writer = sfm_open_writer (handle, dict, sysfile_opts);
329               break;
330             case PORFILE_WRITER:
331               writer = pfm_open_writer (handle, dict, porfile_opts);
332               break;
333             }
334         }
335       else
336         writer = any_writer_open (handle, dict);
337       if (writer == NULL)
338         goto error;
339     }
340
341   if (metadata)
342     {
343       if (!mdd_write (metadata, dict, sav_name))
344         goto error;
345     }
346
347   map = case_map_stage_get_case_map (stage);
348   case_map_stage_destroy (stage);
349   if (map != NULL)
350     writer = case_map_create_output_translator (map, writer);
351   dict_unref (dict);
352
353   fh_unref (handle);
354   fh_unref (metadata);
355   return writer;
356
357  error:
358   case_map_stage_destroy (stage);
359   fh_unref (handle);
360   fh_unref (metadata);
361   casewriter_destroy (writer);
362   dict_unref (dict);
363   case_map_destroy (map);
364   return NULL;
365 }
366
367 /* Writes case *C to the system file specified on XSAVE or XEXPORT. */
368 static int
369 output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
370 {
371   struct output_trns *t = trns_;
372   casewriter_write (t->writer, case_ref (*c));
373   return TRNS_CONTINUE;
374 }
375
376 /* Frees an XSAVE or XEXPORT transformation.
377    Returns true if successful, false if an I/O error occurred. */
378 static bool
379 output_trns_free (void *trns_)
380 {
381   struct output_trns *t = trns_;
382   bool ok = casewriter_destroy (t->writer);
383   free (t);
384   return ok;
385 }