Fortran · 8022 bytes Raw Blame History
1 module tree_module
2 use types_module
3 implicit none
4
5 ! Helper type for array of pointers
6 type node_ptr
7 type(tree_node), pointer :: ptr
8 end type node_ptr
9
10 contains
11
12 recursive subroutine add_to_tree(node, path, is_staged, is_unstaged, is_untracked, has_incoming, is_gitignored)
13 type(tree_node), pointer, intent(in) :: node
14 character(len=*), intent(in) :: path
15 logical, intent(in) :: is_staged, is_unstaged, is_untracked, has_incoming, is_gitignored
16
17 integer :: slash_pos, iostat
18 character(len=512) :: first_part, rest
19 type(tree_node), pointer :: child, new_child
20 logical :: is_directory
21
22 ! Find first slash
23 slash_pos = index(path, '/')
24
25 if (slash_pos == 0) then
26 ! This could be a file or a directory in current directory
27 child => node%first_child
28
29 ! Check if already exists
30 do while (associated(child))
31 if (trim(child%name) == trim(path)) then
32 child%is_staged = child%is_staged .or. is_staged
33 child%is_unstaged = child%is_unstaged .or. is_unstaged
34 child%is_untracked = child%is_untracked .or. is_untracked
35 child%has_incoming = child%has_incoming .or. has_incoming
36 child%is_gitignored = child%is_gitignored .or. is_gitignored
37 return
38 end if
39 if (.not. associated(child%next_sibling)) exit
40 child => child%next_sibling
41 end do
42
43 ! Check if this is a directory
44 inquire(file=trim(path), exist=is_directory, iostat=iostat)
45 if (iostat /= 0) is_directory = .false.
46
47 if (is_directory) then
48 call execute_command_line('test -d "' // trim(path) // '"', exitstat=iostat)
49 is_directory = (iostat == 0)
50 else
51 is_directory = .false.
52 end if
53
54 ! Add new child
55 allocate(new_child)
56 new_child%name = trim(path)
57 new_child%is_file = .not. is_directory
58 new_child%is_staged = is_staged
59 new_child%is_unstaged = is_unstaged
60 new_child%is_untracked = is_untracked
61 new_child%has_incoming = has_incoming
62 new_child%is_gitignored = is_gitignored
63 new_child%is_expanded = .true. ! Directories expanded by default
64 new_child%first_child => null()
65 new_child%next_sibling => null()
66
67 if (.not. associated(node%first_child)) then
68 node%first_child => new_child
69 else
70 child%next_sibling => new_child
71 end if
72 else
73 ! Split path
74 first_part = path(1:slash_pos-1)
75 rest = path(slash_pos+1:)
76
77 ! Find or create subdirectory
78 child => node%first_child
79 do while (associated(child))
80 if (trim(child%name) == trim(first_part)) then
81 call add_to_tree(child, rest, is_staged, is_unstaged, is_untracked, has_incoming, is_gitignored)
82 return
83 end if
84 if (.not. associated(child%next_sibling)) exit
85 child => child%next_sibling
86 end do
87
88 ! Create new directory
89 allocate(new_child)
90 new_child%name = trim(first_part)
91 new_child%is_file = .false.
92 new_child%is_staged = .false.
93 new_child%is_unstaged = .false.
94 new_child%is_untracked = .false.
95 new_child%has_incoming = .false.
96 new_child%is_gitignored = .false.
97 new_child%is_expanded = .true. ! Directories expanded by default
98 new_child%first_child => null()
99 new_child%next_sibling => null()
100
101 if (.not. associated(node%first_child)) then
102 node%first_child => new_child
103 else
104 child%next_sibling => new_child
105 end if
106
107 call add_to_tree(new_child, rest, is_staged, is_unstaged, is_untracked, has_incoming, is_gitignored)
108 end if
109 end subroutine add_to_tree
110
111 recursive subroutine sort_tree(node)
112 type(tree_node), pointer :: node
113 type(tree_node), pointer :: child
114
115 if (.not. associated(node)) return
116
117 ! Sort children of this node
118 call sort_children(node)
119
120 ! Recursively sort all children
121 child => node%first_child
122 do while (associated(child))
123 call sort_tree(child)
124 child => child%next_sibling
125 end do
126 end subroutine sort_tree
127
128 subroutine sort_children(node)
129 type(tree_node), pointer :: node
130 type(tree_node), pointer :: current
131 type(node_ptr), allocatable :: children_array(:)
132 integer :: count, i
133
134 if (.not. associated(node%first_child)) return
135 if (.not. associated(node%first_child%next_sibling)) return
136
137 ! Count children
138 count = 0
139 current => node%first_child
140 do while (associated(current))
141 count = count + 1
142 current => current%next_sibling
143 end do
144
145 ! Allocate array of pointers
146 allocate(children_array(count))
147
148 ! Fill array with pointers to children
149 current => node%first_child
150 do i = 1, count
151 children_array(i)%ptr => current
152 current => current%next_sibling
153 end do
154
155 ! Sort the array using quicksort
156 call quicksort_nodes(children_array, 1, count)
157
158 ! Rebuild linked list from sorted array
159 node%first_child => children_array(1)%ptr
160 do i = 1, count - 1
161 children_array(i)%ptr%next_sibling => children_array(i + 1)%ptr
162 end do
163 children_array(count)%ptr%next_sibling => null()
164
165 deallocate(children_array)
166 end subroutine sort_children
167
168 recursive subroutine quicksort_nodes(arr, low, high)
169 type(node_ptr), intent(inout) :: arr(:)
170 integer, intent(in) :: low, high
171 integer :: pivot_idx
172
173 if (low < high) then
174 call partition_nodes(arr, low, high, pivot_idx)
175 call quicksort_nodes(arr, low, pivot_idx - 1)
176 call quicksort_nodes(arr, pivot_idx + 1, high)
177 end if
178 end subroutine quicksort_nodes
179
180 subroutine partition_nodes(arr, low, high, pivot_idx)
181 type(node_ptr), intent(inout) :: arr(:)
182 integer, intent(in) :: low, high
183 integer, intent(out) :: pivot_idx
184 type(tree_node), pointer :: pivot
185 type(node_ptr) :: temp
186 integer :: i, j
187
188 pivot => arr(high)%ptr
189 i = low - 1
190
191 do j = low, high - 1
192 if (trim(arr(j)%ptr%name) <= trim(pivot%name)) then
193 i = i + 1
194 ! Swap arr(i) and arr(j)
195 temp = arr(i)
196 arr(i) = arr(j)
197 arr(j) = temp
198 end if
199 end do
200
201 ! Swap arr(i+1) and arr(high)
202 temp = arr(i + 1)
203 arr(i + 1) = arr(high)
204 arr(high) = temp
205
206 pivot_idx = i + 1
207 end subroutine partition_nodes
208
209 function should_insert_before(a, b) result(before)
210 type(tree_node), pointer, intent(in) :: a, b
211 logical :: before
212
213 ! Pure alphabetical sorting (like tree command)
214 before = (trim(a%name) < trim(b%name))
215 end function should_insert_before
216
217 recursive subroutine free_tree(node)
218 type(tree_node), pointer :: node
219 type(tree_node), pointer :: child, next_child
220
221 if (.not. associated(node)) return
222
223 ! Free all children
224 child => node%first_child
225 do while (associated(child))
226 next_child => child%next_sibling
227 call free_tree(child)
228 child => next_child
229 end do
230
231 ! Free this node
232 deallocate(node)
233 nullify(node)
234 end subroutine free_tree
235
236 end module tree_module
237