root/src/filemanager/treestore.c

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

DEFINITIONS

This source file includes following definitions.
  1. tree_store_dirty
  2. str_common
  3. pathcmp
  4. decode
  5. tree_store_load_from
  6. encode
  7. tree_store_save_to
  8. tree_store_add_entry
  9. tree_store_notify_remove
  10. remove_entry
  11. process_special_dirs
  12. should_skip_directory
  13. queue_vpath_free
  14. tree_store_whereis
  15. tree_store_get
  16. tree_store_load
  17. tree_store_save
  18. tree_store_add_entry_remove_hook
  19. tree_store_remove_entry_remove_hook
  20. tree_store_remove_entry
  21. tree_store_mark_checked
  22. tree_store_start_check
  23. tree_store_end_check
  24. tree_store_rescan

   1 /*
   2    Tree Store
   3    Contains a storage of the file system tree representation
   4 
   5    This module has been converted to be a widget.
   6 
   7    The program load and saves the tree each time the tree widget is
   8    created and destroyed.  This is required for the future vfs layer,
   9    it will be possible to have tree views over virtual file systems.
  10 
  11    Copyright (C) 1999-2024
  12    Free Software Foundation, Inc.
  13 
  14    Written by:
  15    Janne Kukonlehto, 1994, 1996
  16    Norbert Warmuth, 1997
  17    Miguel de Icaza, 1996, 1999
  18    Slava Zanko <slavazanko@gmail.com>, 2013
  19    Andrew Borodin <aborodin@vmail.ru>, 2013
  20 
  21    This file is part of the Midnight Commander.
  22 
  23    The Midnight Commander is free software: you can redistribute it
  24    and/or modify it under the terms of the GNU General Public License as
  25    published by the Free Software Foundation, either version 3 of the License,
  26    or (at your option) any later version.
  27 
  28    The Midnight Commander is distributed in the hope that it will be useful,
  29    but WITHOUT ANY WARRANTY; without even the implied warranty of
  30    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31    GNU General Public License for more details.
  32 
  33    You should have received a copy of the GNU General Public License
  34    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  35  */
  36 
  37 /** \file treestore.c
  38  *  \brief Source: tree store
  39  *
  40  *  Contains a storage of the file system tree representation.
  41  */
  42 
  43 #include <config.h>
  44 
  45 #include <errno.h>
  46 #include <stdio.h>
  47 #include <stdlib.h>
  48 #include <string.h>
  49 #include <sys/types.h>
  50 #include <sys/stat.h>
  51 #include <unistd.h>
  52 
  53 #include "lib/global.h"
  54 #include "lib/mcconfig.h"
  55 #include "lib/vfs/vfs.h"
  56 #include "lib/fileloc.h"
  57 #include "lib/strutil.h"
  58 #include "lib/hook.h"
  59 #include "lib/util.h"
  60 
  61 #include "src/setup.h"          /* setup_init() */
  62 
  63 #include "treestore.h"
  64 
  65 /*** global variables ****************************************************************************/
  66 
  67 /*** file scope macro definitions ****************************************************************/
  68 
  69 #define TREE_SIGNATURE "Midnight Commander TreeStore v 2.0"
  70 
  71 /*** file scope type declarations ****************************************************************/
  72 
  73 /*** forward declarations (file scope functions) *************************************************/
  74 
  75 static tree_entry *tree_store_add_entry (const vfs_path_t * name);
  76 
  77 /*** file scope variables ************************************************************************/
  78 
  79 static struct TreeStore ts;
  80 
  81 static hook_t *remove_entry_hooks;
  82 
  83 /* --------------------------------------------------------------------------------------------- */
  84 /*** file scope functions ************************************************************************/
  85 /* --------------------------------------------------------------------------------------------- */
  86 
  87 static inline void
  88 tree_store_dirty (gboolean dirty)
     /* [previous][next][first][last][top][bottom][index][help]  */
  89 {
  90     ts.dirty = dirty;
  91 }
  92 
  93 /* --------------------------------------------------------------------------------------------- */
  94 /**
  95   *
  96   * @return the number of common bytes in the strings.
  97   */
  98 
  99 static size_t
 100 str_common (const vfs_path_t *s1_vpath, const vfs_path_t *s2_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 101 {
 102     size_t result = 0;
 103     const char *s1, *s2;
 104 
 105     s1 = vfs_path_as_str (s1_vpath);
 106     s2 = vfs_path_as_str (s2_vpath);
 107 
 108     while (*s1 != '\0' && *s2 != '\0' && *s1++ == *s2++)
 109         result++;
 110 
 111     return result;
 112 }
 113 
 114 /* --------------------------------------------------------------------------------------------- */
 115 /** The directory names are arranged in a single linked list in the same
 116   * order as they are displayed. When the tree is displayed the expected
 117   * order is like this:
 118   * /
 119   * /bin
 120   * /etc
 121   * /etc/X11
 122   * /etc/rc.d
 123   * /etc.old/X11
 124   * /etc.old/rc.d
 125   * /usr
 126   *
 127   * i.e. the required collating sequence when comparing two directory names is 
 128   * '\0' < PATH_SEP < all-other-characters-in-encoding-order
 129   *
 130   * Since strcmp doesn't fulfil this requirement we use pathcmp when
 131   * inserting directory names into the list. The meaning of the return value 
 132   * of pathcmp and strcmp are the same (an integer less than, equal to, or 
 133   * greater than zero if p1 is found to be less than, to match, or be greater 
 134   * than p2.
 135  */
 136 
 137 static int
 138 pathcmp (const vfs_path_t *p1_vpath, const vfs_path_t *p2_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 139 {
 140     int ret_val;
 141     const char *p1, *p2;
 142 
 143     p1 = vfs_path_as_str (p1_vpath);
 144     p2 = vfs_path_as_str (p2_vpath);
 145 
 146     for (; *p1 == *p2; p1++, p2++)
 147         if (*p1 == '\0')
 148             return 0;
 149 
 150     if (*p1 == '\0')
 151         ret_val = -1;
 152     else if (*p2 == '\0')
 153         ret_val = 1;
 154     else if (IS_PATH_SEP (*p1))
 155         ret_val = -1;
 156     else if (IS_PATH_SEP (*p2))
 157         ret_val = 1;
 158     else
 159         ret_val = (*p1 - *p2);
 160 
 161     return ret_val;
 162 }
 163 
 164 /* --------------------------------------------------------------------------------------------- */
 165 
 166 static char *
 167 decode (char *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 168 {
 169     char *res, *p, *q;
 170 
 171     res = g_strdup (buffer);
 172 
 173     for (p = q = res; *p != '\0'; p++, q++)
 174     {
 175         if (*p == '\n')
 176         {
 177             *q = '\0';
 178             return res;
 179         }
 180 
 181         if (*p != '\\')
 182         {
 183             *q = *p;
 184             continue;
 185         }
 186 
 187         p++;
 188 
 189         switch (*p)
 190         {
 191         case 'n':
 192             *q = '\n';
 193             break;
 194         case '\\':
 195             *q = '\\';
 196             break;
 197         default:
 198             break;
 199         }
 200     }
 201 
 202     *q = *p;
 203 
 204     return res;
 205 }
 206 
 207 /* --------------------------------------------------------------------------------------------- */
 208 /** Loads the tree store from the specified filename */
 209 
 210 static int
 211 tree_store_load_from (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 212 {
 213     FILE *file;
 214     char buffer[MC_MAXPATHLEN + 20];
 215 
 216     g_return_val_if_fail (name != NULL, 0);
 217 
 218     if (ts.loaded)
 219         return 1;
 220 
 221     file = fopen (name, "r");
 222 
 223     if (file != NULL
 224         && (fgets (buffer, sizeof (buffer), file) == NULL
 225             || strncmp (buffer, TREE_SIGNATURE, strlen (TREE_SIGNATURE)) != 0))
 226     {
 227         fclose (file);
 228         file = NULL;
 229     }
 230 
 231     if (file != NULL)
 232     {
 233         char oldname[MC_MAXPATHLEN] = "\0";
 234 
 235         ts.loaded = TRUE;
 236 
 237         /* File open -> read contents */
 238         while (fgets (buffer, MC_MAXPATHLEN, file))
 239         {
 240             tree_entry *e;
 241             gboolean scanned;
 242             char *lc_name;
 243 
 244             /* Skip invalid records */
 245             if (buffer[0] != '0' && buffer[0] != '1')
 246                 continue;
 247 
 248             if (buffer[1] != ':')
 249                 continue;
 250 
 251             scanned = buffer[0] == '1';
 252 
 253             lc_name = decode (buffer + 2);
 254             if (!IS_PATH_SEP (lc_name[0]))
 255             {
 256                 /* Clear-text decompression */
 257                 char *s;
 258 
 259                 s = strtok (lc_name, " ");
 260                 if (s != NULL)
 261                 {
 262                     char *different;
 263                     int common;
 264 
 265                     common = atoi (s);
 266                     different = strtok (NULL, "");
 267                     if (different != NULL)
 268                     {
 269                         vfs_path_t *vpath;
 270 
 271                         vpath = vfs_path_from_str (oldname);
 272                         g_strlcpy (oldname + common, different, sizeof (oldname) - (size_t) common);
 273                         if (vfs_file_is_local (vpath))
 274                         {
 275                             vfs_path_t *tmp_vpath;
 276 
 277                             tmp_vpath = vfs_path_from_str (oldname);
 278                             e = tree_store_add_entry (tmp_vpath);
 279                             vfs_path_free (tmp_vpath, TRUE);
 280                             e->scanned = scanned;
 281                         }
 282                         vfs_path_free (vpath, TRUE);
 283                     }
 284                 }
 285             }
 286             else
 287             {
 288                 vfs_path_t *vpath;
 289 
 290                 vpath = vfs_path_from_str (lc_name);
 291                 if (vfs_file_is_local (vpath))
 292                 {
 293                     e = tree_store_add_entry (vpath);
 294                     e->scanned = scanned;
 295                 }
 296                 vfs_path_free (vpath, TRUE);
 297                 g_strlcpy (oldname, lc_name, sizeof (oldname));
 298             }
 299             g_free (lc_name);
 300         }
 301 
 302         fclose (file);
 303     }
 304 
 305     /* Nothing loaded, we add some standard directories */
 306     if (!ts.tree_first)
 307     {
 308         vfs_path_t *tmp_vpath;
 309 
 310         tmp_vpath = vfs_path_from_str (PATH_SEP_STR);
 311         tree_store_add_entry (tmp_vpath);
 312         tree_store_rescan (tmp_vpath);
 313         vfs_path_free (tmp_vpath, TRUE);
 314         ts.loaded = TRUE;
 315     }
 316 
 317     return 1;
 318 }
 319 
 320 /* --------------------------------------------------------------------------------------------- */
 321 
 322 static char *
 323 encode (const vfs_path_t *vpath, size_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 324 {
 325     return str_escape (vfs_path_as_str (vpath) + offset, -1, "\n\\", FALSE);
 326 }
 327 
 328 /* --------------------------------------------------------------------------------------------- */
 329 /** Saves the tree to the specified filename */
 330 
 331 static int
 332 tree_store_save_to (char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 333 {
 334     tree_entry *current;
 335     FILE *file;
 336 
 337     file = fopen (name, "w");
 338     if (file == NULL)
 339         return errno;
 340 
 341     fprintf (file, "%s\n", TREE_SIGNATURE);
 342 
 343     for (current = ts.tree_first; current != NULL; current = current->next)
 344         if (vfs_file_is_local (current->name))
 345         {
 346             int i, common;
 347 
 348             /* Clear-text compression */
 349             if (current->prev != NULL
 350                 && (common = str_common (current->prev->name, current->name)) > 2)
 351             {
 352                 char *encoded;
 353 
 354                 encoded = encode (current->name, common);
 355                 i = fprintf (file, "%d:%d %s\n", current->scanned ? 1 : 0, common, encoded);
 356                 g_free (encoded);
 357             }
 358             else
 359             {
 360                 char *encoded;
 361 
 362                 encoded = encode (current->name, 0);
 363                 i = fprintf (file, "%d:%s\n", current->scanned ? 1 : 0, encoded);
 364                 g_free (encoded);
 365             }
 366 
 367             if (i == EOF)
 368             {
 369                 fprintf (stderr, _("Cannot write to the %s file:\n%s\n"),
 370                          name, unix_error_string (errno));
 371                 break;
 372             }
 373         }
 374 
 375     tree_store_dirty (FALSE);
 376     fclose (file);
 377 
 378     return 0;
 379 }
 380 
 381 /* --------------------------------------------------------------------------------------------- */
 382 
 383 static tree_entry *
 384 tree_store_add_entry (const vfs_path_t *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 {
 386     int flag = -1;
 387     tree_entry *current;
 388     tree_entry *old = NULL;
 389     tree_entry *new;
 390     int submask = 0;
 391 
 392     if (ts.tree_last != NULL && ts.tree_last->next != NULL)
 393         abort ();
 394 
 395     /* Search for the correct place */
 396     for (current = ts.tree_first;
 397          current != NULL && (flag = pathcmp (current->name, name)) < 0; current = current->next)
 398         old = current;
 399 
 400     if (flag == 0)
 401         return current;         /* Already in the list */
 402 
 403     /* Not in the list -> add it */
 404     new = g_new0 (tree_entry, 1);
 405     if (current == NULL)
 406     {
 407         /* Append to the end of the list */
 408         if (ts.tree_first == NULL)
 409         {
 410             /* Empty list */
 411             ts.tree_first = new;
 412             new->prev = NULL;
 413         }
 414         else
 415         {
 416             if (old != NULL)
 417                 old->next = new;
 418             new->prev = old;
 419         }
 420         new->next = NULL;
 421         ts.tree_last = new;
 422     }
 423     else
 424     {
 425         /* Insert in to the middle of the list */
 426         new->prev = old;
 427         if (old != NULL)
 428         {
 429             /* Yes, in the middle */
 430             new->next = old->next;
 431             old->next = new;
 432         }
 433         else
 434         {
 435             /* Nope, in the beginning of the list */
 436             new->next = ts.tree_first;
 437             ts.tree_first = new;
 438         }
 439         new->next->prev = new;
 440     }
 441 
 442     /* Calculate attributes */
 443     new->name = vfs_path_clone (name);
 444     new->sublevel = vfs_path_tokens_count (new->name);
 445 
 446     {
 447         const char *new_name;
 448 
 449         new_name = vfs_path_get_last_path_str (new->name);
 450         new->subname = strrchr (new_name, PATH_SEP);
 451         if (new->subname == NULL)
 452             new->subname = new_name;
 453         else
 454             new->subname++;
 455     }
 456 
 457     if (new->next != NULL)
 458         submask = new->next->submask;
 459 
 460     submask |= 1 << new->sublevel;
 461     submask &= (2 << new->sublevel) - 1;
 462     new->submask = submask;
 463     new->mark = FALSE;
 464 
 465     /* Correct the submasks of the previous entries */
 466     for (current = new->prev;
 467          current != NULL && current->sublevel > new->sublevel; current = current->prev)
 468         current->submask |= 1 << new->sublevel;
 469 
 470     tree_store_dirty (TRUE);
 471     return new;
 472 }
 473 
 474 /* --------------------------------------------------------------------------------------------- */
 475 
 476 static void
 477 tree_store_notify_remove (tree_entry *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
 478 {
 479     hook_t *p;
 480 
 481     for (p = remove_entry_hooks; p != NULL; p = p->next)
 482     {
 483         tree_store_remove_fn r = (tree_store_remove_fn) p->hook_fn;
 484 
 485         r (entry, p->hook_data);
 486     }
 487 }
 488 
 489 /* --------------------------------------------------------------------------------------------- */
 490 
 491 static tree_entry *
 492 remove_entry (tree_entry *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
 493 {
 494     tree_entry *current = entry->prev;
 495     long submask = 0;
 496     tree_entry *ret = NULL;
 497 
 498     tree_store_notify_remove (entry);
 499 
 500     /* Correct the submasks of the previous entries */
 501     if (entry->next != NULL)
 502         submask = entry->next->submask;
 503 
 504     for (; current != NULL && current->sublevel > entry->sublevel; current = current->prev)
 505     {
 506         submask |= 1 << current->sublevel;
 507         submask &= (2 << current->sublevel) - 1;
 508         current->submask = submask;
 509     }
 510 
 511     /* Unlink the entry from the list */
 512     if (entry->prev != NULL)
 513         entry->prev->next = entry->next;
 514     else
 515         ts.tree_first = entry->next;
 516 
 517     if (entry->next != NULL)
 518         entry->next->prev = entry->prev;
 519     else
 520         ts.tree_last = entry->prev;
 521 
 522     /* Free the memory used by the entry */
 523     vfs_path_free (entry->name, TRUE);
 524     g_free (entry);
 525 
 526     return ret;
 527 }
 528 
 529 /* --------------------------------------------------------------------------------------------- */
 530 
 531 static void
 532 process_special_dirs (GList **special_dirs, const char *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 533 {
 534     gchar **start_buff;
 535     mc_config_t *cfg;
 536 
 537     cfg = mc_config_init (file, TRUE);
 538     if (cfg == NULL)
 539         return;
 540 
 541     start_buff = mc_config_get_string_list (cfg, "Special dirs", "list", NULL);
 542     if (start_buff != NULL)
 543     {
 544         gchar **buffers;
 545 
 546         for (buffers = start_buff; *buffers != NULL; buffers++)
 547         {
 548             *special_dirs = g_list_prepend (*special_dirs, *buffers);
 549             *buffers = NULL;
 550         }
 551 
 552         g_strfreev (start_buff);
 553     }
 554     mc_config_deinit (cfg);
 555 }
 556 
 557 /* --------------------------------------------------------------------------------------------- */
 558 
 559 static gboolean
 560 should_skip_directory (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 561 {
 562     static GList *special_dirs = NULL;
 563     GList *l;
 564     static gboolean loaded = FALSE;
 565     gboolean ret = FALSE;
 566 
 567     if (!loaded)
 568     {
 569         const char *profile_name;
 570 
 571         profile_name = setup_init ();
 572         process_special_dirs (&special_dirs, profile_name);
 573         process_special_dirs (&special_dirs, mc_global.profile_name);
 574 
 575         loaded = TRUE;
 576     }
 577 
 578     for (l = special_dirs; l != NULL; l = g_list_next (l))
 579         if (strncmp (vfs_path_as_str (vpath), l->data, strlen (l->data)) == 0)
 580         {
 581             ret = TRUE;
 582             break;
 583         }
 584 
 585     return ret;
 586 }
 587 
 588 /* --------------------------------------------------------------------------------------------- */
 589 
 590 static void
 591 queue_vpath_free (gpointer data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 592 {
 593     vfs_path_free ((vfs_path_t *) data, TRUE);
 594 }
 595 
 596 /* --------------------------------------------------------------------------------------------- */
 597 /*** public functions ****************************************************************************/
 598 /* --------------------------------------------------------------------------------------------- */
 599 
 600 /* Searches for specified directory */
 601 tree_entry *
 602 tree_store_whereis (const vfs_path_t *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 603 {
 604     tree_entry *current;
 605     int flag = -1;
 606 
 607     for (current = ts.tree_first;
 608          current != NULL && (flag = pathcmp (current->name, name)) < 0; current = current->next)
 609         ;
 610 
 611     return flag == 0 ? current : NULL;
 612 }
 613 
 614 /* --------------------------------------------------------------------------------------------- */
 615 
 616 struct TreeStore *
 617 tree_store_get (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 618 {
 619     return &ts;
 620 }
 621 
 622 /* --------------------------------------------------------------------------------------------- */
 623 /**
 624  * \fn int tree_store_load(void)
 625  * \brief Loads the tree from the default location
 626  * \return 1 if success (true), 0 otherwise (false)
 627  */
 628 
 629 int
 630 tree_store_load (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 631 {
 632     char *name;
 633     int retval;
 634 
 635     name = mc_config_get_full_path (MC_TREESTORE_FILE);
 636     retval = tree_store_load_from (name);
 637     g_free (name);
 638 
 639     return retval;
 640 }
 641 
 642 /* --------------------------------------------------------------------------------------------- */
 643 /**
 644  * \fn int tree_store_save(void)
 645  * \brief Saves the tree to the default file in an atomic fashion
 646  * \return 0 if success, errno on error
 647  */
 648 
 649 int
 650 tree_store_save (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 651 {
 652     char *name;
 653     int retval;
 654 
 655     name = mc_config_get_full_path (MC_TREESTORE_FILE);
 656     mc_util_make_backup_if_possible (name, ".tmp");
 657 
 658     retval = tree_store_save_to (name);
 659     if (retval != 0)
 660         mc_util_restore_from_backup_if_possible (name, ".tmp");
 661     else
 662         mc_util_unlink_backup_if_possible (name, ".tmp");
 663 
 664     g_free (name);
 665     return retval;
 666 }
 667 
 668 /* --------------------------------------------------------------------------------------------- */
 669 
 670 void
 671 tree_store_add_entry_remove_hook (tree_store_remove_fn callback, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 672 {
 673     add_hook (&remove_entry_hooks, (void (*)(void *)) callback, data);
 674 }
 675 
 676 /* --------------------------------------------------------------------------------------------- */
 677 
 678 void
 679 tree_store_remove_entry_remove_hook (tree_store_remove_fn callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 680 {
 681     delete_hook (&remove_entry_hooks, (void (*)(void *)) callback);
 682 }
 683 
 684 /* --------------------------------------------------------------------------------------------- */
 685 
 686 void
 687 tree_store_remove_entry (const vfs_path_t *name_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 688 {
 689     tree_entry *current, *base;
 690     size_t len;
 691 
 692     g_return_if_fail (name_vpath != NULL);
 693 
 694     /* Miguel Ugly hack */
 695     {
 696         gboolean is_root;
 697         const char *name_vpath_str;
 698 
 699         name_vpath_str = vfs_path_as_str (name_vpath);
 700         is_root = (IS_PATH_SEP (name_vpath_str[0]) && name_vpath_str[1] == '\0');
 701         if (is_root)
 702             return;
 703     }
 704     /* Miguel Ugly hack end */
 705 
 706     base = tree_store_whereis (name_vpath);
 707     if (base == NULL)
 708         return;                 /* Doesn't exist */
 709 
 710     len = vfs_path_len (base->name);
 711     current = base->next;
 712     while (current != NULL && vfs_path_equal_len (current->name, base->name, len))
 713     {
 714         gboolean ok;
 715         tree_entry *old;
 716         const char *cname;
 717 
 718         cname = vfs_path_as_str (current->name);
 719         ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]));
 720         if (!ok)
 721             break;
 722 
 723         old = current;
 724         current = current->next;
 725         remove_entry (old);
 726     }
 727     remove_entry (base);
 728     tree_store_dirty (TRUE);
 729 }
 730 
 731 /* --------------------------------------------------------------------------------------------- */
 732 /** This subdirectory exists -> clear deletion mark */
 733 
 734 void
 735 tree_store_mark_checked (const char *subname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 736 {
 737     vfs_path_t *name;
 738     tree_entry *current, *base;
 739     int flag = 1;
 740     const char *cname;
 741 
 742     if (!ts.loaded)
 743         return;
 744 
 745     if (ts.check_name == NULL)
 746         return;
 747 
 748     /* Calculate the full name of the subdirectory */
 749     if (DIR_IS_DOT (subname) || DIR_IS_DOTDOT (subname))
 750         return;
 751 
 752     cname = vfs_path_as_str (ts.check_name);
 753     if (IS_PATH_SEP (cname[0]) && cname[1] == '\0')
 754         name = vfs_path_build_filename (PATH_SEP_STR, subname, (char *) NULL);
 755     else
 756         name = vfs_path_append_new (ts.check_name, subname, (char *) NULL);
 757 
 758     /* Search for the subdirectory */
 759     for (current = ts.check_start;
 760          current != NULL && (flag = pathcmp (current->name, name)) < 0; current = current->next)
 761         ;
 762 
 763     if (flag != 0)
 764     {
 765         /* Doesn't exist -> add it */
 766         current = tree_store_add_entry (name);
 767         ts.add_queue_vpath = g_list_prepend (ts.add_queue_vpath, name);
 768     }
 769     else
 770         vfs_path_free (name, TRUE);
 771 
 772     /* Clear the deletion mark from the subdirectory and its children */
 773     base = current;
 774     if (base != NULL)
 775     {
 776         size_t len;
 777 
 778         len = vfs_path_len (base->name);
 779         base->mark = FALSE;
 780         for (current = base->next;
 781              current != NULL && vfs_path_equal_len (current->name, base->name, len);
 782              current = current->next)
 783         {
 784             gboolean ok;
 785 
 786             cname = vfs_path_as_str (current->name);
 787             ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
 788             if (!ok)
 789                 break;
 790 
 791             current->mark = FALSE;
 792         }
 793     }
 794 }
 795 
 796 /* --------------------------------------------------------------------------------------------- */
 797 /** Mark the subdirectories of the current directory for delete */
 798 
 799 tree_entry *
 800 tree_store_start_check (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 801 {
 802     tree_entry *current, *retval;
 803     size_t len;
 804 
 805     if (!ts.loaded)
 806         return NULL;
 807 
 808     g_return_val_if_fail (ts.check_name == NULL, NULL);
 809     ts.check_start = NULL;
 810 
 811     /* Search for the start of subdirectories */
 812     current = tree_store_whereis (vpath);
 813     if (current == NULL)
 814     {
 815         struct stat s;
 816 
 817         if (mc_stat (vpath, &s) == -1 || !S_ISDIR (s.st_mode))
 818             return NULL;
 819 
 820         current = tree_store_add_entry (vpath);
 821         ts.check_name = vfs_path_clone (vpath);
 822 
 823         return current;
 824     }
 825 
 826     ts.check_name = vfs_path_clone (vpath);
 827 
 828     retval = current;
 829 
 830     /* Mark old subdirectories for delete */
 831     ts.check_start = current->next;
 832     len = vfs_path_len (ts.check_name);
 833 
 834     for (current = ts.check_start;
 835          current != NULL && vfs_path_equal_len (current->name, ts.check_name, len);
 836          current = current->next)
 837     {
 838         gboolean ok;
 839         const char *cname;
 840 
 841         cname = vfs_path_as_str (current->name);
 842         ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
 843         if (!ok)
 844             break;
 845 
 846         current->mark = TRUE;
 847     }
 848 
 849     return retval;
 850 }
 851 
 852 /* --------------------------------------------------------------------------------------------- */
 853 /** Delete subdirectories which still have the deletion mark */
 854 
 855 void
 856 tree_store_end_check (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 857 {
 858     tree_entry *current;
 859     size_t len;
 860     GList *the_queue;
 861 
 862     if (!ts.loaded)
 863         return;
 864 
 865     g_return_if_fail (ts.check_name != NULL);
 866 
 867     /* Check delete marks and delete if found */
 868     len = vfs_path_len (ts.check_name);
 869 
 870     current = ts.check_start;
 871     while (current != NULL && vfs_path_equal_len (current->name, ts.check_name, len))
 872     {
 873         gboolean ok;
 874         tree_entry *old;
 875         const char *cname;
 876 
 877         cname = vfs_path_as_str (current->name);
 878         ok = (cname[len] == '\0' || IS_PATH_SEP (cname[len]) || len == 1);
 879         if (!ok)
 880             break;
 881 
 882         old = current;
 883         current = current->next;
 884         if (old->mark)
 885             remove_entry (old);
 886     }
 887 
 888     /* get the stuff in the scan order */
 889     the_queue = g_list_reverse (ts.add_queue_vpath);
 890     ts.add_queue_vpath = NULL;
 891     vfs_path_free (ts.check_name, TRUE);
 892     ts.check_name = NULL;
 893 
 894     g_list_free_full (the_queue, queue_vpath_free);
 895 }
 896 
 897 /* --------------------------------------------------------------------------------------------- */
 898 
 899 tree_entry *
 900 tree_store_rescan (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 901 {
 902     DIR *dirp;
 903     struct stat buf;
 904     tree_entry *entry;
 905 
 906     if (should_skip_directory (vpath))
 907     {
 908         entry = tree_store_add_entry (vpath);
 909         entry->scanned = TRUE;
 910         return entry;
 911     }
 912 
 913     entry = tree_store_start_check (vpath);
 914     if (entry == NULL)
 915         return NULL;
 916 
 917     dirp = mc_opendir (vpath);
 918     if (dirp != NULL)
 919     {
 920         struct vfs_dirent *dp;
 921 
 922         for (dp = mc_readdir (dirp); dp != NULL; dp = mc_readdir (dirp))
 923             if (!DIR_IS_DOT (dp->d_name) && !DIR_IS_DOTDOT (dp->d_name))
 924             {
 925                 vfs_path_t *tmp_vpath;
 926 
 927                 tmp_vpath = vfs_path_append_new (vpath, dp->d_name, (char *) NULL);
 928                 if (mc_lstat (tmp_vpath, &buf) != -1 && S_ISDIR (buf.st_mode))
 929                     tree_store_mark_checked (dp->d_name);
 930                 vfs_path_free (tmp_vpath, TRUE);
 931             }
 932 
 933         mc_closedir (dirp);
 934     }
 935     tree_store_end_check ();
 936     entry->scanned = TRUE;
 937 
 938     return entry;
 939 }
 940 
 941 /* --------------------------------------------------------------------------------------------- */

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