Skip to content

Commit db869aa

Browse files
author
Carlos Une
committed
Add C wrapper for file listing
1 parent 7de24a6 commit db869aa

File tree

3 files changed

+91
-141
lines changed

3 files changed

+91
-141
lines changed

src/c.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* FIXME: fpm --flag '-DENABLE_C_WRAPPER' currently doesn't work with .c files. Use #if..#endif below for the time being. */
2+
#if ((defined(_WIN32) && (defined(__MINGW32__) || defined(__MINGW64__))) || defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__))
3+
#define ENABLE_C_WRAPPER
4+
#endif
5+
6+
#ifdef ENABLE_C_WRAPPER
7+
#include <sys/stat.h>
8+
#include <dirent.h>
9+
10+
int is_dir(const char *path)
11+
{
12+
struct stat m;
13+
int r = stat(path, &m);
14+
return r == 0 && S_ISDIR(m.st_mode);
15+
}
16+
17+
const char *get_d_name(struct dirent *d)
18+
{
19+
return (const char *) d->d_name;
20+
}
21+
22+
#endif

src/fpm_filesystem.F90

Lines changed: 37 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ module fpm_filesystem
77
OS_CYGWIN, OS_SOLARIS, OS_FREEBSD, OS_OPENBSD
88
use fpm_environment, only: separator, get_env
99
use fpm_strings, only: f_string, replace, string_t, split
10-
use iso_c_binding, only: c_int8_t, c_int16_t, c_int32_t, c_int64_t, c_int128_t, c_char, c_ptr, c_int, c_loc, c_long, c_short, &
11-
c_null_char, c_associated, c_f_pointer
10+
use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer
1211
implicit none
1312
private
1413
public :: basename, canon_path, dirname, is_dir, join_path, number_of_rows, read_lines, list_files, env_variable, &
@@ -18,124 +17,8 @@ module fpm_filesystem
1817

1918
integer, parameter :: LINE_BUFFER_LEN = 1000
2019

