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,
55 const char *password_list);
58 main (int argc, char *argv[])
60 const char *input_filename;
61 const char *output_filename;
63 long long int max_cases = LLONG_MAX;
64 struct dictionary *dict = NULL;
65 struct casereader *reader;
66 struct file_handle *input_fh = NULL;
67 const char *encoding = NULL;
68 struct encrypted_file *enc;
70 const char *output_format = NULL;
71 struct file_handle *output_fh = NULL;
72 struct casewriter *writer;
73 const char *password = NULL;
74 struct string alphabet = DS_EMPTY_INITIALIZER;
75 const char *password_list = NULL;
80 set_program_name (argv[0]);
89 OPT_PASSWORD_LIST = UCHAR_MAX + 1,
91 static const struct option long_options[] =
93 { "cases", required_argument, NULL, 'c' },
94 { "encoding", required_argument, NULL, 'e' },
96 { "password", required_argument, NULL, 'p' },
97 { "password-alphabet", required_argument, NULL, 'a' },
98 { "password-length", required_argument, NULL, 'l' },
99 { "password-list", required_argument, NULL, OPT_PASSWORD_LIST },
101 { "output-format", required_argument, NULL, 'O' },
103 { "help", no_argument, NULL, 'h' },
104 { "version", no_argument, NULL, 'v' },
105 { NULL, 0, NULL, 0 },
110 c = getopt_long (argc, argv, "c:e:p:a:l:O:hv", long_options, NULL);
117 max_cases = strtoull (optarg, NULL, 0);
129 length = atoi (optarg);
132 case OPT_PASSWORD_LIST:
133 password_list = optarg;
137 for (const char *p = optarg; *p; )
138 if (p[1] == '-' && p[2] > p[0])
140 for (int ch = p[0]; ch <= p[2]; ch++)
141 ds_put_byte (&alphabet, ch);
145 ds_put_byte (&alphabet, *p++);
149 output_format = optarg;
153 version_etc (stdout, "pspp-convert", PACKAGE_NAME, PACKAGE_VERSION,
154 "Ben Pfaff", "John Darrington", NULL_SENTINEL);
166 if (optind + 2 != argc)
167 error (1, 0, _("exactly two non-option arguments are required; "
168 "use --help for help"));
170 input_filename = argv[optind];
171 output_filename = argv[optind + 1];
172 input_fh = fh_create_file (NULL, input_filename, NULL, fh_default_properties ());
174 if (output_format == NULL)
176 const char *dot = strrchr (output_filename, '.');
178 error (1, 0, _("%s: cannot guess output format (use -O option)"),
181 output_format = dot + 1;
184 output_fh = fh_create_file (NULL, output_filename, NULL, fh_default_properties ());
185 if (encrypted_file_open (&enc, input_fh) > 0)
187 if (encrypted_file_is_sav (enc))
189 if (strcmp (output_format, "sav") && strcmp (output_format, "sys"))
190 error (1, 0, _("can only convert encrypted data file to sav or "
195 if (strcmp (output_format, "sps"))
196 error (1, 0, _("can only convert encrypted syntax file to sps "
200 if (!decrypt_file (enc, input_fh, output_fh, password,
201 ds_cstr (&alphabet), length, password_list))
208 reader = any_reader_open_and_decode (input_fh, encoding, &dict, NULL);
212 if (!strcmp (output_format, "csv") || !strcmp (output_format, "txt"))
214 struct csv_writer_options options;
216 csv_writer_options_init (&options);
217 options.include_var_names = true;
218 writer = csv_writer_open (output_fh, dict, &options);
220 else if (!strcmp (output_format, "sav") || !strcmp (output_format, "sys"))
222 struct sfm_write_options options;
224 options = sfm_writer_default_options ();
225 writer = sfm_open_writer (output_fh, dict, options);
227 else if (!strcmp (output_format, "por"))
229 struct pfm_write_options options;
231 options = pfm_writer_default_options ();
232 writer = pfm_open_writer (output_fh, dict, options);
236 error (1, 0, _("%s: unknown output format (use -O option)"),
241 error (1, 0, _("%s: error opening output file"), output_filename);
243 for (i = 0; i < max_cases; i++)
247 c = casereader_read (reader);
251 casewriter_write (writer, c);
254 if (!casereader_destroy (reader))
255 error (1, 0, _("%s: error reading input file"), input_filename);
256 if (!casewriter_destroy (writer))
257 error (1, 0, _("%s: error writing output file"), output_filename);
260 ds_destroy (&alphabet);
262 fh_unref (output_fh);
270 ds_destroy (&alphabet);
272 fh_unref (output_fh);
281 decrypt_file (struct encrypted_file *enc,
282 const struct file_handle *ifh,
283 const struct file_handle *ofh,
284 const char *password,
285 const char *alphabet,
287 const char *password_list)
291 const char *input_filename = fh_get_file_name (ifh);
292 const char *output_filename = fh_get_file_name (ofh);
297 if (!strcmp (password_list, "-"))
298 password_file = stdin;
301 password_file = fopen (password_list, "r");
303 error (1, errno, _("%s: error opening password file"),
307 struct string pw = DS_EMPTY_INITIALIZER;
308 unsigned int target = 100000;
309 for (unsigned int i = 0; ; i++)
312 if (!ds_read_line (&pw, password_file, SIZE_MAX))
314 if (isatty (STDOUT_FILENO))
319 error (1, 0, _("\n%s: password not in file"), password_list);
321 ds_chomp_byte (&pw, '\n');
326 if (isatty (STDOUT_FILENO))
333 if (encrypted_file_unlock__ (enc, ds_cstr (&pw)))
335 printf ("\npassword is: \"%s\"\n", ds_cstr (&pw));
336 password = ds_cstr (&pw);
341 else if (alphabet[0] && max_length)
343 size_t alphabet_size = strlen (alphabet);
344 char *pw = xmalloc (max_length + 1);
345 int *indexes = xzalloc (max_length * sizeof *indexes);
347 for (int len = password ? strlen (password) : 0;
348 len <= max_length; len++)
350 if (password && len == strlen (password))
352 for (int i = 0; i < len; i++)
354 const char *p = strchr (alphabet, password[i]);
356 error (1, 0, _("%s: '%c' is not in alphabet"),
357 password, password[i]);
358 indexes[i] = p - alphabet;
364 memset (indexes, 0, len * sizeof *indexes);
365 for (int i = 0; i < len; i++)
370 unsigned int target = 0;
371 for (unsigned int j = 0; ; j++)
376 if (isatty (STDOUT_FILENO))
378 printf ("\rlength %d: %s", len, pw);
382 if (encrypted_file_unlock__ (enc, pw))
384 printf ("\npassword is: \"%s\"\n", pw);
390 for (i = 0; i < len; i++)
391 if (++indexes[i] < alphabet_size)
393 pw[i] = alphabet[indexes[i]];
399 pw[i] = alphabet[indexes[i]];
412 if (password == NULL)
414 password = getpass ("password: ");
415 if (password == NULL)
419 if (!encrypted_file_unlock (enc, password))
420 error (1, 0, _("sorry, wrong password"));
423 out = fn_open (ofh, "wb");
425 error (1, errno, ("%s: error opening output file"), output_filename);
429 uint8_t buffer[1024];
432 n = encrypted_file_read (enc, buffer, sizeof buffer);
436 if (fwrite (buffer, 1, n, out) != n)
437 error (1, errno, ("%s: write error"), output_filename);
440 err = encrypted_file_close (enc);
442 error (1, err, ("%s: read error"), input_filename);
444 if (fflush (out) == EOF)
445 error (1, errno, ("%s: write error"), output_filename);
455 %s, a utility for converting SPSS data files to other formats.\n\
456 Usage: %s [OPTION]... INPUT OUTPUT\n\
457 where INPUT is an SPSS data file or encrypted syntax file\n\
458 and OUTPUT is the name of the desired output file.\n\
460 The desired format of OUTPUT is by default inferred from its extension:\n\
461 csv txt comma-separated value\n\
462 sav sys SPSS system file\n\
463 por SPSS portable file\n\
464 sps SPSS syntax file (encrypted syntax input files only)\n\
467 -O, --output-format=FORMAT set specific output format, where FORMAT\n\
468 is one of the extensions listed above\n\
469 -e, --encoding=CHARSET override encoding of input data file\n\
470 -c MAXCASES limit number of cases to copy (default is all cases)\n\
471 Password options (for used with encrypted files):\n\
472 -p PASSWORD individual password\n\
473 -a ALPHABET with -l, alphabet of passwords to try\n\
474 -l MAX-LENGTH with -a, maximum number of characters to try\n\
475 --password-list=FILE try all of the passwords in FILE (one per line)\n\
477 --help display this help and exit\n\
478 --version output version information and exit\n",
479 program_name, program_name);