Fortran · 6697 bytes Raw Blame History
1 module filesystem_ops
2 implicit none
3 private
4
5 public :: get_file_list, get_pwd, get_parent_path, join_path
6 public :: find_in_parent, find_file_in_list, fzf_search, write_exit_dir
7 public :: MAX_PATH, MAX_FILES
8
9 integer, parameter :: MAX_PATH = 512
10 integer, parameter :: MAX_FILES = 500
11
12 contains
13
14 subroutine get_file_list(dir, files, is_dir, is_exec, count)
15 character(len=*), intent(in) :: dir
16 character(len=*), dimension(*), intent(out) :: files
17 logical, dimension(*), intent(out) :: is_dir, is_exec
18 integer, intent(out) :: count
19 integer :: unit, ios, stat
20 character(len=MAX_PATH) :: fullpath
21
22 call execute_command_line("ls -1a '" // trim(dir) // "' > .fortress_ls 2>/dev/null", wait=.true.)
23
24 open(newunit=unit, file=".fortress_ls", status='old', iostat=ios)
25 if (ios /= 0) then
26 count = 0
27 return
28 end if
29
30 count = 0
31 do
32 count = count + 1
33 if (count > MAX_FILES) exit
34 read(unit, '(a)', iostat=ios) files(count)
35 if (ios /= 0) then
36 count = count - 1
37 exit
38 end if
39
40 fullpath = join_path(dir, files(count))
41 call execute_command_line("test -d '" // trim(fullpath) // "'", exitstat=stat, wait=.true.)
42 is_dir(count) = (stat == 0)
43
44 ! Check if executable (but not directories)
45 if (.not. is_dir(count)) then
46 call execute_command_line("test -x '" // trim(fullpath) // "'", exitstat=stat, wait=.true.)
47 is_exec(count) = (stat == 0)
48 else
49 is_exec(count) = .false.
50 end if
51 end do
52
53 close(unit)
54 call execute_command_line("rm -f .fortress_ls 2>/dev/null")
55 end subroutine get_file_list
56
57 function get_pwd() result(path)
58 character(len=MAX_PATH) :: path
59 integer :: unit, ios
60
61 call execute_command_line("pwd > .fortress_pwd 2>/dev/null", wait=.true.)
62 open(newunit=unit, file=".fortress_pwd", status='old', iostat=ios)
63 if (ios == 0) then
64 read(unit, '(a)') path
65 close(unit)
66 else
67 path = "."
68 end if
69 call execute_command_line("rm -f .fortress_pwd 2>/dev/null")
70 end function get_pwd
71
72 function get_parent_path(path) result(parent)
73 character(len=*), intent(in) :: path
74 character(len=MAX_PATH) :: parent
75 integer :: pos
76
77 pos = index(path, "/", back=.true.)
78 if (pos > 1) then
79 parent = path(1:pos-1)
80 else if (pos == 1) then
81 parent = "/"
82 else
83 parent = "."
84 end if
85 end function get_parent_path
86
87 function join_path(base, name) result(full)
88 character(len=*), intent(in) :: base, name
89 character(len=MAX_PATH) :: full
90
91 if (base == "/") then
92 full = "/" // trim(name)
93 else
94 full = trim(base) // "/" // trim(name)
95 end if
96 end function join_path
97
98 function find_in_parent(dir, files, count) result(idx)
99 character(len=*), intent(in) :: dir
100 character(len=*), dimension(*), intent(in) :: files
101 integer, intent(in) :: count
102 integer :: idx, pos
103 character(len=256) :: basename
104
105 pos = index(dir, "/", back=.true.)
106 if (pos > 0) then
107 basename = dir(pos+1:)
108 else
109 basename = dir
110 end if
111
112 do idx = 1, count
113 if (trim(files(idx)) == trim(basename)) return
114 end do
115 idx = 1
116 end function find_in_parent
117
118 function find_file_in_list(target_path, files, count) result(idx)
119 character(len=*), intent(in) :: target_path
120 character(len=*), dimension(*), intent(in) :: files
121 integer, intent(in) :: count
122 integer :: idx, pos
123 character(len=MAX_PATH) :: basename
124
125 ! Extract basename from target_path
126 pos = index(target_path, "/", back=.true.)
127 if (pos > 0) then
128 basename = target_path(pos+1:)
129 else
130 basename = target_path
131 end if
132
133 ! Search for the file in the list
134 do idx = 1, count
135 if (trim(files(idx)) == trim(basename)) return
136 end do
137
138 ! Default to first item if not found
139 idx = 1
140 end function find_file_in_list
141
142 subroutine fzf_search(search_dir, result_path)
143 character(len=*), intent(in) :: search_dir
144 character(len=*), intent(out) :: result_path
145 character(len=MAX_PATH) :: temp_file, fzf_cmd
146 integer :: unit, ios, stat
147
148 result_path = ""
149
150 ! Create temp file for fzf output
151 call get_environment_variable("HOME", temp_file)
152 temp_file = trim(temp_file) // "/.fortress_fzf"
153
154 ! Restore terminal for fzf
155 call execute_command_line("stty icanon echo 2>/dev/null")
156
157 ! Build fzf command: find files, pipe to fzf, save selection
158 fzf_cmd = "cd '" // trim(search_dir) // "' && " // &
159 "find . -type f -o -type d | " // &
160 "sed 's|^\./||' | " // &
161 "fzf --height=40% --reverse --border --preview 'ls -lh {}' " // &
162 "> " // trim(temp_file) // " 2>/dev/null"
163
164 ! Run fzf
165 call execute_command_line(trim(fzf_cmd), exitstat=stat, wait=.true.)
166
167 ! Restore raw mode
168 call execute_command_line("stty -icanon -echo min 1 time 0 2>/dev/null")
169
170 ! Read result if fzf succeeded
171 if (stat == 0) then
172 open(newunit=unit, file=temp_file, status='old', iostat=ios)
173 if (ios == 0) then
174 read(unit, '(a)', iostat=ios) result_path
175 if (ios == 0) then
176 ! Convert relative path to absolute
177 result_path = join_path(search_dir, result_path)
178 end if
179 close(unit)
180 end if
181 end if
182
183 ! Cleanup
184 call execute_command_line("rm -f " // trim(temp_file) // " 2>/dev/null")
185 end subroutine fzf_search
186
187 subroutine write_exit_dir(dir)
188 character(len=*), intent(in) :: dir
189 character(len=MAX_PATH) :: temp_file
190 integer :: unit, ios
191
192 ! Create temp file in HOME directory
193 call get_environment_variable("HOME", temp_file)
194 temp_file = trim(temp_file) // "/.fortress_cd"
195
196 open(newunit=unit, file=temp_file, status='replace', action='write', iostat=ios)
197 if (ios == 0) then
198 write(unit, '(a)') trim(dir)
199 close(unit)
200 end if
201 end subroutine write_exit_dir
202
203 end module filesystem_ops
204