21-
#if (defined(MINGW64))
22-
type, bind(c) :: stat_t
23-
integer(kind=c_int32_t) :: st_dev
24-
integer(kind=c_int16_t) :: st_ino
25-
integer(kind=c_int16_t) :: st_mode
26-
integer(kind=c_int16_t) :: st_nlink
27-
28-
integer(kind=c_int16_t) :: st_uid
29-
integer(kind=c_int16_t) :: st_gid
30-
31-
integer(kind=c_int32_t) :: st_rdev
32-
integer(kind=c_int32_t) :: st_size
33-
34-
integer(kind=c_int64_t) :: st_atime
35-
integer(kind=c_int64_t) :: st_mtime
36-
integer(kind=c_int64_t) :: st_ctime
37-
end type
38-
#elif (defined(MINGW32))
39-
type, bind(c) :: stat_t
40-
integer(kind=c_int32_t) :: st_dev
41-
integer(kind=c_int16_t) :: st_ino
42-
integer(kind=c_int16_t) :: st_mode
43-
integer(kind=c_int16_t) :: st_nlink
44-
45-
integer(kind=c_int16_t) :: st_uid
46-
integer(kind=c_int16_t) :: st_gid
47-
48-
integer(kind=c_int32_t) :: st_rdev
49-
integer(kind=c_int32_t) :: st_size
50-
51-
integer(kind=c_int32_t) :: st_atime
52-
integer(kind=c_int32_t) :: st_mtime
53-
integer(kind=c_int32_t) :: st_ctime
54-
end type
55-
#elif (defined(LINUX64))
56-
type, bind(c) :: stat_t
57-
integer(kind=c_int64_t) :: st_dev
58-
integer(kind=c_int64_t) :: st_ino
59-
integer(kind=c_int64_t) :: st_nlink
60-
integer(kind=c_int32_t) :: st_mode
61-
62-
integer(kind=c_int32_t) :: st_uid
63-
integer(kind=c_int32_t) :: st_gid
64-
integer(kind=c_int32_t) :: pad0
65-
66-
integer(kind=c_int64_t) :: st_rdev
67-
integer(kind=c_int64_t) :: st_size
68-
integer(kind=c_int64_t) :: st_blksize
69-
integer(kind=c_int64_t) :: st_blocks
70-
71-
integer(kind=c_int128_t) :: st_atime
72-
integer(kind=c_int128_t) :: st_mtime
73-
integer(kind=c_int128_t) :: st_ctime
74-
75-
integer(kind=c_int64_t) :: glibc_reserved4
76-
integer(kind=c_int64_t) :: glibc_reserved5
77-
integer(kind=c_int64_t) :: glibc_reserved6
78-
end type
79-
#elif (defined(LINUX32))
80-
type, bind(c) :: stat_t
81-
integer(kind=c_int64_t) :: st_dev
82-
integer(kind=c_int16_t) :: pad1
83-
integer(kind=c_int32_t) :: st_ino
84-
integer(kind=c_int32_t) :: st_mode
85-
integer(kind=c_int32_t) :: st_nlink
86-
87-
integer(kind=c_int32_t) :: st_uid
88-
integer(kind=c_int32_t) :: st_gid
89-
integer(kind=c_int64_t) :: st_rdev
90-
integer(kind=c_int16_t) :: pad2
91-
92-
integer(kind=c_int32_t) :: st_size
93-
integer(kind=c_int32_t) :: st_blksize
94-
integer(kind=c_int32_t) :: st_blocks
95-
96-
integer(kind=c_int64_t) :: st_atime
97-
integer(kind=c_int64_t) :: st_mtime
98-
integer(kind=c_int64_t) :: st_ctime
99-
100-
integer(kind=c_int32_t) :: glibc_reserved4
101-
integer(kind=c_int32_t) :: glibc_reserved5
102-
end type
103-
#endif
104-
105-
#if (defined(MINGW64) || defined(MINGW32))
106-
type, bind(c) :: dirent
107-
integer(kind=c_long) :: d_ino
108-
integer(kind=c_short) :: d_reclen
109-
integer(kind=c_short) :: d_namlen
110-
character(len=1,kind=c_char) :: d_name(260)
111-
end type
112-
#elif (defined(LINUX64))
113-
type, bind(c) :: dirent
114-
integer(kind=c_int64_t) :: d_ino
115-
integer(kind=c_int64_t) :: d_off
116-
integer(kind=c_int16_t) :: d_reclen
117-
integer(kind=c_int8_t) :: d_type
118-
character(len=1,kind=c_char) :: d_name(256)
119-
end type
120-
#elif (defined(LINUX32))
121-
type, bind(c) :: dirent
122-
integer(kind=c_int32_t) :: d_ino
123-
integer(kind=c_int32_t) :: d_off
124-
integer(kind=c_int16_t) :: d_reclen
125-
integer(kind=c_int8_t) :: d_type
126-
character(len=1,kind=c_char) :: d_name(256)
127-
end type
128-
#endif
129-
130-
#if (defined(MINGW64) || defined(MINGW32) || defined(LINUX64) || defined(LINUX32))
20+
#ifdef ENABLE_C_WRAPPER
13121
interface
132-
function c_stat(path, buf) result(r) bind(c, name="stat")
133-
import c_char, c_ptr, c_int
134-
character(kind=c_char), intent(in) :: path(*)
135-
type(c_ptr), value :: buf
136-
integer(kind=c_int) :: r
137-
end function c_stat
138-
13922
function c_opendir(dir) result(r) bind(c, name="opendir")
14023
import c_char, c_ptr
14124
character(kind=c_char), intent(in) :: dir(*)
@@ -153,6 +36,18 @@ function c_closedir(dir) result(r) bind(c, name="closedir")
15336
type(c_ptr), intent(in), value :: dir
15437
integer(kind=c_int) :: r
15538
end function c_closedir
39+
40+
function c_get_d_name(dir) result(r) bind(c, name="get_d_name")
41+
import c_ptr
42+
type(c_ptr), intent(in), value :: dir
43+
type(c_ptr) :: r
44+
end function c_get_d_name
45+
46+
function c_is_dir(path) result(r) bind(c, name="is_dir")
47+
import c_char, c_int
48+
character(kind=c_char), intent(in) :: path(*)
49+
integer(kind=c_int) :: r
50+
end function c_is_dir
15651
end interface
15752
#endif
15853

@@ -450,31 +345,43 @@ subroutine mkdir(dir)
450345
end if
451346
end subroutine mkdir
452347

453-
454-
#if (defined(MINGW64) || defined(MINGW32) || defined(LINUX64) || defined(LINUX32))
348+
#ifdef ENABLE_C_WRAPPER
455349
!> Get file & directory names in directory `dir` using iso_c_binding.
456350
!!
457351
!! - File/directory names return are relative to cwd, ie. preprended with `dir`
458352
!! - Includes files starting with `.` except current directory and parent directory
459353
!!
460-
recursive subroutine list_files(dir, files, recurse)
354+
recursive subroutine list_files(dir, files, recurse, separator)
461355
character(len=*), intent(in) :: dir
462356
type(string_t), allocatable, intent(out) :: files(:)
463357
logical, intent(in), optional :: recurse
358+
character(len=1), optional :: separator
464359

465360
integer :: i
466361
type(string_t), allocatable :: dir_files(:)
467362
type(string_t), allocatable :: sub_dir_files(:)
468363

469364
type(c_ptr) :: dir_handle
470365
type(c_ptr) :: dir_entry_c
471-
type(dirent), pointer :: dir_entry_fortran
366+
character(len=:,kind=c_char), allocatable :: fortran_name
472367
character(len=:), allocatable :: string_fortran
473368
integer, parameter :: N_MAX = 256
474369
type(string_t) :: files_tmp(N_MAX)
475370
integer(kind=c_int) :: r
371+
character(len=1) :: filesep
372+
373+
if (present(separator)) then
374+
filesep = separator
375+
else
376+
select case (get_os_type())
377+
case default
378+
filesep = '/'
379+
case (OS_WINDOWS)
380+
filesep = '\'
381+
end select
382+
end if
476383

