1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
3 /* vfolder-desktop-method.c
5 Copyright (C) 2001 Red Hat, Inc.
6 Copyright (C) 2001 The Dark Prince
8 The Gnome Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
13 The Gnome Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with the Gnome Library; see the file COPYING.LIB. If not,
20 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
24 /* URI scheme for reading the "applications:" vfolder and other
25 * vfolder schemes. Lots of code stolen from the original desktop
38 #include <sys/types.h>
49 #include <libxml/parser.h>
50 #include <libxml/tree.h>
51 #include <libxml/xmlmemory.h>
53 #include <libgnomevfs/gnome-vfs-mime.h>
55 #include <libgnomevfs/gnome-vfs-module.h>
56 #include <libgnomevfs/gnome-vfs-method.h>
57 #include <libgnomevfs/gnome-vfs-utils.h>
58 #include <libgnomevfs/gnome-vfs-ops.h>
59 #include <libgnomevfs/gnome-vfs-module-shared.h>
60 #include <libgnomevfs/gnome-vfs-monitor-private.h>
62 #define DOT_GNOME ".gnome2"
64 typedef struct _VFolderInfo VFolderInfo;
65 typedef struct _Query Query;
66 typedef struct _QueryKeyword QueryKeyword;
67 typedef struct _QueryFilename QueryFilename;
68 typedef struct _Entry Entry;
69 typedef struct _Folder Folder;
70 typedef struct _EntryFile EntryFile;
71 typedef struct _Keyword Keyword;
72 typedef struct _FileMonitorHandle FileMonitorHandle;
73 typedef struct _StatLoc StatLoc;
74 typedef struct _VFolderURI VFolderURI;
76 /* TODO before 2.0: */
77 /* FIXME: also check/monitor desktop_dirs like we do the vfolder
78 * file and the item dirs */
79 /* FIXME: check if thread locks are not completely on crack which
80 * is likely given my experience with threads */
81 /* FIXME: use filename locking, currently we are full of races if
82 * multiple processes write to this filesystem */
83 /* FIXME: implement monitors */
85 /* TODO for later (star trek future): */
86 /* FIXME: Maybe when chaining to file:, we should call the gnome-vfs wrapper
87 * functions, instead of the file: methods directly. */
88 /* FIXME: related to the above: we should support things being on non
89 * file: filesystems. Such as having the vfolder info file on http
90 * somewhere or some such nonsense :) */
92 static GnomeVFSMethod *parent_method = NULL;
94 static GHashTable *infos = NULL;
96 /* Note: I have no clue about how to write thread safe code and this
97 * is my first attempt, so it's probably wrong
99 G_LOCK_DEFINE_STATIC (vfolder_lock);
101 /* Note: all keywords are quarks */
102 /* Note: basenames are unique */
104 #define UNSUPPORTED_INFO_FIELDS (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | \
105 GNOME_VFS_FILE_INFO_FIELDS_DEVICE | \
106 GNOME_VFS_FILE_INFO_FIELDS_INODE | \
107 GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT | \
108 GNOME_VFS_FILE_INFO_FIELDS_ATIME)
124 struct _QueryKeyword {
130 struct _QueryFilename {
144 int alloc; /* not really useful for folders,
145 but oh well, whatever. It's the number
146 of times this is queried in some directory,
147 used for the Unallocated query type */
160 gboolean implicit_keywords; /* the keywords were added by us */
168 char *desktop_file; /* the .directory file */
172 /* The following is for per file
174 /* excluded by filename */
175 GHashTable *excludes;
176 /* included by filename */
178 GHashTable *includes_ht;
184 gboolean dont_show_if_empty;
185 gboolean only_unallocated; /* include only unallocated items */
187 /* lazily done, will run query only when it
197 gboolean trigger_next; /* if true, next check will fail */
198 char name[1]; /* the structure will be long enough to
202 struct _VFolderInfo {
207 time_t user_filename_last_write;
208 char *desktop_dir; /* directory with .directorys */
209 char *user_desktop_dir; /* directory with .directorys */
210 gboolean user_file_active; /* if using user_filename and
214 char *user_item_dir; /* dir where user changes to
217 /* old style dirs to merge in */
220 /* if entries are valid, else
221 * they need to be (re)read */
222 gboolean entries_valid;
226 /* entry hash by basename */
227 GHashTable *entries_ht;
229 /* The root folder */
232 /* The unallocated folders, the folder which only
233 * include unallocated items */
234 GSList *unallocated_folders;
243 /* change monitoring stuff */
244 GnomeVFSMonitorHandle *filename_monitor;
245 GnomeVFSMonitorHandle *user_filename_monitor;
247 /* stat locations (in case we aren't monitoring) */
248 StatLoc *filename_statloc;
249 StatLoc *user_filename_statloc;
251 /* for .directory dirs */
252 /* FIXME: */GnomeVFSMonitorHandle *desktop_dir_monitor;
253 /* FIXME: */GnomeVFSMonitorHandle *user_desktop_dir_monitor;
255 /* stat locations (in case we aren't monitoring) */
256 /* FIXME: */StatLoc *desktop_dir_statloc;
257 /* FIXME: */StatLoc *user_desktop_dir_statloc;
259 /* FIXME: */GSList *file_monitors; /* FileMonitorHandle */
260 /* FIXME: */GSList *free_file_monitors; /* FileMonitorHandle */
261 GSList *folder_monitors; /* FileMonitorHandle */
262 GSList *free_folder_monitors; /* FileMonitorHandle */
264 GSList *item_dir_monitors; /* GnomeVFSMonitorHandle */
266 /* item dirs to stat */
269 /* ctime for folders */
270 time_t modification_time;
275 struct _FileMonitorHandle {
278 gboolean dir_monitor; /* TRUE if dir, FALSE if file */
280 GnomeVFSMonitorHandle *handle; /* A real handle if we're monitoring
281 an actual file here, or NULL */
282 char *filename; /* Just the basename, used in the free_file_list */
283 gboolean is_directory_file;
288 gboolean is_all_scheme;
289 gboolean ends_in_slash;
296 static Entry * entry_ref (Entry *entry);
297 static Entry * entry_ref_alloc (Entry *entry);
298 static void entry_unref (Entry *entry);
299 static void entry_unref_dealloc (Entry *entry);
300 static void query_destroy (Query *query);
301 static void ensure_folder (VFolderInfo *info,
305 gboolean ignore_unallocated);
306 static void ensure_folder_unlocked (VFolderInfo *info,
310 gboolean ignore_unallocated);
311 /* An EVIL function for quick reading of .desktop files,
312 * only reads in one or two keys, but that's ALL we need */
313 static void readitem_entry (const char *filename,
318 static gboolean vfolder_info_reload (VFolderInfo *info,
319 GnomeVFSResult *result,
320 GnomeVFSContext *context,
321 gboolean force_read_items);
322 static gboolean vfolder_info_reload_unlocked (VFolderInfo *info,
323 GnomeVFSResult *result,
324 GnomeVFSContext *context,
325 gboolean force_read_items);
326 static void invalidate_folder_subfolders (Folder *folder,
327 gboolean lock_taken);
328 static Folder * resolve_folder (VFolderInfo *info,
330 gboolean ignore_basename,
331 GnomeVFSResult *result,
332 GnomeVFSContext *context);
333 static gboolean vfolder_info_read_items (VFolderInfo *info,
334 GnomeVFSResult *result,
335 GnomeVFSContext *context);
337 /* assumes vuri->path already set */
339 vfolder_uri_parse_internal (GnomeVFSURI *uri, VFolderURI *vuri)
341 vuri->scheme = (gchar *) gnome_vfs_uri_get_scheme (uri);
343 vuri->ends_in_slash = FALSE;
345 if (strncmp (vuri->scheme, "all-", strlen ("all-")) == 0) {
346 vuri->scheme += strlen ("all-");
347 vuri->is_all_scheme = TRUE;
349 vuri->is_all_scheme = FALSE;
351 if (vuri->path != NULL) {
352 int last_slash = strlen (vuri->path) - 1;
355 /* Note: This handling of paths is somewhat evil, may need a
358 /* kill leading slashes, that is make sure there is
360 for (first = vuri->path; *first == '/'; first++)
362 if (first != vuri->path) {
367 /* kill trailing slashes (leave first if all slashes) */
368 while (last_slash > 0 && vuri->path [last_slash] == '/') {
369 vuri->path [last_slash--] = '\0';
370 vuri->ends_in_slash = TRUE;
373 /* get basename start */
374 while (last_slash >= 0 && vuri->path [last_slash] != '/')
378 vuri->file = vuri->path + last_slash + 1;
380 vuri->file = vuri->path;
382 if (vuri->file[0] == '\0' &&
383 strcmp (vuri->path, "/") == 0) {
387 vuri->ends_in_slash = TRUE;
397 #define VFOLDER_URI_PARSE(_uri, _vuri) { \
399 path = gnome_vfs_unescape_string ((_uri)->text, G_DIR_SEPARATOR_S); \
400 if (path != NULL) { \
401 (_vuri)->path = g_alloca (strlen (path) + 1); \
402 strcpy ((_vuri)->path, path); \
405 (_vuri)->path = NULL; \
407 vfolder_uri_parse_internal ((_uri), (_vuri)); \
410 static FileMonitorHandle *
411 file_monitor_handle_ref_unlocked (FileMonitorHandle *h)
418 file_monitor_handle_unref_unlocked (FileMonitorHandle *h)
421 if (h->refcount == 0) {
422 gnome_vfs_uri_unref (h->uri);
425 g_free (h->filename);
428 if (h->handle != NULL) {
429 gnome_vfs_monitor_cancel (h->handle);
435 /* This includes the .directory files */
437 emit_monitor (Folder *folder, int type)
440 for (li = ((Entry *)folder)->monitors;
443 FileMonitorHandle *handle = li->data;
444 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
450 emit_file_deleted_monitor (VFolderInfo *info, Entry *entry, Folder *folder)
453 for (li = entry->monitors;
458 GnomeVFSResult result;
459 FileMonitorHandle *handle = li->data;
461 /* Evil! EVIL URI PARSING. this will eat a lot of
462 * stack if we have lots of monitors */
464 VFOLDER_URI_PARSE (handle->uri, &vuri);
466 f = resolve_folder (info,
468 TRUE /* ignore_basename */,
473 gnome_vfs_monitor_callback
474 ((GnomeVFSMethodHandle *) handle,
476 GNOME_VFS_MONITOR_EVENT_DELETED);
481 emit_and_delete_monitor (VFolderInfo *info, Folder *folder)
484 for (li = ((Entry *)folder)->monitors;
487 FileMonitorHandle *handle = li->data;
490 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) handle,
492 GNOME_VFS_MONITOR_EVENT_DELETED);
494 if (handle->dir_monitor)
495 info->free_folder_monitors =
496 g_slist_prepend (info->free_folder_monitors,
499 info->free_file_monitors =
500 g_slist_prepend (info->free_file_monitors,
503 g_slist_free (((Entry *)folder)->monitors);
504 ((Entry *)folder)->monitors = NULL;
508 check_ext (const char *name, const char *ext_check)
512 ext = strrchr (name, '.');
514 strcmp (ext, ext_check) != 0)
521 bake_statloc (const char *name,
526 if (stat (name, &s) != 0) {
527 if (errno == ENOENT) {
528 sl = g_malloc0 (sizeof (StatLoc) +
530 sl->last_stat = curtime;
532 sl->trigger_next = FALSE;
533 strcpy (sl->name, name);
538 sl = g_malloc0 (sizeof (StatLoc) +
540 sl->last_stat = curtime;
541 sl->ctime = s.st_ctime;
542 sl->trigger_next = FALSE;
543 strcpy (sl->name, name);
548 /* returns FALSE if we must reread */
550 check_statloc (StatLoc *sl,
555 if (sl->trigger_next) {
556 sl->trigger_next = FALSE;
560 /* don't stat more then once every 3 seconds */
561 if (curtime <= sl->last_stat + 3)
564 sl->last_stat = curtime;
566 if (stat (sl->name, &s) != 0) {
567 if (errno == ENOENT &&
574 if (sl->ctime == s.st_ctime)
581 ensure_dir (const char *dirname, gboolean ignore_basename)
589 parsed = g_path_get_dirname (dirname);
591 parsed = g_strdup (dirname);
593 if (g_file_test (parsed, G_FILE_TEST_IS_DIR)) {
598 p = strchr (parsed, '/');
600 p = strchr (p+1, '/');
604 if (mkdir (parsed, 0700) != 0 &&
610 p = strchr (p+1, '/');
613 if (mkdir (parsed, 0700) != 0 &&
623 /* check for any directory name other then root */
625 any_subdir (const char *dirname)
631 for (p = dirname; *p != '\0'; p++) {
640 destroy_entry_file (EntryFile *efile)
645 g_free (efile->filename);
646 efile->filename = NULL;
648 g_slist_free (efile->keywords);
649 efile->keywords = NULL;
655 destroy_folder (Folder *folder)
662 if (folder->parent != NULL) {
663 folder->parent->subfolders =
664 g_slist_remove (folder->parent->subfolders, folder);
665 folder->parent->up_to_date = FALSE;
666 folder->parent = NULL;
669 g_free (folder->desktop_file);
670 folder->desktop_file = NULL;
672 query_destroy (folder->query);
673 folder->query = NULL;
675 if (folder->excludes != NULL) {
676 g_hash_table_destroy (folder->excludes);
677 folder->excludes = NULL;
680 g_slist_foreach (folder->includes, (GFunc)g_free, NULL);
681 g_slist_free (folder->includes);
682 folder->includes = NULL;
683 if (folder->includes_ht != NULL) {
684 g_hash_table_destroy (folder->includes_ht);
685 folder->includes_ht = NULL;
688 list = folder->subfolders;
689 folder->subfolders = NULL;
690 g_slist_foreach (list, (GFunc)entry_unref, NULL);
693 list = folder->entries;
694 folder->entries = NULL;
695 g_slist_foreach (list, (GFunc)entry_unref, NULL);
702 entry_ref (Entry *entry)
710 entry_ref_alloc (Entry *entry)
721 entry_unref (Entry *entry)
728 if (entry->refcount == 0) {
729 g_free (entry->name);
732 g_slist_foreach (entry->monitors,
733 (GFunc)file_monitor_handle_unref_unlocked,
735 g_slist_free (entry->monitors);
736 entry->monitors = NULL;
738 if (entry->type == ENTRY_FILE)
739 destroy_entry_file ((EntryFile *)entry);
740 else /* ENTRY_FOLDER */
741 destroy_folder ((Folder *)entry);
746 entry_unref_dealloc (Entry *entry)
754 /* Handles ONLY files, not dirs */
755 /* Also allocates the entries as well as refs them */
757 alloc_entries_from_files (VFolderInfo *info, GSList *filenames)
763 for (li = filenames; li != NULL; li = li->next) {
764 char *filename = li->data;
765 GSList *entry_list = g_hash_table_lookup (info->entries_ht, filename);
766 if (entry_list != NULL)
767 files = g_slist_prepend (files,
768 entry_ref_alloc (entry_list->data));
775 matches_query (VFolderInfo *info,
785 #define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
786 switch (query->type) {
788 for (li = query->queries; li != NULL; li = li->next) {
789 Query *subquery = li->data;
790 if (matches_query (info, folder, efile, subquery))
791 return INVERT_IF_NEEDED (TRUE);
793 return INVERT_IF_NEEDED (FALSE);
795 for (li = query->queries; li != NULL; li = li->next) {
796 Query *subquery = li->data;
797 if ( ! matches_query (info, folder, efile, subquery))
798 return INVERT_IF_NEEDED (FALSE);
800 return INVERT_IF_NEEDED (TRUE);
803 QueryKeyword *qkeyword = (QueryKeyword *)query;
804 for (li = efile->keywords; li != NULL; li = li->next) {
805 GQuark keyword = GPOINTER_TO_INT (li->data);
806 if (keyword == qkeyword->keyword)
807 return INVERT_IF_NEEDED (TRUE);
809 return INVERT_IF_NEEDED (FALSE);
813 QueryFilename *qfilename = (QueryFilename *)query;
814 if (strcmp (qfilename->filename, ((Entry *)efile)->name) == 0) {
815 return INVERT_IF_NEEDED (TRUE);
817 return INVERT_IF_NEEDED (FALSE);
821 #undef INVERT_IF_NEEDED
822 g_assert_not_reached ();
828 dump_unallocated_folders (Folder *folder)
831 for (li = folder->subfolders; li != NULL; li = li->next)
832 dump_unallocated_folders (li->data);
834 if (folder->only_unallocated &&
835 folder->entries != NULL) {
836 g_slist_foreach (folder->entries,
837 (GFunc)entry_unref_dealloc, NULL);
838 g_slist_free (folder->entries);
839 folder->entries = NULL;
843 /* Run query, allocs and refs the entries */
845 append_query (VFolderInfo *info, Folder *folder)
849 if (folder->query == NULL &&
850 ! folder->only_unallocated)
853 if (folder->only_unallocated) {
854 /* dump all folders that use unallocated
855 * items only. This sucks if you keep
856 * reading one and then another such
857 * folder, but oh well, life sucks for
858 * you then, but at least you get
859 * consistent results */
860 dump_unallocated_folders (info->root);
862 /* ensure all other folders, so that
863 * after this we know which ones are
865 ensure_folder_unlocked (info,
867 TRUE /* subfolders */,
869 /* avoid infinite loops */
870 TRUE /* ignore_unallocated */);
873 for (li = info->entries; li != NULL; li = li->next) {
874 Entry *entry = li->data;
876 if (/* if not file */
877 entry->type != ENTRY_FILE ||
878 /* if already included */
879 (folder->includes_ht != NULL &&
880 g_hash_table_lookup (folder->includes_ht,
881 entry->name) != NULL))
884 if (folder->only_unallocated &&
888 if (matches_query (info, folder, (EntryFile *)entry,
890 folder->entries = g_slist_prepend
891 (folder->entries, entry_ref_alloc (entry));
895 /* get entries in folder */
896 /* FIXME: support cancellation here */
898 ensure_folder_unlocked (VFolderInfo *info,
902 gboolean ignore_unallocated)
906 for (li = folder->subfolders; li != NULL; li = li->next)
907 ensure_folder_unlocked (info, li->data, subfolders,
908 except, ignore_unallocated);
911 if (except == folder)
914 if (ignore_unallocated &&
915 folder->only_unallocated)
918 if (folder->up_to_date)
921 if (folder->entries != NULL) {
922 g_slist_foreach (folder->entries,
923 (GFunc)entry_unref_dealloc, NULL);
924 g_slist_free (folder->entries);
925 folder->entries = NULL;
928 /* Include includes */
929 folder->entries = alloc_entries_from_files (info, folder->includes);
932 append_query (info, folder);
934 /* We were prepending all this time */
935 folder->entries = g_slist_reverse (folder->entries);
937 /* Include subfolders */
938 /* we always whack them onto the beginning */
939 if (folder->subfolders != NULL) {
940 GSList *subfolders = g_slist_copy (folder->subfolders);
941 g_slist_foreach (subfolders, (GFunc)entry_ref_alloc, NULL);
942 folder->entries = g_slist_concat (subfolders, folder->entries);
945 /* Exclude excludes */
946 if (folder->excludes != NULL) {
948 GSList *entries = folder->entries;
949 folder->entries = NULL;
950 for (li = entries; li != NULL; li = li->next) {
951 Entry *entry = li->data;
952 if (g_hash_table_lookup (folder->excludes, entry->name) == NULL)
953 folder->entries = g_slist_prepend (folder->entries, entry);
955 entry_unref_dealloc (entry);
958 g_slist_free (entries);
960 /* to preserve the Folders then everything else order */
961 folder->entries = g_slist_reverse (folder->entries);
964 folder->up_to_date = TRUE;
966 folder->sorted = FALSE;
970 ensure_folder (VFolderInfo *info,
974 gboolean ignore_unallocated)
976 G_LOCK (vfolder_lock);
977 ensure_folder_unlocked (info, folder, subfolders, except, ignore_unallocated);
978 G_UNLOCK (vfolder_lock);
982 get_directory_file_unlocked (VFolderInfo *info, Folder *folder)
986 /* FIXME: cache dir_files */
988 if (folder->desktop_file == NULL)
991 if (folder->desktop_file[0] == G_DIR_SEPARATOR)
992 return g_strdup (folder->desktop_file);
994 /* Now try the user directory */
995 if (info->user_desktop_dir != NULL) {
996 filename = g_build_filename (info->user_desktop_dir,
997 folder->desktop_file,
999 if (access (filename, F_OK) == 0) {
1006 filename = g_build_filename (info->desktop_dir, folder->desktop_file, NULL);
1007 if (access (filename, F_OK) == 0) {
1016 get_directory_file (VFolderInfo *info, Folder *folder)
1020 G_LOCK (vfolder_lock);
1021 ret = get_directory_file_unlocked (info, folder);
1022 G_UNLOCK (vfolder_lock);
1028 get_sort_order (VFolderInfo *info, Folder *folder)
1036 filename = get_directory_file_unlocked (info, folder);
1037 if (filename == NULL)
1041 readitem_entry (filename,
1051 parsed = g_strsplit (order, ":", -1);
1056 for (i = 0; parsed[i] != NULL; i++) {
1057 char *word = parsed[i];
1061 if (word[0] == '\0') {
1065 list = g_slist_prepend (list, word);
1067 /* we've stolen all strings from it */
1070 return g_slist_reverse (list);
1073 /* get entries in folder */
1075 ensure_folder_sort (VFolderInfo *info, Folder *folder)
1077 GSList *li, *sort_order;
1079 GHashTable *entry_hash;
1081 ensure_folder (info, folder,
1082 FALSE /* subfolders */,
1084 FALSE /* ignore_unallocated */);
1088 G_LOCK (vfolder_lock);
1090 sort_order = get_sort_order (info, folder);
1091 if (sort_order == NULL) {
1092 folder->sorted = TRUE;
1093 G_UNLOCK (vfolder_lock);
1097 entries = folder->entries;
1098 folder->entries = NULL;
1100 entry_hash = g_hash_table_new (g_str_hash, g_str_equal);
1101 for (li = entries; li != NULL; li = li->next) {
1102 Entry *entry = li->data;
1103 g_hash_table_insert (entry_hash, entry->name, li);
1106 for (li = sort_order; li != NULL; li = li->next) {
1107 char *word = li->data;
1111 /* we kill the words here */
1114 entry_list = g_hash_table_lookup (entry_hash, word);
1117 if (entry_list == NULL)
1120 entry = entry_list->data;
1122 entries = g_slist_delete_link (entries, entry_list);
1124 folder->entries = g_slist_prepend (folder->entries,
1128 /* put on those that weren't mentioned in the sort */
1129 for (li = entries; li != NULL; li = li->next) {
1130 Entry *entry = li->data;
1132 folder->entries = g_slist_prepend (folder->entries,
1136 g_hash_table_destroy (entry_hash);
1137 g_slist_free (entries);
1138 g_slist_free (sort_order);
1140 folder->sorted = TRUE;
1142 G_UNLOCK (vfolder_lock);
1146 file_new (const char *name)
1148 EntryFile *efile = g_new0 (EntryFile, 1);
1150 efile->entry.type = ENTRY_FILE;
1151 efile->entry.name = g_strdup (name);
1152 efile->entry.refcount = 1;
1158 folder_new (const char *name)
1160 Folder *folder = g_new0 (Folder, 1);
1162 folder->entry.type = ENTRY_FOLDER;
1163 folder->entry.name = g_strdup (name);
1164 folder->entry.refcount = 1;
1165 folder->read_only = TRUE;
1171 query_new (int type)
1175 if (type == QUERY_KEYWORD)
1176 query = (Query *)g_new0 (QueryKeyword, 1);
1177 else if (type == QUERY_FILENAME)
1178 query = (Query *)g_new0 (QueryFilename, 1);
1180 query = g_new0 (Query, 1);
1188 query_destroy (Query *query)
1193 if (query->type == QUERY_FILENAME) {
1194 QueryFilename *qfile = (QueryFilename *)query;
1195 g_free (qfile->filename);
1196 qfile->filename = NULL;
1197 } else if (query->type == QUERY_OR ||
1198 query->type == QUERY_AND) {
1199 g_slist_foreach (query->queries, (GFunc)query_destroy, NULL);
1200 g_slist_free (query->queries);
1201 query->queries = NULL;
1208 add_folder_monitor_unlocked (VFolderInfo *info,
1209 FileMonitorHandle *handle)
1212 GnomeVFSResult result;
1215 VFOLDER_URI_PARSE (handle->uri, &vuri);
1217 file_monitor_handle_ref_unlocked (handle);
1219 info->folder_monitors =
1220 g_slist_prepend (info->folder_monitors, handle);
1222 folder = resolve_folder (info,
1224 FALSE /* ignore_basename */,
1228 if (folder == NULL) {
1229 file_monitor_handle_ref_unlocked (handle);
1231 info->free_folder_monitors =
1232 g_slist_prepend (info->free_folder_monitors, handle);
1234 if (handle->exists) {
1235 handle->exists = FALSE;
1236 gnome_vfs_monitor_callback
1237 ((GnomeVFSMethodHandle *)handle,
1239 GNOME_VFS_MONITOR_EVENT_DELETED);
1242 file_monitor_handle_ref_unlocked (handle);
1244 ((Entry *)folder)->monitors =
1245 g_slist_prepend (((Entry *)folder)->monitors, handle);
1247 if ( ! handle->exists) {
1248 handle->exists = TRUE;
1249 gnome_vfs_monitor_callback
1250 ((GnomeVFSMethodHandle *)handle,
1252 GNOME_VFS_MONITOR_EVENT_CREATED);
1259 invalidate_folder_T (Folder *folder)
1261 folder->up_to_date = FALSE;
1263 invalidate_folder_subfolders (folder, TRUE);
1267 invalidate_folder (Folder *folder)
1269 G_LOCK (vfolder_lock);
1270 folder->up_to_date = FALSE;
1271 G_UNLOCK (vfolder_lock);
1273 invalidate_folder_subfolders (folder, FALSE);
1277 invalidate_folder_subfolders (Folder *folder,
1278 gboolean lock_taken)
1282 for (li = folder->subfolders; li != NULL; li = li->next) {
1283 Folder *subfolder = li->data;
1286 invalidate_folder (subfolder);
1288 invalidate_folder_T (subfolder);
1291 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
1294 /* FIXME: this is UGLY!, we need to figure out when the file
1295 * got finished changing! */
1297 reread_timeout (gpointer data)
1299 VFolderInfo *info = data;
1300 gboolean force_read_items = info->file_monitors != NULL;
1301 vfolder_info_reload (info, NULL, NULL, force_read_items);
1306 queue_reread_in (VFolderInfo *info, int msec)
1308 G_LOCK (vfolder_lock);
1309 if (info->reread_queue != 0)
1310 g_source_remove (info->reread_queue);
1311 info->reread_queue = g_timeout_add (msec, reread_timeout, info);
1312 G_UNLOCK (vfolder_lock);
1316 vfolder_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1317 const gchar *monitor_uri,
1318 const gchar *info_uri,
1319 GnomeVFSMonitorEventType event_type,
1322 /* FIXME: implement */
1326 vfolder_user_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1327 const gchar *monitor_uri,
1328 const gchar *info_uri,
1329 GnomeVFSMonitorEventType event_type,
1332 /* FIXME: implement */
1336 vfolder_filename_monitor (GnomeVFSMonitorHandle *handle,
1337 const gchar *monitor_uri,
1338 const gchar *info_uri,
1339 GnomeVFSMonitorEventType event_type,
1342 VFolderInfo *info = user_data;
1344 if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1345 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1346 ! info->user_file_active) {
1347 queue_reread_in (info, 200);
1348 } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1349 ! info->user_file_active) {
1350 /* FIXME: is this correct? I mean now
1351 * there probably isn't ANY vfolder file, so we
1352 * init to default values really. I have no clue what's
1354 vfolder_info_reload (info, NULL, NULL,
1355 TRUE /* force read items */);
1360 vfolder_user_filename_monitor (GnomeVFSMonitorHandle *handle,
1361 const gchar *monitor_uri,
1362 const gchar *info_uri,
1363 GnomeVFSMonitorEventType event_type,
1366 VFolderInfo *info = user_data;
1368 if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1369 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1370 info->user_file_active) {
1373 /* see if this was really our own change */
1374 if (info->user_filename_last_write == time (NULL))
1376 /* anal retentive */
1377 if (stat (info->user_filename, &s) == 0 &&
1378 info->user_filename_last_write == s.st_ctime)
1381 queue_reread_in (info, 200);
1382 } else if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1383 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1384 ! info->user_file_active) {
1385 queue_reread_in (info, 200);
1386 } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1387 info->user_file_active) {
1388 gboolean force_read_items = info->file_monitors != NULL;
1389 vfolder_info_reload (info, NULL, NULL, force_read_items);
1394 item_dir_monitor (GnomeVFSMonitorHandle *handle,
1395 const gchar *monitor_uri,
1396 const gchar *info_uri,
1397 GnomeVFSMonitorEventType event_type,
1400 VFolderInfo *info = user_data;
1402 if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1403 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
1404 /* first invalidate all folders */
1405 invalidate_folder (info->root);
1406 /* second invalidate all entries */
1407 info->entries_valid = FALSE;
1409 if (info->file_monitors != NULL) {
1410 GnomeVFSResult result;
1413 /* Whack all monitors here! */
1414 for (li = info->file_monitors;
1417 FileMonitorHandle *h = li->data;
1418 if (h->handle != NULL)
1419 gnome_vfs_monitor_cancel (h->handle);
1423 if (vfolder_info_read_items (info, &result, NULL)) {
1424 info->entries_valid = TRUE;
1431 setup_dir_monitor (VFolderInfo *info, const char *dir, gboolean subdirs,
1432 GnomeVFSResult *result,
1433 GnomeVFSContext *context)
1435 GnomeVFSMonitorHandle *handle;
1440 uri = gnome_vfs_get_uri_from_local_path (dir);
1442 if (gnome_vfs_monitor_add (&handle,
1444 GNOME_VFS_MONITOR_DIRECTORY,
1446 info) != GNOME_VFS_OK) {
1447 StatLoc *sl = bake_statloc (dir, time (NULL));
1449 info->stat_dirs = g_slist_prepend (info->stat_dirs, sl);
1455 if (gnome_vfs_context_check_cancellation (context)) {
1456 gnome_vfs_monitor_cancel (handle);
1457 *result = GNOME_VFS_ERROR_CANCELLED;
1461 info->item_dir_monitors =
1462 g_slist_prepend (info->item_dir_monitors, handle);
1471 while ((de = readdir (dh)) != NULL) {
1474 if (gnome_vfs_context_check_cancellation (context)) {
1475 *result = GNOME_VFS_ERROR_CANCELLED;
1480 if (de->d_name[0] == '.')
1483 full_path = g_build_filename (dir, de->d_name, NULL);
1484 if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
1485 if ( ! setup_dir_monitor (info, full_path,
1501 monitor_setup (VFolderInfo *info,
1502 gboolean setup_filenames,
1503 gboolean setup_itemdirs,
1504 gboolean setup_desktop_dirs,
1505 GnomeVFSResult *result,
1506 GnomeVFSContext *context)
1511 if (setup_filenames) {
1512 uri = gnome_vfs_get_uri_from_local_path
1515 if (gnome_vfs_monitor_add (&info->filename_monitor,
1517 GNOME_VFS_MONITOR_FILE,
1518 vfolder_filename_monitor,
1519 info) != GNOME_VFS_OK) {
1520 info->filename_monitor = NULL;
1521 info->filename_statloc = bake_statloc (info->filename,
1526 if (setup_filenames &&
1527 info->user_filename != NULL) {
1528 uri = gnome_vfs_get_uri_from_local_path
1529 (info->user_filename);
1530 if (gnome_vfs_monitor_add (&info->user_filename_monitor,
1532 GNOME_VFS_MONITOR_FILE,
1533 vfolder_user_filename_monitor,
1534 info) != GNOME_VFS_OK) {
1535 info->user_filename_monitor = NULL;
1536 info->user_filename_statloc =
1537 bake_statloc (info->user_filename,
1544 if (gnome_vfs_context_check_cancellation (context)) {
1545 *result = GNOME_VFS_ERROR_CANCELLED;
1549 if (setup_itemdirs) {
1550 for (li = info->item_dirs; li != NULL; li = li->next) {
1551 const char *dir = li->data;
1552 if ( ! setup_dir_monitor (info, dir,
1553 FALSE /* subdirs */,
1557 if (info->user_item_dir != NULL) {
1558 if ( ! setup_dir_monitor (info, info->user_item_dir,
1559 FALSE /* subdirs */,
1563 for (li = info->merge_dirs; li != NULL; li = li->next) {
1564 const char *dir = li->data;
1565 if ( ! setup_dir_monitor (info, dir,
1572 if (setup_desktop_dirs) {
1573 uri = gnome_vfs_get_uri_from_local_path
1574 (info->desktop_dir);
1576 if (gnome_vfs_monitor_add (&info->desktop_dir_monitor,
1578 GNOME_VFS_MONITOR_FILE,
1579 vfolder_desktop_dir_monitor,
1580 info) != GNOME_VFS_OK) {
1581 info->desktop_dir_monitor = NULL;
1582 info->desktop_dir_statloc =
1583 bake_statloc (info->desktop_dir,
1588 if (setup_desktop_dirs &&
1589 info->user_desktop_dir != NULL) {
1590 uri = gnome_vfs_get_uri_from_local_path
1591 (info->user_desktop_dir);
1592 if (gnome_vfs_monitor_add (&info->user_desktop_dir_monitor,
1594 GNOME_VFS_MONITOR_DIRECTORY,
1595 vfolder_user_desktop_dir_monitor,
1596 info) != GNOME_VFS_OK) {
1597 info->user_desktop_dir_monitor = NULL;
1598 info->user_desktop_dir_statloc =
1599 bake_statloc (info->user_desktop_dir,
1610 vfolder_info_init (VFolderInfo *info, const char *scheme)
1615 info->scheme = g_strdup (scheme);
1617 info->filename = g_strconcat (SYSCONFDIR, "/X11/desktop-menus/",
1620 info->user_filename = g_strconcat (g_get_home_dir (),
1621 "/" DOT_GNOME "/vfolders/",
1622 scheme, ".vfolder-info",
1624 info->desktop_dir = g_strconcat (SYSCONFDIR,
1625 "/gnome-vfs-2.0/vfolders/",
1627 info->user_desktop_dir = g_strconcat (g_get_home_dir (),
1628 "/" DOT_GNOME "/vfolders/",
1631 /* Init the desktop paths */
1633 list = g_slist_prepend (list, g_strdup ("/usr/share/applications/"));
1634 if (strcmp ("/usr/share/applications/", DATADIR "/applications/") != 0)
1635 list = g_slist_prepend (list, g_strdup (DATADIR "/applications/"));
1636 path = g_getenv ("DESKTOP_FILE_PATH");
1639 char **ppath = g_strsplit (path, ":", -1);
1640 for (i = 0; ppath[i] != NULL; i++) {
1641 const char *dir = ppath[i];
1642 list = g_slist_prepend (list, g_strdup (dir));
1646 info->item_dirs = g_slist_reverse (list);
1648 info->user_item_dir = g_strconcat (g_get_home_dir (),
1649 "/" DOT_GNOME "/vfolders/",
1653 info->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1655 info->root = folder_new ("Root");
1657 info->modification_time = time (NULL);
1661 vfolder_info_free_internals_unlocked (VFolderInfo *info)
1666 if (info->filename_monitor != NULL) {
1667 gnome_vfs_monitor_cancel (info->filename_monitor);
1668 info->filename_monitor = NULL;
1671 if (info->user_filename_monitor != NULL) {
1672 gnome_vfs_monitor_cancel (info->user_filename_monitor);
1673 info->user_filename_monitor = NULL;
1676 g_free (info->filename_statloc);
1677 info->filename_statloc = NULL;
1679 g_free (info->user_filename_statloc);
1680 info->user_filename_statloc = NULL;
1683 if (info->desktop_dir_monitor != NULL) {
1684 gnome_vfs_monitor_cancel (info->desktop_dir_monitor);
1685 info->desktop_dir_monitor = NULL;
1688 if (info->user_desktop_dir_monitor != NULL) {
1689 gnome_vfs_monitor_cancel (info->user_desktop_dir_monitor);
1690 info->user_desktop_dir_monitor = NULL;
1693 g_free (info->desktop_dir_statloc);
1694 info->desktop_dir_statloc = NULL;
1696 g_free (info->user_desktop_dir_statloc);
1697 info->user_desktop_dir_statloc = NULL;
1700 g_slist_foreach (info->item_dir_monitors,
1701 (GFunc)gnome_vfs_monitor_cancel, NULL);
1702 g_slist_free (info->item_dir_monitors);
1703 info->item_dir_monitors = NULL;
1705 g_free (info->scheme);
1706 info->scheme = NULL;
1708 g_free (info->filename);
1709 info->filename = NULL;
1711 g_free (info->user_filename);
1712 info->user_filename = NULL;
1714 g_free (info->desktop_dir);
1715 info->desktop_dir = NULL;
1717 g_free (info->user_desktop_dir);
1718 info->user_desktop_dir = NULL;
1720 g_slist_foreach (info->item_dirs, (GFunc)g_free, NULL);
1721 g_slist_free (info->item_dirs);
1722 info->item_dirs = NULL;
1724 g_free (info->user_item_dir);
1725 info->user_item_dir = NULL;
1727 g_slist_foreach (info->merge_dirs, (GFunc)g_free, NULL);
1728 g_slist_free (info->merge_dirs);
1729 info->merge_dirs = NULL;
1731 g_slist_foreach (info->entries, (GFunc)entry_unref, NULL);
1732 g_slist_free (info->entries);
1733 info->entries = NULL;
1735 if (info->entries_ht != NULL)
1736 g_hash_table_destroy (info->entries_ht);
1737 info->entries_ht = NULL;
1739 g_slist_foreach (info->unallocated_folders,
1742 g_slist_free (info->unallocated_folders);
1743 info->unallocated_folders = NULL;
1745 entry_unref ((Entry *)info->root);
1748 g_slist_foreach (info->stat_dirs, (GFunc)g_free, NULL);
1749 g_slist_free (info->stat_dirs);
1750 info->stat_dirs = NULL;
1752 g_slist_foreach (info->folder_monitors,
1753 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1754 g_slist_free (info->folder_monitors);
1755 info->folder_monitors = NULL;
1757 g_slist_foreach (info->free_folder_monitors,
1758 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1759 g_slist_free (info->free_folder_monitors);
1760 info->free_folder_monitors = NULL;
1762 g_slist_foreach (info->file_monitors,
1763 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1764 g_slist_free (info->file_monitors);
1765 info->file_monitors = NULL;
1767 g_slist_foreach (info->free_file_monitors,
1768 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1769 g_slist_free (info->free_file_monitors);
1770 info->free_file_monitors = NULL;
1772 if (info->reread_queue != 0)
1773 g_source_remove (info->reread_queue);
1774 info->reread_queue = 0;
1778 vfolder_info_free_internals (VFolderInfo *info)
1780 G_LOCK (vfolder_lock);
1781 vfolder_info_free_internals_unlocked (info);
1782 G_UNLOCK (vfolder_lock);
1786 vfolder_info_destroy (VFolderInfo *info)
1788 vfolder_info_free_internals (info);
1793 single_query_read (xmlNode *qnode)
1798 if (qnode->type != XML_ELEMENT_NODE ||
1799 qnode->name == NULL)
1804 if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
1805 qnode->xmlChildrenNode != NULL) {
1808 for (iter = qnode->xmlChildrenNode;
1809 iter != NULL && query == NULL;
1811 query = single_query_read (iter);
1812 if (query != NULL) {
1813 query->not = ! query->not;
1816 } else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
1817 xmlChar *word = xmlNodeGetContent (qnode);
1819 query = query_new (QUERY_KEYWORD);
1820 ((QueryKeyword *)query)->keyword =
1821 g_quark_from_string (word);
1826 } else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
1827 xmlChar *file = xmlNodeGetContent (qnode);
1829 query = query_new (QUERY_FILENAME);
1830 ((QueryFilename *)query)->filename =
1836 } else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
1837 query = query_new (QUERY_AND);
1838 } else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
1839 query = query_new (QUERY_OR);
1841 /* We don't understand */
1845 /* This must be OR or AND */
1846 g_assert (query != NULL);
1848 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1849 Query *new_query = single_query_read (node);
1851 if (new_query != NULL)
1852 query->queries = g_slist_prepend
1853 (query->queries, new_query);
1856 query->queries = g_slist_reverse (query->queries);
1862 add_or_set_query (Query **query, Query *new_query)
1864 if (*query == NULL) {
1867 Query *old_query = *query;
1868 *query = query_new (QUERY_OR);
1870 g_slist_append ((*query)->queries, old_query);
1872 g_slist_append ((*query)->queries, new_query);
1877 query_read (xmlNode *qnode)
1884 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1885 if (node->type != XML_ELEMENT_NODE ||
1889 if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
1890 node->xmlChildrenNode != NULL) {
1892 Query *new_query = NULL;
1894 for (iter = node->xmlChildrenNode;
1895 iter != NULL && new_query == NULL;
1897 new_query = single_query_read (iter);
1898 if (new_query != NULL) {
1899 new_query->not = ! new_query->not;
1900 add_or_set_query (&query, new_query);
1903 Query *new_query = single_query_read (node);
1904 if (new_query != NULL)
1905 add_or_set_query (&query, new_query);
1913 folder_read (VFolderInfo *info, xmlNode *fnode)
1918 folder = folder_new (NULL);
1920 for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
1921 if (node->type != XML_ELEMENT_NODE ||
1925 if (g_ascii_strcasecmp (node->name, "Name") == 0) {
1926 xmlChar *name = xmlNodeGetContent (node);
1928 g_free (folder->entry.name);
1929 folder->entry.name = g_strdup (name);
1932 } else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
1933 xmlChar *desktop = xmlNodeGetContent (node);
1934 if (desktop != NULL) {
1935 g_free (folder->desktop_file);
1936 folder->desktop_file = g_strdup (desktop);
1939 } else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
1940 xmlChar *file = xmlNodeGetContent (node);
1943 char *str = g_strdup (file);
1944 folder->includes = g_slist_prepend
1945 (folder->includes, str);
1946 if (folder->includes_ht == NULL) {
1947 folder->includes_ht =
1948 g_hash_table_new_full
1954 li = g_hash_table_lookup (folder->includes_ht,
1958 /* Note: this will NOT change folder->includes
1960 folder->includes = g_slist_delete_link
1961 (folder->includes, li);
1963 g_hash_table_replace (folder->includes_ht,
1964 file, folder->includes);
1967 } else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
1968 xmlChar *file = xmlNodeGetContent (node);
1971 if (folder->excludes == NULL) {
1972 folder->excludes = g_hash_table_new_full
1975 (GDestroyNotify)g_free,
1978 s = g_strdup (file);
1979 g_hash_table_replace (folder->excludes, s, s);
1982 } else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
1985 query = query_read (node);
1987 if (query != NULL) {
1988 if (folder->query != NULL)
1989 query_destroy (folder->query);
1990 folder->query = query;
1992 } else if (g_ascii_strcasecmp (node->name, "OnlyUnallocated") == 0) {
1993 info->unallocated_folders =
1994 g_slist_prepend (info->unallocated_folders,
1995 (Folder *)entry_ref ((Entry *)folder));
1996 folder->only_unallocated = TRUE;
1997 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
1998 Folder *new_folder = folder_read (info, node);
1999 if (new_folder != NULL) {
2000 folder->subfolders =
2001 g_slist_append (folder->subfolders,
2003 new_folder->parent = folder;
2005 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2006 folder->read_only = TRUE;
2007 } else if (g_ascii_strcasecmp (node->name,
2008 "DontShowIfEmpty") == 0) {
2009 folder->dont_show_if_empty = TRUE;
2013 /* Name is required */
2014 if (folder->entry.name == NULL) {
2015 entry_unref ((Entry *)folder);
2019 folder->includes = g_slist_reverse (folder->includes);
2025 subst_home (const char *dir)
2028 return g_strconcat (g_get_home_dir (),
2032 return g_strdup (dir);
2035 /* FORMAT looks like:
2037 * <!-- Merge dirs optional -->
2038 * <MergeDir>/etc/X11/applnk</MergeDir>
2039 * <!-- Only specify if it should override standard location -->
2040 * <ItemDir>/usr/share/applications</ItemDir>
2041 * <!-- This is where the .directories are -->
2042 * <DesktopDir>/etc/X11/gnome/vfolders</DesktopDir>
2043 * <!-- Root folder -->
2047 * <Include>important.desktop</Include>
2049 * <!-- Other folders -->
2051 * <Name>SomeFolder</Name>
2054 * <Name>Test_Folder</Name>
2055 * <!-- could also be absolute -->
2056 * <Desktop>Test_Folder.directory</Desktop>
2060 * <Keyword>Application</Keyword>
2061 * <Keyword>Game</Keyword>
2063 * <Keyword>Clock</Keyword>
2066 * <Include>somefile.desktop</Include>
2067 * <Include>someotherfile.desktop</Include>
2068 * <Exclude>yetanother.desktop</Exclude>
2075 vfolder_info_read_info (VFolderInfo *info,
2076 GnomeVFSResult *result,
2077 GnomeVFSContext *context)
2081 gboolean got_a_vfolder_dir = FALSE;
2084 if (info->user_filename != NULL &&
2085 access (info->user_filename, F_OK) == 0) {
2086 doc = xmlParseFile (info->user_filename);
2088 info->user_file_active = TRUE;
2091 access (info->filename, F_OK) == 0)
2092 doc = xmlParseFile (info->filename);
2094 if (gnome_vfs_context_check_cancellation (context)) {
2096 *result = GNOME_VFS_ERROR_CANCELLED;
2101 || doc->xmlRootNode == NULL
2102 || doc->xmlRootNode->name == NULL
2103 || g_ascii_strcasecmp (doc->xmlRootNode->name, "VFolderInfo") != 0) {
2105 return TRUE; /* FIXME: really, shouldn't we error out? */
2108 for (node = doc->xmlRootNode->xmlChildrenNode; node != NULL; node = node->next) {
2109 if (node->type != XML_ELEMENT_NODE ||
2113 if (gnome_vfs_context_check_cancellation (context)) {
2115 *result = GNOME_VFS_ERROR_CANCELLED;
2119 if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
2120 xmlChar *dir = xmlNodeGetContent (node);
2122 info->merge_dirs = g_slist_append (info->merge_dirs,
2126 } else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
2127 xmlChar *dir = xmlNodeGetContent (node);
2129 if ( ! got_a_vfolder_dir) {
2130 g_slist_foreach (info->item_dirs,
2131 (GFunc)g_free, NULL);
2132 g_slist_free (info->item_dirs);
2133 info->item_dirs = NULL;
2135 got_a_vfolder_dir = TRUE;
2136 info->item_dirs = g_slist_append (info->item_dirs,
2140 } else if (g_ascii_strcasecmp (node->name, "UserItemDir") == 0) {
2141 xmlChar *dir = xmlNodeGetContent (node);
2143 g_free (info->user_item_dir);
2144 info->user_item_dir = subst_home (dir);
2147 } else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
2148 xmlChar *dir = xmlNodeGetContent (node);
2150 g_free (info->desktop_dir);
2151 info->desktop_dir = g_strdup (dir);
2154 } else if (g_ascii_strcasecmp (node->name, "UserDesktopDir") == 0) {
2155 xmlChar *dir = xmlNodeGetContent (node);
2157 g_free (info->user_desktop_dir);
2158 info->user_desktop_dir = subst_home (dir);
2161 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
2162 Folder *folder = folder_read (info, node);
2163 if (folder != NULL) {
2164 if (info->root != NULL)
2165 entry_unref ((Entry *)info->root);
2166 info->root = folder;
2168 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2169 info->read_only = TRUE;
2179 add_xml_tree_from_query (xmlNode *parent, Query *query)
2181 xmlNode *real_parent;
2184 real_parent = xmlNewChild (parent /* parent */,
2187 NULL /* content */);
2189 real_parent = parent;
2191 if (query->type == QUERY_KEYWORD) {
2192 QueryKeyword *qkeyword = (QueryKeyword *)query;
2193 const char *string = g_quark_to_string (qkeyword->keyword);
2195 xmlNewChild (real_parent /* parent */,
2197 "Keyword" /* name */,
2198 string /* content */);
2199 } else if (query->type == QUERY_FILENAME) {
2200 QueryFilename *qfilename = (QueryFilename *)query;
2202 xmlNewChild (real_parent /* parent */,
2204 "Filename" /* name */,
2205 qfilename->filename /* content */);
2206 } else if (query->type == QUERY_OR ||
2207 query->type == QUERY_AND) {
2212 if (query->type == QUERY_OR)
2214 else /* QUERY_AND */
2217 node = xmlNewChild (real_parent /* parent */,
2220 NULL /* content */);
2222 for (li = query->queries; li != NULL; li = li->next) {
2223 Query *subquery = li->data;
2224 add_xml_tree_from_query (node, subquery);
2227 g_assert_not_reached ();
2232 add_excludes_to_xml (gpointer key, gpointer value, gpointer user_data)
2234 const char *filename = key;
2235 xmlNode *folder_node = user_data;
2237 xmlNewChild (folder_node /* parent */,
2239 "Exclude" /* name */,
2240 filename /* content */);
2244 add_xml_tree_from_folder (xmlNode *parent, Folder *folder)
2247 xmlNode *folder_node;
2250 folder_node = xmlNewChild (parent /* parent */,
2252 "Folder" /* name */,
2253 NULL /* content */);
2255 xmlNewChild (folder_node /* parent */,
2258 folder->entry.name /* content */);
2260 if (folder->desktop_file != NULL) {
2261 xmlNewChild (folder_node /* parent */,
2263 "Desktop" /* name */,
2264 folder->desktop_file /* content */);
2267 if (folder->read_only)
2268 xmlNewChild (folder_node /* parent */,
2270 "ReadOnly" /* name */,
2271 NULL /* content */);
2272 if (folder->dont_show_if_empty)
2273 xmlNewChild (folder_node /* parent */,
2275 "DontShowIfEmpty" /* name */,
2276 NULL /* content */);
2277 if (folder->only_unallocated)
2278 xmlNewChild (folder_node /* parent */,
2280 "OnlyUnallocated" /* name */,
2281 NULL /* content */);
2283 for (li = folder->subfolders; li != NULL; li = li->next) {
2284 Folder *subfolder = li->data;
2285 add_xml_tree_from_folder (folder_node, subfolder);
2288 for (li = folder->includes; li != NULL; li = li->next) {
2289 const char *include = li->data;
2290 xmlNewChild (folder_node /* parent */,
2292 "Include" /* name */,
2293 include /* content */);
2296 if (folder->excludes) {
2297 g_hash_table_foreach (folder->excludes,
2298 add_excludes_to_xml,
2302 if (folder->query != NULL) {
2303 xmlNode *query_node;
2304 query_node = xmlNewChild (folder_node /* parent */,
2307 NULL /* content */);
2309 add_xml_tree_from_query (query_node, folder->query);
2314 xml_tree_from_vfolder (VFolderInfo *info)
2320 doc = xmlNewDoc ("1.0");
2322 topnode = xmlNewDocNode (doc /* doc */,
2324 "VFolderInfo" /* name */,
2325 NULL /* content */);
2326 doc->xmlRootNode = topnode;
2328 for (li = info->merge_dirs; li != NULL; li = li->next) {
2329 const char *merge_dir = li->data;
2330 xmlNewChild (topnode /* parent */,
2332 "MergeDir" /* name */,
2333 merge_dir /* content */);
2336 for (li = info->item_dirs; li != NULL; li = li->next) {
2337 const char *item_dir = li->data;
2338 xmlNewChild (topnode /* parent */,
2340 "ItemDir" /* name */,
2341 item_dir /* content */);
2344 if (info->user_item_dir != NULL) {
2345 xmlNewChild (topnode /* parent */,
2347 "UserItemDir" /* name */,
2348 info->user_item_dir /* content */);
2351 if (info->desktop_dir != NULL) {
2352 xmlNewChild (topnode /* parent */,
2354 "DesktopDir" /* name */,
2355 info->desktop_dir /* content */);
2358 if (info->user_desktop_dir != NULL) {
2359 xmlNewChild (topnode /* parent */,
2361 "UserDesktopDir" /* name */,
2362 info->user_desktop_dir /* content */);
2365 if (info->root != NULL)
2366 add_xml_tree_from_folder (topnode, info->root);
2371 /* FIXME: what to do about errors */
2373 vfolder_info_write_user (VFolderInfo *info)
2377 if (info->inhibit_write > 0)
2380 if (info->user_filename == NULL)
2383 doc = xml_tree_from_vfolder (info);
2387 /* FIXME: errors, anyone? */
2388 ensure_dir (info->user_filename,
2389 TRUE /* ignore_basename */);
2391 xmlSaveFormatFile (info->user_filename, doc, TRUE /* format */);
2392 /* not as good as a stat, but cheaper ... hmmm what is
2393 * the likelyhood of this not being the same as ctime */
2394 info->user_filename_last_write = time (NULL);
2398 info->user_file_active = TRUE;
2399 info->dirty = FALSE;
2401 info->modification_time = time (NULL);
2404 /* An EVIL function for quick reading of .desktop files,
2405 * only reads in one or two keys, but that's ALL we need */
2407 readitem_entry (const char *filename,
2415 int keylen1, keylen2;
2418 if (result2 != NULL)
2421 fp = fopen (filename, "r");
2426 keylen1 = strlen (key1);
2428 keylen2 = strlen (key2);
2432 /* This is slightly wrong, it should only look
2433 * at the correct section */
2434 while (fgets (buf, sizeof (buf), fp) != NULL) {
2438 char **result = NULL;
2440 /* check if it's one of the keys */
2441 if (strncmp (buf, key1, keylen1) == 0) {
2444 } else if (keylen2 >= 0 &&
2445 strncmp (buf, key2, keylen2) == 0) {
2454 /* still not our key */
2455 if (!(*p == '=' || *p == ' ')) {
2460 while (*p == ' ' || *p == '=');
2462 /* get rid of trailing \n */
2464 if (p[len-1] == '\n' ||
2468 *result = g_strdup (p);
2470 if (*result1 != NULL &&
2471 (result2 == NULL || *result2 != NULL))
2479 vfolder_info_insert_entry (VFolderInfo *info, EntryFile *efile)
2483 entry_ref ((Entry *)efile);
2485 entry_list = g_hash_table_lookup (info->entries_ht, efile->entry.name);
2487 info->entries = g_slist_prepend (info->entries, efile);
2488 /* The hash table contains the GSList pointer */
2489 g_hash_table_replace (info->entries_ht, efile->entry.name,
2492 if (entry_list != NULL) {
2493 Entry *entry = entry_list->data;
2494 info->entries = g_slist_delete_link (info->entries,
2496 entry_unref (entry);
2501 set_keywords (EntryFile *efile, const char *keywords)
2503 if (keywords != NULL) {
2505 char **parsed = g_strsplit (keywords, ";", -1);
2506 for (i = 0; parsed[i] != NULL; i++) {
2508 const char *word = parsed[i];
2509 /* ignore empties (including end of list) */
2510 if (word[0] == '\0')
2512 quark = g_quark_from_string (word);
2513 efile->keywords = g_slist_prepend
2515 GINT_TO_POINTER (quark));
2517 g_strfreev (parsed);
2522 make_entry_file (const char *dir, const char *name)
2530 filename = g_build_filename (dir, name, NULL);
2532 readitem_entry (filename,
2538 if (only_show_in != NULL) {
2539 gboolean show = FALSE;
2540 char **parsed = g_strsplit (only_show_in, ";", -1);
2541 for (i = 0; parsed[i] != NULL; i++) {
2542 if (strcmp (parsed[i], "GNOME") == 0) {
2547 g_strfreev (parsed);
2550 g_free (only_show_in);
2551 g_free (categories);
2556 efile = file_new (name);
2557 efile->filename = filename;
2559 set_keywords (efile, categories);
2561 g_free (only_show_in);
2562 g_free (categories);
2568 vfolder_info_read_items_from (VFolderInfo *info,
2569 const char *item_dir,
2571 GnomeVFSResult *result,
2572 GnomeVFSContext *context)
2577 dir = opendir (item_dir);
2581 while ((de = readdir (dir)) != NULL) {
2584 if (gnome_vfs_context_check_cancellation (context)) {
2586 *result = GNOME_VFS_ERROR_CANCELLED;
2590 /* files MUST be called .desktop */
2591 if (de->d_name[0] == '.' ||
2592 ! check_ext (de->d_name, ".desktop"))
2595 efile = make_entry_file (item_dir, de->d_name);
2599 efile->per_user = per_user;
2601 vfolder_info_insert_entry (info, efile);
2602 entry_unref ((Entry *)efile);
2611 vfolder_info_read_items_merge (VFolderInfo *info,
2612 const char *merge_dir,
2614 GQuark inherited_keyword,
2615 GnomeVFSResult *result,
2616 GnomeVFSContext *context)
2620 GQuark extra_keyword;
2624 gboolean pass_down_extra_keyword = TRUE;
2626 dir = opendir (merge_dir);
2630 Application = g_quark_from_static_string ("Application");
2631 Merged = g_quark_from_static_string ("Merged");
2633 /* FIXME: this should be a hash or something */
2635 if (subdir == NULL) {
2636 extra_keyword = g_quark_from_static_string ("Core");
2637 pass_down_extra_keyword = FALSE;
2638 } else if (g_ascii_strcasecmp (subdir, "Development") == 0)
2639 extra_keyword = g_quark_from_static_string ("Development");
2640 else if (g_ascii_strcasecmp (subdir, "Editors") == 0)
2641 extra_keyword = g_quark_from_static_string ("TextEditor");
2642 else if (g_ascii_strcasecmp (subdir, "Games") == 0)
2643 extra_keyword = g_quark_from_static_string ("Game");
2644 else if (g_ascii_strcasecmp (subdir, "Graphics") == 0)
2645 extra_keyword = g_quark_from_static_string ("Graphics");
2646 else if (g_ascii_strcasecmp (subdir, "Internet") == 0)
2647 extra_keyword = g_quark_from_static_string ("Network");
2648 else if (g_ascii_strcasecmp (subdir, "Multimedia") == 0)
2649 extra_keyword = g_quark_from_static_string ("AudioVideo");
2650 else if (g_ascii_strcasecmp (subdir, "Office") == 0)
2651 extra_keyword = g_quark_from_static_string ("Office");
2652 else if (g_ascii_strcasecmp (subdir, "Settings") == 0)
2653 extra_keyword = g_quark_from_static_string ("Settings");
2654 else if (g_ascii_strcasecmp (subdir, "System") == 0)
2655 extra_keyword = g_quark_from_static_string ("System");
2656 else if (g_ascii_strcasecmp (subdir, "Utilities") == 0)
2657 extra_keyword = g_quark_from_static_string ("Utility");
2659 while ((de = readdir (dir)) != NULL) {
2662 if (gnome_vfs_context_check_cancellation (context)) {
2664 *result = GNOME_VFS_ERROR_CANCELLED;
2669 if (de->d_name[0] == '.')
2672 /* files MUST be called .desktop, so
2673 * treat all others as dirs. If we're wrong,
2674 * the open will fail, which is ok */
2675 if ( ! check_ext (de->d_name, ".desktop")) {
2676 /* if this is a directory recurse */
2677 char *fullname = g_build_filename (merge_dir, de->d_name, NULL);
2678 if ((pass_down_extra_keyword == TRUE) && (extra_keyword != 0)) {
2679 inheritance = extra_keyword;
2681 inheritance = inherited_keyword;
2684 if ( ! vfolder_info_read_items_merge (info,
2697 /* FIXME: add some keywords about some known apps
2698 * like gimp and whatnot, perhaps take these from the vfolder
2699 * file or some such */
2701 efile = make_entry_file (merge_dir, de->d_name);
2705 /* If no keywords set, then add the standard ones */
2706 if (efile->keywords == NULL) {
2707 efile->keywords = g_slist_prepend
2709 GINT_TO_POINTER (Application));
2711 efile->keywords = g_slist_prepend
2713 GINT_TO_POINTER (Merged));
2715 if (inherited_keyword != 0) {
2716 efile->keywords = g_slist_prepend
2718 GINT_TO_POINTER (inherited_keyword));
2721 if (extra_keyword != 0) {
2722 efile->keywords = g_slist_prepend
2724 GINT_TO_POINTER (extra_keyword));
2726 efile->implicit_keywords = TRUE;
2729 vfolder_info_insert_entry (info, efile);
2730 entry_unref ((Entry *)efile);
2739 find_entry (GSList *list, const char *name)
2743 for (li = list; li != NULL; li = li->next) {
2744 Entry *entry = li->data;
2745 if (strcmp (name, entry->name) == 0)
2752 file_monitor (GnomeVFSMonitorHandle *handle,
2753 const gchar *monitor_uri,
2754 const gchar *info_uri,
2755 GnomeVFSMonitorEventType event_type,
2758 FileMonitorHandle *h = user_data;
2760 /* proxy the event through if it is a changed event
2763 if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED &&
2765 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) h,
2766 h->uri, event_type);
2770 try_free_file_monitors_create_files_unlocked (VFolderInfo *info)
2774 list = g_slist_copy (info->free_file_monitors);
2776 for (li = list; li != NULL; li = li->next) {
2777 FileMonitorHandle *handle = li->data;
2779 GnomeVFSResult result;
2780 char *dirfile = NULL;
2782 if (handle->is_directory_file) {
2786 /* Evil! EVIL URI PARSING. this will eat a lot of
2787 * stack if we have lots of free monitors */
2789 VFOLDER_URI_PARSE (handle->uri, &vuri);
2791 folder = resolve_folder (info,
2793 TRUE /* ignore_basename */,
2800 dirfile = get_directory_file_unlocked (info, folder);
2801 if (dirfile == NULL)
2804 entry = (Entry *)folder;
2808 GnomeVFSResult result;
2812 /* Evil! EVIL URI PARSING. this will eat a lot of
2813 * stack if we have lots of monitors */
2815 VFOLDER_URI_PARSE (handle->uri, &vuri);
2817 f = resolve_folder (info,
2819 TRUE /* ignore_basename */,
2824 ensure_folder_unlocked (
2826 FALSE /* subfolders */,
2828 FALSE /* ignore_unallocated */);
2829 entry = find_entry (f->entries, vuri.file);
2836 info->free_file_monitors =
2837 g_slist_remove (info->free_file_monitors, handle);
2839 g_slist_prepend (entry->monitors, handle);
2841 handle->exists = TRUE;
2842 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
2844 GNOME_VFS_MONITOR_EVENT_CREATED);
2846 /* recreate a handle */
2847 if (handle->handle == NULL &&
2848 entry->type == ENTRY_FILE) {
2849 EntryFile *efile = (EntryFile *)entry;
2850 char *uri = gnome_vfs_get_uri_from_local_path
2853 gnome_vfs_monitor_add (&(handle->handle),
2855 GNOME_VFS_MONITOR_FILE,
2860 } else if (handle->handle == NULL &&
2862 char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2864 gnome_vfs_monitor_add (&(handle->handle),
2866 GNOME_VFS_MONITOR_FILE,
2876 g_slist_free (list);
2879 static void /* unlocked */
2880 rescan_monitors (VFolderInfo *info)
2884 if (info->file_monitors == NULL)
2887 for (li = info->file_monitors; li != NULL; li = li->next) {
2888 FileMonitorHandle *h = li->data;
2889 GnomeVFSResult result;
2891 char *dirfile = NULL;
2893 /* these are handled below */
2897 if (h->is_directory_file) {
2901 /* Evil! EVIL URI PARSING. this will eat a lot of
2902 * stack if we have lots of monitors */
2904 VFOLDER_URI_PARSE (h->uri, &vuri);
2906 folder = resolve_folder (info,
2908 TRUE /* ignore_basename */,
2912 dirfile = get_directory_file_unlocked (info,
2915 if (dirfile == NULL) {
2917 gnome_vfs_monitor_callback
2918 ((GnomeVFSMethodHandle *)h,
2920 GNOME_VFS_MONITOR_EVENT_DELETED);
2921 info->free_file_monitors = g_slist_prepend
2922 (info->free_file_monitors, h);
2923 file_monitor_handle_ref_unlocked (h);
2924 /* it has been unreffed when the entry was
2929 entry = (Entry *)folder;
2933 GnomeVFSResult result;
2937 /* Evil! EVIL URI PARSING. this will eat a lot of
2938 * stack if we have lots of monitors */
2940 VFOLDER_URI_PARSE (h->uri, &vuri);
2942 f = resolve_folder (info,
2944 TRUE /* ignore_basename */,
2949 ensure_folder_unlocked (
2951 FALSE /* subfolders */,
2953 FALSE /* ignore_unallocated */);
2954 entry = find_entry (f->entries, vuri.file);
2957 if (entry == NULL) {
2959 gnome_vfs_monitor_callback
2960 ((GnomeVFSMethodHandle *)h,
2962 GNOME_VFS_MONITOR_EVENT_DELETED);
2963 info->free_file_monitors = g_slist_prepend
2964 (info->free_file_monitors, h);
2965 file_monitor_handle_ref_unlocked (h);
2966 /* it has been unreffed when the entry was
2972 /* recreate a handle */
2973 if (h->handle == NULL &&
2974 entry->type == ENTRY_FILE) {
2975 EntryFile *efile = (EntryFile *)entry;
2976 char *uri = gnome_vfs_get_uri_from_local_path
2979 gnome_vfs_monitor_add (&(h->handle),
2981 GNOME_VFS_MONITOR_FILE,
2986 } else if (h->handle == NULL &&
2988 char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2990 gnome_vfs_monitor_add (&(h->handle),
2992 GNOME_VFS_MONITOR_FILE,
3002 try_free_file_monitors_create_files_unlocked (info);
3005 static gboolean /* unlocked */
3006 vfolder_info_read_items (VFolderInfo *info,
3007 GnomeVFSResult *result,
3008 GnomeVFSContext *context)
3013 for (li = info->merge_dirs; li != NULL; li = li->next) {
3014 const char *merge_dir = li->data;
3016 if ( ! vfolder_info_read_items_merge (info, merge_dir, NULL, FALSE,
3021 /* Then read the real thing (later overrides) */
3022 for (li = info->item_dirs; li != NULL; li = li->next) {
3023 const char *item_dir = li->data;
3025 if ( ! vfolder_info_read_items_from (info, item_dir,
3026 FALSE /* per_user */,
3031 if (info->user_item_dir != NULL) {
3032 if ( ! vfolder_info_read_items_from (info,
3033 info->user_item_dir,
3034 TRUE /* per_user */,
3039 rescan_monitors (info);
3045 string_slist_equal (GSList *list1, GSList *list2)
3049 for (li1 = list1, li2 = list2;
3050 li1 != NULL && li2 != NULL;
3051 li1 = li1->next, li2 = li2->next) {
3052 const char *s1 = li1->data;
3053 const char *s2 = li2->data;
3054 if (strcmp (s1, s2) != 0)
3057 /* if both are not NULL, then lengths are
3065 safe_string_same (const char *string1, const char *string2)
3067 if (string1 == string2 &&
3071 if (string1 != NULL && string2 != NULL &&
3072 strcmp (string1, string2) == 0)
3079 vfolder_info_item_dirs_same (VFolderInfo *info1, VFolderInfo *info2)
3081 if ( ! string_slist_equal (info1->item_dirs,
3085 if ( ! string_slist_equal (info1->merge_dirs,
3089 if ( ! safe_string_same (info1->user_item_dir,
3090 info2->user_item_dir))
3097 vfolder_info_reload_unlocked (VFolderInfo *info,
3098 GnomeVFSResult *result,
3099 GnomeVFSContext *context,
3100 gboolean force_read_items)
3102 VFolderInfo *newinfo;
3103 gboolean setup_filenames;
3104 gboolean setup_itemdirs;
3107 /* FIXME: Hmmm, race, there is no locking YAIKES,
3108 * we need filename locking for changes. eek, eek, eek */
3113 newinfo = g_new0 (VFolderInfo, 1);
3114 vfolder_info_init (newinfo, info->scheme);
3116 g_free (newinfo->filename);
3117 g_free (newinfo->user_filename);
3118 newinfo->filename = g_strdup (info->filename);
3119 newinfo->user_filename = g_strdup (info->user_filename);
3121 if (gnome_vfs_context_check_cancellation (context)) {
3122 vfolder_info_destroy (newinfo);
3123 *result = GNOME_VFS_ERROR_CANCELLED;
3127 if ( ! vfolder_info_read_info (newinfo, result, context)) {
3128 vfolder_info_destroy (newinfo);
3132 /* FIXME: reload logic for 'desktop_dir' and
3133 * 'user_desktop_dir' */
3135 setup_itemdirs = TRUE;
3137 /* Validity of entries and item dirs and all that is unchanged */
3138 if (vfolder_info_item_dirs_same (info, newinfo)) {
3139 newinfo->entries = info->entries;
3140 info->entries = NULL;
3141 newinfo->entries_ht = info->entries_ht;
3142 info->entries_ht = NULL /* some places assume this
3143 non-null, but we're only
3144 going to destroy this */;
3145 newinfo->entries_valid = info->entries_valid;
3147 /* move over the monitors/statlocs since those are valid */
3148 newinfo->item_dir_monitors = info->item_dir_monitors;
3149 info->item_dir_monitors = NULL;
3150 newinfo->stat_dirs = info->stat_dirs;
3151 info->stat_dirs = NULL;
3153 /* No need to resetup dir monitors */
3154 setup_itemdirs = FALSE;
3156 /* No need to do anything with file monitors */
3158 /* Whack all monitors here! */
3159 for (li = info->file_monitors; li != NULL; li = li->next) {
3160 FileMonitorHandle *h = li->data;
3161 if (h->handle != NULL)
3162 gnome_vfs_monitor_cancel (h->handle);
3167 setup_filenames = TRUE;
3169 if (safe_string_same (info->filename, newinfo->filename) &&
3170 safe_string_same (info->user_filename, newinfo->user_filename)) {
3171 newinfo->user_filename_last_write =
3172 info->user_filename_last_write;
3174 /* move over the monitors/statlocs since those are valid */
3175 newinfo->filename_monitor = info->filename_monitor;
3176 info->filename_monitor = NULL;
3177 newinfo->user_filename_monitor = info->user_filename_monitor;
3178 info->user_filename_monitor = NULL;
3180 if (info->filename_statloc != NULL &&
3181 info->filename != NULL)
3182 newinfo->filename_statloc =
3183 bake_statloc (info->filename,
3185 if (info->user_filename_statloc != NULL &&
3186 info->user_filename != NULL)
3187 newinfo->user_filename_statloc =
3188 bake_statloc (info->user_filename,
3191 /* No need to resetup filename monitors */
3192 setup_filenames = FALSE;
3195 /* Note: not cancellable anymore, since we've
3196 * already started nibbling on the info structure,
3197 * so we'd need to back things out or some such,
3198 * too complex, so screw that */
3199 monitor_setup (info,
3202 /* FIXME: setup_desktop_dirs */ TRUE,
3205 for (li = info->folder_monitors;
3208 FileMonitorHandle *handle = li->data;
3211 add_folder_monitor_unlocked (newinfo, handle);
3213 file_monitor_handle_unref_unlocked (handle);
3215 g_slist_free (info->folder_monitors);
3216 info->folder_monitors = NULL;
3218 g_slist_foreach (info->free_folder_monitors,
3219 (GFunc)file_monitor_handle_unref_unlocked, NULL);
3220 g_slist_free (info->free_folder_monitors);
3221 info->folder_monitors = NULL;
3223 /* we can just copy these for now, they will be readded
3224 * and all the fun stuff will be done with them later */
3225 newinfo->file_monitors = info->file_monitors;
3226 info->file_monitors = NULL;
3227 newinfo->free_file_monitors = info->free_file_monitors;
3228 info->free_file_monitors = NULL;
3230 /* emit changed on all folders, a bit drastic, but oh well,
3231 * we also invalidate all folders at the same time, but that is
3232 * irrelevant since they should all just be invalid to begin with */
3233 invalidate_folder_T (info->root);
3235 /* FIXME: make sure if this was enough, I think it was */
3237 vfolder_info_free_internals_unlocked (info);
3238 memcpy (info, newinfo, sizeof (VFolderInfo));
3241 /* must rescan the monitors here */
3242 if (info->entries_valid) {
3243 rescan_monitors (info);
3246 if ( ! info->entries_valid &&
3249 /* FIXME: I bet cancelation plays havoc with monitors,
3250 * I'm not sure however */
3251 if (info->file_monitors != NULL) {
3252 vfolder_info_read_items (info, &res, NULL);
3254 if ( ! vfolder_info_read_items (info, result, context))
3257 info->entries_valid = TRUE;
3264 vfolder_info_reload (VFolderInfo *info,
3265 GnomeVFSResult *result,
3266 GnomeVFSContext *context,
3267 gboolean force_read_items)
3269 G_LOCK (vfolder_lock);
3270 if (vfolder_info_reload_unlocked (info, result, context,
3271 force_read_items)) {
3272 G_UNLOCK (vfolder_lock);
3275 G_UNLOCK (vfolder_lock);
3281 vfolder_info_recheck (VFolderInfo *info,
3282 GnomeVFSResult *result,
3283 GnomeVFSContext *context)
3286 time_t curtime = time (NULL);
3287 gboolean reread = FALSE;
3289 if (info->filename_statloc != NULL &&
3290 ! check_statloc (info->filename_statloc, curtime)) {
3291 if ( ! vfolder_info_reload_unlocked (info, result, context,
3292 FALSE /* force read items */)) {
3293 /* we have failed, make sure we fail
3295 info->filename_statloc->trigger_next = TRUE;
3301 info->user_filename_statloc != NULL &&
3302 ! check_statloc (info->user_filename_statloc, curtime)) {
3303 if ( ! vfolder_info_reload_unlocked (info, result, context,
3304 FALSE /* force read items */)) {
3305 /* we have failed, make sure we fail
3307 info->user_filename_statloc->trigger_next = TRUE;
3313 if (info->entries_valid) {
3314 for (li = info->stat_dirs; li != NULL; li = li->next) {
3315 StatLoc *sl = li->data;
3316 if ( ! check_statloc (sl, curtime)) {
3317 info->entries_valid = FALSE;
3325 static VFolderInfo *
3326 get_vfolder_info_unlocked (const char *scheme,
3327 GnomeVFSResult *result,
3328 GnomeVFSContext *context)
3332 if (infos != NULL &&
3333 (info = g_hash_table_lookup (infos, scheme)) != NULL) {
3334 if ( ! vfolder_info_recheck (info, result, context)) {
3337 if ( ! info->entries_valid) {
3338 g_slist_foreach (info->entries,
3339 (GFunc)entry_unref, NULL);
3340 g_slist_free (info->entries);
3341 info->entries = NULL;
3343 if (info->entries_ht != NULL)
3344 g_hash_table_destroy (info->entries_ht);
3345 info->entries_ht = g_hash_table_new (g_str_hash,
3348 if ( ! vfolder_info_read_items (info,
3350 info->entries_valid = FALSE;
3354 invalidate_folder_T (info->root);
3356 info->entries_valid = TRUE;
3358 /* Update modification time of all folders,
3359 * kind of evil, but it will make adding new items work
3360 * I hope. This is because rereading usually means
3361 * something changed */
3362 info->modification_time = time (NULL);
3367 if (gnome_vfs_context_check_cancellation (context)) {
3368 *result = GNOME_VFS_ERROR_CANCELLED;
3373 infos = g_hash_table_new_full
3374 (g_str_hash, g_str_equal,
3375 (GDestroyNotify)g_free,
3376 (GDestroyNotify)vfolder_info_destroy);
3378 info = g_new0 (VFolderInfo, 1);
3379 vfolder_info_init (info, scheme);
3381 if (gnome_vfs_context_check_cancellation (context)) {
3382 vfolder_info_destroy (info);
3383 *result = GNOME_VFS_ERROR_CANCELLED;
3387 if ( ! vfolder_info_read_info (info, result, context)) {
3388 vfolder_info_destroy (info);
3392 if ( ! monitor_setup (info,
3393 TRUE /* setup_filenames */,
3394 TRUE /* setup_itemdirs */,
3395 TRUE /* setup_desktop_dirs */,
3397 vfolder_info_destroy (info);
3401 g_hash_table_insert (infos, g_strdup (scheme), info);
3403 if ( ! vfolder_info_read_items (info, result, context)) {
3404 info->entries_valid = FALSE;
3407 info->entries_valid = TRUE;
3412 static VFolderInfo *
3413 get_vfolder_info (const char *scheme,
3414 GnomeVFSResult *result,
3415 GnomeVFSContext *context)
3418 G_LOCK (vfolder_lock);
3419 info = get_vfolder_info_unlocked (scheme, result, context);
3420 G_UNLOCK (vfolder_lock);
3426 keywords_to_string (GSList *keywords)
3429 GString *str = g_string_new (NULL);
3431 for (li = keywords; li != NULL; li = li->next) {
3432 GQuark word = GPOINTER_TO_INT (li->data);
3433 g_string_append (str, g_quark_to_string (word));
3434 g_string_append_c (str, ';');
3437 return g_string_free (str, FALSE);
3440 /* copy file and add keywords line */
3442 copy_file_with_keywords (const char *from, const char *to, GSList *keywords)
3448 char *keyword_string;
3450 if ( ! ensure_dir (to,
3451 TRUE /* ignore_basename */))
3454 wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3459 keyword_string = keywords_to_string (keywords);
3461 wfp = fdopen (wfd, "w");
3463 fp = fopen (from, "r");
3465 gboolean wrote_keywords = FALSE;
3466 while (fgets (buf, sizeof (buf), fp) != NULL) {
3467 fprintf (wfp, "%s", buf);
3468 if ( ! wrote_keywords &&
3469 (strncmp (buf, "[Desktop Entry]",
3470 strlen ("[Desktop Entry]")) == 0 ||
3471 strncmp (buf, "[KDE Desktop Entry]",
3472 strlen ("[KDE Desktop Entry]")) == 0)) {
3473 fprintf (wfp, "Categories=%s\n",
3475 wrote_keywords = TRUE;
3481 fprintf (wfp, "[Desktop Entry]\nCategories=%s\n",
3485 /* FIXME: does this close wfd???? */
3490 g_free (keyword_string);
3496 copy_file (const char *from, const char *to)
3501 if ( ! ensure_dir (to,
3502 TRUE /* ignore_basename */))
3505 wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3510 fd = open (from, O_RDONLY);
3515 while ((n = read (fd, buf, sizeof(buf))) > 0) {
3516 write (wfd, buf, n);
3528 make_file_private (VFolderInfo *info, EntryFile *efile)
3531 Entry *entry = (Entry *)efile;
3533 if (efile->per_user)
3536 /* this file already exists so whack its monitors */
3537 if (efile->filename != NULL) {
3540 for (li = entry->monitors; li != NULL; li = li->next) {
3541 FileMonitorHandle *h = li->data;
3542 if (h->handle != NULL)
3543 gnome_vfs_monitor_cancel (h->handle);
3548 newfname = g_build_filename (g_get_home_dir (),
3555 if (efile->implicit_keywords) {
3556 if (efile->filename != NULL &&
3557 ! copy_file_with_keywords (efile->filename,
3560 /* FIXME: what to do with monitors here, they
3561 * have already been whacked, a corner case
3567 if (efile->filename != NULL &&
3568 ! copy_file (efile->filename, newfname)) {
3569 /* FIXME: what to do with monitors here, they
3570 * have already been whacked, a corner case
3577 /* we didn't copy but ensure path anyway */
3578 if (efile->filename == NULL &&
3579 ! ensure_dir (newfname,
3580 TRUE /* ignore_basename */)) {
3585 /* this file already exists so re-add monitors at the new location */
3586 if (efile->filename != NULL) {
3588 char *uri = gnome_vfs_get_uri_from_local_path (newfname);
3590 for (li = entry->monitors; li != NULL; li = li->next) {
3591 FileMonitorHandle *h = li->data;
3593 gnome_vfs_monitor_add (&(h->handle),
3595 GNOME_VFS_MONITOR_FILE,
3603 g_free (efile->filename);
3604 efile->filename = newfname;
3605 efile->per_user = TRUE;
3611 try_free_file_monitors_create_dirfile_unlocked (VFolderInfo *info,
3616 list = g_slist_copy (info->free_file_monitors);
3618 for (li = list; li != NULL; li = li->next) {
3619 FileMonitorHandle *handle = li->data;
3622 GnomeVFSResult result;
3624 if ( ! handle->is_directory_file)
3627 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
3628 * have lots of free monitors */
3630 VFOLDER_URI_PARSE (handle->uri, &vuri);
3632 f = resolve_folder (info,
3634 TRUE /* ignore_basename */,
3641 info->free_file_monitors =
3642 g_slist_remove (info->free_file_monitors, handle);
3643 ((Entry *)folder)->monitors =
3644 g_slist_prepend (((Entry *)folder)->monitors, handle);
3646 handle->exists = TRUE;
3647 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
3649 GNOME_VFS_MONITOR_EVENT_CREATED);
3652 g_slist_free (list);
3656 make_new_dirfile (VFolderInfo *info, Folder *folder)
3658 char *name = g_strdup (folder->entry.name);
3664 for (p = name; *p != '\0'; p++) {
3665 if ( ! ( (*p >= 'a' && *p <= 'z') ||
3666 (*p >= 'A' && *p <= 'Z') ||
3667 (*p >= '0' && *p <= '9') ||
3681 fname = g_strdup_printf ("%s-%d.directory", name, i);
3683 fname = g_strdup_printf ("%s.directory", name);
3686 fullname = g_build_filename
3687 (info->user_desktop_dir, fname, NULL);
3688 fd = open (fullname, O_CREAT | O_WRONLY | O_EXCL, 0600);
3694 folder->desktop_file = fname;
3697 try_free_file_monitors_create_dirfile_unlocked (info, folder);
3701 make_dirfile_private (VFolderInfo *info, Folder *folder)
3709 if (info->user_desktop_dir == NULL)
3712 if ( ! ensure_dir (info->user_desktop_dir,
3713 FALSE /* ignore_basename */))
3717 if (folder->desktop_file == NULL) {
3718 make_new_dirfile (info, folder);
3722 /* FIXME: this is broken! What if the desktop file exists
3723 * in the local but there is a different (but with a same name)
3724 * .directory in the system. */
3725 fname = g_build_filename (info->user_desktop_dir,
3726 folder->desktop_file,
3729 if (access (fname, F_OK) == 0) {
3734 desktop_file = get_directory_file (info, folder);
3736 if (desktop_file == NULL) {
3737 int fd = open (fname, O_CREAT | O_EXCL | O_WRONLY, 0600);
3746 for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3747 FileMonitorHandle *h = li->data;
3748 if (h->is_directory_file) {
3749 if (h->handle != NULL)
3750 gnome_vfs_monitor_cancel (h->handle);
3757 if ( ! copy_file (desktop_file, fname)) {
3760 fname = desktop_file;
3761 desktop_file = NULL;
3764 uri = gnome_vfs_get_uri_from_local_path (fname);
3766 for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3767 FileMonitorHandle *h = li->data;
3769 if (h->is_directory_file) {
3770 gnome_vfs_monitor_add (&(h->handle),
3772 GNOME_VFS_MONITOR_FILE,
3780 g_free (desktop_file);
3787 resolve_folder (VFolderInfo *info,
3789 gboolean ignore_basename,
3790 GnomeVFSResult *result,
3791 GnomeVFSContext *context)
3795 Folder *folder = info->root;
3797 ppath = g_strsplit (path, "/", -1);
3799 if (ppath == NULL ||
3802 *result = GNOME_VFS_ERROR_INVALID_URI;
3806 for (i = 0; ppath [i] != NULL; i++) {
3807 const char *segment = ppath[i];
3809 if (*segment == '\0')
3812 if (ignore_basename && ppath [i + 1] == NULL)
3815 folder = (Folder *) find_entry (folder->subfolders,
3823 if (gnome_vfs_context_check_cancellation (context)) {
3824 *result = GNOME_VFS_ERROR_CANCELLED;
3829 *result = GNOME_VFS_ERROR_NOT_FOUND;
3835 resolve_path (VFolderInfo *info,
3837 const char *basename,
3838 Folder **return_folder,
3839 GnomeVFSResult *result,
3840 GnomeVFSContext *context)
3845 if (strcmp (path, "/") == 0)
3846 return (Entry *)info->root;
3848 folder = resolve_folder (info, path,
3849 TRUE /* ignore_basename */,
3852 if (return_folder != NULL)
3853 *return_folder = folder;
3855 if (folder == NULL) {
3859 /* Make sure we have the entries here */
3860 ensure_folder_unlocked (info, folder,
3861 FALSE /* subfolders */,
3863 FALSE /* ignore_unallocated */);
3865 entry = find_entry (folder->entries, basename);
3868 *result = GNOME_VFS_ERROR_NOT_FOUND;
3874 get_entry_unlocked (VFolderURI *vuri,
3876 gboolean *is_directory_file,
3877 GnomeVFSResult *result,
3878 GnomeVFSContext *context)
3883 if (is_directory_file != NULL)
3884 *is_directory_file = FALSE;
3888 info = get_vfolder_info_unlocked (vuri->scheme, result, context);
3892 if (gnome_vfs_context_check_cancellation (context)) {
3893 *result = GNOME_VFS_ERROR_CANCELLED;
3897 if (vuri->is_all_scheme) {
3900 if (vuri->file == NULL) {
3901 entry = resolve_path (info,
3910 efile_list = g_hash_table_lookup (info->entries_ht, vuri->file);
3912 if (efile_list == NULL) {
3913 *result = GNOME_VFS_ERROR_NOT_FOUND;
3916 return efile_list->data;
3920 if (vuri->file != NULL &&
3921 check_ext (vuri->file, ".directory") == TRUE) {
3924 folder = resolve_folder (info, vuri->path,
3925 TRUE /* ignore_basename */,
3927 if (folder == NULL) {
3931 if (is_directory_file != NULL)
3932 *is_directory_file = TRUE;
3937 return (Entry *)folder;
3939 entry = resolve_path (info, vuri->path, vuri->file, parent,
3946 get_entry (VFolderURI *vuri,
3948 gboolean *is_directory_file,
3949 GnomeVFSResult *result,
3950 GnomeVFSContext *context)
3954 G_LOCK (vfolder_lock);
3955 entry = get_entry_unlocked (vuri,
3959 G_UNLOCK (vfolder_lock);
3964 /* only works for files and only those that exist */
3965 /* unlocked function */
3966 static GnomeVFSURI *
3967 desktop_uri_to_file_uri (VFolderInfo *info,
3968 VFolderURI *desktop_vuri,
3970 gboolean *the_is_directory_file,
3971 Folder **the_folder,
3973 GnomeVFSResult *result,
3974 GnomeVFSContext *context)
3976 gboolean is_directory_file;
3977 GnomeVFSURI *ret_uri;
3978 Folder *folder = NULL;
3981 entry = get_entry_unlocked (desktop_vuri,
3989 if (gnome_vfs_context_check_cancellation (context)) {
3990 *result = GNOME_VFS_ERROR_CANCELLED;
3994 if (the_folder != NULL)
3995 *the_folder = folder;
3997 if (the_entry != NULL)
3999 if (the_is_directory_file != NULL)
4000 *the_is_directory_file = is_directory_file;
4002 if (is_directory_file &&
4003 entry->type == ENTRY_FOLDER) {
4006 folder = (Folder *)entry;
4008 if (the_folder != NULL)
4009 *the_folder = folder;
4011 /* we'll be doing something write like */
4012 if (folder->read_only &&
4014 *result = GNOME_VFS_ERROR_READ_ONLY;
4021 if (gnome_vfs_context_check_cancellation (context)) {
4022 *result = GNOME_VFS_ERROR_CANCELLED;
4026 if ( ! make_dirfile_private (info, folder)) {
4027 *result = GNOME_VFS_ERROR_GENERIC;
4030 fname = g_build_filename (g_get_home_dir (),
4031 folder->desktop_file,
4033 ret_uri = gnome_vfs_uri_new (fname);
4038 desktop_file = get_directory_file_unlocked (info, folder);
4039 if (desktop_file != NULL) {
4040 char *s = gnome_vfs_get_uri_from_local_path
4043 g_free (desktop_file);
4045 ret_uri = gnome_vfs_uri_new (s);
4050 *result = GNOME_VFS_ERROR_NOT_FOUND;
4053 } else if (entry->type == ENTRY_FILE) {
4054 EntryFile *efile = (EntryFile *)entry;
4057 /* we'll be doing something write like */
4058 if (folder != NULL &&
4059 folder->read_only &&
4061 *result = GNOME_VFS_ERROR_READ_ONLY;
4065 if (gnome_vfs_context_check_cancellation (context)) {
4066 *result = GNOME_VFS_ERROR_CANCELLED;
4071 ! make_file_private (info, efile)) {
4072 *result = GNOME_VFS_ERROR_GENERIC;
4076 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4077 ret_uri = gnome_vfs_uri_new (s);
4082 if (the_folder != NULL)
4083 *the_folder = (Folder *)entry;
4084 *result = GNOME_VFS_ERROR_IS_DIRECTORY;
4090 remove_file (Folder *folder, const char *basename)
4095 if (folder->includes_ht != NULL) {
4096 li = g_hash_table_lookup (folder->includes_ht, basename);
4098 char *name = li->data;
4099 folder->includes = g_slist_delete_link
4100 (folder->includes, li);
4101 g_hash_table_remove (folder->includes_ht, basename);
4106 if (folder->excludes == NULL) {
4107 folder->excludes = g_hash_table_new_full
4108 (g_str_hash, g_str_equal,
4109 (GDestroyNotify)g_free,
4112 s = g_strdup (basename);
4113 g_hash_table_replace (folder->excludes, s, s);
4117 add_file (Folder *folder, const char *basename)
4121 if (folder->includes_ht != NULL) {
4122 li = g_hash_table_lookup (folder->includes_ht, basename);
4127 char *str = g_strdup (basename);
4129 g_slist_prepend (folder->includes, str);
4130 if (folder->includes_ht == NULL) {
4131 folder->includes_ht =
4132 g_hash_table_new_full (g_str_hash,
4137 g_hash_table_replace (folder->includes_ht,
4138 str, folder->includes);
4140 if (folder->excludes != NULL)
4141 g_hash_table_remove (folder->excludes, basename);
4144 typedef struct _FileHandle FileHandle;
4145 struct _FileHandle {
4147 GnomeVFSMethodHandle *handle;
4150 gboolean is_directory_file;
4154 make_handle (GnomeVFSMethodHandle **method_handle,
4155 GnomeVFSMethodHandle *file_handle,
4158 gboolean is_directory_file,
4161 if (file_handle != NULL) {
4162 FileHandle *handle = g_new0 (FileHandle, 1);
4164 handle->info = info;
4165 handle->handle = file_handle;
4166 handle->entry = entry_ref (entry);
4167 handle->is_directory_file = is_directory_file;
4168 handle->write = write;
4170 *method_handle = (GnomeVFSMethodHandle *) handle;
4172 *method_handle = NULL;
4177 whack_handle (FileHandle *handle)
4179 entry_unref (handle->entry);
4180 handle->entry = NULL;
4182 handle->handle = NULL;
4183 handle->info = NULL;
4188 static GnomeVFSResult
4189 do_open (GnomeVFSMethod *method,
4190 GnomeVFSMethodHandle **method_handle,
4192 GnomeVFSOpenMode mode,
4193 GnomeVFSContext *context)
4195 GnomeVFSURI *file_uri;
4196 GnomeVFSResult result = GNOME_VFS_OK;
4199 gboolean is_directory_file;
4200 GnomeVFSMethodHandle *file_handle = NULL;
4203 VFOLDER_URI_PARSE (uri, &vuri);
4205 /* These can't be very nice FILE names */
4206 if (vuri.file == NULL ||
4208 return GNOME_VFS_ERROR_INVALID_URI;
4210 info = get_vfolder_info (vuri.scheme, &result, context);
4214 if (mode & GNOME_VFS_OPEN_WRITE &&
4215 (info->read_only || vuri.is_all_scheme))
4216 return GNOME_VFS_ERROR_READ_ONLY;
4218 G_LOCK (vfolder_lock);
4219 file_uri = desktop_uri_to_file_uri (info,
4223 NULL /* the_folder */,
4224 mode & GNOME_VFS_OPEN_WRITE,
4228 if (file_uri == NULL) {
4229 G_UNLOCK (vfolder_lock);
4233 result = (* parent_method->open) (parent_method,
4239 if (result == GNOME_VFS_ERROR_CANCELLED) {
4240 G_UNLOCK (vfolder_lock);
4241 gnome_vfs_uri_unref (file_uri);
4245 make_handle (method_handle,
4250 mode & GNOME_VFS_OPEN_WRITE);
4252 gnome_vfs_uri_unref (file_uri);
4255 vfolder_info_write_user (info);
4258 G_UNLOCK (vfolder_lock);
4264 remove_from_all_except (Folder *root,
4270 if (root != except) {
4271 remove_file (root, name);
4272 if (root->up_to_date) {
4273 for (li = root->entries; li != NULL; li = li->next) {
4274 Entry *entry = li->data;
4275 if (strcmp (name, entry->name) == 0) {
4278 (root->entries, li);
4285 for (li = root->subfolders; li != NULL; li = li->next) {
4286 Folder *subfolder = li->data;
4288 remove_from_all_except (subfolder, name, except);
4292 static GnomeVFSResult
4293 do_create (GnomeVFSMethod *method,
4294 GnomeVFSMethodHandle **method_handle,
4296 GnomeVFSOpenMode mode,
4299 GnomeVFSContext *context)
4301 GnomeVFSResult result = GNOME_VFS_OK;
4302 GnomeVFSMethodHandle *file_handle;
4303 GnomeVFSURI *file_uri;
4312 VFOLDER_URI_PARSE (uri, &vuri);
4314 /* These can't be very nice FILE names */
4315 if (vuri.file == NULL ||
4317 return GNOME_VFS_ERROR_INVALID_URI;
4319 if ( ! check_ext (vuri.file, ".desktop") &&
4320 ! strcmp (vuri.file, ".directory") == 0) {
4321 return GNOME_VFS_ERROR_INVALID_URI;
4324 /* all scheme is read only */
4325 if (vuri.is_all_scheme)
4326 return GNOME_VFS_ERROR_READ_ONLY;
4328 info = get_vfolder_info (vuri.scheme, &result, context);
4332 if (info->user_filename == NULL ||
4334 return GNOME_VFS_ERROR_READ_ONLY;
4336 parent = resolve_folder (info, vuri.path,
4337 TRUE /* ignore_basename */,
4342 if (parent->read_only)
4343 return GNOME_VFS_ERROR_READ_ONLY;
4345 if (strcmp (vuri.file, ".directory") == 0) {
4348 G_LOCK (vfolder_lock);
4352 desktop_file = get_directory_file_unlocked (info, parent);
4353 if (desktop_file != NULL) {
4354 g_free (desktop_file);
4355 G_UNLOCK (vfolder_lock);
4356 return GNOME_VFS_ERROR_FILE_EXISTS;
4360 if ( ! make_dirfile_private (info, parent)) {
4361 G_UNLOCK (vfolder_lock);
4362 return GNOME_VFS_ERROR_GENERIC;
4364 fname = g_build_filename (g_get_home_dir (),
4365 parent->desktop_file,
4367 s = gnome_vfs_get_uri_from_local_path (fname);
4368 file_uri = gnome_vfs_uri_new (s);
4372 if (file_uri == NULL) {
4373 G_UNLOCK (vfolder_lock);
4374 return GNOME_VFS_ERROR_GENERIC;
4377 result = (* parent_method->create) (parent_method,
4384 gnome_vfs_uri_unref (file_uri);
4386 make_handle (method_handle,
4390 TRUE /* is_directory_file */,
4394 vfolder_info_write_user (info);
4396 G_UNLOCK (vfolder_lock);
4401 ensure_folder (info, parent,
4402 FALSE /* subfolders */,
4404 FALSE /* ignore_unallocated */);
4406 entry = find_entry (parent->entries, vuri.file);
4408 if (entry != NULL &&
4409 entry->type == ENTRY_FOLDER)
4410 return GNOME_VFS_ERROR_IS_DIRECTORY;
4412 efile = (EntryFile *)entry;
4414 if (efile != NULL) {
4416 return GNOME_VFS_ERROR_FILE_EXISTS;
4418 G_LOCK (vfolder_lock);
4419 if ( ! make_file_private (info, efile)) {
4420 G_UNLOCK (vfolder_lock);
4421 return GNOME_VFS_ERROR_GENERIC;
4424 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4425 file_uri = gnome_vfs_uri_new (s);
4428 if (file_uri == NULL) {
4429 G_UNLOCK (vfolder_lock);
4430 return GNOME_VFS_ERROR_GENERIC;
4433 result = (* parent_method->create) (parent_method,
4440 gnome_vfs_uri_unref (file_uri);
4442 make_handle (method_handle,
4446 FALSE /* is_directory_file */,
4449 G_UNLOCK (vfolder_lock);
4454 G_LOCK (vfolder_lock);
4456 li = g_hash_table_lookup (info->entries_ht, vuri.file);
4458 if (exclusive && li != NULL) {
4459 G_UNLOCK (vfolder_lock);
4460 return GNOME_VFS_ERROR_FILE_EXISTS;
4464 efile = file_new (vuri.file);
4465 vfolder_info_insert_entry (info, efile);
4466 entry_unref ((Entry *)efile);
4471 /* this will make a private name for this */
4472 if ( ! make_file_private (info, efile)) {
4473 G_UNLOCK (vfolder_lock);
4474 return GNOME_VFS_ERROR_GENERIC;
4477 add_file (parent, vuri.file);
4478 parent->sorted = FALSE;
4480 if (parent->up_to_date)
4481 parent->entries = g_slist_prepend (parent->entries, efile);
4483 /* if we created a brand new name, then we exclude it
4484 * from everywhere else to ensure overall sanity */
4486 remove_from_all_except (info->root, vuri.file, parent);
4488 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4489 file_uri = gnome_vfs_uri_new (s);
4492 result = (* parent_method->create) (parent_method,
4499 gnome_vfs_uri_unref (file_uri);
4501 make_handle (method_handle,
4505 FALSE /* is_directory_file */,
4508 vfolder_info_write_user (info);
4510 G_UNLOCK (vfolder_lock);
4515 static GnomeVFSResult
4516 do_close (GnomeVFSMethod *method,
4517 GnomeVFSMethodHandle *method_handle,
4518 GnomeVFSContext *context)
4520 GnomeVFSResult result;
4521 FileHandle *handle = (FileHandle *)method_handle;
4522 if (method_handle == (GnomeVFSMethodHandle *)method)
4523 return GNOME_VFS_OK;
4525 G_LOCK (vfolder_lock);
4527 result = (* parent_method->close) (parent_method,
4530 handle->handle = NULL;
4532 /* we reread the Categories keyword */
4533 if (handle->write &&
4534 handle->entry != NULL &&
4535 handle->entry->type == ENTRY_FILE) {
4536 EntryFile *efile = (EntryFile *)handle->entry;
4538 readitem_entry (efile->filename,
4543 set_keywords (efile, categories);
4544 g_free (categories);
4545 /* FIXME: what about OnlyShowIn */
4547 /* FIXME: check if the keywords changed, if not, do
4550 /* Perhaps a bit drastic */
4551 /* also this emits the CHANGED monitor signal */
4552 invalidate_folder_T (handle->info->root);
4554 /* the file changed monitor will happen by itself
4555 * as the underlying file is changed */
4556 } else if (handle->write &&
4557 handle->entry != NULL &&
4558 handle->entry->type == ENTRY_FOLDER &&
4559 handle->is_directory_file) {
4560 /* if we're monitoring this directory, emit the CHANGED
4561 * monitor thing, it will also emit a changed on
4562 * the file itself. It is better to emit changed
4564 emit_monitor ((Folder *)(handle->entry),
4565 GNOME_VFS_MONITOR_EVENT_CHANGED);
4568 whack_handle (handle);
4570 G_UNLOCK (vfolder_lock);
4576 fill_buffer (gpointer buffer,
4577 GnomeVFSFileSize num_bytes,
4578 GnomeVFSFileSize *bytes_read)
4582 for (i = 0; i < num_bytes; i++) {
4583 if (rand () % 32 == 0 ||
4587 buf[i] = ((rand()>>4) % 94) + 32;
4589 if (bytes_read != 0)
4593 static GnomeVFSResult
4594 do_read (GnomeVFSMethod *method,
4595 GnomeVFSMethodHandle *method_handle,
4597 GnomeVFSFileSize num_bytes,
4598 GnomeVFSFileSize *bytes_read,
4599 GnomeVFSContext *context)
4601 GnomeVFSResult result;
4602 FileHandle *handle = (FileHandle *)method_handle;
4604 if (method_handle == (GnomeVFSMethodHandle *)method) {
4605 if ((rand () >> 4) & 0x3) {
4606 fill_buffer (buffer, num_bytes, bytes_read);
4607 return GNOME_VFS_OK;
4609 return GNOME_VFS_ERROR_EOF;
4613 result = (* parent_method->read) (parent_method,
4622 static GnomeVFSResult
4623 do_write (GnomeVFSMethod *method,
4624 GnomeVFSMethodHandle *method_handle,
4625 gconstpointer buffer,
4626 GnomeVFSFileSize num_bytes,
4627 GnomeVFSFileSize *bytes_written,
4628 GnomeVFSContext *context)
4630 GnomeVFSResult result;
4631 FileHandle *handle = (FileHandle *)method_handle;
4633 if (method_handle == (GnomeVFSMethodHandle *)method)
4634 return GNOME_VFS_OK;
4636 result = (* parent_method->write) (parent_method,
4646 static GnomeVFSResult
4647 do_seek (GnomeVFSMethod *method,
4648 GnomeVFSMethodHandle *method_handle,
4649 GnomeVFSSeekPosition whence,
4650 GnomeVFSFileOffset offset,
4651 GnomeVFSContext *context)
4653 GnomeVFSResult result;
4654 FileHandle *handle = (FileHandle *)method_handle;
4656 if (method_handle == (GnomeVFSMethodHandle *)method)
4657 return GNOME_VFS_OK;
4659 result = (* parent_method->seek) (parent_method,
4667 static GnomeVFSResult
4668 do_tell (GnomeVFSMethod *method,
4669 GnomeVFSMethodHandle *method_handle,
4670 GnomeVFSFileOffset *offset_return)
4672 GnomeVFSResult result;
4673 FileHandle *handle = (FileHandle *)method_handle;
4675 result = (* parent_method->tell) (parent_method,
4683 static GnomeVFSResult
4684 do_truncate_handle (GnomeVFSMethod *method,
4685 GnomeVFSMethodHandle *method_handle,
4686 GnomeVFSFileSize where,
4687 GnomeVFSContext *context)
4689 GnomeVFSResult result;
4690 FileHandle *handle = (FileHandle *)method_handle;
4692 if (method_handle == (GnomeVFSMethodHandle *)method)
4693 return GNOME_VFS_OK;
4695 result = (* parent_method->truncate_handle) (parent_method,
4703 static GnomeVFSResult
4704 do_truncate (GnomeVFSMethod *method,
4706 GnomeVFSFileSize where,
4707 GnomeVFSContext *context)
4709 GnomeVFSURI *file_uri;
4710 GnomeVFSResult result = GNOME_VFS_OK;
4715 VFOLDER_URI_PARSE (uri, &vuri);
4717 /* These can't be very nice FILE names */
4718 if (vuri.file == NULL ||
4720 return GNOME_VFS_ERROR_INVALID_URI;
4722 if (vuri.is_all_scheme)
4723 return GNOME_VFS_ERROR_READ_ONLY;
4725 info = get_vfolder_info (vuri.scheme, &result, context);
4729 if (info->read_only)
4730 return GNOME_VFS_ERROR_READ_ONLY;
4732 G_LOCK (vfolder_lock);
4733 file_uri = desktop_uri_to_file_uri (info,
4736 NULL /* the_is_directory_file */,
4737 NULL /* the_folder */,
4738 TRUE /* privatize */,
4741 G_UNLOCK (vfolder_lock);
4743 if (file_uri == NULL)
4746 result = (* parent_method->truncate) (parent_method,
4751 gnome_vfs_uri_unref (file_uri);
4754 G_LOCK (vfolder_lock);
4755 vfolder_info_write_user (info);
4756 G_UNLOCK (vfolder_lock);
4759 if (entry->type == ENTRY_FILE) {
4760 EntryFile *efile = (EntryFile *)entry;
4762 G_LOCK (vfolder_lock);
4763 g_slist_free (efile->keywords);
4764 efile->keywords = NULL;
4765 G_UNLOCK (vfolder_lock);
4768 /* Perhaps a bit drastic, but oh well */
4769 invalidate_folder (info->root);
4774 typedef struct _DirHandle DirHandle;
4779 GnomeVFSFileInfoOptions options;
4781 /* List of Entries */
4786 static GnomeVFSResult
4787 do_open_directory (GnomeVFSMethod *method,
4788 GnomeVFSMethodHandle **method_handle,
4790 GnomeVFSFileInfoOptions options,
4791 GnomeVFSContext *context)
4793 GnomeVFSResult result = GNOME_VFS_OK;
4800 VFOLDER_URI_PARSE (uri, &vuri);
4802 info = get_vfolder_info (vuri.scheme, &result, context);
4806 /* In the all- scheme just list all filenames */
4807 if (vuri.is_all_scheme) {
4808 if (any_subdir (vuri.path))
4809 return GNOME_VFS_ERROR_NOT_FOUND;
4811 dh = g_new0 (DirHandle, 1);
4813 dh->options = options;
4816 G_LOCK (vfolder_lock);
4817 dh->list = g_slist_copy (info->entries);
4818 g_slist_foreach (dh->list, (GFunc)entry_ref, NULL);
4819 dh->current = dh->list;
4820 G_UNLOCK (vfolder_lock);
4822 *method_handle = (GnomeVFSMethodHandle*) dh;
4823 return GNOME_VFS_OK;
4826 folder = resolve_folder (info, vuri.path,
4827 FALSE /* ignore_basename */,
4832 /* Make sure we have the entries and sorted here */
4833 ensure_folder_sort (info, folder);
4835 dh = g_new0 (DirHandle, 1);
4837 dh->options = options;
4839 G_LOCK (vfolder_lock);
4840 dh->folder = (Folder *)entry_ref ((Entry *)folder);
4841 dh->list = g_slist_copy (folder->entries);
4842 g_slist_foreach (folder->entries, (GFunc)entry_ref, NULL);
4843 G_UNLOCK (vfolder_lock);
4845 desktop_file = get_directory_file (info, folder);
4846 if (desktop_file != NULL) {
4847 EntryFile *efile = file_new (".directory");
4848 dh->list = g_slist_prepend (dh->list, efile);
4849 g_free (desktop_file);
4852 dh->current = dh->list;
4854 *method_handle = (GnomeVFSMethodHandle*) dh;
4856 return GNOME_VFS_OK;
4859 static GnomeVFSResult
4860 do_close_directory (GnomeVFSMethod *method,
4861 GnomeVFSMethodHandle *method_handle,
4862 GnomeVFSContext *context)
4866 dh = (DirHandle*) method_handle;
4868 G_LOCK (vfolder_lock);
4870 g_slist_foreach (dh->list, (GFunc)entry_unref, NULL);
4871 g_slist_free (dh->list);
4876 if (dh->folder != NULL)
4877 entry_unref ((Entry *)dh->folder);
4884 G_UNLOCK (vfolder_lock);
4886 return GNOME_VFS_OK;
4889 static GnomeVFSResult
4890 do_read_directory (GnomeVFSMethod *method,
4891 GnomeVFSMethodHandle *method_handle,
4892 GnomeVFSFileInfo *file_info,
4893 GnomeVFSContext *context)
4897 GnomeVFSFileInfoOptions options;
4899 dh = (DirHandle*) method_handle;
4901 read_directory_again:
4903 if (dh->current == NULL) {
4904 return GNOME_VFS_ERROR_EOF;
4907 entry = dh->current->data;
4908 dh->current = dh->current->next;
4910 options = dh->options;
4912 if (entry->type == ENTRY_FILE &&
4913 ((EntryFile *)entry)->filename != NULL) {
4914 EntryFile *efile = (EntryFile *)entry;
4915 char *furi = gnome_vfs_get_uri_from_local_path (efile->filename);
4916 GnomeVFSURI *uri = gnome_vfs_uri_new (furi);
4918 /* we always get mime-type by forcing it below */
4919 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
4920 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
4922 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4924 /* Get the file info for this */
4925 (* parent_method->get_file_info) (parent_method,
4931 /* we ignore errors from this since the file_info just
4932 * won't be filled completely if there's an error, that's all */
4934 g_free (file_info->mime_type);
4935 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4936 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4938 /* Now we wipe those fields we don't support */
4939 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
4941 gnome_vfs_uri_unref (uri);
4943 } else if (entry->type == ENTRY_FILE) {
4944 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4946 file_info->name = g_strdup (entry->name);
4947 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4949 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
4950 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4952 /* FIXME: Is this correct? isn't there an xdg mime type? */
4953 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4954 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4956 /* FIXME: get some ctime/mtime */
4957 } else /* ENTRY_FOLDER */ {
4958 Folder *folder = (Folder *)entry;
4960 /* Skip empty folders if they have
4962 if (folder->dont_show_if_empty) {
4963 /* Make sure we have the entries */
4964 ensure_folder (dh->info, folder,
4965 FALSE /* subfolders */,
4967 FALSE /* ignore_unallocated */);
4969 if (folder->entries == NULL) {
4970 /* start this function over on the
4972 goto read_directory_again;
4976 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4978 file_info->name = g_strdup (entry->name);
4979 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4981 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
4982 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4984 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
4985 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4987 file_info->ctime = dh->info->modification_time;
4988 file_info->mtime = dh->info->modification_time;
4989 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
4990 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
4993 return GNOME_VFS_OK;
4996 static GnomeVFSResult
4997 do_get_file_info (GnomeVFSMethod *method,
4999 GnomeVFSFileInfo *file_info,
5000 GnomeVFSFileInfoOptions options,
5001 GnomeVFSContext *context)
5003 GnomeVFSURI *file_uri;
5004 GnomeVFSResult result = GNOME_VFS_OK;
5009 VFOLDER_URI_PARSE (uri, &vuri);
5011 info = get_vfolder_info (vuri.scheme, &result, context);
5015 G_LOCK (vfolder_lock);
5016 file_uri = desktop_uri_to_file_uri (info,
5018 NULL /* the_entry */,
5019 NULL /* the_is_directory_file */,
5021 FALSE /* privatize */,
5024 G_UNLOCK (vfolder_lock);
5026 if (file_uri == NULL &&
5027 result != GNOME_VFS_ERROR_IS_DIRECTORY)
5030 if (file_uri != NULL) {
5031 /* we always get mime-type by forcing it below */
5032 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5033 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5035 result = (* parent_method->get_file_info) (parent_method,
5041 g_free (file_info->mime_type);
5042 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5043 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5045 /* Now we wipe those fields we don't support */
5046 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5048 gnome_vfs_uri_unref (file_uri);
5051 } else if (folder != NULL) {
5052 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
5054 file_info->name = g_strdup (folder->entry.name);
5055 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
5057 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
5058 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
5060 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
5061 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5063 file_info->ctime = info->modification_time;
5064 file_info->mtime = info->modification_time;
5065 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
5066 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
5068 return GNOME_VFS_OK;
5070 return GNOME_VFS_ERROR_NOT_FOUND;
5074 static GnomeVFSResult
5075 do_get_file_info_from_handle (GnomeVFSMethod *method,
5076 GnomeVFSMethodHandle *method_handle,
5077 GnomeVFSFileInfo *file_info,
5078 GnomeVFSFileInfoOptions options,
5079 GnomeVFSContext *context)
5081 GnomeVFSResult result;
5082 FileHandle *handle = (FileHandle *)method_handle;
5084 if (method_handle == (GnomeVFSMethodHandle *)method) {
5085 g_free (file_info->mime_type);
5086 file_info->mime_type = g_strdup ("text/plain");
5087 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5088 return GNOME_VFS_OK;
5091 /* we always get mime-type by forcing it below */
5092 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5093 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5095 result = (* parent_method->get_file_info_from_handle) (parent_method,
5101 /* any file is of the .desktop type */
5102 g_free (file_info->mime_type);
5103 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5104 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5106 /* Now we wipe those fields we don't support */
5107 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5114 do_is_local (GnomeVFSMethod *method,
5115 const GnomeVFSURI *uri)
5121 try_free_folder_monitors_create_unlocked (VFolderInfo *info,
5126 list = g_slist_copy (info->free_folder_monitors);
5128 for (li = list; li != NULL; li = li->next) {
5129 FileMonitorHandle *handle = li->data;
5132 GnomeVFSResult result;
5134 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
5135 * have lots of free monitors */
5137 VFOLDER_URI_PARSE (handle->uri, &vuri);
5139 f = resolve_folder (info,
5141 FALSE /* ignore_basename */,
5148 info->free_folder_monitors =
5149 g_slist_remove (info->free_folder_monitors, handle);
5150 ((Entry *)folder)->monitors =
5151 g_slist_prepend (((Entry *)folder)->monitors, handle);
5153 handle->exists = TRUE;
5154 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
5156 GNOME_VFS_MONITOR_EVENT_CREATED);
5161 static GnomeVFSResult
5162 do_make_directory (GnomeVFSMethod *method,
5165 GnomeVFSContext *context)
5167 GnomeVFSResult result = GNOME_VFS_OK;
5169 Folder *parent, *folder;
5172 VFOLDER_URI_PARSE (uri, &vuri);
5174 if (vuri.is_all_scheme)
5175 return GNOME_VFS_ERROR_READ_ONLY;
5177 info = get_vfolder_info (vuri.scheme, &result, context);
5181 if (info->user_filename == NULL ||
5183 return GNOME_VFS_ERROR_READ_ONLY;
5185 parent = resolve_folder (info, vuri.path,
5186 TRUE /* ignore_basename */,
5190 else if (parent->read_only)
5191 return GNOME_VFS_ERROR_READ_ONLY;
5193 G_LOCK (vfolder_lock);
5195 folder = (Folder *)find_entry (parent->subfolders,
5197 if (folder != NULL) {
5198 G_UNLOCK (vfolder_lock);
5199 return GNOME_VFS_ERROR_FILE_EXISTS;
5202 folder = folder_new (vuri.file);
5203 parent->subfolders = g_slist_append (parent->subfolders, folder);
5204 folder->parent = parent;
5205 parent->up_to_date = FALSE;
5207 try_free_folder_monitors_create_unlocked (info, folder);
5209 /* parent changed */
5210 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5212 vfolder_info_write_user (info);
5213 G_UNLOCK (vfolder_lock);
5215 return GNOME_VFS_OK;
5218 static GnomeVFSResult
5219 do_remove_directory (GnomeVFSMethod *method,
5221 GnomeVFSContext *context)
5223 GnomeVFSResult result = GNOME_VFS_OK;
5228 VFOLDER_URI_PARSE (uri, &vuri);
5230 if (vuri.is_all_scheme)
5231 return GNOME_VFS_ERROR_READ_ONLY;
5233 info = get_vfolder_info (vuri.scheme, &result, context);
5237 if (info->user_filename == NULL ||
5239 return GNOME_VFS_ERROR_READ_ONLY;
5241 G_LOCK (vfolder_lock);
5243 folder = resolve_folder (info, vuri.path,
5244 FALSE /* ignore_basename */,
5246 if (folder == NULL) {
5247 G_UNLOCK (vfolder_lock);
5251 if (folder->read_only ||
5252 (folder->parent != NULL &&
5253 folder->parent->read_only)) {
5254 G_UNLOCK (vfolder_lock);
5255 return GNOME_VFS_ERROR_READ_ONLY;
5258 /* don't make removing directories easy */
5259 if (folder->desktop_file != NULL) {
5260 G_UNLOCK (vfolder_lock);
5261 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5264 /* Make sure we have the entries */
5265 ensure_folder_unlocked (info, folder,
5266 FALSE /* subfolders */,
5268 FALSE /* ignore_unallocated */);
5270 /* don't make removing directories easy */
5271 if (folder->entries != NULL) {
5272 G_UNLOCK (vfolder_lock);
5273 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5276 emit_and_delete_monitor (info, folder);
5278 if (folder->only_unallocated) {
5279 GSList *li = g_slist_find (info->unallocated_folders,
5282 info->unallocated_folders = g_slist_delete_link
5283 (info->unallocated_folders, li);
5284 entry_unref ((Entry *)folder);
5288 if (folder == info->root) {
5290 entry_unref ((Entry *)folder);
5291 info->root = folder_new ("Root");
5293 Folder *parent = folder->parent;
5295 g_assert (parent != NULL);
5297 parent->subfolders =
5298 g_slist_remove (parent->subfolders, folder);
5300 parent->up_to_date = FALSE;
5302 entry_unref ((Entry *)folder);
5304 /* parent changed */
5305 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5308 vfolder_info_write_user (info);
5310 G_UNLOCK (vfolder_lock);
5312 return GNOME_VFS_OK;
5315 /* a fairly evil function that does the whole move bit by copy and
5317 static GnomeVFSResult
5318 long_move (GnomeVFSMethod *method,
5319 VFolderURI *old_vuri,
5320 VFolderURI *new_vuri,
5321 gboolean force_replace,
5322 GnomeVFSContext *context)
5324 GnomeVFSResult result;
5325 GnomeVFSMethodHandle *handle;
5326 GnomeVFSURI *file_uri;
5333 info = get_vfolder_info (old_vuri->scheme, &result, context);
5337 G_LOCK (vfolder_lock);
5338 file_uri = desktop_uri_to_file_uri (info,
5340 NULL /* the_entry */,
5341 NULL /* the_is_directory_file */,
5342 NULL /* the_folder */,
5343 FALSE /* privatize */,
5346 G_UNLOCK (vfolder_lock);
5348 if (file_uri == NULL)
5351 path = gnome_vfs_uri_get_path (file_uri);
5353 gnome_vfs_uri_unref (file_uri);
5354 return GNOME_VFS_ERROR_INVALID_URI;
5357 fd = open (path, O_RDONLY);
5359 gnome_vfs_uri_unref (file_uri);
5360 return gnome_vfs_result_from_errno ();
5363 gnome_vfs_uri_unref (file_uri);
5365 info->inhibit_write++;
5367 result = method->create (method,
5370 GNOME_VFS_OPEN_WRITE,
5371 force_replace /* exclusive */,
5374 if (result != GNOME_VFS_OK) {
5376 info->inhibit_write--;
5380 while ((bytes = read (fd, buf, BUFSIZ)) > 0) {
5381 GnomeVFSFileSize bytes_written = 0;
5382 result = method->write (method,
5388 if (result == GNOME_VFS_OK &&
5389 bytes_written != bytes)
5390 result = GNOME_VFS_ERROR_NO_SPACE;
5391 if (result != GNOME_VFS_OK) {
5393 method->close (method, handle, context);
5394 /* FIXME: is this completely correct ? */
5395 method->unlink (method,
5398 G_LOCK (vfolder_lock);
5399 info->inhibit_write--;
5400 vfolder_info_write_user (info);
5401 G_UNLOCK (vfolder_lock);
5408 result = method->close (method, handle, context);
5409 if (result != GNOME_VFS_OK) {
5410 G_LOCK (vfolder_lock);
5411 info->inhibit_write--;
5412 vfolder_info_write_user (info);
5413 G_UNLOCK (vfolder_lock);
5417 result = method->unlink (method, old_vuri->uri, context);
5419 G_LOCK (vfolder_lock);
5420 info->inhibit_write--;
5421 vfolder_info_write_user (info);
5422 G_UNLOCK (vfolder_lock);
5427 static GnomeVFSResult
5428 move_directory_file (VFolderInfo *info,
5432 if (old_folder->desktop_file == NULL)
5433 return GNOME_VFS_ERROR_NOT_FOUND;
5435 /* "move" the desktop file */
5436 g_free (new_folder->desktop_file);
5437 new_folder->desktop_file = old_folder->desktop_file;
5438 old_folder->desktop_file = NULL;
5440 /* is this too drastic, it will requery the folder? */
5441 new_folder->up_to_date = FALSE;
5442 old_folder->up_to_date = FALSE;
5444 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5445 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5447 vfolder_info_write_user (info);
5449 return GNOME_VFS_OK;
5453 is_sub (Folder *master, Folder *sub)
5457 for (li = master->subfolders; li != NULL; li = li->next) {
5458 Folder *subfolder = li->data;
5460 if (subfolder == sub ||
5461 is_sub (subfolder, sub))
5468 static GnomeVFSResult
5469 move_folder (VFolderInfo *info,
5470 Folder *old_folder, Entry *old_entry,
5471 Folder *new_folder, Entry *new_entry)
5473 Folder *source = (Folder *)old_entry;
5476 if (new_entry != NULL &&
5477 new_entry->type != ENTRY_FOLDER)
5478 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
5480 if (new_entry != NULL) {
5481 target = (Folder *)new_entry;
5483 target = new_folder;
5486 /* move to where we are, yay, we're done :) */
5487 if (source->parent == target)
5488 return GNOME_VFS_OK;
5490 if (source == target ||
5491 is_sub (source, target))
5492 return GNOME_VFS_ERROR_LOOP;
5494 /* this will never happen, but we're paranoid */
5495 if (source->parent == NULL)
5496 return GNOME_VFS_ERROR_LOOP;
5498 source->parent->subfolders = g_slist_remove (source->parent->subfolders,
5500 target->subfolders = g_slist_append (target->subfolders,
5503 source->parent = target;
5505 source->up_to_date = FALSE;
5506 target->up_to_date = FALSE;
5508 emit_monitor (source, GNOME_VFS_MONITOR_EVENT_CHANGED);
5509 emit_monitor (target, GNOME_VFS_MONITOR_EVENT_CHANGED);
5511 vfolder_info_write_user (info);
5513 return GNOME_VFS_OK;
5516 static GnomeVFSResult
5517 do_move (GnomeVFSMethod *method,
5518 GnomeVFSURI *old_uri,
5519 GnomeVFSURI *new_uri,
5520 gboolean force_replace,
5521 GnomeVFSContext *context)
5523 GnomeVFSResult result = GNOME_VFS_OK;
5525 Folder *old_folder, *new_folder;
5526 Entry *old_entry, *new_entry;
5527 gboolean old_is_directory_file, new_is_directory_file;
5528 VFolderURI old_vuri, new_vuri;
5530 VFOLDER_URI_PARSE (old_uri, &old_vuri);
5531 VFOLDER_URI_PARSE (new_uri, &new_vuri);
5533 if (old_vuri.file == NULL)
5534 return GNOME_VFS_ERROR_INVALID_URI;
5536 if (old_vuri.is_all_scheme)
5537 return GNOME_VFS_ERROR_READ_ONLY;
5539 if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
5540 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
5542 info = get_vfolder_info (old_vuri.scheme, &result, context);
5546 if (info->read_only)
5547 return GNOME_VFS_ERROR_READ_ONLY;
5549 old_entry = get_entry (&old_vuri,
5551 &old_is_directory_file,
5554 if (old_entry == NULL)
5557 if (old_folder != NULL && old_folder->read_only)
5558 return GNOME_VFS_ERROR_READ_ONLY;
5560 new_entry = get_entry (&new_vuri,
5562 &new_is_directory_file,
5565 if (new_entry == NULL && new_folder == NULL)
5568 if (new_folder != NULL && new_folder->read_only)
5569 return GNOME_VFS_ERROR_READ_ONLY;
5571 if (new_is_directory_file != old_is_directory_file) {
5572 /* this will do another set of lookups
5573 * perhaps this can be done in a nicer way,
5574 * but is this the common case? I don't think so */
5575 return long_move (method, &old_vuri, &new_vuri,
5576 force_replace, context);
5579 if (new_is_directory_file) {
5580 g_assert (old_entry != NULL);
5581 g_assert (new_entry != NULL);
5582 G_LOCK (vfolder_lock);
5583 result = move_directory_file (info,
5584 (Folder *)old_entry,
5585 (Folder *)new_entry);
5586 G_UNLOCK (vfolder_lock);
5590 if (old_entry->type == ENTRY_FOLDER) {
5591 G_LOCK (vfolder_lock);
5592 result = move_folder (info,
5593 old_folder, old_entry,
5594 new_folder, new_entry);
5595 G_UNLOCK (vfolder_lock);
5599 /* move into self, just whack the old one */
5600 if (old_entry == new_entry) {
5602 if (new_folder == old_folder)
5603 return GNOME_VFS_OK;
5605 if ( ! force_replace)
5606 return GNOME_VFS_ERROR_FILE_EXISTS;
5608 G_LOCK (vfolder_lock);
5610 remove_file (old_folder, old_vuri.file);
5612 old_folder->entries = g_slist_remove (old_folder->entries,
5614 entry_unref (old_entry);
5616 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5618 vfolder_info_write_user (info);
5620 G_UNLOCK (vfolder_lock);
5622 return GNOME_VFS_OK;
5625 /* this is a simple move */
5626 if (new_entry == NULL ||
5627 new_entry->type == ENTRY_FOLDER) {
5628 if (new_entry != NULL) {
5629 new_folder = (Folder *)new_entry;
5631 /* a file and a totally different one */
5632 if (strcmp (new_vuri.file, old_entry->name) != 0) {
5633 /* yay, a long move */
5634 /* this will do another set of lookups
5635 * perhaps this can be done in a nicer way,
5636 * but is this the common case? I don't think
5638 return long_move (method, &old_vuri, &new_vuri,
5639 force_replace, context);
5644 if (new_folder == old_folder)
5645 return GNOME_VFS_OK;
5647 G_LOCK (vfolder_lock);
5649 remove_file (old_folder, old_entry->name);
5650 add_file (new_folder, old_entry->name);
5652 new_folder->entries = g_slist_prepend (new_folder->entries,
5654 entry_ref (old_entry);
5655 new_folder->sorted = FALSE;
5657 old_folder->entries = g_slist_remove (old_folder->entries,
5659 entry_unref (old_entry);
5661 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5662 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5664 vfolder_info_write_user (info);
5666 G_UNLOCK (vfolder_lock);
5668 return GNOME_VFS_OK;
5671 /* do we EVER get here? */
5673 /* this will do another set of lookups
5674 * perhaps this can be done in a nicer way,
5675 * but is this the common case? I don't think so */
5676 return long_move (method, &old_vuri, &new_vuri,
5677 force_replace, context);
5680 static GnomeVFSResult
5681 do_unlink (GnomeVFSMethod *method,
5683 GnomeVFSContext *context)
5685 GnomeVFSResult result = GNOME_VFS_OK;
5688 gboolean is_directory_file;
5693 VFOLDER_URI_PARSE (uri, &vuri);
5695 if (vuri.file == NULL)
5696 return GNOME_VFS_ERROR_INVALID_URI;
5698 if (vuri.is_all_scheme == TRUE)
5699 return GNOME_VFS_ERROR_READ_ONLY;
5701 info = get_vfolder_info (vuri.scheme, &result, context);
5704 else if (info->read_only)
5705 return GNOME_VFS_ERROR_READ_ONLY;
5707 entry = get_entry (&vuri,
5713 else if (the_folder != NULL &&
5714 the_folder->read_only)
5715 return GNOME_VFS_ERROR_READ_ONLY;
5717 if (entry->type == ENTRY_FOLDER &&
5718 is_directory_file) {
5719 Folder *folder = (Folder *)entry;
5721 if (folder->desktop_file == NULL)
5722 return GNOME_VFS_ERROR_NOT_FOUND;
5724 G_LOCK (vfolder_lock);
5726 g_free (folder->desktop_file);
5727 folder->desktop_file = NULL;
5729 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5731 vfolder_info_write_user (info);
5733 G_UNLOCK (vfolder_lock);
5735 return GNOME_VFS_OK;
5736 } else if (entry->type == ENTRY_FOLDER) {
5737 return GNOME_VFS_ERROR_IS_DIRECTORY;
5738 } else if (the_folder == NULL) {
5739 return GNOME_VFS_ERROR_NOT_FOUND;
5742 G_LOCK (vfolder_lock);
5744 the_folder->entries = g_slist_remove (the_folder->entries,
5746 entry_unref (entry);
5748 remove_file (the_folder, vuri.file);
5750 emit_monitor (the_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5752 /* evil, we must remove this from the unallocated folders as well
5753 * so that it magically doesn't appear there. But it's not so simple.
5754 * We only want to remove it if it isn't in that folder already. */
5755 for (li = info->unallocated_folders;
5758 Folder *folder = li->data;
5761 /* This is actually really evil since ensuring
5762 * an unallocated folder clears all other unallocated
5763 * folders in it's wake. I'm not sure it's worth
5764 * optimizing however */
5765 ensure_folder_unlocked (info, folder,
5766 FALSE /* subfolders */,
5768 FALSE /* ignore_unallocated */);
5769 l = g_slist_find (folder->entries, entry);
5771 remove_file (folder, vuri.file);
5775 emit_file_deleted_monitor (info, entry, the_folder);
5777 /* FIXME: if this was a user file and this is the only
5778 * reference to it, unlink it. */
5780 vfolder_info_write_user (info);
5782 G_UNLOCK (vfolder_lock);
5784 return GNOME_VFS_OK;
5787 static GnomeVFSResult
5788 do_check_same_fs (GnomeVFSMethod *method,
5789 GnomeVFSURI *source_uri,
5790 GnomeVFSURI *target_uri,
5791 gboolean *same_fs_return,
5792 GnomeVFSContext *context)
5794 VFolderURI source_vuri, target_vuri;
5796 *same_fs_return = FALSE;
5798 VFOLDER_URI_PARSE (source_uri, &source_vuri);
5799 VFOLDER_URI_PARSE (target_uri, &target_vuri);
5801 if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
5802 source_vuri.is_all_scheme != target_vuri.is_all_scheme)
5803 *same_fs_return = FALSE;
5805 *same_fs_return = TRUE;
5807 return GNOME_VFS_OK;
5810 static GnomeVFSResult
5811 do_set_file_info (GnomeVFSMethod *method,
5813 const GnomeVFSFileInfo *info,
5814 GnomeVFSSetFileInfoMask mask,
5815 GnomeVFSContext *context)
5819 VFOLDER_URI_PARSE (uri, &vuri);
5821 if (vuri.file == NULL)
5822 return GNOME_VFS_ERROR_INVALID_URI;
5824 if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
5825 GnomeVFSResult result = GNOME_VFS_OK;
5826 char *dirname = gnome_vfs_uri_extract_dirname (uri);
5827 GnomeVFSURI *new_uri = gnome_vfs_uri_dup (uri);
5829 G_LOCK (vfolder_lock);
5830 g_free (new_uri->text);
5831 new_uri->text = g_build_path ("/", dirname, info->name, NULL);
5832 G_UNLOCK (vfolder_lock);
5834 result = do_move (method,
5837 FALSE /* force_replace */,
5841 gnome_vfs_uri_unref (new_uri);
5844 /* We don't support setting any of this other permission,
5845 * times and all that voodoo */
5846 return GNOME_VFS_ERROR_NOT_SUPPORTED;
5850 static GnomeVFSResult
5851 do_monitor_add (GnomeVFSMethod *method,
5852 GnomeVFSMethodHandle **method_handle_return,
5854 GnomeVFSMonitorType monitor_type)
5858 GnomeVFSResult result;
5861 GnomeVFSURI *file_uri;
5862 FileMonitorHandle *handle;
5863 gboolean is_directory_file;
5865 VFOLDER_URI_PARSE (uri, &vuri);
5867 info = get_vfolder_info (vuri.scheme, &result, NULL);
5871 if (monitor_type == GNOME_VFS_MONITOR_DIRECTORY) {
5872 G_LOCK (vfolder_lock);
5874 folder = resolve_folder (info,
5876 FALSE /* ignore_basename */,
5880 handle = g_new0 (FileMonitorHandle, 1);
5881 handle->refcount = 2;
5882 handle->uri = gnome_vfs_uri_dup (uri);
5883 handle->dir_monitor = TRUE;
5884 handle->handle = NULL;
5885 handle->filename = NULL;
5887 if (folder == NULL) {
5888 handle->exists = FALSE;
5889 info->free_folder_monitors =
5890 g_slist_prepend (info->free_folder_monitors,
5893 handle->exists = TRUE;
5894 ((Entry *)folder)->monitors =
5895 g_slist_prepend (((Entry *)folder)->monitors,
5899 info->folder_monitors =
5900 g_slist_prepend (info->folder_monitors, handle);
5902 G_UNLOCK (vfolder_lock);
5904 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5906 return GNOME_VFS_OK;
5908 /* These can't be very nice FILE names */
5909 if (vuri.file == NULL ||
5911 return GNOME_VFS_ERROR_INVALID_URI;
5913 G_LOCK (vfolder_lock);
5914 file_uri = desktop_uri_to_file_uri (info,
5918 NULL /* the_folder */,
5923 handle = g_new0 (FileMonitorHandle, 1);
5924 handle->refcount = 2;
5925 handle->uri = gnome_vfs_uri_dup (uri);
5926 handle->dir_monitor = FALSE;
5927 handle->handle = NULL;
5928 handle->filename = g_strdup (vuri.file);
5929 handle->is_directory_file = is_directory_file;
5931 info->file_monitors =
5932 g_slist_prepend (info->file_monitors, handle);
5935 if (file_uri == NULL) {
5936 handle->exists = FALSE;
5937 info->free_file_monitors =
5938 g_slist_prepend (info->free_file_monitors,
5941 char *uri_string = gnome_vfs_uri_to_string (file_uri, 0);
5942 handle->exists = TRUE;
5943 gnome_vfs_monitor_add (&(handle->handle),
5945 GNOME_VFS_MONITOR_FILE,
5948 g_free (uri_string);
5950 entry->monitors = g_slist_prepend (entry->monitors,
5952 gnome_vfs_uri_unref (file_uri);
5955 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5957 G_UNLOCK (vfolder_lock);
5959 return GNOME_VFS_OK;
5963 static GnomeVFSResult
5964 do_monitor_cancel (GnomeVFSMethod *method,
5965 GnomeVFSMethodHandle *method_handle)
5967 FileMonitorHandle *handle;
5970 GnomeVFSResult result;
5974 handle = (FileMonitorHandle *)method_handle;
5976 /* FIXME: is this correct? */
5977 if (method_handle == NULL)
5978 return GNOME_VFS_OK;
5980 VFOLDER_URI_PARSE (handle->uri, &vuri);
5982 info = get_vfolder_info (vuri.scheme, &result, NULL);
5986 if (handle->dir_monitor) {
5987 G_LOCK (vfolder_lock);
5989 folder = resolve_folder (info,
5991 FALSE /* ignore_basename */,
5995 for (li = info->folder_monitors; li != NULL; li = li->next) {
5996 FileMonitorHandle *h = li->data;
5999 info->folder_monitors = g_slist_delete_link
6000 (info->folder_monitors, li);
6001 file_monitor_handle_unref_unlocked (h);
6005 if (folder == NULL) {
6006 for (li = info->free_folder_monitors;
6009 FileMonitorHandle *h = li->data;
6012 info->free_folder_monitors = g_slist_delete_link
6013 (info->free_folder_monitors, li);
6014 file_monitor_handle_unref_unlocked (h);
6018 for (li = ((Entry *)folder)->monitors;
6021 FileMonitorHandle *h = li->data;
6024 ((Entry *)folder)->monitors =
6026 (((Entry *)folder)->monitors, li);
6027 file_monitor_handle_unref_unlocked (h);
6032 G_UNLOCK (vfolder_lock);
6034 return GNOME_VFS_OK;
6036 G_LOCK (vfolder_lock);
6038 for (li = info->file_monitors; li != NULL; li = li->next) {
6039 FileMonitorHandle *h = li->data;
6042 info->file_monitors = g_slist_delete_link
6043 (info->file_monitors, li);
6044 file_monitor_handle_unref_unlocked (h);
6048 for (li = info->free_file_monitors;
6051 FileMonitorHandle *h = li->data;
6054 info->free_file_monitors = g_slist_delete_link
6055 (info->free_file_monitors, li);
6056 file_monitor_handle_unref_unlocked (h);
6060 for (li = info->entries; li != NULL; li = li->next) {
6061 Entry *e = li->data;
6062 GSList *link = g_slist_find (e->monitors, handle);
6067 e->monitors = g_slist_delete_link (e->monitors, link);
6069 file_monitor_handle_unref_unlocked (handle);
6073 G_UNLOCK (vfolder_lock);
6075 /* Note: last unref of our monitor will cancel the
6076 * underlying handle */
6078 return GNOME_VFS_OK;
6083 /* gnome-vfs bureaucracy */
6085 static GnomeVFSMethod method = {
6086 sizeof (GnomeVFSMethod),
6088 NULL, /* do_create, */
6091 NULL, /* do_write, */
6094 NULL, /* do_truncate_handle, */
6099 do_get_file_info_from_handle,
6101 NULL, /* do_make_directory, */
6102 NULL, /* do_remove_directory, */
6103 NULL, /* do_move, */
6104 NULL, /* do_unlink, */
6106 NULL, /* do_set_file_info, */
6107 NULL, /* do_truncate, */
6108 NULL /* find_directory */,
6109 NULL /* create_symbolic_link */,
6115 vfs_module_init (const char *method_name,
6118 parent_method = gnome_vfs_method_get ("file");
6120 if (parent_method == NULL) {
6121 g_error ("Could not find 'file' method for gnome-vfs");
6129 vfs_module_shutdown (GnomeVFSMethod *method)
6134 g_hash_table_destroy (infos);