Merge commit 'origin/stable'
[pspp-builds.git] / src / language / data-io / save.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2009 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/dictionary.h>
27 #include <data/por-file-writer.h>
28 #include <data/procedure.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 "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 variable *saved_filter_variable;
109   struct casewriter *output;
110   bool ok;
111
112   output = parse_write_command (lexer, ds, writer_type, PROC_CMD,
113                                 &retain_unselected);
114   if (output == NULL)
115     return CMD_CASCADING_FAILURE;
116
117   saved_filter_variable = dict_get_filter (dataset_dict (ds));
118   if (retain_unselected)
119     dict_set_filter (dataset_dict (ds), NULL);
120
121   casereader_transfer (proc_open (ds), output);
122   ok = casewriter_destroy (output);
123   ok = proc_commit (ds) && ok;
124
125   dict_set_filter (dataset_dict (ds), saved_filter_variable);
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 dictionary *dict;    /* Dictionary for output file. */
165   struct casewriter *writer;  /* Writer. */
166   struct case_map *map;       /* Map from input data to data for writer. */
167
168   /* Common options. */
169   bool print_map;             /* Print map?  TODO. */
170   bool print_short_names;     /* Print long-to-short name map.  TODO. */
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   dict = dict_clone (dataset_dict (ds));
183   writer = NULL;
184   map = NULL;
185   print_map = false;
186   print_short_names = false;
187   sysfile_opts = sfm_writer_default_options ();
188   porfile_opts = pfm_writer_default_options ();
189
190   case_map_prepare_dict (dict);
191   dict_delete_scratch_vars (dict);
192
193   lex_match (lexer, '/');
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, '=');
205
206           handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
207           if (handle == NULL)
208             goto error;
209         }
210       else if (lex_match_id (lexer, "NAMES"))
211         print_short_names = true;
212       else if (lex_match_id (lexer, "PERMISSIONS"))
213         {
214           bool cw;
215
216           lex_match (lexer, '=');
217           if (lex_match_id (lexer, "READONLY"))
218             cw = false;
219           else if (lex_match_id (lexer, "WRITEABLE"))
220             cw = true;
221           else
222             {
223               lex_error (lexer, _("expecting %s or %s"),
224                          "READONLY", "WRITEABLE");
225               goto error;
226             }
227           sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
228         }
229       else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED"))
230         {
231           lex_match (lexer, '=');
232           if (lex_match_id (lexer, "RETAIN"))
233             *retain_unselected = true;
234           else if (lex_match_id (lexer, "DELETE"))
235             *retain_unselected = false;
236           else
237             {
238               lex_error (lexer, _("expecting %s or %s"), "RETAIN", "DELETE");
239               goto error;
240             }
241         }
242       else if (writer_type == SYSFILE_WRITER
243                && lex_match_id (lexer, "COMPRESSED"))
244         sysfile_opts.compress = true;
245       else if (writer_type == SYSFILE_WRITER
246                && lex_match_id (lexer, "UNCOMPRESSED"))
247         sysfile_opts.compress = false;
248       else if (writer_type == SYSFILE_WRITER
249                && lex_match_id (lexer, "VERSION"))
250         {
251           lex_match (lexer, '=');
252           if (!lex_force_int (lexer))
253             goto error;
254           sysfile_opts.version = lex_integer (lexer);
255           lex_get (lexer);
256         }
257       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
258         {
259           lex_match (lexer, '=');
260           if (lex_match_id (lexer, "COMMUNICATIONS"))
261             porfile_opts.type = PFM_COMM;
262           else if (lex_match_id (lexer, "TAPE"))
263             porfile_opts.type = PFM_TAPE;
264           else
265             {
266               lex_error (lexer, _("expecting %s or %s"), "COMM", "TAPE");
267               goto error;
268             }
269         }
270       else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
271         {
272           lex_match (lexer, '=');
273           if (!lex_force_int (lexer))
274             goto error;
275           porfile_opts.digits = lex_integer (lexer);
276           lex_get (lexer);
277         }
278       else if (!parse_dict_trim (lexer, dict))
279         goto error;
280
281       if (!lex_match (lexer, '/'))
282         break;
283     }
284   if (lex_end_of_command (lexer) != CMD_SUCCESS)
285     goto error;
286
287   if (handle == NULL)
288     {
289       lex_sbc_missing (lexer, "OUTFILE");
290       goto error;
291     }
292
293   dict_delete_scratch_vars (dict);
294   dict_compact_values (dict);
295
296   if (fh_get_referent (handle) == FH_REF_FILE)
297     {
298       switch (writer_type)
299         {
300         case SYSFILE_WRITER:
301           writer = sfm_open_writer (handle, dict, sysfile_opts);
302           break;
303         case PORFILE_WRITER:
304           writer = pfm_open_writer (handle, dict, porfile_opts);
305           break;
306         }
307     }
308   else
309     writer = any_writer_open (handle, dict);
310   if (writer == NULL)
311     goto error;
312
313   map = case_map_from_dict (dict);
314   if (map != NULL)
315     writer = case_map_create_output_translator (map, writer);
316   dict_destroy (dict);
317
318   fh_unref (handle);
319   return writer;
320
321  error:
322   fh_unref (handle);
323   casewriter_destroy (writer);
324   dict_destroy (dict);
325   case_map_destroy (map);
326   return NULL;
327 }
328
329 /* Writes case *C to the system file specified on XSAVE or XEXPORT. */
330 static int
331 output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
332 {
333   struct output_trns *t = trns_;
334   casewriter_write (t->writer, case_ref (*c));
335   return TRNS_CONTINUE;
336 }
337
338 /* Frees an XSAVE or XEXPORT transformation.
339    Returns true if successful, false if an I/O error occurred. */
340 static bool
341 output_trns_free (void *trns_)
342 {
343   struct output_trns *t = trns_;
344   bool ok = casewriter_destroy (t->writer);
345   free (t);
346   return ok;
347 }