mirror of
https://github.com/MidnightCommander/mc
synced 2025-01-08 04:22:05 +03:00
a39568367e
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru> * Converted memory managment to Glib. Now we use g_new()/g_malloc()/ g_strdup()/g_free() routings. Also, copy_strings() replaced by g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by g_snprintf(). * Some sequences of malloc()/sprintf() changed to g_strdup_printf(). * mad.[ch]: Modified, to work with new GLib's memory managment. Fixed a missing #undef for tempnam, which caused dead loop. Add several new functions to emulate GLib memory managment. *main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD messages to the file. * util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp() and strdup() - we have g_ equivalences. Remove get_full_name() - it is similar to concat_dir_and_file(). Some other tricks with g_* functions. * global.h: Modified, extended. Now it is main memory mangment include - i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h", "util.h" and "mad.h" done there. This elimanates problem with proper or- der of #include's. * All around the source - changed order of #include's, most of them gone to global.h (see above), minor changes, like "0" -> NULL in string func- tions.
817 lines
16 KiB
C
817 lines
16 KiB
C
/*
|
|
* Tree Store
|
|
*
|
|
* Contains a storage of the file system tree representation
|
|
*
|
|
Copyright (C) 1994, 1995, 1996, 1997 The Free Software Foundation
|
|
|
|
Written: 1994, 1996 Janne Kukonlehto
|
|
1997 Norbert Warmuth
|
|
1996, 1999 Miguel de Icaza
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
This module has been converted to be a widget.
|
|
|
|
The program load and saves the tree each time the tree widget is
|
|
created and destroyed. This is required for the future vfs layer,
|
|
it will be possible to have tree views over virtual file systems.
|
|
|
|
*/
|
|
#include <config.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "global.h"
|
|
#include "treestore.h"
|
|
#include "../vfs/vfs.h"
|
|
#ifdef OS2_NT
|
|
# include <io.h>
|
|
#endif
|
|
|
|
#define TREE_SIGNATURE "Midnight Commander TreeStore v 2.0"
|
|
|
|
static TreeStore ts;
|
|
|
|
void (*tree_store_dirty_notify)(int state) = NULL;
|
|
|
|
void
|
|
tree_store_dirty (int state)
|
|
{
|
|
ts.dirty = state;
|
|
|
|
if (tree_store_dirty_notify)
|
|
(*tree_store_dirty_notify)(state);
|
|
}
|
|
|
|
/* Returns number of common characters */
|
|
static int str_common (char *s1, char *s2)
|
|
{
|
|
int result = 0;
|
|
|
|
while (*s1++ == *s2++)
|
|
result++;
|
|
return result;
|
|
}
|
|
|
|
/* The directory names are arranged in a single linked list in the same
|
|
order as they are displayed. When the tree is displayed the expected
|
|
order is like this:
|
|
/
|
|
/bin
|
|
/etc
|
|
/etc/X11
|
|
/etc/rc.d
|
|
/etc.old/X11
|
|
/etc.old/rc.d
|
|
/usr
|
|
|
|
i.e. the required collating sequence when comparing two directory names is
|
|
'\0' < PATH_SEP < all-other-characters-in-encoding-order
|
|
|
|
Since strcmp doesn't fulfil this requirement we use pathcmp when
|
|
inserting directory names into the list. The meaning of the return value
|
|
of pathcmp and strcmp are the same (an integer less than, equal to, or
|
|
greater than zero if p1 is found to be less than, to match, or be greater
|
|
than p2.
|
|
*/
|
|
static int
|
|
pathcmp (const char *p1, const char *p2)
|
|
{
|
|
for ( ;*p1 == *p2; p1++, p2++)
|
|
if (*p1 == '\0' )
|
|
return 0;
|
|
|
|
if (*p1 == '\0')
|
|
return -1;
|
|
if (*p2 == '\0')
|
|
return 1;
|
|
if (*p1 == PATH_SEP)
|
|
return -1;
|
|
if (*p2 == PATH_SEP)
|
|
return 1;
|
|
return (*p1 - *p2);
|
|
}
|
|
|
|
/* Searches for specified directory */
|
|
tree_entry *
|
|
tree_store_whereis (char *name)
|
|
{
|
|
tree_entry *current = ts.tree_first;
|
|
int flag = -1;
|
|
|
|
while (current && (flag = pathcmp (current->name, name)) < 0)
|
|
current = current->next;
|
|
|
|
if (flag == 0)
|
|
return current;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
TreeStore *
|
|
tree_store_init (void)
|
|
{
|
|
ts.tree_first = 0;
|
|
ts.tree_last = 0;
|
|
ts.refcount++;
|
|
|
|
return &ts;
|
|
}
|
|
|
|
void
|
|
tree_store_destroy (void)
|
|
{
|
|
tree_entry *current, *old;
|
|
|
|
ts.refcount--;
|
|
|
|
current = ts.tree_first;
|
|
while (current){
|
|
old = current;
|
|
current = current->next;
|
|
g_free (old->name);
|
|
g_free (old);
|
|
}
|
|
|
|
ts.tree_first = NULL;
|
|
ts.tree_last = NULL;
|
|
ts.loaded = FALSE;
|
|
}
|
|
|
|
static char *
|
|
decode (char *buffer)
|
|
{
|
|
char *res = g_strdup (buffer);
|
|
char *p, *q;
|
|
|
|
for (p = q = res; *p; p++, q++){
|
|
if (*p == '\n'){
|
|
*q = 0;
|
|
return res;
|
|
}
|
|
|
|
if (*p != '\\'){
|
|
*q = *p;
|
|
continue;
|
|
}
|
|
|
|
p++;
|
|
|
|
switch (*p){
|
|
case 'n':
|
|
*q = '\n';
|
|
break;
|
|
case '\\':
|
|
*q = '\\';
|
|
break;
|
|
}
|
|
}
|
|
*q = *p;
|
|
|
|
return res;
|
|
}
|
|
|
|
int
|
|
tree_store_load (char *name)
|
|
{
|
|
FILE *file;
|
|
char buffer [MC_MAXPATHLEN + 20], oldname[MC_MAXPATHLEN];
|
|
char *different;
|
|
int len, common;
|
|
int do_load;
|
|
|
|
g_return_val_if_fail (name != NULL, TRUE);
|
|
|
|
if (ts.loaded)
|
|
return TRUE;
|
|
|
|
file = fopen (name, "r");
|
|
|
|
if (file){
|
|
fgets (buffer, sizeof (buffer), file);
|
|
|
|
if (strncmp (buffer, TREE_SIGNATURE, strlen (TREE_SIGNATURE)) != 0){
|
|
fclose (file);
|
|
do_load = FALSE;
|
|
} else
|
|
do_load = TRUE;
|
|
} else
|
|
do_load = FALSE;
|
|
|
|
if (do_load){
|
|
ts.loaded = TRUE;
|
|
|
|
/* File open -> read contents */
|
|
oldname [0] = 0;
|
|
while (fgets (buffer, MC_MAXPATHLEN, file)){
|
|
tree_entry *e;
|
|
int scanned;
|
|
char *name;
|
|
|
|
/* Skip invalid records */
|
|
if ((buffer [0] != '0' && buffer [0] != '1'))
|
|
continue;
|
|
|
|
if (buffer [1] != ':')
|
|
continue;
|
|
|
|
scanned = buffer [0] == '1';
|
|
|
|
name = decode (buffer+2);
|
|
|
|
len = strlen (name);
|
|
#ifdef OS2_NT
|
|
/* .ado: Drives for NT and OS/2 */
|
|
if ((len > 2) &&
|
|
isalpha(name[0]) &&
|
|
(name[1] == ':') &&
|
|
(name[2] == '\\')) {
|
|
tree_store_add_entry (name);
|
|
strcpy (oldname, name);
|
|
} else
|
|
#endif
|
|
/* UNIX Version */
|
|
if (name [0] != PATH_SEP){
|
|
/* Clear-text decompression */
|
|
char *s = strtok (name, " ");
|
|
|
|
if (s){
|
|
common = atoi (s);
|
|
different = strtok (NULL, "");
|
|
if (different){
|
|
strcpy (oldname + common, different);
|
|
e = tree_store_add_entry (oldname);
|
|
e->scanned = scanned;
|
|
}
|
|
}
|
|
} else {
|
|
e = tree_store_add_entry (name);
|
|
e->scanned = scanned;
|
|
strcpy (oldname, name);
|
|
}
|
|
g_free (name);
|
|
}
|
|
fclose (file);
|
|
}
|
|
|
|
/* Nothing loaded, we add some standard directories */
|
|
if (!ts.tree_first){
|
|
tree_store_add_entry (PATH_SEP_STR);
|
|
tree_store_rescan (PATH_SEP_STR);
|
|
ts.loaded = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static char *
|
|
encode (char *string)
|
|
{
|
|
int special_chars;
|
|
char *p, *q;
|
|
char *res;
|
|
|
|
for (special_chars = 0, p = string; *p; p++){
|
|
if (*p == '\n' || *p == '\\')
|
|
special_chars++;
|
|
}
|
|
|
|
res = g_malloc (p - string + special_chars + 1);
|
|
for (p = string, q = res; *p; p++, q++){
|
|
if (*p != '\n' && *p != '\\'){
|
|
*q = *p;
|
|
continue;
|
|
}
|
|
|
|
*q++ = '\\';
|
|
|
|
switch (*p){
|
|
case '\n':
|
|
*q = 'n';
|
|
break;
|
|
|
|
case '\\':
|
|
*q = '\\';
|
|
break;
|
|
}
|
|
}
|
|
*q = 0;
|
|
return res;
|
|
}
|
|
|
|
int
|
|
tree_store_save (char *name)
|
|
{
|
|
tree_entry *current;
|
|
FILE *file;
|
|
|
|
file = fopen (name, "w");
|
|
if (!file)
|
|
return errno;
|
|
|
|
fprintf (file, "%s\n", TREE_SIGNATURE);
|
|
|
|
current = ts.tree_first;
|
|
while (current){
|
|
int i, common;
|
|
|
|
/* Clear-text compression */
|
|
if (current->prev && (common = str_common (current->prev->name, current->name)) > 2){
|
|
char *encoded = encode (current->name + common);
|
|
|
|
i = fprintf (file, "%d:%d %s\n", current->scanned, common, encoded);
|
|
g_free (encoded);
|
|
} else {
|
|
char *encoded = encode (current->name);
|
|
|
|
i = fprintf (file, "%d:%s\n", current->scanned, encoded);
|
|
g_free (encoded);
|
|
}
|
|
|
|
if (i == EOF){
|
|
fprintf (stderr, _("Can't write to the %s file:\n%s\n"), name,
|
|
unix_error_string (errno));
|
|
break;
|
|
}
|
|
current = current->next;
|
|
}
|
|
tree_store_dirty (FALSE);
|
|
fclose (file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
tree_entry *
|
|
tree_store_add_entry (char *name)
|
|
{
|
|
int flag = -1;
|
|
tree_entry *current = ts.tree_first;
|
|
tree_entry *old = NULL;
|
|
tree_entry *new;
|
|
int i, len;
|
|
int submask = 0;
|
|
|
|
if (ts.tree_last && ts.tree_last->next)
|
|
abort ();
|
|
|
|
/* Search for the correct place */
|
|
while (current && (flag = pathcmp (current->name, name)) < 0){
|
|
old = current;
|
|
current = current->next;
|
|
}
|
|
|
|
if (flag == 0)
|
|
return current; /* Already in the list */
|
|
|
|
/* Not in the list -> add it */
|
|
new = g_new (tree_entry, 1);
|
|
if (!current){
|
|
/* Append to the end of the list */
|
|
if (!ts.tree_first){
|
|
/* Empty list */
|
|
ts.tree_first = new;
|
|
new->prev = NULL;
|
|
} else {
|
|
old->next = new;
|
|
new->prev = old;
|
|
}
|
|
new->next = NULL;
|
|
ts.tree_last = new;
|
|
} else {
|
|
/* Insert in to the middle of the list */
|
|
new->prev = old;
|
|
if (old){
|
|
/* Yes, in the middle */
|
|
new->next = old->next;
|
|
old->next = new;
|
|
} else {
|
|
/* Nope, in the beginning of the list */
|
|
new->next = ts.tree_first;
|
|
ts.tree_first = new;
|
|
}
|
|
new->next->prev = new;
|
|
}
|
|
|
|
/* Calculate attributes */
|
|
new->name = g_strdup (name);
|
|
len = strlen (new->name);
|
|
new->sublevel = 0;
|
|
for (i = 0; i < len; i++)
|
|
if (new->name [i] == PATH_SEP){
|
|
new->sublevel++;
|
|
new->subname = new->name + i + 1;
|
|
}
|
|
if (new->next)
|
|
submask = new->next->submask;
|
|
else
|
|
submask = 0;
|
|
submask |= 1 << new->sublevel;
|
|
submask &= (2 << new->sublevel) - 1;
|
|
new->submask = submask;
|
|
new->mark = 0;
|
|
|
|
/* Correct the submasks of the previous entries */
|
|
current = new->prev;
|
|
while (current && current->sublevel > new->sublevel){
|
|
current->submask |= 1 << new->sublevel;
|
|
current = current->prev;
|
|
}
|
|
|
|
/* The entry has now been added */
|
|
|
|
if (new->sublevel > 1){
|
|
/* Let's check if the parent directory is in the tree */
|
|
char *parent = g_strdup (new->name);
|
|
int i;
|
|
|
|
for (i = strlen (parent) - 1; i > 1; i--){
|
|
if (parent [i] == PATH_SEP){
|
|
parent [i] = 0;
|
|
tree_store_add_entry (parent);
|
|
break;
|
|
}
|
|
}
|
|
g_free (parent);
|
|
}
|
|
|
|
tree_store_dirty (TRUE);
|
|
return new;
|
|
}
|
|
|
|
tree_entry *
|
|
remove_entry (tree_entry *entry)
|
|
{
|
|
tree_entry *current = entry->prev;
|
|
long submask = 0;
|
|
tree_entry *ret = NULL;
|
|
|
|
tree_store_notify_remove (entry);
|
|
|
|
/* Correct the submasks of the previous entries */
|
|
if (entry->next)
|
|
submask = entry->next->submask;
|
|
while (current && current->sublevel > entry->sublevel){
|
|
submask |= 1 << current->sublevel;
|
|
submask &= (2 << current->sublevel) - 1;
|
|
current->submask = submask;
|
|
current = current->prev;
|
|
}
|
|
|
|
/* Unlink the entry from the list */
|
|
if (entry->prev)
|
|
entry->prev->next = entry->next;
|
|
else
|
|
ts.tree_first = entry->next;
|
|
if (entry->next)
|
|
entry->next->prev = entry->prev;
|
|
else
|
|
ts.tree_last = entry->prev;
|
|
|
|
/* Free the memory used by the entry */
|
|
g_free (entry->name);
|
|
g_free (entry);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
tree_store_remove_entry (char *name)
|
|
{
|
|
tree_entry *current, *base, *old;
|
|
int len, base_sublevel;
|
|
|
|
g_return_if_fail (name != NULL);
|
|
|
|
/* Miguel Ugly hack */
|
|
if (name [0] == PATH_SEP && name [1] == 0)
|
|
return;
|
|
/* Miguel Ugly hack end */
|
|
|
|
base = tree_store_whereis (name);
|
|
if (!base)
|
|
return; /* Doesn't exist */
|
|
if (ts.check_name [0] == PATH_SEP && ts.check_name [1] == 0)
|
|
base_sublevel = base->sublevel;
|
|
else
|
|
base_sublevel = base->sublevel + 1;
|
|
len = strlen (base->name);
|
|
current = base->next;
|
|
while (current
|
|
&& strncmp (current->name, base->name, len) == 0
|
|
&& (current->name[len] == '\0' || current->name[len] == PATH_SEP)){
|
|
old = current;
|
|
current = current->next;
|
|
remove_entry (old);
|
|
}
|
|
remove_entry (base);
|
|
tree_store_dirty (TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
/* This subdirectory exists -> clear deletion mark */
|
|
void
|
|
tree_store_mark_checked (const char *subname)
|
|
{
|
|
char *name;
|
|
tree_entry *current, *base;
|
|
int flag = 1, len;
|
|
|
|
if (!ts.loaded)
|
|
return;
|
|
g_return_if_fail (ts.check_name != NULL);
|
|
|
|
/* Calculate the full name of the subdirectory */
|
|
if (subname [0] == '.' &&
|
|
(subname [1] == 0 || (subname [1] == '.' && subname [2] == 0)))
|
|
return;
|
|
if (ts.check_name [0] == PATH_SEP && ts.check_name [1] == 0)
|
|
name = g_strconcat (PATH_SEP_STR, subname, NULL);
|
|
else
|
|
name = concat_dir_and_file (ts.check_name, subname);
|
|
|
|
/* Search for the subdirectory */
|
|
current = ts.check_start;
|
|
while (current && (flag = pathcmp (current->name, name)) < 0)
|
|
current = current->next;
|
|
|
|
if (flag != 0){
|
|
/* Doesn't exist -> add it */
|
|
current = tree_store_add_entry (name);
|
|
tree_store_notify_add (current);
|
|
}
|
|
g_free (name);
|
|
|
|
/* Clear the deletion mark from the subdirectory and its children */
|
|
base = current;
|
|
if (base){
|
|
len = strlen (base->name);
|
|
base->mark = 0;
|
|
current = base->next;
|
|
while (current
|
|
&& strncmp (current->name, base->name, len) == 0
|
|
&& (current->name[len] == '\0' || current->name[len] == PATH_SEP || len == 1)){
|
|
current->mark = 0;
|
|
current = current->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Mark the subdirectories of the current directory for delete */
|
|
tree_entry *
|
|
tree_store_start_check (char *path)
|
|
{
|
|
tree_entry *current, *retval;
|
|
int len;
|
|
|
|
if (!ts.loaded)
|
|
return NULL;
|
|
|
|
ts.check_start = NULL;
|
|
|
|
/* Search for the start of subdirectories */
|
|
current = tree_store_whereis (path);
|
|
if (!current){
|
|
struct stat s;
|
|
|
|
if (mc_stat (path, &s) == -1)
|
|
return NULL;
|
|
|
|
if (!S_ISDIR (s.st_mode))
|
|
return NULL;
|
|
|
|
current = tree_store_add_entry (path);
|
|
ts.check_name = g_strdup (path);
|
|
|
|
return current;
|
|
}
|
|
|
|
ts.check_name = g_strdup (path);
|
|
|
|
retval = current;
|
|
|
|
/* Mark old subdirectories for delete */
|
|
ts.check_start = current->next;
|
|
len = strlen (ts.check_name);
|
|
|
|
current = ts.check_start;
|
|
while (current
|
|
&& strncmp (current->name, ts.check_name, len) == 0
|
|
&& (current->name[len] == '\0' || current->name[len] == PATH_SEP || len == 1)){
|
|
current->mark = 1;
|
|
current = current->next;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
tree_entry *
|
|
tree_store_start_check_cwd (void)
|
|
{
|
|
char buffer [MC_MAXPATHLEN];
|
|
|
|
mc_get_current_wd (buffer, MC_MAXPATHLEN);
|
|
return tree_store_start_check (buffer);
|
|
}
|
|
|
|
/* Delete subdirectories which still have the deletion mark */
|
|
void
|
|
tree_store_end_check (void)
|
|
{
|
|
tree_entry *current, *old;
|
|
int len;
|
|
|
|
if (!ts.loaded)
|
|
return;
|
|
|
|
g_return_if_fail (ts.check_name);
|
|
|
|
/* Check delete marks and delete if found */
|
|
len = strlen (ts.check_name);
|
|
|
|
current = ts.check_start;
|
|
while (current
|
|
&& strncmp (current->name, ts.check_name, len) == 0
|
|
&& (current->name[len] == '\0' || current->name[len] == PATH_SEP || len == 1)){
|
|
old = current;
|
|
current = current->next;
|
|
if (old->mark)
|
|
remove_entry (old);
|
|
}
|
|
|
|
g_free (ts.check_name);
|
|
ts.check_name = NULL;
|
|
}
|
|
|
|
tree_entry *
|
|
tree_store_rescan (char *dir)
|
|
{
|
|
DIR *dirp;
|
|
struct dirent *dp;
|
|
struct stat buf;
|
|
tree_entry *entry;
|
|
|
|
entry = tree_store_start_check (dir);
|
|
|
|
if (!entry)
|
|
return NULL;
|
|
|
|
dirp = mc_opendir (dir);
|
|
if (dirp){
|
|
for (dp = mc_readdir (dirp); dp; dp = mc_readdir (dirp)){
|
|
char *full_name;
|
|
|
|
if ((dp->d_name [0] == '.' && dp->d_name [1] == 0)
|
|
|| (dp->d_name [1] == '.' && dp->d_name [2] == 0))
|
|
continue;
|
|
|
|
full_name = concat_dir_and_file (dir, dp->d_name);
|
|
if (mc_lstat (full_name, &buf) != -1){
|
|
if (S_ISDIR (buf.st_mode))
|
|
tree_store_mark_checked (dp->d_name);
|
|
}
|
|
g_free (full_name);
|
|
}
|
|
mc_closedir (dirp);
|
|
}
|
|
tree_store_end_check ();
|
|
entry->scanned = 1;
|
|
|
|
return entry;
|
|
}
|
|
|
|
static Hook *remove_entry_hooks;
|
|
static Hook *add_entry_hooks;
|
|
|
|
void
|
|
tree_store_add_entry_remove_hook (tree_store_remove_fn callback, void *data)
|
|
{
|
|
add_hook (&remove_entry_hooks, (void (*)(void *))callback, data);
|
|
}
|
|
|
|
void
|
|
tree_store_remove_entry_remove_hook (tree_store_remove_fn callback)
|
|
{
|
|
delete_hook (&remove_entry_hooks, (void (*)(void *))callback);
|
|
}
|
|
|
|
void
|
|
tree_store_notify_remove (tree_entry *entry)
|
|
{
|
|
Hook *p = remove_entry_hooks;
|
|
tree_store_remove_fn r;
|
|
|
|
|
|
while (p){
|
|
r = (tree_store_remove_fn) p->hook_fn;
|
|
r (entry, p->hook_data);
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
void
|
|
tree_store_add_entry_add_hook (tree_store_add_fn callback, void *data)
|
|
{
|
|
add_hook (&add_entry_hooks, (void (*)(void *))callback, data);
|
|
}
|
|
|
|
void
|
|
tree_store_remove_entry_add_hook (tree_store_add_fn callback)
|
|
{
|
|
delete_hook (&add_entry_hooks, (void (*)(void *))callback);
|
|
}
|
|
|
|
void
|
|
tree_store_notify_add (tree_entry *entry)
|
|
{
|
|
Hook *p = add_entry_hooks;
|
|
tree_store_add_fn r;
|
|
|
|
|
|
while (p){
|
|
r = (tree_store_add_fn) p->hook_fn;
|
|
r (entry, p->hook_data);
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
tree_scan *
|
|
tree_store_opendir (char *path)
|
|
{
|
|
tree_entry *entry;
|
|
tree_scan *scan;
|
|
|
|
entry = tree_store_whereis (path);
|
|
if (!entry || (entry && !entry->scanned)){
|
|
entry = tree_store_rescan (path);
|
|
|
|
if (!entry)
|
|
return NULL;
|
|
}
|
|
|
|
if (entry->next == NULL)
|
|
return NULL;
|
|
|
|
scan = g_new (tree_scan, 1);
|
|
scan->base = entry;
|
|
scan->current = entry->next;
|
|
scan->sublevel = entry->next->sublevel;
|
|
|
|
scan->base_dir_len = strlen (path);
|
|
return scan;
|
|
}
|
|
|
|
tree_entry *
|
|
tree_store_readdir (tree_scan *scan)
|
|
{
|
|
tree_entry *entry;
|
|
int len;
|
|
|
|
g_assert (scan != NULL);
|
|
|
|
len = scan->base_dir_len;
|
|
entry = scan->current;
|
|
while (entry &&
|
|
(strncmp (entry->name, scan->base->name, len) == 0) &&
|
|
(entry->name [len] == 0 || entry->name [len] == PATH_SEP || len == 1)){
|
|
|
|
if (entry->sublevel == scan->sublevel){
|
|
scan->current = entry->next;
|
|
return entry;
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
tree_store_closedir (tree_scan *scanner)
|
|
{
|
|
g_assert (scanner != NULL);
|
|
|
|
g_free (scanner);
|
|
}
|