Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / dir.c
blob23f61ac9e09221cbc86aeb2752640a42dbdb0436
1 /* Directory routines
2 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 /** \file dir.c
20 * \brief Source: directory routines
23 #include <config.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
30 #include "lib/global.h"
31 #include "lib/tty/tty.h"
32 #include "lib/search.h"
33 #include "lib/vfs/mc-vfs/vfs.h"
34 #include "lib/fs.h"
35 #include "lib/strutil.h"
36 #include "lib/util.h"
37 #include "lib/widget.h" /* message() */
39 #include "treestore.h"
40 #include "dir.h"
41 #include "setup.h" /* panels_options */
42 #include "layout.h" /* rotate_dash() */
44 /*** global variables ****************************************************************************/
46 /*** file scope macro definitions ****************************************************************/
48 #define MY_ISDIR(x) (\
49 (is_exe (x->st.st_mode) && !(S_ISDIR (x->st.st_mode) || x->f.link_to_dir) && (exec_first == 1)) \
50 ? 1 \
51 : ( (S_ISDIR (x->st.st_mode) || x->f.link_to_dir) ? 2 : 0) )
53 /*** file scope type declarations ****************************************************************/
55 /*** file scope variables ************************************************************************/
57 /* Reverse flag */
58 static int reverse = 1;
60 /* Are the files sorted case sensitively? */
61 static int case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
63 /* Are the exec_bit files top in list */
64 static int exec_first = 1;
66 static dir_list dir_copy = { 0, 0 };
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
72 sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
73 { N_("&Unsorted"), unsorted },
74 { N_("&Name"), sort_name },
75 { N_("&Extension"), sort_ext },
76 { N_("&Modify time"), sort_time },
77 { N_("&Access time"), sort_atime },
78 { N_("C&Hange time"), sort_ctime },
79 { N_("&Size"), sort_size },
80 { N_("&Inode"), sort_inode },
84 static inline int
85 key_collate (const char *t1, const char *t2)
87 int dotdot = 0;
88 int ret;
90 dotdot = (t1[0] == '.' ? 1 : 0) | ((t2[0] == '.' ? 1 : 0) << 1);
92 switch (dotdot)
94 case 0:
95 case 3:
96 ret = str_key_collate (t1, t2, case_sensitive) * reverse;
97 break;
98 case 1:
99 ret = -1; /* t1 < t2 */
100 break;
101 case 2:
102 ret = 1; /* t1 > t2 */
103 break;
104 default:
105 ret = 0; /* it must not happen */
108 return ret;
111 /* --------------------------------------------------------------------------------------------- */
112 /** clear keys, should be call after sorting is finished */
114 static void
115 clean_sort_keys (dir_list * list, int start, int count)
117 int i;
119 for (i = 0; i < count; i++)
121 str_release_key (list->list[i + start].sort_key, case_sensitive);
122 list->list[i + start].sort_key = NULL;
123 str_release_key (list->list[i + start].second_sort_key, case_sensitive);
124 list->list[i + start].second_sort_key = NULL;
128 /* --------------------------------------------------------------------------------------------- */
130 * If you change handle_dirent then check also handle_path.
131 * @returns -1 = failure, 0 = don't add, 1 = add to the list
134 static int
135 handle_dirent (dir_list * list, const char *fltr, struct dirent *dp,
136 struct stat *buf1, int next_free, int *link_to_dir, int *stale_link)
138 if (dp->d_name[0] == '.' && dp->d_name[1] == 0)
139 return 0;
140 if (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == 0)
141 return 0;
142 if (!panels_options.show_dot_files && (dp->d_name[0] == '.'))
143 return 0;
144 if (!panels_options.show_backups && dp->d_name[NLENGTH (dp) - 1] == '~')
145 return 0;
147 if (mc_lstat (dp->d_name, buf1) == -1)
150 * lstat() fails - such entries should be identified by
151 * buf1->st_mode being 0.
152 * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
154 memset (buf1, 0, sizeof (*buf1));
157 if (S_ISDIR (buf1->st_mode))
158 tree_store_mark_checked (dp->d_name);
160 /* A link to a file or a directory? */
161 *link_to_dir = 0;
162 *stale_link = 0;
163 if (S_ISLNK (buf1->st_mode))
165 struct stat buf2;
166 if (!mc_stat (dp->d_name, &buf2))
167 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
168 else
169 *stale_link = 1;
171 if (!(S_ISDIR (buf1->st_mode) || *link_to_dir) && (fltr != NULL)
172 && !mc_search (fltr, dp->d_name, MC_SEARCH_T_GLOB))
173 return 0;
175 /* Need to grow the *list? */
176 if (next_free == list->size)
178 list->list = g_try_realloc (list->list, sizeof (file_entry) * (list->size + RESIZE_STEPS));
179 if (list->list == NULL)
180 return -1;
181 list->size += RESIZE_STEPS;
183 return 1;
186 /* --------------------------------------------------------------------------------------------- */
187 /** get info about ".." */
189 static gboolean
190 get_dotdot_dir_stat (const char *path, struct stat *st)
192 gboolean ret = FALSE;
194 if ((path != NULL) && (path[0] != '\0') && (st != NULL))
196 char *dotdot_dir;
197 struct stat s;
199 dotdot_dir = g_strdup_printf ("%s/../", path);
200 canonicalize_pathname (dotdot_dir);
201 ret = mc_stat (dotdot_dir, &s) == 0;
202 g_free (dotdot_dir);
203 *st = s;
206 return ret;
209 /* --------------------------------------------------------------------------------------------- */
211 static void
212 alloc_dir_copy (int size)
214 if (dir_copy.size < size)
216 if (dir_copy.list)
218 int i;
219 for (i = 0; i < dir_copy.size; i++)
220 g_free (dir_copy.list[i].fname);
221 g_free (dir_copy.list);
224 dir_copy.list = g_new0 (file_entry, size);
225 dir_copy.size = size;
229 /* --------------------------------------------------------------------------------------------- */
230 /*** public functions ****************************************************************************/
231 /* --------------------------------------------------------------------------------------------- */
234 unsorted (file_entry * a, file_entry * b)
236 (void) a;
237 (void) b;
238 return 0;
241 /* --------------------------------------------------------------------------------------------- */
244 sort_name (file_entry * a, file_entry * b)
246 int ad = MY_ISDIR (a);
247 int bd = MY_ISDIR (b);
249 if (ad == bd || panels_options.mix_all_files)
251 /* create key if does not exist, key will be freed after sorting */
252 if (a->sort_key == NULL)
253 a->sort_key = str_create_key_for_filename (a->fname, case_sensitive);
254 if (b->sort_key == NULL)
255 b->sort_key = str_create_key_for_filename (b->fname, case_sensitive);
257 return key_collate (a->sort_key, b->sort_key);
259 return bd - ad;
262 /* --------------------------------------------------------------------------------------------- */
265 sort_vers (file_entry * a, file_entry * b)
267 int ad = MY_ISDIR (a);
268 int bd = MY_ISDIR (b);
270 if (ad == bd || panels_options.mix_all_files)
272 return str_verscmp (a->fname, b->fname) * reverse;
274 else
276 return bd - ad;
280 /* --------------------------------------------------------------------------------------------- */
283 sort_ext (file_entry * a, file_entry * b)
285 int r;
286 int ad = MY_ISDIR (a);
287 int bd = MY_ISDIR (b);
289 if (ad == bd || panels_options.mix_all_files)
291 if (a->second_sort_key == NULL)
292 a->second_sort_key = str_create_key (extension (a->fname), case_sensitive);
293 if (b->second_sort_key == NULL)
294 b->second_sort_key = str_create_key (extension (b->fname), case_sensitive);
296 r = str_key_collate (a->second_sort_key, b->second_sort_key, case_sensitive);
297 if (r)
298 return r * reverse;
299 else
300 return sort_name (a, b);
302 else
303 return bd - ad;
306 /* --------------------------------------------------------------------------------------------- */
309 sort_time (file_entry * a, file_entry * b)
311 int ad = MY_ISDIR (a);
312 int bd = MY_ISDIR (b);
314 if (ad == bd || panels_options.mix_all_files)
316 int result = a->st.st_mtime < b->st.st_mtime ? -1 : a->st.st_mtime > b->st.st_mtime;
317 if (result != 0)
318 return result * reverse;
319 else
320 return sort_name (a, b);
322 else
323 return bd - ad;
326 /* --------------------------------------------------------------------------------------------- */
329 sort_ctime (file_entry * a, file_entry * b)
331 int ad = MY_ISDIR (a);
332 int bd = MY_ISDIR (b);
334 if (ad == bd || panels_options.mix_all_files)
336 int result = a->st.st_ctime < b->st.st_ctime ? -1 : a->st.st_ctime > b->st.st_ctime;
337 if (result != 0)
338 return result * reverse;
339 else
340 return sort_name (a, b);
342 else
343 return bd - ad;
346 /* --------------------------------------------------------------------------------------------- */
349 sort_atime (file_entry * a, file_entry * b)
351 int ad = MY_ISDIR (a);
352 int bd = MY_ISDIR (b);
354 if (ad == bd || panels_options.mix_all_files)
356 int result = a->st.st_atime < b->st.st_atime ? -1 : a->st.st_atime > b->st.st_atime;
357 if (result != 0)
358 return result * reverse;
359 else
360 return sort_name (a, b);
362 else
363 return bd - ad;
366 /* --------------------------------------------------------------------------------------------- */
369 sort_inode (file_entry * a, file_entry * b)
371 int ad = MY_ISDIR (a);
372 int bd = MY_ISDIR (b);
374 if (ad == bd || panels_options.mix_all_files)
375 return (a->st.st_ino - b->st.st_ino) * reverse;
376 else
377 return bd - ad;
380 /* --------------------------------------------------------------------------------------------- */
383 sort_size (file_entry * a, file_entry * b)
385 int ad = MY_ISDIR (a);
386 int bd = MY_ISDIR (b);
387 int result = 0;
389 if (ad != bd && !panels_options.mix_all_files)
390 return bd - ad;
392 result = a->st.st_size < b->st.st_size ? -1 : a->st.st_size > b->st.st_size;
393 if (result != 0)
394 return result * reverse;
395 else
396 return sort_name (a, b);
399 /* --------------------------------------------------------------------------------------------- */
401 void
402 do_sort (dir_list * list, sortfn * sort, int top, int reverse_f, int case_sensitive_f,
403 int exec_first_f)
405 int dot_dot_found = 0;
407 if (top == 0)
408 return;
410 /* If there is an ".." entry the caller must take care to
411 ensure that it occupies the first list element. */
412 if (!strcmp (list->list[0].fname, ".."))
413 dot_dot_found = 1;
415 reverse = reverse_f ? -1 : 1;
416 case_sensitive = case_sensitive_f;
417 exec_first = exec_first_f;
418 qsort (&(list->list)[dot_dot_found], top + 1 - dot_dot_found, sizeof (file_entry), sort);
420 clean_sort_keys (list, dot_dot_found, top + 1 - dot_dot_found);
423 /* --------------------------------------------------------------------------------------------- */
425 void
426 clean_dir (dir_list * list, int count)
428 int i;
430 for (i = 0; i < count; i++)
432 g_free (list->list[i].fname);
433 list->list[i].fname = NULL;
437 /* --------------------------------------------------------------------------------------------- */
438 /** Used to set up a directory list when there is no access to a directory */
440 gboolean
441 set_zero_dir (dir_list * list)
443 /* Need to grow the *list? */
444 if (list->size == 0)
446 list->list = g_try_realloc (list->list, sizeof (file_entry) * (list->size + RESIZE_STEPS));
447 if (list->list == NULL)
448 return FALSE;
450 list->size += RESIZE_STEPS;
453 memset (&(list->list)[0], 0, sizeof (file_entry));
454 list->list[0].fnamelen = 2;
455 list->list[0].fname = g_strdup ("..");
456 list->list[0].f.link_to_dir = 0;
457 list->list[0].f.stale_link = 0;
458 list->list[0].f.dir_size_computed = 0;
459 list->list[0].f.marked = 0;
460 list->list[0].st.st_mode = 040755;
461 return TRUE;
464 /* --------------------------------------------------------------------------------------------- */
466 handle_path is a simplified handle_dirent. The difference is that
467 handle_path doesn't pay attention to panels_options.show_dot_files
468 and panels_options.show_backups.
469 Moreover handle_path can't be used with a filemask.
470 If you change handle_path then check also handle_dirent. */
471 /* Return values: -1 = failure, 0 = don't add, 1 = add to the list */
474 handle_path (dir_list * list, const char *path,
475 struct stat *buf1, int next_free, int *link_to_dir, int *stale_link)
477 if (path[0] == '.' && path[1] == 0)
478 return 0;
479 if (path[0] == '.' && path[1] == '.' && path[2] == 0)
480 return 0;
481 if (mc_lstat (path, buf1) == -1)
482 return 0;
484 if (S_ISDIR (buf1->st_mode))
485 tree_store_mark_checked (path);
487 /* A link to a file or a directory? */
488 *link_to_dir = 0;
489 *stale_link = 0;
490 if (S_ISLNK (buf1->st_mode))
492 struct stat buf2;
493 if (!mc_stat (path, &buf2))
494 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
495 else
496 *stale_link = 1;
499 /* Need to grow the *list? */
500 if (next_free == list->size)
502 list->list = g_try_realloc (list->list, sizeof (file_entry) * (list->size + RESIZE_STEPS));
503 if (list->list == NULL)
504 return -1;
505 list->size += RESIZE_STEPS;
507 return 1;
510 /* --------------------------------------------------------------------------------------------- */
513 do_load_dir (const char *path, dir_list * list, sortfn * sort, int lc_reverse,
514 int lc_case_sensitive, int exec_ff, const char *fltr)
516 DIR *dirp;
517 struct dirent *dp;
518 int status, link_to_dir, stale_link;
519 int next_free = 0;
520 struct stat st;
522 /* ".." (if any) must be the first entry in the list */
523 if (!set_zero_dir (list))
524 return next_free;
526 if (get_dotdot_dir_stat (path, &st))
527 list->list[next_free].st = st;
528 next_free++;
530 dirp = mc_opendir (path);
531 if (!dirp)
533 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
534 return next_free;
537 tree_store_start_check (path);
539 /* Do not add a ".." entry to the root directory */
540 if ((path[0] == PATH_SEP) && (path[1] == '\0'))
541 next_free--;
543 while ((dp = mc_readdir (dirp)))
545 status = handle_dirent (list, fltr, dp, &st, next_free, &link_to_dir, &stale_link);
546 if (status == 0)
547 continue;
548 if (status == -1)
550 tree_store_end_check ();
551 mc_closedir (dirp);
552 return next_free;
554 list->list[next_free].fnamelen = NLENGTH (dp);
555 list->list[next_free].fname = g_strdup (dp->d_name);
556 list->list[next_free].f.marked = 0;
557 list->list[next_free].f.link_to_dir = link_to_dir;
558 list->list[next_free].f.stale_link = stale_link;
559 list->list[next_free].f.dir_size_computed = 0;
560 list->list[next_free].st = st;
561 list->list[next_free].sort_key = NULL;
562 list->list[next_free].second_sort_key = NULL;
563 next_free++;
565 if ((next_free & 31) == 0)
566 rotate_dash ();
569 if (next_free != 0)
570 do_sort (list, sort, next_free - 1, lc_reverse, lc_case_sensitive, exec_ff);
572 mc_closedir (dirp);
573 tree_store_end_check ();
574 return next_free;
578 /* --------------------------------------------------------------------------------------------- */
580 gboolean
581 if_link_is_exe (const char *full_name, const file_entry * file)
583 struct stat b;
585 if (S_ISLNK (file->st.st_mode) && mc_stat (full_name, &b) == 0)
586 return is_exe (b.st_mode);
587 return TRUE;
590 /* --------------------------------------------------------------------------------------------- */
591 /** If fltr is null, then it is a match */
594 do_reload_dir (const char *path, dir_list * list, sortfn * sort, int count,
595 int rev, int lc_case_sensitive, int exec_ff, const char *fltr)
597 DIR *dirp;
598 struct dirent *dp;
599 int next_free = 0;
600 int i, status, link_to_dir, stale_link;
601 struct stat st;
602 int marked_cnt;
603 GHashTable *marked_files;
605 dirp = mc_opendir (path);
606 if (!dirp)
608 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
609 clean_dir (list, count);
610 return set_zero_dir (list) ? 1 : 0;
613 tree_store_start_check (path);
614 marked_files = g_hash_table_new (g_str_hash, g_str_equal);
615 alloc_dir_copy (list->size);
616 for (marked_cnt = i = 0; i < count; i++)
618 dir_copy.list[i].fnamelen = list->list[i].fnamelen;
619 dir_copy.list[i].fname = list->list[i].fname;
620 dir_copy.list[i].f.marked = list->list[i].f.marked;
621 dir_copy.list[i].f.dir_size_computed = list->list[i].f.dir_size_computed;
622 dir_copy.list[i].f.link_to_dir = list->list[i].f.link_to_dir;
623 dir_copy.list[i].f.stale_link = list->list[i].f.stale_link;
624 dir_copy.list[i].sort_key = NULL;
625 dir_copy.list[i].second_sort_key = NULL;
626 if (list->list[i].f.marked)
628 g_hash_table_insert (marked_files, dir_copy.list[i].fname, &dir_copy.list[i]);
629 marked_cnt++;
633 /* Add ".." except to the root directory. The ".." entry
634 (if any) must be the first in the list. */
635 if (!((path[0] == PATH_SEP) && (path[1] == '\0')))
637 if (!set_zero_dir (list))
639 clean_dir (list, count);
640 clean_dir (&dir_copy, count);
641 return next_free;
644 if (get_dotdot_dir_stat (path, &st))
645 list->list[next_free].st = st;
647 next_free++;
650 while ((dp = mc_readdir (dirp)))
652 status = handle_dirent (list, fltr, dp, &st, next_free, &link_to_dir, &stale_link);
653 if (status == 0)
654 continue;
655 if (status == -1)
657 mc_closedir (dirp);
658 /* Norbert (Feb 12, 1997):
659 Just in case someone finds this memory leak:
660 -1 means big trouble (at the moment no memory left),
661 I don't bother with further cleanup because if one gets to
662 this point he will have more problems than a few memory
663 leaks and because one 'clean_dir' would not be enough (and
664 because I don't want to spent the time to make it working,
665 IMHO it's not worthwhile).
666 clean_dir (&dir_copy, count);
668 tree_store_end_check ();
669 g_hash_table_destroy (marked_files);
670 return next_free;
673 list->list[next_free].f.marked = 0;
676 * If we have marked files in the copy, scan through the copy
677 * to find matching file. Decrease number of remaining marks if
678 * we copied one.
680 if (marked_cnt > 0)
682 if ((g_hash_table_lookup (marked_files, dp->d_name)))
684 list->list[next_free].f.marked = 1;
685 marked_cnt--;
689 list->list[next_free].fnamelen = NLENGTH (dp);
690 list->list[next_free].fname = g_strdup (dp->d_name);
691 list->list[next_free].f.link_to_dir = link_to_dir;
692 list->list[next_free].f.stale_link = stale_link;
693 list->list[next_free].f.dir_size_computed = 0;
694 list->list[next_free].st = st;
695 list->list[next_free].sort_key = NULL;
696 list->list[next_free].second_sort_key = NULL;
697 next_free++;
698 if (!(next_free % 16))
699 rotate_dash ();
701 mc_closedir (dirp);
702 tree_store_end_check ();
703 g_hash_table_destroy (marked_files);
704 if (next_free)
706 do_sort (list, sort, next_free - 1, rev, lc_case_sensitive, exec_ff);
708 clean_dir (&dir_copy, count);
709 return next_free;
712 /* --------------------------------------------------------------------------------------------- */