72 character(:),
allocatable :: help
73 character(:),
allocatable :: name
74 character(1) :: s_name =
""
75 logical :: has_s_name = .false.
76 logical :: required = .false.
77 logical :: blank = .false.
78 logical :: was_read = .false.
79 logical :: has_value = .false.
80 logical :: has_default = .false.
81 logical :: repeated = .false.
82 integer(i4) :: read_count = 0_i4
83 character(:),
allocatable ::
value
84 character(:),
allocatable :: value_name
85 character(:),
allocatable :: default
101 character(:),
allocatable :: prog
102 character(:),
allocatable :: description
103 character(:),
allocatable :: version
104 logical :: has_help = .true.
105 logical :: has_version = .false.
106 logical :: has_blank_option = .false.
107 logical :: has_logger = .false.
108 type(
option),
dimension(:),
allocatable :: options
145 character(*),
optional,
intent(in) :: prog
146 character(*),
optional,
intent(in) :: description
147 logical,
optional,
intent(in) :: add_help_option
148 logical,
optional,
intent(in) :: add_version_option
149 character(*),
optional,
intent(in) :: version
150 logical,
optional,
intent(in) :: add_logger_options
153 character(:),
allocatable :: arg, prog_
157 if (
present(prog))
then
160 call get_command_argument(0, length=n)
161 allocate(
character(n) :: arg, prog_)
162 call get_command_argument(0,
value=arg)
168 if (
present(description))
new_cli_parser%description = description
169 if (
present(add_help_option))
new_cli_parser%has_help = add_help_option
170 if (
present(add_version_option))
new_cli_parser%has_version = add_version_option
171 if (
present(add_logger_options))
new_cli_parser%has_logger = add_logger_options
174 name=
"help", s_name=
"h", help=
"Print this help message.")
177 call error_message(
"cli_parser: when adding the version option, you need to provide a version")
179 name=
"version", s_name=
"V", help=
"Print the version of the program.")
185 name=
"verbose", s_name=
"v", repeated=.true., help=
"Increase logging verbosity level.")
187 name=
"quiet", s_name=
"q", repeated=.true., help=
"Decrease logging verbosity level.")
189 name=
"log-output-hostname", help=
"Output hostname while logging.")
191 name=
"log-force-colors", help=
"Forces colors for the logger.")
193 name=
"log-no-colors", help=
"Disable colors while logging.")
195 name=
"log-no-format", help=
"Disable formatting while logging.")
197 name=
"log-output-date", help=
"Output date while logging.")
199 name=
"log-output-time", help=
"Output time while logging.")
205 type(
option) function
new_option(name, s_name, help, has_value, value_name, default, required, blank, repeated)
207 character(*),
intent(in) :: name
208 character(1),
optional,
intent(in) :: s_name
209 character(*),
optional,
intent(in) :: help
210 logical,
optional,
intent(in) :: has_value
211 character(*),
optional,
intent(in) :: value_name
212 character(*),
optional,
intent(in) :: default
213 logical,
optional,
intent(in) :: required
214 logical,
optional,
intent(in) :: blank
215 logical,
optional,
intent(in) :: repeated
220 if (len(name) <= 1_i4) &
221 call error_message(
"option: long-name needs at least 2 characters: " // name)
227 call error_message(
"option: short name needs to be non empty: " // name)
229 if (
present(required))
new_option%required = required
231 if (
present(has_value))
then
234 call error_message(
"option: blank option needs a value: " // name)
244 if (
present(value_name))
new_option%value_name = value_name
251 call error_message(
"option: option without value can't be required: " // name)
254 call error_message(
"option: option with defined default value can't be required: " // name)
256 if (
present(repeated))
new_option%repeated = repeated
258 call error_message(
"option: repeatedly readable options shouldn't expect a value: " // name)
263 subroutine add_option(self, name, s_name, help, has_value, value_name, default, required, blank, repeated)
266 character(*),
intent(in) :: name
267 character(1),
optional,
intent(in) :: s_name
268 character(*),
optional,
intent(in) :: help
269 logical,
optional,
intent(in) :: has_value
270 character(*),
optional,
intent(in) :: value_name
271 character(*),
optional,
intent(in) :: default
272 logical,
optional,
intent(in) :: required
273 logical,
optional,
intent(in) :: blank
274 logical,
optional,
intent(in) :: repeated
276 type(
option),
dimension(size(self%options)) :: tmp_options
277 type(
option) :: added_option
280 added_option =
option(name, s_name, help, has_value, value_name, default, required, blank, repeated)
281 if (added_option%blank .and. self%has_blank_option)
then
282 call error_message(
"cli_parser%add_option: only one blank option possible: " // name)
283 else if (added_option%blank)
then
284 self%has_blank_option = .true.
287 tmp_options = self%options
288 do i = 1,
size(tmp_options)
289 if (tmp_options(i)%name == added_option%name) &
290 call error_message(
"cli_parser%add_option: name already present: " // added_option%name)
291 if (tmp_options(i)%has_s_name .and. added_option%has_s_name &
292 .and. (tmp_options(i)%s_name == added_option%s_name)) &
293 call error_message(
"cli_parser%add_option: short name already present: " // added_option%s_name)
296 deallocate(self%options)
297 allocate(self%options(
size(tmp_options) + 1))
298 self%options(1:
size(tmp_options)) = tmp_options
299 self%options(
size(tmp_options) + 1) = added_option
317 class(
option),
intent(inout) :: self
318 character(*),
intent(in) :: arg
320 is_given_arg = (arg ==
"--" // self%name) .or. (arg ==
"-" // self%s_name)
329 character(*),
intent(in) :: name
330 logical,
intent(in),
optional :: long
331 logical,
intent(in),
optional :: short
332 logical,
intent(in),
optional :: raise_error
335 logical :: raise_error_, long_, short_
337 raise_error_ = .true.
340 if (
present(raise_error) ) raise_error_ = raise_error
341 if (
present(long) ) long_ = long
342 if (
present(short) ) short_ = short
346 do i = 1, self%cnt_options()
347 if ((long_ .and. self%options(i)%name == name) .or. (short_ .and. self%options(i)%s_name == name))
then
353 if (
get_option_index == 0_i4 .and. raise_error_)
call error_message(
"cli_parser: unknown option: " // name)
362 character(*),
intent(in) :: name
366 i = self%get_option_index(name)
376 character(*),
intent(in) :: name
380 opt = self%get_option(name)
390 character(*),
intent(in) :: name
394 opt = self%get_option(name)
404 character(*),
intent(in) :: name
406 has_option = self%get_option_index(name, raise_error=.false.) > 0
418 if (.not. self%has_blank_option) &
419 call error_message(
"cli_parser%get_blank_option_index: no blank option defined.")
422 do i = 1, self%cnt_options()
423 if (self%options(i)%blank)
then
436 character(*),
intent(in) :: name
441 opt = self%get_option(name)
442 if (.not. opt%has_value) &
443 call error_message(
"cli_parser%option_value: option has no value: " // name)
451 class(
option),
intent(inout) :: self
453 character(:),
allocatable :: opt_str
458 opt_str =
" <" // self%value_name //
">"
460 opt_str =
" --" // self%name
461 if (self%has_s_name) opt_str = opt_str //
" / -" // self%s_name
462 if (self%has_value) opt_str = opt_str //
" <" // self%value_name //
">"
465 call message(opt_str)
466 call message(
" Description: ", self%help)
467 if (self%has_default)
call message(
" Default: ", self%default)
468 if (self%repeated)
call message(
" Can be repeated.")
469 if (self%required)
call message(
" (required)")
479 character(:),
allocatable :: blank_str
482 if (self%has_blank_option) blank_str =
" <" // self%options(self%get_blank_option_index())%value_name //
">"
484 call message(self%description)
486 call message(
" Usage: ", self%prog,
" [options]", blank_str)
488 call message(
"Options:")
491 if (self%has_blank_option)
call self%options(self%get_blank_option_index())%print_info
494 do i = 1, self%cnt_options()
495 if ((.not. self%options(i)%required) .or. self%options(i)%blank) cycle
497 call self%options(i)%print_info
501 do i = 1, self%cnt_options()
502 if (self%options(i)%required .or. self%options(i)%blank) cycle
504 call self%options(i)%print_info
515 logical :: is_multi, long
516 integer(i4) :: i, j, id, n
517 character(:),
allocatable :: arg, val, err_name, names(:)
518 integer(i4),
allocatable :: counts(:)
521 arg_loop:
do while (i <= command_argument_count())
522 call get_command_argument(i, length=n)
523 if (
allocated(arg))
deallocate(arg)
524 allocate(
character(n) :: arg)
525 call get_command_argument(i,
value=arg)
527 if (.not. arg(1:1) ==
"-")
then
528 if (self%has_blank_option .and. i == command_argument_count())
then
529 self%options(self%get_blank_option_index())%was_read = .true.
530 self%options(self%get_blank_option_index())%value = arg
533 call error_message(
"cli_parser%parse: unknown argument: " // arg)
538 long = arg(2:2) ==
"-"
539 is_multi = sum(counts) > 1
540 do j = 1,
size(names)
542 id = self%get_option_index(names(j), long=long, short=.not.long)
544 if ((counts(j) > 1 .or. self%options(id)%was_read) .and. .not.self%options(id)%repeated) &
545 call error_message(
"cli_parser%parse: option given multiple times: " // self%options(id)%name)
547 self%options(id)%was_read = .true.
548 self%options(id)%read_count = self%options(id)%read_count + counts(j)
550 if (self%options(id)%has_value)
then
552 call error_message(
"cli_parser%parse: option expects a value: " // self%options(id)%name)
553 if (i == command_argument_count()) &
554 call error_message(
"cli_parser%parse: required value missing for: " // self%options(id)%name)
555 call get_command_argument(i + 1, length=n)
556 if (
allocated(val))
deallocate(val)
557 allocate(
character(n) :: val)
558 call get_command_argument(i + 1,
value=val)
559 self%options(id)%value = val
564 deallocate(names, counts)
567 if (self%has_help)
then
568 if (self%option_was_read(
"help"))
then
569 call self%print_help()
574 if (self%has_version)
then
575 if (self%option_was_read(
"version"))
then
576 call message(self%version)
582 check_req:
do j = 1, self%cnt_options()
583 if ((.not. self%options(j)%was_read) .and. self%options(j)%has_default)
then
584 self%options(j)%value = self%options(j)%default
585 self%options(j)%was_read = .true.
587 if (self%options(j)%required .and. (.not. self%options(j)%was_read))
then
588 if (self%options(j)%blank)
then
589 err_name =
"<" // self%options(j)%value_name //
">"
591 err_name =
"--" // self%options(j)%name
593 call error_message(
"cli_parser%parse: required option missing: " // err_name)
598 if ( self%has_logger )
then
600 verbose = self%option_read_count(
"verbose"), &
601 quiet = self%option_read_count(
"quiet"), &
602 log_output_hostname = self%option_was_read(
"log-output-hostname"), &
603 log_force_colors = self%option_was_read(
"log-force-colors"), &
604 log_no_colors = self%option_was_read(
"log-no-colors"), &
605 log_no_format = self%option_was_read(
"log-no-format"), &
606 log_output_date = self%option_was_read(
"log-output-date"), &
607 log_output_time = self%option_was_read(
"log-output-time") &
618 character(*),
intent(in) :: arg
619 character(:),
intent(out),
allocatable :: names(:)
620 integer(i4),
intent(out),
allocatable :: counts(:)
622 character,
allocatable :: s_names(:)
625 if ( arg(1:1) /=
"-" )
call error_message(
"cli_parser%parse: invalid argument: " // arg)
626 if ( len(arg) < 2 )
call error_message(
"cli_parser%parse: found empty argument: " // arg)
629 if ( arg(2:2) ==
"-" )
then
630 if ( len(arg) == 2 )
call error_message(
"cli_parser%parse: found empty argument: " // arg)
631 allocate(
character(len(arg)-2) :: names(1))
632 names(1) = arg(3:len(arg))
637 call append(s_names, arg(2:2))
641 j = findchar(s_names, arg(i:i))
643 call append(s_names, arg(i:i))
646 counts(j) = counts(j) + 1_i4
649 allocate(
character(1) :: names(size(s_names)))
653 integer(i4) function findchar(array, chr)
654 character,
intent(in) :: array(:)
655 character,
intent(in) :: chr
660 do i = 1,
size(array)
661 if (array(i) == chr)
then
667 end function findchar
Append (rows) scalars, vectors, and matrixes onto existing array.
Append values on existing arrays.
Module to parse command line arguments.
character(:) function, allocatable option_value(self, name)
Get the parsed value from an option by name from the cli_parser.
type(option) function new_option(name, s_name, help, has_value, value_name, default, required, blank, repeated)
Create a new option.
logical function option_was_read(self, name)
Whether the option was read by the cli_parser given by name.
subroutine print_info(self)
Print info for an option.
subroutine print_help(self)
Print help message for the cli_parser.
subroutine parse(self)
Parse the given command line arguments with the cli_parser.
integer(i4) function get_blank_option_index(self)
Get the index of the blank option.
logical function is_given_arg(self, arg)
check if this option is the given argument.
type(option) function get_option(self, name)
Get an option from cli_parser by name.
logical function has_option(self, name)
Whether the option is defined in cli_parser given by name.
integer(i4) function option_read_count(self, name)
Read count for the option in the cli_parser given by name.
integer(i4) function cnt_options(self)
Get option count from the cli_parser.
subroutine add_option(self, name, s_name, help, has_value, value_name, default, required, blank, repeated)
Add a new option to the cli_parser.
type(cli_parser) function new_cli_parser(prog, description, add_help_option, add_version_option, version, add_logger_options)
Create a new cli_parser.
integer(i4) function get_option_index(self, name, long, short, raise_error)
Get the option index from cli_parser by name.
subroutine parse_arg(arg, names, counts)
Parse given argument.
Define number representations.
integer, parameter i4
4 Byte Integer Kind
Module providing a logging framework.
subroutine, public log_set_config(verbose, quiet, log_output_hostname, log_force_colors, log_no_colors, log_output_date, log_output_time, log_no_format)
Set logging configuration.
Write out concatenated strings.
subroutine, public error_message(t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15, t16, uni, advance, show, raise, reset_format)
Write out an error message to stderr and call stop 1.
subroutine, public message(t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15, t16, uni, advance, show, reset_format)
Write out an error message to stdout.
Path and directory management.
subroutine, public path_split(path, head, tail)
Splitting the path into head and tail.
This is a parser for command line arguments.
This is a container for a single command line option.