cppcheck: reduce variable scope.
[midnight-commander.git] / src / filemanager / dir.c
blobca7cc28411ef4148955fa507da80893106c25e3e
1 /*
2 Directory routines
4 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2011, 2013
6 The Free Software Foundation, Inc.
8 Written by:
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
32 #include <config.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
39 #include "lib/global.h"
40 #include "lib/tty/tty.h"
41 #include "lib/search.h"
42 #include "lib/vfs/vfs.h"
43 #include "lib/fs.h"
44 #include "lib/strutil.h"
45 #include "lib/util.h"
46 #include "lib/widget.h" /* message() */
48 #include "src/setup.h" /* panels_options */
50 #include "treestore.h"
51 #include "dir.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) \
60 ? 1 \
61 : ( (S_ISDIR (x->st.st_mode) || x->f.link_to_dir) ? 2 : 0) )
63 /*** file scope type declarations ****************************************************************/
65 /*** file scope variables ************************************************************************/
67 /* Reverse flag */
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 },
94 static inline int
95 key_collate (const char *t1, const char *t2)
97 int dotdot = 0;
98 int ret;
100 dotdot = (t1[0] == '.' ? 1 : 0) | ((t2[0] == '.' ? 1 : 0) << 1);
102 switch (dotdot)
104 case 0:
105 case 3:
106 ret = str_key_collate (t1, t2, case_sensitive) * reverse;
107 break;
108 case 1:
109 ret = -1; /* t1 < t2 */
110 break;
111 case 2:
112 ret = 1; /* t1 > t2 */
113 break;
114 default:
115 ret = 0; /* it must not happen */
118 return ret;
121 /* --------------------------------------------------------------------------------------------- */
123 * clear keys, should be call after sorting is finished.
126 static void
127 clean_sort_keys (dir_list * list, int start, int count)
129 int i;
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
149 static gboolean
150 handle_dirent (struct dirent *dp, const char *fltr, struct stat *buf1, int *link_to_dir,
151 int *stale_link)
153 vfs_path_t *vpath;
155 if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
156 return FALSE;
157 if (!panels_options.show_dot_files && (dp->d_name[0] == '.'))
158 return FALSE;
159 if (!panels_options.show_backups && dp->d_name[strlen (dp->d_name) - 1] == '~')
160 return FALSE;
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? */
177 *link_to_dir = 0;
178 *stale_link = 0;
179 if (S_ISLNK (buf1->st_mode))
181 struct stat buf2;
183 if (mc_stat (vpath, &buf2) == 0)
184 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
185 else
186 *stale_link = 1;
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 ".." */
198 static gboolean
199 dir_get_dotdot_stat (const vfs_path_t * vpath, struct stat *st)
201 gboolean ret = FALSE;
203 if ((vpath != NULL) && (st != NULL))
205 const char *path;
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);
218 return ret;
221 /* --------------------------------------------------------------------------------------------- */
223 static void
224 alloc_dir_copy (int size)
226 if (dir_copy.size < size)
228 if (dir_copy.list != NULL)
230 int i;
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;
244 dir_copy.len = 0;
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
260 gboolean
261 dir_list_grow (dir_list * list, int delta)
263 int size;
264 gboolean clear = FALSE;
266 if (list == NULL)
267 return FALSE;
269 if (delta == 0)
270 return TRUE;
272 size = list->size + delta;
273 if (size <= 0)
275 size = DIR_LIST_MIN_SIZE;
276 clear = TRUE;
279 if (size != list->size)
281 file_entry_t *fe;
283 fe = g_try_renew (file_entry_t, list->list, size);
284 if (fe == NULL)
285 return FALSE;
287 list->list = fe;
288 list->size = size;
291 list->len = clear ? 0 : min (list->len, size);
293 return TRUE;
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
309 gboolean
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))
317 return FALSE;
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;
326 fentry->st = *st;
327 fentry->sort_key = NULL;
328 fentry->second_sort_key = NULL;
330 list->len++;
332 return TRUE;
335 /* --------------------------------------------------------------------------------------------- */
338 unsorted (file_entry_t * a, file_entry_t * b)
340 (void) a;
341 (void) b;
342 return 0;
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);
363 return bd - ad;
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;
378 else
380 return bd - ad;
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)
394 int r;
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);
402 if (r)
403 return r * reverse;
404 else
405 return sort_name (a, b);
407 else
408 return bd - ad;
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;
422 if (result != 0)
423 return result * reverse;
424 else
425 return sort_name (a, b);
427 else
428 return bd - ad;
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;
442 if (result != 0)
443 return result * reverse;
444 else
445 return sort_name (a, b);
447 else
448 return bd - ad;
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;
462 if (result != 0)
463 return result * reverse;
464 else
465 return sort_name (a, b);
467 else
468 return bd - ad;
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;
481 else
482 return bd - ad;
485 /* --------------------------------------------------------------------------------------------- */
488 sort_size (file_entry_t * a, file_entry_t * b)
490 int ad = MY_ISDIR (a);
491 int bd = MY_ISDIR (b);
492 int result = 0;
494 if (ad != bd && !panels_options.mix_all_files)
495 return bd - ad;
497 result = a->st.st_size < b->st.st_size ? -1 : a->st.st_size > b->st.st_size;
498 if (result != 0)
499 return result * reverse;
500 else
501 return sort_name (a, b);
504 /* --------------------------------------------------------------------------------------------- */
506 void
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;
512 if (list->len < 2)
513 return;
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))
519 dot_dot_found = 1;
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 /* --------------------------------------------------------------------------------------------- */
531 void
532 dir_list_clean (dir_list * list)
534 int i;
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;
545 list->len = 0;
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 */
553 gboolean
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))
561 list->len = 0;
562 return FALSE;
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;
574 list->len = 1;
575 return TRUE;
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 */
587 gboolean
588 handle_path (const char *path, struct stat * buf1, int *link_to_dir, int *stale_link)
590 vfs_path_t *vpath;
592 if (DIR_IS_DOT (path) || DIR_IS_DOTDOT (path))
593 return FALSE;
595 vpath = vfs_path_from_str (path);
596 if (mc_lstat (vpath, buf1) == -1)
598 vfs_path_free (vpath);
599 return FALSE;
602 if (S_ISDIR (buf1->st_mode))
603 tree_store_mark_checked (path);
605 /* A link to a file or a directory? */
606 *link_to_dir = 0;
607 *stale_link = 0;
608 if (S_ISLNK (buf1->st_mode))
610 struct stat buf2;
612 if (mc_stat (vpath, &buf2) == 0)
613 *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
614 else
615 *stale_link = 1;
618 vfs_path_free (vpath);
620 return TRUE;
623 /* --------------------------------------------------------------------------------------------- */
625 void
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)
629 DIR *dirp;
630 struct dirent *dp;
631 int link_to_dir, stale_link;
632 struct stat st;
633 file_entry_t *fentry;
635 /* ".." (if any) must be the first entry in the list */
636 if (!dir_list_init (list))
637 return;
639 fentry = &list->list[0];
640 if (dir_get_dotdot_stat (vpath, &st))
641 fentry->st = st;
643 dirp = mc_opendir (vpath);
644 if (dirp == NULL)
646 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
647 return;
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'))
658 list->len--;
661 while ((dp = mc_readdir (dirp)) != NULL)
663 if (!handle_dirent (dp, fltr, &st, &link_to_dir, &stale_link))
664 continue;
666 if (!dir_list_append (list, dp->d_name, &st, link_to_dir != 0, stale_link != 0))
667 goto ret;
669 if ((list->len & 31) == 0)
670 rotate_dash (TRUE);
673 dir_list_sort (list, sort, sort_op);
675 ret:
676 mc_closedir (dirp);
677 tree_store_end_check ();
678 rotate_dash (FALSE);
681 /* --------------------------------------------------------------------------------------------- */
683 gboolean
684 if_link_is_exe (const vfs_path_t * full_name_vpath, const file_entry_t * file)
686 struct stat b;
688 if (S_ISLNK (file->st.st_mode) && mc_stat (full_name_vpath, &b) == 0)
689 return is_exe (b.st_mode);
690 return TRUE;
693 /* --------------------------------------------------------------------------------------------- */
694 /** If fltr is null, then it is a match */
696 void
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)
700 DIR *dirp;
701 struct dirent *dp;
702 int i, link_to_dir, stale_link;
703 struct stat st;
704 int marked_cnt;
705 GHashTable *marked_files;
706 const char *tmp_path;
708 dirp = mc_opendir (vpath);
709 if (dirp == NULL)
711 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
712 dir_list_clean (list);
713 dir_list_init (list);
714 return;
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);
739 marked_cnt++;
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;
746 if (!
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);
753 return;
756 if (dir_get_dotdot_stat (vpath, &st))
758 file_entry_t *fentry;
760 fentry = &list->list[0];
761 fentry->st = st;
765 while ((dp = mc_readdir (dirp)) != NULL)
767 file_entry_t *fentry;
768 if (!handle_dirent (dp, fltr, &st, &link_to_dir, &stale_link))
769 continue;
771 if (!dir_list_append (list, dp->d_name, &st, link_to_dir != 0, stale_link != 0))
773 mc_closedir (dirp);
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);
786 return;
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
795 * we copied one.
797 if (marked_cnt > 0 && g_hash_table_lookup (marked_files, dp->d_name) != NULL)
799 fentry->f.marked = 1;
800 marked_cnt--;
803 if ((list->len & 15) == 0)
804 rotate_dash (TRUE);
806 mc_closedir (dirp);
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);
813 rotate_dash (FALSE);
816 /* --------------------------------------------------------------------------------------------- */