Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28701 Discussions

How to get an executable's location?

canavanin
Beginner
2,317 Views
Hi everyone,

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 or 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
[fortran]getarg(0,path)[/fortran]
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!
0 Kudos
1 Solution
John4
Valued Contributor I
2,317 Views
In UNIX and UNIX-like, there's usually a /proc//exe link pointing to the executable's real location. With the aid of POSIX's readlink, you can get the path you want, e.g.:
[fortran]program test_cmdpath
    use ISO_C_BINDING
    use IFPORT

    implicit none

    interface
        function readlink(path, buf, bufsize) bind(C, NAME = 'readlink')
            import
            integer(C_SIZE_T) :: readlink
            character(KIND = C_CHAR), intent(IN) :: path(*)
            character(KIND = C_CHAR) :: buf(*)
            integer(C_SIZE_T), value :: bufsize
        end function
    end interface

    integer :: pid, i, idx
    integer(C_SIZE_T) :: szret
    character(256) :: path
    character(KIND = C_CHAR) :: cbuf(256)

    pid = GETPID()

    write (path, '(i0)') pid
    path = '/proc/'//TRIM(path)//'/exe'

    szret = readlink(TRIM(path)//C_NULL_CHAR, cbuf, SIZE(cbuf, KIND = C_SIZE_T))
    if (szret == -1) stop 'Error reading link'

    path = ''
    do i = 1, SIZE(cbuf)
        if (cbuf(i) == C_NULL_CHAR) exit
        path(i:i) = cbuf(i)
    enddo

    print '("Command full path: ", A)', TRIM(path)

    idx = INDEX(path, '/', BACK = .TRUE.)

    print '("Command directory: ", A)', TRIM(path(:idx - 1))

end program test_cmdpath[/fortran]



View solution in original post

0 Kudos
4 Replies
mecej4
Honored Contributor III
2,317 Views
If your user was able to run the program from a directory other than the one containing the executable by typing only its name, rather than the full path, then the directory containing the executable was ascertained by examining the value of $PATH.

To retrieve the path, your program could take the output of which and use the prefix from the result. This could be done by calling system in your program, or by using a shell script which obtains the path and invokes your program with the path as an extra argument.
0 Kudos
canavanin
Beginner
2,317 Views
Dear mecej4

Thank you for your reply! The trouble with this approach is that it would restrict the users to only using directories in their path for program storage; alternatively they'd have to extend $PATH. We would like to keep things as simple and unrestricted as possible, i.e. we'd like the users to be able to place the program in whatever directory they choose and be able to use it right away, without having to set anything up. At least as long as this is possible... (admittedly, extending the path isn't a big deal, but many of our users are likely not to be familiar with such things and may be scared off by what they might perceive to be some technical obstacles, but maybe I simply ought to have stronger faith in our users!?)

0 Kudos
John4
Valued Contributor I
2,318 Views
In UNIX and UNIX-like, there's usually a /proc//exe link pointing to the executable's real location. With the aid of POSIX's readlink, you can get the path you want, e.g.:
[fortran]program test_cmdpath
    use ISO_C_BINDING
    use IFPORT

    implicit none

    interface
        function readlink(path, buf, bufsize) bind(C, NAME = 'readlink')
            import
            integer(C_SIZE_T) :: readlink
            character(KIND = C_CHAR), intent(IN) :: path(*)
            character(KIND = C_CHAR) :: buf(*)
            integer(C_SIZE_T), value :: bufsize
        end function
    end interface

    integer :: pid, i, idx
    integer(C_SIZE_T) :: szret
    character(256) :: path
    character(KIND = C_CHAR) :: cbuf(256)

    pid = GETPID()

    write (path, '(i0)') pid
    path = '/proc/'//TRIM(path)//'/exe'

    szret = readlink(TRIM(path)//C_NULL_CHAR, cbuf, SIZE(cbuf, KIND = C_SIZE_T))
    if (szret == -1) stop 'Error reading link'

    path = ''
    do i = 1, SIZE(cbuf)
        if (cbuf(i) == C_NULL_CHAR) exit
        path(i:i) = cbuf(i)
    enddo

    print '("Command full path: ", A)', TRIM(path)

    idx = INDEX(path, '/', BACK = .TRUE.)

    print '("Command directory: ", A)', TRIM(path(:idx - 1))

end program test_cmdpath[/fortran]



0 Kudos
canavanin
Beginner
2,317 Views
Dear John

Thanks a lot for this reply, the code seems to do just what I've been looking for! I've compiled and run your program, and it all works very nicely. Now I'll try to incorporate it in my code, let's hope everything will go smoothly. Thanks a lot once more.
0 Kudos
Reply