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

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