Fixed typo in comment
[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/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.compression = ANY_COMP_SIMPLE;
238       else if (writer_type == SYSFILE_WRITER
239                && lex_match_id (lexer, "UNCOMPRESSED"))
240         sysfile_opts.compression = ANY_COMP_NONE;
241       else if (writer_type == SYSFILE_WRITER
242                && lex_match_id (lexer, "ZCOMPRESSED"))
243         sysfile_opts.compression = ANY_COMP_ZLIB;
244       else if (writer_type == SYSFILE_WRITER
245                && lex_match_id (lexer, "VERSION"))
246         {
247           lex_match (lexer, T_EQUALS);
248           if (!lex_force_int (lexer))
249             goto error;
250           sysfile_opts.version = lex_integer (lexer);
251           lex_get (lexer);
252         }
253       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
254         {
255           lex_match (lexer, T_EQUALS);
256           if (lex_match_id (lexer, "COMMUNICATIONS"))
257             porfile_opts.type = PFM_COMM;
258           else if (lex_match_id (lexer, "TAPE"))
259             porfile_opts.type = PFM_TAPE;
260           else
261             {
262               lex_error_expecting (lexer, "COMM", "TAPE", NULL_SENTINEL);
263               goto error;
264             }
265         }
266       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
267         {
268           lex_match (lexer, T_EQUALS);
269           if (!lex_force_int (lexer))
270             goto error;
271           porfile_opts.digits = lex_integer (lexer);
272           lex_get (lexer);
273         }
274       else if (!parse_dict_trim (lexer, dict))
275         goto error;
276
277       if (!lex_match (lexer, T_SLASH))
278         break;
279     }
280   if (lex_end_of_command (lexer) != CMD_SUCCESS)
281     goto error;
282
283   if (handle == NULL)
284     {
285       lex_sbc_missing ("OUTFILE");
286       goto error;
287     }
288
289   dict_delete_scratch_vars (dict);
290   dict_compact_values (dict);
291
292   if (fh_get_referent (handle) == FH_REF_FILE)
293     {
294       switch (writer_type)
295         {
296         case SYSFILE_WRITER:
297           writer = sfm_open_writer (handle, dict, sysfile_opts);
298           break;
299         case PORFILE_WRITER:
300           writer = pfm_open_writer (handle, dict, porfile_opts);
301           break;
302         }
303     }
304   else
305     writer = any_writer_open (handle, dict);
306   if (writer == NULL)
307     goto error;
308
309   map = case_map_stage_get_case_map (stage);
310   case_map_stage_destroy (stage);
311   if (map != NULL)
312     writer = case_map_create_output_translator (map, writer);
313   dict_destroy (dict);
314
315   fh_unref (handle);
316   return writer;
317
318  error:
319   case_map_stage_destroy (stage);
320   fh_unref (handle);
321   casewriter_destroy (writer);
322   dict_destroy (dict);
323   case_map_destroy (map);
324   return NULL;
325 }
326
327 /* Writes case *C to the system file specified on XSAVE or XEXPORT. */
328 static int
329 output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
330 {
331   struct output_trns *t = trns_;
332   casewriter_write (t->writer, case_ref (*c));
333   return TRNS_CONTINUE;
334 }
335
336 /* Frees an XSAVE or XEXPORT transformation.
337    Returns true if successful, false if an I/O error occurred. */
338 static bool
339 output_trns_free (void *trns_)
340 {
341   struct output_trns *t = trns_;
342   bool ok = casewriter_destroy (t->writer);
343   free (t);
344   return ok;
345 }