1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
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.
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.
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/>. */
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"
41 #include "gl/getpass.h"
42 #include "gl/progname.h"
43 #include "gl/version-etc.h"
46 #define _(msgid) gettext (msgid)
48 static void usage (void);
50 static bool decrypt_file (struct encrypted_file *enc,
51 const struct file_handle *input_filename,
52 const struct file_handle *output_filename,
54 const char *alphabet, int max_length);
57 main (int argc, char *argv[])
59 const char *input_filename;
60 const char *output_filename;
62 long long int max_cases = LLONG_MAX;
63 struct dictionary *dict = NULL;
64 struct casereader *reader;
65 struct file_handle *input_fh = NULL;
66 const char *encoding = NULL;
67 struct encrypted_file *enc;
69 const char *output_format = NULL;
70 struct file_handle *output_fh = NULL;
71 struct casewriter *writer;
72 const char *password = NULL;
73 struct string alphabet = DS_EMPTY_INITIALIZER;
78 set_program_name (argv[0]);
85 static const struct option long_options[] =
87 { "cases", required_argument, NULL, 'c' },
88 { "encoding", required_argument, NULL, 'e' },
90 { "password", required_argument, NULL, 'p' },
91 { "password-alphabet", required_argument, NULL, 'a' },
92 { "password-length", required_argument, NULL, 'l' },
94 { "output-format", required_argument, NULL, 'O' },
96 { "help", no_argument, NULL, 'h' },
97 { "version", no_argument, NULL, 'v' },
103 c = getopt_long (argc, argv, "c:e:p:a:l:O:hv", long_options, NULL);
110 max_cases = strtoull (optarg, NULL, 0);
122 length = atoi (optarg);
126 for (const char *p = optarg; *p; )
127 if (p[1] == '-' && p[2] > p[0])
129 for (int ch = p[0]; ch <= p[2]; ch++)
130 ds_put_byte (&alphabet, ch);
134 ds_put_byte (&alphabet, *p++);
138 output_format = optarg;
142 version_etc (stdout, "pspp-convert", PACKAGE_NAME, PACKAGE_VERSION,
143 "Ben Pfaff", "John Darrington", NULL_SENTINEL);
155 if (optind + 2 != argc)
156 error (1, 0, _("exactly two non-option arguments are required; "
157 "use --help for help"));
159 input_filename = argv[optind];
160 output_filename = argv[optind + 1];
161 input_fh = fh_create_file (NULL, input_filename, NULL, fh_default_properties ());
163 if (output_format == NULL)
165 const char *dot = strrchr (output_filename, '.');
167 error (1, 0, _("%s: cannot guess output format (use -O option)"),
170 output_format = dot + 1;
173 output_fh = fh_create_file (NULL, output_filename, NULL, fh_default_properties ());
174 if (encrypted_file_open (&enc, input_fh) > 0)
176 if (encrypted_file_is_sav (enc))
178 if (strcmp (output_format, "sav") && strcmp (output_format, "sys"))
179 error (1, 0, _("can only convert encrypted data file to sav or "
184 if (strcmp (output_format, "sps"))
185 error (1, 0, _("can only convert encrypted syntax file to sps "
189 if (!decrypt_file (enc, input_fh, output_fh, password,
190 ds_cstr (&alphabet), length))
197 reader = any_reader_open_and_decode (input_fh, encoding, &dict, NULL);
201 if (!strcmp (output_format, "csv") || !strcmp (output_format, "txt"))
203 struct csv_writer_options options;
205 csv_writer_options_init (&options);
206 options.include_var_names = true;
207 writer = csv_writer_open (output_fh, dict, &options);
209 else if (!strcmp (output_format, "sav") || !strcmp (output_format, "sys"))
211 struct sfm_write_options options;
213 options = sfm_writer_default_options ();
214 writer = sfm_open_writer (output_fh, dict, options);
216 else if (!strcmp (output_format, "por"))
218 struct pfm_write_options options;
220 options = pfm_writer_default_options ();
221 writer = pfm_open_writer (output_fh, dict, options);
225 error (1, 0, _("%s: unknown output format (use -O option)"),
230 error (1, 0, _("%s: error opening output file"), output_filename);
232 for (i = 0; i < max_cases; i++)
236 c = casereader_read (reader);
240 casewriter_write (writer, c);
243 if (!casereader_destroy (reader))
244 error (1, 0, _("%s: error reading input file"), input_filename);
245 if (!casewriter_destroy (writer))
246 error (1, 0, _("%s: error writing output file"), output_filename);
249 ds_destroy (&alphabet);
251 fh_unref (output_fh);
259 ds_destroy (&alphabet);
261 fh_unref (output_fh);
270 decrypt_file (struct encrypted_file *enc,
271 const struct file_handle *ifh,
272 const struct file_handle *ofh,
273 const char *password,
274 const char *alphabet,
279 const char *input_filename = fh_get_file_name (ifh);
280 const char *output_filename = fh_get_file_name (ofh);
282 if (alphabet[0] && max_length)
284 size_t alphabet_size = strlen (alphabet);
285 char *pw = xmalloc (max_length + 1);
286 int *indexes = xzalloc (max_length * sizeof *indexes);
288 for (int len = password ? strlen (password) : 0;
289 len <= max_length; len++)
291 if (password && len == strlen (password))
293 for (int i = 0; i < len; i++)
295 const char *p = strchr (alphabet, password[i]);
297 error (1, 0, _("%s: '%c' is not in alphabet"),
298 password, password[i]);
299 indexes[i] = p - alphabet;
305 memset (indexes, 0, len * sizeof *indexes);
306 for (int i = 0; i < len; i++)
311 unsigned int target = 0;
312 for (unsigned int j = 0; ; j++)
317 if (isatty (STDOUT_FILENO))
319 printf ("\rlength %d: %s", len, pw);
323 if (encrypted_file_unlock__ (enc, pw))
325 printf ("\npassword is: \"%s\"\n", pw);
331 for (i = 0; i < len; i++)
332 if (++indexes[i] < alphabet_size)
334 pw[i] = alphabet[indexes[i]];
340 pw[i] = alphabet[indexes[i]];
353 if (password == NULL)
355 password = getpass ("password: ");
356 if (password == NULL)
360 if (!encrypted_file_unlock (enc, password))
361 error (1, 0, _("sorry, wrong password"));
364 out = fn_open (ofh, "wb");
366 error (1, errno, ("%s: error opening output file"), output_filename);
370 uint8_t buffer[1024];
373 n = encrypted_file_read (enc, buffer, sizeof buffer);
377 if (fwrite (buffer, 1, n, out) != n)
378 error (1, errno, ("%s: write error"), output_filename);
381 err = encrypted_file_close (enc);
383 error (1, err, ("%s: read error"), input_filename);
385 if (fflush (out) == EOF)
386 error (1, errno, ("%s: write error"), output_filename);
396 %s, a utility for converting SPSS data files to other formats.\n\
397 Usage: %s [OPTION]... INPUT OUTPUT\n\
398 where INPUT is an SPSS data file or encrypted syntax file\n\
399 and OUTPUT is the name of the desired output file.\n\
401 The desired format of OUTPUT is by default inferred from its extension:\n\
402 csv txt comma-separated value\n\
403 sav sys SPSS system file\n\
404 por SPSS portable file\n\
405 sps SPSS syntax file (encrypted syntax input files only)\n\
408 -O, --output-format=FORMAT set specific output format, where FORMAT\n\
409 is one of the extensions listed above\n\
410 -e, --encoding=CHARSET override encoding of input data file\n\
411 -c MAXCASES limit number of cases to copy (default is all cases)\n\
412 -p PASSWORD password for encrypted files\n\
413 --help display this help and exit\n\
414 --version output version information and exit\n",
415 program_name, program_name);