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