477-
if (.not. is_dir_c(dir(1:len_trim(dir))//c_null_char)) then
384+
if (c_is_dir(dir(1:len_trim(dir))//c_null_char) .eq. 0) then
478385
allocate (files(0))
479386
return
480387
end if
@@ -493,8 +400,7 @@ recursive subroutine list_files(dir, files, recurse)
493400
if (.not. c_associated(dir_entry_c)) then
494401
exit
495402
else
496-
call c_f_pointer(dir_entry_c, dir_entry_fortran)
497-
string_fortran = f_string(dir_entry_fortran%d_name)
403+
string_fortran = f_string(c_get_d_name(dir_entry_c))
498404

499405
if ((string_fortran .eq. '.' .or. string_fortran .eq. '..')) then
500406
cycle
@@ -507,7 +413,7 @@ recursive subroutine list_files(dir, files, recurse)
507413
i = 1
508414
end if
509415

510-
files_tmp(i)%s = join_path(dir, string_fortran)
416+
files_tmp(i)%s = dir // filesep // string_fortran
511417
end if
512418
end do
513419

@@ -528,8 +434,8 @@ recursive subroutine list_files(dir, files, recurse)
528434
allocate(sub_dir_files(0))
529435

530436
do i=1,size(files)
531-
if (is_dir_c(files(i)%s//c_null_char)) then
532-
call list_files(files(i)%s, dir_files, recurse=.true.)
437+
if (c_is_dir(files(i)%s//c_null_char) .ne. 0) then
438+
call list_files(files(i)%s, dir_files, recurse=.true., separator=filesep)
533439
sub_dir_files = [sub_dir_files, dir_files]
534440
end if
535441
end do
@@ -539,18 +445,6 @@ recursive subroutine list_files(dir, files, recurse)
539445
end if
540446
end subroutine list_files
541447

542-
function is_dir_c(path) result(r)
543-
character(kind=c_char), intent(in) :: path(*)
544-
logical :: r
545-
type(stat_t), target :: buf
546-
integer(kind=c_int) :: exists
547-
integer(kind=c_int), parameter :: S_IFMT = 61440
548-
integer(kind=c_int), parameter :: S_IFDIR = 16384
549-
550-
exists = c_stat(path, c_loc(buf))
551-
r = exists .eq. 0 .and. iand(int(buf%st_mode, kind=c_int), S_IFMT) .eq. S_IFDIR
552-
end function is_dir_c
553-
554448
#else
555449
!> Get file & directory names in directory `dir`.
556450
!!
@@ -616,8 +510,10 @@ recursive subroutine list_files(dir, files, recurse)
616510
end if
617511

618512
end subroutine list_files
513+
619514
#endif
620515

516+
621517
!> test if pathname already exists
622518
logical function exists(filename) result(r)
623519
character(len=*), intent(in) :: filename

src/fpm_strings.f90

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
module fpm_strings
3131
use iso_fortran_env, only: int64
32+
use iso_c_binding, only: c_char, c_ptr, c_int, c_null_char, c_associated, c_f_pointer, c_size_t
3233
implicit none
3334

3435
private
@@ -70,6 +71,10 @@ module fpm_strings
7071
module procedure new_string_t
7172
end interface string_t
7273

74+
interface f_string
75+
module procedure f_string, f_string_cptr, f_string_cptr_n
76+
end interface f_string
77+
7378
contains
7479

7580
!> test if a CHARACTER string ends with a specified suffix
@@ -125,6 +130,33 @@ function f_string(c_string)
125130
end function f_string
126131

127132

133+
!> return Fortran character variable when given a null-terminated c_ptr
134+
function f_string_cptr(cptr) result(s)
135+
type(c_ptr), intent(in), value :: cptr
136+
character(len=:,kind=c_char), allocatable :: s
137+
138+
interface
139+
function c_strlen(s) result(r) bind(c, name="strlen")
140+
import c_size_t, c_ptr
141+
type(c_ptr), intent(in), value :: s
142+
integer(kind=c_size_t) :: r
143+
end function
144+
end interface
145+
146+
s = f_string_cptr_n(cptr, c_strlen(cptr))
147+
end function
148+
149+
!> return Fortran character variable when given a null-terminated c_ptr and its length
150+
function f_string_cptr_n(cptr, n) result(s)
151+
type(c_ptr), intent(in), value :: cptr
152+
integer(kind=c_size_t), intent(in) :: n
153+
character(len=n,kind=c_char) :: s
154+
character(len=n,kind=c_char), pointer :: sptr
155+
156+
call c_f_pointer(cptr, sptr)
157+
s = sptr
158+
end function
159+
128160
!> Hash a character(*) string of default kind
129161
pure function fnv_1a_char(input, seed) result(hash)
130162
character(*), intent(in) :: input

0 commit comments

Comments
 (0)