The Fortran module M_KRACKEN(3f) lets you use Unix-like command line arguments very easily. You can call your command like this:
krackenbasic -r 333.333 -f /home/urbanjs/testin -l -i 300
with very little code.
KRACKEN(3f) provides:
program krackenbasic
use M_kracken
character(255) filename
logical lval
! define command arguments, default values and crack command line
call kracken('cmd','-i 10 -r 10e3 -d 4.1123344d0 -l .false. -f input')
! get values
call retrev('cmd_f',filename,iflen,ier) ! get -f FILENAME
lval = lget('cmd_l') ! get -l present?
rval = rget('cmd_r') ! get -r RVAL
dval = dget('cmd_d') ! get -d DBLEVAL
ival = iget('cmd_i') ! get -i IVAL
! all done parsing; do something with the values
print *, "filename=",filename(:iflen)
print *, " i=",ival, " r=",rval, " l=",lval, "d=",dval
end program krackenbasic
Here's another simple example that isolates all the parsing in a main program that then calls the real application
program krackenbasic
use M_kracken
! define command arguments, default values and crack command line
call kracken('cmd', &
& '-int 20 &
& -real 10e3 &
& -dble 4.11223344556677d0 &
& -logical .false. &
& -file input' &
& )
! all done parsing; do something with the values
call mymain( &
& sget('cmd_file'), &
& lget('cmd_logical'), &
& rget('cmd_real'), &
& dget('cmd_dble'), &
& iget('cmd_int') &
& )
end program krackenbasic
subroutine mymain(filename,flag,value1,value2,ivalue1)
character*(*) filename
doubleprecision value2
logical flag
! do something with the values
print *, 'filename=',trim(filename)
print *, 'values=',flag,value1,value2,ivalue1
end subroutine mymain
! expected output:
! filename=input
! values= F 10000.0000 4.1122334455667700 20
SUBROUTINE kracken(verb, string[,ierror])
! arbitrary command name, usually 'cmd'
CHARACTER(LEN=*), INTENT(IN) :: verb
! prototype command to define keywords and defaults
! this string is simply a list of all keywords and their
! default values exactly as you would type them on the
! command line
CHARACTER(LEN=*), INTENT(IN) :: string
! If an error occurs such as an unknown keyword the
! calling program will be stopped unless the optional
! parameter IERROR is present. If present, it is up
! to the calling program to decide what to do if
! a non-zero value is returned.
INTEGER, INTENT(OUT), OPTIONAL :: ierror
END SUBROUTINE kracken
CALL KRACKEN('cmd','-start 1 -end 100')
SUBROUTINE retrev(name, val, len, ier)
! parameter name of form VERB_KEYWORD
CHARACTER(len=*),intent(in) :: name
! returned parameter value
CHARACTER(len=*),intent(out):: val
! length of returned string
INTEGER,intent(out) :: len
! error flag. Any non-zero value means an error occurred
INTEGER,intent(out) :: ier
END SUBROUTINE retrev
CALL RETREV('cmd_start',val,len,ier)
SUBROUTINE string_to_real(chars, valu, ierr)
! string representing numeric value to convert
CHARACTER(len=*), intent(in) :: chars
! real value returned from reading string CHARS
REAL, intent(out) :: valu
! if non-zero an error occurred
INTEGER, intent(out) :: ierr
END SUBROUTINE string_to_real
val='1'
call string_to_real(VAL,x,ierr)
SUBROUTINE delim(INLINE,array,n,icount,ibegin,iterm,ilen,dlim)
CHARACTER(*) INLINE, dlim *(*)
! INLINE is the string to break into tokens
CHARACTER array(n)*(*)
! array is the array to fill with tokens
INTEGER icount, ibegin(n), iterm(n), ilen
! icount is how many tokens are found
! ibegin(1:icount) = starting column numbers for the tokens in INLINE
! iend(1:icount)=ending column numbers for the tokens in INLINE
! ilen is the position of last non-blank character in INLINE
! dlim is a string of single characters to use as delimiters
END SUBROUTINE delim
If ARRAY(N) fills before reaching the end of the line the routine stops. Check "if(iend(icount) .eq. ilen)" to see if you got to the end.
There are also convenience functions for getting simple values
lval=lget(VERB_ARGNAME) !gets a "logical" value.
rval=rget(VERB_ARGNAME) !gets a "real" value.
dval=rget(VERB_ARGNAME) !gets a "doubleprecision" value.
ival=iget(VERB_ARGNAME) !gets a "integer" value
sval=sget(VERB_ARGNAME) !gets a "character" value
The following example is a bit long compared to typical use; but exercises all the options. The command generated will have parameters -i, -r, -l, -par1, -par2, and -par3. And here is a main program that does just that ...
program krackentest
use M_kracken
! call with an arbitrary verb name and a prototype string that defines
! allowable switch names and default values
call kracken('mycommand',
&' -i 10 -r 200.3 -l -par1 "#N#" -par2 -par3 10 20 30 -files', &
& ierror)
! that's it. You defined your command arguments and their default
! values and parsed the user-supplied command line arguments.
!
! Now you can just retrieve the values as strings using
! names of the form VERB_SWITCHNAME anywhere in your program.
! Note that the special name "VERB_oo" is for the string
! before any switch. to see how look at the SAMPLES() procedure
call samples()
end
When the user types
mycommand one two -l -i 10 -three four -files file1 file2 file3
the program should complain about -three not being a valid option and will create names that can be used with RETREV(3f).
So lets take a look at something that calls RETREV(3F) and the convenience functions (LGET(),IGET(),RGET(),DGET(),....). For completeness, I show the use of string_to_real(3F) to convert the string returned by RETREV(3F) to a number; and the use of DELIM(3F) to break down a returned list into words. Note that as long as the routines are not called many times (because they are slower than common alternatives), you can just put the calls to RETREV() where you need the information instead of passing the values around or putting them into a COMMON or MODULE.
C=======================================================================--------
SUBROUTINE samples
USE M_kracken
CHARACTER*255 value
LOGICAL lval
C everything before any switch is always VERB_oo
call RETREV('mycommand_oo',value,len,ier)
write(*,*)'before any switch=',value(:len)
C Getting an integer from -i value
inumber=IGET('mycommand_i')
write(*,*)'value for -i =',inumber,' divided by 2 is ',inumber/2
C Getting a real number from -r value
anumber=RGET('mycommand_r')
write(*,*)'value for -r =',anumber,'divided by 2 is ',anumber/2
C Getting a logical value from -l value
lval=LGET('mycommand_l')
write(*,*)'value for -l =',lval
C Getting a string (with a default) from -par1
call RETREV('mycommand_par1',value,len,ier)
write(*,*)'value for -par1 =',value(:len)
C Getting a string (with no default) from -par2
call RETREV('mycommand_par2',value,len,ier)
write(*,*)'value for -par2 =',value(:len)
C Getting a string with a multi-word default and splitting it from -par3
call listem('mycommand_par3','-par3',.true.)
C List of files
call listem('mycommand_files','-files',.false.)
end
C=======================================================================--------
SUBROUTINE listem(keyword,label,toreal)
USE M_kracken
C Just getting fancy and showing the use of DELIM(3F)
C SAMPLE that decomposes list of strings and optionally, numbers
C and prints it.
C Delimit with space, comma, or colon
LOGICAL toreal
CHARACTER*(*) keyword
CHARACTER*(*) label
CHARACTER*255 value
C for DELIM call if you want to break down a list
CHARACTER*255 array(132)
INTEGER ibegin(132)
INTEGER iterm(132)
C get the value of the keyword and the length
CALL RETREV(keyword,value,len,ier)
WRITE(*,*)'value for ',label,'=',value(:len)
C split the list into one word per array
call DELIM(value,array,132,igot,ibegin,iterm,ilen,' ,:')
C print and optionally convert each word into a numeric value
DO i10=1,igot
WRITE(*,*)i10,') ',array(i10)(:len_trim(array(i10)))
IF(toreal)THEN
CALL string_to_real(array(i10),anumber,ier)
IF(ier.eq.0)THEN
WRITE(*,*)' which is a number'
ELSE
WRITE(*,*) ' which is not a number'
ENDIF
ENDIF
ENDDO
RETURN
END
C=======================================================================--------
This shows the expected output of the execution:
# Remember the command was defined by
# call kracken('mycommand',&
# &' -i 10 -r 200.3 -l -par1 "#N#" -par2 -par3 10 20 30 -- ',&
& ierror)
#
# user types
mycommand one two -l -i 10 -three four -- file1 file2 file3
# response should be
>:error: UNKNOWN OPTION mycommand_three
before any switch=one two
value for -i =10
divided by 2 is 5
value for -r =200.3
divided by 2 is 100.15
value for -par1 =#N#
value for -par2 =
value for -par3=10 20 30
1) 10
which is a number
2) 20
which is a number
3) 30
which is a number
value for --=file1 file2 file3
1) file1
2) file2
3) file3
You can ignore this section if you like, but there is a little bit of special processing that you can take advantage of ...
The menu mode feature is in a state of flux and may change significantly ...
All commands automatically have the parameter "-?". If it is present, a menu appears after any specified options have been applied that allows for changing parameters interactively.
The default prompts are the keywords themselves and their current values. To set your own prompts call SETPROMPTS(3f):
call setprompts(verb_name,options_and_prompts)
where the special prompt string "#N#" means to not allow prompting for this parameter. For example:
! set prompts for interactive mode ...
call setprompts('copy',' &
& -oo "#N#" &
& -i Enter input file name &
& -o Enter output file name &
& -version "#N#" &
& -help "#N#" &
& ')
call kracken('copy','-i -o -version .false. -help .false')
Then the command
copy -?
would only prompt for the -i and -o parameters.