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

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