root/src/filemanager/dir.c

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

DEFINITIONS

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

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

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