The accepted answer contains two errors (it passes the wrong value as the length of the string to GETCWD
, and leaves in the C_NULL_CHAR
). This answer corrects those mistakes and makes the interface more usable from Fortran.
The basic idea is the same: call getcwd
or _getcwd
using C, and call the C wrapper using Fortran's C interoperability features. On the Fortran side, a wrapper subroutine is used to handle the string length, so it does not have to be passed explicitly.
Also, C_INT
and C_CHAR
are not necessary the same as default integers and default characters, which are wanted on the Fortran side (though in practice I am not aware of any system where C_CHAR
and default char differ). The wrapper also converts those. Also, the string returned from C contains the terminating C_NULL_CHAR
, which must be removed for the string to be usable on the Fortran side.
The C code:
#ifdef _WIN32
#include <direct.h>
#define GETCWD _getcwd
#else
#include <unistd.h>
#define GETCWD getcwd
#endif
/* Return 0 on success, 1 on error. */
int getCWDHelper(char *str, int len)
{
return GETCWD(str, len) != str;
}
The Fortran code:
module cwd
use iso_c_binding, only: C_INT, C_CHAR, C_NULL_CHAR
implicit none
private
public :: getCWD
interface
function getCWDHelper(str, len) bind(C, name="getCWDHelper")
use iso_c_binding, only: C_INT, C_CHAR
integer(kind=C_INT) :: getCWDHelper
character(kind=C_CHAR), intent(out) :: str(*)
integer(kind=C_INT), value :: len
end function getCWDHelper
end interface
contains
! Writes the current working directory path into str.
! Returns 0 on success, or 1 on error.
function getCWD(str)
integer :: getCWD
character(*), intent(out) :: str
integer :: i, length
character(len=len(str), kind=C_CHAR) :: str_copy
! Call the C helper, passing the length as the correct int kind
getCWD = getCWDHelper(str_copy, len(str_copy, kind=C_INT))
if (getCWD /= 0) then
str = '' ! Error, clear the string
return
end if
! Copy the C_CHAR string to the output string,
! removing the C_NULL_CHAR and clearing the rest.
length = index(str_copy, C_NULL_CHAR) - 1
do i = 1, length
str(i:i) = char(ichar(str_copy(i:i)))
end do
str(length+1:) = ''
end function getCWD
end module
Test code:
program test
use cwd, only: getCWD
implicit none
character(len=255) :: path
integer :: error
error = getCWD(path)
print *, error
if (error == 0) print *, path
end program
Making the return value allocatable and looping to get a sufficient size is left as an exercise to the reader.