mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-08-21 22:06:34 -04:00
git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3980 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
338 lines
10 KiB
Fortran
338 lines
10 KiB
Fortran
module options
|
|
!
|
|
! Source code copied from:
|
|
! http://fortranwiki.org/fortran/show/Command-line+arguments
|
|
!
|
|
implicit none
|
|
|
|
type option
|
|
!> Long name.
|
|
character(len=100) :: name
|
|
!> Does the option require an argument?
|
|
logical :: has_arg
|
|
!> Corresponding short name.
|
|
character :: chr
|
|
!> Description.
|
|
character(len=500) :: descr
|
|
!> Argument name, if required.
|
|
character(len=20) :: argname
|
|
contains
|
|
procedure :: print => print_opt
|
|
end type option
|
|
|
|
contains
|
|
|
|
!> Parse command line options. Options and their arguments must come before
|
|
!> all non-option arguments. Short options have the form "-X", long options
|
|
!> have the form "--XXXX..." where "X" is any character. Parsing can be
|
|
!> stopped with the option '--'.
|
|
!> The following code snippet illustrates the intended use:
|
|
!> \code
|
|
!> do
|
|
!> call getopt (..., optchar=c, ...)
|
|
!> if (stat /= 0) then
|
|
!> ! optional error handling
|
|
!> exit
|
|
!> end if
|
|
!> select case (c)
|
|
!> ! process options
|
|
!> end select
|
|
!> end do
|
|
!> \endcode
|
|
subroutine getopt (options, longopts, optchar, optarg, arglen, stat, &
|
|
offset, remain, err)
|
|
use iso_fortran_env, only: error_unit
|
|
|
|
!> String containing the characters that are valid short options. If
|
|
!> present, command line arguments are scanned for those options.
|
|
!> If a character is followed by a colon (:) its corresponding option
|
|
!> requires an argument. E.g. "vn:" defines two options -v and -n with -n
|
|
!> requiring an argument.
|
|
character(len=*), intent(in), optional :: options
|
|
|
|
!> Array of long options. If present, options of the form '--XXXX...' are
|
|
!> recognised. Each option has an associated option character. This can be
|
|
!> any character of default kind, it is just an identifier. It can, but
|
|
!> doesn't have to, match any character in the options argument. In fact it
|
|
!> is possible to only pass long options and no short options at all.
|
|
!> Only name, has_arg and chr need to be set.
|
|
type(option), intent(in), optional :: longopts(:)
|
|
|
|
!> If stat is not 1, optchar contains the option character that was parsed.
|
|
!> Otherwise its value is undefined.
|
|
character, intent(out), optional :: optchar
|
|
|
|
!> If stat is 0 and the parsed option requires an argument, optarg contains
|
|
!> the first len(optarg) (but at most 500) characters of that argument.
|
|
!> Otherwise its value is undefined. If the arguments length exceeds 500
|
|
!> characters and err is .true., a warning is issued.
|
|
character(len=*), intent(out), optional :: optarg
|
|
|
|
!> If stat is 0 and the parsed option requires an argument, arglen contains
|
|
!> the actual length of that argument. Otherwise its value is undefined.
|
|
!> This can be used to make sure the argument was not truncated by the
|
|
!> limited length of optarg.
|
|
integer, intent(out), optional :: arglen
|
|
|
|
!> Status indicator. Can have the following values:
|
|
!> - 0: An option was successfully parsed.
|
|
!> - 1: Parsing stopped successfully because a non-option or '--' was
|
|
!> encountered.
|
|
!> - -1: An unrecognised option was encountered.
|
|
!> - -2: A required argument was missing.
|
|
!> .
|
|
!> Its value is never undefined.
|
|
integer, intent(out), optional :: stat
|
|
|
|
!> If stat is 1, offset contains the number of the argument before the
|
|
!> first non-option argument, i.e. offset+n is the nth non-option argument.
|
|
!> If stat is not 1, offset contains the number of the argument that would
|
|
!> be parsed in the next call to getopt. This number can be greater than
|
|
!> the actual number of arguments.
|
|
integer, intent(out), optional :: offset
|
|
|
|
!> If stat is 1, remain contains the number of remaining non-option
|
|
!> arguments, i.e. the non-option arguments are in the range
|
|
!> (offset+1:offset+remain). If stat is not 1, remain is undefined.
|
|
integer, intent(out), optional :: remain
|
|
|
|
!> If err is present and .true., getopt prints messages to the standard
|
|
!> error unit if an error is encountered (i.e. whenever stat would be set
|
|
!> to a negative value).
|
|
logical, intent(in), optional :: err
|
|
|
|
integer, save :: pos = 1, cnt = 0
|
|
character(len=500), save :: arg
|
|
|
|
integer :: chrpos, length, st, id = 0
|
|
character :: chr
|
|
logical :: long
|
|
|
|
if (cnt == 0) cnt = command_argument_count()
|
|
long = .false.
|
|
|
|
! no more arguments left
|
|
if (pos > cnt) then
|
|
pos = pos - 1
|
|
st = 1
|
|
goto 10
|
|
end if
|
|
|
|
call get_command_argument (pos, arg, length)
|
|
|
|
! is argument an option?
|
|
if (arg(1:1) == '-') then
|
|
|
|
chr = arg(2:2)
|
|
|
|
! too long ('-xxxx...') for one dash?
|
|
if (chr /= '-' .and. len_trim(arg) > 2) then
|
|
st = -1
|
|
goto 10
|
|
end if
|
|
|
|
! forced stop ('--')
|
|
if (chr == '-' .and. arg(3:3) == ' ') then
|
|
st = 1
|
|
goto 10
|
|
end if
|
|
|
|
! long option ('--xxx...')
|
|
if (chr == '-') then
|
|
|
|
long = .true.
|
|
|
|
! check if valid
|
|
id = lookup(arg(3:))
|
|
|
|
! option is invalid, stop
|
|
if (id == 0) then
|
|
st = -1
|
|
goto 10
|
|
end if
|
|
|
|
chr = longopts(id)%chr
|
|
|
|
! check if option requires an argument
|
|
if (.not. longopts(id)%has_arg) then
|
|
st = 0
|
|
goto 10
|
|
end if
|
|
|
|
! check if there are still arguments left
|
|
if (pos == cnt) then
|
|
st = -2
|
|
goto 10
|
|
end if
|
|
|
|
! go to next position
|
|
pos = pos + 1
|
|
|
|
! get argument
|
|
call get_command_argument (pos, arg, length)
|
|
|
|
! make sure it is not an option
|
|
if (arg(1:1) == '-') then
|
|
st = -2
|
|
pos = pos - 1
|
|
goto 10
|
|
end if
|
|
|
|
end if
|
|
|
|
! short option
|
|
! check if valid
|
|
if (present(options)) then
|
|
chrpos = scan(options, chr)
|
|
else
|
|
chrpos = 0
|
|
end if
|
|
|
|
! option is invalid, stop
|
|
if (chrpos == 0) then
|
|
st = -1
|
|
goto 10
|
|
end if
|
|
|
|
! look for argument requirement
|
|
if (chrpos < len_trim(options)) then
|
|
if (options(chrpos+1:chrpos+1) == ':') then
|
|
|
|
! check if there are still arguments left
|
|
if (pos == cnt) then
|
|
st = -2
|
|
goto 10
|
|
end if
|
|
|
|
! go to next position
|
|
pos = pos + 1
|
|
|
|
! get argument
|
|
call get_command_argument (pos, arg, length)
|
|
|
|
! make sure it is not an option
|
|
if (arg(1:1) == '-') then
|
|
st = -2
|
|
pos = pos - 1
|
|
goto 10
|
|
end if
|
|
|
|
end if
|
|
end if
|
|
|
|
! if we get to this point, no error happened
|
|
! return option and the argument (if there is one)
|
|
st = 0
|
|
goto 10
|
|
end if
|
|
|
|
! not an option, parsing stops
|
|
st = 1
|
|
! we are already at the first non-option argument
|
|
! go one step back to the last option or option argument
|
|
pos = pos - 1
|
|
|
|
|
|
! error handling and setting of return values
|
|
10 continue
|
|
|
|
if (present(err)) then
|
|
if (err) then
|
|
|
|
select case (st)
|
|
case (-1)
|
|
write (error_unit, *) "error: unrecognised option: " // trim(arg)
|
|
case (-2)
|
|
if (.not. long) then
|
|
write (error_unit, *) "error: option -" // chr &
|
|
// " requires an argument"
|
|
else
|
|
write (error_unit, *) "error: option --" &
|
|
// trim(longopts(id)%name) // " requires an argument"
|
|
end if
|
|
end select
|
|
|
|
end if
|
|
end if
|
|
|
|
if (present(optchar)) optchar = chr
|
|
if (present(optarg)) optarg = arg
|
|
if (present(arglen)) arglen = length
|
|
if (present(stat)) stat = st
|
|
if (present(offset)) offset = pos
|
|
if (present(remain)) remain = cnt-pos
|
|
|
|
! setup pos for next call to getopt
|
|
pos = pos + 1
|
|
|
|
contains
|
|
|
|
integer function lookup (name)
|
|
character(len=*), intent(in) :: name
|
|
integer :: i
|
|
|
|
! if there are no long options, skip the loop
|
|
if (.not. present(longopts)) goto 10
|
|
|
|
do i = 1, size(longopts)
|
|
if (name == longopts(i)%name) then
|
|
lookup = i
|
|
return
|
|
end if
|
|
end do
|
|
! if we get to this point, the option was not found
|
|
|
|
10 lookup = 0
|
|
end function lookup
|
|
|
|
end subroutine getopt
|
|
|
|
!============================================================================
|
|
|
|
!> Print an option in the style of a man page. I.e.
|
|
!> \code
|
|
!> -o arg
|
|
!> --option arg
|
|
!> description.................................................................
|
|
!> ............................................................................
|
|
!> \endcode
|
|
subroutine print_opt (opt, unit)
|
|
!> the option
|
|
class(option), intent(in) :: opt
|
|
!> logical unit number
|
|
integer, intent(in) :: unit
|
|
|
|
integer :: l, c1, c2
|
|
|
|
if (opt%has_arg) then
|
|
write (unit, '(1x,"-",a,1x,a)') opt%chr, trim(opt%argname)
|
|
write (unit, '(1x,"--",a,1x,a)') trim(opt%name), trim(opt%argname)
|
|
else
|
|
write (unit, '(1x,"-",a)') opt%chr
|
|
write (unit, '(1x,"--",a)') trim(opt%name)
|
|
end if
|
|
l = len_trim(opt%descr)
|
|
|
|
! c1 is the first character of the line
|
|
! c2 is one past the last character of the line
|
|
c1 = 1
|
|
do
|
|
if (c1 > l) exit
|
|
! print at maximum 4+76 = 80 characters
|
|
c2 = min(c1 + 76, 500)
|
|
! if not at the end of the whole string
|
|
if (c2 /= 500) then
|
|
! find the end of a word
|
|
do
|
|
if (opt%descr(c2:c2) == ' ') exit
|
|
c2 = c2-1
|
|
end do
|
|
end if
|
|
write (unit, '(4x,a)') opt%descr(c1:c2-1)
|
|
c1 = c2+1
|
|
end do
|
|
|
|
end subroutine print_opt
|
|
|
|
end module options
|