BraWl
Loading...
Searching...
No Matches
command_line.f90
Go to the documentation of this file.
1
32
33 use kinds
34
35 implicit none
36
37 private
38 public :: get_arg, get_arg_value, arg_present ! main accessors
39 public :: arg_count, dump_names, str_wrapper ! helpers
40
41 logical :: initial_parse_done = .false.
42
44 ! init. to default values
46 character(len=:), allocatable :: name
47 character(len=:), allocatable :: value
48 logical :: has_value = .true. !whether a value was supplied (c.f. empty)
49 end type
50
53 character(len=:), allocatable :: str
54 end type
55
73 interface get_arg
75 module procedure get_arg_num_int, get_arg_name_int
76 module procedure get_arg_num_long, get_arg_name_long
78 module procedure get_arg_num_dbl, get_arg_name_dbl
79 module procedure get_arg_num_str, get_arg_name_str
80 end interface
81
83 type(cmd_arg), dimension(:), allocatable :: all_args
85 integer :: num_args = 0
86 integer, parameter :: max_string_len = 200
87 private :: all_args, num_args
88
89 contains
90
101 subroutine parse_args()
102
103 ! strictly we can't be sure any max_string_len is enough
104 ! but for command-line args it's enough if we're sensible
105 ! we wont overflow, but our strings may get truncated
106 ! we'll print an warning, since this is probably unintended input
107
108 ! note: some codes have reason to disable implicit re-allocation so we
109 ! take the extra effort to allocate all our strings manually
110
111 integer :: i_arg, i_tok, indx
112 type(cmd_arg), dimension(:), allocatable :: all_args_tmp
113 character(len=max_string_len) :: arg, tmp, tmp_name, tmp_val
114 integer :: arg_in_length, tmp_len
115 logical :: truncated
116
117 truncated = .false.
118
119 num_args = command_argument_count()
120 if(num_args > 0) then
121
122 ! if this is not the first call, all_args may be already allocated
123 ! deallocate if needed, and allocate to sufficient size
124 ! will be trimmed to actual size after parsing
125 if(allocated(all_args)) deallocate(all_args)
126 allocate(all_args(num_args))
127
128 i_arg = 1 !index of current arg
129 i_tok = 1 ! index of current input token
130 ! loop over all arguments and extract
131 do while (i_tok <= num_args)
132 ! first extract name and value parts in all cases
133 ! this consumes 1, 2 or 3 tokens depending on spaces
134
135 call get_command_argument(i_tok, arg, length=arg_in_length)
136 i_tok = i_tok + 1
137
138 if(arg_in_length > max_string_len) truncated = .true.
139
140 ! location of the '=' sign
141 ! if not found, return value of this is 0
142 indx = index(arg, '=')
143
144 !look at next chars - remove all whitespace
145 tmp = adjustl(arg(indx+1:))
146 tmp_len = len_trim(tmp)
147 if(indx > 1 .and. tmp_len > 0) then
148 ! all characters after '='
149 tmp_val = tmp
150 ! all characters up to '=', not including it
151 ! but with any leading spaces removed
152 tmp_name = adjustl(arg(1:indx-1))
153 else if(indx > 1) then
154 ! have an '=' but no following value
155 ! consume next token
156 call get_command_argument(i_tok, tmp, length=arg_in_length)
157 i_tok = i_tok + 1
158 if(arg_in_length > max_string_len) truncated = .true.
159
160 tmp_val = adjustl(tmp)
161 tmp_name = adjustl(arg(1:indx-1))
162 else ! have not yet found the equals!
163 ! set name, then hunt value...
164 tmp_name = adjustl(arg)
165
166 !peek next token - will need either 0, 1 or 2 more
167 call get_command_argument(i_tok, tmp, length=arg_in_length)
168 if(arg_in_length > max_string_len) truncated = .true.
169
170 indx = index(adjustl(tmp), '=')
171 if(indx /= 1) then
172 ! next token does not lead with '=', assume this is a flag and
173 ! do not consume next. set value for clarity, and mark
174 tmp_val = ""
175 all_args(i_arg)%has_value = .false.
176 else
177 ! consume this one and possibly one more
178 i_tok = i_tok + 1
179 if(len_trim(adjustl(tmp)) > 1) then
180 !this token has content following the '='
181 tmp_val = adjustl(tmp(2:))
182 else
183 ! consume another
184 call get_command_argument(i_tok, tmp, length=arg_in_length)
185 i_tok = i_tok + 1
186 if(arg_in_length > max_string_len) truncated = .true.
187
188 tmp_val = adjustl(tmp)
189 end if
190 end if
191 end if
192
193 ! explicitly allocate and set the values
194 allocate(character(len=len_trim(tmp_name)) :: all_args(i_arg)%name)
195 all_args(i_arg)%name = trim(tmp_name)
196 allocate(character(len=len_trim(tmp_val)) :: all_args(i_arg)%value)
197 all_args(i_arg)%value = trim(tmp_val)
198
199 i_arg = i_arg + 1
200 end do
201 !i_arg is now the actual parsed count
202 !shrink array to get rid of excess unfilled space
203 num_args = i_arg-1
204 call move_alloc(all_args, all_args_tmp)
205 allocate(all_args(num_args))
206 all_args = all_args_tmp(1:num_args)
207 deallocate(all_args_tmp)
208 endif
209
210 if(truncated) print'(a,i0, a)', "warning: very long argument truncated. to support arguments&
211 & longer than ", max_string_len, " increase the max_string_len parameter"
212
213
214 end subroutine parse_args
215
217 subroutine initial_parse
218
219 if(.not. initial_parse_done) then
220 call parse_args
221 initial_parse_done = .true.
222 end if
223
224 end subroutine initial_parse
225
229 function arg_count()
230 integer :: arg_count
231
232 call initial_parse
234 end function
235
236!------------------------------------------------------------------
237
249 function get_arg_num_logical(num, val, exists)
250
251 logical :: get_arg_num_logical
252 integer, intent(in) :: num
253 logical, intent(inout) :: val
254 logical, intent(out), optional :: exists
255 logical :: found
256 integer :: ierr
257
258 call initial_parse
259
260 found = .false.
261 ! check requested number is in range
262 if(num <= num_args .and. num > 0) then
263 ! read it from string into value
264 read(all_args(num)%value, *, iostat=ierr) val
265 found = .true.
266 end if
267
268 if(present(exists)) then
269 exists = found
270 end if
271
272 ! return value is whether value is found and correctly parsed
273 get_arg_num_logical = (found .and. (ierr == 0))
274
275 end function get_arg_num_logical
276
291 function get_arg_name_logical(name, val, exists)
292
293 logical :: get_arg_name_logical
294 character(len=*), intent(in) :: name
295 logical, intent(inout) :: val
296 integer :: i
297 logical, intent(out), optional :: exists
298 logical :: found
299 integer :: ierr
300
301 call initial_parse
302
303 found = .false.
304 val = .false.
305 ierr = 0
306 ! our cmd_arg type is already initialised to the sentinel
307 do i = 1, num_args
308 if(all_args(i)%name == trim(adjustl(name))) then
309 found = .true.
310 if(all_args(i)%has_value) then
311 read(all_args(i)%value, *, iostat=ierr) val
312 else
313 val = .true.
314 end if
315 exit
316 end if
317 end do
318
319 if(present(exists)) then
320 exists = found
321 end if
322
323 ! return value is whether value is found and correctly parsed
324 get_arg_name_logical = (found .and. (ierr == 0))
325
326 end function get_arg_name_logical
327
328
340 function get_arg_num_dbl(num, val, exists)
341
342 logical :: get_arg_num_dbl
343 integer, intent(in) :: num
344 real(kind=real64), intent(inout) :: val
345 logical, intent(out), optional :: exists
346 logical :: found
347 integer :: ierr
348
349 call initial_parse
350
351 found = .false.
352 ! check requested number is in range
353 if(num <= num_args .and. num > 0) then
354 ! read it from string into value
355 read(all_args(num)%value, *, iostat=ierr) val
356 found = .true.
357 end if
358
359 if(present(exists)) then
360 exists = found
361 end if
362
363 ! return value is whether value is found and correctly parsed
364 get_arg_num_dbl = (found .and. (ierr == 0))
365
366 end function get_arg_num_dbl
367
379 function get_arg_name_dbl(name, val, exists)
380
381 logical :: get_arg_name_dbl
382 character(len=*), intent(in) :: name
383 real(kind=real64), intent(inout) :: val
384 integer :: i
385 logical, intent(out), optional :: exists
386 logical :: found
387 integer :: ierr
388
389 call initial_parse
390
391 found = .false.
392 ! our cmd_arg type is already initialised to the sentinel
393 do i = 1, num_args
394 if(all_args(i)%name == trim(adjustl(name))) then
395 found = .true.
396 read(all_args(i)%value, *, iostat=ierr) val
397 exit
398 end if
399 end do
400
401 if(present(exists)) then
402 exists = found
403 end if
404
405 ! return value is whether value is found and correctly parsed
406 get_arg_name_dbl = (found .and. (ierr == 0))
407
408 end function get_arg_name_dbl
409
410 ! command line parsing should be avoided in performance critical code
411 ! so extra overhead from double call and downcast is not a problem
412
424 function get_arg_num_float(num, val, exists)
425 logical :: get_arg_num_float
426 integer, intent(in) :: num
427 real(kind=real32), intent(inout) :: val
428 real(kind=real64) :: tmp
429 logical, intent(out), optional :: exists
430
431 get_arg_num_float = get_arg_num_dbl(num, tmp, exists)
432 if( abs(tmp) < huge(val)) then
433 !value in range. convert. note: there may be precision loss
434 val = real(tmp, kind=real32)
435 else
436 !value out of range, can't be parsed
437 get_arg_num_float = .false.
438 end if
439
440 end function
441
453 function get_arg_name_float(name, val, exists)
454 logical :: get_arg_name_float
455 character(len=*), intent(in) :: name
456 real(kind=real32), intent(inout) :: val
457 real(kind=real64) :: tmp
458 logical, intent(out), optional :: exists
459
460 get_arg_name_float = get_arg_name_dbl(name, tmp, exists)
461 if( abs(tmp) < huge(val)) then
462 !value in range. convert. note: there may be precision loss
463 val = real(tmp, kind=real32)
464 else
465 !value out of range, can't be parsed
466 get_arg_name_float = .false.
467 end if
468
469 end function
470
471
482 function get_arg_num_int(num, val, exists)
483
484 logical :: get_arg_num_int
485 integer, intent(in) :: num
486 integer(kind=int32), intent(inout) :: val
487 logical, intent(out), optional :: exists
488 logical :: found
489 integer :: ierr
490
491 call initial_parse
492
493 found = .false.
494 ! check requested number is in range
495 if(num <= num_args .and. num > 0) then
496 ! read it from string into value
497 ! we don't need to specify the format in general
498 read(all_args(num)%value, *, iostat=ierr) val
499 found = .true.
500 end if
501
502 if(present(exists)) then
503 exists = found
504 end if
505
506 ! return value is whether value is found and correctly parsed
507 get_arg_num_int = (found .and. (ierr == 0))
508
509 end function get_arg_num_int
510
521 function get_arg_name_int(name, val, exists)
522
523 logical :: get_arg_name_int
524 character(len=*), intent(in) :: name
525 integer(kind=int32), intent(inout) :: val
526 integer :: i
527 logical, intent(out), optional :: exists
528 logical :: found
529 integer :: ierr
530
531 call initial_parse
532
533 found = .false.
534 ! our cmd_arg type is already initialised to the sentinel
535 do i = 1, num_args
536 if(all_args(i)%name == trim(adjustl(name))) then
537 found = .true.
538 read(all_args(i)%value, *, iostat=ierr) val
539 exit
540 end if
541 end do
542
543 if(present(exists)) then
544 exists = found
545 end if
546
547 ! return value is whether value is found and correctly parsed
548 get_arg_name_int = (found .and. (ierr == 0))
549
550 end function get_arg_name_int
551
562 function get_arg_num_long(num, val, exists)
563
564 logical :: get_arg_num_long
565 integer, intent(in) :: num
566 integer(kind=int64), intent(inout) :: val
567 logical, intent(out), optional :: exists
568 logical :: found
569 integer :: ierr
570
571 call initial_parse
572
573 found = .false.
574 ! check requested number is in range
575 if(num <= num_args .and. num > 0) then
576 ! read it from string into value
577 ! we don't need to specify the format in general
578 read(all_args(num)%value, *, iostat=ierr) val
579 found = .true.
580 end if
581
582 if(present(exists)) then
583 exists = found
584 end if
585
586 ! return value is whether value is found and correctly parsed
587 get_arg_num_long = (found .and. (ierr == 0))
588
589 end function get_arg_num_long
590
601 function get_arg_name_long(name, val, exists)
602
603 logical :: get_arg_name_long
604 character(len=*), intent(in) :: name
605 integer(kind=int64), intent(inout) :: val
606 integer :: i
607 logical, intent(out), optional :: exists
608 logical :: found
609 integer :: ierr
610
611 call initial_parse
612
613 found = .false.
614 ! our cmd_arg type is already initialised to the sentinel
615 do i = 1, num_args
616 if(all_args(i)%name == trim(adjustl(name))) then
617 found = .true.
618 read(all_args(i)%value, *, iostat=ierr) val
619 exit
620 end if
621 end do
622
623 if(present(exists)) then
624 exists = found
625 end if
626
627 ! return value is whether value is found and correctly parsed
628 get_arg_name_long = (found .and. (ierr == 0))
629
630 end function get_arg_name_long
631
643 function get_arg_num_str(num, val, exists)
644
645 logical :: get_arg_num_str
646 integer, intent(in) :: num
647 character(len=*), intent(inout) :: val
648 logical, intent(out), optional :: exists
649 logical :: found
650
651 call initial_parse
652
653 found = .false.
654 ! check requested number is in range
655 if(num <= num_args .and. num > 0) then
656 ! read it from string into value
657 ! we don't need to specify the format in general
658 val = all_args(num)%value
659 found = .true.
660 end if
661
662 if(present(exists)) then
663 exists = found
664 end if
665
666 ! return value is whether value is found and correctly parsed
667 get_arg_num_str = found
668
669 end function get_arg_num_str
670
686 function get_arg_name_str(name, val, exists)
687
688 logical :: get_arg_name_str
689 character(len=*), intent(in) :: name
690 character(len=*), intent(inout) :: val
691 integer :: i
692 logical, intent(out), optional :: exists
693 logical :: found
694
695 call initial_parse
696
697 found = .false.
698 do i = 1, num_args
699 if(all_args(i)%name == trim(adjustl(name))) then
700 found = .true.
701 val = all_args(i)%value
702 exit
703 end if
704 end do
705
706 if(present(exists)) then
707 exists = found
708 end if
709
710 ! return value is whether value is found and correctly parsed
711 get_arg_name_str = found
712
713 end function get_arg_name_str
714
715!--------------------------------------------------------------------
716
727 function arg_present(name, has_value) result(found)
728
729 logical :: found
730 character(len=*), intent(in) :: name
731 logical, intent(out), optional :: has_value
732 integer :: i
733
734 call initial_parse
735
736 found = .false.
737 if(present(has_value)) has_value = .false.
738 do i = 1, num_args
739 if(all_args(i)%name == trim(adjustl(name))) then
740 found = .true.
741 if(present(has_value)) has_value = all_args(i)%has_value
742 exit
743 endif
744 end do
745
746 end function arg_present
747
756 function dump_names()
757 type(str_wrapper), dimension(:), allocatable :: dump_names
758 integer :: i
759
760 ! do this the long way again to support non implicit allocations
761 allocate(dump_names(num_args))
762 do i = 1, num_args
763 allocate(dump_names(i)%str, source =all_args(i)%name)
764 end do
765
766 end function dump_names
767
779 function get_arg_value(name, exists)
780
781 character(len=:), allocatable :: get_arg_value
782 character(len=*), intent(in) :: name
783 logical, intent(out), optional :: exists
784 integer :: i
785 logical :: found
786
787 call initial_parse
788
789 found = .false.
790
791 do i = 1, num_args
792 if(all_args(i)%name .eq. trim(adjustl(name))) then
793 allocate(get_arg_value, source=all_args(i)%value)
794 found = .true.
795 exit
796 end if
797 end do
798
799 ! return empty string, not unallocated one.
800 if(.not. allocated(get_arg_value)) allocate(character(len=0) :: get_arg_value)
801 if(present(exists)) then
802 exists = found
803 end if
804
805 end function get_arg_value
806
807end module command_line
read arguments by name or number
logical function get_arg_name_dbl(name, val, exists)
read by name for double precision values
type(cmd_arg), dimension(:), allocatable, private all_args
the argument list
logical function get_arg_num_float(num, val, exists)
Read by number for single precision (float) values.
logical function get_arg_num_long(num, val, exists)
read by number for long integer values
logical function, public arg_present(name, has_value)
check presence of an argument by name
logical function get_arg_num_str(num, val, exists)
read by number for string/character values
logical function get_arg_num_dbl(num, val, exists)
read by number for double precision values
logical function get_arg_name_float(name, val, exists)
read by name for single precision (float) values
subroutine parse_args()
Parse out command line args.
integer, private num_args
the number of arguments
subroutine initial_parse
helper function - do a parse if it hasn't been done yet
character(len=:) function, allocatable, public get_arg_value(name, exists)
lookup an argument by name and return the value as an (allocatable) string if the name is not present...
logical function get_arg_num_int(num, val, exists)
read by number for integer values
logical function get_arg_name_int(name, val, exists)
read by name for integer values
type(str_wrapper) function, dimension(:), allocatable, public dump_names()
get all the argument names (by copy) order will probably match input order, but this is not guarantee...
integer function, public arg_count()
get the number of arguments note: total count may not match command_argument_count due to parsing key...
logical function get_arg_name_long(name, val, exists)
read by name for long integer values
logical function get_arg_name_logical(name, val, exists)
Read by name for logical values.
logical function get_arg_num_logical(num, val, exists)
read by number for logical values
logical function get_arg_name_str(name, val, exists)
read by name for string values
logical initial_parse_done
integer, parameter max_string_len
integer, parameter real32
Normal "float" (32 bit = 4 bytes, approx -3.4e38 to 3.4e38 and covering values down to about 1e-38 ma...
Definition kinds.f90:54
integer, parameter real64
Longer "double" (64 bit, approx -1.8e308 to 1.8e308 and covering values down to about 2e-308 magnitud...
Definition kinds.f90:59
type containing a key-value pair
wrapper to allow array of allocatable strings