Skip to content

Add filesystem interaction #874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ set(SRC
stdlib_hashmaps.f90
stdlib_hashmap_chaining.f90
stdlib_hashmap_open.f90
stdlib_io_filesystem.f90
stdlib_logger.f90
stdlib_sorting_radix_sort.f90
stdlib_system.F90
Expand All @@ -120,6 +121,17 @@ set(SRC
${outPreprocFiles}
)

# Files that have cpp directives and need to be preprocessed.
set(hasCPP
stdlib_io_filesystem.f90
)

if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
set_source_files_properties(${hasCPP} PROPERTIES COMPILE_FLAGS "-cpp")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")
set_source_files_properties(${hasCPP} PROPERTIES COMPILE_FLAGS "-fpp")
endif()

add_library(${PROJECT_NAME} ${SRC})

set_target_properties(
Expand Down
112 changes: 112 additions & 0 deletions src/stdlib_io_filesystem.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
! SPDX-Identifier: MIT

!> Interaction with the filesystem.
module stdlib_io_filesystem
use stdlib_string_type, only: string_type
implicit none
private

public :: exists, list_dir, run, temp_dir

character(*), parameter :: temp_dir = 'temp'
character(*), parameter :: listed_contents = temp_dir//'/listed_contents.txt'

contains

!> Version: experimental
!>
!> Whether a file or directory exists at the given path.
!> [Specification](../page/specs/stdlib_io.html#exists)
logical function exists(filename)
!> Name of the file or directory.
character(len=*), intent(in) :: filename

inquire(file=filename, exist=exists)

#if defined(__INTEL_COMPILER)
if (.not. exists) inquire(directory=filename, exist=exists)
#endif
end

!> Version: experimental
!>
!> List files and directories of a directory. Does not list hidden files.
!> [Specification](../page/specs/stdlib_io.html#list_dir)
subroutine list_dir(dir, files, iostat, iomsg)
!> Directory to list.
character(len=*), intent(in) :: dir
!> List of files and directories.
type(string_type), allocatable, intent(out) :: files(:)
!> Status of listing.
integer, optional, intent(out) :: iostat
!> Error message.
character(len=:), allocatable, optional, intent(out) :: iomsg

integer :: unit, stat
character(len=256) :: line

stat = 0

if (.not. exists(temp_dir)) then
call run('mkdir '//temp_dir, stat)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that under Windows OS, mkdir temp will create a temp folder under the current path pwd. Is this appropriate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a very pragmatic solution, but I think it should be fine for now

if (stat /= 0) then
if (present(iostat)) iostat = stat
if (present(iomsg)) iomsg = "Failed to create temporary directory '"//temp_dir//"'."
return
end if
end if

call run('ls '//dir//' > '//listed_contents, stat)
if (stat /= 0) then
if (present(iostat)) iostat = stat
if (present(iomsg)) iomsg = "Failed to list files in directory '"//dir//"'."
return
end if

open(newunit=unit, file=listed_contents, status='old', action='read', iostat=stat)
if (stat /= 0) then
if (present(iostat)) iostat = stat
if (present(iomsg)) iomsg = "Failed to open file '"//listed_contents//"'."
return
end if

allocate(files(0))
do
read(unit, '(A)', iostat=stat) line
if (stat /= 0) exit
files = [files, string_type(line)]
end do
close(unit, status="delete")
end

!> Version: experimental
!>
!> Run a command in the shell.
!> [Specification](../page/specs/stdlib_io.html#run)
subroutine run(command, iostat, iomsg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe an option to redirect the command output (stdout, stderr) to a string variable would be very useful. It is being done in the fpm version of this function already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it slightly exceeds the scope of this PR but it can easily be done in a follow-up PR

!> Command to run.
character(len=*), intent(in) :: command
!> Status of the operation.
integer, intent(out), optional :: iostat
!> Error message.
character(len=:), allocatable, intent(out), optional :: iomsg

integer :: exitstat, cmdstat
character(len=256) :: cmdmsg

if (present(iostat)) iostat = 0
exitstat = 0; cmdstat = 0

call execute_command_line(command, exitstat=exitstat, cmdstat=cmdstat, cmdmsg=cmdmsg)
if (exitstat /= 0 .or. cmdstat /= 0) then
if (present(iostat)) then
if (exitstat /= 0) then
iostat = exitstat
else
iostat = cmdstat
end if
end if
if (present(iomsg) .and. trim(adjustl(cmdmsg)) /= '') iomsg = cmdmsg
end if
end
end
1 change: 1 addition & 0 deletions test/io/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ADDTEST(savetxt_qp)
set_tests_properties(loadtxt_qp PROPERTIES LABELS quadruple_precision)
set_tests_properties(savetxt_qp PROPERTIES LABELS quadruple_precision)

ADDTEST(filesystem)
ADDTEST(getline)
ADDTEST(npy)
ADDTEST(open)
Expand Down
Loading
Loading