root/src/filemanager/dir.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. key_collate
  2. compare_by_names
  3. clean_sort_keys
  4. handle_dirent
  5. dir_get_dotdot_stat
  6. alloc_dir_copy
  7. dir_list_grow
  8. dir_list_append
  9. unsorted
  10. sort_name
  11. sort_vers
  12. sort_ext
  13. sort_time
  14. sort_ctime
  15. sort_atime
  16. sort_inode
  17. sort_size
  18. dir_list_sort
  19. dir_list_clean
  20. dir_list_free_list
  21. dir_list_init
  22. handle_path
  23. dir_list_load
  24. if_link_is_exe
  25. dir_list_reload
  26. file_filter_clear

   1 /*
   2    Directory routines
   3 
   4    Copyright (C) 1994-2022
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Slava Zanko <slavazanko@gmail.com>, 2013
   9    Andrew Borodin <aborodin@vmail.ru>, 2013-2022
  10 
  11    This file is part of the Midnight Commander.
  12 
  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.
  17 
  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.
  22 
  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/>.
  25  */
  26 
  27 /** \file src/filemanager/dir.c
  28  *  \brief Source: directory routines
  29  */
  30 
  31 #include <config.h>
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <sys/stat.h>
  37 
  38 #include "lib/global.h"
  39 #include "lib/tty/tty.h"
  40 #include "lib/search.h"
  41 #include "lib/vfs/vfs.h"
  42 #include "lib/fs.h"
  43 #include "lib/strutil.h"
  44 #include "lib/util.h"
  45 
  46 #include "src/setup.h"          /* panels_options */
  47 
  48 #include "treestore.h"
  49 #include "file.h"               /* file_is_symlink_to_dir() */
  50 #include "dir.h"
  51 
  52 /*** global variables ****************************************************************************/
  53 
  54 /*** file scope macro definitions ****************************************************************/
  55 
  56 #define MY_ISDIR(x) (\
  57     (is_exe (x->st.st_mode) && !(S_ISDIR (x->st.st_mode) || link_isdir (x)) && exec_first) \
  58         ? 1 \
  59         : ( (S_ISDIR (x->st.st_mode) || link_isdir (x)) ? 2 : 0) )
  60 
  61 /*** file scope type declarations ****************************************************************/
  62 
  63 /*** file scope variables ************************************************************************/
  64 
  65 /* Reverse flag */
  66 static int reverse = 1;
  67 
  68 /* Are the files sorted case sensitively? */
  69 static gboolean case_sensitive = OS_SORT_CASE_SENSITIVE_DEFAULT;
  70 
  71 /* Are the exec_bit files top in list */
  72 static gboolean exec_first = TRUE;
  73 
  74 static dir_list dir_copy = { NULL, 0, 0, NULL };
  75 
  76 /*** file scope functions ************************************************************************/
  77 /* --------------------------------------------------------------------------------------------- */
  78 
  79 /*
  80    sort_orders_t sort_orders [SORT_TYPES_TOTAL] = {
  81    { N_("&Unsorted"),    unsorted },
  82    { N_("&Name"),        sort_name },
  83    { N_("&Extension"),   sort_ext },
  84    { N_("&Modify time"), sort_time },
  85    { N_("&Access time"), sort_atime },
  86    { N_("C&Hange time"), sort_ctime },
  87    { N_("&Size"),        sort_size },
  88    { N_("&Inode"),       sort_inode },
  89    };
  90  */
  91 
  92 static inline int
  93 key_collate (const char *t1, const char *t2)
     /* [previous][next][first][last][top][bottom][index][help]  */
  94 {
  95     int dotdot = 0;
  96     int ret;
  97 
  98     dotdot = (t1[0] == '.' ? 1 : 0) | ((t2[0] == '.' ? 1 : 0) << 1);
  99 
 100     switch (dotdot)
 101     {
 102     case 0:
 103     case 3:
 104         ret = str_key_collate (t1, t2, case_sensitive) * reverse;
 105         break;
 106     case 1:
 107         ret = -1;               /* t1 < t2 */
 108         break;
 109     case 2:
 110         ret = 1;                /* t1 > t2 */
 111         break;
 112     default:
 113         ret = 0;                /* it must not happen */
 114     }
 115 
 116     return ret;
 117 }
 118 
 119 /* --------------------------------------------------------------------------------------------- */
 120 
 121 static inline int
 122 compare_by_names (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 123 {
 124     /* create key if does not exist, key will be freed after sorting */
 125     if (a->sort_key == NULL)
 126         a->sort_key = str_create_key_for_filename (a->fname->str, case_sensitive);
 127     if (b->sort_key == NULL)
 128         b->sort_key = str_create_key_for_filename (b->fname->str, case_sensitive);
 129 
 130     return key_collate (a->sort_key, b->sort_key);
 131 }
 132 
 133 /* --------------------------------------------------------------------------------------------- */
 134 /**
 135  * clear keys, should be call after sorting is finished.
 136  */
 137 
 138 static void
 139 clean_sort_keys (dir_list * list, int start, int count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 140 {
 141     int i;
 142 
 143     for (i = 0; i < count; i++)
 144     {
 145         file_entry_t *fentry;
 146 
 147         fentry = &list->list[i + start];
 148         str_release_key (fentry->sort_key, case_sensitive);
 149         fentry->sort_key = NULL;
 150         str_release_key (fentry->second_sort_key, case_sensitive);
 151         fentry->second_sort_key = NULL;
 152     }
 153 }
 154 
 155 /* --------------------------------------------------------------------------------------------- */
 156 /**
 157  * If you change handle_dirent then check also handle_path.
 158  * @return FALSE = don't add, TRUE = add to the list
 159  */
 160 
 161 static gboolean
 162 handle_dirent (struct vfs_dirent *dp, const file_filter_t * filter, struct stat *buf1,
     /* [previous][next][first][last][top][bottom][index][help]  */
 163                gboolean * link_to_dir, gboolean * stale_link)
 164 {
 165     vfs_path_t *vpath;
 166     gboolean ok = TRUE;
 167 
 168     if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
 169         return FALSE;
 170     if (!panels_options.show_dot_files && (dp->d_name[0] == '.'))
 171         return FALSE;
 172     if (!panels_options.show_backups && dp->d_name[strlen (dp->d_name) - 1] == '~')
 173         return FALSE;
 174 
 175     vpath = vfs_path_from_str (dp->d_name);
 176     if (mc_lstat (vpath, buf1) == -1)
 177     {
 178         /*
 179          * lstat() fails - such entries should be identified by
 180          * buf1->st_mode being 0.
 181          * It happens on QNX Neutrino for /fs/cd0 if no CD is inserted.
 182          */
 183         memset (buf1, 0, sizeof (*buf1));
 184     }
 185 
 186     if (S_ISDIR (buf1->st_mode))
 187         tree_store_mark_checked (dp->d_name);
 188 
 189     /* A link to a file or a directory? */
 190     *link_to_dir = file_is_symlink_to_dir (vpath, buf1, stale_link);
 191 
 192     vfs_path_free (vpath, TRUE);
 193 
 194     if (filter != NULL && filter->handler != NULL)
 195     {
 196         gboolean files_only = (filter->flags & SELECT_FILES_ONLY) != 0;
 197 
 198         ok = ((S_ISDIR (buf1->st_mode) || *link_to_dir) && files_only)
 199             || mc_search_run (filter->handler, dp->d_name, 0, strlen (dp->d_name), NULL);
 200     }
 201 
 202     return ok;
 203 }
 204 
 205 /* --------------------------------------------------------------------------------------------- */
 206 /** get info about ".." */
 207 
 208 static gboolean
 209 dir_get_dotdot_stat (const vfs_path_t * vpath, struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 210 {
 211     gboolean ret = FALSE;
 212 
 213     if ((vpath != NULL) && (st != NULL))
 214     {
 215         const char *path;
 216 
 217         path = vfs_path_get_by_index (vpath, 0)->path;
 218         if (path != NULL && *path != '\0')
 219         {
 220             vfs_path_t *tmp_vpath;
 221 
 222             tmp_vpath = vfs_path_append_new (vpath, "..", (char *) NULL);
 223             ret = mc_stat (tmp_vpath, st) == 0;
 224             vfs_path_free (tmp_vpath, TRUE);
 225         }
 226     }
 227 
 228     return ret;
 229 }
 230 
 231 /* --------------------------------------------------------------------------------------------- */
 232 
 233 static void
 234 alloc_dir_copy (int size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 235 {
 236     if (dir_copy.size < size)
 237     {
 238         if (dir_copy.list != NULL)
 239             dir_list_free_list (&dir_copy);
 240 
 241         dir_copy.list = g_new0 (file_entry_t, size);
 242         dir_copy.size = size;
 243         dir_copy.len = 0;
 244     }
 245 }
 246 
 247 /* --------------------------------------------------------------------------------------------- */
 248 /*** public functions ****************************************************************************/
 249 /* --------------------------------------------------------------------------------------------- */
 250 /**
 251  * Increase or decrease directory list size.
 252  *
 253  * @param list directory list
 254  * @param delta value by increase (if positive) or decrease (if negative) list size
 255  *
 256  * @return FALSE on failure, TRUE on success
 257  */
 258 
 259 gboolean
 260 dir_list_grow (dir_list * list, int delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 261 {
 262     int size;
 263     gboolean clear_flag = FALSE;
 264 
 265     if (list == NULL)
 266         return FALSE;
 267 
 268     if (delta == 0)
 269         return TRUE;
 270 
 271     size = list->size + delta;
 272     if (size <= 0)
 273     {
 274         size = DIR_LIST_MIN_SIZE;
 275         clear_flag = TRUE;
 276     }
 277 
 278     if (size != list->size)
 279     {
 280         file_entry_t *fe;
 281 
 282         fe = g_try_renew (file_entry_t, list->list, size);
 283         if (fe == NULL)
 284             return FALSE;
 285 
 286         list->list = fe;
 287         list->size = size;
 288     }
 289 
 290     list->len = clear_flag ? 0 : MIN (list->len, size);
 291 
 292     return TRUE;
 293 }
 294 
 295 /* --------------------------------------------------------------------------------------------- */
 296 /**
 297  * Append file info to the directory list.
 298  *
 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
 304  *
 305  * @return FALSE on failure, TRUE on success
 306  */
 307 
 308 gboolean
 309 dir_list_append (dir_list * list, const char *fname, const struct stat * st,
     /* [previous][next][first][last][top][bottom][index][help]  */
 310                  gboolean link_to_dir, gboolean stale_link)
 311 {
 312     file_entry_t *fentry;
 313 
 314     /* Need to grow the *list? */
 315     if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
 316         return FALSE;
 317 
 318     fentry = &list->list[list->len];
 319     fentry->fname = g_string_new (fname);
 320     fentry->f.marked = 0;
 321     fentry->f.link_to_dir = link_to_dir ? 1 : 0;
 322     fentry->f.stale_link = stale_link ? 1 : 0;
 323     fentry->f.dir_size_computed = 0;
 324     fentry->st = *st;
 325     fentry->sort_key = NULL;
 326     fentry->second_sort_key = NULL;
 327 
 328     list->len++;
 329 
 330     return TRUE;
 331 }
 332 
 333 /* --------------------------------------------------------------------------------------------- */
 334 
 335 int
 336 unsorted (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 337 {
 338     (void) a;
 339     (void) b;
 340 
 341     return 0;
 342 }
 343 
 344 /* --------------------------------------------------------------------------------------------- */
 345 
 346 int
 347 sort_name (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 348 {
 349     int ad = MY_ISDIR (a);
 350     int bd = MY_ISDIR (b);
 351 
 352     if (ad == bd || panels_options.mix_all_files)
 353         return compare_by_names (a, b);
 354 
 355     return bd - ad;
 356 }
 357 
 358 /* --------------------------------------------------------------------------------------------- */
 359 
 360 int
 361 sort_vers (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 362 {
 363     int ad = MY_ISDIR (a);
 364     int bd = MY_ISDIR (b);
 365 
 366     if (ad == bd || panels_options.mix_all_files)
 367     {
 368         int result;
 369 
 370         result = filevercmp (a->fname->str, b->fname->str);
 371         if (result != 0)
 372             return result * reverse;
 373 
 374         return compare_by_names (a, b);
 375     }
 376 
 377     return bd - ad;
 378 }
 379 
 380 /* --------------------------------------------------------------------------------------------- */
 381 
 382 int
 383 sort_ext (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 384 {
 385     int ad = MY_ISDIR (a);
 386     int bd = MY_ISDIR (b);
 387 
 388     if (ad == bd || panels_options.mix_all_files)
 389     {
 390         int r;
 391 
 392         if (a->second_sort_key == NULL)
 393             a->second_sort_key = str_create_key (extension (a->fname->str), case_sensitive);
 394         if (b->second_sort_key == NULL)
 395             b->second_sort_key = str_create_key (extension (b->fname->str), case_sensitive);
 396 
 397         r = str_key_collate (a->second_sort_key, b->second_sort_key, case_sensitive);
 398         if (r != 0)
 399             return r * reverse;
 400 
 401         return compare_by_names (a, b);
 402     }
 403 
 404     return bd - ad;
 405 }
 406 
 407 /* --------------------------------------------------------------------------------------------- */
 408 
 409 int
 410 sort_time (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 411 {
 412     int ad = MY_ISDIR (a);
 413     int bd = MY_ISDIR (b);
 414 
 415     if (ad == bd || panels_options.mix_all_files)
 416     {
 417         int result = _GL_CMP (a->st.st_mtime, b->st.st_mtime);
 418 
 419         if (result != 0)
 420             return result * reverse;
 421 
 422         return compare_by_names (a, b);
 423     }
 424 
 425     return bd - ad;
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */
 429 
 430 int
 431 sort_ctime (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 432 {
 433     int ad = MY_ISDIR (a);
 434     int bd = MY_ISDIR (b);
 435 
 436     if (ad == bd || panels_options.mix_all_files)
 437     {
 438         int result = _GL_CMP (a->st.st_ctime, b->st.st_ctime);
 439 
 440         if (result != 0)
 441             return result * reverse;
 442 
 443         return compare_by_names (a, b);
 444     }
 445 
 446     return bd - ad;
 447 }
 448 
 449 /* --------------------------------------------------------------------------------------------- */
 450 
 451 int
 452 sort_atime (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 453 {
 454     int ad = MY_ISDIR (a);
 455     int bd = MY_ISDIR (b);
 456 
 457     if (ad == bd || panels_options.mix_all_files)
 458     {
 459         int result = _GL_CMP (a->st.st_atime, b->st.st_atime);
 460 
 461         if (result != 0)
 462             return result * reverse;
 463 
 464         return compare_by_names (a, b);
 465     }
 466 
 467     return bd - ad;
 468 }
 469 
 470 /* --------------------------------------------------------------------------------------------- */
 471 
 472 int
 473 sort_inode (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 474 {
 475     int ad = MY_ISDIR (a);
 476     int bd = MY_ISDIR (b);
 477 
 478     if (ad == bd || panels_options.mix_all_files)
 479         return (a->st.st_ino - b->st.st_ino) * reverse;
 480 
 481     return bd - ad;
 482 }
 483 
 484 /* --------------------------------------------------------------------------------------------- */
 485 
 486 int
 487 sort_size (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 488 {
 489     int ad = MY_ISDIR (a);
 490     int bd = MY_ISDIR (b);
 491 
 492     if (ad == bd || panels_options.mix_all_files)
 493     {
 494         int result = _GL_CMP (a->st.st_size, b->st.st_size);
 495 
 496         if (result != 0)
 497             return result * reverse;
 498 
 499         return compare_by_names (a, b);
 500     }
 501 
 502     return bd - ad;
 503 }
 504 
 505 /* --------------------------------------------------------------------------------------------- */
 506 
 507 void
 508 dir_list_sort (dir_list * list, GCompareFunc sort, const dir_sort_options_t * sort_op)
     /* [previous][next][first][last][top][bottom][index][help]  */
 509 {
 510     if (list->len > 1 && sort != (GCompareFunc) unsorted)
 511     {
 512         file_entry_t *fentry = &list->list[0];
 513         int dot_dot_found;
 514 
 515         /* If there is an ".." entry the caller must take care to
 516            ensure that it occupies the first list element. */
 517         dot_dot_found = DIR_IS_DOTDOT (fentry->fname->str) ? 1 : 0;
 518         reverse = sort_op->reverse ? -1 : 1;
 519         case_sensitive = sort_op->case_sensitive ? 1 : 0;
 520         exec_first = sort_op->exec_first;
 521         qsort (&(list->list)[dot_dot_found], list->len - dot_dot_found, sizeof (file_entry_t),
 522                sort);
 523 
 524         clean_sort_keys (list, dot_dot_found, list->len - dot_dot_found);
 525     }
 526 }
 527 
 528 /* --------------------------------------------------------------------------------------------- */
 529 
 530 void
 531 dir_list_clean (dir_list * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 532 {
 533     int i;
 534 
 535     for (i = 0; i < list->len; i++)
 536     {
 537         file_entry_t *fentry;
 538 
 539         fentry = &list->list[i];
 540         g_string_free (fentry->fname, TRUE);
 541         fentry->fname = NULL;
 542     }
 543 
 544     list->len = 0;
 545     /* reduce memory usage */
 546     dir_list_grow (list, DIR_LIST_MIN_SIZE - list->size);
 547 }
 548 
 549 /* --------------------------------------------------------------------------------------------- */
 550 
 551 void
 552 dir_list_free_list (dir_list * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 553 {
 554     int i;
 555 
 556     for (i = 0; i < list->len; i++)
 557     {
 558         file_entry_t *fentry;
 559 
 560         fentry = &list->list[i];
 561         g_string_free (fentry->fname, TRUE);
 562     }
 563 
 564     MC_PTR_FREE (list->list);
 565     list->len = 0;
 566     list->size = 0;
 567 }
 568 
 569 /* --------------------------------------------------------------------------------------------- */
 570 /** Used to set up a directory list when there is no access to a directory */
 571 
 572 gboolean
 573 dir_list_init (dir_list * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 574 {
 575     file_entry_t *fentry;
 576 
 577     /* Need to grow the *list? */
 578     if (list->size == 0 && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
 579     {
 580         list->len = 0;
 581         return FALSE;
 582     }
 583 
 584     fentry = &list->list[0];
 585     memset (fentry, 0, sizeof (*fentry));
 586     fentry->fname = g_string_new ("..");
 587     fentry->f.link_to_dir = 0;
 588     fentry->f.stale_link = 0;
 589     fentry->f.dir_size_computed = 0;
 590     fentry->f.marked = 0;
 591     fentry->st.st_mode = 040755;
 592     list->len = 1;
 593     return TRUE;
 594 }
 595 
 596 /* --------------------------------------------------------------------------------------------- */
 597 /**
 598    handle_path is a simplified handle_dirent. The difference is that
 599    handle_path doesn't pay attention to panels_options.show_dot_files
 600    and panels_options.show_backups.
 601    Moreover handle_path can't be used with a filemask.
 602    If you change handle_path then check also handle_dirent. */
 603 /* Return values: FALSE = don't add, TRUE = add to the list */
 604 
 605 gboolean
 606 handle_path (const char *path, struct stat * buf1, gboolean * link_to_dir, gboolean * stale_link)
     /* [previous][next][first][last][top][bottom][index][help]  */
 607 {
 608     vfs_path_t *vpath;
 609 
 610     if (DIR_IS_DOT (path) || DIR_IS_DOTDOT (path))
 611         return FALSE;
 612 
 613     vpath = vfs_path_from_str (path);
 614     if (mc_lstat (vpath, buf1) == -1)
 615     {
 616         vfs_path_free (vpath, TRUE);
 617         return FALSE;
 618     }
 619 
 620     if (S_ISDIR (buf1->st_mode))
 621         tree_store_mark_checked (path);
 622 
 623     /* A link to a file or a directory? */
 624     *link_to_dir = FALSE;
 625     *stale_link = FALSE;
 626     if (S_ISLNK (buf1->st_mode))
 627     {
 628         struct stat buf2;
 629 
 630         if (mc_stat (vpath, &buf2) == 0)
 631             *link_to_dir = S_ISDIR (buf2.st_mode) != 0;
 632         else
 633             *stale_link = TRUE;
 634     }
 635 
 636     vfs_path_free (vpath, TRUE);
 637 
 638     return TRUE;
 639 }
 640 
 641 /* --------------------------------------------------------------------------------------------- */
 642 
 643 gboolean
 644 dir_list_load (dir_list * list, const vfs_path_t * vpath, GCompareFunc sort,
     /* [previous][next][first][last][top][bottom][index][help]  */
 645                const dir_sort_options_t * sort_op, const file_filter_t * filter)
 646 {
 647     DIR *dirp;
 648     struct vfs_dirent *dp;
 649     struct stat st;
 650     file_entry_t *fentry;
 651     const char *vpath_str;
 652     gboolean ret = TRUE;
 653 
 654     /* ".." (if any) must be the first entry in the list */
 655     if (!dir_list_init (list))
 656         return FALSE;
 657 
 658     fentry = &list->list[0];
 659     if (dir_get_dotdot_stat (vpath, &st))
 660         fentry->st = st;
 661 
 662     if (list->callback != NULL)
 663         list->callback (DIR_OPEN, (void *) vpath);
 664     dirp = mc_opendir (vpath);
 665     if (dirp == NULL)
 666         return FALSE;
 667 
 668     tree_store_start_check (vpath);
 669 
 670     vpath_str = vfs_path_as_str (vpath);
 671     /* Do not add a ".." entry to the root directory */
 672     if (IS_PATH_SEP (vpath_str[0]) && vpath_str[1] == '\0')
 673         dir_list_clean (list);
 674 
 675     while (ret && (dp = mc_readdir (dirp)) != NULL)
 676     {
 677         gboolean link_to_dir, stale_link;
 678 
 679         if (list->callback != NULL)
 680             list->callback (DIR_READ, dp);
 681 
 682         if (!handle_dirent (dp, filter, &st, &link_to_dir, &stale_link))
 683             continue;
 684 
 685         if (!dir_list_append (list, dp->d_name, &st, link_to_dir, stale_link))
 686             ret = FALSE;
 687     }
 688 
 689     if (ret)
 690         dir_list_sort (list, sort, sort_op);
 691 
 692     if (list->callback != NULL)
 693         list->callback (DIR_CLOSE, NULL);
 694     mc_closedir (dirp);
 695     tree_store_end_check ();
 696 
 697     return ret;
 698 }
 699 
 700 /* --------------------------------------------------------------------------------------------- */
 701 
 702 gboolean
 703 if_link_is_exe (const vfs_path_t * full_name_vpath, const file_entry_t * file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 704 {
 705     struct stat b;
 706 
 707     if (S_ISLNK (file->st.st_mode) && mc_stat (full_name_vpath, &b) == 0)
 708         return is_exe (b.st_mode);
 709 
 710     return TRUE;
 711 }
 712 
 713 /* --------------------------------------------------------------------------------------------- */
 714 /** If filter is null, then it is a match */
 715 
 716 gboolean
 717 dir_list_reload (dir_list * list, const vfs_path_t * vpath, GCompareFunc sort,
     /* [previous][next][first][last][top][bottom][index][help]  */
 718                  const dir_sort_options_t * sort_op, const file_filter_t * filter)
 719 {
 720     DIR *dirp;
 721     struct vfs_dirent *dp;
 722     int i;
 723     struct stat st;
 724     int marked_cnt;
 725     GHashTable *marked_files;
 726     const char *tmp_path;
 727     gboolean ret = TRUE;
 728 
 729     if (list->callback != NULL)
 730         list->callback (DIR_OPEN, (void *) vpath);
 731     dirp = mc_opendir (vpath);
 732     if (dirp == NULL)
 733     {
 734         dir_list_clean (list);
 735         dir_list_init (list);
 736         return FALSE;
 737     }
 738 
 739     tree_store_start_check (vpath);
 740 
 741     marked_files = g_hash_table_new (g_str_hash, g_str_equal);
 742     alloc_dir_copy (list->len);
 743     for (marked_cnt = i = 0; i < list->len; i++)
 744     {
 745         file_entry_t *fentry, *dfentry;
 746 
 747         fentry = &list->list[i];
 748         dfentry = &dir_copy.list[i];
 749 
 750         dfentry->fname = mc_g_string_dup (fentry->fname);
 751         dfentry->f.marked = fentry->f.marked;
 752         dfentry->f.dir_size_computed = fentry->f.dir_size_computed;
 753         dfentry->f.link_to_dir = fentry->f.link_to_dir;
 754         dfentry->f.stale_link = fentry->f.stale_link;
 755         dfentry->sort_key = NULL;
 756         dfentry->second_sort_key = NULL;
 757         if (fentry->f.marked)
 758         {
 759             g_hash_table_insert (marked_files, dfentry->fname->str, dfentry);
 760             marked_cnt++;
 761         }
 762     }
 763 
 764     /* save len for later dir_list_clean() */
 765     dir_copy.len = list->len;
 766 
 767     /* Add ".." except to the root directory. The ".." entry
 768        (if any) must be the first in the list. */
 769     tmp_path = vfs_path_get_by_index (vpath, 0)->path;
 770     if (vfs_path_elements_count (vpath) == 1 && IS_PATH_SEP (tmp_path[0]) && tmp_path[1] == '\0')
 771     {
 772         /* root directory */
 773         dir_list_clean (list);
 774     }
 775     else
 776     {
 777         dir_list_clean (list);
 778         if (!dir_list_init (list))
 779         {
 780             dir_list_free_list (&dir_copy);
 781             mc_closedir (dirp);
 782             return FALSE;
 783         }
 784 
 785         if (dir_get_dotdot_stat (vpath, &st))
 786         {
 787             file_entry_t *fentry;
 788 
 789             fentry = &list->list[0];
 790             fentry->st = st;
 791         }
 792     }
 793 
 794     while (ret && (dp = mc_readdir (dirp)) != NULL)
 795     {
 796         gboolean link_to_dir, stale_link;
 797 
 798         if (list->callback != NULL)
 799             list->callback (DIR_READ, dp);
 800 
 801         if (!handle_dirent (dp, filter, &st, &link_to_dir, &stale_link))
 802             continue;
 803 
 804         if (!dir_list_append (list, dp->d_name, &st, link_to_dir, stale_link))
 805             ret = FALSE;
 806         else
 807         {
 808             file_entry_t *fentry;
 809 
 810             fentry = &list->list[list->len - 1];
 811 
 812             /*
 813              * If we have marked files in the copy, scan through the copy
 814              * to find matching file.  Decrease number of remaining marks if
 815              * we copied one.
 816              */
 817             fentry->f.marked = (marked_cnt > 0
 818                                 && g_hash_table_lookup (marked_files, dp->d_name) != NULL);
 819             if (fentry->f.marked)
 820                 marked_cnt--;
 821         }
 822     }
 823 
 824     if (ret)
 825         dir_list_sort (list, sort, sort_op);
 826 
 827     if (list->callback != NULL)
 828         list->callback (DIR_CLOSE, NULL);
 829     mc_closedir (dirp);
 830     tree_store_end_check ();
 831 
 832     g_hash_table_destroy (marked_files);
 833     dir_list_free_list (&dir_copy);
 834 
 835     return ret;
 836 }
 837 
 838 /* --------------------------------------------------------------------------------------------- */
 839 
 840 void
 841 file_filter_clear (file_filter_t * filter)
     /* [previous][next][first][last][top][bottom][index][help]  */
 842 {
 843     MC_PTR_FREE (filter->value);
 844     mc_search_free (filter->handler);
 845     filter->handler = NULL;
 846     /* keep filter->flags */
 847 }
 848 
 849 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */