+
+error:
+ casereader_destroy (reader);
+ ds_destroy (&alphabet);
+ dict_unref (dict);
+ fh_unref (output_fh);
+ fh_unref (input_fh);
+ fh_done ();
+ i18n_done ();
+
+ return 1;
+}
+
+static bool
+decrypt_file (struct encrypted_file *enc,
+ const struct file_handle *ifh,
+ const struct file_handle *ofh,
+ const char *password,
+ const char *alphabet,
+ int max_length,
+ const char *password_list)
+{
+ FILE *out;
+ int err;
+ const char *input_filename = fh_get_file_name (ifh);
+ const char *output_filename = fh_get_file_name (ofh);
+
+ if (password_list)
+ {
+ FILE *password_file;
+ if (!strcmp (password_list, "-"))
+ password_file = stdin;
+ else
+ {
+ password_file = fopen (password_list, "r");
+ if (!password_file)
+ error (1, errno, _("%s: error opening password file"),
+ password_list);
+ }
+
+ struct string pw = DS_EMPTY_INITIALIZER;
+ unsigned int target = 100000;
+ for (unsigned int i = 0; ; i++)
+ {
+ ds_clear (&pw);
+ if (!ds_read_line (&pw, password_file, SIZE_MAX))
+ {
+ if (isatty (STDOUT_FILENO))
+ {
+ putchar ('\r');
+ fflush (stdout);
+ }
+ error (1, 0, _("\n%s: password not in file"), password_list);
+ }
+ ds_chomp_byte (&pw, '\n');
+
+ if (i >= target)
+ {
+ target += 100000;
+ if (isatty (STDOUT_FILENO))
+ {
+ printf ("\r%u", i);
+ fflush (stdout);
+ }
+ }
+
+ if (encrypted_file_unlock__ (enc, ds_cstr (&pw)))
+ {
+ printf ("\npassword is: \"%s\"\n", ds_cstr (&pw));
+ password = ds_cstr (&pw);
+ break;
+ }
+ }
+ }
+ else if (alphabet[0] && max_length)
+ {
+ size_t alphabet_size = strlen (alphabet);
+ char *pw = xmalloc (max_length + 1);
+ int *indexes = xzalloc (max_length * sizeof *indexes);
+
+ for (int len = password ? strlen (password) : 0;
+ len <= max_length; len++)
+ {
+ if (password && len == strlen (password))
+ {
+ for (int i = 0; i < len; i++)
+ {
+ const char *p = strchr (alphabet, password[i]);
+ if (!p)
+ error (1, 0, _("%s: '%c' is not in alphabet"),
+ password, password[i]);
+ indexes[i] = p - alphabet;
+ pw[i] = *p;
+ }
+ }
+ else
+ {
+ memset (indexes, 0, len * sizeof *indexes);
+ for (int i = 0; i < len; i++)
+ pw[i] = alphabet[0];
+ }
+ pw[len] = '\0';
+
+ unsigned int target = 0;
+ for (unsigned int j = 0; ; j++)
+ {
+ if (j >= target)
+ {
+ target += 100000;
+ if (isatty (STDOUT_FILENO))
+ {
+ printf ("\rlength %d: %s", len, pw);
+ fflush (stdout);
+ }
+ }
+ if (encrypted_file_unlock__ (enc, pw))
+ {
+ printf ("\npassword is: \"%s\"\n", pw);
+ password = pw;
+ goto success;
+ }
+
+ int i;
+ for (i = 0; i < len; i++)
+ if (++indexes[i] < alphabet_size)
+ {
+ pw[i] = alphabet[indexes[i]];
+ break;
+ }
+ else
+ {
+ indexes[i] = 0;
+ pw[i] = alphabet[indexes[i]];
+ }
+ if (i == len)
+ break;
+ }
+ }
+ free (indexes);
+ free (pw);
+
+ success:;
+ }
+ else
+ {
+ if (password == NULL)
+ {
+ password = getpass ("password: ");
+ if (password == NULL)
+ return false;
+ }
+
+ if (!encrypted_file_unlock (enc, password))
+ error (1, 0, _("sorry, wrong password"));
+ }
+
+ out = fn_open (ofh, "wb");
+ if (out == NULL)
+ error (1, errno, ("%s: error opening output file"), output_filename);
+
+ for (;;)
+ {
+ uint8_t buffer[1024];
+ size_t n;
+
+ n = encrypted_file_read (enc, buffer, sizeof buffer);
+ if (n == 0)
+ break;
+
+ if (fwrite (buffer, 1, n, out) != n)
+ error (1, errno, ("%s: write error"), output_filename);
+ }
+
+ err = encrypted_file_close (enc);
+ if (err)
+ error (1, err, ("%s: read error"), input_filename);
+
+ if (fflush (out) == EOF)
+ error (1, errno, ("%s: write error"), output_filename);
+ fn_close (ofh, out);
+
+ return true;