Fix memory leak 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;
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   fh_unref (output_fh);
224   fh_unref (input_fh);
225   fh_done ();
226   i18n_done ();
227
228   return 0;
229
230 error:
231   fh_unref (output_fh);
232   fh_unref (input_fh);
233   fh_done ();
234   i18n_done ();
235
236   return 1;
237 }
238
239 static bool
240 decrypt_file (struct encrypted_file *enc,
241               const struct file_handle *ifh,
242               const struct file_handle *ofh,
243               const char *password)
244 {
245   FILE *out;
246   int err;
247   const char *input_filename = fh_get_file_name (ifh);
248   const char *output_filename = fh_get_file_name (ofh);
249
250   if (password == NULL)
251     {
252       password = getpass ("password: ");
253       if (password == NULL)
254         return false;
255     }
256
257   if (!encrypted_file_unlock (enc, password))
258     error (1, 0, _("sorry, wrong password"));
259
260   out = fn_open (ofh, "wb");
261   if (out == NULL)
262     error (1, errno, ("%s: error opening output file"), output_filename);
263
264   for (;;)
265     {
266       uint8_t buffer[1024];
267       size_t n;
268
269       n = encrypted_file_read (enc, buffer, sizeof buffer);
270       if (n == 0)
271         break;
272
273       if (fwrite (buffer, 1, n, out) != n)
274         error (1, errno, ("%s: write error"), output_filename);
275     }
276
277   err = encrypted_file_close (enc);
278   if (err)
279     error (1, err, ("%s: read error"), input_filename);
280
281   if (fflush (out) == EOF)
282     error (1, errno, ("%s: write error"), output_filename);
283   fn_close (ofh, out);
284
285   return true;
286 }
287
288 static void
289 usage (void)
290 {
291   printf ("\
292 %s, a utility for converting SPSS data files to other formats.\n\
293 Usage: %s [OPTION]... INPUT OUTPUT\n\
294 where INPUT is an SPSS data file or encrypted syntax file\n\
295   and OUTPUT is the name of the desired output file.\n\
296 \n\
297 The desired format of OUTPUT is by default inferred from its extension:\n\
298   csv txt             comma-separated value\n\
299   sav sys             SPSS system file\n\
300   por                 SPSS portable file\n\
301   sps                 SPSS syntax file (encrypted syntax input files only)\n\
302 \n\
303 Options:\n\
304   -O, --output-format=FORMAT  set specific output format, where FORMAT\n\
305                       is one of the extensions listed above\n\
306   -e, --encoding=CHARSET  override encoding of input data file\n\
307   -c MAXCASES         limit number of cases to copy (default is all cases)\n\
308   -p PASSWORD         password for encrypted files\n\
309   --help              display this help and exit\n\
310   --version           output version information and exit\n",
311           program_name, program_name);
312 }