Fix memory leak in pspp-convert
[pspp] / utilities / pspp-convert.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2013, 2014, 2015 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 <errno.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include "data/any-reader.h"
26 #include "data/casereader.h"
27 #include "data/casewriter.h"
28 #include "data/csv-file-writer.h"
29 #include "data/encrypted-file.h"
30 #include "data/file-name.h"
31 #include "data/por-file-writer.h"
32 #include "data/settings.h"
33 #include "data/sys-file-writer.h"
34 #include "data/file-handle-def.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/cast.h"
37 #include "libpspp/i18n.h"
38
39 #include "gl/error.h"
40 #include "gl/getpass.h"
41 #include "gl/progname.h"
42 #include "gl/version-etc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46
47 static void usage (void);
48
49 static bool decrypt_file (struct encrypted_file *enc,
50                           const struct file_handle *input_filename,
51                           const struct file_handle *output_filename,
52                           const char *password);
53
54 int
55 main (int argc, char *argv[])
56 {
57   const char *input_filename;
58   const char *output_filename;
59
60   long long int max_cases = LLONG_MAX;
61   struct dictionary *dict = NULL;
62   struct casereader *reader;
63   struct file_handle *input_fh;
64   const char *encoding = NULL;
65   struct encrypted_file *enc;
66
67   const char *output_format = NULL;
68   struct file_handle *output_fh;
69   struct casewriter *writer;
70   const char *password = NULL;
71
72   long long int i;
73
74   set_program_name (argv[0]);
75   i18n_init ();
76   fh_init ();
77   settings_init ();
78
79   for (;;)
80     {
81       static const struct option long_options[] =
82         {
83           { "cases",    required_argument, NULL, 'c' },
84           { "encoding", required_argument, NULL, 'e' },
85           { "password", required_argument, NULL, 'p' },
86
87           { "output-format", required_argument, NULL, 'O' },
88
89           { "help",    no_argument,       NULL, 'h' },
90           { "version", no_argument,       NULL, 'v' },
91           { NULL,      0,                 NULL, 0 },
92         };
93
94       int c;
95
96       c = getopt_long (argc, argv, "c:e:p:O:hv", long_options, NULL);
97       if (c == -1)
98         break;
99
100       switch (c)
101         {
102         case 'c':
103           max_cases = strtoull (optarg, NULL, 0);
104           break;
105
106         case 'e':
107           encoding = optarg;
108           break;
109
110         case 'p':
111           password = optarg;
112           break;
113
114         case 'O':
115           output_format = optarg;
116           break;
117
118         case 'v':
119           version_etc (stdout, "pspp-convert", PACKAGE_NAME, PACKAGE_VERSION,
120                        "Ben Pfaff", "John Darrington", NULL_SENTINEL);
121           exit (EXIT_SUCCESS);
122
123         case 'h':
124           usage ();
125           exit (EXIT_SUCCESS);
126
127         default:
128           goto error;
129         }
130     }
131
132   if (optind + 2 != argc)
133     error (1, 0, _("exactly two non-option arguments are required; "
134                    "use --help for help"));
135
136   input_filename = argv[optind];
137   output_filename = argv[optind + 1];
138   input_fh = fh_create_file (NULL, input_filename, NULL, fh_default_properties ());
139
140   if (output_format == NULL)
141     {
142       const char *dot = strrchr (output_filename, '.');
143       if (dot == NULL)
144         error (1, 0, _("%s: cannot guess output format (use -O option)"),
145                output_filename);
146
147       output_format = dot + 1;
148     }
149
150   output_fh = fh_create_file (NULL, output_filename, NULL, fh_default_properties ());
151   if (encrypted_file_open (&enc, input_fh) > 0)
152     {
153       if (encrypted_file_is_sav (enc))
154         {
155           if (strcmp (output_format, "sav") && strcmp (output_format, "sys"))
156             error (1, 0, _("can only convert encrypted data file to sav or "
157                            "sys format"));
158         }
159       else
160         {
161           if (strcmp (output_format, "sps"))
162             error (1, 0, _("can only convert encrypted syntax file to sps "
163                            "format"));
164         }
165
166       if (! decrypt_file (enc, input_fh, output_fh, password))
167         goto error;
168           
169       goto exit;
170     }
171
172
173   reader = any_reader_open_and_decode (input_fh, encoding, &dict, NULL);
174   if (reader == NULL)
175     goto error;
176
177   if (!strcmp (output_format, "csv") || !strcmp (output_format, "txt"))
178     {
179       struct csv_writer_options options;
180
181       csv_writer_options_init (&options);
182       options.include_var_names = true;
183       writer = csv_writer_open (output_fh, dict, &options);
184     }
185   else if (!strcmp (output_format, "sav") || !strcmp (output_format, "sys"))
186     {
187       struct sfm_write_options options;
188
189       options = sfm_writer_default_options ();
190       writer = sfm_open_writer (output_fh, dict, options);
191     }
192   else if (!strcmp (output_format, "por"))
193     {
194       struct pfm_write_options options;
195
196       options = pfm_writer_default_options ();
197       writer = pfm_open_writer (output_fh, dict, options);
198     }
199   else
200     {
201       error (1, 0, _("%s: unknown output format (use -O option)"),
202              output_filename);
203       NOT_REACHED ();
204     }
205
206   for (i = 0; i < max_cases; i++)
207     {
208       struct ccase *c;
209
210       c = casereader_read (reader);
211       if (c == NULL)
212         break;
213
214       casewriter_write (writer, c);
215     }
216
217   if (!casereader_destroy (reader))
218     error (1, 0, _("%s: error reading input file"), input_filename);
219   if (!casewriter_destroy (writer))
220     error (1, 0, _("%s: error writing output file"), output_filename);
221
222 exit:
223   dict_destroy (dict);
224   fh_unref (output_fh);
225   fh_unref (input_fh);
226   fh_done ();
227   i18n_done ();
228
229   return 0;
230
231 error:
232   dict_destroy (dict);
233   fh_unref (output_fh);
234   fh_unref (input_fh);
235   fh_done ();
236   i18n_done ();
237
238   return 1;
239 }
240
241 static bool
242 decrypt_file (struct encrypted_file *enc,
243               const struct file_handle *ifh,
244               const struct file_handle *ofh,
245               const char *password)
246 {
247   FILE *out;
248   int err;
249   const char *input_filename = fh_get_file_name (ifh);
250   const char *output_filename = fh_get_file_name (ofh);
251
252   if (password == NULL)
253     {
254       password = getpass ("password: ");
255       if (password == NULL)
256         return false;
257     }
258
259   if (!encrypted_file_unlock (enc, password))
260     error (1, 0, _("sorry, wrong password"));
261
262   out = fn_open (ofh, "wb");
263   if (out == NULL)
264     error (1, errno, ("%s: error opening output file"), output_filename);
265
266   for (;;)
267     {
268       uint8_t buffer[1024];
269       size_t n;
270
271       n = encrypted_file_read (enc, buffer, sizeof buffer);
272       if (n == 0)
273         break;
274
275       if (fwrite (buffer, 1, n, out) != n)
276         error (1, errno, ("%s: write error"), output_filename);
277     }
278
279   err = encrypted_file_close (enc);
280   if (err)
281     error (1, err, ("%s: read error"), input_filename);
282
283   if (fflush (out) == EOF)
284     error (1, errno, ("%s: write error"), output_filename);
285   fn_close (ofh, out);
286
287   return true;
288 }
289
290 static void
291 usage (void)
292 {
293   printf ("\
294 %s, a utility for converting SPSS data files to other formats.\n\
295 Usage: %s [OPTION]... INPUT OUTPUT\n\
296 where INPUT is an SPSS data file or encrypted syntax file\n\
297   and OUTPUT is the name of the desired output file.\n\
298 \n\
299 The desired format of OUTPUT is by default inferred from its extension:\n\
300   csv txt             comma-separated value\n\
301   sav sys             SPSS system file\n\
302   por                 SPSS portable file\n\
303   sps                 SPSS syntax file (encrypted syntax input files only)\n\
304 \n\
305 Options:\n\
306   -O, --output-format=FORMAT  set specific output format, where FORMAT\n\
307                       is one of the extensions listed above\n\
308   -e, --encoding=CHARSET  override encoding of input data file\n\
309   -c MAXCASES         limit number of cases to copy (default is all cases)\n\
310   -p PASSWORD         password for encrypted files\n\
311   --help              display this help and exit\n\
312   --version           output version information and exit\n",
313           program_name, program_name);
314 }