ce5fd875eace83ad57692bef1f8e11ee9fb25bb9
[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 void 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           exit (EXIT_FAILURE);
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       decrypt_file (enc, input_fh, output_fh, password);
167       goto exit;
168     }
169
170
171   reader = any_reader_open_and_decode (input_fh, encoding, &dict, NULL);
172   if (reader == NULL)
173     exit (1);
174
175
176   if (!strcmp (output_format, "csv") || !strcmp (output_format, "txt"))
177     {
178       struct csv_writer_options options;
179
180       csv_writer_options_init (&options);
181       options.include_var_names = true;
182       writer = csv_writer_open (output_fh, dict, &options);
183     }
184   else if (!strcmp (output_format, "sav") || !strcmp (output_format, "sys"))
185     {
186       struct sfm_write_options options;
187
188       options = sfm_writer_default_options ();
189       writer = sfm_open_writer (output_fh, dict, options);
190     }
191   else if (!strcmp (output_format, "por"))
192     {
193       struct pfm_write_options options;
194
195       options = pfm_writer_default_options ();
196       writer = pfm_open_writer (output_fh, dict, options);
197     }
198   else
199     {
200       error (1, 0, _("%s: unknown output format (use -O option)"),
201              output_filename);
202       NOT_REACHED ();
203     }
204
205   for (i = 0; i < max_cases; i++)
206     {
207       struct ccase *c;
208
209       c = casereader_read (reader);
210       if (c == NULL)
211         break;
212
213       casewriter_write (writer, c);
214     }
215
216   if (!casereader_destroy (reader))
217     error (1, 0, _("%s: error reading input file"), input_filename);
218   if (!casewriter_destroy (writer))
219     error (1, 0, _("%s: error writing output file"), output_filename);
220
221 exit:
222   fh_done ();
223   i18n_done ();
224
225   return 0;
226 }
227
228 static void
229 decrypt_file (struct encrypted_file *enc,
230               const struct file_handle *ifh,
231               const struct file_handle *ofh,
232               const char *password)
233 {
234   FILE *out;
235   int err;
236   const char *input_filename = fh_get_file_name (ifh);
237   const char *output_filename = fh_get_file_name (ofh);
238
239   if (password == NULL)
240     {
241       password = getpass ("password: ");
242       if (password == NULL)
243         exit (1);
244     }
245
246   if (!encrypted_file_unlock (enc, password))
247     error (1, 0, _("sorry, wrong password"));
248
249   out = fn_open (ofh, "wb");
250   if (out == NULL)
251     error (1, errno, ("%s: error opening output file"), output_filename);
252
253   for (;;)
254     {
255       uint8_t buffer[1024];
256       size_t n;
257
258       n = encrypted_file_read (enc, buffer, sizeof buffer);
259       if (n == 0)
260         break;
261
262       if (fwrite (buffer, 1, n, out) != n)
263         error (1, errno, ("%s: write error"), output_filename);
264     }
265
266   err = encrypted_file_close (enc);
267   if (err)
268     error (1, err, ("%s: read error"), input_filename);
269
270   if (fflush (out) == EOF)
271     error (1, errno, ("%s: write error"), output_filename);
272   fn_close (ofh, out);
273 }
274
275 static void
276 usage (void)
277 {
278   printf ("\
279 %s, a utility for converting SPSS data files to other formats.\n\
280 Usage: %s [OPTION]... INPUT OUTPUT\n\
281 where INPUT is an SPSS data file or encrypted syntax file\n\
282   and OUTPUT is the name of the desired output file.\n\
283 \n\
284 The desired format of OUTPUT is by default inferred from its extension:\n\
285   csv txt             comma-separated value\n\
286   sav sys             SPSS system file\n\
287   por                 SPSS portable file\n\
288   sps                 SPSS syntax file (encrypted syntax input files only)\n\
289 \n\
290 Options:\n\
291   -O, --output-format=FORMAT  set specific output format, where FORMAT\n\
292                       is one of the extensions listed above\n\
293   -e, --encoding=CHARSET  override encoding of input data file\n\
294   -c MAXCASES         limit number of cases to copy (default is all cases)\n\
295   -p PASSWORD         password for encrypted files\n\
296   --help              display this help and exit\n\
297   --version           output version information and exit\n",
298           program_name, program_name);
299 }