targ.f90 Source File


Contents

Source Code


Source Code

! SPDX-Identifier: LGPL-3.0-or-later
module targ
   use tomlf
   implicit none
   private

   public :: arg_type, targ_type
   public :: get_arguments, new_argument_parser, get_options

   type :: arg_type
      character(len=:), allocatable :: arg
   end type arg_type

   type :: opt_type
      character(len=:), allocatable :: name
      integer :: require
   end type opt_type

   type :: targ_type
      integer :: nopt
      type(opt_type), allocatable :: opt(:)
   contains
      procedure :: add_option
   end type targ_type

   integer, parameter :: initial_size = 16

contains


subroutine get_arguments(args)
   type(arg_type), allocatable, intent(out) :: args(:)
   integer :: iarg, length
   character(len=:), allocatable :: tmp

   allocate(args(command_argument_count()))

   do iarg = 1, size(args)
      call get_command_argument(iarg, length=length)
      allocate(character(len=length) :: tmp)
      if (length > 0) then
         call get_command_argument(iarg, tmp)
      end if
      call move_alloc(tmp, args(iarg)%arg)
   end do

end subroutine get_arguments


subroutine get_options(self, args, table)
   type(targ_type), intent(in) :: self
   type(arg_type), allocatable, intent(inout) :: args(:)
   type(arg_type), allocatable :: tmp(:)
   type(toml_table), allocatable, intent(out) :: table
   type(toml_array), pointer :: array
   character(len=:), allocatable :: arg
   integer :: iarg, ipos, iopt, ii
   logical :: getopts
   table = toml_table()
   allocate(tmp(size(args)))
   getopts = .true.
   ipos = 0
   iarg = 0
   do while(iarg < size(args))
      iarg = iarg + 1
      call move_alloc(args(iarg)%arg, arg)
      if (getopts) then
         getopts = arg /= "--"
         if (.not.getopts) cycle
         call match_option(self, arg, iopt)
         if (iopt > 0) then
            select case(self%opt(iopt)%require)
            case(0)
               call set_value(table, self%opt(iopt)%name, .true.)
            case(1)
               iarg = iarg + 1
               if (iarg <= size(args)) then
                  call set_value(table, self%opt(iopt)%name, args(iarg)%arg)
               end if
            case default
               if (iarg + self%opt(iopt)%require <= size(args)) then
                  call add_array(table, self%opt(iopt)%name, array)
                  do ii = 1, self%opt(iopt)%require
                     call set_value(array, ii, args(iarg+ii)%arg)
                  end do
                  iarg = iarg + self%opt(iopt)%require
               end if
            end select
            cycle
         end if
      end if
      ipos = ipos + 1
      call move_alloc(arg, tmp(ipos)%arg)
   end do

   deallocate(args)
   allocate(args(ipos))
   do ipos = 1, size(args)
      call move_alloc(tmp(ipos)%arg, args(ipos)%arg)
   end do

end subroutine get_options


subroutine match_option(self, arg, iopt)
   type(targ_type), intent(in) :: self
   character(len=*), intent(in) :: arg
   integer, intent(out) :: iopt
   integer :: ii
   iopt = 0
   do ii = 1, self%nopt
      if (self%opt(ii)%name == arg(3:)) then
         iopt = ii
         exit
      end if
   end do
end subroutine match_option


function new_argument_parser(nopt) result(self)
   integer, intent(in), optional :: nopt
   type(targ_type) :: self

   self%nopt = 0
   if (present(nopt)) then
      allocate(self%opt(nopt))
   else
      allocate(self%opt(initial_size))
   end if

end function new_argument_parser


subroutine add_option(self, name, require)
   class(targ_type), intent(inout) :: self
   character(len=*), intent(in) :: name
   integer, intent(in), optional :: require
   integer :: m

   m = size(self%opt)
   if (self%nopt >= m) then
      call resize(self%opt, m + m/2 + 1)
   end if

   self%nopt = self%nopt + 1
   self%opt(self%nopt)%name = name
   if (present(require)) then
      self%opt(self%nopt)%require = require
   else
      self%opt(self%nopt)%require = 0
   end if

end subroutine add_option


subroutine resize(list, n)

   !> Array to be resized
   type(opt_type), allocatable, intent(inout), target :: list(:)

   !> New size of the list
   integer, intent(in) :: n

   type(opt_type), allocatable, target :: tmp(:)
   integer :: i


   if (allocated(list)) then
      call move_alloc(list, tmp)
      allocate(list(n))

      do i = 1, min(size(tmp), n)
         list(i) = tmp(i)
      end do

      deallocate(tmp)
   else
      allocate(list(n))
   end if

end subroutine resize


end module targ