Commit | Line | Data |
---|---|---|
db4042d6 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 (lstat(file_path, &link_info) == -1) { | |
46 | if (exit_on_error) | |
47 | PRINT_ERRNO_AND_EXIT(); | |
48 | else { | |
49 | /* Sometimes a file is created and then removed, this under Linux | |
50 | * inotify events can create confusion because we try to stat a | |
51 | * file that no longer exists. The file is removed before we can do | |
52 | * the stat. So we ignore the errno and return NULL to take account | |
53 | * of it. */ | |
54 | ||
55 | /* PRINT_ERRNO_INFO(); */ | |
56 | free(file_path); | |
57 | free(file); | |
58 | ||
59 | return NULL; | |
60 | } | |
61 | } else if (S_ISLNK(link_info.st_mode)) { | |
62 | if (link_info.st_size == 0) | |
63 | link_info.st_size = PATH_MAX; | |
64 | ||
65 | file->link_path = malloc(link_info.st_size + 1); | |
66 | file->link_path[link_info.st_size] = '\0'; | |
67 | file->link_path[0] = '\0'; | |
68 | file->link = TRUE; | |
69 | ||
70 | if (readlink(file_path, file->link_path, link_info.st_size) == -1) | |
71 | PRINT_ERRNO_INFO(); | |
72 | } else { | |
73 | file->link_path = NULL; | |
74 | file->link = FALSE; | |
75 | } | |
76 | if (stat(file_path, &info) == -1) { | |
77 | if (exit_on_error) | |
78 | PRINT_ERRNO_AND_EXIT(); | |
79 | else { | |
80 | PRINT_ERRNO_INFO(); | |
81 | file->type = file_type; | |
82 | } | |
83 | } else if (S_ISDIR(info.st_mode)) | |
84 | file->type = directory_type; | |
85 | else | |
86 | file->type = file_type; | |
87 | ||
88 | if (file->link == TRUE) | |
89 | file->info = link_info; | |
90 | else | |
91 | file->info = info; | |
92 | ||
93 | file->name = strdup(name); | |
94 | file->read = FALSE; | |
95 | file->open = FALSE; | |
96 | file->show_dotfiles = FALSE; | |
97 | file->line = NULL; | |
98 | file->dotfiles = NULL; | |
99 | file->watch = -1; | |
100 | ||
101 | if (path != NULL) | |
102 | free(file_path); | |
103 | ||
104 | return file; | |
105 | } | |
106 | ||
107 | gboolean read_directory(GNode *directory) | |
108 | { | |
109 | DIR *dir; | |
110 | struct dirent *entry; | |
111 | File *new_file; | |
112 | char *path = get_path(directory); | |
113 | int i; | |
114 | ||
115 | if ((dir = opendir(path)) == NULL) { | |
116 | PRINT_ERRNO_INFO(); | |
117 | return FALSE; | |
118 | } | |
119 | for (errno = 0, i = 0; (entry = readdir(dir)) != NULL; ) { | |
120 | if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) | |
121 | continue; | |
122 | ||
123 | if ((new_file = create_new_file(entry->d_name, path, FALSE)) == NULL) | |
124 | continue; | |
125 | ||
126 | if (new_file->name[0] == '.') { | |
127 | insert_sorted_in_dotfiles(directory, new_file); | |
128 | i = 1; | |
129 | } else { | |
130 | insert_sorted_in_tree(directory, new_file); | |
131 | i = 1; | |
132 | } | |
133 | } | |
134 | if (errno == EBADF) | |
135 | PRINT_ERRNO_INFO(); | |
136 | if (closedir(dir) == -1) | |
137 | PRINT_ERRNO_INFO(); | |
138 | free(path); | |
139 | if (i > 0) { | |
140 | FILE(directory)->read = TRUE; | |
141 | #ifdef INOTIFY | |
142 | add_watch_directory(directory); | |
143 | #endif | |
144 | return TRUE; | |
145 | } | |
146 | ||
147 | return FALSE; | |
148 | } | |
149 | ||
150 | void free_element_data(gpointer element_data, gpointer data) | |
151 | { | |
152 | if (FILE((GNode *) element_data)->type == directory_type) | |
153 | destroy_directory_content((GNode *) element_data); | |
154 | free_node_data((GNode *) element_data, NULL); | |
155 | } | |
156 | ||
157 | void destroy_dotfiles(GList *dotfiles) | |
158 | { | |
159 | g_list_foreach(dotfiles, free_element_data, NULL); | |
160 | g_list_free(dotfiles); | |
161 | } | |
162 | ||
163 | gboolean free_node_data(GNode *node, gpointer data) | |
164 | { | |
165 | free(FILE(node)->name); | |
166 | free(FILE(node)->link_path); | |
167 | destroy_dotfiles(FILE(node)->dotfiles); | |
168 | free(node->data); | |
169 | ||
170 | return FALSE; | |
171 | } | |
172 | ||
173 | void destroy_directory_content(GNode *directory) | |
174 | { | |
175 | destroy_directory_content_real(directory, TRUE); | |
176 | } | |
177 | ||
178 | void destroy_directory_content_real(GNode *directory, gboolean remove_watch) | |
179 | { | |
180 | GNode *file_ptr, *file_ptr2; | |
181 | ||
182 | #ifdef INOTIFY | |
183 | if (FILE(directory)->read == TRUE) | |
184 | remove_watch_directory(directory, remove_watch); | |
185 | #endif | |
186 | for (file_ptr = g_node_first_child(directory); file_ptr != NULL; ) { | |
187 | file_ptr2 = file_ptr; | |
188 | file_ptr = g_node_next_sibling(file_ptr); | |
189 | ||
190 | g_node_unlink(file_ptr2); | |
191 | g_node_traverse(file_ptr2, G_PRE_ORDER, G_TRAVERSE_ALL, -1, free_node_data, NULL); | |
192 | g_node_destroy(file_ptr2); | |
193 | } | |
194 | destroy_dotfiles(FILE(directory)->dotfiles); | |
195 | FILE(directory)->dotfiles = NULL; | |
196 | FILE(directory)->read = FALSE; | |
197 | } | |
198 | ||
199 | void add_dotfiles(GNode *directory) | |
200 | { | |
201 | GList *line_ptr; | |
202 | GNode *file_ptr; | |
203 | int position; | |
204 | ||
205 | for (line_ptr = g_list_next(FILE(directory)->line), position = 0; | |
206 | FILE(directory)->dotfiles != NULL; position++) { | |
207 | file_ptr = NODE(FILE(directory)->dotfiles); | |
208 | FILE(directory)->dotfiles = g_list_delete_link(FILE(directory)->dotfiles, | |
209 | FILE(directory)->dotfiles); | |
210 | g_node_insert(directory, position, file_ptr); | |
211 | lines = g_list_insert_before(lines, line_ptr, file_ptr); | |
212 | FILE(file_ptr)->line = g_list_previous(line_ptr); | |
213 | if (FILE(file_ptr)->type == directory_type && FILE(file_ptr)->open == TRUE) | |
214 | add_directory_content(file_ptr); | |
215 | } | |
216 | FILE(directory)->show_dotfiles = TRUE; | |
217 | } | |
218 | ||
219 | void remove_dotfiles(GNode *directory) | |
220 | { | |
221 | GNode *file_ptr; | |
222 | GList *line_ptr, *line_ptr_aux; | |
223 | ||
224 | for (file_ptr = g_node_first_child(directory); file_ptr != NULL && | |
225 | FILE(file_ptr)->name[0] == '.'; ) { | |
226 | lines = g_list_delete_link(lines, FILE(file_ptr)->line); | |
227 | FILE(directory)->dotfiles = g_list_insert(FILE(directory)->dotfiles, file_ptr, -1); | |
228 | FILE(file_ptr)->line = NULL; | |
229 | file_ptr = g_node_next_sibling(file_ptr); | |
230 | g_node_unlink(NODE(g_list_last(FILE(directory)->dotfiles))); | |
231 | ||
232 | for (line_ptr = g_list_previous(FILE(file_ptr)->line); | |
233 | line_ptr != FILE(directory)->line; ) { | |
234 | line_ptr_aux = line_ptr; | |
235 | line_ptr = g_list_previous(line_ptr); | |
236 | FILE(NODE(line_ptr_aux))->line = NULL; | |
237 | lines = g_list_delete_link(lines, line_ptr_aux); | |
238 | } | |
239 | } | |
240 | FILE(directory)->show_dotfiles = FALSE; | |
241 | } | |
242 | ||
243 | int file_cmp(File *file1, File *file2) | |
244 | { | |
245 | if (file1->name[0] == '.' && file2->name[0] != '.') | |
246 | return -1; | |
247 | else if (file1->name[0] != '.' && file2->name[0] == '.') | |
248 | return 1; | |
249 | else if (file1->type == file2->type) | |
250 | return strcmp(file1->name, file2->name); | |
251 | else if (file1->type == directory_type) | |
252 | return -1; | |
253 | else | |
254 | return 1; | |
255 | } | |
256 | ||
257 | int file_cmp_list(gconstpointer file1, gconstpointer file2) | |
258 | { | |
259 | return file_cmp(FILE((GNode *) file1), FILE((GNode *) file2)); | |
260 | } | |
261 | ||
262 | void insert_sorted_in_dotfiles(GNode *directory, File *file) | |
263 | { | |
264 | FILE(directory)->dotfiles = g_list_insert_sorted(FILE(directory)->dotfiles, | |
265 | g_node_new(file), file_cmp_list); | |
266 | } | |
267 | ||
268 | GNode *insert_sorted_in_tree(GNode *directory, File *file) | |
269 | { | |
270 | GNode *file_ptr, *new_file; | |
271 | ||
272 | if (G_NODE_IS_LEAF(directory)) { | |
273 | g_node_append_data(directory, file); | |
274 | return g_node_first_child(directory); | |
275 | } else if (file_cmp(file, FILE(g_node_first_child(directory))) < 0) { | |
276 | g_node_prepend_data(directory, file); | |
277 | return g_node_first_child(directory); | |
278 | } else { | |
279 | for (file_ptr = g_node_last_child(directory); file_ptr != NULL; | |
280 | file_ptr = g_node_prev_sibling(file_ptr)) { | |
281 | if (file_cmp(file, FILE(file_ptr)) > 0) { | |
282 | new_file = g_node_new(file); | |
283 | g_node_insert_after(directory, file_ptr, new_file); | |
284 | break; | |
285 | } | |
286 | } | |
287 | ||
288 | return new_file; | |
289 | } | |
290 | } | |
291 | ||
292 | char *get_path(GNode *file) | |
293 | { | |
294 | GNode *file_ptr; | |
295 | GString *path = g_string_new(FILE(file)->name); | |
296 | ||
297 | for (file_ptr = file->parent; file_ptr != NULL; file_ptr = file_ptr->parent) { | |
298 | g_string_prepend(path, "/"); | |
299 | g_string_prepend(path, FILE(file_ptr)->name); | |
300 | } | |
301 | ||
302 | return g_string_free(path, FALSE); | |
303 | } | |
304 | ||
305 | GNode *get_next_file_real(GNode *file, gboolean go_deeper) | |
306 | { | |
307 | GNode *file_ptr, *directory_ptr; | |
308 | ||
309 | if (go_deeper && (file_ptr = g_node_first_child(file)) != NULL && FILE(file)->open == TRUE) | |
310 | return file_ptr; | |
311 | else if ((file_ptr = g_node_next_sibling(file)) != NULL) | |
312 | return file_ptr; | |
313 | else { | |
314 | for (directory_ptr = file->parent; directory_ptr != NULL; | |
315 | directory_ptr = directory_ptr->parent) { | |
316 | if ((file_ptr = g_node_next_sibling(directory_ptr)) != NULL) | |
317 | return file_ptr; | |
318 | } | |
319 | ||
320 | return NULL; | |
321 | } | |
322 | } | |
323 | ||
324 | GNode *get_previous_file(GNode *file) | |
325 | { | |
326 | GNode *file_ptr; | |
327 | ||
328 | if (G_NODE_IS_ROOT(file)) | |
329 | return NULL; | |
330 | else if (file == g_node_first_sibling(file)) | |
331 | return file->parent; | |
332 | else if (!G_NODE_IS_LEAF(file_ptr = g_node_prev_sibling(file)) && | |
333 | FILE(file_ptr)->open == TRUE) { | |
334 | do | |
335 | file_ptr = g_node_last_child(file_ptr); | |
336 | while (!G_NODE_IS_LEAF(file_ptr) && FILE(file_ptr)->open == TRUE); | |
337 | return file_ptr; | |
338 | } else | |
339 | return file_ptr; | |
340 | } | |
341 | ||
342 | GNode *get_next_file(GNode *file) | |
343 | { | |
344 | return get_next_file_real(file, TRUE); | |
345 | } | |
346 | ||
347 | GNode *get_next_file_not_deepper(GNode *file) | |
348 | { | |
349 | return get_next_file_real(file, FALSE); | |
350 | } | |
351 | ||
352 | char *get_file_info(GNode *file) | |
353 | { | |
354 | struct passwd *pw; | |
355 | struct group *grp; | |
356 | struct tm *mtime; | |
357 | char mode[11], mtime_str[17], size_suffix; | |
358 | double size; | |
359 | ||
360 | switch (FILE(file)->info.st_mode & S_IFMT) { | |
361 | case S_IFSOCK: | |
362 | mode[0] = 's'; | |
363 | break; | |
364 | case S_IFLNK: | |
365 | mode[0] = 'l'; | |
366 | break; | |
367 | case S_IFREG: | |
368 | mode[0] = '-'; | |
369 | break; | |
370 | case S_IFBLK: | |
371 | mode[0] = 'b'; | |
372 | break; | |
373 | case S_IFDIR: | |
374 | mode[0] = 'd'; | |
375 | break; | |
376 | case S_IFCHR: | |
377 | mode[0] = 'c'; | |
378 | break; | |
379 | case S_IFIFO: | |
380 | mode[0] = 'p'; | |
381 | break; | |
382 | } | |
383 | mode[1] = (FILE(file)->info.st_mode & S_IRUSR) ? 'r' : '-'; | |
384 | mode[2] = (FILE(file)->info.st_mode & S_IWUSR) ? 'w' : '-'; | |
385 | mode[3] = (FILE(file)->info.st_mode & S_IXUSR) ? 'x' : '-'; | |
386 | ||
387 | mode[4] = (FILE(file)->info.st_mode & S_IRGRP) ? 'r' : '-'; | |
388 | mode[5] = (FILE(file)->info.st_mode & S_IWGRP) ? 'w' : '-'; | |
389 | mode[6] = (FILE(file)->info.st_mode & S_IXGRP) ? 'x' : '-'; | |
390 | ||
391 | mode[7] = (FILE(file)->info.st_mode & S_IROTH) ? 'r' : '-'; | |
392 | mode[8] = (FILE(file)->info.st_mode & S_IWOTH) ? 'w' : '-'; | |
393 | mode[9] = (FILE(file)->info.st_mode & S_IXOTH) ? 'x' : '-'; | |
394 | mode[10] = '\0'; | |
395 | ||
396 | if (FILE(file)->info.st_mode & S_ISUID && mode[3] == 'x') | |
397 | mode[9] = 's'; | |
398 | else if (FILE(file)->info.st_mode & S_ISUID && mode[3] == '-') | |
399 | mode[9] = 'S'; | |
400 | if (FILE(file)->info.st_mode & S_ISGID && mode[6] == 'x') | |
401 | mode[9] = 's'; | |
402 | else if (FILE(file)->info.st_mode & S_ISGID && mode[6] == '-') | |
403 | mode[9] = 'S'; | |
404 | if (FILE(file)->info.st_mode & S_ISVTX && mode[9] == 'x') | |
405 | mode[9] = 't'; | |
406 | else if (FILE(file)->info.st_mode & S_ISVTX && mode[9] == '-') | |
407 | mode[9] = 'T'; | |
408 | ||
409 | if ((pw = getpwuid(FILE(file)->info.st_uid)) == NULL) { | |
410 | if (errno == 0) { | |
411 | struct passwd pw_unknown; | |
412 | ||
413 | pw_unknown.pw_name = "unknown"; | |
414 | pw = &pw_unknown; | |
415 | } else { | |
416 | PRINT_ERRNO_INFO(); | |
417 | return NULL; | |
418 | } | |
419 | } if ((grp = getgrgid(FILE(file)->info.st_gid)) == NULL) { | |
420 | if (errno == 0) { | |
421 | struct group grp_unknown; | |
422 | ||
423 | grp_unknown.gr_name = "unknown"; | |
424 | grp = &grp_unknown; | |
425 | } else { | |
426 | PRINT_ERRNO_INFO(); | |
427 | return NULL; | |
428 | } | |
429 | } | |
430 | ||
431 | if ((size =(double) FILE(file)->info.st_size / (1024.0 * 1024.0 * 1024.0)) > 1.0) | |
432 | size_suffix = 'G'; | |
433 | else if ((size =(double) FILE(file)->info.st_size / (1024.0 * 1024.0)) > 1.0) | |
434 | size_suffix = 'M'; | |
435 | else if ((size =(double) FILE(file)->info.st_size / (1024.0)) > 1.0) | |
436 | size_suffix = 'K'; | |
437 | else { | |
438 | size =(double) FILE(file)->info.st_size; | |
439 | size_suffix = ' '; | |
440 | } | |
441 | ||
442 | if ((mtime = localtime(&(FILE(file)->info.st_mtime))) == NULL) { | |
443 | PRINT_ERRNO_INFO(); | |
444 | return NULL; | |
445 | } | |
446 | if (strftime(mtime_str, sizeof(mtime_str), "%Y-%m-%d %H:%M", mtime) == 0) { | |
447 | PRINT_ERRNO_INFO(); | |
448 | return NULL; | |
449 | } | |
450 | ||
451 | if (FILE(file)->link == TRUE) { | |
452 | if (size_suffix == ' ') | |
453 | return g_strdup_printf("%s %s %s %i %s %s -> %s", mode, pw->pw_name, grp->gr_name, | |
454 | (int) size, mtime_str, FILE(file)->name, FILE(file)->link_path); | |
455 | else | |
456 | return g_strdup_printf("%s %s %s %.2f%c %s %s -> %s", mode, pw->pw_name, grp->gr_name, | |
457 | size, size_suffix, mtime_str, FILE(file)->name, FILE(file)->link_path); | |
458 | } else { | |
459 | if (size_suffix == ' ') | |
460 | return g_strdup_printf("%s %s %s %i %s %s", mode, pw->pw_name, grp->gr_name, | |
461 | (int) size, mtime_str, FILE(file)->name); | |
462 | else | |
463 | return g_strdup_printf("%s %s %s %.2f%c %s %s", mode, pw->pw_name, grp->gr_name, | |
464 | size, size_suffix, mtime_str, FILE(file)->name); | |
465 | } | |
466 | } |