Reputation: 2709
I have a program written in Fortran 90/95; after invocation it always reads a certain data file. For the users' convenience I'd like them to just have to place this file in the same directory as the executable itself, without making them set some environment variable/extend $PATH, and without forcing them to use a certain directory for that purpose. The program should 'simply' look for the file in the directory in which it itself is stored, NOT in the directory from which it is run. So far, however, I have failed to find a solution to this problem. I tried using
getarg(0,path)
but that only gave me whatever string I had used to invoke the program, not its absolute path.
If you have any suggestions, also concerning workarounds, please don't hesitate to reply. Thanks a lot in advance!
Upvotes: 2
Views: 6514
Reputation: 749
If you are on a POSIX-compatible system, you may call the realpath
(or readlink
) C function to resolve the target of the link /proc/self/exe
, which always points to the current program being run.
So you would do
full_path_to_current_program = real_path('/proc/self/exe')
where real_path
is just a wrapper around a POSIX function.
The actual implementation is straightforward but verbose, mostly due to the character conversions. Assuming use iso_c_binding
is in scope, this works:
function real_path(path)
character(*), intent(in) :: path
character(:), allocatable :: real_path
interface
type(c_ptr) function realpath_c(path, resolved_path) bind(c, name='realpath')
use iso_c_binding
implicit none
type(c_ptr), value :: path, resolved_path
end function
subroutine free_c(ptr) bind(c, name='free')
use iso_c_binding
implicit none
type(c_ptr), value :: ptr
end subroutine
end interface
character(c_char), allocatable, target :: path_c(:)
type(c_ptr) :: resolved_path_c
path_c = str_f2c(path)
resolved_path_c = realpath_c(c_loc(path_c), c_null_ptr)
real_path = str_c2f(resolved_path_c)
call free_c(resolved_path_c)
end function
function str_f2c(str) result(str__c)
character(*), intent(in) :: str
character(kind=c_char) :: str__c(len(str) + 1)
integer :: i
str__c = [character(kind=c_char) :: (str(i:i), i = 1, len(str)), c_null_char]
end function
function str_c2f(c_str) result(f_str)
type(c_ptr), intent(in) :: c_str
character(:), allocatable :: f_str
interface
integer(c_size_t) function strlen_c(s) bind(c, name='strlen')
use iso_c_binding
implicit none
type(c_ptr), value :: s
end function
end interface
character(kind=c_char), pointer :: f_char_array(:)
integer :: i, n
n = int(strlen_c(c_str))
call c_f_pointer(c_str, f_char_array, [n])
allocate(character(n) :: f_str)
do i = 1, n
f_str(i:i) = f_char_array(i)
enddo
end function
Upvotes: 0
Reputation: 1
canavanin, have you found a solution in the meantime? In g77 getarg(0,path)
delivers the executable with the full path, but not in gfortran. However, this shortcoming of gfortran seems to be windows specific only. See Here which demonstrates getarg's functioning in a bash shell...
---corection: I have found out now that the problem is not GFORTRAN vs G77, it's the specific build. The most recent GCC / GFORTRAN version (4.54) of the DJGPP (DOS port) distribution makes the getarg(0,path) deliver the full path in front of the [executable].exe. The separation character is "/" not "\".
Upvotes: 0
Reputation: 11
In Digital Visual Fortran (etc.), in Windows, I do it like this:
SUBROUTINE GetFullExeName(FULLNAME,L)
!******************************************************************************
!
! Gets the full name of the current executing program.
!
!******************************************************************************
USE DFWIN
CHARACTER*(*) FULLNAME ! full name
INTEGER L ! length
L= GetModuleFileName(NULL,FULLNAME,LEN(FULLNAME)) ! windows API
FULLNAME(L+1:) = ' '
END
Upvotes: -1
Reputation: 9
I think this is a difficult problem to solve in fortran, and I think you're right in your approach (putting the data file in the same directory as the executable). Fortran needs something along the lines of python's
os.path.dirname(os.path.realpath(sys.argv[0]))
Unfortunately, fortran is absolutely terrible for I/O and your question highlights just one small facet.
Just in case someone else jumps the gun and thinks this is a trivial question - consider the case where you don't know the OS that people are going to run this executable on, and you don't know the executable's name.
My, probably awful, workaround is to use INDEX to look for "/". If it returns nonzero then the user "must" be on a linux system, so use INDEX to strip away the executable's name and you've got your path. Then look for "\", and if INDEX finds something then assume OS=windows
and strip away the executable.
Upvotes: 0
Reputation: 37198
Requiring that the binary is in some specific directory strikes me as weird. As long as the binary is found on the PATH, stuff should work. What you can do instead is to try to read the data file from the current working directory, i.e. the directory the user is in when launching the program.
I you don't want to require users to always copy the data file around you could search a few "default" places and then use the first one where the file is found, e.g. the current working directory, then $HOME/.your_program/file.dat, and finally /usr/local/share/your_program_name/file.dat, or something like that.
Edit If you, however, wish to continue down this wrongheaded path, at least on Linux you can use readlink() (you probably need to create a C wrapper for this, see the ISO C BINDING in recent Fortran compilers) to check the /proc/self/exe symlink.
As an aside, GETARG is not part of the Fortran standard, so you're relying on a vendor extension (which admittedly is quite widely supported). As of Fortran 2003, the standard feature for doing this is the GET_COMMAND_ARGUMENT intrinsic.
Upvotes: 2