work on SAVE DATA COLLECTION
[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
40 #include "gl/xalloc.h"
41
42 #include "gettext.h"
43 #define _(msgid) gettext (msgid)
44
45 /* Writing system and portable files. */
46
47 /* Type of output file. */
48 enum writer_type
49   {
50     SYSFILE_WRITER,     /* System file. */
51     PORFILE_WRITER      /* Portable file. */
52   };
53
54 /* Type of a command. */
55 enum command_type
56   {
57     XFORM_CMD,          /* Transformation. */
58     PROC_CMD            /* Procedure. */
59   };
60
61 static int parse_output_proc (struct lexer *, struct dataset *,
62                               enum writer_type);
63 static int parse_output_trns (struct lexer *, struct dataset *,
64                               enum writer_type);
65
66 int
67 cmd_save (struct lexer *lexer, struct dataset *ds)
68 {
69   return parse_output_proc (lexer, ds, SYSFILE_WRITER);
70 }
71
72 int
73 cmd_save_data_collection (struct lexer *lexer, struct dataset *ds)
74 {
75   return parse_output_proc (lexer, ds, SYSFILE_WRITER);
76 }
77
78 int
79 cmd_export (struct lexer *lexer, struct dataset *ds)
80 {
81   return parse_output_proc (lexer, ds, PORFILE_WRITER);
82 }
83
84 int
85 cmd_xsave (struct lexer *lexer, struct dataset *ds)
86 {
87   return parse_output_trns (lexer, ds, SYSFILE_WRITER);
88 }
89
90 int
91 cmd_xexport (struct lexer *lexer, struct dataset *ds)
92 {
93   return parse_output_trns (lexer, ds, PORFILE_WRITER);
94 }
95 \f
96 struct output_trns
97   {
98     struct casewriter *writer;          /* Writer. */
99   };
100
101 static trns_proc_func output_trns_proc;
102 static trns_free_func output_trns_free;
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_proc, output_trns_free, 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
170   /* Common options. */
171   struct sfm_write_options sysfile_opts;
172   struct pfm_write_options porfile_opts;
173
174   assert (writer_type == SYSFILE_WRITER || writer_type == PORFILE_WRITER);
175   assert (command_type == XFORM_CMD || command_type == PROC_CMD);
176   assert ((retain_unselected != NULL) == (command_type == PROC_CMD));
177
178   if (command_type == PROC_CMD)
179     *retain_unselected = true;
180
181   handle = NULL;
182   metadata = NULL;
183   dict = dict_clone (dataset_dict (ds));
184   writer = NULL;
185   stage = NULL;
186   map = NULL;
187   sysfile_opts = sfm_writer_default_options ();
188   porfile_opts = pfm_writer_default_options ();
189
190   stage = case_map_stage_create (dict);
191   dict_delete_scratch_vars (dict);
192
193   lex_match (lexer, T_SLASH);
194   for (;;)
195     {
196       if (lex_match_id (lexer, "OUTFILE"))
197         {
198           if (handle != NULL)
199             {
200               lex_sbc_only_once ("OUTFILE");
201               goto error;
202             }
203
204           lex_match (lexer, T_EQUALS);
205
206           handle = fh_parse (lexer, FH_REF_FILE, NULL);
207           if (handle == NULL)
208             goto error;
209         }
210       else if (lex_match_id (lexer, "METADATA"))
211         {
212           if (metadata != NULL)
213             {
214               lex_sbc_only_once ("METADATA");
215               goto error;
216             }
217
218           lex_match (lexer, T_EQUALS);
219
220           metadata = fh_parse (lexer, FH_REF_FILE, NULL);
221           if (metadata == NULL)
222             goto error;
223         }
224       else if (lex_match_id (lexer, "NAMES"))
225         {
226           /* Not yet implemented. */
227         }
228       else if (lex_match_id (lexer, "PERMISSIONS"))
229         {
230           bool cw;
231
232           lex_match (lexer, T_EQUALS);
233           if (lex_match_id (lexer, "READONLY"))
234             cw = false;
235           else if (lex_match_id (lexer, "WRITEABLE"))
236             cw = true;
237           else
238             {
239               lex_error_expecting (lexer, "READONLY", "WRITEABLE",
240                                    NULL_SENTINEL);
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", NULL_SENTINEL);
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 (lexer))
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", NULL_SENTINEL);
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 (lexer))
293             goto error;
294           porfile_opts.digits = lex_integer (lexer);
295           lex_get (lexer);
296         }
297       else if (!parse_dict_trim (lexer, dict))
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 == NULL)
307     {
308       lex_sbc_missing ("OUTFILE");
309       goto error;
310     }
311
312   dict_delete_scratch_vars (dict);
313   dict_compact_values (dict);
314
315   if (fh_get_referent (handle) == FH_REF_FILE)
316     {
317       switch (writer_type)
318         {
319         case SYSFILE_WRITER:
320           writer = sfm_open_writer (handle, dict, sysfile_opts);
321           break;
322         case PORFILE_WRITER:
323           writer = pfm_open_writer (handle, dict, porfile_opts);
324           break;
325         }
326     }
327   else
328     writer = any_writer_open (handle, dict);
329   if (writer == NULL)
330     goto error;
331
332   if (metadata)
333     {
334       const char *sav_name = (fh_get_referent (handle) == FH_REF_FILE
335                               ? fh_get_file_name (handle)
336                               : fh_get_name (handle));
337       if (!mdd_write (metadata, dict, sav_name))
338         goto error;
339     }
340
341   map = case_map_stage_get_case_map (stage);
342   case_map_stage_destroy (stage);
343   if (map != NULL)
344     writer = case_map_create_output_translator (map, writer);
345   dict_destroy (dict);
346
347   fh_unref (handle);
348   fh_unref (metadata);
349   return writer;
350
351  error:
352   case_map_stage_destroy (stage);
353   fh_unref (handle);
354   fh_unref (metadata);
355   casewriter_destroy (writer);
356   dict_destroy (dict);
357   case_map_destroy (map);
358   return NULL;
359 }
360
361 /* Writes case *C to the system file specified on XSAVE or XEXPORT. */
362 static int
363 output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
364 {
365   struct output_trns *t = trns_;
366   casewriter_write (t->writer, case_ref (*c));
367   return TRNS_CONTINUE;
368 }
369
370 /* Frees an XSAVE or XEXPORT transformation.
371    Returns true if successful, false if an I/O error occurred. */
372 static bool
373 output_trns_free (void *trns_)
374 {
375   struct output_trns *t = trns_;
376   bool ok = casewriter_destroy (t->writer);
377   free (t);
378   return ok;
379 }