4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
8 Slava Zanko <slavazanko@gmail.com>, 2013
9 Andrew Borodin <aborodin@vmail.ru>, 2013
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /** \file src/filemanager/dir.c
28 * \brief Source: directory routines
38 #include "lib/global.h"
39 #include "lib/tty/tty.h"
40 #include "lib/search.h"
41 #include "lib/vfs/vfs.h"
43 #include "lib/strutil.h"
45 #include "lib/widget.h" /* message() */
47 #include "src/setup.h" /* panels_options */
49 #include "treestore.h"
51 #include "layout.h" /* rotate_dash() */
53 /*** global variables ****************************************************************************/
55 /*** file scope macro definitions ****************************************************************/
57 #define MY_ISDIR(x) (\
58 (is_exe (x->st.st_mode) && !(S_ISDIR (x->st.st_mode) || link_isdir (x)) && exec_first) \
60 : ( (S_ISDIR (x->st.st_mode) || link_isdir (x)) ? 2 : 0) )
62 /*** file scope type declarations ****************************************************************/
64 /*** file scope variables ************************************************************************/
67 static int reverse
= 1;
69 /* Are the files sorted case sensitively? */
70 static int case_sensitive
= OS_SORT_CASE_SENSITIVE_DEFAULT
;
72 /* Are the exec_bit files top in list */
73 static gboolean exec_first
= TRUE
;
75 static dir_list dir_copy
= { NULL
, 0, 0 };
77 /*** file scope functions ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
81 sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
82 { N_("&Unsorted"), unsorted },
83 { N_("&Name"), sort_name },
84 { N_("&Extension"), sort_ext },
85 { N_("&Modify time"), sort_time },
86 { N_("&Access time"), sort_atime },
87 { N_("C&Hange time"), sort_ctime },
88 { N_("&Size"), sort_size },
89 { N_("&Inode"), sort_inode },
94 key_collate (const char *t1
, const char *t2
)
99 dotdot
= (t1
[0] == '.' ? 1 : 0) | ((t2
[0] == '.' ? 1 : 0) << 1);
105 ret
= str_key_collate (t1
, t2
, case_sensitive
) * reverse
;
108 ret
= -1; /* t1 < t2 */
111 ret
= 1; /* t1 > t2 */
114 ret
= 0; /* it must not happen */
120 /* --------------------------------------------------------------------------------------------- */
122 * clear keys, should be call after sorting is finished.
126 clean_sort_keys (dir_list
* list
, int start
, int count
)
130 for (i
= 0; i
< count
; i
++)
132 file_entry_t
*fentry
;
134 fentry
= &list
->list
[i
+ start
];
135 str_release_key (fentry
->sort_key
, case_sensitive
);
136 fentry
->sort_key
= NULL
;
137 str_release_key (fentry
->second_sort_key
, case_sensitive
);
138 fentry
->second_sort_key
= NULL
;
142 /* --------------------------------------------------------------------------------------------- */
144 * If you change handle_dirent then check also handle_path.
145 * @return FALSE = don't add, TRUE = add to the list
149 handle_dirent (struct dirent
*dp
, const char *fltr
, struct stat
*buf1
, int *link_to_dir
,
154 if (DIR_IS_DOT (dp
->d_name
) || DIR_IS_DOTDOT (dp
->d_name
))
156 if (!panels_options
.show_dot_files
&& (dp
->d_name
[0] == '.'))
158 if (!panels_options
.show_backups
&& dp
->d_name
[strlen (dp
->d_name
) - 1] == '~')
161 vpath
= vfs_path_from_str (dp
->d_name
);
162 if (mc_lstat (vpath
, buf1
) == -1)
165 * lstat() fails - such entries should be identified by
166 * buf1->st_mode being 0.
167 * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
169 memset (buf1
, 0, sizeof (*buf1
));
172 if (S_ISDIR (buf1
->st_mode
))
173 tree_store_mark_checked (dp
->d_name
);
175 /* A link to a file or a directory? */
178 if (S_ISLNK (buf1
->st_mode
))
182 if (mc_stat (vpath
, &buf2
) == 0)
183 *link_to_dir
= S_ISDIR (buf2
.st_mode
) != 0;
188 vfs_path_free (vpath
);
190 return (S_ISDIR (buf1
->st_mode
) || *link_to_dir
!= 0 || fltr
== NULL
191 || mc_search (fltr
, NULL
, dp
->d_name
, MC_SEARCH_T_GLOB
));
194 /* --------------------------------------------------------------------------------------------- */
195 /** get info about ".." */
198 dir_get_dotdot_stat (const vfs_path_t
* vpath
, struct stat
*st
)
200 gboolean ret
= FALSE
;
202 if ((vpath
!= NULL
) && (st
!= NULL
))
206 path
= vfs_path_get_by_index (vpath
, 0)->path
;
207 if (path
!= NULL
&& *path
!= '\0')
209 vfs_path_t
*tmp_vpath
;
211 tmp_vpath
= vfs_path_append_new (vpath
, "..", (char *) NULL
);
212 ret
= mc_stat (tmp_vpath
, st
) == 0;
213 vfs_path_free (tmp_vpath
);
220 /* --------------------------------------------------------------------------------------------- */
223 alloc_dir_copy (int size
)
225 if (dir_copy
.size
< size
)
227 if (dir_copy
.list
!= NULL
)
231 for (i
= 0; i
< dir_copy
.len
; i
++)
233 file_entry_t
*fentry
;
235 fentry
= &(dir_copy
.list
)[i
];
236 g_free (fentry
->fname
);
238 g_free (dir_copy
.list
);
241 dir_copy
.list
= g_new0 (file_entry_t
, size
);
242 dir_copy
.size
= size
;
247 /* --------------------------------------------------------------------------------------------- */
248 /*** public functions ****************************************************************************/
249 /* --------------------------------------------------------------------------------------------- */
251 * Increase or decrease directory list size.
253 * @param list directory list
254 * @param delta value by increase (if positive) or decrease (if negative) list size
256 * @return FALSE on failure, TRUE on success
260 dir_list_grow (dir_list
* list
, int delta
)
263 gboolean clear_flag
= FALSE
;
271 size
= list
->size
+ delta
;
274 size
= DIR_LIST_MIN_SIZE
;
278 if (size
!= list
->size
)
282 fe
= g_try_renew (file_entry_t
, list
->list
, size
);
290 list
->len
= clear_flag
? 0 : MIN (list
->len
, size
);
295 /* --------------------------------------------------------------------------------------------- */
297 * Append file info to the directory list.
299 * @param list directory list
300 * @param fname file name
301 * @param st file stat info
302 * @param link_to_dir is file link to directory
303 * @param stale_link is file stale elink
305 * @return FALSE on failure, TRUE on success
309 dir_list_append (dir_list
* list
, const char *fname
, const struct stat
* st
,
310 gboolean link_to_dir
, gboolean stale_link
)
312 file_entry_t
*fentry
;
314 /* Need to grow the *list? */
315 if (list
->len
== list
->size
&& !dir_list_grow (list
, DIR_LIST_RESIZE_STEP
))
318 fentry
= &list
->list
[list
->len
];
319 fentry
->fnamelen
= strlen (fname
);
320 fentry
->fname
= g_strndup (fname
, fentry
->fnamelen
);
321 fentry
->f
.marked
= 0;
322 fentry
->f
.link_to_dir
= link_to_dir
? 1 : 0;
323 fentry
->f
.stale_link
= stale_link
? 1 : 0;
324 fentry
->f
.dir_size_computed
= 0;
326 fentry
->sort_key
= NULL
;
327 fentry
->second_sort_key
= NULL
;
334 /* --------------------------------------------------------------------------------------------- */
337 unsorted (file_entry_t
* a
, file_entry_t
* b
)
344 /* --------------------------------------------------------------------------------------------- */
347 sort_name (file_entry_t
* a
, file_entry_t
* b
)
349 int ad
= MY_ISDIR (a
);
350 int bd
= MY_ISDIR (b
);
352 if (ad
== bd
|| panels_options
.mix_all_files
)
354 /* create key if does not exist, key will be freed after sorting */
355 if (a
->sort_key
== NULL
)
356 a
->sort_key
= str_create_key_for_filename (a
->fname
, case_sensitive
);
357 if (b
->sort_key
== NULL
)
358 b
->sort_key
= str_create_key_for_filename (b
->fname
, case_sensitive
);
360 return key_collate (a
->sort_key
, b
->sort_key
);
365 /* --------------------------------------------------------------------------------------------- */
368 sort_vers (file_entry_t
* a
, file_entry_t
* b
)
370 int ad
= MY_ISDIR (a
);
371 int bd
= MY_ISDIR (b
);
373 if (ad
== bd
|| panels_options
.mix_all_files
)
375 return str_verscmp (a
->fname
, b
->fname
) * reverse
;
383 /* --------------------------------------------------------------------------------------------- */
386 sort_ext (file_entry_t
* a
, file_entry_t
* b
)
388 int ad
= MY_ISDIR (a
);
389 int bd
= MY_ISDIR (b
);
391 if (ad
== bd
|| panels_options
.mix_all_files
)
395 if (a
->second_sort_key
== NULL
)
396 a
->second_sort_key
= str_create_key (extension (a
->fname
), case_sensitive
);
397 if (b
->second_sort_key
== NULL
)
398 b
->second_sort_key
= str_create_key (extension (b
->fname
), case_sensitive
);
400 r
= str_key_collate (a
->second_sort_key
, b
->second_sort_key
, case_sensitive
);
404 return sort_name (a
, b
);
410 /* --------------------------------------------------------------------------------------------- */
413 sort_time (file_entry_t
* a
, file_entry_t
* b
)
415 int ad
= MY_ISDIR (a
);
416 int bd
= MY_ISDIR (b
);
418 if (ad
== bd
|| panels_options
.mix_all_files
)
420 int result
= a
->st
.st_mtime
< b
->st
.st_mtime
? -1 : a
->st
.st_mtime
> b
->st
.st_mtime
;
422 return result
* reverse
;
424 return sort_name (a
, b
);
430 /* --------------------------------------------------------------------------------------------- */
433 sort_ctime (file_entry_t
* a
, file_entry_t
* b
)
435 int ad
= MY_ISDIR (a
);
436 int bd
= MY_ISDIR (b
);
438 if (ad
== bd
|| panels_options
.mix_all_files
)
440 int result
= a
->st
.st_ctime
< b
->st
.st_ctime
? -1 : a
->st
.st_ctime
> b
->st
.st_ctime
;
442 return result
* reverse
;
444 return sort_name (a
, b
);
450 /* --------------------------------------------------------------------------------------------- */
453 sort_atime (file_entry_t
* a
, file_entry_t
* b
)
455 int ad
= MY_ISDIR (a
);
456 int bd
= MY_ISDIR (b
);
458 if (ad
== bd
|| panels_options
.mix_all_files
)
460 int result
= a
->st
.st_atime
< b
->st
.st_atime
? -1 : a
->st
.st_atime
> b
->st
.st_atime
;
462 return result
* reverse
;
464 return sort_name (a
, b
);
470 /* --------------------------------------------------------------------------------------------- */
473 sort_inode (file_entry_t
* a
, file_entry_t
* b
)
475 int ad
= MY_ISDIR (a
);
476 int bd
= MY_ISDIR (b
);
478 if (ad
== bd
|| panels_options
.mix_all_files
)
479 return (a
->st
.st_ino
- b
->st
.st_ino
) * reverse
;
484 /* --------------------------------------------------------------------------------------------- */
487 sort_size (file_entry_t
* a
, file_entry_t
* b
)
489 int ad
= MY_ISDIR (a
);
490 int bd
= MY_ISDIR (b
);
493 if (ad
!= bd
&& !panels_options
.mix_all_files
)
496 result
= a
->st
.st_size
< b
->st
.st_size
? -1 : a
->st
.st_size
> b
->st
.st_size
;
498 return result
* reverse
;
500 return sort_name (a
, b
);
503 /* --------------------------------------------------------------------------------------------- */
506 dir_list_sort (dir_list
* list
, GCompareFunc sort
, const dir_sort_options_t
* sort_op
)
508 file_entry_t
*fentry
;
509 int dot_dot_found
= 0;
511 if (list
->len
< 2 || sort
== (GCompareFunc
) unsorted
)
514 /* If there is an ".." entry the caller must take care to
515 ensure that it occupies the first list element. */
516 fentry
= &list
->list
[0];
517 if (DIR_IS_DOTDOT (fentry
->fname
))
520 reverse
= sort_op
->reverse
? -1 : 1;
521 case_sensitive
= sort_op
->case_sensitive
? 1 : 0;
522 exec_first
= sort_op
->exec_first
;
523 qsort (&(list
->list
)[dot_dot_found
], list
->len
- dot_dot_found
, sizeof (file_entry_t
), sort
);
525 clean_sort_keys (list
, dot_dot_found
, list
->len
- dot_dot_found
);
528 /* --------------------------------------------------------------------------------------------- */
531 dir_list_clean (dir_list
* list
)
535 for (i
= 0; i
< list
->len
; i
++)
537 file_entry_t
*fentry
;
539 fentry
= &list
->list
[i
];
540 MC_PTR_FREE (fentry
->fname
);
544 /* reduce memory usage */
545 dir_list_grow (list
, DIR_LIST_MIN_SIZE
- list
->size
);
548 /* --------------------------------------------------------------------------------------------- */
549 /** Used to set up a directory list when there is no access to a directory */
552 dir_list_init (dir_list
* list
)
554 file_entry_t
*fentry
;
556 /* Need to grow the *list? */
557 if (list
->size
== 0 && !dir_list_grow (list
, DIR_LIST_RESIZE_STEP
))
563 fentry
= &list
->list
[0];
564 memset (fentry
, 0, sizeof (*fentry
));
565 fentry
->fnamelen
= 2;
566 fentry
->fname
= g_strndup ("..", fentry
->fnamelen
);
567 fentry
->f
.link_to_dir
= 0;
568 fentry
->f
.stale_link
= 0;
569 fentry
->f
.dir_size_computed
= 0;
570 fentry
->f
.marked
= 0;
571 fentry
->st
.st_mode
= 040755;
576 /* --------------------------------------------------------------------------------------------- */
578 handle_path is a simplified handle_dirent. The difference is that
579 handle_path doesn't pay attention to panels_options.show_dot_files
580 and panels_options.show_backups.
581 Moreover handle_path can't be used with a filemask.
582 If you change handle_path then check also handle_dirent. */
583 /* Return values: FALSE = don't add, TRUE = add to the list */
586 handle_path (const char *path
, struct stat
* buf1
, int *link_to_dir
, int *stale_link
)
590 if (DIR_IS_DOT (path
) || DIR_IS_DOTDOT (path
))
593 vpath
= vfs_path_from_str (path
);
594 if (mc_lstat (vpath
, buf1
) == -1)
596 vfs_path_free (vpath
);
600 if (S_ISDIR (buf1
->st_mode
))
601 tree_store_mark_checked (path
);
603 /* A link to a file or a directory? */
606 if (S_ISLNK (buf1
->st_mode
))
610 if (mc_stat (vpath
, &buf2
) == 0)
611 *link_to_dir
= S_ISDIR (buf2
.st_mode
) != 0;
616 vfs_path_free (vpath
);
621 /* --------------------------------------------------------------------------------------------- */
624 dir_list_load (dir_list
* list
, const vfs_path_t
* vpath
, GCompareFunc sort
,
625 const dir_sort_options_t
* sort_op
, const char *fltr
)
629 int link_to_dir
, stale_link
;
631 file_entry_t
*fentry
;
632 const char *vpath_str
;
634 /* ".." (if any) must be the first entry in the list */
635 if (!dir_list_init (list
))
638 fentry
= &list
->list
[0];
639 if (dir_get_dotdot_stat (vpath
, &st
))
642 dirp
= mc_opendir (vpath
);
645 message (D_ERROR
, MSG_ERROR
, _("Cannot read directory contents"));
649 tree_store_start_check (vpath
);
651 vpath_str
= vfs_path_as_str (vpath
);
652 /* Do not add a ".." entry to the root directory */
653 if (IS_PATH_SEP (vpath_str
[0]) && vpath_str
[1] == '\0')
654 dir_list_clean (list
);
656 while ((dp
= mc_readdir (dirp
)) != NULL
)
658 if (!handle_dirent (dp
, fltr
, &st
, &link_to_dir
, &stale_link
))
661 if (!dir_list_append (list
, dp
->d_name
, &st
, link_to_dir
!= 0, stale_link
!= 0))
664 if ((list
->len
& 31) == 0)
668 dir_list_sort (list
, sort
, sort_op
);
672 tree_store_end_check ();
676 /* --------------------------------------------------------------------------------------------- */
679 if_link_is_exe (const vfs_path_t
* full_name_vpath
, const file_entry_t
* file
)
683 if (S_ISLNK (file
->st
.st_mode
) && mc_stat (full_name_vpath
, &b
) == 0)
684 return is_exe (b
.st_mode
);
688 /* --------------------------------------------------------------------------------------------- */
689 /** If fltr is null, then it is a match */
692 dir_list_reload (dir_list
* list
, const vfs_path_t
* vpath
, GCompareFunc sort
,
693 const dir_sort_options_t
* sort_op
, const char *fltr
)
697 int i
, link_to_dir
, stale_link
;
700 GHashTable
*marked_files
;
701 const char *tmp_path
;
703 dirp
= mc_opendir (vpath
);
706 message (D_ERROR
, MSG_ERROR
, _("Cannot read directory contents"));
707 dir_list_clean (list
);
708 dir_list_init (list
);
712 tree_store_start_check (vpath
);
714 marked_files
= g_hash_table_new (g_str_hash
, g_str_equal
);
715 alloc_dir_copy (list
->len
);
716 for (marked_cnt
= i
= 0; i
< list
->len
; i
++)
718 file_entry_t
*fentry
, *dfentry
;
720 fentry
= &list
->list
[i
];
721 dfentry
= &dir_copy
.list
[i
];
723 dfentry
->fnamelen
= fentry
->fnamelen
;
724 dfentry
->fname
= g_strndup (fentry
->fname
, fentry
->fnamelen
);
725 dfentry
->f
.marked
= fentry
->f
.marked
;
726 dfentry
->f
.dir_size_computed
= fentry
->f
.dir_size_computed
;
727 dfentry
->f
.link_to_dir
= fentry
->f
.link_to_dir
;
728 dfentry
->f
.stale_link
= fentry
->f
.stale_link
;
729 dfentry
->sort_key
= NULL
;
730 dfentry
->second_sort_key
= NULL
;
731 if (fentry
->f
.marked
)
733 g_hash_table_insert (marked_files
, dfentry
->fname
, dfentry
);
738 /* save len for later dir_list_clean() */
739 dir_copy
.len
= list
->len
;
741 /* Add ".." except to the root directory. The ".." entry
742 (if any) must be the first in the list. */
743 tmp_path
= vfs_path_get_by_index (vpath
, 0)->path
;
744 if (vfs_path_elements_count (vpath
) == 1 && IS_PATH_SEP (tmp_path
[0]) && tmp_path
[1] == '\0')
747 dir_list_clean (list
);
751 dir_list_clean (list
);
752 if (!dir_list_init (list
))
754 dir_list_clean (&dir_copy
);
758 if (dir_get_dotdot_stat (vpath
, &st
))
760 file_entry_t
*fentry
;
762 fentry
= &list
->list
[0];
767 while ((dp
= mc_readdir (dirp
)) != NULL
)
769 file_entry_t
*fentry
;
771 if (!handle_dirent (dp
, fltr
, &st
, &link_to_dir
, &stale_link
))
774 if (!dir_list_append (list
, dp
->d_name
, &st
, link_to_dir
!= 0, stale_link
!= 0))
777 /* Norbert (Feb 12, 1997):
778 Just in case someone finds this memory leak:
779 -1 means big trouble (at the moment no memory left),
780 I don't bother with further cleanup because if one gets to
781 this point he will have more problems than a few memory
782 leaks and because one 'dir_list_clean' would not be enough (and
783 because I don't want to spent the time to make it working,
784 IMHO it's not worthwhile).
785 dir_list_clean (&dir_copy);
787 tree_store_end_check ();
788 g_hash_table_destroy (marked_files
);
791 fentry
= &list
->list
[list
->len
- 1];
793 fentry
->f
.marked
= 0;
796 * If we have marked files in the copy, scan through the copy
797 * to find matching file. Decrease number of remaining marks if
800 if (marked_cnt
> 0 && g_hash_table_lookup (marked_files
, dp
->d_name
) != NULL
)
802 fentry
->f
.marked
= 1;
806 if ((list
->len
& 15) == 0)
810 tree_store_end_check ();
811 g_hash_table_destroy (marked_files
);
813 dir_list_sort (list
, sort
, sort_op
);
815 dir_list_clean (&dir_copy
);
819 /* --------------------------------------------------------------------------------------------- */