pivot table procedure conceptually works
[pspp] / utilities / pspp-convert.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2013, 2014 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/file-name.h"
30 #include "data/por-file-writer.h"
31 #include "data/settings.h"
32 #include "data/sys-file-encryption.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_sav_file (struct encrypted_sys_file *enc,
50                               const char *input_filename,
51                               const char *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_sys_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   if (output_format == NULL)
139     {
140       const char *dot = strrchr (output_filename, '.');
141       if (dot == NULL)
142         error (1, 0, _("%s: cannot guess output format (use -O option)"),
143                output_filename);
144
145       output_format = dot + 1;
146     }
147
148   if (encrypted_sys_file_open (&enc, input_filename) > 0)
149     {
150       if (strcmp (output_format, "sav") && strcmp (output_format, "sys"))
151         error (1, 0, _("can only convert encrypted data file to sav or sys "
152                        "format"));
153
154       decrypt_sav_file (enc, input_filename, output_filename, password);
155       goto exit;
156     }
157
158   input_fh = fh_create_file (NULL, input_filename, fh_default_properties ());
159   reader = any_reader_open_and_decode (input_fh, encoding, &dict, NULL);
160   if (reader == NULL)
161     exit (1);
162
163   output_fh = fh_create_file (NULL, output_filename, fh_default_properties ());
164   if (!strcmp (output_format, "csv") || !strcmp (output_format, "txt"))
165     {
166       struct csv_writer_options options;
167
168       csv_writer_options_init (&options);
169       options.include_var_names = true;
170       writer = csv_writer_open (output_fh, dict, &options);
171     }
172   else if (!strcmp (output_format, "sav") || !strcmp (output_format, "sys"))
173     {
174       struct sfm_write_options options;
175
176       options = sfm_writer_default_options ();
177       writer = sfm_open_writer (output_fh, dict, options);
178     }
179   else if (!strcmp (output_format, "por"))
180     {
181       struct pfm_write_options options;
182
183       options = pfm_writer_default_options ();
184       writer = pfm_open_writer (output_fh, dict, options);
185     }
186   else
187     {
188       error (1, 0, _("%s: unknown output format (use -O option)"),
189              output_filename);
190       NOT_REACHED ();
191     }
192
193   for (i = 0; i < max_cases; i++)
194     {
195       struct ccase *c;
196
197       c = casereader_read (reader);
198       if (c == NULL)
199         break;
200
201       casewriter_write (writer, c);
202     }
203
204   if (!casereader_destroy (reader))
205     error (1, 0, _("%s: error reading input file"), input_filename);
206   if (!casewriter_destroy (writer))
207     error (1, 0, _("%s: error writing output file"), output_filename);
208
209 exit:
210   fh_done ();
211   i18n_done ();
212
213   return 0;
214 }
215
216 static void
217 decrypt_sav_file (struct encrypted_sys_file *enc,
218                   const char *input_filename,
219                   const char *output_filename,
220                   const char *password)
221 {
222   FILE *out;
223   int err;
224
225   if (password == NULL)
226     {
227       password = getpass ("password: ");
228       if (password == NULL)
229         exit (1);
230     }
231
232   if (!encrypted_sys_file_unlock (enc, password))
233     error (1, 0, _("sorry, wrong password"));
234
235   out = fn_open (output_filename, "wb");
236   if (out == NULL)
237     error (1, errno, ("%s: error opening output file"), output_filename);
238
239   for (;;)
240     {
241       uint8_t buffer[1024];
242       size_t n;
243
244       n = encrypted_sys_file_read (enc, buffer, sizeof buffer);
245       if (n == 0)
246         break;
247
248       if (fwrite (buffer, 1, n, out) != n)
249         error (1, errno, ("%s: write error"), output_filename);
250     }
251
252   err = encrypted_sys_file_close (enc);
253   if (err)
254     error (1, err, ("%s: read error"), input_filename);
255
256   if (fflush (out) == EOF)
257     error (1, errno, ("%s: write error"), output_filename);
258   fn_close (output_filename, out);
259 }
260
261 static void
262 usage (void)
263 {
264   printf ("\
265 %s, a utility for converting SPSS data files to other formats.\n\
266 Usage: %s [OPTION]... INPUT OUTPUT\n\
267 where INPUT is an SPSS system or portable file\n\
268   and OUTPUT is the name of the desired output file.\n\
269 \n\
270 The desired format of OUTPUT is by default inferred from its extension:\n\
271   csv txt             comma-separated value\n\
272   sav sys             SPSS system file\n\
273   por                 SPSS portable file\n\
274 \n\
275 Options:\n\
276   -O, --output-format=FORMAT  set specific output format, where FORMAT\n\
277                       is one of the extensions listed above\n\
278   -e, --encoding=CHARSET  override encoding of input data file\n\
279   -c MAXCASES         limit number of cases to copy (default is all cases)\n\
280   -p PASSWORD         password for encrypted .sav files\n\
281   --help              display this help and exit\n\
282   --version           output version information and exit\n",
283           program_name, program_name);
284 }