Crypt-RandPasswd

 view release on metacpan or  search on metacpan

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

pseudo-random number generator functions of the underlying C library.

However, the random function can be replaced by the user if desired.
(See L</rng>.)

=head1 Functions

=cut


sub word($$);
sub letters($$);
sub chars($$);

sub random_chars_in_range($$$$);
sub rand_int_in_range($$);
sub random_element($);

sub rng($);
sub restrict($);
sub init();


sub _random_word($);
sub _random_unit($);
sub _improper_word(@);
sub _have_initial_y(@);
sub _have_final_split(@);
sub _illegal_placement(@);


#
# Global Variables:
#

$Crypt::RandPasswd::seed = undef; # by default; causes srand() to use its own, which can be pretty good.
$Crypt::RandPasswd::initialized = 0;


lib/Crypt/RandPasswd.pm  view on Meta::CPAN

    $digram{'ck'}{'wh'} = ILLEGAL_PAIR;
    $digram{'ck'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
    $digram{'ck'}{'ck'} = ILLEGAL_PAIR;

    ##############################################################################################
    # } END DIGRAM
    ##############################################################################################



sub report(@) {
    $main::DEBUG and print @_;
}




=head2 word

  word = word( minlen, maxlen );
  ( word, hyphenated_form ) = word( minlen, maxlen );

Generates a random word, as well as its hyphenated form.
The length of the returned word will be between minlen and maxlen.  

=cut

sub word($$) {
    @_ > 2 and shift;
    my( $minlen, $maxlen ) = @_;

    $minlen <= $maxlen or die "minlen $minlen is greater than maxlen $maxlen";

    init();

    # 
    # Check for zero length words.  This is technically not an error,
    # so we take the short cut and return empty words.

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

=head2 letters

  word = letters( minlen, maxlen );

Generates a string of random letters.
The length of the returned word is between minlen and maxlen.  
Calls C<random_chars_in_range( 'a' =E<gt> 'z' )>.

=cut

sub letters($$) {
    @_ > 2 and shift;
    my( $minlen, $maxlen ) = @_;
    random_chars_in_range( $minlen, $maxlen, 'a' => 'z' ); # range of lowercase letters in ASCII
}


=head2 chars

  word = chars( minlen, maxlen );

Generates a string of random printable characters.
The length of the returned word is between minlen and maxlen.  
Calls C<random_chars_in_range( '!' =E<gt> '~' )>.

=cut

sub chars($$) {
    @_ > 2 and shift;
    my( $minlen, $maxlen ) = @_;
    random_chars_in_range( $minlen, $maxlen, '!' => '~' ); # range of printable chars in ASCII
}



=head2 random_chars_in_range

  word = random_chars_in_range( minlen, maxlen, lo_char => hi_char );
  
Generates a string of printable characters.
The length of the returned string is between minlen and maxlen.  
Each character is selected from the range of ASCII characters
delimited by (lo_char,hi_char).

=cut

sub random_chars_in_range($$$$) {
     my( $minlen, $maxlen, $lo_char, $hi_char ) = @_;

     $minlen <= $maxlen or die "minlen $minlen is greater than maxlen $maxlen";

     init();

     my $string_size = rand_int_in_range( $minlen, $maxlen );

     my $string;
     for ( my $try = 1 ; $try <= MAX_UNACCEPTABLE and not defined $string; $try++ ) {

lib/Crypt/RandPasswd.pm  view on Meta::CPAN


  n = rand_int_in_range( min, max );

Returns an integer between min and max, inclusive.
Calls C<rng> like so:

  n = min + int( rng( max - min + 1 ) )

=cut

sub rand_int_in_range($$) {
    my( $min, $max ) = @_;
    $min + int( rng( $max - $min + 1 ) )
}


=head2 random_element

  e = random_element( \@elts )

Selects a random element from an array, which is passed by ref.

=cut

sub random_element($) {
    my $ar = shift;
    $ar->[ rand_int_in_range( 0, $#{$ar} ) ]
}



=head2 rng

  r = rng( n );

lib/Crypt/RandPasswd.pm  view on Meta::CPAN


    {
        local $^W; # squelch sub redef warning.
        *Crypt::RandPasswd::rng = \&my_rng;
    }

See L<rand>.

=cut

sub rng($) {
  my $x = shift;
  rand($x)
}



=head2 restrict

  word = restrict( word );

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

You may install a different form to implement other restrictions,
by doing something like this:

    {
      local $^W; # squelch sub redef warning.
      *Crypt::RandPasswd::restrict = \&my_filter;
    }

=cut

sub restrict($) { $_[0] } # MUST return a real scalar; returning @_ causes scalar(@_) !!!


=head2 init

This initializes the environment, which by default simply seeds the random number generator.

=cut

# can be called multiple times without harm, since it remembers whether
# it has already been called.

sub init() {
    unless ( $Crypt::RandPasswd::initialized )  {
        # only do stuff if I haven't already been called before.

        $Crypt::RandPasswd::initialized = 1;
        if ( defined $Crypt::RandPasswd::seed ) {
            srand( $Crypt::RandPasswd::seed );
        }
        else {
            srand; # use default, which can be pretty good.
        }

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

# 
# _random_word
# 
# This is the routine that returns a random word.
# It collects random syllables until a predetermined word length is found. 
# If a retry threshold is reached, another word is tried.  
# 
# returns ( word, hyphenated_word ).
# 

sub _random_word($) {
    my( $pwlen ) = @_;

    my $word = '';
    my @word_syllables;

    my $max_retries = ( 4 * $pwlen ) + scalar( @grams );

    my $tries = 0;       # count of retries.


lib/Crypt/RandPasswd.pm  view on Meta::CPAN

my @numbers = map {
  ( ($_) x $occurrence_frequencies{$_} )
} @grams;

my @vowel_numbers = map {
  ( ($_) x $occurrence_frequencies{$_} )
} @vowel_grams;



sub _random_unit($) {
    my $type = shift; # byte

    random_element( $type & VOWEL
        ? \@vowel_numbers # Sometimes, we are asked to explicitly get a vowel (i.e., if
                          # a digram pair expects one following it).  This is a shortcut
                          # to do that and avoid looping with rejected consonants.

        : \@numbers       # Get any letter according to the English distribution.
    )
}

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

#   2. Three consecutive vowel units.
#   3. Three consecutive consonant units.
# 
# The checks are made against units (1 or 2 letters), not against
# the individual letters, so three consecutive units can have
# the length of 6 at most.
# 
# returns boolean
# 

sub _improper_word(@) {
    my @units = @_;

    my $failure; # bool, init False.

    for my $unit_count ( 0 .. $#units ) {
        # 
        # Check for ILLEGAL_PAIR. 
        # This should have been caught for units within a syllable,
        # but in some cases it would have gone unnoticed for units between syllables
        # (e.g., when saved units in get_syllable() were not used).

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

# 
# _have_initial_y
# 
# Treating y as a vowel is sometimes a problem.  Some words get formed that look irregular.  
# One special group is when y starts a word and is the only vowel in the first syllable.
# The word ycl is one example.  We discard words like these.
# 
# return boolean
# 

sub _have_initial_y(@) {
    my @units = @_;

    my $vowel_count = 0;
    my $normal_vowel_count = 0;

    for my $unit_count ( 0 .. $#units ) {
        #
        # Count vowels.
        #
        if ( $rules{$units[$unit_count]} & VOWEL ) {

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

# 
# Besides the problem with the letter y, there is one with
# a silent e at the end of words, like face or nice. 
# We allow this silent e, but we do not allow it as the only
# vowel at the end of the word or syllables like ble will
# be generated.
# 
# returns boolean
# 

sub _have_final_split(@) {
    my @units = @_;

    my $vowel_count = 0;

    #
    # Count all the vowels in the word.
    #
    for my $unit_count ( 0 .. $#units ) {
        if ( $rules{$units[$unit_count]} & VOWEL ) {
            $vowel_count++;

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

   f. vowel is generated and 2a is satisfied, but no syllable break is possible in previous 3 pairs.
   g. Second and third units of syllable must begin, and first unit is "alternate_vowel".


=cut

# global (like a C static)
use vars qw( @saved_pair );
@saved_pair = (); # 0..2 elements, which are units (grams).

sub get_syllable($) {
    my $pwlen = shift;

    # these used to be "out" params:
    my $syllable;               # string, returned
    my @units_in_syllable = (); # array of units, returned


    # grams:
    my $unit;
    my $current_unit;

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

# alt_get_syllable
# 
# Takes an integer, the maximum number of chars to generate. (or is it minimum?)
# 
# returns a list of ( string, units-in-syllable )
# 
# I<This is an alternative version of C<get_syllable()>, which
# can be useful for unit testing the other functions.>
# 

sub alt_get_syllable($) { # alternative version, has no smarts.
   my $pwlen = shift; # max or min?
   for ( 0 .. $#grams ) {
       my $syl = '';
       my @syl_units = ();
       while ( @syl_units < 3 ) {
           my $unit = _random_unit( NO_SPECIAL_RULE );
           $syl .= $unit;
           push @syl_units, $unit;
           length($syl) >= $pwlen and return( $syl, @syl_units );
       }

lib/Crypt/RandPasswd.pm  view on Meta::CPAN

# goes through an individual syllable and checks for illegal
# combinations of letters that go beyond looking at digrams. 
# 
# We look at things like 3 consecutive vowels or consonants,
# or syllables with consonants between vowels
# (unless one of them is the final silent e).
# 
# returns boolean.
#

sub _illegal_placement(@) {
    my @units = @_;

    my $vowel_count = 0;
    my $failure = 0; # false

    for my $unit_count ( 0 .. $#units ) {
        last if $failure;

        if ( $unit_count >= 1 ) {
            #



( run in 0.240 second using v1.01-cache-2.11-cpan-1f129e94a17 )