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 ) {
#