a5848e59f5ce12f08b5de428df5fb7154cc9ed09
[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               goto error;
243             }
244           sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
245         }
246       else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED"))
247         {
248           lex_match (lexer, T_EQUALS);
249           if (lex_match_id (lexer, "RETAIN"))
250             *retain_unselected = true;
251           else if (lex_match_id (lexer, "DELETE"))
252             *retain_unselected = false;
253           else
254             {
255               lex_error_expecting (lexer, "RETAIN", "DELETE");
256               goto error;
257             }
258         }
259       else if (writer_type == SYSFILE_WRITER
260                && lex_match_id (lexer, "COMPRESSED"))
261         sysfile_opts.compression = ANY_COMP_SIMPLE;
262       else if (writer_type == SYSFILE_WRITER
263                && lex_match_id (lexer, "UNCOMPRESSED"))
264         sysfile_opts.compression = ANY_COMP_NONE;
265       else if (writer_type == SYSFILE_WRITER
266                && lex_match_id (lexer, "ZCOMPRESSED"))
267         sysfile_opts.compression = ANY_COMP_ZLIB;
268       else if (writer_type == SYSFILE_WRITER
269                && lex_match_id (lexer, "VERSION"))
270         {
271           lex_match (lexer, T_EQUALS);
272           if (!lex_force_int_range (lexer, "VERSION", 2, 3))
273             goto error;
274           sysfile_opts.version = lex_integer (lexer);
275           lex_get (lexer);
276         }
277       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
278         {
279           lex_match (lexer, T_EQUALS);
280           if (lex_match_id (lexer, "COMMUNICATIONS"))
281             porfile_opts.type = PFM_COMM;
282           else if (lex_match_id (lexer, "TAPE"))
283             porfile_opts.type = PFM_TAPE;
284           else
285             {
286               lex_error_expecting (lexer, "COMM", "TAPE");
287               goto error;
288             }
289         }
290       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
291         {
292           lex_match (lexer, T_EQUALS);
293           if (!lex_force_int_range (lexer, "DIGITS", 1, INT_MAX))
294             goto error;
295           porfile_opts.digits = lex_integer (lexer);
296           lex_get (lexer);
297         }
298       else if (!parse_dict_trim (lexer, dict, false))
299         goto error;
300
301       if (!lex_match (lexer, T_SLASH))
302         break;
303     }
304   if (lex_end_of_command (lexer) != CMD_SUCCESS)
305     goto error;
306
307   if (!handle && !metadata)
308     {
309       msg (SE, _("The OUTFILE or METADATA subcommand is required."));
310       goto error;
311     }
312
313   dict_delete_scratch_vars (dict);
314   dict_compact_values (dict);
315
316   if (handle)
317     {
318       if (metadata)
319         sav_name = (fh_get_referent (handle) == FH_REF_FILE
320                     ? fh_get_file_name (handle)
321                     : fh_get_name (handle));
322       if (fh_get_referent (handle) == FH_REF_FILE)
323         {
324           switch (writer_type)
325             {
326             case SYSFILE_WRITER:
327               writer = sfm_open_writer (handle, dict, sysfile_opts);
328               break;
329             case PORFILE_WRITER:
330               writer = pfm_open_writer (handle, dict, porfile_opts);
331               break;
332             }
333         }
334       else
335         writer = any_writer_open (handle, dict);
336       if (writer == NULL)
337         goto error;
338     }
339
340   if (metadata)
341     {
342       if (!mdd_write (metadata, dict, sav_name))
343         goto error;
344     }
345
346   map = case_map_stage_get_case_map (stage);
347   case_map_stage_destroy (stage);
348   if (map != NULL)
349     writer = case_map_create_output_translator (map, writer);
350   dict_unref (dict);
351
352   fh_unref (handle);
353   fh_unref (metadata);
354   return writer;
355
356  error:
357   case_map_stage_destroy (stage);
358   fh_unref (handle);
359   fh_unref (metadata);
360   casewriter_destroy (writer);
361   dict_unref (dict);
362   case_map_destroy (map);
363   return NULL;
364 }
365
366 /* Writes case *C to the system file specified on XSAVE or XEXPORT. */
367 static int
368 output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
369 {
370   struct output_trns *t = trns_;
371   casewriter_write (t->writer, case_ref (*c));
372   return TRNS_CONTINUE;
373 }
374
375 /* Frees an XSAVE or XEXPORT transformation.
376    Returns true if successful, false if an I/O error occurred. */
377 static bool
378 output_trns_free (void *trns_)
379 {
380   struct output_trns *t = trns_;
381   bool ok = casewriter_destroy (t->writer);
382   free (t);
383   return ok;
384 }