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