mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-31 13:10:19 -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
 |