canavanin
canavanin

Reputation: 2709

How do I get my executable's location using Fortran?

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

Answers (5)

knia
knia

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

Peter Schmolck
Peter Schmolck

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

Matthew Nicoll
Matthew Nicoll

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

King Willins
King Willins

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

janneb
janneb

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

Related Questions