You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnome-vfs2/vfolder-desktop-method.c

6131 lines
140 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
/* vfolder-desktop-method.c
Copyright (C) 2001 Red Hat, Inc.
Copyright (C) 2001 The Dark Prince
The Gnome Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/* URI scheme for reading the "applications:" vfolder and other
* vfolder schemes. Lots of code stolen from the original desktop
* reading URI scheme.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* Debugging foo: */
/*#define D(x) x */
#define D(x) ;
#include <glib.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-module.h>
#include <libgnomevfs/gnome-vfs-method.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-module-shared.h>
#include <libgnomevfs/gnome-vfs-monitor-private.h>
#define DOT_GNOME ".gnome2"
typedef struct _VFolderInfo VFolderInfo;
typedef struct _Query Query;
typedef struct _QueryKeyword QueryKeyword;
typedef struct _QueryFilename QueryFilename;
typedef struct _Entry Entry;
typedef struct _Folder Folder;
typedef struct _EntryFile EntryFile;
typedef struct _Keyword Keyword;
typedef struct _FileMonitorHandle FileMonitorHandle;
typedef struct _StatLoc StatLoc;
typedef struct _VFolderURI VFolderURI;
/* TODO before 2.0: */
/* FIXME: also check/monitor desktop_dirs like we do the vfolder
* file and the item dirs */
/* FIXME: check if thread locks are not completely on crack which
* is likely given my experience with threads */
/* FIXME: use filename locking, currently we are full of races if
* multiple processes write to this filesystem */
/* FIXME: implement monitors */
/* TODO for later (star trek future): */
/* FIXME: Maybe when chaining to file:, we should call the gnome-vfs wrapper
* functions, instead of the file: methods directly. */
/* FIXME: related to the above: we should support things being on non
* file: filesystems. Such as having the vfolder info file on http
* somewhere or some such nonsense :) */
static GnomeVFSMethod *parent_method = NULL;
static GHashTable *infos = NULL;
/* Note: I have no clue about how to write thread safe code and this
* is my first attempt, so it's probably wrong
* -George */
G_LOCK_DEFINE_STATIC (vfolder_lock);
/* Note: all keywords are quarks */
/* Note: basenames are unique */
#define UNSUPPORTED_INFO_FIELDS (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | \
GNOME_VFS_FILE_INFO_FIELDS_DEVICE | \
GNOME_VFS_FILE_INFO_FIELDS_INODE | \
GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT | \
GNOME_VFS_FILE_INFO_FIELDS_ATIME)
enum {
QUERY_OR,
QUERY_AND,
QUERY_KEYWORD,
QUERY_FILENAME
};
struct _Query {
int type;
gboolean not;
GSList *queries;
};
struct _QueryKeyword {
int type;
gboolean not;
GQuark keyword;
};
struct _QueryFilename {
int type;
gboolean not;
char *filename;
};
enum {
ENTRY_FILE,
ENTRY_FOLDER
};
struct _Entry {
int type;
int refcount;
int alloc; /* not really useful for folders,
but oh well, whatever. It's the number
of times this is queried in some directory,
used for the Unallocated query type */
char *name;
GSList *monitors;
};
struct _EntryFile {
Entry entry;
char *filename;
gboolean per_user;
GSList *keywords;
gboolean implicit_keywords; /* the keywords were added by us */
};
struct _Folder {
Entry entry;
Folder *parent;
char *desktop_file; /* the .directory file */
Query *query;
/* The following is for per file
* access */
/* excluded by filename */
GHashTable *excludes;
/* included by filename */
GSList *includes;
GHashTable *includes_ht;
GSList *subfolders;
/* Some flags */
gboolean read_only;
gboolean dont_show_if_empty;
gboolean only_unallocated; /* include only unallocated items */
/* lazily done, will run query only when it
* needs to */
gboolean up_to_date;
gboolean sorted;
GSList *entries;
};
struct _StatLoc {
time_t ctime;
time_t last_stat;
gboolean trigger_next; /* if true, next check will fail */
char name[1]; /* the structure will be long enough to
fit name */
};
struct _VFolderInfo {
char *scheme;
char *filename;
char *user_filename;
time_t user_filename_last_write;
char *desktop_dir; /* directory with .directorys */
char *user_desktop_dir; /* directory with .directorys */
gboolean user_file_active; /* if using user_filename and
not filename */
GSList *item_dirs;
char *user_item_dir; /* dir where user changes to
items are stored */
/* old style dirs to merge in */
GSList *merge_dirs;
/* if entries are valid, else
* they need to be (re)read */
gboolean entries_valid;
GSList *entries;
/* entry hash by basename */
GHashTable *entries_ht;
/* The root folder */
Folder *root;
/* The unallocated folders, the folder which only
* include unallocated items */
GSList *unallocated_folders;
/* some flags */
gboolean read_only;
gboolean dirty;
int inhibit_write;
/* change monitoring stuff */
GnomeVFSMonitorHandle *filename_monitor;
GnomeVFSMonitorHandle *user_filename_monitor;
/* stat locations (in case we aren't monitoring) */
StatLoc *filename_statloc;
StatLoc *user_filename_statloc;
/* for .directory dirs */
/* FIXME: */GnomeVFSMonitorHandle *desktop_dir_monitor;
/* FIXME: */GnomeVFSMonitorHandle *user_desktop_dir_monitor;
/* stat locations (in case we aren't monitoring) */
/* FIXME: */StatLoc *desktop_dir_statloc;
/* FIXME: */StatLoc *user_desktop_dir_statloc;
/* FIXME: */GSList *file_monitors; /* FileMonitorHandle */
/* FIXME: */GSList *free_file_monitors; /* FileMonitorHandle */
GSList *folder_monitors; /* FileMonitorHandle */
GSList *free_folder_monitors; /* FileMonitorHandle */
GSList *item_dir_monitors; /* GnomeVFSMonitorHandle */
/* item dirs to stat */
GSList *stat_dirs;
/* ctime for folders */
time_t modification_time;
guint reread_queue;
};
struct _FileMonitorHandle {
int refcount;
gboolean exists;
gboolean dir_monitor; /* TRUE if dir, FALSE if file */
GnomeVFSURI *uri;
GnomeVFSMonitorHandle *handle; /* A real handle if we're monitoring
an actual file here, or NULL */
char *filename; /* Just the basename, used in the free_file_list */
gboolean is_directory_file;
};
struct _VFolderURI {
const gchar *scheme;
gboolean is_all_scheme;
gboolean ends_in_slash;
gchar *path;
gchar *file;
GnomeVFSURI *uri;
};
static Entry * entry_ref (Entry *entry);
static Entry * entry_ref_alloc (Entry *entry);
static void entry_unref (Entry *entry);
static void entry_unref_dealloc (Entry *entry);
static void query_destroy (Query *query);
static void ensure_folder (VFolderInfo *info,
Folder *folder,
gboolean subfolders,
Folder *except,
gboolean ignore_unallocated);
static void ensure_folder_unlocked (VFolderInfo *info,
Folder *folder,
gboolean subfolders,
Folder *except,
gboolean ignore_unallocated);
/* An EVIL function for quick reading of .desktop files,
* only reads in one or two keys, but that's ALL we need */
static void readitem_entry (const char *filename,
const char *key1,
char **result1,
const char *key2,
char **result2);
static gboolean vfolder_info_reload (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context,
gboolean force_read_items);
static gboolean vfolder_info_reload_unlocked (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context,
gboolean force_read_items);
static void invalidate_folder_subfolders (Folder *folder,
gboolean lock_taken);
static Folder * resolve_folder (VFolderInfo *info,
const char *path,
gboolean ignore_basename,
GnomeVFSResult *result,
GnomeVFSContext *context);
static gboolean vfolder_info_read_items (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context);
/* assumes vuri->path already set */
static gboolean
vfolder_uri_parse_internal (GnomeVFSURI *uri, VFolderURI *vuri)
{
vuri->scheme = (gchar *) gnome_vfs_uri_get_scheme (uri);
vuri->ends_in_slash = FALSE;
if (strncmp (vuri->scheme, "all-", strlen ("all-")) == 0) {
vuri->scheme += strlen ("all-");
vuri->is_all_scheme = TRUE;
} else
vuri->is_all_scheme = FALSE;
if (vuri->path != NULL) {
int last_slash = strlen (vuri->path) - 1;
char *first;
/* Note: This handling of paths is somewhat evil, may need a
* bit of a rework */
/* kill leading slashes, that is make sure there is
* only one */
for (first = vuri->path; *first == '/'; first++)
;
if (first != vuri->path) {
first--;
vuri->path = first;
}
/* kill trailing slashes (leave first if all slashes) */
while (last_slash > 0 && vuri->path [last_slash] == '/') {
vuri->path [last_slash--] = '\0';
vuri->ends_in_slash = TRUE;
}
/* get basename start */
while (last_slash >= 0 && vuri->path [last_slash] != '/')
last_slash--;
if (last_slash > -1)
vuri->file = vuri->path + last_slash + 1;
else
vuri->file = vuri->path;
if (vuri->file[0] == '\0' &&
strcmp (vuri->path, "/") == 0) {
vuri->file = NULL;
}
} else {
vuri->ends_in_slash = TRUE;
vuri->path = "/";
vuri->file = NULL;
}
vuri->uri = uri;
return TRUE;
}
#define VFOLDER_URI_PARSE(_uri, _vuri) { \
gchar *path; \
path = gnome_vfs_unescape_string ((_uri)->text, G_DIR_SEPARATOR_S); \
if (path != NULL) { \
(_vuri)->path = g_alloca (strlen (path) + 1); \
strcpy ((_vuri)->path, path); \
g_free (path); \
} else { \
(_vuri)->path = NULL; \
} \
vfolder_uri_parse_internal ((_uri), (_vuri)); \
}
static FileMonitorHandle *
file_monitor_handle_ref_unlocked (FileMonitorHandle *h)
{
h->refcount ++;
return h;
}
static void
file_monitor_handle_unref_unlocked (FileMonitorHandle *h)
{
h->refcount --;
if (h->refcount == 0) {
gnome_vfs_uri_unref (h->uri);
h->uri = NULL;
g_free (h->filename);
h->filename = NULL;
if (h->handle != NULL) {
gnome_vfs_monitor_cancel (h->handle);
h->handle = NULL;
}
}
}
/* This includes the .directory files */
static void
emit_monitor (Folder *folder, int type)
{
GSList *li;
for (li = ((Entry *)folder)->monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *handle = li->data;
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
handle->uri, type);
}
}
static void
emit_file_deleted_monitor (VFolderInfo *info, Entry *entry, Folder *folder)
{
GSList *li;
for (li = entry->monitors;
li != NULL;
li = li->next) {
VFolderURI vuri;
Folder *f;
GnomeVFSResult result;
FileMonitorHandle *handle = li->data;
/* Evil! EVIL URI PARSING. this will eat a lot of
* stack if we have lots of monitors */
VFOLDER_URI_PARSE (handle->uri, &vuri);
f = resolve_folder (info,
vuri.path,
TRUE /* ignore_basename */,
&result,
NULL);
if (f == folder)
gnome_vfs_monitor_callback
((GnomeVFSMethodHandle *) handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_DELETED);
}
}
static void
emit_and_delete_monitor (VFolderInfo *info, Folder *folder)
{
GSList *li;
for (li = ((Entry *)folder)->monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *handle = li->data;
li->data = NULL;
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_DELETED);
if (handle->dir_monitor)
info->free_folder_monitors =
g_slist_prepend (info->free_folder_monitors,
handle);
else
info->free_file_monitors =
g_slist_prepend (info->free_file_monitors,
handle);
}
g_slist_free (((Entry *)folder)->monitors);
((Entry *)folder)->monitors = NULL;
}
static gboolean
check_ext (const char *name, const char *ext_check)
{
const char *ext;
ext = strrchr (name, '.');
if (ext == NULL ||
strcmp (ext, ext_check) != 0)
return FALSE;
else
return TRUE;
}
static StatLoc *
bake_statloc (const char *name,
time_t curtime)
{
struct stat s;
StatLoc *sl = NULL;
if (stat (name, &s) != 0) {
if (errno == ENOENT) {
sl = g_malloc0 (sizeof (StatLoc) +
strlen (name) + 1);
sl->last_stat = curtime;
sl->ctime = 0;
sl->trigger_next = FALSE;
strcpy (sl->name, name);
}
return sl;
}
sl = g_malloc0 (sizeof (StatLoc) +
strlen (name) + 1);
sl->last_stat = curtime;
sl->ctime = s.st_ctime;
sl->trigger_next = FALSE;
strcpy (sl->name, name);
return sl;
}
/* returns FALSE if we must reread */
static gboolean
check_statloc (StatLoc *sl,
time_t curtime)
{
struct stat s;
if (sl->trigger_next) {
sl->trigger_next = FALSE;
return FALSE;
}
/* don't stat more then once every 3 seconds */
if (curtime <= sl->last_stat + 3)
return TRUE;
sl->last_stat = curtime;
if (stat (sl->name, &s) != 0) {
if (errno == ENOENT &&
sl->ctime == 0)
return TRUE;
else
return FALSE;
}
if (sl->ctime == s.st_ctime)
return TRUE;
else
return FALSE;
}
static gboolean
ensure_dir (const char *dirname, gboolean ignore_basename)
{
char *parsed, *p;
if (dirname == NULL)
return FALSE;
if (ignore_basename)
parsed = g_path_get_dirname (dirname);
else
parsed = g_strdup (dirname);
if (g_file_test (parsed, G_FILE_TEST_IS_DIR)) {
g_free (parsed);
return TRUE;
}
p = strchr (parsed, '/');
if (p == parsed)
p = strchr (p+1, '/');
while (p != NULL) {
*p = '\0';
if (mkdir (parsed, 0700) != 0 &&
errno != EEXIST) {
g_free (parsed);
return FALSE;
}
*p = '/';
p = strchr (p+1, '/');
}
if (mkdir (parsed, 0700) != 0 &&
errno != EEXIST) {
g_free (parsed);
return FALSE;
}
g_free (parsed);
return TRUE;
}
/* check for any directory name other then root */
static gboolean
any_subdir (const char *dirname)
{
const char *p;
if (dirname == NULL)
return FALSE;
for (p = dirname; *p != '\0'; p++) {
if (*p != '/') {
return TRUE;
}
}
return FALSE;
}
static void
destroy_entry_file (EntryFile *efile)
{
if (efile == NULL)
return;
g_free (efile->filename);
efile->filename = NULL;
g_slist_free (efile->keywords);
efile->keywords = NULL;
g_free (efile);
}
static void
destroy_folder (Folder *folder)
{
GSList *list;
if (folder == NULL)
return;
if (folder->parent != NULL) {
folder->parent->subfolders =
g_slist_remove (folder->parent->subfolders, folder);
folder->parent->up_to_date = FALSE;
folder->parent = NULL;
}
g_free (folder->desktop_file);
folder->desktop_file = NULL;
query_destroy (folder->query);
folder->query = NULL;
if (folder->excludes != NULL) {
g_hash_table_destroy (folder->excludes);
folder->excludes = NULL;
}
g_slist_foreach (folder->includes, (GFunc)g_free, NULL);
g_slist_free (folder->includes);
folder->includes = NULL;
if (folder->includes_ht != NULL) {
g_hash_table_destroy (folder->includes_ht);
folder->includes_ht = NULL;
}
list = folder->subfolders;
folder->subfolders = NULL;
g_slist_foreach (list, (GFunc)entry_unref, NULL);
g_slist_free (list);
list = folder->entries;
folder->entries = NULL;
g_slist_foreach (list, (GFunc)entry_unref, NULL);
g_slist_free (list);
g_free (folder);
}
static Entry *
entry_ref (Entry *entry)
{
if (entry != NULL)
entry->refcount++;
return entry;
}
static Entry *
entry_ref_alloc (Entry *entry)
{
entry_ref (entry);
if (entry != NULL)
entry->alloc++;
return entry;
}
static void
entry_unref (Entry *entry)
{
if (entry == NULL)
return;
entry->refcount--;
if (entry->refcount == 0) {
g_free (entry->name);
entry->name = NULL;
g_slist_foreach (entry->monitors,
(GFunc)file_monitor_handle_unref_unlocked,
NULL);
g_slist_free (entry->monitors);
entry->monitors = NULL;
if (entry->type == ENTRY_FILE)
destroy_entry_file ((EntryFile *)entry);
else /* ENTRY_FOLDER */
destroy_folder ((Folder *)entry);
}
}
static void
entry_unref_dealloc (Entry *entry)
{
if (entry != NULL) {
entry->alloc --;
entry_unref (entry);
}
}
/* Handles ONLY files, not dirs */
/* Also allocates the entries as well as refs them */
static GSList *
alloc_entries_from_files (VFolderInfo *info, GSList *filenames)
{
GSList *li;
GSList *files;
files = NULL;
for (li = filenames; li != NULL; li = li->next) {
char *filename = li->data;
GSList *entry_list = g_hash_table_lookup (info->entries_ht, filename);
if (entry_list != NULL)
files = g_slist_prepend (files,
entry_ref_alloc (entry_list->data));
}
return files;
}
static gboolean
matches_query (VFolderInfo *info,
Folder *folder,
EntryFile *efile,
Query *query)
{
GSList *li;
if (query == NULL)
return TRUE;
#define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
switch (query->type) {
case QUERY_OR:
for (li = query->queries; li != NULL; li = li->next) {
Query *subquery = li->data;
if (matches_query (info, folder, efile, subquery))
return INVERT_IF_NEEDED (TRUE);
}
return INVERT_IF_NEEDED (FALSE);
case QUERY_AND:
for (li = query->queries; li != NULL; li = li->next) {
Query *subquery = li->data;
if ( ! matches_query (info, folder, efile, subquery))
return INVERT_IF_NEEDED (FALSE);
}
return INVERT_IF_NEEDED (TRUE);
case QUERY_KEYWORD:
{
QueryKeyword *qkeyword = (QueryKeyword *)query;
for (li = efile->keywords; li != NULL; li = li->next) {
GQuark keyword = GPOINTER_TO_INT (li->data);
if (keyword == qkeyword->keyword)
return INVERT_IF_NEEDED (TRUE);
}
return INVERT_IF_NEEDED (FALSE);
}
case QUERY_FILENAME:
{
QueryFilename *qfilename = (QueryFilename *)query;
if (strcmp (qfilename->filename, ((Entry *)efile)->name) == 0) {
return INVERT_IF_NEEDED (TRUE);
} else {
return INVERT_IF_NEEDED (FALSE);
}
}
}
#undef INVERT_IF_NEEDED
g_assert_not_reached ();
/* huh? */
return FALSE;
}
static void
dump_unallocated_folders (Folder *folder)
{
GSList *li;
for (li = folder->subfolders; li != NULL; li = li->next)
dump_unallocated_folders (li->data);
if (folder->only_unallocated &&
folder->entries != NULL) {
g_slist_foreach (folder->entries,
(GFunc)entry_unref_dealloc, NULL);
g_slist_free (folder->entries);
folder->entries = NULL;
}
}
/* Run query, allocs and refs the entries */
static void
append_query (VFolderInfo *info, Folder *folder)
{
GSList *li;
if (folder->query == NULL &&
! folder->only_unallocated)
return;
if (folder->only_unallocated) {
/* dump all folders that use unallocated
* items only. This sucks if you keep
* reading one and then another such
* folder, but oh well, life sucks for
* you then, but at least you get
* consistent results */
dump_unallocated_folders (info->root);
/* ensure all other folders, so that
* after this we know which ones are
* unallocated */
ensure_folder_unlocked (info,
info->root,
TRUE /* subfolders */,
folder /* except */,
/* avoid infinite loops */
TRUE /* ignore_unallocated */);
}
for (li = info->entries; li != NULL; li = li->next) {
Entry *entry = li->data;
if (/* if not file */
entry->type != ENTRY_FILE ||
/* if already included */
(folder->includes_ht != NULL &&
g_hash_table_lookup (folder->includes_ht,
entry->name) != NULL))
continue;
if (folder->only_unallocated &&
entry->alloc != 0)
continue;
if (matches_query (info, folder, (EntryFile *)entry,
folder->query))
folder->entries = g_slist_prepend
(folder->entries, entry_ref_alloc (entry));
}
}
/* get entries in folder */
/* FIXME: support cancellation here */
static void
ensure_folder_unlocked (VFolderInfo *info,
Folder *folder,
gboolean subfolders,
Folder *except,
gboolean ignore_unallocated)
{
if (subfolders) {
GSList *li;
for (li = folder->subfolders; li != NULL; li = li->next)
ensure_folder_unlocked (info, li->data, subfolders,
except, ignore_unallocated);
}
if (except == folder)
return;
if (ignore_unallocated &&
folder->only_unallocated)
return;
if (folder->up_to_date)
return;
if (folder->entries != NULL) {
g_slist_foreach (folder->entries,
(GFunc)entry_unref_dealloc, NULL);
g_slist_free (folder->entries);
folder->entries = NULL;
}
/* Include includes */
folder->entries = alloc_entries_from_files (info, folder->includes);
/* Run query */
append_query (info, folder);
/* We were prepending all this time */
folder->entries = g_slist_reverse (folder->entries);
/* Include subfolders */
/* we always whack them onto the beginning */
if (folder->subfolders != NULL) {
GSList *subfolders = g_slist_copy (folder->subfolders);
g_slist_foreach (subfolders, (GFunc)entry_ref_alloc, NULL);
folder->entries = g_slist_concat (subfolders, folder->entries);
}
/* Exclude excludes */
if (folder->excludes != NULL) {
GSList *li;
GSList *entries = folder->entries;
folder->entries = NULL;
for (li = entries; li != NULL; li = li->next) {
Entry *entry = li->data;
if (g_hash_table_lookup (folder->excludes, entry->name) == NULL)
folder->entries = g_slist_prepend (folder->entries, entry);
else
entry_unref_dealloc (entry);
}
g_slist_free (entries);
/* to preserve the Folders then everything else order */
folder->entries = g_slist_reverse (folder->entries);
}
folder->up_to_date = TRUE;
/* not yet sorted */
folder->sorted = FALSE;
}
static void
ensure_folder (VFolderInfo *info,
Folder *folder,
gboolean subfolders,
Folder *except,
gboolean ignore_unallocated)
{
G_LOCK (vfolder_lock);
ensure_folder_unlocked (info, folder, subfolders, except, ignore_unallocated);
G_UNLOCK (vfolder_lock);
}
static char *
get_directory_file_unlocked (VFolderInfo *info, Folder *folder)
{
char *filename;
/* FIXME: cache dir_files */
if (folder->desktop_file == NULL)
return NULL;
if (folder->desktop_file[0] == G_DIR_SEPARATOR)
return g_strdup (folder->desktop_file);
/* Now try the user directory */
if (info->user_desktop_dir != NULL) {
filename = g_build_filename (info->user_desktop_dir,
folder->desktop_file,
NULL);
if (access (filename, F_OK) == 0) {
return filename;
}
g_free (filename);
}
filename = g_build_filename (info->desktop_dir, folder->desktop_file, NULL);
if (access (filename, F_OK) == 0) {
return filename;
}
g_free (filename);
return NULL;
}
static char *
get_directory_file (VFolderInfo *info, Folder *folder)
{
char *ret;
G_LOCK (vfolder_lock);
ret = get_directory_file_unlocked (info, folder);
G_UNLOCK (vfolder_lock);
return ret;
}
static GSList *
get_sort_order (VFolderInfo *info, Folder *folder)
{
GSList *list;
char **parsed;
char *order;
int i;
char *filename;
filename = get_directory_file_unlocked (info, folder);
if (filename == NULL)
return NULL;
order = NULL;
readitem_entry (filename,
"SortOrder",
&order,
NULL,
NULL);
g_free (filename);
if (order == NULL)
return NULL;
parsed = g_strsplit (order, ":", -1);
g_free (order);
list = NULL;
for (i = 0; parsed[i] != NULL; i++) {
char *word = parsed[i];
/* steal */
parsed[i] = NULL;
/* ignore empty */
if (word[0] == '\0') {
g_free (word);
continue;
}
list = g_slist_prepend (list, word);
}
/* we've stolen all strings from it */
g_free (parsed);
return g_slist_reverse (list);
}
/* get entries in folder */
static void
ensure_folder_sort (VFolderInfo *info, Folder *folder)
{
GSList *li, *sort_order;
GSList *entries;
GHashTable *entry_hash;
ensure_folder (info, folder,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
if (folder->sorted)
return;
G_LOCK (vfolder_lock);
sort_order = get_sort_order (info, folder);
if (sort_order == NULL) {
folder->sorted = TRUE;
G_UNLOCK (vfolder_lock);
return;
}
entries = folder->entries;
folder->entries = NULL;
entry_hash = g_hash_table_new (g_str_hash, g_str_equal);
for (li = entries; li != NULL; li = li->next) {
Entry *entry = li->data;
g_hash_table_insert (entry_hash, entry->name, li);
}
for (li = sort_order; li != NULL; li = li->next) {
char *word = li->data;
GSList *entry_list;
Entry *entry;
/* we kill the words here */
li->data = NULL;
entry_list = g_hash_table_lookup (entry_hash, word);
g_free (word);
if (entry_list == NULL)
continue;
entry = entry_list->data;
entries = g_slist_delete_link (entries, entry_list);
folder->entries = g_slist_prepend (folder->entries,
entry);
}
/* put on those that weren't mentioned in the sort */
for (li = entries; li != NULL; li = li->next) {
Entry *entry = li->data;
folder->entries = g_slist_prepend (folder->entries,
entry);
}
g_hash_table_destroy (entry_hash);
g_slist_free (entries);
g_slist_free (sort_order);
folder->sorted = TRUE;
G_UNLOCK (vfolder_lock);
}
static EntryFile *
file_new (const char *name)
{
EntryFile *efile = g_new0 (EntryFile, 1);
efile->entry.type = ENTRY_FILE;
efile->entry.name = g_strdup (name);
efile->entry.refcount = 1;
return efile;
}
static Folder *
folder_new (const char *name)
{
Folder *folder = g_new0 (Folder, 1);
folder->entry.type = ENTRY_FOLDER;
folder->entry.name = g_strdup (name);
folder->entry.refcount = 1;
return folder;
}
static Query *
query_new (int type)
{
Query *query;
if (type == QUERY_KEYWORD)
query = (Query *)g_new0 (QueryKeyword, 1);
else if (type == QUERY_FILENAME)
query = (Query *)g_new0 (QueryFilename, 1);
else
query = g_new0 (Query, 1);
query->type = type;
return query;
}
static void
query_destroy (Query *query)
{
if (query == NULL)
return;
if (query->type == QUERY_FILENAME) {
QueryFilename *qfile = (QueryFilename *)query;
g_free (qfile->filename);
qfile->filename = NULL;
} else if (query->type == QUERY_OR ||
query->type == QUERY_AND) {
g_slist_foreach (query->queries, (GFunc)query_destroy, NULL);
g_slist_free (query->queries);
query->queries = NULL;
}
g_free (query);
}
static void
add_folder_monitor_unlocked (VFolderInfo *info,
FileMonitorHandle *handle)
{
VFolderURI vuri;
GnomeVFSResult result;
Folder *folder;
VFOLDER_URI_PARSE (handle->uri, &vuri);
file_monitor_handle_ref_unlocked (handle);
info->folder_monitors =
g_slist_prepend (info->folder_monitors, handle);
folder = resolve_folder (info,
vuri.path,
FALSE /* ignore_basename */,
&result,
NULL);
if (folder == NULL) {
file_monitor_handle_ref_unlocked (handle);
info->free_folder_monitors =
g_slist_prepend (info->free_folder_monitors, handle);
if (handle->exists) {
handle->exists = FALSE;
gnome_vfs_monitor_callback
((GnomeVFSMethodHandle *)handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_DELETED);
}
} else {
file_monitor_handle_ref_unlocked (handle);
((Entry *)folder)->monitors =
g_slist_prepend (((Entry *)folder)->monitors, handle);
if ( ! handle->exists) {
handle->exists = TRUE;
gnome_vfs_monitor_callback
((GnomeVFSMethodHandle *)handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_CREATED);
}
}
}
static inline void
invalidate_folder_T (Folder *folder)
{
folder->up_to_date = FALSE;
invalidate_folder_subfolders (folder, TRUE);
}
static inline void
invalidate_folder (Folder *folder)
{
G_LOCK (vfolder_lock);
folder->up_to_date = FALSE;
G_UNLOCK (vfolder_lock);
invalidate_folder_subfolders (folder, FALSE);
}
static void
invalidate_folder_subfolders (Folder *folder,
gboolean lock_taken)
{
GSList *li;
for (li = folder->subfolders; li != NULL; li = li->next) {
Folder *subfolder = li->data;
if (!lock_taken)
invalidate_folder (subfolder);
else
invalidate_folder_T (subfolder);
}
emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
}
/* FIXME: this is UGLY!, we need to figure out when the file
* got finished changing! */
static gboolean
reread_timeout (gpointer data)
{
VFolderInfo *info = data;
gboolean force_read_items = info->file_monitors != NULL;
vfolder_info_reload (info, NULL, NULL, force_read_items);
return FALSE;
}
static void
queue_reread_in (VFolderInfo *info, int msec)
{
G_LOCK (vfolder_lock);
if (info->reread_queue != 0)
g_source_remove (info->reread_queue);
info->reread_queue = g_timeout_add (msec, reread_timeout, info);
G_UNLOCK (vfolder_lock);
}
static void
vfolder_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
/* FIXME: implement */
}
static void
vfolder_user_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
/* FIXME: implement */
}
static void
vfolder_filename_monitor (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
VFolderInfo *info = user_data;
if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
! info->user_file_active) {
queue_reread_in (info, 200);
} else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
! info->user_file_active) {
/* FIXME: is this correct? I mean now
* there probably isn't ANY vfolder file, so we
* init to default values really. I have no clue what's
* right here */
vfolder_info_reload (info, NULL, NULL,
TRUE /* force read items */);
}
}
static void
vfolder_user_filename_monitor (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
VFolderInfo *info = user_data;
if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
info->user_file_active) {
struct stat s;
/* see if this was really our own change */
if (info->user_filename_last_write == time (NULL))
return;
/* anal retentive */
if (stat (info->user_filename, &s) == 0 &&
info->user_filename_last_write == s.st_ctime)
return;
queue_reread_in (info, 200);
} else if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
! info->user_file_active) {
queue_reread_in (info, 200);
} else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
info->user_file_active) {
gboolean force_read_items = info->file_monitors != NULL;
vfolder_info_reload (info, NULL, NULL, force_read_items);
}
}
static void
item_dir_monitor (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
VFolderInfo *info = user_data;
if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
/* first invalidate all folders */
invalidate_folder (info->root);
/* second invalidate all entries */
info->entries_valid = FALSE;
if (info->file_monitors != NULL) {
GnomeVFSResult result;
GSList *li;
/* Whack all monitors here! */
for (li = info->file_monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *h = li->data;
if (h->handle != NULL)
gnome_vfs_monitor_cancel (h->handle);
h->handle = NULL;
}
if (vfolder_info_read_items (info, &result, NULL)) {
info->entries_valid = TRUE;
}
}
}
}
static gboolean
setup_dir_monitor (VFolderInfo *info, const char *dir, gboolean subdirs,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
GnomeVFSMonitorHandle *handle;
DIR *dh;
struct dirent *de;
char *uri;
uri = gnome_vfs_get_uri_from_local_path (dir);
if (gnome_vfs_monitor_add (&handle,
uri,
GNOME_VFS_MONITOR_DIRECTORY,
item_dir_monitor,
info) != GNOME_VFS_OK) {
StatLoc *sl = bake_statloc (dir, time (NULL));
if (sl != NULL)
info->stat_dirs = g_slist_prepend (info->stat_dirs, sl);
g_free (uri);
return TRUE;
}
g_free (uri);
if (gnome_vfs_context_check_cancellation (context)) {
gnome_vfs_monitor_cancel (handle);
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
info->item_dir_monitors =
g_slist_prepend (info->item_dir_monitors, handle);
if ( ! subdirs)
return TRUE;
dh = opendir (dir);
if (dh == NULL)
return TRUE;
while ((de = readdir (dh)) != NULL) {
char *full_path;
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
closedir (dh);
return FALSE;
}
if (de->d_name[0] == '.')
continue;
full_path = g_build_filename (dir, de->d_name, NULL);
if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
if ( ! setup_dir_monitor (info, full_path,
TRUE /* subdirs */,
result, context)) {
closedir (dh);
return FALSE;
}
}
g_free (full_path);
}
closedir (dh);
return TRUE;
}
static gboolean
monitor_setup (VFolderInfo *info,
gboolean setup_filenames,
gboolean setup_itemdirs,
gboolean setup_desktop_dirs,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
char *uri;
GSList *li;
if (setup_filenames) {
uri = gnome_vfs_get_uri_from_local_path
(info->filename);
if (gnome_vfs_monitor_add (&info->filename_monitor,
uri,
GNOME_VFS_MONITOR_FILE,
vfolder_filename_monitor,
info) != GNOME_VFS_OK) {
info->filename_monitor = NULL;
info->filename_statloc = bake_statloc (info->filename,
time (NULL));
}
g_free (uri);
}
if (setup_filenames &&
info->user_filename != NULL) {
uri = gnome_vfs_get_uri_from_local_path
(info->user_filename);
if (gnome_vfs_monitor_add (&info->user_filename_monitor,
uri,
GNOME_VFS_MONITOR_FILE,
vfolder_user_filename_monitor,
info) != GNOME_VFS_OK) {
info->user_filename_monitor = NULL;
info->user_filename_statloc =
bake_statloc (info->user_filename,
time (NULL));
}
g_free (uri);
}
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
if (setup_itemdirs) {
for (li = info->item_dirs; li != NULL; li = li->next) {
const char *dir = li->data;
if ( ! setup_dir_monitor (info, dir,
FALSE /* subdirs */,
result, context))
return FALSE;
}
if (info->user_item_dir != NULL) {
if ( ! setup_dir_monitor (info, info->user_item_dir,
FALSE /* subdirs */,
result, context))
return FALSE;
}
for (li = info->merge_dirs; li != NULL; li = li->next) {
const char *dir = li->data;
if ( ! setup_dir_monitor (info, dir,
TRUE /* subdirs */,
result, context))
return FALSE;
}
}
if (setup_desktop_dirs) {
uri = gnome_vfs_get_uri_from_local_path
(info->desktop_dir);
if (gnome_vfs_monitor_add (&info->desktop_dir_monitor,
uri,
GNOME_VFS_MONITOR_FILE,
vfolder_desktop_dir_monitor,
info) != GNOME_VFS_OK) {
info->desktop_dir_monitor = NULL;
info->desktop_dir_statloc =
bake_statloc (info->desktop_dir,
time (NULL));
}
g_free (uri);
}
if (setup_desktop_dirs &&
info->user_desktop_dir != NULL) {
uri = gnome_vfs_get_uri_from_local_path
(info->user_desktop_dir);
if (gnome_vfs_monitor_add (&info->user_desktop_dir_monitor,
uri,
GNOME_VFS_MONITOR_DIRECTORY,
vfolder_user_desktop_dir_monitor,
info) != GNOME_VFS_OK) {
info->user_desktop_dir_monitor = NULL;
info->user_desktop_dir_statloc =
bake_statloc (info->user_desktop_dir,
time (NULL));
}
g_free (uri);
}
return TRUE;
}
static void
vfolder_info_init (VFolderInfo *info, const char *scheme)
{
const char *path;
GSList *list;
info->scheme = g_strdup (scheme);
info->filename = g_strconcat (SYSCONFDIR,
"/gnome-vfs-2.0/vfolders/",
scheme, ".vfolder-info",
NULL);
info->user_filename = g_strconcat (g_get_home_dir (),
"/" DOT_GNOME "/vfolders/",
scheme, ".vfolder-info",
NULL);
info->desktop_dir = g_strconcat (SYSCONFDIR,
"/gnome-vfs-2.0/vfolders/",
NULL);
info->user_desktop_dir = g_strconcat (g_get_home_dir (),
"/" DOT_GNOME "/vfolders/",
NULL);
/* Init the desktop paths */
list = NULL;
list = g_slist_prepend (list, g_strdup ("/usr/share/applications/"));
if (strcmp ("/usr/share/applications/", DATADIR "/applications/") != 0)
list = g_slist_prepend (list, g_strdup (DATADIR "/applications/"));
path = g_getenv ("DESKTOP_FILE_PATH");
if (path != NULL) {
int i;
char **ppath = g_strsplit (path, ":", -1);
for (i = 0; ppath[i] != NULL; i++) {
const char *dir = ppath[i];
list = g_slist_prepend (list, g_strdup (dir));
}
g_strfreev (ppath);
}
info->item_dirs = g_slist_reverse (list);
info->user_item_dir = g_strconcat (g_get_home_dir (),
"/" DOT_GNOME "/vfolders/",
scheme,
NULL);
info->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
info->root = folder_new ("Root");
info->modification_time = time (NULL);
}
static void
vfolder_info_free_internals_unlocked (VFolderInfo *info)
{
if (info == NULL)
return;
if (info->filename_monitor != NULL) {
gnome_vfs_monitor_cancel (info->filename_monitor);
info->filename_monitor = NULL;
}
if (info->user_filename_monitor != NULL) {
gnome_vfs_monitor_cancel (info->user_filename_monitor);
info->user_filename_monitor = NULL;
}
g_free (info->filename_statloc);
info->filename_statloc = NULL;
g_free (info->user_filename_statloc);
info->user_filename_statloc = NULL;
if (info->desktop_dir_monitor != NULL) {
gnome_vfs_monitor_cancel (info->desktop_dir_monitor);
info->desktop_dir_monitor = NULL;
}
if (info->user_desktop_dir_monitor != NULL) {
gnome_vfs_monitor_cancel (info->user_desktop_dir_monitor);
info->user_desktop_dir_monitor = NULL;
}
g_free (info->desktop_dir_statloc);
info->desktop_dir_statloc = NULL;
g_free (info->user_desktop_dir_statloc);
info->user_desktop_dir_statloc = NULL;
g_slist_foreach (info->item_dir_monitors,
(GFunc)gnome_vfs_monitor_cancel, NULL);
g_slist_free (info->item_dir_monitors);
info->item_dir_monitors = NULL;
g_free (info->scheme);
info->scheme = NULL;
g_free (info->filename);
info->filename = NULL;
g_free (info->user_filename);
info->user_filename = NULL;
g_free (info->desktop_dir);
info->desktop_dir = NULL;
g_free (info->user_desktop_dir);
info->user_desktop_dir = NULL;
g_slist_foreach (info->item_dirs, (GFunc)g_free, NULL);
g_slist_free (info->item_dirs);
info->item_dirs = NULL;
g_free (info->user_item_dir);
info->user_item_dir = NULL;
g_slist_foreach (info->merge_dirs, (GFunc)g_free, NULL);
g_slist_free (info->merge_dirs);
info->merge_dirs = NULL;
g_slist_foreach (info->entries, (GFunc)entry_unref, NULL);
g_slist_free (info->entries);
info->entries = NULL;
if (info->entries_ht != NULL)
g_hash_table_destroy (info->entries_ht);
info->entries_ht = NULL;
g_slist_foreach (info->unallocated_folders,
(GFunc)entry_unref,
NULL);
g_slist_free (info->unallocated_folders);
info->unallocated_folders = NULL;
entry_unref ((Entry *)info->root);
info->root = NULL;
g_slist_foreach (info->stat_dirs, (GFunc)g_free, NULL);
g_slist_free (info->stat_dirs);
info->stat_dirs = NULL;
g_slist_foreach (info->folder_monitors,
(GFunc)file_monitor_handle_unref_unlocked, NULL);
g_slist_free (info->folder_monitors);
info->folder_monitors = NULL;
g_slist_foreach (info->free_folder_monitors,
(GFunc)file_monitor_handle_unref_unlocked, NULL);
g_slist_free (info->free_folder_monitors);
info->free_folder_monitors = NULL;
g_slist_foreach (info->file_monitors,
(GFunc)file_monitor_handle_unref_unlocked, NULL);
g_slist_free (info->file_monitors);
info->file_monitors = NULL;
g_slist_foreach (info->free_file_monitors,
(GFunc)file_monitor_handle_unref_unlocked, NULL);
g_slist_free (info->free_file_monitors);
info->free_file_monitors = NULL;
if (info->reread_queue != 0)
g_source_remove (info->reread_queue);
info->reread_queue = 0;
}
static void
vfolder_info_free_internals (VFolderInfo *info)
{
G_LOCK (vfolder_lock);
vfolder_info_free_internals_unlocked (info);
G_UNLOCK (vfolder_lock);
}
static void
vfolder_info_destroy (VFolderInfo *info)
{
vfolder_info_free_internals (info);
g_free (info);
}
static Query *
single_query_read (xmlNode *qnode)
{
Query *query;
xmlNode *node;
if (qnode->type != XML_ELEMENT_NODE ||
qnode->name == NULL)
return NULL;
query = NULL;
if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
qnode->xmlChildrenNode != NULL) {
xmlNode *iter;
query = NULL;
for (iter = qnode->xmlChildrenNode;
iter != NULL && query == NULL;
iter = iter->next)
query = single_query_read (iter);
if (query != NULL) {
query->not = ! query->not;
}
return query;
} else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
xmlChar *word = xmlNodeGetContent (qnode);
if (word != NULL) {
query = query_new (QUERY_KEYWORD);
((QueryKeyword *)query)->keyword =
g_quark_from_string (word);
xmlFree (word);
}
return query;
} else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
xmlChar *file = xmlNodeGetContent (qnode);
if (file != NULL) {
query = query_new (QUERY_FILENAME);
((QueryFilename *)query)->filename =
g_strdup (file);
xmlFree (file);
}
return query;
} else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
query = query_new (QUERY_AND);
} else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
query = query_new (QUERY_OR);
} else {
/* We don't understand */
return NULL;
}
/* This must be OR or AND */
g_assert (query != NULL);
for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
Query *new_query = single_query_read (node);
if (new_query != NULL)
query->queries = g_slist_prepend
(query->queries, new_query);
}
query->queries = g_slist_reverse (query->queries);
return query;
}
static void
add_or_set_query (Query **query, Query *new_query)
{
if (*query == NULL) {
*query = new_query;
} else {
Query *old_query = *query;
*query = query_new (QUERY_OR);
(*query)->queries =
g_slist_append ((*query)->queries, old_query);
(*query)->queries =
g_slist_append ((*query)->queries, new_query);
}
}
static Query *
query_read (xmlNode *qnode)
{
Query *query;
xmlNode *node;
query = NULL;
for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
if (node->type != XML_ELEMENT_NODE ||
node->name == NULL)
continue;
if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
node->xmlChildrenNode != NULL) {
xmlNode *iter;
Query *new_query = NULL;
for (iter = node->xmlChildrenNode;
iter != NULL && new_query == NULL;
iter = iter->next)
new_query = single_query_read (iter);
if (new_query != NULL) {
new_query->not = ! new_query->not;
add_or_set_query (&query, new_query);
}
} else {
Query *new_query = single_query_read (node);
if (new_query != NULL)
add_or_set_query (&query, new_query);
}
}
return query;
}
static Folder *
folder_read (VFolderInfo *info, xmlNode *fnode)
{
Folder *folder;
xmlNode *node;
folder = folder_new (NULL);
for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
if (node->type != XML_ELEMENT_NODE ||
node->name == NULL)
continue;
if (g_ascii_strcasecmp (node->name, "Name") == 0) {
xmlChar *name = xmlNodeGetContent (node);
if (name != NULL) {
g_free (folder->entry.name);
folder->entry.name = g_strdup (name);
xmlFree (name);
}
} else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
xmlChar *desktop = xmlNodeGetContent (node);
if (desktop != NULL) {
g_free (folder->desktop_file);
folder->desktop_file = g_strdup (desktop);
xmlFree (desktop);
}
} else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
xmlChar *file = xmlNodeGetContent (node);
if (file != NULL) {
GSList *li;
char *str = g_strdup (file);
folder->includes = g_slist_prepend
(folder->includes, str);
if (folder->includes_ht == NULL) {
folder->includes_ht =
g_hash_table_new_full
(g_str_hash,
g_str_equal,
NULL,
NULL);
}
li = g_hash_table_lookup (folder->includes_ht,
file);
if (li != NULL) {
g_free (li->data);
/* Note: this will NOT change folder->includes
* pointer! */
folder->includes = g_slist_delete_link
(folder->includes, li);
}
g_hash_table_replace (folder->includes_ht,
file, folder->includes);
xmlFree (file);
}
} else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
xmlChar *file = xmlNodeGetContent (node);
if (file != NULL) {
char *s;
if (folder->excludes == NULL) {
folder->excludes = g_hash_table_new_full
(g_str_hash,
g_str_equal,
(GDestroyNotify)g_free,
NULL);
}
s = g_strdup (file);
g_hash_table_replace (folder->excludes, s, s);
xmlFree (file);
}
} else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
Query *query;
query = query_read (node);
if (query != NULL) {
if (folder->query != NULL)
query_destroy (folder->query);
folder->query = query;
}
} else if (g_ascii_strcasecmp (node->name, "OnlyUnallocated") == 0) {
info->unallocated_folders =
g_slist_prepend (info->unallocated_folders,
(Folder *)entry_ref ((Entry *)folder));
folder->only_unallocated = TRUE;
} else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
Folder *new_folder = folder_read (info, node);
if (new_folder != NULL) {
folder->subfolders =
g_slist_append (folder->subfolders,
new_folder);
new_folder->parent = folder;
}
} else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
folder->read_only = TRUE;
} else if (g_ascii_strcasecmp (node->name,
"DontShowIfEmpty") == 0) {
folder->dont_show_if_empty = TRUE;
}
}
/* Name is required */
if (folder->entry.name == NULL) {
entry_unref ((Entry *)folder);
folder = NULL;
}
folder->includes = g_slist_reverse (folder->includes);
return folder;
}
static char *
subst_home (const char *dir)
{
if (dir[0] == '~')
return g_strconcat (g_get_home_dir (),
&dir[1],
NULL);
else
return g_strdup (dir);
}
/* FORMAT looks like:
* <VFolderInfo>
* <!-- Merge dirs optional -->
* <MergeDir>/etc/X11/applnk</MergeDir>
* <!-- Only specify if it should override standard location -->
* <ItemDir>/usr/share/applications</ItemDir>
* <!-- This is where the .directories are -->
* <DesktopDir>/etc/X11/gnome/vfolders</DesktopDir>
* <!-- Root folder -->
* <Folder>
* <Name>Root</Name>
*
* <Include>important.desktop</Include>
*
* <!-- Other folders -->
* <Folder>
* <Name>SomeFolder</Name>
* </Folder>
* <Folder>
* <Name>Test_Folder</Name>
* <!-- could also be absolute -->
* <Desktop>Test_Folder.directory</Desktop>
* <Query>
* <Or>
* <And>
* <Keyword>Application</Keyword>
* <Keyword>Game</Keyword>
* </And>
* <Keyword>Clock</Keyword>
* </Or>
* </Query>
* <Include>somefile.desktop</Include>
* <Include>someotherfile.desktop</Include>
* <Exclude>yetanother.desktop</Exclude>
* </Folder>
* </Folder>
* </VFolderInfo>
*/
static gboolean
vfolder_info_read_info (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
xmlDoc *doc;
xmlNode *node;
gboolean got_a_vfolder_dir = FALSE;
doc = NULL;
if (info->user_filename != NULL &&
access (info->user_filename, F_OK) == 0) {
doc = xmlParseFile (info->user_filename);
if (doc != NULL)
info->user_file_active = TRUE;
}
if (doc == NULL &&
access (info->filename, F_OK) == 0)
doc = xmlParseFile (info->filename);
if (gnome_vfs_context_check_cancellation (context)) {
xmlFreeDoc(doc);
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
if (doc == NULL
|| doc->xmlRootNode == NULL
|| doc->xmlRootNode->name == NULL
|| g_ascii_strcasecmp (doc->xmlRootNode->name, "VFolderInfo") != 0) {
xmlFreeDoc(doc);
return TRUE; /* FIXME: really, shouldn't we error out? */
}
for (node = doc->xmlRootNode->xmlChildrenNode; node != NULL; node = node->next) {
if (node->type != XML_ELEMENT_NODE ||
node->name == NULL)
continue;
if (gnome_vfs_context_check_cancellation (context)) {
xmlFreeDoc(doc);
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
xmlChar *dir = xmlNodeGetContent (node);
if (dir != NULL) {
info->merge_dirs = g_slist_append (info->merge_dirs,
g_strdup (dir));
xmlFree (dir);
}
} else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
xmlChar *dir = xmlNodeGetContent (node);
if (dir != NULL) {
if ( ! got_a_vfolder_dir) {
g_slist_foreach (info->item_dirs,
(GFunc)g_free, NULL);
g_slist_free (info->item_dirs);
info->item_dirs = NULL;
}
got_a_vfolder_dir = TRUE;
info->item_dirs = g_slist_append (info->item_dirs,
g_strdup (dir));
xmlFree (dir);
}
} else if (g_ascii_strcasecmp (node->name, "UserItemDir") == 0) {
xmlChar *dir = xmlNodeGetContent (node);
if (dir != NULL) {
g_free (info->user_item_dir);
info->user_item_dir = subst_home (dir);
xmlFree (dir);
}
} else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
xmlChar *dir = xmlNodeGetContent (node);
if (dir != NULL) {
g_free (info->desktop_dir);
info->desktop_dir = g_strdup (dir);
xmlFree (dir);
}
} else if (g_ascii_strcasecmp (node->name, "UserDesktopDir") == 0) {
xmlChar *dir = xmlNodeGetContent (node);
if (dir != NULL) {
g_free (info->user_desktop_dir);
info->user_desktop_dir = subst_home (dir);
xmlFree (dir);
}
} else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
Folder *folder = folder_read (info, node);
if (folder != NULL) {
if (info->root != NULL)
entry_unref ((Entry *)info->root);
info->root = folder;
}
} else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
info->read_only = TRUE;
}
}
xmlFreeDoc(doc);
return TRUE;
}
static void
add_xml_tree_from_query (xmlNode *parent, Query *query)
{
xmlNode *real_parent;
if (query->not)
real_parent = xmlNewChild (parent /* parent */,
NULL /* ns */,
"Not" /* name */,
NULL /* content */);
else
real_parent = parent;
if (query->type == QUERY_KEYWORD) {
QueryKeyword *qkeyword = (QueryKeyword *)query;
const char *string = g_quark_to_string (qkeyword->keyword);
xmlNewChild (real_parent /* parent */,
NULL /* ns */,
"Keyword" /* name */,
string /* content */);
} else if (query->type == QUERY_FILENAME) {
QueryFilename *qfilename = (QueryFilename *)query;
xmlNewChild (real_parent /* parent */,
NULL /* ns */,
"Filename" /* name */,
qfilename->filename /* content */);
} else if (query->type == QUERY_OR ||
query->type == QUERY_AND) {
xmlNode *node;
const char *name;
GSList *li;
if (query->type == QUERY_OR)
name = "Or";
else /* QUERY_AND */
name = "And";
node = xmlNewChild (real_parent /* parent */,
NULL /* ns */,
name /* name */,
NULL /* content */);
for (li = query->queries; li != NULL; li = li->next) {
Query *subquery = li->data;
add_xml_tree_from_query (node, subquery);
}
} else {
g_assert_not_reached ();
}
}
static void
add_excludes_to_xml (gpointer key, gpointer value, gpointer user_data)
{
const char *filename = key;
xmlNode *folder_node = user_data;
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"Exclude" /* name */,
filename /* content */);
}
static void
add_xml_tree_from_folder (xmlNode *parent, Folder *folder)
{
GSList *li;
xmlNode *folder_node;
folder_node = xmlNewChild (parent /* parent */,
NULL /* ns */,
"Folder" /* name */,
NULL /* content */);
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"Name" /* name */,
folder->entry.name /* content */);
if (folder->desktop_file != NULL) {
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"Desktop" /* name */,
folder->desktop_file /* content */);
}
if (folder->read_only)
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"ReadOnly" /* name */,
NULL /* content */);
if (folder->dont_show_if_empty)
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"DontShowIfEmpty" /* name */,
NULL /* content */);
if (folder->only_unallocated)
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"OnlyUnallocated" /* name */,
NULL /* content */);
for (li = folder->subfolders; li != NULL; li = li->next) {
Folder *subfolder = li->data;
add_xml_tree_from_folder (folder_node, subfolder);
}
for (li = folder->includes; li != NULL; li = li->next) {
const char *include = li->data;
xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"Include" /* name */,
include /* content */);
}
if (folder->excludes) {
g_hash_table_foreach (folder->excludes,
add_excludes_to_xml,
folder_node);
}
if (folder->query != NULL) {
xmlNode *query_node;
query_node = xmlNewChild (folder_node /* parent */,
NULL /* ns */,
"Query" /* name */,
NULL /* content */);
add_xml_tree_from_query (query_node, folder->query);
}
}
static xmlDoc *
xml_tree_from_vfolder (VFolderInfo *info)
{
xmlDoc *doc;
xmlNode *topnode;
GSList *li;
doc = xmlNewDoc ("1.0");
topnode = xmlNewDocNode (doc /* doc */,
NULL /* ns */,
"VFolderInfo" /* name */,
NULL /* content */);
doc->xmlRootNode = topnode;
for (li = info->merge_dirs; li != NULL; li = li->next) {
const char *merge_dir = li->data;
xmlNewChild (topnode /* parent */,
NULL /* ns */,
"MergeDir" /* name */,
merge_dir /* content */);
}
for (li = info->item_dirs; li != NULL; li = li->next) {
const char *item_dir = li->data;
xmlNewChild (topnode /* parent */,
NULL /* ns */,
"ItemDir" /* name */,
item_dir /* content */);
}
if (info->user_item_dir != NULL) {
xmlNewChild (topnode /* parent */,
NULL /* ns */,
"UserItemDir" /* name */,
info->user_item_dir /* content */);
}
if (info->desktop_dir != NULL) {
xmlNewChild (topnode /* parent */,
NULL /* ns */,
"DesktopDir" /* name */,
info->desktop_dir /* content */);
}
if (info->user_desktop_dir != NULL) {
xmlNewChild (topnode /* parent */,
NULL /* ns */,
"UserDesktopDir" /* name */,
info->user_desktop_dir /* content */);
}
if (info->root != NULL)
add_xml_tree_from_folder (topnode, info->root);
return doc;
}
/* FIXME: what to do about errors */
static void
vfolder_info_write_user (VFolderInfo *info)
{
xmlDoc *doc;
if (info->inhibit_write > 0)
return;
if (info->user_filename == NULL)
return;
doc = xml_tree_from_vfolder (info);
if (doc == NULL)
return;
/* FIXME: errors, anyone? */
ensure_dir (info->user_filename,
TRUE /* ignore_basename */);
xmlSaveFormatFile (info->user_filename, doc, TRUE /* format */);
/* not as good as a stat, but cheaper ... hmmm what is
* the likelyhood of this not being the same as ctime */
info->user_filename_last_write = time (NULL);
xmlFreeDoc(doc);
info->user_file_active = TRUE;
info->dirty = FALSE;
info->modification_time = time (NULL);
}
/* An EVIL function for quick reading of .desktop files,
* only reads in one or two keys, but that's ALL we need */
static void
readitem_entry (const char *filename,
const char *key1,
char **result1,
const char *key2,
char **result2)
{
FILE *fp;
char buf[1024];
int keylen1, keylen2;
*result1 = NULL;
if (result2 != NULL)
*result2 = NULL;
fp = fopen (filename, "r");
if (fp == NULL)
return;
keylen1 = strlen (key1);
if (key2 != NULL)
keylen2 = strlen (key2);
else
keylen2 = -1;
/* This is slightly wrong, it should only look
* at the correct section */
while (fgets (buf, sizeof (buf), fp) != NULL) {
char *p;
int len;
int keylen;
char **result = NULL;
/* check if it's one of the keys */
if (strncmp (buf, key1, keylen1) == 0) {
result = result1;
keylen = keylen1;
} else if (keylen2 >= 0 &&
strncmp (buf, key2, keylen2) == 0) {
result = result2;
keylen = keylen2;
} else {
continue;
}
p = &buf[keylen];
/* still not our key */
if (!(*p == '=' || *p == ' ')) {
continue;
}
do
p++;
while (*p == ' ' || *p == '=');
/* get rid of trailing \n */
len = strlen (p);
if (p[len-1] == '\n' ||
p[len-1] == '\r')
p[len-1] = '\0';
*result = g_strdup (p);
if (*result1 == NULL ||
(result2 != NULL && *result2 == NULL))
break;
}
fclose (fp);
}
static void
vfolder_info_insert_entry (VFolderInfo *info, EntryFile *efile)
{
GSList *entry_list;
entry_ref ((Entry *)efile);
entry_list = g_hash_table_lookup (info->entries_ht, efile->entry.name);
info->entries = g_slist_prepend (info->entries, efile);
/* The hash table contains the GSList pointer */
g_hash_table_replace (info->entries_ht, efile->entry.name,
info->entries);
if (entry_list != NULL) {
Entry *entry = entry_list->data;
info->entries = g_slist_delete_link (info->entries,
entry_list);
entry_unref (entry);
}
}
static void
set_keywords (EntryFile *efile, const char *keywords)
{
if (keywords != NULL) {
int i;
char **parsed = g_strsplit (keywords, ";", -1);
for (i = 0; parsed[i] != NULL; i++) {
GQuark quark;
const char *word = parsed[i];
/* ignore empties (including end of list) */
if (word[0] == '\0')
continue;
quark = g_quark_from_string (word);
efile->keywords = g_slist_prepend
(efile->keywords,
GINT_TO_POINTER (quark));
}
g_strfreev (parsed);
}
}
static EntryFile *
make_entry_file (const char *dir, const char *name)
{
EntryFile *efile;
char *categories;
char *only_show_in;
char *filename;
int i;
filename = g_build_filename (dir, name, NULL);
readitem_entry (filename,
"Categories",
&categories,
"OnlyShowIn",
&only_show_in);
if (only_show_in != NULL) {
gboolean show = FALSE;
char **parsed = g_strsplit (only_show_in, ";", -1);
for (i = 0; parsed[i] != NULL; i++) {
if (strcmp (parsed[i], "GNOME") == 0) {
show = TRUE;
break;
}
}
g_strfreev (parsed);
if ( ! show) {
g_free (filename);
g_free (only_show_in);
g_free (categories);
return NULL;
}
}
efile = file_new (name);
efile->filename = filename;
set_keywords (efile, categories);
g_free (only_show_in);
g_free (categories);
return efile;
}
static gboolean
vfolder_info_read_items_from (VFolderInfo *info,
const char *item_dir,
gboolean per_user,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
DIR *dir;
struct dirent *de;
dir = opendir (item_dir);
if (dir == NULL)
return TRUE;
while ((de = readdir (dir)) != NULL) {
EntryFile *efile;
if (gnome_vfs_context_check_cancellation (context)) {
closedir (dir);
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
/* files MUST be called .desktop */
if (de->d_name[0] == '.' ||
! check_ext (de->d_name, ".desktop"))
continue;
efile = make_entry_file (item_dir, de->d_name);
if (efile == NULL)
continue;
efile->per_user = per_user;
vfolder_info_insert_entry (info, efile);
entry_unref ((Entry *)efile);
}
closedir (dir);
return TRUE;
}
static gboolean
vfolder_info_read_items_merge (VFolderInfo *info,
const char *merge_dir,
const char *subdir,
GQuark inherited_keyword,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
DIR *dir;
struct dirent *de;
GQuark extra_keyword;
GQuark Application;
GQuark Merged;
GQuark inheritance;
gboolean pass_down_extra_keyword = TRUE;
dir = opendir (merge_dir);
if (dir == NULL)
return TRUE;
Application = g_quark_from_static_string ("Application");
Merged = g_quark_from_static_string ("Merged");
/* FIXME: this should be a hash or something */
extra_keyword = 0;
if (subdir == NULL) {
extra_keyword = g_quark_from_static_string ("Core");
pass_down_extra_keyword = FALSE;
} else if (g_ascii_strcasecmp (subdir, "Development") == 0)
extra_keyword = g_quark_from_static_string ("Development");
else if (g_ascii_strcasecmp (subdir, "Editors") == 0)
extra_keyword = g_quark_from_static_string ("TextEditor");
else if (g_ascii_strcasecmp (subdir, "Games") == 0)
extra_keyword = g_quark_from_static_string ("Game");
else if (g_ascii_strcasecmp (subdir, "Graphics") == 0)
extra_keyword = g_quark_from_static_string ("Graphics");
else if (g_ascii_strcasecmp (subdir, "Internet") == 0)
extra_keyword = g_quark_from_static_string ("Network");
else if (g_ascii_strcasecmp (subdir, "Multimedia") == 0)
extra_keyword = g_quark_from_static_string ("AudioVideo");
else if (g_ascii_strcasecmp (subdir, "Office") == 0)
extra_keyword = g_quark_from_static_string ("Office");
else if (g_ascii_strcasecmp (subdir, "Settings") == 0)
extra_keyword = g_quark_from_static_string ("Settings");
else if (g_ascii_strcasecmp (subdir, "System") == 0)
extra_keyword = g_quark_from_static_string ("System");
else if (g_ascii_strcasecmp (subdir, "Utilities") == 0)
extra_keyword = g_quark_from_static_string ("Utility");
while ((de = readdir (dir)) != NULL) {
EntryFile *efile;
if (gnome_vfs_context_check_cancellation (context)) {
closedir (dir);
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
/* ignore hidden */
if (de->d_name[0] == '.')
continue;
/* files MUST be called .desktop, so
* treat all others as dirs. If we're wrong,
* the open will fail, which is ok */
if ( ! check_ext (de->d_name, ".desktop")) {
/* if this is a directory recurse */
char *fullname = g_build_filename (merge_dir, de->d_name, NULL);
if ((pass_down_extra_keyword == TRUE) && (extra_keyword != 0)) {
inheritance = extra_keyword;
} else {
inheritance = inherited_keyword;
}
if ( ! vfolder_info_read_items_merge (info,
fullname,
de->d_name,
inheritance,
result,
context)) {
g_free (fullname);
return FALSE;
}
g_free (fullname);
continue;
}
/* FIXME: add some keywords about some known apps
* like gimp and whatnot, perhaps take these from the vfolder
* file or some such */
efile = make_entry_file (merge_dir, de->d_name);
if (efile == NULL)
continue;
/* If no keywords set, then add the standard ones */
if (efile->keywords == NULL) {
efile->keywords = g_slist_prepend
(efile->keywords,
GINT_TO_POINTER (Application));
efile->keywords = g_slist_prepend
(efile->keywords,
GINT_TO_POINTER (Merged));
if (inherited_keyword != 0) {
efile->keywords = g_slist_prepend
(efile->keywords,
GINT_TO_POINTER (inherited_keyword));
}
if (extra_keyword != 0) {
efile->keywords = g_slist_prepend
(efile->keywords,
GINT_TO_POINTER (extra_keyword));
}
efile->implicit_keywords = TRUE;
}
vfolder_info_insert_entry (info, efile);
entry_unref ((Entry *)efile);
}
closedir (dir);
return TRUE;
}
static Entry *
find_entry (GSList *list, const char *name)
{
GSList *li;
for (li = list; li != NULL; li = li->next) {
Entry *entry = li->data;
if (strcmp (name, entry->name) == 0)
return entry;
}
return NULL;
}
static void
file_monitor (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri,
const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
gpointer user_data)
{
FileMonitorHandle *h = user_data;
/* proxy the event through if it is a changed event
* only */
if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED &&
h->handle != NULL)
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) h,
h->uri, event_type);
}
static void
try_free_file_monitors_create_files_unlocked (VFolderInfo *info)
{
GSList *li, *list;
list = g_slist_copy (info->free_file_monitors);
for (li = list; li != NULL; li = li->next) {
FileMonitorHandle *handle = li->data;
Entry *entry;
GnomeVFSResult result;
char *dirfile = NULL;
if (handle->is_directory_file) {
VFolderURI vuri;
Folder *folder;
/* Evil! EVIL URI PARSING. this will eat a lot of
* stack if we have lots of free monitors */
VFOLDER_URI_PARSE (handle->uri, &vuri);
folder = resolve_folder (info,
vuri.path,
TRUE /* ignore_basename */,
&result,
NULL);
if (folder == NULL)
continue;
dirfile = get_directory_file_unlocked (info, folder);
if (dirfile == NULL)
continue;
entry = (Entry *)folder;
} else {
VFolderURI vuri;
Folder *f;
GnomeVFSResult result;
entry = NULL;
/* Evil! EVIL URI PARSING. this will eat a lot of
* stack if we have lots of monitors */
VFOLDER_URI_PARSE (handle->uri, &vuri);
f = resolve_folder (info,
vuri.path,
TRUE /* ignore_basename */,
&result,
NULL);
if (f != NULL) {
ensure_folder_unlocked (
info, f,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
entry = find_entry (f->entries, vuri.file);
}
if (entry == NULL)
continue;
}
info->free_file_monitors =
g_slist_remove (info->free_file_monitors, handle);
entry->monitors =
g_slist_prepend (entry->monitors, handle);
handle->exists = TRUE;
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_CREATED);
/* recreate a handle */
if (handle->handle == NULL &&
entry->type == ENTRY_FILE) {
EntryFile *efile = (EntryFile *)entry;
char *uri = gnome_vfs_get_uri_from_local_path
(efile->filename);
gnome_vfs_monitor_add (&(handle->handle),
uri,
GNOME_VFS_MONITOR_FILE,
file_monitor,
handle);
g_free (uri);
} else if (handle->handle == NULL &&
dirfile != NULL) {
char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
gnome_vfs_monitor_add (&(handle->handle),
uri,
GNOME_VFS_MONITOR_FILE,
file_monitor,
handle);
g_free (uri);
}
g_free (dirfile);
}
g_slist_free (list);
}
static void /* unlocked */
rescan_monitors (VFolderInfo *info)
{
GSList *li;
if (info->file_monitors == NULL)
return;
for (li = info->file_monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
GnomeVFSResult result;
Entry *entry;
char *dirfile = NULL;
/* these are handled below */
if ( ! h->exists)
continue;
if (h->is_directory_file) {
VFolderURI vuri;
Folder *folder;
/* Evil! EVIL URI PARSING. this will eat a lot of
* stack if we have lots of monitors */
VFOLDER_URI_PARSE (h->uri, &vuri);
folder = resolve_folder (info,
vuri.path,
TRUE /* ignore_basename */,
&result,
NULL);
if (folder != NULL)
dirfile = get_directory_file_unlocked (info,
folder);
if (dirfile == NULL) {
h->exists = FALSE;
gnome_vfs_monitor_callback
((GnomeVFSMethodHandle *)h,
h->uri,
GNOME_VFS_MONITOR_EVENT_DELETED);
info->free_file_monitors = g_slist_prepend
(info->free_file_monitors, h);
file_monitor_handle_ref_unlocked (h);
/* it has been unreffed when the entry was
* whacked */
continue;
}
entry = (Entry *)folder;
} else {
VFolderURI vuri;
Folder *f;
GnomeVFSResult result;
entry = NULL;
/* Evil! EVIL URI PARSING. this will eat a lot of
* stack if we have lots of monitors */
VFOLDER_URI_PARSE (h->uri, &vuri);
f = resolve_folder (info,
vuri.path,
TRUE /* ignore_basename */,
&result,
NULL);
if (f != NULL) {
ensure_folder_unlocked (
info, f,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
entry = find_entry (f->entries, vuri.file);
}
if (entry == NULL) {
h->exists = FALSE;
gnome_vfs_monitor_callback
((GnomeVFSMethodHandle *)h,
h->uri,
GNOME_VFS_MONITOR_EVENT_DELETED);
info->free_file_monitors = g_slist_prepend
(info->free_file_monitors, h);
file_monitor_handle_ref_unlocked (h);
/* it has been unreffed when the entry was
* whacked */
continue;
}
}
/* recreate a handle */
if (h->handle == NULL &&
entry->type == ENTRY_FILE) {
EntryFile *efile = (EntryFile *)entry;
char *uri = gnome_vfs_get_uri_from_local_path
(efile->filename);
gnome_vfs_monitor_add (&(h->handle),
uri,
GNOME_VFS_MONITOR_FILE,
file_monitor,
h);
g_free (uri);
} else if (h->handle == NULL &&
dirfile != NULL) {
char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
gnome_vfs_monitor_add (&(h->handle),
uri,
GNOME_VFS_MONITOR_FILE,
file_monitor,
h);
g_free (uri);
}
g_free (dirfile);
}
try_free_file_monitors_create_files_unlocked (info);
}
static gboolean /* unlocked */
vfolder_info_read_items (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
GSList *li;
/* First merge */
for (li = info->merge_dirs; li != NULL; li = li->next) {
const char *merge_dir = li->data;
if ( ! vfolder_info_read_items_merge (info, merge_dir, NULL, FALSE,
result, context))
return FALSE;
}
/* Then read the real thing (later overrides) */
for (li = info->item_dirs; li != NULL; li = li->next) {
const char *item_dir = li->data;
if ( ! vfolder_info_read_items_from (info, item_dir,
FALSE /* per_user */,
result, context))
return FALSE;
}
if (info->user_item_dir != NULL) {
if ( ! vfolder_info_read_items_from (info,
info->user_item_dir,
TRUE /* per_user */,
result, context))
return FALSE;
}
rescan_monitors (info);
return TRUE;
}
static gboolean
string_slist_equal (GSList *list1, GSList *list2)
{
GSList *li1, *li2;
for (li1 = list1, li2 = list2;
li1 != NULL && li2 != NULL;
li1 = li1->next, li2 = li2->next) {
const char *s1 = li1->data;
const char *s2 = li2->data;
if (strcmp (s1, s2) != 0)
return FALSE;
}
/* if both are not NULL, then lengths are
* different */
if (li1 != li2)
return FALSE;
return TRUE;
}
static gboolean
safe_string_same (const char *string1, const char *string2)
{
if (string1 == string2 &&
string1 == NULL)
return TRUE;
if (string1 != NULL && string2 != NULL &&
strcmp (string1, string2) == 0)
return TRUE;
return FALSE;
}
static gboolean
vfolder_info_item_dirs_same (VFolderInfo *info1, VFolderInfo *info2)
{
if ( ! string_slist_equal (info1->item_dirs,
info2->item_dirs))
return FALSE;
if ( ! string_slist_equal (info1->merge_dirs,
info2->merge_dirs))
return FALSE;
if ( ! safe_string_same (info1->user_item_dir,
info2->user_item_dir))
return FALSE;
return TRUE;
}
static gboolean
vfolder_info_reload_unlocked (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context,
gboolean force_read_items)
{
VFolderInfo *newinfo;
gboolean setup_filenames;
gboolean setup_itemdirs;
GSList *li;
/* FIXME: Hmmm, race, there is no locking YAIKES,
* we need filename locking for changes. eek, eek, eek */
if (info->dirty) {
return TRUE;
}
newinfo = g_new0 (VFolderInfo, 1);
vfolder_info_init (newinfo, info->scheme);
g_free (newinfo->filename);
g_free (newinfo->user_filename);
newinfo->filename = g_strdup (info->filename);
newinfo->user_filename = g_strdup (info->user_filename);
if (gnome_vfs_context_check_cancellation (context)) {
vfolder_info_destroy (newinfo);
*result = GNOME_VFS_ERROR_CANCELLED;
return FALSE;
}
if ( ! vfolder_info_read_info (newinfo, result, context)) {
vfolder_info_destroy (newinfo);
return FALSE;
}
/* FIXME: reload logic for 'desktop_dir' and
* 'user_desktop_dir' */
setup_itemdirs = TRUE;
/* Validity of entries and item dirs and all that is unchanged */
if (vfolder_info_item_dirs_same (info, newinfo)) {
newinfo->entries = info->entries;
info->entries = NULL;
newinfo->entries_ht = info->entries_ht;
info->entries_ht = NULL /* some places assume this
non-null, but we're only
going to destroy this */;
newinfo->entries_valid = info->entries_valid;
/* move over the monitors/statlocs since those are valid */
newinfo->item_dir_monitors = info->item_dir_monitors;
info->item_dir_monitors = NULL;
newinfo->stat_dirs = info->stat_dirs;
info->stat_dirs = NULL;
/* No need to resetup dir monitors */
setup_itemdirs = FALSE;
/* No need to do anything with file monitors */
} else {
/* Whack all monitors here! */
for (li = info->file_monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
if (h->handle != NULL)
gnome_vfs_monitor_cancel (h->handle);
h->handle = NULL;
}
}
setup_filenames = TRUE;
if (safe_string_same (info->filename, newinfo->filename) &&
safe_string_same (info->user_filename, newinfo->user_filename)) {
newinfo->user_filename_last_write =
info->user_filename_last_write;
/* move over the monitors/statlocs since those are valid */
newinfo->filename_monitor = info->filename_monitor;
info->filename_monitor = NULL;
newinfo->user_filename_monitor = info->user_filename_monitor;
info->user_filename_monitor = NULL;
if (info->filename_statloc != NULL &&
info->filename != NULL)
newinfo->filename_statloc =
bake_statloc (info->filename,
time (NULL));
if (info->user_filename_statloc != NULL &&
info->user_filename != NULL)
newinfo->user_filename_statloc =
bake_statloc (info->user_filename,
time (NULL));
/* No need to resetup filename monitors */
setup_filenames = FALSE;
}
/* Note: not cancellable anymore, since we've
* already started nibbling on the info structure,
* so we'd need to back things out or some such,
* too complex, so screw that */
monitor_setup (info,
setup_filenames,
setup_itemdirs,
/* FIXME: setup_desktop_dirs */ TRUE,
NULL, NULL);
for (li = info->folder_monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *handle = li->data;
li->data = NULL;
add_folder_monitor_unlocked (newinfo, handle);
file_monitor_handle_unref_unlocked (handle);
}
g_slist_free (info->folder_monitors);
info->folder_monitors = NULL;
g_slist_foreach (info->free_folder_monitors,
(GFunc)file_monitor_handle_unref_unlocked, NULL);
g_slist_free (info->free_folder_monitors);
info->folder_monitors = NULL;
/* we can just copy these for now, they will be readded
* and all the fun stuff will be done with them later */
newinfo->file_monitors = info->file_monitors;
info->file_monitors = NULL;
newinfo->free_file_monitors = info->free_file_monitors;
info->free_file_monitors = NULL;
/* emit changed on all folders, a bit drastic, but oh well,
* we also invalidate all folders at the same time, but that is
* irrelevant since they should all just be invalid to begin with */
invalidate_folder_T (info->root);
/* FIXME: make sure if this was enough, I think it was */
vfolder_info_free_internals_unlocked (info);
memcpy (info, newinfo, sizeof (VFolderInfo));
g_free (newinfo);
/* must rescan the monitors here */
if (info->entries_valid) {
rescan_monitors (info);
}
if ( ! info->entries_valid &&
force_read_items) {
GnomeVFSResult res;
/* FIXME: I bet cancelation plays havoc with monitors,
* I'm not sure however */
if (info->file_monitors != NULL) {
vfolder_info_read_items (info, &res, NULL);
} else {
if ( ! vfolder_info_read_items (info, result, context))
return FALSE;
}
info->entries_valid = TRUE;
}
return TRUE;
}
static gboolean
vfolder_info_reload (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context,
gboolean force_read_items)
{
G_LOCK (vfolder_lock);
if (vfolder_info_reload_unlocked (info, result, context,
force_read_items)) {
G_UNLOCK (vfolder_lock);
return TRUE;
} else {
G_UNLOCK (vfolder_lock);
return FALSE;
}
}
static gboolean
vfolder_info_recheck (VFolderInfo *info,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
GSList *li;
time_t curtime = time (NULL);
gboolean reread = FALSE;
if (info->filename_statloc != NULL &&
! check_statloc (info->filename_statloc, curtime)) {
if ( ! vfolder_info_reload_unlocked (info, result, context,
FALSE /* force read items */)) {
/* we have failed, make sure we fail
* next time too */
info->filename_statloc->trigger_next = TRUE;
return FALSE;
}
reread = TRUE;
}
if ( ! reread &&
info->user_filename_statloc != NULL &&
! check_statloc (info->user_filename_statloc, curtime)) {
if ( ! vfolder_info_reload_unlocked (info, result, context,
FALSE /* force read items */)) {
/* we have failed, make sure we fail
* next time too */
info->user_filename_statloc->trigger_next = TRUE;
return FALSE;
}
reread = TRUE;
}
if (info->entries_valid) {
for (li = info->stat_dirs; li != NULL; li = li->next) {
StatLoc *sl = li->data;
if ( ! check_statloc (sl, curtime)) {
info->entries_valid = FALSE;
break;
}
}
}
return TRUE;
}
static VFolderInfo *
get_vfolder_info_unlocked (const char *scheme,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
VFolderInfo *info;
if (infos != NULL &&
(info = g_hash_table_lookup (infos, scheme)) != NULL) {
if ( ! vfolder_info_recheck (info, result, context)) {
return NULL;
}
if ( ! info->entries_valid) {
g_slist_foreach (info->entries,
(GFunc)entry_unref, NULL);
g_slist_free (info->entries);
info->entries = NULL;
if (info->entries_ht != NULL)
g_hash_table_destroy (info->entries_ht);
info->entries_ht = g_hash_table_new (g_str_hash,
g_str_equal);
if ( ! vfolder_info_read_items (info,
result, context)) {
info->entries_valid = FALSE;
return NULL;
}
invalidate_folder_T (info->root);
info->entries_valid = TRUE;
}
return info;
}
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if (infos == NULL)
infos = g_hash_table_new_full
(g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
(GDestroyNotify)vfolder_info_destroy);
info = g_new0 (VFolderInfo, 1);
vfolder_info_init (info, scheme);
if (gnome_vfs_context_check_cancellation (context)) {
vfolder_info_destroy (info);
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if ( ! vfolder_info_read_info (info, result, context)) {
vfolder_info_destroy (info);
return NULL;
}
if ( ! monitor_setup (info,
TRUE /* setup_filenames */,
TRUE /* setup_itemdirs */,
TRUE /* setup_desktop_dirs */,
result, context)) {
vfolder_info_destroy (info);
return NULL;
}
g_hash_table_insert (infos, g_strdup (scheme), info);
if ( ! vfolder_info_read_items (info, result, context)) {
info->entries_valid = FALSE;
return NULL;
}
info->entries_valid = TRUE;
return info;
}
static VFolderInfo *
get_vfolder_info (const char *scheme,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
VFolderInfo *info;
G_LOCK (vfolder_lock);
info = get_vfolder_info_unlocked (scheme, result, context);
G_UNLOCK (vfolder_lock);
return info;
}
static char *
keywords_to_string (GSList *keywords)
{
GSList *li;
GString *str = g_string_new (NULL);
for (li = keywords; li != NULL; li = li->next) {
GQuark word = GPOINTER_TO_INT (li->data);
g_string_append (str, g_quark_to_string (word));
g_string_append_c (str, ';');
}
return g_string_free (str, FALSE);
}
/* copy file and add keywords line */
static gboolean
copy_file_with_keywords (const char *from, const char *to, GSList *keywords)
{
FILE *fp;
FILE *wfp;
int wfd;
char buf[BUFSIZ];
char *keyword_string;
if ( ! ensure_dir (to,
TRUE /* ignore_basename */))
return FALSE;
wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
if (wfd < 0) {
return FALSE;
}
keyword_string = keywords_to_string (keywords);
wfp = fdopen (wfd, "w");
fp = fopen (from, "r");
if (fp != NULL) {
gboolean wrote_keywords = FALSE;
while (fgets (buf, sizeof (buf), fp) != NULL) {
fprintf (wfp, "%s", buf);
if ( ! wrote_keywords &&
(strncmp (buf, "[Desktop Entry]",
strlen ("[Desktop Entry]")) == 0 ||
strncmp (buf, "[KDE Desktop Entry]",
strlen ("[KDE Desktop Entry]")) == 0)) {
fprintf (wfp, "Categories=%s\n",
keyword_string);
wrote_keywords = TRUE;
}
}
fclose (fp);
} else {
fprintf (wfp, "[Desktop Entry]\nCategories=%s\n",
keyword_string);
}
/* FIXME: does this close wfd???? */
fclose (wfp);
close (wfd);
g_free (keyword_string);
return TRUE;
}
static gboolean
copy_file (const char *from, const char *to)
{
int fd;
int wfd;
if ( ! ensure_dir (to,
TRUE /* ignore_basename */))
return FALSE;
wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
if (wfd < 0) {
return FALSE;
}
fd = open (from, O_RDONLY);
if (fd >= 0) {
char buf[1024];
ssize_t n;
while ((n = read (fd, buf, sizeof(buf))) > 0) {
write (wfd, buf, n);
}
close (fd);
}
close (wfd);
return TRUE;
}
static gboolean
make_file_private (VFolderInfo *info, EntryFile *efile)
{
char *newfname;
Entry *entry = (Entry *)efile;
if (efile->per_user)
return TRUE;
/* this file already exists so whack its monitors */
if (efile->filename != NULL) {
GSList *li;
for (li = entry->monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
if (h->handle != NULL)
gnome_vfs_monitor_cancel (h->handle);
h->handle = NULL;
}
}
newfname = g_build_filename (g_get_home_dir (),
DOT_GNOME,
"vfolders",
info->scheme,
efile->entry.name,
NULL);
if (efile->implicit_keywords) {
if (efile->filename != NULL &&
! copy_file_with_keywords (efile->filename,
newfname,
efile->keywords)) {
/* FIXME: what to do with monitors here, they
* have already been whacked, a corner case
* not handled! */
g_free (newfname);
return FALSE;
}
} else {
if (efile->filename != NULL &&
! copy_file (efile->filename, newfname)) {
/* FIXME: what to do with monitors here, they
* have already been whacked, a corner case
* not handled! */
g_free (newfname);
return FALSE;
}
}
/* we didn't copy but ensure path anyway */
if (efile->filename == NULL &&
! ensure_dir (newfname,
TRUE /* ignore_basename */)) {
g_free (newfname);
return FALSE;
}
/* this file already exists so re-add monitors at the new location */
if (efile->filename != NULL) {
GSList *li;
char *uri = gnome_vfs_get_uri_from_local_path (newfname);
for (li = entry->monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
gnome_vfs_monitor_add (&(h->handle),
uri,
GNOME_VFS_MONITOR_FILE,
file_monitor,
h);
}
g_free (uri);
}
g_free (efile->filename);
efile->filename = newfname;
efile->per_user = TRUE;
return TRUE;
}
static void
try_free_file_monitors_create_dirfile_unlocked (VFolderInfo *info,
Folder *folder)
{
GSList *li, *list;
list = g_slist_copy (info->free_file_monitors);
for (li = list; li != NULL; li = li->next) {
FileMonitorHandle *handle = li->data;
Folder *f;
VFolderURI vuri;
GnomeVFSResult result;
if ( ! handle->is_directory_file)
continue;
/* Evil! EVIL URI PARSING. this will eat a lot of stack if we
* have lots of free monitors */
VFOLDER_URI_PARSE (handle->uri, &vuri);
f = resolve_folder (info,
vuri.path,
TRUE /* ignore_basename */,
&result,
NULL);
if (folder != f)
continue;
info->free_file_monitors =
g_slist_remove (info->free_file_monitors, handle);
((Entry *)folder)->monitors =
g_slist_prepend (((Entry *)folder)->monitors, handle);
handle->exists = TRUE;
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_CREATED);
}
g_slist_free (list);
}
static void
make_new_dirfile (VFolderInfo *info, Folder *folder)
{
char *name = g_strdup (folder->entry.name);
char *fname;
char *p;
int i;
int fd;
for (p = name; *p != '\0'; p++) {
if ( ! ( (*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z') ||
(*p >= '0' && *p <= '9') ||
*p == '_')) {
*p = '_';
}
}
i = 0;
fname = NULL;
do {
char *fullname;
g_free (fname);
if (i > 0) {
fname = g_strdup_printf ("%s-%d.directory", name, i);
} else {
fname = g_strdup_printf ("%s.directory", name);
}
fullname = g_build_filename
(info->user_desktop_dir, fname, NULL);
fd = open (fullname, O_CREAT | O_WRONLY | O_EXCL, 0600);
g_free (fullname);
} while (fd < 0);
close (fd);
folder->desktop_file = fname;
info->dirty = TRUE;
try_free_file_monitors_create_dirfile_unlocked (info, folder);
}
static gboolean
make_dirfile_private (VFolderInfo *info, Folder *folder)
{
char *fname;
char *desktop_file;
GSList *li;
char *uri;
gboolean ret;
if (info->user_desktop_dir == NULL)
return FALSE;
if ( ! ensure_dir (info->user_desktop_dir,
FALSE /* ignore_basename */))
return FALSE;
if (folder->desktop_file == NULL) {
make_new_dirfile (info, folder);
return TRUE;
}
/* FIXME: this is broken! What if the desktop file exists
* in the local but there is a different (but with a same name)
* .directory in the system. */
fname = g_build_filename (info->user_desktop_dir,
folder->desktop_file,
NULL);
if (access (fname, F_OK) == 0) {
g_free (fname);
return TRUE;
}
desktop_file = get_directory_file (info, folder);
if (desktop_file == NULL) {
int fd = open (fname, O_CREAT | O_EXCL | O_WRONLY, 0600);
g_free (fname);
if (fd >= 0) {
close (fd);
return TRUE;
}
return FALSE;
}
for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
if (h->is_directory_file) {
if (h->handle != NULL)
gnome_vfs_monitor_cancel (h->handle);
h->handle = NULL;
}
}
ret = TRUE;
if ( ! copy_file (desktop_file, fname)) {
ret = FALSE;
g_free (fname);
fname = desktop_file;
desktop_file = NULL;
}
uri = gnome_vfs_get_uri_from_local_path (fname);
for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
if (h->is_directory_file) {
gnome_vfs_monitor_add (&(h->handle),
uri,
GNOME_VFS_MONITOR_FILE,
file_monitor,
h);
}
}
g_free (uri);
g_free (desktop_file);
g_free (fname);
return ret;
}
static Folder *
resolve_folder (VFolderInfo *info,
const char *path,
gboolean ignore_basename,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
char **ppath;
int i;
Folder *folder = info->root;
ppath = g_strsplit (path, "/", -1);
if (ppath == NULL ||
ppath[0] == NULL) {
g_strfreev (ppath);
*result = GNOME_VFS_ERROR_INVALID_URI;
return NULL;
}
for (i = 0; ppath [i] != NULL; i++) {
const char *segment = ppath[i];
if (*segment == '\0')
continue;
if (ignore_basename && ppath [i + 1] == NULL)
break;
else {
folder = (Folder *) find_entry (folder->subfolders,
segment);
if (folder == NULL)
break;
}
}
g_strfreev (ppath);
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if (folder == NULL)
*result = GNOME_VFS_ERROR_NOT_FOUND;
return folder;
}
static Entry *
resolve_path (VFolderInfo *info,
const char *path,
const char *basename,
Folder **return_folder,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
Entry *entry;
Folder *folder;
if (strcmp (path, "/") == 0)
return (Entry *)info->root;
folder = resolve_folder (info, path,
TRUE /* ignore_basename */,
result, context);
if (return_folder != NULL)
*return_folder = folder;
if (folder == NULL) {
return NULL;
}
/* Make sure we have the entries here */
ensure_folder_unlocked (info, folder,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
entry = find_entry (folder->entries, basename);
if (entry == NULL)
*result = GNOME_VFS_ERROR_NOT_FOUND;
return entry;
}
static Entry *
get_entry_unlocked (VFolderURI *vuri,
Folder **parent,
gboolean *is_directory_file,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
VFolderInfo *info;
Entry *entry;
if (is_directory_file != NULL)
*is_directory_file = FALSE;
if (parent != NULL)
*parent = NULL;
info = get_vfolder_info_unlocked (vuri->scheme, result, context);
if (info == NULL)
return NULL;
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if (vuri->is_all_scheme) {
GSList *efile_list;
if (vuri->file == NULL) {
entry = resolve_path (info,
vuri->path,
vuri->file,
parent,
result,
context);
return entry;
}
efile_list = g_hash_table_lookup (info->entries_ht, vuri->file);
if (efile_list == NULL) {
*result = GNOME_VFS_ERROR_NOT_FOUND;
return NULL;
} else {
return efile_list->data;
}
}
if (vuri->file != NULL &&
check_ext (vuri->file, ".directory") == TRUE) {
Folder *folder;
folder = resolve_folder (info, vuri->path,
TRUE /* ignore_basename */,
result, context);
if (folder == NULL) {
return NULL;
}
if (is_directory_file != NULL)
*is_directory_file = TRUE;
if (parent != NULL)
*parent = folder;
return (Entry *)folder;
} else {
entry = resolve_path (info, vuri->path, vuri->file, parent,
result, context);
return entry;
}
}
static Entry *
get_entry (VFolderURI *vuri,
Folder **parent,
gboolean *is_directory_file,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
Entry *entry;
G_LOCK (vfolder_lock);
entry = get_entry_unlocked (vuri,
parent,
is_directory_file,
result, context);
G_UNLOCK (vfolder_lock);
return entry;
}
/* only works for files and only those that exist */
/* unlocked function */
static GnomeVFSURI *
desktop_uri_to_file_uri (VFolderInfo *info,
VFolderURI *desktop_vuri,
Entry **the_entry,
gboolean *the_is_directory_file,
Folder **the_folder,
gboolean privatize,
GnomeVFSResult *result,
GnomeVFSContext *context)
{
gboolean is_directory_file;
GnomeVFSURI *ret_uri;
Folder *folder = NULL;
Entry *entry;
entry = get_entry_unlocked (desktop_vuri,
&folder,
&is_directory_file,
result,
context);
if (entry == NULL)
return NULL;
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if (the_folder != NULL)
*the_folder = folder;
if (the_entry != NULL)
*the_entry = entry;
if (the_is_directory_file != NULL)
*the_is_directory_file = is_directory_file;
if (is_directory_file &&
entry->type == ENTRY_FOLDER) {
char *desktop_file;
folder = (Folder *)entry;
if (the_folder != NULL)
*the_folder = folder;
/* we'll be doing something write like */
if (folder->read_only &&
privatize) {
*result = GNOME_VFS_ERROR_READ_ONLY;
return NULL;
}
if (privatize) {
char *fname;
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if ( ! make_dirfile_private (info, folder)) {
*result = GNOME_VFS_ERROR_GENERIC;
return NULL;
}
fname = g_build_filename (g_get_home_dir (),
folder->desktop_file,
NULL);
ret_uri = gnome_vfs_uri_new (fname);
g_free (fname);
return ret_uri;
}
desktop_file = get_directory_file_unlocked (info, folder);
if (desktop_file != NULL) {
char *s = gnome_vfs_get_uri_from_local_path
(desktop_file);
g_free (desktop_file);
ret_uri = gnome_vfs_uri_new (s);
g_free (s);
return ret_uri;
} else {
*result = GNOME_VFS_ERROR_NOT_FOUND;
return NULL;
}
} else if (entry->type == ENTRY_FILE) {
EntryFile *efile = (EntryFile *)entry;
char *s;
/* we'll be doing something write like */
if (folder != NULL &&
folder->read_only &&
privatize) {
*result = GNOME_VFS_ERROR_READ_ONLY;
return NULL;
}
if (gnome_vfs_context_check_cancellation (context)) {
*result = GNOME_VFS_ERROR_CANCELLED;
return NULL;
}
if (privatize &&
! make_file_private (info, efile)) {
*result = GNOME_VFS_ERROR_GENERIC;
return NULL;
}
s = gnome_vfs_get_uri_from_local_path (efile->filename);
ret_uri = gnome_vfs_uri_new (s);
g_free (s);
return ret_uri;
} else {
if (the_folder != NULL)
*the_folder = (Folder *)entry;
*result = GNOME_VFS_ERROR_IS_DIRECTORY;
return NULL;
}
}
static void
remove_file (Folder *folder, const char *basename)
{
GSList *li;
char *s;
if (folder->includes_ht != NULL) {
li = g_hash_table_lookup (folder->includes_ht, basename);
if (li != NULL) {
char *name = li->data;
folder->includes = g_slist_delete_link
(folder->includes, li);
g_hash_table_remove (folder->includes_ht, basename);
g_free (name);
}
}
if (folder->excludes == NULL) {
folder->excludes = g_hash_table_new_full
(g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
NULL);
}
s = g_strdup (basename);
g_hash_table_replace (folder->excludes, s, s);
}
static void
add_file (Folder *folder, const char *basename)
{
GSList *li = NULL;
if (folder->includes_ht != NULL) {
li = g_hash_table_lookup (folder->includes_ht, basename);
}
/* if not found */
if (li == NULL) {
char *str = g_strdup (basename);
folder->includes =
g_slist_prepend (folder->includes, str);
if (folder->includes_ht == NULL) {
folder->includes_ht =
g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
NULL);
}
g_hash_table_replace (folder->includes_ht,
str, folder->includes);
}
if (folder->excludes != NULL)
g_hash_table_remove (folder->excludes, basename);
}
typedef struct _FileHandle FileHandle;
struct _FileHandle {
VFolderInfo *info;
GnomeVFSMethodHandle *handle;
Entry *entry;
gboolean write;
gboolean is_directory_file;
};
static void
make_handle (GnomeVFSMethodHandle **method_handle,
GnomeVFSMethodHandle *file_handle,
VFolderInfo *info,
Entry *entry,
gboolean is_directory_file,
gboolean write)
{
if (file_handle != NULL) {
FileHandle *handle = g_new0 (FileHandle, 1);
handle->info = info;
handle->handle = file_handle;
handle->entry = entry_ref (entry);
handle->is_directory_file = is_directory_file;
handle->write = write;
*method_handle = (GnomeVFSMethodHandle *) handle;
} else {
*method_handle = NULL;
}
}
static void
whack_handle (FileHandle *handle)
{
entry_unref (handle->entry);
handle->entry = NULL;
handle->handle = NULL;
handle->info = NULL;
g_free (handle);
}
static GnomeVFSResult
do_open (GnomeVFSMethod *method,
GnomeVFSMethodHandle **method_handle,
GnomeVFSURI *uri,
GnomeVFSOpenMode mode,
GnomeVFSContext *context)
{
GnomeVFSURI *file_uri;
GnomeVFSResult result = GNOME_VFS_OK;
VFolderInfo *info;
Entry *entry;
gboolean is_directory_file;
GnomeVFSMethodHandle *file_handle = NULL;
VFolderURI vuri;
VFOLDER_URI_PARSE (uri, &vuri);
/* These can't be very nice FILE names */
if (vuri.file == NULL ||
vuri.ends_in_slash)
return GNOME_VFS_ERROR_INVALID_URI;
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
if (mode & GNOME_VFS_OPEN_WRITE &&
(info->read_only || vuri.is_all_scheme))
return GNOME_VFS_ERROR_READ_ONLY;
G_LOCK (vfolder_lock);
file_uri = desktop_uri_to_file_uri (info,
&vuri,
&entry,
&is_directory_file,
NULL /* the_folder */,
mode & GNOME_VFS_OPEN_WRITE,
&result,
context);
if (file_uri == NULL) {
G_UNLOCK (vfolder_lock);
return result;
}
result = (* parent_method->open) (parent_method,
&file_handle,
file_uri,
mode,
context);
if (result == GNOME_VFS_ERROR_CANCELLED) {
G_UNLOCK (vfolder_lock);
gnome_vfs_uri_unref (file_uri);
return result;
}
make_handle (method_handle,
file_handle,
info,
entry,
is_directory_file,
mode & GNOME_VFS_OPEN_WRITE);
gnome_vfs_uri_unref (file_uri);
if (info->dirty) {
vfolder_info_write_user (info);
}
G_UNLOCK (vfolder_lock);
return result;
}
static void
remove_from_all_except (Folder *root,
const char *name,
Folder *except)
{
GSList *li;
if (root != except) {
remove_file (root, name);
if (root->up_to_date) {
for (li = root->entries; li != NULL; li = li->next) {
Entry *entry = li->data;
if (strcmp (name, entry->name) == 0) {
root->entries =
g_slist_delete_link
(root->entries, li);
break;
}
}
}
}
for (li = root->subfolders; li != NULL; li = li->next) {
Folder *subfolder = li->data;
remove_from_all_except (subfolder, name, except);
}
}
static GnomeVFSResult
do_create (GnomeVFSMethod *method,
GnomeVFSMethodHandle **method_handle,
GnomeVFSURI *uri,
GnomeVFSOpenMode mode,
gboolean exclusive,
guint perm,
GnomeVFSContext *context)
{
GnomeVFSResult result = GNOME_VFS_OK;
GnomeVFSMethodHandle *file_handle;
GnomeVFSURI *file_uri;
VFolderURI vuri;
VFolderInfo *info;
Folder *parent;
Entry *entry;
EntryFile *efile;
char *s;
GSList *li;
VFOLDER_URI_PARSE (uri, &vuri);
/* These can't be very nice FILE names */
if (vuri.file == NULL ||
vuri.ends_in_slash)
return GNOME_VFS_ERROR_INVALID_URI;
if ( ! check_ext (vuri.file, ".desktop") &&
! strcmp (vuri.file, ".directory") == 0) {
return GNOME_VFS_ERROR_INVALID_URI;
}
/* all scheme is read only */
if (vuri.is_all_scheme)
return GNOME_VFS_ERROR_READ_ONLY;
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
if (info->user_filename == NULL ||
info->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
parent = resolve_folder (info, vuri.path,
TRUE /* ignore_basename */,
&result, context);
if (parent == NULL)
return result;
if (parent->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
if (strcmp (vuri.file, ".directory") == 0) {
char *fname;
G_LOCK (vfolder_lock);
if (exclusive) {
char *desktop_file;
desktop_file = get_directory_file_unlocked (info, parent);
if (desktop_file != NULL) {
g_free (desktop_file);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_FILE_EXISTS;
}
}
if ( ! make_dirfile_private (info, parent)) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_GENERIC;
}
fname = g_build_filename (g_get_home_dir (),
parent->desktop_file,
NULL);
s = gnome_vfs_get_uri_from_local_path (fname);
file_uri = gnome_vfs_uri_new (s);
g_free (fname);
g_free (s);
if (file_uri == NULL) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_GENERIC;
}
result = (* parent_method->create) (parent_method,
&file_handle,
file_uri,
mode,
exclusive,
perm,
context);
gnome_vfs_uri_unref (file_uri);
make_handle (method_handle,
file_handle,
info,
(Entry *)parent,
TRUE /* is_directory_file */,
TRUE /* write */);
if (info->dirty)
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return result;
}
ensure_folder (info, parent,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
entry = find_entry (parent->entries, vuri.file);
if (entry != NULL &&
entry->type == ENTRY_FOLDER)
return GNOME_VFS_ERROR_IS_DIRECTORY;
efile = (EntryFile *)entry;
if (efile != NULL) {
if (exclusive)
return GNOME_VFS_ERROR_FILE_EXISTS;
G_LOCK (vfolder_lock);
if ( ! make_file_private (info, efile)) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_GENERIC;
}
s = gnome_vfs_get_uri_from_local_path (efile->filename);
file_uri = gnome_vfs_uri_new (s);
g_free (s);
if (file_uri == NULL) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_GENERIC;
}
result = (* parent_method->create) (parent_method,
&file_handle,
file_uri,
mode,
exclusive,
perm,
context);
gnome_vfs_uri_unref (file_uri);
make_handle (method_handle,
file_handle,
info,
(Entry *)efile,
FALSE /* is_directory_file */,
TRUE /* write */);
G_UNLOCK (vfolder_lock);
return result;
}
G_LOCK (vfolder_lock);
li = g_hash_table_lookup (info->entries_ht, vuri.file);
if (exclusive && li != NULL) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_FILE_EXISTS;
}
if (li == NULL) {
efile = file_new (vuri.file);
vfolder_info_insert_entry (info, efile);
entry_unref ((Entry *)efile);
} else {
efile = li->data;
}
/* this will make a private name for this */
if ( ! make_file_private (info, efile)) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_GENERIC;
}
add_file (parent, vuri.file);
parent->sorted = FALSE;
if (parent->up_to_date)
parent->entries = g_slist_prepend (parent->entries, efile);
/* if we created a brand new name, then we exclude it
* from everywhere else to ensure overall sanity */
if (li == NULL)
remove_from_all_except (info->root, vuri.file, parent);
s = gnome_vfs_get_uri_from_local_path (efile->filename);
file_uri = gnome_vfs_uri_new (s);
g_free (s);
result = (* parent_method->create) (parent_method,
&file_handle,
file_uri,
mode,
exclusive,
perm,
context);
gnome_vfs_uri_unref (file_uri);
make_handle (method_handle,
file_handle,
info,
(Entry *)efile,
FALSE /* is_directory_file */,
TRUE /* write */);
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return result;
}
static GnomeVFSResult
do_close (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSContext *context)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
if (method_handle == (GnomeVFSMethodHandle *)method)
return GNOME_VFS_OK;
G_LOCK (vfolder_lock);
result = (* parent_method->close) (parent_method,
handle->handle,
context);
handle->handle = NULL;
/* we reread the Categories keyword */
if (handle->write &&
handle->entry != NULL &&
handle->entry->type == ENTRY_FILE) {
EntryFile *efile = (EntryFile *)handle->entry;
char *categories;
readitem_entry (efile->filename,
"Categories",
&categories,
NULL,
NULL);
set_keywords (efile, categories);
g_free (categories);
/* FIXME: what about OnlyShowIn */
/* FIXME: check if the keywords changed, if not, do
* nothing */
/* Perhaps a bit drastic */
/* also this emits the CHANGED monitor signal */
invalidate_folder_T (handle->info->root);
/* the file changed monitor will happen by itself
* as the underlying file is changed */
} else if (handle->write &&
handle->entry != NULL &&
handle->entry->type == ENTRY_FOLDER &&
handle->is_directory_file) {
/* if we're monitoring this directory, emit the CHANGED
* monitor thing, it will also emit a changed on
* the file itself. It is better to emit changed
* just in case. */
emit_monitor ((Folder *)(handle->entry),
GNOME_VFS_MONITOR_EVENT_CHANGED);
}
whack_handle (handle);
G_UNLOCK (vfolder_lock);
return result;
}
static void
fill_buffer (gpointer buffer,
GnomeVFSFileSize num_bytes,
GnomeVFSFileSize *bytes_read)
{
char *buf = buffer;
GnomeVFSFileSize i;
for (i = 0; i < num_bytes; i++) {
if (rand () % 32 == 0 ||
i == num_bytes-1)
buf[i] = '\n';
else
buf[i] = ((rand()>>4) % 94) + 32;
}
if (bytes_read != 0)
*bytes_read = i;
}
static GnomeVFSResult
do_read (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
gpointer buffer,
GnomeVFSFileSize num_bytes,
GnomeVFSFileSize *bytes_read,
GnomeVFSContext *context)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
if (method_handle == (GnomeVFSMethodHandle *)method) {
if ((rand () >> 4) & 0x3) {
fill_buffer (buffer, num_bytes, bytes_read);
return GNOME_VFS_OK;
} else {
return GNOME_VFS_ERROR_EOF;
}
}
result = (* parent_method->read) (parent_method,
handle->handle,
buffer, num_bytes,
bytes_read,
context);
return result;
}
static GnomeVFSResult
do_write (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
gconstpointer buffer,
GnomeVFSFileSize num_bytes,
GnomeVFSFileSize *bytes_written,
GnomeVFSContext *context)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
if (method_handle == (GnomeVFSMethodHandle *)method)
return GNOME_VFS_OK;
result = (* parent_method->write) (parent_method,
handle->handle,
buffer, num_bytes,
bytes_written,
context);
return result;
}
static GnomeVFSResult
do_seek (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSSeekPosition whence,
GnomeVFSFileOffset offset,
GnomeVFSContext *context)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
if (method_handle == (GnomeVFSMethodHandle *)method)
return GNOME_VFS_OK;
result = (* parent_method->seek) (parent_method,
handle->handle,
whence, offset,
context);
return result;
}
static GnomeVFSResult
do_tell (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSFileOffset *offset_return)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
result = (* parent_method->tell) (parent_method,
handle->handle,
offset_return);
return result;
}
static GnomeVFSResult
do_truncate_handle (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSFileSize where,
GnomeVFSContext *context)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
if (method_handle == (GnomeVFSMethodHandle *)method)
return GNOME_VFS_OK;
result = (* parent_method->truncate_handle) (parent_method,
handle->handle,
where,
context);
return result;
}
static GnomeVFSResult
do_truncate (GnomeVFSMethod *method,
GnomeVFSURI *uri,
GnomeVFSFileSize where,
GnomeVFSContext *context)
{
GnomeVFSURI *file_uri;
GnomeVFSResult result = GNOME_VFS_OK;
VFolderInfo *info;
Entry *entry;
VFolderURI vuri;
VFOLDER_URI_PARSE (uri, &vuri);
/* These can't be very nice FILE names */
if (vuri.file == NULL ||
vuri.ends_in_slash)
return GNOME_VFS_ERROR_INVALID_URI;
if (vuri.is_all_scheme)
return GNOME_VFS_ERROR_READ_ONLY;
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
if (info->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
G_LOCK (vfolder_lock);
file_uri = desktop_uri_to_file_uri (info,
&vuri,
&entry,
NULL /* the_is_directory_file */,
NULL /* the_folder */,
TRUE /* privatize */,
&result,
context);
G_UNLOCK (vfolder_lock);
if (file_uri == NULL)
return result;
result = (* parent_method->truncate) (parent_method,
file_uri,
where,
context);
gnome_vfs_uri_unref (file_uri);
if (info->dirty) {
G_LOCK (vfolder_lock);
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
}
if (entry->type == ENTRY_FILE) {
EntryFile *efile = (EntryFile *)entry;
G_LOCK (vfolder_lock);
g_slist_free (efile->keywords);
efile->keywords = NULL;
G_UNLOCK (vfolder_lock);
}
/* Perhaps a bit drastic, but oh well */
invalidate_folder (info->root);
return result;
}
typedef struct _DirHandle DirHandle;
struct _DirHandle {
VFolderInfo *info;
Folder *folder;
GnomeVFSFileInfoOptions options;
/* List of Entries */
GSList *list;
GSList *current;
};
static GnomeVFSResult
do_open_directory (GnomeVFSMethod *method,
GnomeVFSMethodHandle **method_handle,
GnomeVFSURI *uri,
GnomeVFSFileInfoOptions options,
GnomeVFSContext *context)
{
GnomeVFSResult result = GNOME_VFS_OK;
VFolderURI vuri;
DirHandle *dh;
Folder *folder;
VFolderInfo *info;
char *desktop_file;
VFOLDER_URI_PARSE (uri, &vuri);
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
/* In the all- scheme just list all filenames */
if (vuri.is_all_scheme) {
if (any_subdir (vuri.path))
return GNOME_VFS_ERROR_NOT_FOUND;
dh = g_new0 (DirHandle, 1);
dh->info = info;
dh->options = options;
dh->folder = NULL;
G_LOCK (vfolder_lock);
dh->list = g_slist_copy (info->entries);
g_slist_foreach (dh->list, (GFunc)entry_ref, NULL);
dh->current = dh->list;
G_UNLOCK (vfolder_lock);
*method_handle = (GnomeVFSMethodHandle*) dh;
return GNOME_VFS_OK;
}
folder = resolve_folder (info, vuri.path,
FALSE /* ignore_basename */,
&result, context);
if (folder == NULL)
return result;
/* Make sure we have the entries and sorted here */
ensure_folder_sort (info, folder);
dh = g_new0 (DirHandle, 1);
dh->info = info;
dh->options = options;
G_LOCK (vfolder_lock);
dh->folder = (Folder *)entry_ref ((Entry *)folder);
dh->list = g_slist_copy (folder->entries);
g_slist_foreach (folder->entries, (GFunc)entry_ref, NULL);
G_UNLOCK (vfolder_lock);
desktop_file = get_directory_file (info, folder);
if (desktop_file != NULL) {
EntryFile *efile = file_new (".directory");
dh->list = g_slist_prepend (dh->list, efile);
g_free (desktop_file);
}
dh->current = dh->list;
*method_handle = (GnomeVFSMethodHandle*) dh;
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_close_directory (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSContext *context)
{
DirHandle *dh;
dh = (DirHandle*) method_handle;
G_LOCK (vfolder_lock);
g_slist_foreach (dh->list, (GFunc)entry_unref, NULL);
g_slist_free (dh->list);
dh->list = NULL;
dh->current = NULL;
if (dh->folder != NULL)
entry_unref ((Entry *)dh->folder);
dh->folder = NULL;
dh->info = NULL;
g_free (dh);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_read_directory (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSFileInfo *file_info,
GnomeVFSContext *context)
{
DirHandle *dh;
Entry *entry;
GnomeVFSFileInfoOptions options;
dh = (DirHandle*) method_handle;
read_directory_again:
if (dh->current == NULL) {
return GNOME_VFS_ERROR_EOF;
}
entry = dh->current->data;
dh->current = dh->current->next;
options = dh->options;
if (entry->type == ENTRY_FILE &&
((EntryFile *)entry)->filename != NULL) {
EntryFile *efile = (EntryFile *)entry;
char *furi = gnome_vfs_get_uri_from_local_path (efile->filename);
GnomeVFSURI *uri = gnome_vfs_uri_new (furi);
/* we always get mime-type by forcing it below */
if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
/* Get the file info for this */
(* parent_method->get_file_info) (parent_method,
uri,
file_info,
options,
context);
/* we ignore errors from this since the file_info just
* won't be filled completely if there's an error, that's all */
g_free (file_info->mime_type);
file_info->mime_type = g_strdup ("application/x-gnome-app-info");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
/* Now we wipe those fields we don't support */
file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
gnome_vfs_uri_unref (uri);
g_free (furi);
} else if (entry->type == ENTRY_FILE) {
file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
file_info->name = g_strdup (entry->name);
GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
/* FIXME: Is this correct? isn't there an xdg mime type? */
file_info->mime_type = g_strdup ("application/x-gnome-app-info");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
/* FIXME: get some ctime/mtime */
} else /* ENTRY_FOLDER */ {
Folder *folder = (Folder *)entry;
/* Skip empty folders if they have
* the flag set */
if (folder->dont_show_if_empty) {
/* Make sure we have the entries */
ensure_folder (dh->info, folder,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
if (folder->entries == NULL) {
/* start this function over on the
* next item */
goto read_directory_again;
}
}
file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
file_info->name = g_strdup (entry->name);
GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
file_info->ctime = dh->info->modification_time;
file_info->mtime = dh->info->modification_time;
file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
GNOME_VFS_FILE_INFO_FIELDS_MTIME);
}
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_get_file_info (GnomeVFSMethod *method,
GnomeVFSURI *uri,
GnomeVFSFileInfo *file_info,
GnomeVFSFileInfoOptions options,
GnomeVFSContext *context)
{
GnomeVFSURI *file_uri;
GnomeVFSResult result = GNOME_VFS_OK;
Folder *folder;
VFolderInfo *info;
VFolderURI vuri;
VFOLDER_URI_PARSE (uri, &vuri);
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
G_LOCK (vfolder_lock);
file_uri = desktop_uri_to_file_uri (info,
&vuri,
NULL /* the_entry */,
NULL /* the_is_directory_file */,
&folder,
FALSE /* privatize */,
&result,
context);
G_UNLOCK (vfolder_lock);
if (file_uri == NULL &&
result != GNOME_VFS_ERROR_IS_DIRECTORY)
return result;
if (file_uri != NULL) {
/* we always get mime-type by forcing it below */
if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
result = (* parent_method->get_file_info) (parent_method,
file_uri,
file_info,
options,
context);
g_free (file_info->mime_type);
file_info->mime_type = g_strdup ("application/x-gnome-app-info");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
/* Now we wipe those fields we don't support */
file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
gnome_vfs_uri_unref (file_uri);
return result;
} else if (folder != NULL) {
file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
file_info->name = g_strdup (folder->entry.name);
GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
file_info->ctime = info->modification_time;
file_info->mtime = info->modification_time;
file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
GNOME_VFS_FILE_INFO_FIELDS_MTIME);
return GNOME_VFS_OK;
} else {
return GNOME_VFS_ERROR_NOT_FOUND;
}
}
static GnomeVFSResult
do_get_file_info_from_handle (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle,
GnomeVFSFileInfo *file_info,
GnomeVFSFileInfoOptions options,
GnomeVFSContext *context)
{
GnomeVFSResult result;
FileHandle *handle = (FileHandle *)method_handle;
if (method_handle == (GnomeVFSMethodHandle *)method) {
g_free (file_info->mime_type);
file_info->mime_type = g_strdup ("text/plain");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
return GNOME_VFS_OK;
}
/* we always get mime-type by forcing it below */
if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
result = (* parent_method->get_file_info_from_handle) (parent_method,
handle->handle,
file_info,
options,
context);
/* any file is of the .desktop type */
g_free (file_info->mime_type);
file_info->mime_type = g_strdup ("application/x-gnome-app-info");
file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
/* Now we wipe those fields we don't support */
file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
return result;
}
static gboolean
do_is_local (GnomeVFSMethod *method,
const GnomeVFSURI *uri)
{
return TRUE;
}
static void
try_free_folder_monitors_create_unlocked (VFolderInfo *info,
Folder *folder)
{
GSList *li, *list;
list = g_slist_copy (info->free_folder_monitors);
for (li = list; li != NULL; li = li->next) {
FileMonitorHandle *handle = li->data;
Folder *f;
VFolderURI vuri;
GnomeVFSResult result;
/* Evil! EVIL URI PARSING. this will eat a lot of stack if we
* have lots of free monitors */
VFOLDER_URI_PARSE (handle->uri, &vuri);
f = resolve_folder (info,
vuri.path,
FALSE /* ignore_basename */,
&result,
NULL);
if (folder != f)
continue;
info->free_folder_monitors =
g_slist_remove (info->free_folder_monitors, handle);
((Entry *)folder)->monitors =
g_slist_prepend (((Entry *)folder)->monitors, handle);
handle->exists = TRUE;
gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
handle->uri,
GNOME_VFS_MONITOR_EVENT_CREATED);
}
}
static GnomeVFSResult
do_make_directory (GnomeVFSMethod *method,
GnomeVFSURI *uri,
guint perm,
GnomeVFSContext *context)
{
GnomeVFSResult result = GNOME_VFS_OK;
VFolderInfo *info;
Folder *parent, *folder;
VFolderURI vuri;
VFOLDER_URI_PARSE (uri, &vuri);
if (vuri.is_all_scheme)
return GNOME_VFS_ERROR_READ_ONLY;
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
if (info->user_filename == NULL ||
info->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
parent = resolve_folder (info, vuri.path,
TRUE /* ignore_basename */,
&result, context);
if (parent == NULL)
return result;
else if (parent->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
G_LOCK (vfolder_lock);
folder = (Folder *)find_entry (parent->subfolders,
vuri.file);
if (folder != NULL) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_FILE_EXISTS;
}
folder = folder_new (vuri.file);
parent->subfolders = g_slist_append (parent->subfolders, folder);
folder->parent = parent;
parent->up_to_date = FALSE;
try_free_folder_monitors_create_unlocked (info, folder);
/* parent changed */
emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_remove_directory (GnomeVFSMethod *method,
GnomeVFSURI *uri,
GnomeVFSContext *context)
{
GnomeVFSResult result = GNOME_VFS_OK;
Folder *folder;
VFolderInfo *info;
VFolderURI vuri;
VFOLDER_URI_PARSE (uri, &vuri);
if (vuri.is_all_scheme)
return GNOME_VFS_ERROR_READ_ONLY;
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
if (info->user_filename == NULL ||
info->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
G_LOCK (vfolder_lock);
folder = resolve_folder (info, vuri.path,
FALSE /* ignore_basename */,
&result, context);
if (folder == NULL) {
G_UNLOCK (vfolder_lock);
return result;
}
if (folder->read_only ||
(folder->parent != NULL &&
folder->parent->read_only)) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_READ_ONLY;
}
/* don't make removing directories easy */
if (folder->desktop_file != NULL) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
}
/* Make sure we have the entries */
ensure_folder_unlocked (info, folder,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
/* don't make removing directories easy */
if (folder->entries != NULL) {
G_UNLOCK (vfolder_lock);
return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
}
emit_and_delete_monitor (info, folder);
if (folder->only_unallocated) {
GSList *li = g_slist_find (info->unallocated_folders,
folder);
if (li != NULL) {
info->unallocated_folders = g_slist_delete_link
(info->unallocated_folders, li);
entry_unref ((Entry *)folder);
}
}
if (folder == info->root) {
info->root = NULL;
entry_unref ((Entry *)folder);
info->root = folder_new ("Root");
} else {
Folder *parent = folder->parent;
g_assert (parent != NULL);
parent->subfolders =
g_slist_remove (parent->subfolders, folder);
parent->up_to_date = FALSE;
entry_unref ((Entry *)folder);
/* parent changed */
emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
}
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
/* a fairly evil function that does the whole move bit by copy and
* remove */
static GnomeVFSResult
long_move (GnomeVFSMethod *method,
VFolderURI *old_vuri,
VFolderURI *new_vuri,
gboolean force_replace,
GnomeVFSContext *context)
{
GnomeVFSResult result;
GnomeVFSMethodHandle *handle;
GnomeVFSURI *file_uri;
const char *path;
int fd;
char buf[BUFSIZ];
int bytes;
VFolderInfo *info;
info = get_vfolder_info (old_vuri->scheme, &result, context);
if (info == NULL)
return result;
G_LOCK (vfolder_lock);
file_uri = desktop_uri_to_file_uri (info,
old_vuri,
NULL /* the_entry */,
NULL /* the_is_directory_file */,
NULL /* the_folder */,
FALSE /* privatize */,
&result,
context);
G_UNLOCK (vfolder_lock);
if (file_uri == NULL)
return result;
path = gnome_vfs_uri_get_path (file_uri);
if (path == NULL) {
gnome_vfs_uri_unref (file_uri);
return GNOME_VFS_ERROR_INVALID_URI;
}
fd = open (path, O_RDONLY);
if (fd < 0) {
gnome_vfs_uri_unref (file_uri);
return gnome_vfs_result_from_errno ();
}
gnome_vfs_uri_unref (file_uri);
info->inhibit_write++;
result = method->create (method,
&handle,
new_vuri->uri,
GNOME_VFS_OPEN_WRITE,
force_replace /* exclusive */,
0600 /* perm */,
context);
if (result != GNOME_VFS_OK) {
close (fd);
info->inhibit_write--;
return result;
}
while ((bytes = read (fd, buf, BUFSIZ)) > 0) {
GnomeVFSFileSize bytes_written = 0;
result = method->write (method,
handle,
buf,
bytes,
&bytes_written,
context);
if (result == GNOME_VFS_OK &&
bytes_written != bytes)
result = GNOME_VFS_ERROR_NO_SPACE;
if (result != GNOME_VFS_OK) {
close (fd);
method->close (method, handle, context);
/* FIXME: is this completely correct ? */
method->unlink (method,
new_vuri->uri,
context);
G_LOCK (vfolder_lock);
info->inhibit_write--;
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return result;
}
}
close (fd);
result = method->close (method, handle, context);
if (result != GNOME_VFS_OK) {
G_LOCK (vfolder_lock);
info->inhibit_write--;
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return result;
}
result = method->unlink (method, old_vuri->uri, context);
G_LOCK (vfolder_lock);
info->inhibit_write--;
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return result;
}
static GnomeVFSResult
move_directory_file (VFolderInfo *info,
Folder *old_folder,
Folder *new_folder)
{
if (old_folder->desktop_file == NULL)
return GNOME_VFS_ERROR_NOT_FOUND;
/* "move" the desktop file */
g_free (new_folder->desktop_file);
new_folder->desktop_file = old_folder->desktop_file;
old_folder->desktop_file = NULL;
/* is this too drastic, it will requery the folder? */
new_folder->up_to_date = FALSE;
old_folder->up_to_date = FALSE;
emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
vfolder_info_write_user (info);
return GNOME_VFS_OK;
}
static gboolean
is_sub (Folder *master, Folder *sub)
{
GSList *li;
for (li = master->subfolders; li != NULL; li = li->next) {
Folder *subfolder = li->data;
if (subfolder == sub ||
is_sub (subfolder, sub))
return TRUE;
}
return FALSE;
}
static GnomeVFSResult
move_folder (VFolderInfo *info,
Folder *old_folder, Entry *old_entry,
Folder *new_folder, Entry *new_entry)
{
Folder *source = (Folder *)old_entry;
Folder *target;
if (new_entry != NULL &&
new_entry->type != ENTRY_FOLDER)
return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
if (new_entry != NULL) {
target = (Folder *)new_entry;
} else {
target = new_folder;
}
/* move to where we are, yay, we're done :) */
if (source->parent == target)
return GNOME_VFS_OK;
if (source == target ||
is_sub (source, target))
return GNOME_VFS_ERROR_LOOP;
/* this will never happen, but we're paranoid */
if (source->parent == NULL)
return GNOME_VFS_ERROR_LOOP;
source->parent->subfolders = g_slist_remove (source->parent->subfolders,
source);
target->subfolders = g_slist_append (target->subfolders,
source);
source->parent = target;
source->up_to_date = FALSE;
target->up_to_date = FALSE;
emit_monitor (source, GNOME_VFS_MONITOR_EVENT_CHANGED);
emit_monitor (target, GNOME_VFS_MONITOR_EVENT_CHANGED);
vfolder_info_write_user (info);
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_move (GnomeVFSMethod *method,
GnomeVFSURI *old_uri,
GnomeVFSURI *new_uri,
gboolean force_replace,
GnomeVFSContext *context)
{
GnomeVFSResult result = GNOME_VFS_OK;
VFolderInfo *info;
Folder *old_folder, *new_folder;
Entry *old_entry, *new_entry;
gboolean old_is_directory_file, new_is_directory_file;
VFolderURI old_vuri, new_vuri;
VFOLDER_URI_PARSE (old_uri, &old_vuri);
VFOLDER_URI_PARSE (new_uri, &new_vuri);
if (old_vuri.file == NULL)
return GNOME_VFS_ERROR_INVALID_URI;
if (old_vuri.is_all_scheme)
return GNOME_VFS_ERROR_READ_ONLY;
if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
info = get_vfolder_info (old_vuri.scheme, &result, context);
if (info == NULL)
return result;
if (info->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
old_entry = get_entry (&old_vuri,
&old_folder,
&old_is_directory_file,
&result,
context);
if (old_entry == NULL)
return result;
if (old_folder != NULL && old_folder->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
new_entry = get_entry (&new_vuri,
&new_folder,
&new_is_directory_file,
&result,
context);
if (new_entry == NULL && new_folder == NULL)
return result;
if (new_folder != NULL && new_folder->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
if (new_is_directory_file != old_is_directory_file) {
/* this will do another set of lookups
* perhaps this can be done in a nicer way,
* but is this the common case? I don't think so */
return long_move (method, &old_vuri, &new_vuri,
force_replace, context);
}
if (new_is_directory_file) {
g_assert (old_entry != NULL);
g_assert (new_entry != NULL);
G_LOCK (vfolder_lock);
result = move_directory_file (info,
(Folder *)old_entry,
(Folder *)new_entry);
G_UNLOCK (vfolder_lock);
return result;
}
if (old_entry->type == ENTRY_FOLDER) {
G_LOCK (vfolder_lock);
result = move_folder (info,
old_folder, old_entry,
new_folder, new_entry);
G_UNLOCK (vfolder_lock);
return result;
}
/* move into self, just whack the old one */
if (old_entry == new_entry) {
/* same folder */
if (new_folder == old_folder)
return GNOME_VFS_OK;
if ( ! force_replace)
return GNOME_VFS_ERROR_FILE_EXISTS;
G_LOCK (vfolder_lock);
remove_file (old_folder, old_vuri.file);
old_folder->entries = g_slist_remove (old_folder->entries,
old_entry);
entry_unref (old_entry);
emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
/* this is a simple move */
if (new_entry == NULL ||
new_entry->type == ENTRY_FOLDER) {
if (new_entry != NULL) {
new_folder = (Folder *)new_entry;
} else {
/* a file and a totally different one */
if (strcmp (new_vuri.file, old_entry->name) != 0) {
/* yay, a long move */
/* this will do another set of lookups
* perhaps this can be done in a nicer way,
* but is this the common case? I don't think
* so */
return long_move (method, &old_vuri, &new_vuri,
force_replace, context);
}
}
/* same folder */
if (new_folder == old_folder)
return GNOME_VFS_OK;
G_LOCK (vfolder_lock);
remove_file (old_folder, old_entry->name);
add_file (new_folder, old_entry->name);
new_folder->entries = g_slist_prepend (new_folder->entries,
old_entry);
entry_ref (old_entry);
new_folder->sorted = FALSE;
old_folder->entries = g_slist_remove (old_folder->entries,
old_entry);
entry_unref (old_entry);
emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
/* do we EVER get here? */
/* this will do another set of lookups
* perhaps this can be done in a nicer way,
* but is this the common case? I don't think so */
return long_move (method, &old_vuri, &new_vuri,
force_replace, context);
}
static GnomeVFSResult
do_unlink (GnomeVFSMethod *method,
GnomeVFSURI *uri,
GnomeVFSContext *context)
{
GnomeVFSResult result = GNOME_VFS_OK;
Entry *entry;
Folder *the_folder;
gboolean is_directory_file;
VFolderInfo *info;
VFolderURI vuri;
GSList *li;
VFOLDER_URI_PARSE (uri, &vuri);
if (vuri.file == NULL)
return GNOME_VFS_ERROR_INVALID_URI;
if (vuri.is_all_scheme == TRUE)
return GNOME_VFS_ERROR_READ_ONLY;
info = get_vfolder_info (vuri.scheme, &result, context);
if (info == NULL)
return result;
else if (info->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
entry = get_entry (&vuri,
&the_folder,
&is_directory_file,
&result, context);
if (entry == NULL)
return result;
else if (the_folder != NULL &&
the_folder->read_only)
return GNOME_VFS_ERROR_READ_ONLY;
if (entry->type == ENTRY_FOLDER &&
is_directory_file) {
Folder *folder = (Folder *)entry;
if (folder->desktop_file == NULL)
return GNOME_VFS_ERROR_NOT_FOUND;
G_LOCK (vfolder_lock);
g_free (folder->desktop_file);
folder->desktop_file = NULL;
emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
} else if (entry->type == ENTRY_FOLDER) {
return GNOME_VFS_ERROR_IS_DIRECTORY;
} else if (the_folder == NULL) {
return GNOME_VFS_ERROR_NOT_FOUND;
}
G_LOCK (vfolder_lock);
the_folder->entries = g_slist_remove (the_folder->entries,
entry);
entry_unref (entry);
remove_file (the_folder, vuri.file);
emit_monitor (the_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
/* evil, we must remove this from the unallocated folders as well
* so that it magically doesn't appear there. But it's not so simple.
* We only want to remove it if it isn't in that folder already. */
for (li = info->unallocated_folders;
li != NULL;
li = li->next) {
Folder *folder = li->data;
GSList *l;
/* This is actually really evil since ensuring
* an unallocated folder clears all other unallocated
* folders in it's wake. I'm not sure it's worth
* optimizing however */
ensure_folder_unlocked (info, folder,
FALSE /* subfolders */,
NULL /* except */,
FALSE /* ignore_unallocated */);
l = g_slist_find (folder->entries, entry);
if (l == NULL) {
remove_file (folder, vuri.file);
}
}
emit_file_deleted_monitor (info, entry, the_folder);
/* FIXME: if this was a user file and this is the only
* reference to it, unlink it. */
vfolder_info_write_user (info);
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_check_same_fs (GnomeVFSMethod *method,
GnomeVFSURI *source_uri,
GnomeVFSURI *target_uri,
gboolean *same_fs_return,
GnomeVFSContext *context)
{
VFolderURI source_vuri, target_vuri;
*same_fs_return = FALSE;
VFOLDER_URI_PARSE (source_uri, &source_vuri);
VFOLDER_URI_PARSE (target_uri, &target_vuri);
if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
source_vuri.is_all_scheme != target_vuri.is_all_scheme)
*same_fs_return = FALSE;
else
*same_fs_return = TRUE;
return GNOME_VFS_OK;
}
static GnomeVFSResult
do_set_file_info (GnomeVFSMethod *method,
GnomeVFSURI *uri,
const GnomeVFSFileInfo *info,
GnomeVFSSetFileInfoMask mask,
GnomeVFSContext *context)
{
VFolderURI vuri;
VFOLDER_URI_PARSE (uri, &vuri);
if (vuri.file == NULL)
return GNOME_VFS_ERROR_INVALID_URI;
if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
GnomeVFSResult result = GNOME_VFS_OK;
char *dirname = gnome_vfs_uri_extract_dirname (uri);
GnomeVFSURI *new_uri = gnome_vfs_uri_dup (uri);
G_LOCK (vfolder_lock);
g_free (new_uri->text);
new_uri->text = g_build_path ("/", dirname, info->name, NULL);
G_UNLOCK (vfolder_lock);
result = do_move (method,
uri,
new_uri,
FALSE /* force_replace */,
context);
g_free (dirname);
gnome_vfs_uri_unref (new_uri);
return result;
} else {
/* We don't support setting any of this other permission,
* times and all that voodoo */
return GNOME_VFS_ERROR_NOT_SUPPORTED;
}
}
static GnomeVFSResult
do_monitor_add (GnomeVFSMethod *method,
GnomeVFSMethodHandle **method_handle_return,
GnomeVFSURI *uri,
GnomeVFSMonitorType monitor_type)
{
VFolderInfo *info;
VFolderURI vuri;
GnomeVFSResult result;
Folder *folder;
Entry *entry;
GnomeVFSURI *file_uri;
FileMonitorHandle *handle;
gboolean is_directory_file;
VFOLDER_URI_PARSE (uri, &vuri);
info = get_vfolder_info (vuri.scheme, &result, NULL);
if (info == NULL)
return result;
if (monitor_type == GNOME_VFS_MONITOR_DIRECTORY) {
G_LOCK (vfolder_lock);
folder = resolve_folder (info,
vuri.path,
FALSE /* ignore_basename */,
&result,
NULL);
handle = g_new0 (FileMonitorHandle, 1);
handle->refcount = 2;
handle->uri = gnome_vfs_uri_dup (uri);
handle->dir_monitor = TRUE;
handle->handle = NULL;
handle->filename = NULL;
if (folder == NULL) {
handle->exists = FALSE;
info->free_folder_monitors =
g_slist_prepend (info->free_folder_monitors,
handle);
} else {
handle->exists = TRUE;
((Entry *)folder)->monitors =
g_slist_prepend (((Entry *)folder)->monitors,
handle);
}
info->folder_monitors =
g_slist_prepend (info->folder_monitors, handle);
G_UNLOCK (vfolder_lock);
*method_handle_return = (GnomeVFSMethodHandle *) handle;
return GNOME_VFS_OK;
} else {
/* These can't be very nice FILE names */
if (vuri.file == NULL ||
vuri.ends_in_slash)
return GNOME_VFS_ERROR_INVALID_URI;
G_LOCK (vfolder_lock);
file_uri = desktop_uri_to_file_uri (info,
&vuri,
&entry,
&is_directory_file,
NULL /* the_folder */,
FALSE,
&result,
NULL);
handle = g_new0 (FileMonitorHandle, 1);
handle->refcount = 2;
handle->uri = gnome_vfs_uri_dup (uri);
handle->dir_monitor = FALSE;
handle->handle = NULL;
handle->filename = g_strdup (vuri.file);
handle->is_directory_file = is_directory_file;
info->file_monitors =
g_slist_prepend (info->file_monitors, handle);
if (file_uri == NULL) {
handle->exists = FALSE;
info->free_file_monitors =
g_slist_prepend (info->free_file_monitors,
handle);
} else {
char *uri_string = gnome_vfs_uri_to_string (file_uri, 0);
handle->exists = TRUE;
gnome_vfs_monitor_add (&(handle->handle),
uri_string,
GNOME_VFS_MONITOR_FILE,
file_monitor,
handle);
g_free (uri_string);
entry->monitors = g_slist_prepend (entry->monitors,
handle);
gnome_vfs_uri_unref (file_uri);
}
*method_handle_return = (GnomeVFSMethodHandle *) handle;
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
}
}
static GnomeVFSResult
do_monitor_cancel (GnomeVFSMethod *method,
GnomeVFSMethodHandle *method_handle)
{
FileMonitorHandle *handle;
VFolderInfo *info;
VFolderURI vuri;
GnomeVFSResult result;
Folder *folder;
GSList *li;
handle = (FileMonitorHandle *)method_handle;
/* FIXME: is this correct? */
if (method_handle == NULL)
return GNOME_VFS_OK;
VFOLDER_URI_PARSE (handle->uri, &vuri);
info = get_vfolder_info (vuri.scheme, &result, NULL);
if (info == NULL)
return result;
if (handle->dir_monitor) {
G_LOCK (vfolder_lock);
folder = resolve_folder (info,
vuri.path,
FALSE /* ignore_basename */,
&result,
NULL);
for (li = info->folder_monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
if (h != handle)
continue;
info->folder_monitors = g_slist_delete_link
(info->folder_monitors, li);
file_monitor_handle_unref_unlocked (h);
break;
}
if (folder == NULL) {
for (li = info->free_folder_monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *h = li->data;
if (h != handle)
continue;
info->free_folder_monitors = g_slist_delete_link
(info->free_folder_monitors, li);
file_monitor_handle_unref_unlocked (h);
break;
}
} else {
for (li = ((Entry *)folder)->monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *h = li->data;
if (h != handle)
continue;
((Entry *)folder)->monitors =
g_slist_delete_link
(((Entry *)folder)->monitors, li);
file_monitor_handle_unref_unlocked (h);
break;
}
}
G_UNLOCK (vfolder_lock);
return GNOME_VFS_OK;
} else {
G_LOCK (vfolder_lock);
for (li = info->file_monitors; li != NULL; li = li->next) {
FileMonitorHandle *h = li->data;
if (h != handle)
continue;
info->file_monitors = g_slist_delete_link
(info->file_monitors, li);
file_monitor_handle_unref_unlocked (h);
break;
}
for (li = info->free_file_monitors;
li != NULL;
li = li->next) {
FileMonitorHandle *h = li->data;
if (h != handle)
continue;
info->free_file_monitors = g_slist_delete_link
(info->free_file_monitors, li);
file_monitor_handle_unref_unlocked (h);
break;
}
for (li = info->entries; li != NULL; li = li->next) {
Entry *e = li->data;
GSList *link = g_slist_find (e->monitors, handle);
if (link == NULL)
continue;
link->data = NULL;
e->monitors = g_slist_delete_link (e->monitors, link);
file_monitor_handle_unref_unlocked (handle);
break;
}
G_UNLOCK (vfolder_lock);
/* Note: last unref of our monitor will cancel the
* underlying handle */
return GNOME_VFS_OK;
}
}
/* gnome-vfs bureaucracy */
static GnomeVFSMethod method = {
sizeof (GnomeVFSMethod),
do_open,
do_create,
do_close,
do_read,
do_write,
do_seek,
do_tell,
do_truncate_handle,
do_open_directory,
do_close_directory,
do_read_directory,
do_get_file_info,
do_get_file_info_from_handle,
do_is_local,
do_make_directory,
do_remove_directory,
do_move,
do_unlink,
do_check_same_fs,
do_set_file_info,
do_truncate,
NULL /* find_directory */,
NULL /* create_symbolic_link */,
do_monitor_add,
do_monitor_cancel
};
GnomeVFSMethod *
vfs_module_init (const char *method_name,
const char *args)
{
parent_method = gnome_vfs_method_get ("file");
if (parent_method == NULL) {
g_error ("Could not find 'file' method for gnome-vfs");
return NULL;
}
return &method;
}
void
vfs_module_shutdown (GnomeVFSMethod *method)
{
if (infos == NULL)
return;
g_hash_table_destroy (infos);
infos = NULL;
}