#!/usr/bin/perl
##
## pwgen(1) clone: Passwords generator
## Copyright (c) 2007-2011 SATOH Fumiyasu @ OSS Technology, Inc.
##               <http://www.osstech.co.jp/>
##
## License: GNU General Public License version 3
## Date: 2011-01-19, since 2005-05-23
##

use strict;
use warnings;
use Getopt::Long;

use constant TRUE => 1;
use constant FALSE => 0;

## FIXME: srand(truely random number);

my $seed_digits = '0123456789';
my $seed_uppers = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
my $seed_lowers = 'abcdefghijklmnopqrstuvwxyz';
my $seed_symbols = '#%&\'()*+,-./:;<=>?@[]^_{|}~'; ## Except '"', '$', '\', '`'
#my $seed_symbols = '"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
my $seed_ambiguous = '0DOQ1lI2Z5S6G8B9q';

my $p_length = 32;
my $p_number = 8;
my $flag_digit = TRUE;
my $flag_upper = TRUE;
my $flag_lower = TRUE;
my $flag_symbol = FALSE;
my $flag_ambiguous = FALSE;

my $cmd_usage = "Usage: $0 [OPTIONS] [LENGTH [NUMBER]]

Arguments:
  LENGTH
    Password length
  NUMBER
    Number of passwords

Options:
  -y, --symbols
    Include at least one special symbol in the password
  -B, --ambiguous
    Don't include ambiguous characters in the password
";

{
  ## Trap warning messages from Getopt::Long
  local($SIG{'__WARN__'}) = sub {
    my ($msg) = shift(@_);
    chomp($msg);
    print STDERR "$0: ERROR: $msg\n";
  };
  ## I don't like default behavior.
  Getopt::Long::Configure('bundling');
  Getopt::Long::Configure('no_ignore_case');
  Getopt::Long::Configure('no_auto_abbrev');
  GetOptions(
    'h|help' =>		sub { print $cmd_usage; exit(0); },
    'N|num-passwords=i' =>\$p_number,
    'C' =>		sub { }, ## FIXME
    '1' =>		sub { }, ## FIXME
    'a|alt-phonics' =>	sub { }, ## FIXME
    'c|capitalize' =>	sub { $flag_upper = TRUE; },
    'A|no-capitalize' =>sub { $flag_upper = FALSE; },
    'n|numerals' =>	sub { $flag_digit = TRUE; },
    '0|no-numerals' =>	sub { $flag_digit = FALSE; },
    's|secure' =>	sub { }, ## FIXME
    'v|no-vowels' =>	sub { }, ## FIXME
    'y|symbols' =>	\$flag_symbol,
    'H|sha1=s' =>	sub { }, ## FIXME
    'B|ambiguous' =>	\$flag_ambiguous,
  ) || exit(1);
}

$p_length = shift(@ARGV) if (@ARGV);
$p_number = shift(@ARGV) if (@ARGV);

my $seed = '';
$seed .= $seed_digits if ($flag_digit);
$seed .= $seed_uppers if ($flag_upper);
$seed .= $seed_lowers if ($flag_lower);
$seed .= $seed_symbols if ($flag_symbol);
if ($flag_ambiguous) {
  my $ambiguous_re = '(?:' . join('|', split(//, $seed_ambiguous)) . ')';
  $seed =~ s/$ambiguous_re//go;
}

my @seed = split(//, $seed);

for (my $i = 0; $i < $p_number; $i++) {
  my $password = '';
  for (my $j = 0; $j < $p_length; $j++) {
    $password .= @seed[rand(@seed)];
  }
  print "$password\n";
}

