|
|
/* -*- 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;
|
|
|
}
|