fortrangoingonforty/ferp / be9dcf3

Browse files

Fix symlink handling for -r option

- Add is_symlink() function using lstat to detect symbolic links
- Skip symlinks in collect_files when follow_links is false (-r)
- Symlinks are followed only with -R (dereference-recursive)
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
be9dcf3a188e28d113f2aeef3d69381c6a0d0bc0
Parents
f31f4ea
Tree
56ac964

1 changed file

StatusFile+-
M src/ferp_dir.f90 33 1
src/ferp_dir.f90modified
@@ -5,7 +5,7 @@ module ferp_dir
55
   implicit none
66
   private
77
 
8
-  public :: is_directory, is_regular_file, collect_files
8
+  public :: is_directory, is_regular_file, is_symlink, collect_files
99
   public :: glob_match
1010
   public :: read_patterns_from_file, matches_any_pattern
1111
 
@@ -89,6 +89,35 @@ contains
8989
 
9090
   end function is_regular_file
9191
 
92
+  function is_symlink(path) result(is_link)
93
+    !> Check if path is a symbolic link using lstat
94
+    character(len=*), intent(in) :: path
95
+    logical :: is_link
96
+
97
+    character(len=max_path_len+1) :: c_path
98
+    character(len=STAT_BUF_SIZE), target :: statbuf
99
+    integer(c_int) :: istat
100
+    integer :: mode_offset, mode_val
101
+
102
+    is_link = .false.
103
+
104
+    c_path = trim(path) // c_null_char
105
+    istat = c_lstat(c_path, c_loc(statbuf))
106
+
107
+    if (istat /= 0) return
108
+
109
+    ! On Linux x86_64, st_mode is at offset 24 (bytes 25-28)
110
+    ! st_mode is typically uint32_t
111
+    mode_offset = 24
112
+    mode_val = transfer(statbuf(mode_offset+1:mode_offset+4), 0)
113
+
114
+    ! S_IFLNK = 0120000 (octal) = 40960 (decimal)
115
+    ! The file type is in bits 12-15 of mode
116
+    ! S_IFMT mask = 0170000 (octal) = 61440
117
+    is_link = iand(mode_val, 61440) == 40960
118
+
119
+  end function is_symlink
120
+
92121
   subroutine collect_files(start_path, file_list, num_files, recursive, &
93122
                            follow_links, include_globs, num_include, &
94123
                            exclude_globs, num_exclude, exclude_dirs, num_exclude_dirs)
@@ -159,6 +188,9 @@ contains
159188
           entry_path = trim(current_dir) // '/' // trim(entry_name)
160189
         end if
161190
 
191
+        ! Skip symlinks if not following them (like grep -r vs grep -R)
192
+        if (.not. follow_links .and. is_symlink(entry_path)) cycle
193
+
162194
         ! Check if it's a directory
163195
         if (is_directory(entry_path)) then
164196
           if (recursive) then