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-2019
   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 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);
 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);
 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->fnamelen = strlen (fname);
 298     fentry->fname = g_strndup (fname, fentry->fnamelen);
 299     fentry->f.marked = 0;
 300     fentry->f.link_to_dir = link_to_dir ? 1 : 0;
 301     fentry->f.stale_link = stale_link ? 1 : 0;
 302     fentry->f.dir_size_computed = 0;
 303     fentry->st = *st;
 304     fentry->sort_key = NULL;
 305     fentry->second_sort_key = NULL;
 306 
 307     list->len++;
 308 
 309     return TRUE;
 310 }
 311 
 312 /* --------------------------------------------------------------------------------------------- */
 313 
 314 int
 315 unsorted (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 316 {
 317     (void) a;
 318     (void) b;
 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, case_sensitive);
 335         if (b->sort_key == NULL)
 336             b->sort_key = str_create_key_for_filename (b->fname, case_sensitive);
 337 
 338         return key_collate (a->sort_key, b->sort_key);
 339     }
 340     return bd - ad;
 341 }
 342 
 343 /* --------------------------------------------------------------------------------------------- */
 344 
 345 int
 346 sort_vers (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 347 {
 348     int ad = MY_ISDIR (a);
 349     int bd = MY_ISDIR (b);
 350 
 351     if (ad == bd || panels_options.mix_all_files)
 352     {
 353         return filevercmp (a->fname, b->fname) * reverse;
 354     }
 355     else
 356     {
 357         return bd - ad;
 358     }
 359 }
 360 
 361 /* --------------------------------------------------------------------------------------------- */
 362 
 363 int
 364 sort_ext (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 365 {
 366     int ad = MY_ISDIR (a);
 367     int bd = MY_ISDIR (b);
 368 
 369     if (ad == bd || panels_options.mix_all_files)
 370     {
 371         int r;
 372 
 373         if (a->second_sort_key == NULL)
 374             a->second_sort_key = str_create_key (extension (a->fname), case_sensitive);
 375         if (b->second_sort_key == NULL)
 376             b->second_sort_key = str_create_key (extension (b->fname), case_sensitive);
 377 
 378         r = str_key_collate (a->second_sort_key, b->second_sort_key, case_sensitive);
 379         if (r != 0)
 380             return r * reverse;
 381         else
 382             return sort_name (a, b);
 383     }
 384     else
 385         return bd - ad;
 386 }
 387 
 388 /* --------------------------------------------------------------------------------------------- */
 389 
 390 int
 391 sort_time (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 392 {
 393     int ad = MY_ISDIR (a);
 394     int bd = MY_ISDIR (b);
 395 
 396     if (ad == bd || panels_options.mix_all_files)
 397     {
 398         int result = a->st.st_mtime < b->st.st_mtime ? -1 : a->st.st_mtime > b->st.st_mtime;
 399         if (result != 0)
 400             return result * reverse;
 401         else
 402             return sort_name (a, b);
 403     }
 404     else
 405         return bd - ad;
 406 }
 407 
 408 /* --------------------------------------------------------------------------------------------- */
 409 
 410 int
 411 sort_ctime (file_entry_t * a, file_entry_t * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 412 {
 413     int ad = MY_ISDIR (a);
 414     int bd = MY_ISDIR (b);
 415 
 416     if (ad == bd || panels_options.mix_all_files)
 417     {
 418         int result = a->st.st_ctime < b->st.st_ctime ? -1 : a->st.st_ctime > b->st.st_ctime;
 419         if (result != 0)
 420             return result * reverse;
 421         else
 422             return sort_name (a, b);
 423     }
 424     else
 425         return bd - ad;
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */
 429 
 430 int
 431 sort_atime (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 = a->st.st_atime < b->st.st_atime ? -1 : a->st.st_atime > b->st.st_atime;
 439         if (result != 0)
 440             return result * reverse;
 441         else
 442             return sort_name (a, b);
 443     }
 444     else
 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     else
 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     int result = 0;
 470 
 471     if (ad != bd && !panels_options.mix_all_files)
 472         return bd - ad;
 473 
 474     result = a->st.st_size < b->st.st_size ? -1 : a->st.st_size > b->st.st_size;
 475     if (result != 0)
 476         return result * reverse;
 477     else
 478         return sort_name (a, b);
 479 }
 480 
 481 /* --------------------------------------------------------------------------------------------- */
 482 
 483 void
 484 dir_list_sort (dir_list * list, GCompareFunc sort, const dir_sort_options_t * sort_op)
     /* [previous][next][first][last][top][bottom][index][help]  */
 485 {
 486     file_entry_t *fentry;
 487     int dot_dot_found = 0;
 488 
 489     if (list->len < 2 || sort == (GCompareFunc) unsorted)
 490         return;
 491 
 492     /* If there is an ".." entry the caller must take care to
 493        ensure that it occupies the first list element. */
 494     fentry = &list->list[0];
 495     if (DIR_IS_DOTDOT (fentry->fname))
 496         dot_dot_found = 1;
 497 
 498     reverse = sort_op->reverse ? -1 : 1;
 499     case_sensitive = sort_op->case_sensitive ? 1 : 0;
 500     exec_first = sort_op->exec_first;
 501     qsort (&(list->list)[dot_dot_found], list->len - dot_dot_found, sizeof (file_entry_t), sort);
 502 
 503     clean_sort_keys (list, dot_dot_found, list->len - dot_dot_found);
 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         MC_PTR_FREE (fentry->fname);
 519     }
 520 
 521     list->len = 0;
 522     /* reduce memory usage */
 523     dir_list_grow (list, DIR_LIST_MIN_SIZE - list->size);
 524 }
 525 
 526 /* --------------------------------------------------------------------------------------------- */
 527 
 528 void
 529 dir_list_free_list (dir_list * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 530 {
 531     int i;
 532 
 533     for (i = 0; i < list->len; i++)
 534     {
 535         file_entry_t *fentry;
 536 
 537         fentry = &list->list[i];
 538         g_free (fentry->fname);
 539     }
 540 
 541     MC_PTR_FREE (list->list);
 542     list->len = 0;
 543     list->size = 0;
 544 }
 545 
 546 /* --------------------------------------------------------------------------------------------- */
 547 /** Used to set up a directory list when there is no access to a directory */
 548 
 549 gboolean
 550 dir_list_init (dir_list * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 551 {
 552     file_entry_t *fentry;
 553 
 554     /* Need to grow the *list? */
 555     if (list->size == 0 && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
 556     {
 557         list->len = 0;
 558         return FALSE;
 559     }
 560 
 561     fentry = &list->list[0];
 562     memset (fentry, 0, sizeof (*fentry));
 563     fentry->fnamelen = 2;
 564     fentry->fname = g_strndup ("..", fentry->fnamelen);
 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);
 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);
 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 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     return TRUE;
 688 }
 689 
 690 /* --------------------------------------------------------------------------------------------- */
 691 /** If fltr is null, then it is a match */
 692 
 693 gboolean
 694 dir_list_reload (dir_list * list, const vfs_path_t * vpath, GCompareFunc sort,
     /* [previous][next][first][last][top][bottom][index][help]  */
 695                  const dir_sort_options_t * sort_op, const char *fltr)
 696 {
 697     DIR *dirp;
 698     struct dirent *dp;
 699     int i;
 700     struct stat st;
 701     int marked_cnt;
 702     GHashTable *marked_files;
 703     const char *tmp_path;
 704     gboolean ret = TRUE;
 705 
 706     if (list->callback != NULL)
 707         list->callback (DIR_OPEN, (void *) vpath);
 708     dirp = mc_opendir (vpath);
 709     if (dirp == NULL)
 710     {
 711         dir_list_clean (list);
 712         dir_list_init (list);
 713         return FALSE;
 714     }
 715 
 716     tree_store_start_check (vpath);
 717 
 718     marked_files = g_hash_table_new (g_str_hash, g_str_equal);
 719     alloc_dir_copy (list->len);
 720     for (marked_cnt = i = 0; i < list->len; i++)
 721     {
 722         file_entry_t *fentry, *dfentry;
 723 
 724         fentry = &list->list[i];
 725         dfentry = &dir_copy.list[i];
 726 
 727         dfentry->fnamelen = fentry->fnamelen;
 728         dfentry->fname = g_strndup (fentry->fname, fentry->fnamelen);
 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, 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]  */