GPAK  1.0.0
A general-purpose archive library
filesystem_tree.c
1 #include "filesystem_tree.h"
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 
7 filesystem_tree_node_t* _create_node(const char* _name, filesystem_tree_node_t* _parent)
8 {
10  node->name_ = strdup(_name);
11  node->parent_ = _parent;
12  node->children_ = NULL;
13  node->num_children_ = 0ull;
14  node->files_ = NULL;
15  node->num_files_ = 0ull;
16  return node;
17 }
18 
19 filesystem_tree_node_t* _add_directory(filesystem_tree_node_t* _root, const char* _dir_name)
20 {
21  filesystem_tree_node_t* new_node = _create_node(_dir_name, _root);
22  _root->children_ = (filesystem_tree_node_t**)realloc(_root->children_, sizeof(filesystem_tree_node_t*) * (_root->num_children_ + 1));
23  _root->children_[_root->num_children_++] = new_node;
24  return new_node;
25 }
26 
27 filesystem_tree_file_t* _create_file(const char* _name, const char* _file_path, pak_entry_t _entry)
28 {
30  file->name_ = strdup(_name);
31  file->path_ = strdup(_file_path);
32  file->entry_ = _entry;
33 
34  return file;
35 }
36 
37 filesystem_tree_file_t* _add_file(filesystem_tree_node_t* _directory, const char* _file_name, const char* _file_path, pak_entry_t _entry)
38 {
39  filesystem_tree_file_t* new_file = _create_file(_file_name, _file_path, _entry);
40  _directory->files_ = (filesystem_tree_file_t**)realloc(_directory->files_, sizeof(filesystem_tree_file_t*) * (_directory->num_files_ + 1));
41  _directory->files_[_directory->num_files_++] = new_file;
42  return new_file;
43 }
44 
45 filesystem_tree_node_t* _find_directory(filesystem_tree_node_t* _root, const char* _path)
46 {
47  if (!_root || !_path || !*_path)
48  return NULL;
49 
50  if (strcmp(_root->name_, _path) == 0)
51  return _root;
52 
53  for (size_t i = 0; i < _root->num_children_; i++)
54  {
55  filesystem_tree_node_t* found = _find_directory(_root->children_[i], _path);
56  if (found)
57  return found;
58  }
59 
60  return NULL;
61 }
62 
63 filesystem_tree_file_t* _find_file(filesystem_tree_node_t* _directory, const char* _file_name)
64 {
65  if (!_directory || !_file_name || !*_file_name)
66  return NULL;
67 
68  for (size_t i = 0; i < _directory->num_files_; i++)
69  {
70  if (strcmp(_directory->files_[i]->name_, _file_name) == 0)
71  return _directory->files_[i];
72  }
73 
74  return NULL;
75 }
76 
77 void _free_file(filesystem_tree_file_t* _file)
78 {
79  if (_file)
80  {
81  free(_file->path_);
82  free(_file->name_);
83  free(_file);
84  }
85 }
86 
87 void _free_node(filesystem_tree_node_t* _node)
88 {
89  if (_node)
90  {
91  for (size_t i = 0; i < _node->num_children_; i++)
92  _free_node(_node->children_[i]);
93 
94  for (size_t i = 0; i < _node->num_files_; i++)
95  _free_file(_node->files_[i]);
96 
97  free(_node->name_);
98  free(_node->children_);
99  free(_node->files_);
100  free(_node);
101  }
102 }
103 
104 
106 {
107  return _create_node("", NULL);
108 }
109 
111 {
112  if (!_root || !_path || !*_path)
113  return;
114 
115  char* dir_path = strdup(_path);
116  char* dir_name = strtok(dir_path, "/");
117 
118  filesystem_tree_node_t* current = _root;
119  while (dir_name)
120  {
121  filesystem_tree_node_t* found = _find_directory(current, dir_name);
122  if (!found)
123  found = _add_directory(current, dir_name);
124 
125  current = found;
126  dir_name = strtok(NULL, "/");
127  }
128 
129  free(dir_path);
130 }
131 
132 void filesystem_tree_add_file(filesystem_tree_node_t* _root, const char* _path, const char* _file_path, pak_entry_t _entry)
133 {
134  if (!_root || !_path || !*_path)
135  return;
136 
137  char* dir_path = strdup(_path);
138  char* dir_name = strtok(dir_path, "/");
139  char* file_name = NULL;
140 
141  filesystem_tree_node_t* current = _root;
142  while (dir_name)
143  {
144  file_name = strtok(NULL, "/");
145  if (file_name)
146  {
147  filesystem_tree_node_t* found = _find_directory(current, dir_name);
148  if (!found)
149  found = _add_directory(current, dir_name);
150 
151  current = found;
152  dir_name = file_name;
153  }
154  else
155  break;
156  }
157 
158  if (dir_name && !file_name)
159  _add_file(current, dir_name, _file_path, _entry);
160 
161  free(dir_path);
162 }
163 
165 {
166  if (!_root || !_path || !*_path)
167  return NULL;
168 
169  char* dir_path = strdup(_path);
170  char* dir_name = strtok(dir_path, "/");
171 
172  filesystem_tree_node_t* current = _root;
173  while (dir_name)
174  {
175  current = _find_directory(current, dir_name);
176  if (!current)
177  break;
178  dir_name = strtok(NULL, "/");
179  }
180 
181  free(dir_path);
182  return current;
183 }
184 
186  if (!_root || !_path || !*_path)
187  return NULL;
188 
189  char* dir_path = strdup(_path);
190  char* dir_name = strtok(dir_path, "/");
191  char* file_name = NULL;
192 
193  filesystem_tree_node_t* current = _root;
194  while (dir_name)
195  {
196  file_name = strtok(NULL, "/");
197  if (file_name)
198  {
199  filesystem_tree_node_t* found = _find_directory(current, dir_name);
200  if (!found)
201  {
202  free(dir_path);
203  return NULL;
204  }
205 
206  current = found;
207  dir_name = file_name;
208  }
209  else
210  break;
211  }
212 
213  filesystem_tree_file_t* file = NULL;
214  if (dir_name && !file_name)
215  file = _find_file(current, dir_name);
216 
217  free(dir_path);
218  return file;
219 }
220 
222 {
223  if (!_node || !_node->parent_)
224  return strdup("");
225 
226  char* parent_path = filesystem_tree_directory_path(_node->parent_);
227  size_t path_length = strlen(parent_path) + strlen(_node->name_) + 2;
228  char* path = (char*)malloc(path_length * sizeof(char));
229 
230  snprintf(path, path_length, "%s%s/", parent_path, _node->name_);
231 
232  free(parent_path);
233  return path;
234 }
235 
237 {
238  if (!_node || !_file)
239  return NULL;
240 
241  char* dir_path = filesystem_tree_directory_path(_node);
242  size_t path_length = strlen(dir_path) + strlen(_file->name_) + 1;
243  char* file_path = (char*)malloc(path_length * sizeof(char));
244 
245  snprintf(file_path, path_length, "%s%s", dir_path, _file->name_);
246 
247  free(dir_path);
248  return file_path;
249 }
250 
252 {
253  if (_root)
254  _free_node(_root);
255 }
256 
257 
258 static void _iterator_stack_push(filesystem_tree_iterator_t* _iterator, filesystem_iterator_state_t _state)
259 {
260  if (_iterator->stack_size_ >= _iterator->stack_capacity_)
261  {
262  _iterator->stack_capacity_ *= 2;
263  _iterator->stack_ = (filesystem_iterator_state_t*)realloc(_iterator->stack_, sizeof(filesystem_iterator_state_t) * _iterator->stack_capacity_);
264  }
265 
266  _iterator->stack_[_iterator->stack_size_++] = _state;
267 }
268 
269 static filesystem_iterator_state_t _iterator_stack_pop(filesystem_tree_iterator_t* _iterator)
270 {
271  if (_iterator->stack_size_ > 0)
272  return _iterator->stack_[--_iterator->stack_size_];
273 
274  filesystem_iterator_state_t empty_state = { .node_ = NULL, .child_index_ = 0ull, .file_index_ = 0ull };
275  return empty_state;
276 }
277 
279 {
280  if (!_root)
281  return NULL;
282 
284  iterator->stack_capacity_ = 16ull;
285  iterator->stack_size_ = 0ull;
286  iterator->stack_ = (filesystem_iterator_state_t*)malloc(sizeof(filesystem_iterator_state_t) * iterator->stack_capacity_);
287 
288  filesystem_iterator_state_t initial_state = { .node_ = _root, .child_index_ = 0ull, .file_index_ = 0ull };
289  iterator->stack_[iterator->stack_size_++] = initial_state;
290 
291  return iterator;
292 }
293 
295 {
296  if (_iterator->stack_size_ == 0)
297  return NULL;
298 
299  filesystem_iterator_state_t current_state = _iterator->stack_[_iterator->stack_size_ - 1];
300 
301  if (current_state.child_index_ < current_state.node_->num_children_)
302  {
303  filesystem_tree_node_t* next_directory = current_state.node_->children_[current_state.child_index_];
304  filesystem_iterator_state_t child_state = { .node_ = next_directory, .child_index_ = 0, .file_index_ = 0 };
305 
306  // Update the current state's child_index
307  current_state.child_index_++;
308  _iterator->stack_[_iterator->stack_size_ - 1] = current_state;
309 
310  // Push the child_state onto the stack
311  _iterator_stack_push(_iterator, child_state);
312 
313  return next_directory;
314  }
315  else
316 
317  // If there are no more children, remove the current state from the stack
318  _iterator_stack_pop(_iterator);
319 
320  // Call the function recursively to find the next directory
321  return filesystem_iterator_next_directory(_iterator);
322 }
323 
325 {
326  if (_iterator->stack_size_ == 0)
327  return NULL;
328 
329  filesystem_iterator_state_t current_state = _iterator->stack_[_iterator->stack_size_ - 1];
330 
331  if (current_state.child_index_ < current_state.node_->num_children_)
332  {
333  filesystem_tree_node_t* next_directory = current_state.node_->children_[current_state.child_index_];
334 
335  // Update the current state's child_index
336  current_state.child_index_++;
337  _iterator->stack_[_iterator->stack_size_ - 1] = current_state;
338 
339  return next_directory;
340  }
341 
342  return NULL;
343 }
344 
346 {
347  if (_iterator->stack_size_ == 0)
348  return NULL;
349 
350  filesystem_iterator_state_t current_state = _iterator->stack_[_iterator->stack_size_ - 1];
351 
352  if (current_state.file_index_ < current_state.node_->num_files_)
353  {
354  filesystem_tree_file_t* next_file = current_state.node_->files_[current_state.file_index_];
355  current_state.file_index_++;
356  _iterator->stack_[_iterator->stack_size_ - 1] = current_state;
357  return next_file;
358  }
359 
360  return NULL;
361 }
362 
364 {
365  if (_iterator)
366  {
367  free(_iterator->stack_);
368  free(_iterator);
369  }
370 }
GPAK_API filesystem_tree_node_t * filesystem_tree_find_directory(filesystem_tree_node_t *_root, const char *_path)
GPAK_API filesystem_tree_node_t * filesystem_iterator_next_subling_directory(filesystem_tree_iterator_t *_iterator)
GPAK_API char * filesystem_tree_file_path(filesystem_tree_node_t *_node, filesystem_tree_file_t *_file)
GPAK_API filesystem_tree_node_t * filesystem_tree_create()
GPAK_API filesystem_tree_node_t * filesystem_iterator_next_directory(filesystem_tree_iterator_t *_iterator)
GPAK_API void filesystem_iterator_free(filesystem_tree_iterator_t *_iterator)
GPAK_API void filesystem_tree_delete(filesystem_tree_node_t *_root)
GPAK_API char * filesystem_tree_directory_path(filesystem_tree_node_t *_node)
GPAK_API void filesystem_tree_add_file(filesystem_tree_node_t *_root, const char *_path, const char *_file_path, pak_entry_t _entry)
GPAK_API void filesystem_tree_add_directory(filesystem_tree_node_t *_root, const char *_path)
GPAK_API filesystem_tree_iterator_t * filesystem_iterator_create(filesystem_tree_node_t *_root)
GPAK_API filesystem_tree_file_t * filesystem_iterator_next_file(filesystem_tree_iterator_t *_iterator)
GPAK_API filesystem_tree_file_t * filesystem_tree_find_file(filesystem_tree_node_t *_root, const char *_path)
filesystem_tree_node_t * node_
Definition: gpak_data.h:347
pak_entry_t entry_
Definition: gpak_data.h:306
filesystem_iterator_state_t * stack_
Definition: gpak_data.h:367
struct filesystem_tree_node * parent_
Definition: gpak_data.h:325
struct filesystem_tree_node ** children_
Definition: gpak_data.h:326
filesystem_tree_file_t ** files_
Definition: gpak_data.h:328
Definition: gpak_data.h:133