RosettaCodeData/Task/Password-generator/Fortran/password-generator.f
2026-04-30 12:34:36 -04:00

181 lines
6.2 KiB
Fortran

!
! Password generator
! tested with Intel ifx (IFX) 2025.2.1 20250806 on Kubuntu 25.10
! GNU gfortran (Ubuntu 15.2.0-4ubuntu4) 15.2.0 on Kubuntu 25.10
! but not with VSI Fortran x86-64 beause that compiler does not know the
! Fortran 2003 style command line handling.
! U.B., April 2026
!
program passwordGenerator
implicit none
character (len=50) :: Password
character (len=*), parameter ::lowerCase = 'abcdefghijklmnopqrstuvwxyz'
character (len=*), parameter ::upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
character (len=*), parameter ::digits='0123456789'
character (len=*), parameter ::others = '!"#$%&''()*+,-./:;<=>?@[]^_{|}~'
character (len=*), parameter :: any = lowerCase//upperCase//digits//others
character(len=12), dimension(:), allocatable :: args
integer :: pwLen, nPassw
integer :: ii, remLen, num_args, pos, ios
character :: c
num_args = command_argument_count()
! Provide defaults for the command line options.
pwLen = 20
nPassw=1
if (num_args .gt. 0) then
allocate(args(num_args)) ! omitted checking the return status of the allocation
do ii = 1, num_args
call get_command_argument(ii,args(ii))
end do
!
! from the task description: The user must be able to specify the password
! length and the number of passwords to generate.
! Expect Command line arguments: -l [integer], -n [integer]
! Help is displayed if any command line argument other -l length or -n Number of Passwds
!
ii = 1
do while (num_args .ge. ii)
if (args(ii) .eq. '-l') then
ii = ii + 1
if (ii .le. num_args) then
read (args(ii), *, iostat = ios) pwLen
if (ios .ne. 0 .or. pwLen .gt. 50 ) then
write (*, '(/A,X,A)') args(ii), 'Is not a valid input for the passwpord length: '
call help()
stop
else
ii = ii + 1 ! Prepare to get next argument
endif
else
write (*, '(/A)') 'Missing password length after "-l"'
call help()
stop
endif
else if (args(ii) .eq. '-n') then
ii = ii + 1
if (ii .le. num_args) then
read (args(ii), *, iostat = ios) nPassw
if (ios .ne. 0) then
write (*, '(/A,X,A)') args(ii), 'Is not a valid input for number of passwpords.'
call help()
stop
else
ii = ii + 1 ! Prepare to get next argument
endif
else
write (*, '(/A)') 'Missing number of passwords after "-n"'
call help()
stop
end if
! From Task description: the You may also allow the user to specify a
! seed value, and give the option of excluding
! visually similar characters. These 2 features are
! optional, hence omitted here.
! else if (...) Add the code for more options here
else
! provide help if any argument other than -l <len> or -n <num> given
call help()
stop
end if
enddo
endif
! from the task description: The randomness should be from a system source or library.
! If RANDOM_SEED is called without arguments, it is seeded with random data
! retrieved from the operating system.
call random_seed()
do ii=1, nPassw
password = ' ' ! Start with all blank.
remlen = pwLen ! Remaining chars to fill
! from the task description: The generated password(s) must include
! at least 1 (of each of the four groups)
call addRandomCharacter (upperCase)
call addRandomCharacter (lowerCase)
call addRandomCharacter (digits)
call addRandomCharacter (others)
! Fill up ramaining locations with characters of any type
do while (remLen .gt. 0)
call addRandomCharacter (any)
enddo
write (*,'(A)'), password
end do
contains
! =====================================================
! Print help text to explain the command line arguments
! =====================================================
subroutine help()
write (*, '(/A/)') 'Valid command line arguments are:'
write (*, '(A)') '-n <nPassword> : Number of passwords to generate'
write (*, '(A)') '-l <length> : Length of password(s) (<=50)'
end subroutine help
! ====================================================================================
! Randomly select a character from charset, and place it at a randomly selected empty
! space in the password
! ====================================================================================
subroutine addRandomCharacter (charset)
character (len=*), intent(in) :: charset
integer:: pos
character :: c
pos = randEmptyPos()
c=randomCharacter(charset)
Password (pos:pos) = c
remLen = remLen-1
end subroutine addRandomCharacter
! ========================================================
! Randomly select a character from the given character set
! ========================================================
function randomCharacter (charset) result (chr)
character (len=*), intent(in) :: charset
character :: chr
integer :: ipos, l
l = len (charset)
ipos = randominInterval (1, l)
chr = charset(ipos:ipos)
end function randomCharacter
! ========================================
! Select random empty position in password
! ========================================
function randEmptyPos() result (retv)
integer :: retv
integer :: ipos, ic, ii
ipos = randominInterval (1, remLen)
ic=0
do ii=1, pwLen
if (Password(ii:ii) .eq. ' ') ic = ic + 1
if (ic .eq. ipos) then
retv = ii
exit
endif
end do
end function randEmptyPos
! ===========================================================
! Generate random number between @lo and @hi (inclusive)
! Assume Random Number Generator has been initialized before.
! ===========================================================
function randominInterval (lo, hi) result (r)
integer, intent(in) :: lo, hi ! the interval
integer :: r ! resultant (pseudo-)random number
real :: rnd ! Fortran random number generator generates float values
call random_number (rnd) ! 0. <= rnd < 1.
r = lo + FLOOR((hi+1-lo)*rnd) ! We want to choose one between [lo,hi]: add +1 to possibly include "hi".
end function randominInterval
end program passwordGenerator