Commit | Line | Data |
---|---|---|
21c4e167 H |
1 | /* Copyright (C) 2008 Ricardo Catalinas Jiménez <jimenezrick@gmail.com> |
2 | * | |
3 | * This program is free software: you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License as published by | |
5 | * the Free Software Foundation, either version 3 of the License, or | |
6 | * (at your option) any later version. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include "fm.h" | |
18 | ||
19 | GNode *tree_root; | |
20 | ||
21 | void init_tree(char *name) | |
22 | { | |
23 | char *path; | |
24 | ||
25 | if (name != NULL) | |
26 | tree_root = g_node_new(create_new_file(name, NULL, TRUE)); | |
27 | else { | |
28 | path = g_get_current_dir(); | |
29 | tree_root = g_node_new(create_new_file(path, NULL, TRUE)); | |
30 | free(path); | |
31 | } | |
32 | } | |
33 | ||
34 | File *create_new_file(char *name, char *path, gboolean exit_on_error) | |
35 | { | |
36 | struct stat info, link_info; | |
37 | File *file = malloc(sizeof(File)); | |
38 | char *file_path; | |
39 | ||
40 | if (path != NULL) | |
41 | file_path = g_strconcat(path, "/", name, NULL); | |
42 | else | |
43 | file_path = name; | |
44 | ||
45 | if (stat(file_path, &info) == -1) { | |
46 | if (exit_on_error) | |
47 | PRINT_ERRNO_AND_EXIT(); | |
48 | else { | |
49 | PRINT_ERRNO_INFO(); | |
50 | file->type = file_type; | |
51 | file->no_info = TRUE; | |
52 | } | |
53 | } else if (S_ISDIR(info.st_mode)) { | |
54 | file->type = directory_type; | |
55 | file->no_info = FALSE; | |
56 | } else { | |
57 | file->type = file_type; | |
58 | file->no_info = FALSE; | |
59 | } | |
60 | ||
61 | if (lstat(file_path, &link_info) == -1) { | |
62 | PRINT_ERRNO_INFO(); | |
63 | file->link_path = NULL; | |
64 | file->link = FALSE; | |
65 | } else if (S_ISLNK(link_info.st_mode)) { | |
66 | if (link_info.st_size == 0) | |
67 | link_info.st_size = PATH_MAX; | |
68 | ||
69 | file->link_path = malloc(link_info.st_size + 1); | |
70 | file->link_path[link_info.st_size] = '\0'; | |
71 | file->link_path[0] = '\0'; | |
72 | file->link = TRUE; | |
73 | ||
74 | if (readlink(file_path, file->link_path, link_info.st_size) == -1) | |
75 | PRINT_ERRNO_INFO(); | |
76 | } else { | |
77 | file->link_path = NULL; | |
78 | file->link = FALSE; | |
79 | } | |
80 | if (file->link == TRUE) { | |
81 | file->no_info = FALSE; | |
82 | file->info = link_info; | |
83 | } else if (file->no_info == FALSE) | |
84 | file->info = info; | |
85 | ||
86 | file->name = strdup(name); | |
87 | file->read = FALSE; | |
88 | file->open = FALSE; | |
89 | file->show_dotfiles = FALSE; | |
90 | file->line = NULL; | |
91 | file->dotfiles = NULL; | |
92 | ||
93 | if (path != NULL) | |
94 | free(file_path); | |
95 | ||
96 | return file; | |
97 | } | |
98 | ||
99 | gboolean read_directory(GNode *directory) | |
100 | { | |
101 | DIR *dir; | |
102 | struct dirent *entry; | |
103 | File *new_file; | |
104 | char *path = get_path(directory); | |
105 | ||
106 | if ((dir = opendir(path)) == NULL) { | |
107 | PRINT_ERRNO_INFO(); | |
108 | return FALSE; | |
109 | } | |
110 | for (; (entry = readdir(dir)) != NULL; ) { | |
111 | if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) | |
112 | continue; | |
113 | ||
114 | if ((new_file = create_new_file(entry->d_name, path, FALSE)) == NULL) | |
115 | continue; | |
116 | ||
117 | if (new_file->name[0] == '.') | |
118 | insert_sorted_in_dotfiles(directory, new_file); | |
119 | else | |
120 | insert_sorted_in_tree(directory, new_file); | |
121 | } | |
122 | if (errno == EBADF) | |
123 | PRINT_ERRNO_AND_EXIT(); | |
124 | if (closedir(dir) == -1) | |
125 | PRINT_ERRNO_AND_EXIT(); | |
126 | free(path); | |
127 | FILE(directory)->read = TRUE; | |
128 | ||
129 | return TRUE; | |
130 | } | |
131 | ||
132 | void free_element_data(gpointer element_data, gpointer data) | |
133 | { | |
134 | if (FILE((GNode *) element_data)->type == directory_type) | |
135 | destroy_directory_content((GNode *) element_data); | |
136 | free_node_data((GNode *) element_data, NULL); | |
137 | } | |
138 | ||
139 | void destroy_dotfiles(GList *dotfiles) | |
140 | { | |
141 | g_list_foreach(dotfiles, free_element_data, NULL); | |
142 | g_list_free(dotfiles); | |
143 | } | |
144 | ||
145 | gboolean free_node_data(GNode *node, gpointer data) | |
146 | { | |
147 | free(FILE(node)->name); | |
148 | free(FILE(node)->link_path); | |
149 | destroy_dotfiles(FILE(node)->dotfiles); | |
150 | free(node->data); | |
151 | ||
152 | return FALSE; | |
153 | } | |
154 | ||
155 | void destroy_directory_content(GNode *directory) | |
156 | { | |
157 | GNode *file_ptr, *file_ptr2; | |
158 | ||
159 | for (file_ptr = g_node_first_child(directory); file_ptr != NULL; ) { | |
160 | file_ptr2 = file_ptr; | |
161 | file_ptr = g_node_next_sibling(file_ptr); | |
162 | ||
163 | g_node_unlink(file_ptr2); | |
164 | g_node_traverse(file_ptr2, G_PRE_ORDER, G_TRAVERSE_ALL, -1, free_node_data, NULL); | |
165 | g_node_destroy(file_ptr2); | |
166 | } | |
167 | destroy_dotfiles(FILE(directory)->dotfiles); | |
168 | FILE(directory)->dotfiles = NULL; | |
169 | FILE(directory)->read = FALSE; | |
170 | } | |
171 | ||
172 | void add_dotfiles(GNode *directory) | |
173 | { | |
174 | GList *line_ptr; | |
175 | GNode *file_ptr; | |
176 | int position; | |
177 | ||
178 | for (line_ptr = g_list_next(FILE(directory)->line), position = 0; | |
179 | FILE(directory)->dotfiles != NULL; position++) { | |
180 | file_ptr = NODE(FILE(directory)->dotfiles); | |
181 | FILE(directory)->dotfiles = g_list_delete_link(FILE(directory)->dotfiles, | |
182 | FILE(directory)->dotfiles); | |
183 | g_node_insert(directory, position, file_ptr); | |
184 | lines = g_list_insert_before(lines, line_ptr, file_ptr); | |
185 | FILE(file_ptr)->line = g_list_previous(line_ptr); | |
186 | if (FILE(file_ptr)->type == directory_type && FILE(file_ptr)->open == TRUE) | |
187 | add_directory_content(file_ptr); | |
188 | } | |
189 | FILE(directory)->show_dotfiles = TRUE; | |
190 | } | |
191 | ||
192 | void remove_dotfiles(GNode *directory) | |
193 | { | |
194 | GNode *file_ptr; | |
195 | GList *line_ptr, *line_ptr_aux; | |
196 | ||
197 | for (file_ptr = g_node_first_child(directory); file_ptr != NULL && | |
198 | FILE(file_ptr)->name[0] == '.'; ) { | |
199 | lines = g_list_delete_link(lines, FILE(file_ptr)->line); | |
200 | FILE(directory)->dotfiles = g_list_insert(FILE(directory)->dotfiles, file_ptr, -1); | |
201 | FILE(file_ptr)->line = NULL; | |
202 | file_ptr = g_node_next_sibling(file_ptr); | |
203 | g_node_unlink(NODE(g_list_last(FILE(directory)->dotfiles))); | |
204 | ||
205 | for (line_ptr = g_list_previous(FILE(file_ptr)->line); | |
206 | line_ptr != FILE(directory)->line; ) { | |
207 | line_ptr_aux = line_ptr; | |
208 | line_ptr = g_list_previous(line_ptr); | |
209 | FILE(NODE(line_ptr_aux))->line = NULL; | |
210 | lines = g_list_delete_link(lines, line_ptr_aux); | |
211 | } | |
212 | } | |
213 | FILE(directory)->show_dotfiles = FALSE; | |
214 | } | |
215 | ||
216 | int file_cmp(File *file1, File *file2) | |
217 | { | |
218 | if (file1->type == file2->type) | |
219 | return strcmp(file1->name, file2->name); | |
220 | else if (file1->type == directory_type) | |
221 | return -1; | |
222 | else | |
223 | return 1; | |
224 | } | |
225 | ||
226 | int file_cmp_list(gconstpointer file1, gconstpointer file2) | |
227 | { | |
228 | return file_cmp(FILE((GNode *) file1), FILE((GNode *) file2)); | |
229 | } | |
230 | ||
231 | void insert_sorted_in_dotfiles(GNode *directory, File *file) | |
232 | { | |
233 | FILE(directory)->dotfiles = g_list_insert_sorted(FILE(directory)->dotfiles, | |
234 | g_node_new(file), file_cmp_list); | |
235 | } | |
236 | ||
237 | void insert_sorted_in_tree(GNode *directory, File *file) | |
238 | { | |
239 | GNode *file_ptr; | |
240 | ||
241 | if (G_NODE_IS_LEAF(directory)) | |
242 | g_node_append_data(directory, file); | |
243 | else if (file_cmp(file, FILE(g_node_first_child(directory))) < 0) | |
244 | g_node_prepend_data(directory, file); | |
245 | else { | |
246 | for (file_ptr = g_node_last_child(directory); file_ptr != NULL; | |
247 | file_ptr = g_node_prev_sibling(file_ptr)) { | |
248 | if (file_cmp(file, FILE(file_ptr)) > 0) { | |
249 | g_node_insert_after(directory, file_ptr, g_node_new(file)); | |
250 | break; | |
251 | } | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | char *get_path(GNode *file) | |
257 | { | |
258 | GNode *file_ptr; | |
259 | GString *path = g_string_new(FILE(file)->name); | |
260 | ||
261 | for (file_ptr = file->parent; file_ptr != NULL; file_ptr = file_ptr->parent) { | |
262 | g_string_prepend(path, "/"); | |
263 | g_string_prepend(path, FILE(file_ptr)->name); | |
264 | } | |
265 | ||
266 | return g_string_free(path, FALSE); | |
267 | } | |
268 | ||
269 | GNode *get_next_file__real(GNode *file, gboolean go_deeper) | |
270 | { | |
271 | GNode *file_ptr, *directory_ptr; | |
272 | ||
273 | if (go_deeper && (file_ptr = g_node_first_child(file)) != NULL && FILE(file)->open == TRUE) | |
274 | return file_ptr; | |
275 | else if ((file_ptr = g_node_next_sibling(file)) != NULL) | |
276 | return file_ptr; | |
277 | else { | |
278 | for (directory_ptr = file->parent; directory_ptr != NULL; | |
279 | directory_ptr = directory_ptr->parent) { | |
280 | if ((file_ptr = g_node_next_sibling(directory_ptr)) != NULL) | |
281 | return file_ptr; | |
282 | } | |
283 | ||
284 | return NULL; | |
285 | } | |
286 | } | |
287 | ||
288 | GNode *get_previous_file(GNode *file) | |
289 | { | |
290 | GNode *file_ptr; | |
291 | ||
292 | if (G_NODE_IS_ROOT(file)) | |
293 | return NULL; | |
294 | else if (file == g_node_first_sibling(file)) | |
295 | return file->parent; | |
296 | else if (!G_NODE_IS_LEAF(file_ptr = g_node_prev_sibling(file)) && | |
297 | FILE(file_ptr)->open == TRUE) { | |
298 | do | |
299 | file_ptr = g_node_last_child(file_ptr); | |
300 | while (!G_NODE_IS_LEAF(file_ptr) && FILE(file_ptr)->open == TRUE); | |
301 | return file_ptr; | |
302 | } else | |
303 | return file_ptr; | |
304 | } | |
305 | ||
306 | GNode *get_next_file(GNode *file) | |
307 | { | |
308 | return get_next_file__real(file, TRUE); | |
309 | } | |
310 | ||
311 | GNode *get_next_file_not_deepper(GNode *file) | |
312 | { | |
313 | return get_next_file__real(file, FALSE); | |
314 | } | |
315 | ||
316 | char *get_file_info(GNode *file) | |
317 | { | |
318 | struct passwd *pw; | |
319 | struct group *grp; | |
320 | struct tm *mtime; | |
321 | char mode[11], mtime_str[17], size_suffix; | |
322 | double size; | |
323 | ||
324 | switch (FILE(file)->info.st_mode & S_IFMT) { | |
325 | case S_IFSOCK: | |
326 | mode[0] = 's'; | |
327 | break; | |
328 | case S_IFLNK: | |
329 | mode[0] = 'l'; | |
330 | break; | |
331 | case S_IFREG: | |
332 | mode[0] = '-'; | |
333 | break; | |
334 | case S_IFBLK: | |
335 | mode[0] = 'b'; | |
336 | break; | |
337 | case S_IFDIR: | |
338 | mode[0] = 'd'; | |
339 | break; | |
340 | case S_IFCHR: | |
341 | mode[0] = 'c'; | |
342 | break; | |
343 | case S_IFIFO: | |
344 | mode[0] = 'p'; | |
345 | break; | |
346 | } | |
347 | mode[1] = (FILE(file)->info.st_mode & S_IRUSR) ? 'r' : '-'; | |
348 | mode[2] = (FILE(file)->info.st_mode & S_IWUSR) ? 'w' : '-'; | |
349 | mode[3] = (FILE(file)->info.st_mode & S_IXUSR) ? 'x' : '-'; | |
350 | ||
351 | mode[4] = (FILE(file)->info.st_mode & S_IRGRP) ? 'r' : '-'; | |
352 | mode[5] = (FILE(file)->info.st_mode & S_IWGRP) ? 'w' : '-'; | |
353 | mode[6] = (FILE(file)->info.st_mode & S_IXGRP) ? 'x' : '-'; | |
354 | ||
355 | mode[7] = (FILE(file)->info.st_mode & S_IROTH) ? 'r' : '-'; | |
356 | mode[8] = (FILE(file)->info.st_mode & S_IWOTH) ? 'w' : '-'; | |
357 | mode[9] = (FILE(file)->info.st_mode & S_IXOTH) ? 'x' : '-'; | |
358 | mode[10] = '\0'; | |
359 | ||
360 | if (FILE(file)->info.st_mode & S_ISUID && mode[3] == 'x') | |
361 | mode[9] = 's'; | |
362 | else if (FILE(file)->info.st_mode & S_ISUID && mode[3] == '-') | |
363 | mode[9] = 'S'; | |
364 | if (FILE(file)->info.st_mode & S_ISGID && mode[6] == 'x') | |
365 | mode[9] = 's'; | |
366 | else if (FILE(file)->info.st_mode & S_ISGID && mode[6] == '-') | |
367 | mode[9] = 'S'; | |
368 | if (FILE(file)->info.st_mode & S_ISVTX && mode[9] == 'x') | |
369 | mode[9] = 't'; | |
370 | else if (FILE(file)->info.st_mode & S_ISVTX && mode[9] == '-') | |
371 | mode[9] = 'T'; | |
372 | ||
373 | if ((pw = getpwuid(FILE(file)->info.st_uid)) == NULL) { | |
374 | PRINT_ERRNO_INFO(); | |
375 | return NULL; | |
376 | } if ((grp = getgrgid(FILE(file)->info.st_gid)) == NULL) { | |
377 | PRINT_ERRNO_INFO(); | |
378 | return NULL; | |
379 | } | |
380 | ||
381 | if ((size =(double) FILE(file)->info.st_size / (1024.0 * 1024.0 * 1024.0)) > 1.0) | |
382 | size_suffix = 'G'; | |
383 | else if ((size =(double) FILE(file)->info.st_size / (1024.0 * 1024.0)) > 1.0) | |
384 | size_suffix = 'M'; | |
385 | else if ((size =(double) FILE(file)->info.st_size / (1024.0)) > 1.0) | |
386 | size_suffix = 'K'; | |
387 | else { | |
388 | size =(double) FILE(file)->info.st_size; | |
389 | size_suffix = ' '; | |
390 | } | |
391 | ||
392 | if ((mtime = localtime(&(FILE(file)->info.st_mtime))) == NULL) { | |
393 | PRINT_ERRNO_INFO(); | |
394 | return NULL; | |
395 | } | |
396 | if (strftime(mtime_str, sizeof(mtime_str), "%Y-%m-%d %H:%M", mtime) == 0) { | |
397 | PRINT_ERRNO_INFO(); | |
398 | return NULL; | |
399 | } | |
400 | ||
401 | if (FILE(file)->link == TRUE) { | |
402 | if (size_suffix == ' ') | |
403 | return g_strdup_printf("%s %s %s %i %s %s -> %s", mode, pw->pw_name, grp->gr_name, | |
404 | (int) size, mtime_str, FILE(file)->name, FILE(file)->link_path); | |
405 | else | |
406 | return g_strdup_printf("%s %s %s %.2f%c %s %s -> %s", mode, pw->pw_name, grp->gr_name, | |
407 | size, size_suffix, mtime_str, FILE(file)->name, FILE(file)->link_path); | |
408 | } else { | |
409 | if (size_suffix == ' ') | |
410 | return g_strdup_printf("%s %s %s %i %s %s", mode, pw->pw_name, grp->gr_name, | |
411 | (int) size, mtime_str, FILE(file)->name); | |
412 | else | |
413 | return g_strdup_printf("%s %s %s %.2f%c %s %s", mode, pw->pw_name, grp->gr_name, | |
414 | size, size_suffix, mtime_str, FILE(file)->name); | |
415 | } | |
416 | } |