4 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
6 The Free Software Foundation, Inc.
9 Slava Zanko <slavazanko@gmail.com>, 2013
10 Andrew Borodin <aborodin@vmail.ru>, 2013
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file src/filemanager/dir.c
29 * \brief Source: directory routines
39 #include "lib/global.h"
40 #include "lib/tty/tty.h"
41 #include "lib/search.h"
42 #include "lib/vfs/vfs.h"
44 #include "lib/strutil.h"
46 #include "lib/widget.h" /* message() */
48 #include "src/setup.h" /* panels_options */
50 #include "treestore.h"
52 #include "layout.h" /* rotate_dash() */
54 /*** global variables ****************************************************************************/
56 /*** file scope macro definitions ****************************************************************/
58 #define MY_ISDIR(x) (\
59 (is_exe (x->st.st_mode) && !(S_ISDIR (x->st.st_mode) || x->f.link_to_dir) && exec_first) \
61 : ( (S_ISDIR (x->st.st_mode) || x->f.link_to_dir) ? 2 : 0) )
63 /*** file scope type declarations ****************************************************************/
65 /*** file scope variables ************************************************************************/
68 static int reverse
= 1;
70 /* Are the files sorted case sensitively? */
71 static int case_sensitive
= OS_SORT_CASE_SENSITIVE_DEFAULT
;
73 /* Are the exec_bit files top in list */
74 static gboolean exec_first
= TRUE
;
76 static dir_list dir_copy
= { NULL
, 0, 0 };
78 /*** file scope functions ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
82 sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
83 { N_("&Unsorted"), unsorted },
84 { N_("&Name"), sort_name },
85 { N_("&Extension"), sort_ext },
86 { N_("&Modify time"), sort_time },
87 { N_("&Access time"), sort_atime },
88 { N_("C&Hange time"), sort_ctime },
89 { N_("&Size"), sort_size },
90 { N_("&Inode"), sort_inode },
95 key_collate (const char *t1
, const char *t2
)
100 dotdot
= (t1
[0] == '.' ? 1 : 0) | ((t2
[0] == '.' ? 1 : 0) << 1);
106 ret
= str_key_collate (t1
, t2
, case_sensitive
) * reverse
;
109 ret
= -1; /* t1 < t2 */
112 ret
= 1; /* t1 > t2 */
115 ret
= 0; /* it must not happen */
121 /* --------------------------------------------------------------------------------------------- */
123 * clear keys, should be call after sorting is finished.
127 clean_sort_keys (dir_list
* list
, int start
, int count
)
131 for (i
= 0; i
< count
; i
++)
133 file_entry_t
*fentry
;
135 fentry
= &list
->list
[i
+ start
];
136 str_release_key (fentry
->sort_key
, case_sensitive
);
137 fentry
->sort_key
= NULL
;
138 str_release_key (fentry
->second_sort_key
, case_sensitive
);
139 fentry
->second_sort_key
= NULL
;
143 /* --------------------------------------------------------------------------------------------- */
145 * If you change handle_dirent then check also handle_path.
146 * @return FALSE = don't add, TRUE = add to the list
150 handle_dirent (struct dirent
*dp
, const char *fltr
, struct stat
*buf1
, int *link_to_dir
,
155 if (DIR_IS_DOT (dp
->d_name
) || DIR_IS_DOTDOT (dp
->d_name
))
157 if (!panels_options
.show_dot_files
&& (dp
->d_name
[0] == '.'))
159 if (!panels_options
.show_backups
&& dp
->d_name
[strlen (dp
->d_name
) - 1] == '~')
162 vpath
= vfs_path_from_str (dp
->d_name
);
163 if (mc_lstat (vpath
, buf1
) == -1)
166 * lstat() fails - such entries should be identified by
167 * buf1->st_mode being 0.
168 * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
170 memset (buf1
, 0, sizeof (*buf1
));
173 if (S_ISDIR (buf1
->st_mode
))
174 tree_store_mark_checked (dp
->d_name
);
176 /* A link to a file or a directory? */
179 if (S_ISLNK (buf1
->st_mode
))
183 if (mc_stat (vpath
, &buf2
) == 0)
184 *link_to_dir
= S_ISDIR (buf2
.st_mode
) != 0;
189 vfs_path_free (vpath
);
191 return (S_ISDIR (buf1
->st_mode
) || *link_to_dir
!= 0 || fltr
== NULL
192 || mc_search (fltr
, NULL
, dp
->d_name
, MC_SEARCH_T_GLOB
));
195 /* --------------------------------------------------------------------------------------------- */
196 /** get info about ".." */
199 dir_get_dotdot_stat (const vfs_path_t
* vpath
, struct stat
*st
)
201 gboolean ret
= FALSE
;
203 if ((vpath
!= NULL
) && (st
!= NULL
))
207 path
= vfs_path_get_by_index (vpath
, 0)->path
;
208 if (path
!= NULL
&& *path
!= '\0')
210 vfs_path_t
*tmp_vpath
;
212 tmp_vpath
= vfs_path_append_new (vpath
, "..", NULL
);
213 ret
= mc_stat (tmp_vpath
, st
) == 0;
214 vfs_path_free (tmp_vpath
);
221 /* --------------------------------------------------------------------------------------------- */
224 alloc_dir_copy (int size
)
226 if (dir_copy
.size
< size
)
228 if (dir_copy
.list
!= NULL
)
232 for (i
= 0; i
< dir_copy
.len
; i
++)
234 file_entry_t
*fentry
;
236 fentry
= &(dir_copy
.list
)[i
];
237 g_free (fentry
->fname
);
239 g_free (dir_copy
.list
);
242 dir_copy
.list
= g_new0 (file_entry_t
, size
);
243 dir_copy
.size
= size
;
248 /* --------------------------------------------------------------------------------------------- */
249 /*** public functions ****************************************************************************/
250 /* --------------------------------------------------------------------------------------------- */
252 * Increase or decrease directory list size.
254 * @param list directory list
255 * @param delta value by increase (if positive) or decrease (if negative) list size
257 * @return FALSE on failure, TRUE on success
261 dir_list_grow (dir_list
* list
, int delta
)
264 gboolean clear
= FALSE
;
272 size
= list
->size
+ delta
;
275 size
= DIR_LIST_MIN_SIZE
;
279 if (size
!= list
->size
)
283 fe
= g_try_renew (file_entry_t
, list
->list
, size
);
291 list
->len
= clear
? 0 : min (list
->len
, size
);
296 /* --------------------------------------------------------------------------------------------- */
298 * Append file info to the directory list.
300 * @param list directory list
301 * @param fname file name
302 * @param st file stat info
303 * @param link_to_dir is file link to directory
304 * @param stale_link is file stale elink
306 * @return FALSE on failure, TRUE on success
310 dir_list_append (dir_list
* list
, const char *fname
, const struct stat
* st
,
311 gboolean link_to_dir
, gboolean stale_link
)
313 file_entry_t
*fentry
;
315 /* Need to grow the *list? */
316 if (list
->len
== list
->size
&& !dir_list_grow (list
, DIR_LIST_RESIZE_STEP
))
319 fentry
= &list
->list
[list
->len
];
320 fentry
->fnamelen
= strlen (fname
);
321 fentry
->fname
= g_strndup (fname
, fentry
->fnamelen
);
322 fentry
->f
.marked
= 0;
323 fentry
->f
.link_to_dir
= link_to_dir
? 1 : 0;
324 fentry
->f
.stale_link
= stale_link
? 1 : 0;
325 fentry
->f
.dir_size_computed
= 0;
327 fentry
->sort_key
= NULL
;
328 fentry
->second_sort_key
= NULL
;
335 /* --------------------------------------------------------------------------------------------- */
338 unsorted (file_entry_t
* a
, file_entry_t
* b
)
345 /* --------------------------------------------------------------------------------------------- */
348 sort_name (file_entry_t
* a
, file_entry_t
* b
)
350 int ad
= MY_ISDIR (a
);
351 int bd
= MY_ISDIR (b
);
353 if (ad
== bd
|| panels_options
.mix_all_files
)
355 /* create key if does not exist, key will be freed after sorting */
356 if (a
->sort_key
== NULL
)
357 a
->sort_key
= str_create_key_for_filename (a
->fname
, case_sensitive
);
358 if (b
->sort_key
== NULL
)
359 b
->sort_key
= str_create_key_for_filename (b
->fname
, case_sensitive
);
361 return key_collate (a
->sort_key
, b
->sort_key
);
366 /* --------------------------------------------------------------------------------------------- */
369 sort_vers (file_entry_t
* a
, file_entry_t
* b
)
371 int ad
= MY_ISDIR (a
);
372 int bd
= MY_ISDIR (b
);
374 if (ad
== bd
|| panels_options
.mix_all_files
)
376 return str_verscmp (a
->fname
, b
->fname
) * reverse
;
384 /* --------------------------------------------------------------------------------------------- */
387 sort_ext (file_entry_t
* a
, file_entry_t
* b
)
389 int ad
= MY_ISDIR (a
);
390 int bd
= MY_ISDIR (b
);
392 if (ad
== bd
|| panels_options
.mix_all_files
)
396 if (a
->second_sort_key
== NULL
)
397 a
->second_sort_key
= str_create_key (extension (a
->fname
), case_sensitive
);
398 if (b
->second_sort_key
== NULL
)
399 b
->second_sort_key
= str_create_key (extension (b
->fname
), case_sensitive
);
401 r
= str_key_collate (a
->second_sort_key
, b
->second_sort_key
, case_sensitive
);
405 return sort_name (a
, b
);
411 /* --------------------------------------------------------------------------------------------- */
414 sort_time (file_entry_t
* a
, file_entry_t
* b
)
416 int ad
= MY_ISDIR (a
);
417 int bd
= MY_ISDIR (b
);
419 if (ad
== bd
|| panels_options
.mix_all_files
)
421 int result
= a
->st
.st_mtime
< b
->st
.st_mtime
? -1 : a
->st
.st_mtime
> b
->st
.st_mtime
;
423 return result
* reverse
;
425 return sort_name (a
, b
);
431 /* --------------------------------------------------------------------------------------------- */
434 sort_ctime (file_entry_t
* a
, file_entry_t
* b
)
436 int ad
= MY_ISDIR (a
);
437 int bd
= MY_ISDIR (b
);
439 if (ad
== bd
|| panels_options
.mix_all_files
)
441 int result
= a
->st
.st_ctime
< b
->st
.st_ctime
? -1 : a
->st
.st_ctime
> b
->st
.st_ctime
;
443 return result
* reverse
;
445 return sort_name (a
, b
);
451 /* --------------------------------------------------------------------------------------------- */
454 sort_atime (file_entry_t
* a
, file_entry_t
* b
)
456 int ad
= MY_ISDIR (a
);
457 int bd
= MY_ISDIR (b
);
459 if (ad
== bd
|| panels_options
.mix_all_files
)
461 int result
= a
->st
.st_atime
< b
->st
.st_atime
? -1 : a
->st
.st_atime
> b
->st
.st_atime
;
463 return result
* reverse
;
465 return sort_name (a
, b
);
471 /* --------------------------------------------------------------------------------------------- */
474 sort_inode (file_entry_t
* a
, file_entry_t
* b
)
476 int ad
= MY_ISDIR (a
);
477 int bd
= MY_ISDIR (b
);
479 if (ad
== bd
|| panels_options
.mix_all_files
)
480 return (a
->st
.st_ino
- b
->st
.st_ino
) * reverse
;
485 /* --------------------------------------------------------------------------------------------- */
488 sort_size (file_entry_t
* a
, file_entry_t
* b
)
490 int ad
= MY_ISDIR (a
);
491 int bd
= MY_ISDIR (b
);
494 if (ad
!= bd
&& !panels_options
.mix_all_files
)
497 result
= a
->st
.st_size
< b
->st
.st_size
? -1 : a
->st
.st_size
> b
->st
.st_size
;
499 return result
* reverse
;
501 return sort_name (a
, b
);
504 /* --------------------------------------------------------------------------------------------- */
507 dir_list_sort (dir_list
* list
, GCompareFunc sort
, const dir_sort_options_t
* sort_op
)
509 file_entry_t
*fentry
;
510 int dot_dot_found
= 0;
515 /* If there is an ".." entry the caller must take care to
516 ensure that it occupies the first list element. */
517 fentry
= &list
->list
[0];
518 if (DIR_IS_DOTDOT (fentry
->fname
))
521 reverse
= sort_op
->reverse
? -1 : 1;
522 case_sensitive
= sort_op
->case_sensitive
? 1 : 0;
523 exec_first
= sort_op
->exec_first
;
524 qsort (&(list
->list
)[dot_dot_found
], list
->len
- dot_dot_found
, sizeof (file_entry_t
), sort
);
526 clean_sort_keys (list
, dot_dot_found
, list
->len
- dot_dot_found
);
529 /* --------------------------------------------------------------------------------------------- */
532 dir_list_clean (dir_list
* list
)
536 for (i
= 0; i
< list
->len
; i
++)
538 file_entry_t
*fentry
;
540 fentry
= &list
->list
[i
];
541 g_free (fentry
->fname
);
542 fentry
->fname
= NULL
;
546 /* reduce memory usage */
547 dir_list_grow (list
, DIR_LIST_MIN_SIZE
- list
->size
);
550 /* --------------------------------------------------------------------------------------------- */
551 /** Used to set up a directory list when there is no access to a directory */
554 dir_list_init (dir_list
* list
)
556 file_entry_t
*fentry
;
558 /* Need to grow the *list? */
559 if (list
->size
== 0 && !dir_list_grow (list
, DIR_LIST_RESIZE_STEP
))
565 fentry
= &list
->list
[0];
566 memset (fentry
, 0, sizeof (file_entry_t
));
567 fentry
->fnamelen
= 2;
568 fentry
->fname
= g_strndup ("..", fentry
->fnamelen
);
569 fentry
->f
.link_to_dir
= 0;
570 fentry
->f
.stale_link
= 0;
571 fentry
->f
.dir_size_computed
= 0;
572 fentry
->f
.marked
= 0;
573 fentry
->st
.st_mode
= 040755;
578 /* --------------------------------------------------------------------------------------------- */
580 handle_path is a simplified handle_dirent. The difference is that
581 handle_path doesn't pay attention to panels_options.show_dot_files
582 and panels_options.show_backups.
583 Moreover handle_path can't be used with a filemask.
584 If you change handle_path then check also handle_dirent. */
585 /* Return values: FALSE = don't add, TRUE = add to the list */
588 handle_path (const char *path
, struct stat
* buf1
, int *link_to_dir
, int *stale_link
)
592 if (DIR_IS_DOT (path
) || DIR_IS_DOTDOT (path
))
595 vpath
= vfs_path_from_str (path
);
596 if (mc_lstat (vpath
, buf1
) == -1)
598 vfs_path_free (vpath
);
602 if (S_ISDIR (buf1
->st_mode
))
603 tree_store_mark_checked (path
);
605 /* A link to a file or a directory? */
608 if (S_ISLNK (buf1
->st_mode
))
612 if (mc_stat (vpath
, &buf2
) == 0)
613 *link_to_dir
= S_ISDIR (buf2
.st_mode
) != 0;
618 vfs_path_free (vpath
);
623 /* --------------------------------------------------------------------------------------------- */
626 dir_list_load (dir_list
* list
, const vfs_path_t
* vpath
, GCompareFunc sort
,
627 const dir_sort_options_t
* sort_op
, const char *fltr
)
631 int link_to_dir
, stale_link
;
633 file_entry_t
*fentry
;
635 /* ".." (if any) must be the first entry in the list */
636 if (!dir_list_init (list
))
639 fentry
= &list
->list
[0];
640 if (dir_get_dotdot_stat (vpath
, &st
))
643 dirp
= mc_opendir (vpath
);
646 message (D_ERROR
, MSG_ERROR
, _("Cannot read directory contents"));
650 tree_store_start_check (vpath
);
653 const char *vpath_str
;
655 vpath_str
= vfs_path_as_str (vpath
);
656 /* Do not add a ".." entry to the root directory */
657 if ((vpath_str
[0] == PATH_SEP
) && (vpath_str
[1] == '\0'))
661 while ((dp
= mc_readdir (dirp
)) != NULL
)
663 if (!handle_dirent (dp
, fltr
, &st
, &link_to_dir
, &stale_link
))
666 if (!dir_list_append (list
, dp
->d_name
, &st
, link_to_dir
!= 0, stale_link
!= 0))
669 if ((list
->len
& 31) == 0)
673 dir_list_sort (list
, sort
, sort_op
);
677 tree_store_end_check ();
681 /* --------------------------------------------------------------------------------------------- */
684 if_link_is_exe (const vfs_path_t
* full_name_vpath
, const file_entry_t
* file
)
688 if (S_ISLNK (file
->st
.st_mode
) && mc_stat (full_name_vpath
, &b
) == 0)
689 return is_exe (b
.st_mode
);
693 /* --------------------------------------------------------------------------------------------- */
694 /** If fltr is null, then it is a match */
697 dir_list_reload (dir_list
* list
, const vfs_path_t
* vpath
, GCompareFunc sort
,
698 const dir_sort_options_t
* sort_op
, const char *fltr
)
702 int i
, link_to_dir
, stale_link
;
705 GHashTable
*marked_files
;
706 const char *tmp_path
;
708 dirp
= mc_opendir (vpath
);
711 message (D_ERROR
, MSG_ERROR
, _("Cannot read directory contents"));
712 dir_list_clean (list
);
713 dir_list_init (list
);
717 tree_store_start_check (vpath
);
719 marked_files
= g_hash_table_new (g_str_hash
, g_str_equal
);
720 alloc_dir_copy (list
->len
);
721 for (marked_cnt
= i
= 0; i
< list
->len
; i
++)
723 file_entry_t
*fentry
, *dfentry
;
725 fentry
= &list
->list
[i
];
726 dfentry
= &dir_copy
.list
[i
];
728 dfentry
->fnamelen
= fentry
->fnamelen
;
729 dfentry
->fname
= fentry
->fname
;
730 dfentry
->f
.marked
= fentry
->f
.marked
;
731 dfentry
->f
.dir_size_computed
= fentry
->f
.dir_size_computed
;
732 dfentry
->f
.link_to_dir
= fentry
->f
.link_to_dir
;
733 dfentry
->f
.stale_link
= fentry
->f
.stale_link
;
734 dfentry
->sort_key
= NULL
;
735 dfentry
->second_sort_key
= NULL
;
736 if (fentry
->f
.marked
)
738 g_hash_table_insert (marked_files
, dfentry
->fname
, dfentry
);
743 /* Add ".." except to the root directory. The ".." entry
744 (if any) must be the first in the list. */
745 tmp_path
= vfs_path_get_by_index (vpath
, 0)->path
;
747 (vfs_path_elements_count (vpath
) == 1 && (tmp_path
[0] == PATH_SEP
)
748 && (tmp_path
[1] == '\0')))
750 if (!dir_list_init (list
))
752 dir_list_clean (&dir_copy
);
756 if (dir_get_dotdot_stat (vpath
, &st
))
758 file_entry_t
*fentry
;
760 fentry
= &list
->list
[0];
765 while ((dp
= mc_readdir (dirp
)) != NULL
)
767 file_entry_t
*fentry
;
768 if (!handle_dirent (dp
, fltr
, &st
, &link_to_dir
, &stale_link
))
771 if (!dir_list_append (list
, dp
->d_name
, &st
, link_to_dir
!= 0, stale_link
!= 0))
774 /* Norbert (Feb 12, 1997):
775 Just in case someone finds this memory leak:
776 -1 means big trouble (at the moment no memory left),
777 I don't bother with further cleanup because if one gets to
778 this point he will have more problems than a few memory
779 leaks and because one 'dir_list_clean' would not be enough (and
780 because I don't want to spent the time to make it working,
781 IMHO it's not worthwhile).
782 dir_list_clean (&dir_copy);
784 tree_store_end_check ();
785 g_hash_table_destroy (marked_files
);
788 fentry
= &list
->list
[list
->len
- 1];
790 fentry
->f
.marked
= 0;
793 * If we have marked files in the copy, scan through the copy
794 * to find matching file. Decrease number of remaining marks if
797 if (marked_cnt
> 0 && g_hash_table_lookup (marked_files
, dp
->d_name
) != NULL
)
799 fentry
->f
.marked
= 1;
803 if ((list
->len
& 15) == 0)
807 tree_store_end_check ();
808 g_hash_table_destroy (marked_files
);
810 dir_list_sort (list
, sort
, sort_op
);
812 dir_list_clean (&dir_copy
);
816 /* --------------------------------------------------------------------------------------------- */