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
, 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
)
390 int ad
= MY_ISDIR (a
);
391 int bd
= MY_ISDIR (b
);
393 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;
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 g_free (fentry
->fname
);
541 fentry
->fname
= NULL
;
545 /* reduce memory usage */
546 dir_list_grow (list
, DIR_LIST_MIN_SIZE
- list
->size
);
549 /* --------------------------------------------------------------------------------------------- */
550 /** Used to set up a directory list when there is no access to a directory */
553 dir_list_init (dir_list
* list
)
555 file_entry_t
*fentry
;
557 /* Need to grow the *list? */
558 if (list
->size
== 0 && !dir_list_grow (list
, DIR_LIST_RESIZE_STEP
))
564 fentry
= &list
->list
[0];
565 memset (fentry
, 0, sizeof (file_entry_t
));
566 fentry
->fnamelen
= 2;
567 fentry
->fname
= g_strndup ("..", fentry
->fnamelen
);
568 fentry
->f
.link_to_dir
= 0;
569 fentry
->f
.stale_link
= 0;
570 fentry
->f
.dir_size_computed
= 0;
571 fentry
->f
.marked
= 0;
572 fentry
->st
.st_mode
= 040755;
577 /* --------------------------------------------------------------------------------------------- */
579 handle_path is a simplified handle_dirent. The difference is that
580 handle_path doesn't pay attention to panels_options.show_dot_files
581 and panels_options.show_backups.
582 Moreover handle_path can't be used with a filemask.
583 If you change handle_path then check also handle_dirent. */
584 /* Return values: FALSE = don't add, TRUE = add to the list */
587 handle_path (const char *path
, struct stat
* buf1
, int *link_to_dir
, int *stale_link
)
591 if (DIR_IS_DOT (path
) || DIR_IS_DOTDOT (path
))
594 vpath
= vfs_path_from_str (path
);
595 if (mc_lstat (vpath
, buf1
) == -1)
597 vfs_path_free (vpath
);
601 if (S_ISDIR (buf1
->st_mode
))
602 tree_store_mark_checked (path
);
604 /* A link to a file or a directory? */
607 if (S_ISLNK (buf1
->st_mode
))
611 if (mc_stat (vpath
, &buf2
) == 0)
612 *link_to_dir
= S_ISDIR (buf2
.st_mode
) != 0;
617 vfs_path_free (vpath
);
622 /* --------------------------------------------------------------------------------------------- */
625 dir_list_load (dir_list
* list
, const vfs_path_t
* vpath
, GCompareFunc sort
,
626 const dir_sort_options_t
* sort_op
, const char *fltr
)
630 int link_to_dir
, stale_link
;
632 file_entry_t
*fentry
;
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
);
652 const char *vpath_str
;
654 vpath_str
= vfs_path_as_str (vpath
);
655 /* Do not add a ".." entry to the root directory */
656 if ((vpath_str
[0] == PATH_SEP
) && (vpath_str
[1] == '\0'))
660 while ((dp
= mc_readdir (dirp
)) != NULL
)
662 if (!handle_dirent (dp
, fltr
, &st
, &link_to_dir
, &stale_link
))
665 if (!dir_list_append (list
, dp
->d_name
, &st
, link_to_dir
!= 0, stale_link
!= 0))
668 if ((list
->len
& 31) == 0)
672 dir_list_sort (list
, sort
, sort_op
);
676 tree_store_end_check ();
680 /* --------------------------------------------------------------------------------------------- */
683 if_link_is_exe (const vfs_path_t
* full_name_vpath
, const file_entry_t
* file
)
687 if (S_ISLNK (file
->st
.st_mode
) && mc_stat (full_name_vpath
, &b
) == 0)
688 return is_exe (b
.st_mode
);
692 /* --------------------------------------------------------------------------------------------- */
693 /** If fltr is null, then it is a match */
696 dir_list_reload (dir_list
* list
, const vfs_path_t
* vpath
, GCompareFunc sort
,
697 const dir_sort_options_t
* sort_op
, const char *fltr
)
701 int i
, link_to_dir
, stale_link
;
704 GHashTable
*marked_files
;
705 const char *tmp_path
;
707 dirp
= mc_opendir (vpath
);
710 message (D_ERROR
, MSG_ERROR
, _("Cannot read directory contents"));
711 dir_list_clean (list
);
712 dir_list_init (list
);
716 tree_store_start_check (vpath
);
718 marked_files
= g_hash_table_new (g_str_hash
, g_str_equal
);
719 alloc_dir_copy (list
->len
);
720 for (marked_cnt
= i
= 0; i
< list
->len
; i
++)
722 file_entry_t
*fentry
, *dfentry
;
724 fentry
= &list
->list
[i
];
725 dfentry
= &dir_copy
.list
[i
];
727 dfentry
->fnamelen
= fentry
->fnamelen
;
728 dfentry
->fname
= fentry
->fname
;
729 dfentry
->f
.marked
= fentry
->f
.marked
;
730 dfentry
->f
.dir_size_computed
= fentry
->f
.dir_size_computed
;
731 dfentry
->f
.link_to_dir
= fentry
->f
.link_to_dir
;
732 dfentry
->f
.stale_link
= fentry
->f
.stale_link
;
733 dfentry
->sort_key
= NULL
;
734 dfentry
->second_sort_key
= NULL
;
735 if (fentry
->f
.marked
)
737 g_hash_table_insert (marked_files
, dfentry
->fname
, dfentry
);
742 /* Add ".." except to the root directory. The ".." entry
743 (if any) must be the first in the list. */
744 tmp_path
= vfs_path_get_by_index (vpath
, 0)->path
;
746 (vfs_path_elements_count (vpath
) == 1 && (tmp_path
[0] == PATH_SEP
)
747 && (tmp_path
[1] == '\0')))
749 if (!dir_list_init (list
))
751 dir_list_clean (&dir_copy
);
755 if (dir_get_dotdot_stat (vpath
, &st
))
757 file_entry_t
*fentry
;
759 fentry
= &list
->list
[0];
764 while ((dp
= mc_readdir (dirp
)) != NULL
)
766 file_entry_t
*fentry
;
767 if (!handle_dirent (dp
, fltr
, &st
, &link_to_dir
, &stale_link
))
770 if (!dir_list_append (list
, dp
->d_name
, &st
, link_to_dir
!= 0, stale_link
!= 0))
773 /* Norbert (Feb 12, 1997):
774 Just in case someone finds this memory leak:
775 -1 means big trouble (at the moment no memory left),
776 I don't bother with further cleanup because if one gets to
777 this point he will have more problems than a few memory
778 leaks and because one 'dir_list_clean' would not be enough (and
779 because I don't want to spent the time to make it working,
780 IMHO it's not worthwhile).
781 dir_list_clean (&dir_copy);
783 tree_store_end_check ();
784 g_hash_table_destroy (marked_files
);
787 fentry
= &list
->list
[list
->len
- 1];
789 fentry
->f
.marked
= 0;
792 * If we have marked files in the copy, scan through the copy
793 * to find matching file. Decrease number of remaining marks if
796 if (marked_cnt
> 0 && g_hash_table_lookup (marked_files
, dp
->d_name
) != NULL
)
798 fentry
->f
.marked
= 1;
802 if ((list
->len
& 15) == 0)
806 tree_store_end_check ();
807 g_hash_table_destroy (marked_files
);
809 dir_list_sort (list
, sort
, sort_op
);
811 dir_list_clean (&dir_copy
);
815 /* --------------------------------------------------------------------------------------------- */