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;
1170 query_new (int type)
1174 if (type == QUERY_KEYWORD)
1175 query = (Query *)g_new0 (QueryKeyword, 1);
1176 else if (type == QUERY_FILENAME)
1177 query = (Query *)g_new0 (QueryFilename, 1);
1179 query = g_new0 (Query, 1);
1187 query_destroy (Query *query)
1192 if (query->type == QUERY_FILENAME) {
1193 QueryFilename *qfile = (QueryFilename *)query;
1194 g_free (qfile->filename);
1195 qfile->filename = NULL;
1196 } else if (query->type == QUERY_OR ||
1197 query->type == QUERY_AND) {
1198 g_slist_foreach (query->queries, (GFunc)query_destroy, NULL);
1199 g_slist_free (query->queries);
1200 query->queries = NULL;
1207 add_folder_monitor_unlocked (VFolderInfo *info,
1208 FileMonitorHandle *handle)
1211 GnomeVFSResult result;
1214 VFOLDER_URI_PARSE (handle->uri, &vuri);
1216 file_monitor_handle_ref_unlocked (handle);
1218 info->folder_monitors =
1219 g_slist_prepend (info->folder_monitors, handle);
1221 folder = resolve_folder (info,
1223 FALSE /* ignore_basename */,
1227 if (folder == NULL) {
1228 file_monitor_handle_ref_unlocked (handle);
1230 info->free_folder_monitors =
1231 g_slist_prepend (info->free_folder_monitors, handle);
1233 if (handle->exists) {
1234 handle->exists = FALSE;
1235 gnome_vfs_monitor_callback
1236 ((GnomeVFSMethodHandle *)handle,
1238 GNOME_VFS_MONITOR_EVENT_DELETED);
1241 file_monitor_handle_ref_unlocked (handle);
1243 ((Entry *)folder)->monitors =
1244 g_slist_prepend (((Entry *)folder)->monitors, handle);
1246 if ( ! handle->exists) {
1247 handle->exists = TRUE;
1248 gnome_vfs_monitor_callback
1249 ((GnomeVFSMethodHandle *)handle,
1251 GNOME_VFS_MONITOR_EVENT_CREATED);
1258 invalidate_folder_T (Folder *folder)
1260 folder->up_to_date = FALSE;
1262 invalidate_folder_subfolders (folder, TRUE);
1266 invalidate_folder (Folder *folder)
1268 G_LOCK (vfolder_lock);
1269 folder->up_to_date = FALSE;
1270 G_UNLOCK (vfolder_lock);
1272 invalidate_folder_subfolders (folder, FALSE);
1276 invalidate_folder_subfolders (Folder *folder,
1277 gboolean lock_taken)
1281 for (li = folder->subfolders; li != NULL; li = li->next) {
1282 Folder *subfolder = li->data;
1285 invalidate_folder (subfolder);
1287 invalidate_folder_T (subfolder);
1290 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
1293 /* FIXME: this is UGLY!, we need to figure out when the file
1294 * got finished changing! */
1296 reread_timeout (gpointer data)
1298 VFolderInfo *info = data;
1299 gboolean force_read_items = info->file_monitors != NULL;
1300 vfolder_info_reload (info, NULL, NULL, force_read_items);
1305 queue_reread_in (VFolderInfo *info, int msec)
1307 G_LOCK (vfolder_lock);
1308 if (info->reread_queue != 0)
1309 g_source_remove (info->reread_queue);
1310 info->reread_queue = g_timeout_add (msec, reread_timeout, info);
1311 G_UNLOCK (vfolder_lock);
1315 vfolder_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1316 const gchar *monitor_uri,
1317 const gchar *info_uri,
1318 GnomeVFSMonitorEventType event_type,
1321 /* FIXME: implement */
1325 vfolder_user_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1326 const gchar *monitor_uri,
1327 const gchar *info_uri,
1328 GnomeVFSMonitorEventType event_type,
1331 /* FIXME: implement */
1335 vfolder_filename_monitor (GnomeVFSMonitorHandle *handle,
1336 const gchar *monitor_uri,
1337 const gchar *info_uri,
1338 GnomeVFSMonitorEventType event_type,
1341 VFolderInfo *info = user_data;
1343 if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1344 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1345 ! info->user_file_active) {
1346 queue_reread_in (info, 200);
1347 } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1348 ! info->user_file_active) {
1349 /* FIXME: is this correct? I mean now
1350 * there probably isn't ANY vfolder file, so we
1351 * init to default values really. I have no clue what's
1353 vfolder_info_reload (info, NULL, NULL,
1354 TRUE /* force read items */);
1359 vfolder_user_filename_monitor (GnomeVFSMonitorHandle *handle,
1360 const gchar *monitor_uri,
1361 const gchar *info_uri,
1362 GnomeVFSMonitorEventType event_type,
1365 VFolderInfo *info = user_data;
1367 if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1368 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1369 info->user_file_active) {
1372 /* see if this was really our own change */
1373 if (info->user_filename_last_write == time (NULL))
1375 /* anal retentive */
1376 if (stat (info->user_filename, &s) == 0 &&
1377 info->user_filename_last_write == s.st_ctime)
1380 queue_reread_in (info, 200);
1381 } else if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1382 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1383 ! info->user_file_active) {
1384 queue_reread_in (info, 200);
1385 } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1386 info->user_file_active) {
1387 gboolean force_read_items = info->file_monitors != NULL;
1388 vfolder_info_reload (info, NULL, NULL, force_read_items);
1393 item_dir_monitor (GnomeVFSMonitorHandle *handle,
1394 const gchar *monitor_uri,
1395 const gchar *info_uri,
1396 GnomeVFSMonitorEventType event_type,
1399 VFolderInfo *info = user_data;
1401 if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1402 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
1403 /* first invalidate all folders */
1404 invalidate_folder (info->root);
1405 /* second invalidate all entries */
1406 info->entries_valid = FALSE;
1408 if (info->file_monitors != NULL) {
1409 GnomeVFSResult result;
1412 /* Whack all monitors here! */
1413 for (li = info->file_monitors;
1416 FileMonitorHandle *h = li->data;
1417 if (h->handle != NULL)
1418 gnome_vfs_monitor_cancel (h->handle);
1422 if (vfolder_info_read_items (info, &result, NULL)) {
1423 info->entries_valid = TRUE;
1430 setup_dir_monitor (VFolderInfo *info, const char *dir, gboolean subdirs,
1431 GnomeVFSResult *result,
1432 GnomeVFSContext *context)
1434 GnomeVFSMonitorHandle *handle;
1439 uri = gnome_vfs_get_uri_from_local_path (dir);
1441 if (gnome_vfs_monitor_add (&handle,
1443 GNOME_VFS_MONITOR_DIRECTORY,
1445 info) != GNOME_VFS_OK) {
1446 StatLoc *sl = bake_statloc (dir, time (NULL));
1448 info->stat_dirs = g_slist_prepend (info->stat_dirs, sl);
1454 if (gnome_vfs_context_check_cancellation (context)) {
1455 gnome_vfs_monitor_cancel (handle);
1456 *result = GNOME_VFS_ERROR_CANCELLED;
1460 info->item_dir_monitors =
1461 g_slist_prepend (info->item_dir_monitors, handle);
1470 while ((de = readdir (dh)) != NULL) {
1473 if (gnome_vfs_context_check_cancellation (context)) {
1474 *result = GNOME_VFS_ERROR_CANCELLED;
1479 if (de->d_name[0] == '.')
1482 full_path = g_build_filename (dir, de->d_name, NULL);
1483 if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
1484 if ( ! setup_dir_monitor (info, full_path,
1500 monitor_setup (VFolderInfo *info,
1501 gboolean setup_filenames,
1502 gboolean setup_itemdirs,
1503 gboolean setup_desktop_dirs,
1504 GnomeVFSResult *result,
1505 GnomeVFSContext *context)
1510 if (setup_filenames) {
1511 uri = gnome_vfs_get_uri_from_local_path
1514 if (gnome_vfs_monitor_add (&info->filename_monitor,
1516 GNOME_VFS_MONITOR_FILE,
1517 vfolder_filename_monitor,
1518 info) != GNOME_VFS_OK) {
1519 info->filename_monitor = NULL;
1520 info->filename_statloc = bake_statloc (info->filename,
1525 if (setup_filenames &&
1526 info->user_filename != NULL) {
1527 uri = gnome_vfs_get_uri_from_local_path
1528 (info->user_filename);
1529 if (gnome_vfs_monitor_add (&info->user_filename_monitor,
1531 GNOME_VFS_MONITOR_FILE,
1532 vfolder_user_filename_monitor,
1533 info) != GNOME_VFS_OK) {
1534 info->user_filename_monitor = NULL;
1535 info->user_filename_statloc =
1536 bake_statloc (info->user_filename,
1543 if (gnome_vfs_context_check_cancellation (context)) {
1544 *result = GNOME_VFS_ERROR_CANCELLED;
1548 if (setup_itemdirs) {
1549 for (li = info->item_dirs; li != NULL; li = li->next) {
1550 const char *dir = li->data;
1551 if ( ! setup_dir_monitor (info, dir,
1552 FALSE /* subdirs */,
1556 if (info->user_item_dir != NULL) {
1557 if ( ! setup_dir_monitor (info, info->user_item_dir,
1558 FALSE /* subdirs */,
1562 for (li = info->merge_dirs; li != NULL; li = li->next) {
1563 const char *dir = li->data;
1564 if ( ! setup_dir_monitor (info, dir,
1571 if (setup_desktop_dirs) {
1572 uri = gnome_vfs_get_uri_from_local_path
1573 (info->desktop_dir);
1575 if (gnome_vfs_monitor_add (&info->desktop_dir_monitor,
1577 GNOME_VFS_MONITOR_FILE,
1578 vfolder_desktop_dir_monitor,
1579 info) != GNOME_VFS_OK) {
1580 info->desktop_dir_monitor = NULL;
1581 info->desktop_dir_statloc =
1582 bake_statloc (info->desktop_dir,
1587 if (setup_desktop_dirs &&
1588 info->user_desktop_dir != NULL) {
1589 uri = gnome_vfs_get_uri_from_local_path
1590 (info->user_desktop_dir);
1591 if (gnome_vfs_monitor_add (&info->user_desktop_dir_monitor,
1593 GNOME_VFS_MONITOR_DIRECTORY,
1594 vfolder_user_desktop_dir_monitor,
1595 info) != GNOME_VFS_OK) {
1596 info->user_desktop_dir_monitor = NULL;
1597 info->user_desktop_dir_statloc =
1598 bake_statloc (info->user_desktop_dir,
1609 vfolder_info_init (VFolderInfo *info, const char *scheme)
1614 info->scheme = g_strdup (scheme);
1616 info->filename = g_strconcat (SYSCONFDIR, "/X11/desktop-menus/",
1619 info->user_filename = g_strconcat (g_get_home_dir (),
1620 "/" DOT_GNOME "/vfolders/",
1621 scheme, ".vfolder-info",
1623 info->desktop_dir = g_strconcat (SYSCONFDIR,
1624 "/gnome-vfs-2.0/vfolders/",
1626 info->user_desktop_dir = g_strconcat (g_get_home_dir (),
1627 "/" DOT_GNOME "/vfolders/",
1630 /* Init the desktop paths */
1632 list = g_slist_prepend (list, g_strdup ("/usr/share/applications/"));
1633 if (strcmp ("/usr/share/applications/", DATADIR "/applications/") != 0)
1634 list = g_slist_prepend (list, g_strdup (DATADIR "/applications/"));
1635 path = g_getenv ("DESKTOP_FILE_PATH");
1638 char **ppath = g_strsplit (path, ":", -1);
1639 for (i = 0; ppath[i] != NULL; i++) {
1640 const char *dir = ppath[i];
1641 list = g_slist_prepend (list, g_strdup (dir));
1645 info->item_dirs = g_slist_reverse (list);
1647 info->user_item_dir = g_strconcat (g_get_home_dir (),
1648 "/" DOT_GNOME "/vfolders/",
1652 info->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1654 info->root = folder_new ("Root");
1656 info->modification_time = time (NULL);
1660 vfolder_info_free_internals_unlocked (VFolderInfo *info)
1665 if (info->filename_monitor != NULL) {
1666 gnome_vfs_monitor_cancel (info->filename_monitor);
1667 info->filename_monitor = NULL;
1670 if (info->user_filename_monitor != NULL) {
1671 gnome_vfs_monitor_cancel (info->user_filename_monitor);
1672 info->user_filename_monitor = NULL;
1675 g_free (info->filename_statloc);
1676 info->filename_statloc = NULL;
1678 g_free (info->user_filename_statloc);
1679 info->user_filename_statloc = NULL;
1682 if (info->desktop_dir_monitor != NULL) {
1683 gnome_vfs_monitor_cancel (info->desktop_dir_monitor);
1684 info->desktop_dir_monitor = NULL;
1687 if (info->user_desktop_dir_monitor != NULL) {
1688 gnome_vfs_monitor_cancel (info->user_desktop_dir_monitor);
1689 info->user_desktop_dir_monitor = NULL;
1692 g_free (info->desktop_dir_statloc);
1693 info->desktop_dir_statloc = NULL;
1695 g_free (info->user_desktop_dir_statloc);
1696 info->user_desktop_dir_statloc = NULL;
1699 g_slist_foreach (info->item_dir_monitors,
1700 (GFunc)gnome_vfs_monitor_cancel, NULL);
1701 g_slist_free (info->item_dir_monitors);
1702 info->item_dir_monitors = NULL;
1704 g_free (info->scheme);
1705 info->scheme = NULL;
1707 g_free (info->filename);
1708 info->filename = NULL;
1710 g_free (info->user_filename);
1711 info->user_filename = NULL;
1713 g_free (info->desktop_dir);
1714 info->desktop_dir = NULL;
1716 g_free (info->user_desktop_dir);
1717 info->user_desktop_dir = NULL;
1719 g_slist_foreach (info->item_dirs, (GFunc)g_free, NULL);
1720 g_slist_free (info->item_dirs);
1721 info->item_dirs = NULL;
1723 g_free (info->user_item_dir);
1724 info->user_item_dir = NULL;
1726 g_slist_foreach (info->merge_dirs, (GFunc)g_free, NULL);
1727 g_slist_free (info->merge_dirs);
1728 info->merge_dirs = NULL;
1730 g_slist_foreach (info->entries, (GFunc)entry_unref, NULL);
1731 g_slist_free (info->entries);
1732 info->entries = NULL;
1734 if (info->entries_ht != NULL)
1735 g_hash_table_destroy (info->entries_ht);
1736 info->entries_ht = NULL;
1738 g_slist_foreach (info->unallocated_folders,
1741 g_slist_free (info->unallocated_folders);
1742 info->unallocated_folders = NULL;
1744 entry_unref ((Entry *)info->root);
1747 g_slist_foreach (info->stat_dirs, (GFunc)g_free, NULL);
1748 g_slist_free (info->stat_dirs);
1749 info->stat_dirs = NULL;
1751 g_slist_foreach (info->folder_monitors,
1752 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1753 g_slist_free (info->folder_monitors);
1754 info->folder_monitors = NULL;
1756 g_slist_foreach (info->free_folder_monitors,
1757 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1758 g_slist_free (info->free_folder_monitors);
1759 info->free_folder_monitors = NULL;
1761 g_slist_foreach (info->file_monitors,
1762 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1763 g_slist_free (info->file_monitors);
1764 info->file_monitors = NULL;
1766 g_slist_foreach (info->free_file_monitors,
1767 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1768 g_slist_free (info->free_file_monitors);
1769 info->free_file_monitors = NULL;
1771 if (info->reread_queue != 0)
1772 g_source_remove (info->reread_queue);
1773 info->reread_queue = 0;
1777 vfolder_info_free_internals (VFolderInfo *info)
1779 G_LOCK (vfolder_lock);
1780 vfolder_info_free_internals_unlocked (info);
1781 G_UNLOCK (vfolder_lock);
1785 vfolder_info_destroy (VFolderInfo *info)
1787 vfolder_info_free_internals (info);
1792 single_query_read (xmlNode *qnode)
1797 if (qnode->type != XML_ELEMENT_NODE ||
1798 qnode->name == NULL)
1803 if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
1804 qnode->xmlChildrenNode != NULL) {
1807 for (iter = qnode->xmlChildrenNode;
1808 iter != NULL && query == NULL;
1810 query = single_query_read (iter);
1811 if (query != NULL) {
1812 query->not = ! query->not;
1815 } else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
1816 xmlChar *word = xmlNodeGetContent (qnode);
1818 query = query_new (QUERY_KEYWORD);
1819 ((QueryKeyword *)query)->keyword =
1820 g_quark_from_string (word);
1825 } else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
1826 xmlChar *file = xmlNodeGetContent (qnode);
1828 query = query_new (QUERY_FILENAME);
1829 ((QueryFilename *)query)->filename =
1835 } else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
1836 query = query_new (QUERY_AND);
1837 } else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
1838 query = query_new (QUERY_OR);
1840 /* We don't understand */
1844 /* This must be OR or AND */
1845 g_assert (query != NULL);
1847 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1848 Query *new_query = single_query_read (node);
1850 if (new_query != NULL)
1851 query->queries = g_slist_prepend
1852 (query->queries, new_query);
1855 query->queries = g_slist_reverse (query->queries);
1861 add_or_set_query (Query **query, Query *new_query)
1863 if (*query == NULL) {
1866 Query *old_query = *query;
1867 *query = query_new (QUERY_OR);
1869 g_slist_append ((*query)->queries, old_query);
1871 g_slist_append ((*query)->queries, new_query);
1876 query_read (xmlNode *qnode)
1883 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1884 if (node->type != XML_ELEMENT_NODE ||
1888 if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
1889 node->xmlChildrenNode != NULL) {
1891 Query *new_query = NULL;
1893 for (iter = node->xmlChildrenNode;
1894 iter != NULL && new_query == NULL;
1896 new_query = single_query_read (iter);
1897 if (new_query != NULL) {
1898 new_query->not = ! new_query->not;
1899 add_or_set_query (&query, new_query);
1902 Query *new_query = single_query_read (node);
1903 if (new_query != NULL)
1904 add_or_set_query (&query, new_query);
1912 folder_read (VFolderInfo *info, xmlNode *fnode)
1917 folder = folder_new (NULL);
1919 for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
1920 if (node->type != XML_ELEMENT_NODE ||
1924 if (g_ascii_strcasecmp (node->name, "Name") == 0) {
1925 xmlChar *name = xmlNodeGetContent (node);
1927 g_free (folder->entry.name);
1928 folder->entry.name = g_strdup (name);
1931 } else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
1932 xmlChar *desktop = xmlNodeGetContent (node);
1933 if (desktop != NULL) {
1934 g_free (folder->desktop_file);
1935 folder->desktop_file = g_strdup (desktop);
1938 } else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
1939 xmlChar *file = xmlNodeGetContent (node);
1942 char *str = g_strdup (file);
1943 folder->includes = g_slist_prepend
1944 (folder->includes, str);
1945 if (folder->includes_ht == NULL) {
1946 folder->includes_ht =
1947 g_hash_table_new_full
1953 li = g_hash_table_lookup (folder->includes_ht,
1957 /* Note: this will NOT change folder->includes
1959 folder->includes = g_slist_delete_link
1960 (folder->includes, li);
1962 g_hash_table_replace (folder->includes_ht,
1963 file, folder->includes);
1966 } else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
1967 xmlChar *file = xmlNodeGetContent (node);
1970 if (folder->excludes == NULL) {
1971 folder->excludes = g_hash_table_new_full
1974 (GDestroyNotify)g_free,
1977 s = g_strdup (file);
1978 g_hash_table_replace (folder->excludes, s, s);
1981 } else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
1984 query = query_read (node);
1986 if (query != NULL) {
1987 if (folder->query != NULL)
1988 query_destroy (folder->query);
1989 folder->query = query;
1991 } else if (g_ascii_strcasecmp (node->name, "OnlyUnallocated") == 0) {
1992 info->unallocated_folders =
1993 g_slist_prepend (info->unallocated_folders,
1994 (Folder *)entry_ref ((Entry *)folder));
1995 folder->only_unallocated = TRUE;
1996 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
1997 Folder *new_folder = folder_read (info, node);
1998 if (new_folder != NULL) {
1999 folder->subfolders =
2000 g_slist_append (folder->subfolders,
2002 new_folder->parent = folder;
2004 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2005 folder->read_only = TRUE;
2006 } else if (g_ascii_strcasecmp (node->name,
2007 "DontShowIfEmpty") == 0) {
2008 folder->dont_show_if_empty = TRUE;
2012 /* Name is required */
2013 if (folder->entry.name == NULL) {
2014 entry_unref ((Entry *)folder);
2018 folder->includes = g_slist_reverse (folder->includes);
2024 subst_home (const char *dir)
2027 return g_strconcat (g_get_home_dir (),
2031 return g_strdup (dir);
2034 /* FORMAT looks like:
2036 * <!-- Merge dirs optional -->
2037 * <MergeDir>/etc/X11/applnk</MergeDir>
2038 * <!-- Only specify if it should override standard location -->
2039 * <ItemDir>/usr/share/applications</ItemDir>
2040 * <!-- This is where the .directories are -->
2041 * <DesktopDir>/etc/X11/gnome/vfolders</DesktopDir>
2042 * <!-- Root folder -->
2046 * <Include>important.desktop</Include>
2048 * <!-- Other folders -->
2050 * <Name>SomeFolder</Name>
2053 * <Name>Test_Folder</Name>
2054 * <!-- could also be absolute -->
2055 * <Desktop>Test_Folder.directory</Desktop>
2059 * <Keyword>Application</Keyword>
2060 * <Keyword>Game</Keyword>
2062 * <Keyword>Clock</Keyword>
2065 * <Include>somefile.desktop</Include>
2066 * <Include>someotherfile.desktop</Include>
2067 * <Exclude>yetanother.desktop</Exclude>
2074 vfolder_info_read_info (VFolderInfo *info,
2075 GnomeVFSResult *result,
2076 GnomeVFSContext *context)
2080 gboolean got_a_vfolder_dir = FALSE;
2083 if (info->user_filename != NULL &&
2084 access (info->user_filename, F_OK) == 0) {
2085 doc = xmlParseFile (info->user_filename);
2087 info->user_file_active = TRUE;
2090 access (info->filename, F_OK) == 0)
2091 doc = xmlParseFile (info->filename);
2093 if (gnome_vfs_context_check_cancellation (context)) {
2095 *result = GNOME_VFS_ERROR_CANCELLED;
2100 || doc->xmlRootNode == NULL
2101 || doc->xmlRootNode->name == NULL
2102 || g_ascii_strcasecmp (doc->xmlRootNode->name, "VFolderInfo") != 0) {
2104 return TRUE; /* FIXME: really, shouldn't we error out? */
2107 for (node = doc->xmlRootNode->xmlChildrenNode; node != NULL; node = node->next) {
2108 if (node->type != XML_ELEMENT_NODE ||
2112 if (gnome_vfs_context_check_cancellation (context)) {
2114 *result = GNOME_VFS_ERROR_CANCELLED;
2118 if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
2119 xmlChar *dir = xmlNodeGetContent (node);
2121 info->merge_dirs = g_slist_append (info->merge_dirs,
2125 } else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
2126 xmlChar *dir = xmlNodeGetContent (node);
2128 if ( ! got_a_vfolder_dir) {
2129 g_slist_foreach (info->item_dirs,
2130 (GFunc)g_free, NULL);
2131 g_slist_free (info->item_dirs);
2132 info->item_dirs = NULL;
2134 got_a_vfolder_dir = TRUE;
2135 info->item_dirs = g_slist_append (info->item_dirs,
2139 } else if (g_ascii_strcasecmp (node->name, "UserItemDir") == 0) {
2140 xmlChar *dir = xmlNodeGetContent (node);
2142 g_free (info->user_item_dir);
2143 info->user_item_dir = subst_home (dir);
2146 } else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
2147 xmlChar *dir = xmlNodeGetContent (node);
2149 g_free (info->desktop_dir);
2150 info->desktop_dir = g_strdup (dir);
2153 } else if (g_ascii_strcasecmp (node->name, "UserDesktopDir") == 0) {
2154 xmlChar *dir = xmlNodeGetContent (node);
2156 g_free (info->user_desktop_dir);
2157 info->user_desktop_dir = subst_home (dir);
2160 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
2161 Folder *folder = folder_read (info, node);
2162 if (folder != NULL) {
2163 if (info->root != NULL)
2164 entry_unref ((Entry *)info->root);
2165 info->root = folder;
2167 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2168 info->read_only = TRUE;
2178 add_xml_tree_from_query (xmlNode *parent, Query *query)
2180 xmlNode *real_parent;
2183 real_parent = xmlNewChild (parent /* parent */,
2186 NULL /* content */);
2188 real_parent = parent;
2190 if (query->type == QUERY_KEYWORD) {
2191 QueryKeyword *qkeyword = (QueryKeyword *)query;
2192 const char *string = g_quark_to_string (qkeyword->keyword);
2194 xmlNewChild (real_parent /* parent */,
2196 "Keyword" /* name */,
2197 string /* content */);
2198 } else if (query->type == QUERY_FILENAME) {
2199 QueryFilename *qfilename = (QueryFilename *)query;
2201 xmlNewChild (real_parent /* parent */,
2203 "Filename" /* name */,
2204 qfilename->filename /* content */);
2205 } else if (query->type == QUERY_OR ||
2206 query->type == QUERY_AND) {
2211 if (query->type == QUERY_OR)
2213 else /* QUERY_AND */
2216 node = xmlNewChild (real_parent /* parent */,
2219 NULL /* content */);
2221 for (li = query->queries; li != NULL; li = li->next) {
2222 Query *subquery = li->data;
2223 add_xml_tree_from_query (node, subquery);
2226 g_assert_not_reached ();
2231 add_excludes_to_xml (gpointer key, gpointer value, gpointer user_data)
2233 const char *filename = key;
2234 xmlNode *folder_node = user_data;
2236 xmlNewChild (folder_node /* parent */,
2238 "Exclude" /* name */,
2239 filename /* content */);
2243 add_xml_tree_from_folder (xmlNode *parent, Folder *folder)
2246 xmlNode *folder_node;
2249 folder_node = xmlNewChild (parent /* parent */,
2251 "Folder" /* name */,
2252 NULL /* content */);
2254 xmlNewChild (folder_node /* parent */,
2257 folder->entry.name /* content */);
2259 if (folder->desktop_file != NULL) {
2260 xmlNewChild (folder_node /* parent */,
2262 "Desktop" /* name */,
2263 folder->desktop_file /* content */);
2266 if (folder->read_only)
2267 xmlNewChild (folder_node /* parent */,
2269 "ReadOnly" /* name */,
2270 NULL /* content */);
2271 if (folder->dont_show_if_empty)
2272 xmlNewChild (folder_node /* parent */,
2274 "DontShowIfEmpty" /* name */,
2275 NULL /* content */);
2276 if (folder->only_unallocated)
2277 xmlNewChild (folder_node /* parent */,
2279 "OnlyUnallocated" /* name */,
2280 NULL /* content */);
2282 for (li = folder->subfolders; li != NULL; li = li->next) {
2283 Folder *subfolder = li->data;
2284 add_xml_tree_from_folder (folder_node, subfolder);
2287 for (li = folder->includes; li != NULL; li = li->next) {
2288 const char *include = li->data;
2289 xmlNewChild (folder_node /* parent */,
2291 "Include" /* name */,
2292 include /* content */);
2295 if (folder->excludes) {
2296 g_hash_table_foreach (folder->excludes,
2297 add_excludes_to_xml,
2301 if (folder->query != NULL) {
2302 xmlNode *query_node;
2303 query_node = xmlNewChild (folder_node /* parent */,
2306 NULL /* content */);
2308 add_xml_tree_from_query (query_node, folder->query);
2313 xml_tree_from_vfolder (VFolderInfo *info)
2319 doc = xmlNewDoc ("1.0");
2321 topnode = xmlNewDocNode (doc /* doc */,
2323 "VFolderInfo" /* name */,
2324 NULL /* content */);
2325 doc->xmlRootNode = topnode;
2327 for (li = info->merge_dirs; li != NULL; li = li->next) {
2328 const char *merge_dir = li->data;
2329 xmlNewChild (topnode /* parent */,
2331 "MergeDir" /* name */,
2332 merge_dir /* content */);
2335 for (li = info->item_dirs; li != NULL; li = li->next) {
2336 const char *item_dir = li->data;
2337 xmlNewChild (topnode /* parent */,
2339 "ItemDir" /* name */,
2340 item_dir /* content */);
2343 if (info->user_item_dir != NULL) {
2344 xmlNewChild (topnode /* parent */,
2346 "UserItemDir" /* name */,
2347 info->user_item_dir /* content */);
2350 if (info->desktop_dir != NULL) {
2351 xmlNewChild (topnode /* parent */,
2353 "DesktopDir" /* name */,
2354 info->desktop_dir /* content */);
2357 if (info->user_desktop_dir != NULL) {
2358 xmlNewChild (topnode /* parent */,
2360 "UserDesktopDir" /* name */,
2361 info->user_desktop_dir /* content */);
2364 if (info->root != NULL)
2365 add_xml_tree_from_folder (topnode, info->root);
2370 /* FIXME: what to do about errors */
2372 vfolder_info_write_user (VFolderInfo *info)
2376 if (info->inhibit_write > 0)
2379 if (info->user_filename == NULL)
2382 doc = xml_tree_from_vfolder (info);
2386 /* FIXME: errors, anyone? */
2387 ensure_dir (info->user_filename,
2388 TRUE /* ignore_basename */);
2390 xmlSaveFormatFile (info->user_filename, doc, TRUE /* format */);
2391 /* not as good as a stat, but cheaper ... hmmm what is
2392 * the likelyhood of this not being the same as ctime */
2393 info->user_filename_last_write = time (NULL);
2397 info->user_file_active = TRUE;
2398 info->dirty = FALSE;
2400 info->modification_time = time (NULL);
2403 /* An EVIL function for quick reading of .desktop files,
2404 * only reads in one or two keys, but that's ALL we need */
2406 readitem_entry (const char *filename,
2414 int keylen1, keylen2;
2417 if (result2 != NULL)
2420 fp = fopen (filename, "r");
2425 keylen1 = strlen (key1);
2427 keylen2 = strlen (key2);
2431 /* This is slightly wrong, it should only look
2432 * at the correct section */
2433 while (fgets (buf, sizeof (buf), fp) != NULL) {
2437 char **result = NULL;
2439 /* check if it's one of the keys */
2440 if (strncmp (buf, key1, keylen1) == 0) {
2443 } else if (keylen2 >= 0 &&
2444 strncmp (buf, key2, keylen2) == 0) {
2453 /* still not our key */
2454 if (!(*p == '=' || *p == ' ')) {
2459 while (*p == ' ' || *p == '=');
2461 /* get rid of trailing \n */
2463 if (p[len-1] == '\n' ||
2467 *result = g_strdup (p);
2469 if (*result1 != NULL &&
2470 (result2 == NULL || *result2 != NULL))
2478 vfolder_info_insert_entry (VFolderInfo *info, EntryFile *efile)
2482 entry_ref ((Entry *)efile);
2484 entry_list = g_hash_table_lookup (info->entries_ht, efile->entry.name);
2486 info->entries = g_slist_prepend (info->entries, efile);
2487 /* The hash table contains the GSList pointer */
2488 g_hash_table_replace (info->entries_ht, efile->entry.name,
2491 if (entry_list != NULL) {
2492 Entry *entry = entry_list->data;
2493 info->entries = g_slist_delete_link (info->entries,
2495 entry_unref (entry);
2500 set_keywords (EntryFile *efile, const char *keywords)
2502 if (keywords != NULL) {
2504 char **parsed = g_strsplit (keywords, ";", -1);
2505 for (i = 0; parsed[i] != NULL; i++) {
2507 const char *word = parsed[i];
2508 /* ignore empties (including end of list) */
2509 if (word[0] == '\0')
2511 quark = g_quark_from_string (word);
2512 efile->keywords = g_slist_prepend
2514 GINT_TO_POINTER (quark));
2516 g_strfreev (parsed);
2521 make_entry_file (const char *dir, const char *name)
2529 filename = g_build_filename (dir, name, NULL);
2531 readitem_entry (filename,
2537 if (only_show_in != NULL) {
2538 gboolean show = FALSE;
2539 char **parsed = g_strsplit (only_show_in, ";", -1);
2540 for (i = 0; parsed[i] != NULL; i++) {
2541 if (strcmp (parsed[i], "GNOME") == 0) {
2546 g_strfreev (parsed);
2549 g_free (only_show_in);
2550 g_free (categories);
2555 efile = file_new (name);
2556 efile->filename = filename;
2558 set_keywords (efile, categories);
2560 g_free (only_show_in);
2561 g_free (categories);
2567 vfolder_info_read_items_from (VFolderInfo *info,
2568 const char *item_dir,
2570 GnomeVFSResult *result,
2571 GnomeVFSContext *context)
2576 dir = opendir (item_dir);
2580 while ((de = readdir (dir)) != NULL) {
2583 if (gnome_vfs_context_check_cancellation (context)) {
2585 *result = GNOME_VFS_ERROR_CANCELLED;
2589 /* files MUST be called .desktop */
2590 if (de->d_name[0] == '.' ||
2591 ! check_ext (de->d_name, ".desktop"))
2594 efile = make_entry_file (item_dir, de->d_name);
2598 efile->per_user = per_user;
2600 vfolder_info_insert_entry (info, efile);
2601 entry_unref ((Entry *)efile);
2610 vfolder_info_read_items_merge (VFolderInfo *info,
2611 const char *merge_dir,
2613 GQuark inherited_keyword,
2614 GnomeVFSResult *result,
2615 GnomeVFSContext *context)
2619 GQuark extra_keyword;
2623 gboolean pass_down_extra_keyword = TRUE;
2625 dir = opendir (merge_dir);
2629 Application = g_quark_from_static_string ("Application");
2630 Merged = g_quark_from_static_string ("Merged");
2632 /* FIXME: this should be a hash or something */
2634 if (subdir == NULL) {
2635 extra_keyword = g_quark_from_static_string ("Core");
2636 pass_down_extra_keyword = FALSE;
2637 } else if (g_ascii_strcasecmp (subdir, "Development") == 0)
2638 extra_keyword = g_quark_from_static_string ("Development");
2639 else if (g_ascii_strcasecmp (subdir, "Editors") == 0)
2640 extra_keyword = g_quark_from_static_string ("TextEditor");
2641 else if (g_ascii_strcasecmp (subdir, "Games") == 0)
2642 extra_keyword = g_quark_from_static_string ("Game");
2643 else if (g_ascii_strcasecmp (subdir, "Graphics") == 0)
2644 extra_keyword = g_quark_from_static_string ("Graphics");
2645 else if (g_ascii_strcasecmp (subdir, "Internet") == 0)
2646 extra_keyword = g_quark_from_static_string ("Network");
2647 else if (g_ascii_strcasecmp (subdir, "Multimedia") == 0)
2648 extra_keyword = g_quark_from_static_string ("AudioVideo");
2649 else if (g_ascii_strcasecmp (subdir, "Office") == 0)
2650 extra_keyword = g_quark_from_static_string ("Office");
2651 else if (g_ascii_strcasecmp (subdir, "Settings") == 0)
2652 extra_keyword = g_quark_from_static_string ("Settings");
2653 else if (g_ascii_strcasecmp (subdir, "System") == 0)
2654 extra_keyword = g_quark_from_static_string ("System");
2655 else if (g_ascii_strcasecmp (subdir, "Utilities") == 0)
2656 extra_keyword = g_quark_from_static_string ("Utility");
2658 while ((de = readdir (dir)) != NULL) {
2661 if (gnome_vfs_context_check_cancellation (context)) {
2663 *result = GNOME_VFS_ERROR_CANCELLED;
2668 if (de->d_name[0] == '.')
2671 /* files MUST be called .desktop, so
2672 * treat all others as dirs. If we're wrong,
2673 * the open will fail, which is ok */
2674 if ( ! check_ext (de->d_name, ".desktop")) {
2675 /* if this is a directory recurse */
2676 char *fullname = g_build_filename (merge_dir, de->d_name, NULL);
2677 if ((pass_down_extra_keyword == TRUE) && (extra_keyword != 0)) {
2678 inheritance = extra_keyword;
2680 inheritance = inherited_keyword;
2683 if ( ! vfolder_info_read_items_merge (info,
2696 /* FIXME: add some keywords about some known apps
2697 * like gimp and whatnot, perhaps take these from the vfolder
2698 * file or some such */
2700 efile = make_entry_file (merge_dir, de->d_name);
2704 /* If no keywords set, then add the standard ones */
2705 if (efile->keywords == NULL) {
2706 efile->keywords = g_slist_prepend
2708 GINT_TO_POINTER (Application));
2710 efile->keywords = g_slist_prepend
2712 GINT_TO_POINTER (Merged));
2714 if (inherited_keyword != 0) {
2715 efile->keywords = g_slist_prepend
2717 GINT_TO_POINTER (inherited_keyword));
2720 if (extra_keyword != 0) {
2721 efile->keywords = g_slist_prepend
2723 GINT_TO_POINTER (extra_keyword));
2725 efile->implicit_keywords = TRUE;
2728 vfolder_info_insert_entry (info, efile);
2729 entry_unref ((Entry *)efile);
2738 find_entry (GSList *list, const char *name)
2742 for (li = list; li != NULL; li = li->next) {
2743 Entry *entry = li->data;
2744 if (strcmp (name, entry->name) == 0)
2751 file_monitor (GnomeVFSMonitorHandle *handle,
2752 const gchar *monitor_uri,
2753 const gchar *info_uri,
2754 GnomeVFSMonitorEventType event_type,
2757 FileMonitorHandle *h = user_data;
2759 /* proxy the event through if it is a changed event
2762 if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED &&
2764 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) h,
2765 h->uri, event_type);
2769 try_free_file_monitors_create_files_unlocked (VFolderInfo *info)
2773 list = g_slist_copy (info->free_file_monitors);
2775 for (li = list; li != NULL; li = li->next) {
2776 FileMonitorHandle *handle = li->data;
2778 GnomeVFSResult result;
2779 char *dirfile = NULL;
2781 if (handle->is_directory_file) {
2785 /* Evil! EVIL URI PARSING. this will eat a lot of
2786 * stack if we have lots of free monitors */
2788 VFOLDER_URI_PARSE (handle->uri, &vuri);
2790 folder = resolve_folder (info,
2792 TRUE /* ignore_basename */,
2799 dirfile = get_directory_file_unlocked (info, folder);
2800 if (dirfile == NULL)
2803 entry = (Entry *)folder;
2807 GnomeVFSResult result;
2811 /* Evil! EVIL URI PARSING. this will eat a lot of
2812 * stack if we have lots of monitors */
2814 VFOLDER_URI_PARSE (handle->uri, &vuri);
2816 f = resolve_folder (info,
2818 TRUE /* ignore_basename */,
2823 ensure_folder_unlocked (
2825 FALSE /* subfolders */,
2827 FALSE /* ignore_unallocated */);
2828 entry = find_entry (f->entries, vuri.file);
2835 info->free_file_monitors =
2836 g_slist_remove (info->free_file_monitors, handle);
2838 g_slist_prepend (entry->monitors, handle);
2840 handle->exists = TRUE;
2841 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
2843 GNOME_VFS_MONITOR_EVENT_CREATED);
2845 /* recreate a handle */
2846 if (handle->handle == NULL &&
2847 entry->type == ENTRY_FILE) {
2848 EntryFile *efile = (EntryFile *)entry;
2849 char *uri = gnome_vfs_get_uri_from_local_path
2852 gnome_vfs_monitor_add (&(handle->handle),
2854 GNOME_VFS_MONITOR_FILE,
2859 } else if (handle->handle == NULL &&
2861 char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2863 gnome_vfs_monitor_add (&(handle->handle),
2865 GNOME_VFS_MONITOR_FILE,
2875 g_slist_free (list);
2878 static void /* unlocked */
2879 rescan_monitors (VFolderInfo *info)
2883 if (info->file_monitors == NULL)
2886 for (li = info->file_monitors; li != NULL; li = li->next) {
2887 FileMonitorHandle *h = li->data;
2888 GnomeVFSResult result;
2890 char *dirfile = NULL;
2892 /* these are handled below */
2896 if (h->is_directory_file) {
2900 /* Evil! EVIL URI PARSING. this will eat a lot of
2901 * stack if we have lots of monitors */
2903 VFOLDER_URI_PARSE (h->uri, &vuri);
2905 folder = resolve_folder (info,
2907 TRUE /* ignore_basename */,
2911 dirfile = get_directory_file_unlocked (info,
2914 if (dirfile == NULL) {
2916 gnome_vfs_monitor_callback
2917 ((GnomeVFSMethodHandle *)h,
2919 GNOME_VFS_MONITOR_EVENT_DELETED);
2920 info->free_file_monitors = g_slist_prepend
2921 (info->free_file_monitors, h);
2922 file_monitor_handle_ref_unlocked (h);
2923 /* it has been unreffed when the entry was
2928 entry = (Entry *)folder;
2932 GnomeVFSResult result;
2936 /* Evil! EVIL URI PARSING. this will eat a lot of
2937 * stack if we have lots of monitors */
2939 VFOLDER_URI_PARSE (h->uri, &vuri);
2941 f = resolve_folder (info,
2943 TRUE /* ignore_basename */,
2948 ensure_folder_unlocked (
2950 FALSE /* subfolders */,
2952 FALSE /* ignore_unallocated */);
2953 entry = find_entry (f->entries, vuri.file);
2956 if (entry == NULL) {
2958 gnome_vfs_monitor_callback
2959 ((GnomeVFSMethodHandle *)h,
2961 GNOME_VFS_MONITOR_EVENT_DELETED);
2962 info->free_file_monitors = g_slist_prepend
2963 (info->free_file_monitors, h);
2964 file_monitor_handle_ref_unlocked (h);
2965 /* it has been unreffed when the entry was
2971 /* recreate a handle */
2972 if (h->handle == NULL &&
2973 entry->type == ENTRY_FILE) {
2974 EntryFile *efile = (EntryFile *)entry;
2975 char *uri = gnome_vfs_get_uri_from_local_path
2978 gnome_vfs_monitor_add (&(h->handle),
2980 GNOME_VFS_MONITOR_FILE,
2985 } else if (h->handle == NULL &&
2987 char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2989 gnome_vfs_monitor_add (&(h->handle),
2991 GNOME_VFS_MONITOR_FILE,
3001 try_free_file_monitors_create_files_unlocked (info);
3004 static gboolean /* unlocked */
3005 vfolder_info_read_items (VFolderInfo *info,
3006 GnomeVFSResult *result,
3007 GnomeVFSContext *context)
3012 for (li = info->merge_dirs; li != NULL; li = li->next) {
3013 const char *merge_dir = li->data;
3015 if ( ! vfolder_info_read_items_merge (info, merge_dir, NULL, FALSE,
3020 /* Then read the real thing (later overrides) */
3021 for (li = info->item_dirs; li != NULL; li = li->next) {
3022 const char *item_dir = li->data;
3024 if ( ! vfolder_info_read_items_from (info, item_dir,
3025 FALSE /* per_user */,
3030 if (info->user_item_dir != NULL) {
3031 if ( ! vfolder_info_read_items_from (info,
3032 info->user_item_dir,
3033 TRUE /* per_user */,
3038 rescan_monitors (info);
3044 string_slist_equal (GSList *list1, GSList *list2)
3048 for (li1 = list1, li2 = list2;
3049 li1 != NULL && li2 != NULL;
3050 li1 = li1->next, li2 = li2->next) {
3051 const char *s1 = li1->data;
3052 const char *s2 = li2->data;
3053 if (strcmp (s1, s2) != 0)
3056 /* if both are not NULL, then lengths are
3064 safe_string_same (const char *string1, const char *string2)
3066 if (string1 == string2 &&
3070 if (string1 != NULL && string2 != NULL &&
3071 strcmp (string1, string2) == 0)
3078 vfolder_info_item_dirs_same (VFolderInfo *info1, VFolderInfo *info2)
3080 if ( ! string_slist_equal (info1->item_dirs,
3084 if ( ! string_slist_equal (info1->merge_dirs,
3088 if ( ! safe_string_same (info1->user_item_dir,
3089 info2->user_item_dir))
3096 vfolder_info_reload_unlocked (VFolderInfo *info,
3097 GnomeVFSResult *result,
3098 GnomeVFSContext *context,
3099 gboolean force_read_items)
3101 VFolderInfo *newinfo;
3102 gboolean setup_filenames;
3103 gboolean setup_itemdirs;
3106 /* FIXME: Hmmm, race, there is no locking YAIKES,
3107 * we need filename locking for changes. eek, eek, eek */
3112 newinfo = g_new0 (VFolderInfo, 1);
3113 vfolder_info_init (newinfo, info->scheme);
3115 g_free (newinfo->filename);
3116 g_free (newinfo->user_filename);
3117 newinfo->filename = g_strdup (info->filename);
3118 newinfo->user_filename = g_strdup (info->user_filename);
3120 if (gnome_vfs_context_check_cancellation (context)) {
3121 vfolder_info_destroy (newinfo);
3122 *result = GNOME_VFS_ERROR_CANCELLED;
3126 if ( ! vfolder_info_read_info (newinfo, result, context)) {
3127 vfolder_info_destroy (newinfo);
3131 /* FIXME: reload logic for 'desktop_dir' and
3132 * 'user_desktop_dir' */
3134 setup_itemdirs = TRUE;
3136 /* Validity of entries and item dirs and all that is unchanged */
3137 if (vfolder_info_item_dirs_same (info, newinfo)) {
3138 newinfo->entries = info->entries;
3139 info->entries = NULL;
3140 newinfo->entries_ht = info->entries_ht;
3141 info->entries_ht = NULL /* some places assume this
3142 non-null, but we're only
3143 going to destroy this */;
3144 newinfo->entries_valid = info->entries_valid;
3146 /* move over the monitors/statlocs since those are valid */
3147 newinfo->item_dir_monitors = info->item_dir_monitors;
3148 info->item_dir_monitors = NULL;
3149 newinfo->stat_dirs = info->stat_dirs;
3150 info->stat_dirs = NULL;
3152 /* No need to resetup dir monitors */
3153 setup_itemdirs = FALSE;
3155 /* No need to do anything with file monitors */
3157 /* Whack all monitors here! */
3158 for (li = info->file_monitors; li != NULL; li = li->next) {
3159 FileMonitorHandle *h = li->data;
3160 if (h->handle != NULL)
3161 gnome_vfs_monitor_cancel (h->handle);
3166 setup_filenames = TRUE;
3168 if (safe_string_same (info->filename, newinfo->filename) &&
3169 safe_string_same (info->user_filename, newinfo->user_filename)) {
3170 newinfo->user_filename_last_write =
3171 info->user_filename_last_write;
3173 /* move over the monitors/statlocs since those are valid */
3174 newinfo->filename_monitor = info->filename_monitor;
3175 info->filename_monitor = NULL;
3176 newinfo->user_filename_monitor = info->user_filename_monitor;
3177 info->user_filename_monitor = NULL;
3179 if (info->filename_statloc != NULL &&
3180 info->filename != NULL)
3181 newinfo->filename_statloc =
3182 bake_statloc (info->filename,
3184 if (info->user_filename_statloc != NULL &&
3185 info->user_filename != NULL)
3186 newinfo->user_filename_statloc =
3187 bake_statloc (info->user_filename,
3190 /* No need to resetup filename monitors */
3191 setup_filenames = FALSE;
3194 /* Note: not cancellable anymore, since we've
3195 * already started nibbling on the info structure,
3196 * so we'd need to back things out or some such,
3197 * too complex, so screw that */
3198 monitor_setup (info,
3201 /* FIXME: setup_desktop_dirs */ TRUE,
3204 for (li = info->folder_monitors;
3207 FileMonitorHandle *handle = li->data;
3210 add_folder_monitor_unlocked (newinfo, handle);
3212 file_monitor_handle_unref_unlocked (handle);
3214 g_slist_free (info->folder_monitors);
3215 info->folder_monitors = NULL;
3217 g_slist_foreach (info->free_folder_monitors,
3218 (GFunc)file_monitor_handle_unref_unlocked, NULL);
3219 g_slist_free (info->free_folder_monitors);
3220 info->folder_monitors = NULL;
3222 /* we can just copy these for now, they will be readded
3223 * and all the fun stuff will be done with them later */
3224 newinfo->file_monitors = info->file_monitors;
3225 info->file_monitors = NULL;
3226 newinfo->free_file_monitors = info->free_file_monitors;
3227 info->free_file_monitors = NULL;
3229 /* emit changed on all folders, a bit drastic, but oh well,
3230 * we also invalidate all folders at the same time, but that is
3231 * irrelevant since they should all just be invalid to begin with */
3232 invalidate_folder_T (info->root);
3234 /* FIXME: make sure if this was enough, I think it was */
3236 vfolder_info_free_internals_unlocked (info);
3237 memcpy (info, newinfo, sizeof (VFolderInfo));
3240 /* must rescan the monitors here */
3241 if (info->entries_valid) {
3242 rescan_monitors (info);
3245 if ( ! info->entries_valid &&
3248 /* FIXME: I bet cancelation plays havoc with monitors,
3249 * I'm not sure however */
3250 if (info->file_monitors != NULL) {
3251 vfolder_info_read_items (info, &res, NULL);
3253 if ( ! vfolder_info_read_items (info, result, context))
3256 info->entries_valid = TRUE;
3263 vfolder_info_reload (VFolderInfo *info,
3264 GnomeVFSResult *result,
3265 GnomeVFSContext *context,
3266 gboolean force_read_items)
3268 G_LOCK (vfolder_lock);
3269 if (vfolder_info_reload_unlocked (info, result, context,
3270 force_read_items)) {
3271 G_UNLOCK (vfolder_lock);
3274 G_UNLOCK (vfolder_lock);
3280 vfolder_info_recheck (VFolderInfo *info,
3281 GnomeVFSResult *result,
3282 GnomeVFSContext *context)
3285 time_t curtime = time (NULL);
3286 gboolean reread = FALSE;
3288 if (info->filename_statloc != NULL &&
3289 ! check_statloc (info->filename_statloc, curtime)) {
3290 if ( ! vfolder_info_reload_unlocked (info, result, context,
3291 FALSE /* force read items */)) {
3292 /* we have failed, make sure we fail
3294 info->filename_statloc->trigger_next = TRUE;
3300 info->user_filename_statloc != NULL &&
3301 ! check_statloc (info->user_filename_statloc, curtime)) {
3302 if ( ! vfolder_info_reload_unlocked (info, result, context,
3303 FALSE /* force read items */)) {
3304 /* we have failed, make sure we fail
3306 info->user_filename_statloc->trigger_next = TRUE;
3312 if (info->entries_valid) {
3313 for (li = info->stat_dirs; li != NULL; li = li->next) {
3314 StatLoc *sl = li->data;
3315 if ( ! check_statloc (sl, curtime)) {
3316 info->entries_valid = FALSE;
3324 static VFolderInfo *
3325 get_vfolder_info_unlocked (const char *scheme,
3326 GnomeVFSResult *result,
3327 GnomeVFSContext *context)
3331 if (infos != NULL &&
3332 (info = g_hash_table_lookup (infos, scheme)) != NULL) {
3333 if ( ! vfolder_info_recheck (info, result, context)) {
3336 if ( ! info->entries_valid) {
3337 g_slist_foreach (info->entries,
3338 (GFunc)entry_unref, NULL);
3339 g_slist_free (info->entries);
3340 info->entries = NULL;
3342 if (info->entries_ht != NULL)
3343 g_hash_table_destroy (info->entries_ht);
3344 info->entries_ht = g_hash_table_new (g_str_hash,
3347 if ( ! vfolder_info_read_items (info,
3349 info->entries_valid = FALSE;
3353 invalidate_folder_T (info->root);
3355 info->entries_valid = TRUE;
3357 /* Update modification time of all folders,
3358 * kind of evil, but it will make adding new items work
3359 * I hope. This is because rereading usually means
3360 * something changed */
3361 info->modification_time = time (NULL);
3366 if (gnome_vfs_context_check_cancellation (context)) {
3367 *result = GNOME_VFS_ERROR_CANCELLED;
3372 infos = g_hash_table_new_full
3373 (g_str_hash, g_str_equal,
3374 (GDestroyNotify)g_free,
3375 (GDestroyNotify)vfolder_info_destroy);
3377 info = g_new0 (VFolderInfo, 1);
3378 vfolder_info_init (info, scheme);
3380 if (gnome_vfs_context_check_cancellation (context)) {
3381 vfolder_info_destroy (info);
3382 *result = GNOME_VFS_ERROR_CANCELLED;
3386 if ( ! vfolder_info_read_info (info, result, context)) {
3387 vfolder_info_destroy (info);
3391 if ( ! monitor_setup (info,
3392 TRUE /* setup_filenames */,
3393 TRUE /* setup_itemdirs */,
3394 TRUE /* setup_desktop_dirs */,
3396 vfolder_info_destroy (info);
3400 g_hash_table_insert (infos, g_strdup (scheme), info);
3402 if ( ! vfolder_info_read_items (info, result, context)) {
3403 info->entries_valid = FALSE;
3406 info->entries_valid = TRUE;
3411 static VFolderInfo *
3412 get_vfolder_info (const char *scheme,
3413 GnomeVFSResult *result,
3414 GnomeVFSContext *context)
3417 G_LOCK (vfolder_lock);
3418 info = get_vfolder_info_unlocked (scheme, result, context);
3419 G_UNLOCK (vfolder_lock);
3425 keywords_to_string (GSList *keywords)
3428 GString *str = g_string_new (NULL);
3430 for (li = keywords; li != NULL; li = li->next) {
3431 GQuark word = GPOINTER_TO_INT (li->data);
3432 g_string_append (str, g_quark_to_string (word));
3433 g_string_append_c (str, ';');
3436 return g_string_free (str, FALSE);
3439 /* copy file and add keywords line */
3441 copy_file_with_keywords (const char *from, const char *to, GSList *keywords)
3447 char *keyword_string;
3449 if ( ! ensure_dir (to,
3450 TRUE /* ignore_basename */))
3453 wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3458 keyword_string = keywords_to_string (keywords);
3460 wfp = fdopen (wfd, "w");
3462 fp = fopen (from, "r");
3464 gboolean wrote_keywords = FALSE;
3465 while (fgets (buf, sizeof (buf), fp) != NULL) {
3466 fprintf (wfp, "%s", buf);
3467 if ( ! wrote_keywords &&
3468 (strncmp (buf, "[Desktop Entry]",
3469 strlen ("[Desktop Entry]")) == 0 ||
3470 strncmp (buf, "[KDE Desktop Entry]",
3471 strlen ("[KDE Desktop Entry]")) == 0)) {
3472 fprintf (wfp, "Categories=%s\n",
3474 wrote_keywords = TRUE;
3480 fprintf (wfp, "[Desktop Entry]\nCategories=%s\n",
3484 /* FIXME: does this close wfd???? */
3489 g_free (keyword_string);
3495 copy_file (const char *from, const char *to)
3500 if ( ! ensure_dir (to,
3501 TRUE /* ignore_basename */))
3504 wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3509 fd = open (from, O_RDONLY);
3514 while ((n = read (fd, buf, sizeof(buf))) > 0) {
3515 write (wfd, buf, n);
3527 make_file_private (VFolderInfo *info, EntryFile *efile)
3530 Entry *entry = (Entry *)efile;
3532 if (efile->per_user)
3535 /* this file already exists so whack its monitors */
3536 if (efile->filename != NULL) {
3539 for (li = entry->monitors; li != NULL; li = li->next) {
3540 FileMonitorHandle *h = li->data;
3541 if (h->handle != NULL)
3542 gnome_vfs_monitor_cancel (h->handle);
3547 newfname = g_build_filename (g_get_home_dir (),
3554 if (efile->implicit_keywords) {
3555 if (efile->filename != NULL &&
3556 ! copy_file_with_keywords (efile->filename,
3559 /* FIXME: what to do with monitors here, they
3560 * have already been whacked, a corner case
3566 if (efile->filename != NULL &&
3567 ! copy_file (efile->filename, newfname)) {
3568 /* FIXME: what to do with monitors here, they
3569 * have already been whacked, a corner case
3576 /* we didn't copy but ensure path anyway */
3577 if (efile->filename == NULL &&
3578 ! ensure_dir (newfname,
3579 TRUE /* ignore_basename */)) {
3584 /* this file already exists so re-add monitors at the new location */
3585 if (efile->filename != NULL) {
3587 char *uri = gnome_vfs_get_uri_from_local_path (newfname);
3589 for (li = entry->monitors; li != NULL; li = li->next) {
3590 FileMonitorHandle *h = li->data;
3592 gnome_vfs_monitor_add (&(h->handle),
3594 GNOME_VFS_MONITOR_FILE,
3602 g_free (efile->filename);
3603 efile->filename = newfname;
3604 efile->per_user = TRUE;
3610 try_free_file_monitors_create_dirfile_unlocked (VFolderInfo *info,
3615 list = g_slist_copy (info->free_file_monitors);
3617 for (li = list; li != NULL; li = li->next) {
3618 FileMonitorHandle *handle = li->data;
3621 GnomeVFSResult result;
3623 if ( ! handle->is_directory_file)
3626 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
3627 * have lots of free monitors */
3629 VFOLDER_URI_PARSE (handle->uri, &vuri);
3631 f = resolve_folder (info,
3633 TRUE /* ignore_basename */,
3640 info->free_file_monitors =
3641 g_slist_remove (info->free_file_monitors, handle);
3642 ((Entry *)folder)->monitors =
3643 g_slist_prepend (((Entry *)folder)->monitors, handle);
3645 handle->exists = TRUE;
3646 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
3648 GNOME_VFS_MONITOR_EVENT_CREATED);
3651 g_slist_free (list);
3655 make_new_dirfile (VFolderInfo *info, Folder *folder)
3657 char *name = g_strdup (folder->entry.name);
3663 for (p = name; *p != '\0'; p++) {
3664 if ( ! ( (*p >= 'a' && *p <= 'z') ||
3665 (*p >= 'A' && *p <= 'Z') ||
3666 (*p >= '0' && *p <= '9') ||
3680 fname = g_strdup_printf ("%s-%d.directory", name, i);
3682 fname = g_strdup_printf ("%s.directory", name);
3685 fullname = g_build_filename
3686 (info->user_desktop_dir, fname, NULL);
3687 fd = open (fullname, O_CREAT | O_WRONLY | O_EXCL, 0600);
3693 folder->desktop_file = fname;
3696 try_free_file_monitors_create_dirfile_unlocked (info, folder);
3700 make_dirfile_private (VFolderInfo *info, Folder *folder)
3708 if (info->user_desktop_dir == NULL)
3711 if ( ! ensure_dir (info->user_desktop_dir,
3712 FALSE /* ignore_basename */))
3716 if (folder->desktop_file == NULL) {
3717 make_new_dirfile (info, folder);
3721 /* FIXME: this is broken! What if the desktop file exists
3722 * in the local but there is a different (but with a same name)
3723 * .directory in the system. */
3724 fname = g_build_filename (info->user_desktop_dir,
3725 folder->desktop_file,
3728 if (access (fname, F_OK) == 0) {
3733 desktop_file = get_directory_file (info, folder);
3735 if (desktop_file == NULL) {
3736 int fd = open (fname, O_CREAT | O_EXCL | O_WRONLY, 0600);
3745 for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3746 FileMonitorHandle *h = li->data;
3747 if (h->is_directory_file) {
3748 if (h->handle != NULL)
3749 gnome_vfs_monitor_cancel (h->handle);
3756 if ( ! copy_file (desktop_file, fname)) {
3759 fname = desktop_file;
3760 desktop_file = NULL;
3763 uri = gnome_vfs_get_uri_from_local_path (fname);
3765 for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3766 FileMonitorHandle *h = li->data;
3768 if (h->is_directory_file) {
3769 gnome_vfs_monitor_add (&(h->handle),
3771 GNOME_VFS_MONITOR_FILE,
3779 g_free (desktop_file);
3786 resolve_folder (VFolderInfo *info,
3788 gboolean ignore_basename,
3789 GnomeVFSResult *result,
3790 GnomeVFSContext *context)
3794 Folder *folder = info->root;
3796 ppath = g_strsplit (path, "/", -1);
3798 if (ppath == NULL ||
3801 *result = GNOME_VFS_ERROR_INVALID_URI;
3805 for (i = 0; ppath [i] != NULL; i++) {
3806 const char *segment = ppath[i];
3808 if (*segment == '\0')
3811 if (ignore_basename && ppath [i + 1] == NULL)
3814 folder = (Folder *) find_entry (folder->subfolders,
3822 if (gnome_vfs_context_check_cancellation (context)) {
3823 *result = GNOME_VFS_ERROR_CANCELLED;
3828 *result = GNOME_VFS_ERROR_NOT_FOUND;
3834 resolve_path (VFolderInfo *info,
3836 const char *basename,
3837 Folder **return_folder,
3838 GnomeVFSResult *result,
3839 GnomeVFSContext *context)
3844 if (strcmp (path, "/") == 0)
3845 return (Entry *)info->root;
3847 folder = resolve_folder (info, path,
3848 TRUE /* ignore_basename */,
3851 if (return_folder != NULL)
3852 *return_folder = folder;
3854 if (folder == NULL) {
3858 /* Make sure we have the entries here */
3859 ensure_folder_unlocked (info, folder,
3860 FALSE /* subfolders */,
3862 FALSE /* ignore_unallocated */);
3864 entry = find_entry (folder->entries, basename);
3867 *result = GNOME_VFS_ERROR_NOT_FOUND;
3873 get_entry_unlocked (VFolderURI *vuri,
3875 gboolean *is_directory_file,
3876 GnomeVFSResult *result,
3877 GnomeVFSContext *context)
3882 if (is_directory_file != NULL)
3883 *is_directory_file = FALSE;
3887 info = get_vfolder_info_unlocked (vuri->scheme, result, context);
3891 if (gnome_vfs_context_check_cancellation (context)) {
3892 *result = GNOME_VFS_ERROR_CANCELLED;
3896 if (vuri->is_all_scheme) {
3899 if (vuri->file == NULL) {
3900 entry = resolve_path (info,
3909 efile_list = g_hash_table_lookup (info->entries_ht, vuri->file);
3911 if (efile_list == NULL) {
3912 *result = GNOME_VFS_ERROR_NOT_FOUND;
3915 return efile_list->data;
3919 if (vuri->file != NULL &&
3920 check_ext (vuri->file, ".directory") == TRUE) {
3923 folder = resolve_folder (info, vuri->path,
3924 TRUE /* ignore_basename */,
3926 if (folder == NULL) {
3930 if (is_directory_file != NULL)
3931 *is_directory_file = TRUE;
3936 return (Entry *)folder;
3938 entry = resolve_path (info, vuri->path, vuri->file, parent,
3945 get_entry (VFolderURI *vuri,
3947 gboolean *is_directory_file,
3948 GnomeVFSResult *result,
3949 GnomeVFSContext *context)
3953 G_LOCK (vfolder_lock);
3954 entry = get_entry_unlocked (vuri,
3958 G_UNLOCK (vfolder_lock);
3963 /* only works for files and only those that exist */
3964 /* unlocked function */
3965 static GnomeVFSURI *
3966 desktop_uri_to_file_uri (VFolderInfo *info,
3967 VFolderURI *desktop_vuri,
3969 gboolean *the_is_directory_file,
3970 Folder **the_folder,
3972 GnomeVFSResult *result,
3973 GnomeVFSContext *context)
3975 gboolean is_directory_file;
3976 GnomeVFSURI *ret_uri;
3977 Folder *folder = NULL;
3980 entry = get_entry_unlocked (desktop_vuri,
3988 if (gnome_vfs_context_check_cancellation (context)) {
3989 *result = GNOME_VFS_ERROR_CANCELLED;
3993 if (the_folder != NULL)
3994 *the_folder = folder;
3996 if (the_entry != NULL)
3998 if (the_is_directory_file != NULL)
3999 *the_is_directory_file = is_directory_file;
4001 if (is_directory_file &&
4002 entry->type == ENTRY_FOLDER) {
4005 folder = (Folder *)entry;
4007 if (the_folder != NULL)
4008 *the_folder = folder;
4010 /* we'll be doing something write like */
4011 if (folder->read_only &&
4013 *result = GNOME_VFS_ERROR_READ_ONLY;
4020 if (gnome_vfs_context_check_cancellation (context)) {
4021 *result = GNOME_VFS_ERROR_CANCELLED;
4025 if ( ! make_dirfile_private (info, folder)) {
4026 *result = GNOME_VFS_ERROR_GENERIC;
4029 fname = g_build_filename (g_get_home_dir (),
4030 folder->desktop_file,
4032 ret_uri = gnome_vfs_uri_new (fname);
4037 desktop_file = get_directory_file_unlocked (info, folder);
4038 if (desktop_file != NULL) {
4039 char *s = gnome_vfs_get_uri_from_local_path
4042 g_free (desktop_file);
4044 ret_uri = gnome_vfs_uri_new (s);
4049 *result = GNOME_VFS_ERROR_NOT_FOUND;
4052 } else if (entry->type == ENTRY_FILE) {
4053 EntryFile *efile = (EntryFile *)entry;
4056 /* we'll be doing something write like */
4057 if (folder != NULL &&
4058 folder->read_only &&
4060 *result = GNOME_VFS_ERROR_READ_ONLY;
4064 if (gnome_vfs_context_check_cancellation (context)) {
4065 *result = GNOME_VFS_ERROR_CANCELLED;
4070 ! make_file_private (info, efile)) {
4071 *result = GNOME_VFS_ERROR_GENERIC;
4075 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4076 ret_uri = gnome_vfs_uri_new (s);
4081 if (the_folder != NULL)
4082 *the_folder = (Folder *)entry;
4083 *result = GNOME_VFS_ERROR_IS_DIRECTORY;
4089 remove_file (Folder *folder, const char *basename)
4094 if (folder->includes_ht != NULL) {
4095 li = g_hash_table_lookup (folder->includes_ht, basename);
4097 char *name = li->data;
4098 folder->includes = g_slist_delete_link
4099 (folder->includes, li);
4100 g_hash_table_remove (folder->includes_ht, basename);
4105 if (folder->excludes == NULL) {
4106 folder->excludes = g_hash_table_new_full
4107 (g_str_hash, g_str_equal,
4108 (GDestroyNotify)g_free,
4111 s = g_strdup (basename);
4112 g_hash_table_replace (folder->excludes, s, s);
4116 add_file (Folder *folder, const char *basename)
4120 if (folder->includes_ht != NULL) {
4121 li = g_hash_table_lookup (folder->includes_ht, basename);
4126 char *str = g_strdup (basename);
4128 g_slist_prepend (folder->includes, str);
4129 if (folder->includes_ht == NULL) {
4130 folder->includes_ht =
4131 g_hash_table_new_full (g_str_hash,
4136 g_hash_table_replace (folder->includes_ht,
4137 str, folder->includes);
4139 if (folder->excludes != NULL)
4140 g_hash_table_remove (folder->excludes, basename);
4143 typedef struct _FileHandle FileHandle;
4144 struct _FileHandle {
4146 GnomeVFSMethodHandle *handle;
4149 gboolean is_directory_file;
4153 make_handle (GnomeVFSMethodHandle **method_handle,
4154 GnomeVFSMethodHandle *file_handle,
4157 gboolean is_directory_file,
4160 if (file_handle != NULL) {
4161 FileHandle *handle = g_new0 (FileHandle, 1);
4163 handle->info = info;
4164 handle->handle = file_handle;
4165 handle->entry = entry_ref (entry);
4166 handle->is_directory_file = is_directory_file;
4167 handle->write = write;
4169 *method_handle = (GnomeVFSMethodHandle *) handle;
4171 *method_handle = NULL;
4176 whack_handle (FileHandle *handle)
4178 entry_unref (handle->entry);
4179 handle->entry = NULL;
4181 handle->handle = NULL;
4182 handle->info = NULL;
4187 static GnomeVFSResult
4188 do_open (GnomeVFSMethod *method,
4189 GnomeVFSMethodHandle **method_handle,
4191 GnomeVFSOpenMode mode,
4192 GnomeVFSContext *context)
4194 GnomeVFSURI *file_uri;
4195 GnomeVFSResult result = GNOME_VFS_OK;
4198 gboolean is_directory_file;
4199 GnomeVFSMethodHandle *file_handle = NULL;
4202 VFOLDER_URI_PARSE (uri, &vuri);
4204 /* These can't be very nice FILE names */
4205 if (vuri.file == NULL ||
4207 return GNOME_VFS_ERROR_INVALID_URI;
4209 info = get_vfolder_info (vuri.scheme, &result, context);
4213 if (mode & GNOME_VFS_OPEN_WRITE &&
4214 (info->read_only || vuri.is_all_scheme))
4215 return GNOME_VFS_ERROR_READ_ONLY;
4217 G_LOCK (vfolder_lock);
4218 file_uri = desktop_uri_to_file_uri (info,
4222 NULL /* the_folder */,
4223 mode & GNOME_VFS_OPEN_WRITE,
4227 if (file_uri == NULL) {
4228 G_UNLOCK (vfolder_lock);
4232 result = (* parent_method->open) (parent_method,
4238 if (result == GNOME_VFS_ERROR_CANCELLED) {
4239 G_UNLOCK (vfolder_lock);
4240 gnome_vfs_uri_unref (file_uri);
4244 make_handle (method_handle,
4249 mode & GNOME_VFS_OPEN_WRITE);
4251 gnome_vfs_uri_unref (file_uri);
4254 vfolder_info_write_user (info);
4257 G_UNLOCK (vfolder_lock);
4263 remove_from_all_except (Folder *root,
4269 if (root != except) {
4270 remove_file (root, name);
4271 if (root->up_to_date) {
4272 for (li = root->entries; li != NULL; li = li->next) {
4273 Entry *entry = li->data;
4274 if (strcmp (name, entry->name) == 0) {
4277 (root->entries, li);
4284 for (li = root->subfolders; li != NULL; li = li->next) {
4285 Folder *subfolder = li->data;
4287 remove_from_all_except (subfolder, name, except);
4291 static GnomeVFSResult
4292 do_create (GnomeVFSMethod *method,
4293 GnomeVFSMethodHandle **method_handle,
4295 GnomeVFSOpenMode mode,
4298 GnomeVFSContext *context)
4300 GnomeVFSResult result = GNOME_VFS_OK;
4301 GnomeVFSMethodHandle *file_handle;
4302 GnomeVFSURI *file_uri;
4311 VFOLDER_URI_PARSE (uri, &vuri);
4313 /* These can't be very nice FILE names */
4314 if (vuri.file == NULL ||
4316 return GNOME_VFS_ERROR_INVALID_URI;
4318 if ( ! check_ext (vuri.file, ".desktop") &&
4319 ! strcmp (vuri.file, ".directory") == 0) {
4320 return GNOME_VFS_ERROR_INVALID_URI;
4323 /* all scheme is read only */
4324 if (vuri.is_all_scheme)
4325 return GNOME_VFS_ERROR_READ_ONLY;
4327 info = get_vfolder_info (vuri.scheme, &result, context);
4331 if (info->user_filename == NULL ||
4333 return GNOME_VFS_ERROR_READ_ONLY;
4335 parent = resolve_folder (info, vuri.path,
4336 TRUE /* ignore_basename */,
4341 if (parent->read_only)
4342 return GNOME_VFS_ERROR_READ_ONLY;
4344 if (strcmp (vuri.file, ".directory") == 0) {
4347 G_LOCK (vfolder_lock);
4351 desktop_file = get_directory_file_unlocked (info, parent);
4352 if (desktop_file != NULL) {
4353 g_free (desktop_file);
4354 G_UNLOCK (vfolder_lock);
4355 return GNOME_VFS_ERROR_FILE_EXISTS;
4359 if ( ! make_dirfile_private (info, parent)) {
4360 G_UNLOCK (vfolder_lock);
4361 return GNOME_VFS_ERROR_GENERIC;
4363 fname = g_build_filename (g_get_home_dir (),
4364 parent->desktop_file,
4366 s = gnome_vfs_get_uri_from_local_path (fname);
4367 file_uri = gnome_vfs_uri_new (s);
4371 if (file_uri == NULL) {
4372 G_UNLOCK (vfolder_lock);
4373 return GNOME_VFS_ERROR_GENERIC;
4376 result = (* parent_method->create) (parent_method,
4383 gnome_vfs_uri_unref (file_uri);
4385 make_handle (method_handle,
4389 TRUE /* is_directory_file */,
4393 vfolder_info_write_user (info);
4395 G_UNLOCK (vfolder_lock);
4400 ensure_folder (info, parent,
4401 FALSE /* subfolders */,
4403 FALSE /* ignore_unallocated */);
4405 entry = find_entry (parent->entries, vuri.file);
4407 if (entry != NULL &&
4408 entry->type == ENTRY_FOLDER)
4409 return GNOME_VFS_ERROR_IS_DIRECTORY;
4411 efile = (EntryFile *)entry;
4413 if (efile != NULL) {
4415 return GNOME_VFS_ERROR_FILE_EXISTS;
4417 G_LOCK (vfolder_lock);
4418 if ( ! make_file_private (info, efile)) {
4419 G_UNLOCK (vfolder_lock);
4420 return GNOME_VFS_ERROR_GENERIC;
4423 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4424 file_uri = gnome_vfs_uri_new (s);
4427 if (file_uri == NULL) {
4428 G_UNLOCK (vfolder_lock);
4429 return GNOME_VFS_ERROR_GENERIC;
4432 result = (* parent_method->create) (parent_method,
4439 gnome_vfs_uri_unref (file_uri);
4441 make_handle (method_handle,
4445 FALSE /* is_directory_file */,
4448 G_UNLOCK (vfolder_lock);
4453 G_LOCK (vfolder_lock);
4455 li = g_hash_table_lookup (info->entries_ht, vuri.file);
4457 if (exclusive && li != NULL) {
4458 G_UNLOCK (vfolder_lock);
4459 return GNOME_VFS_ERROR_FILE_EXISTS;
4463 efile = file_new (vuri.file);
4464 vfolder_info_insert_entry (info, efile);
4465 entry_unref ((Entry *)efile);
4470 /* this will make a private name for this */
4471 if ( ! make_file_private (info, efile)) {
4472 G_UNLOCK (vfolder_lock);
4473 return GNOME_VFS_ERROR_GENERIC;
4476 add_file (parent, vuri.file);
4477 parent->sorted = FALSE;
4479 if (parent->up_to_date)
4480 parent->entries = g_slist_prepend (parent->entries, efile);
4482 /* if we created a brand new name, then we exclude it
4483 * from everywhere else to ensure overall sanity */
4485 remove_from_all_except (info->root, vuri.file, parent);
4487 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4488 file_uri = gnome_vfs_uri_new (s);
4491 result = (* parent_method->create) (parent_method,
4498 gnome_vfs_uri_unref (file_uri);
4500 make_handle (method_handle,
4504 FALSE /* is_directory_file */,
4507 vfolder_info_write_user (info);
4509 G_UNLOCK (vfolder_lock);
4514 static GnomeVFSResult
4515 do_close (GnomeVFSMethod *method,
4516 GnomeVFSMethodHandle *method_handle,
4517 GnomeVFSContext *context)
4519 GnomeVFSResult result;
4520 FileHandle *handle = (FileHandle *)method_handle;
4521 if (method_handle == (GnomeVFSMethodHandle *)method)
4522 return GNOME_VFS_OK;
4524 G_LOCK (vfolder_lock);
4526 result = (* parent_method->close) (parent_method,
4529 handle->handle = NULL;
4531 /* we reread the Categories keyword */
4532 if (handle->write &&
4533 handle->entry != NULL &&
4534 handle->entry->type == ENTRY_FILE) {
4535 EntryFile *efile = (EntryFile *)handle->entry;
4537 readitem_entry (efile->filename,
4542 set_keywords (efile, categories);
4543 g_free (categories);
4544 /* FIXME: what about OnlyShowIn */
4546 /* FIXME: check if the keywords changed, if not, do
4549 /* Perhaps a bit drastic */
4550 /* also this emits the CHANGED monitor signal */
4551 invalidate_folder_T (handle->info->root);
4553 /* the file changed monitor will happen by itself
4554 * as the underlying file is changed */
4555 } else if (handle->write &&
4556 handle->entry != NULL &&
4557 handle->entry->type == ENTRY_FOLDER &&
4558 handle->is_directory_file) {
4559 /* if we're monitoring this directory, emit the CHANGED
4560 * monitor thing, it will also emit a changed on
4561 * the file itself. It is better to emit changed
4563 emit_monitor ((Folder *)(handle->entry),
4564 GNOME_VFS_MONITOR_EVENT_CHANGED);
4567 whack_handle (handle);
4569 G_UNLOCK (vfolder_lock);
4575 fill_buffer (gpointer buffer,
4576 GnomeVFSFileSize num_bytes,
4577 GnomeVFSFileSize *bytes_read)
4581 for (i = 0; i < num_bytes; i++) {
4582 if (rand () % 32 == 0 ||
4586 buf[i] = ((rand()>>4) % 94) + 32;
4588 if (bytes_read != 0)
4592 static GnomeVFSResult
4593 do_read (GnomeVFSMethod *method,
4594 GnomeVFSMethodHandle *method_handle,
4596 GnomeVFSFileSize num_bytes,
4597 GnomeVFSFileSize *bytes_read,
4598 GnomeVFSContext *context)
4600 GnomeVFSResult result;
4601 FileHandle *handle = (FileHandle *)method_handle;
4603 if (method_handle == (GnomeVFSMethodHandle *)method) {
4604 if ((rand () >> 4) & 0x3) {
4605 fill_buffer (buffer, num_bytes, bytes_read);
4606 return GNOME_VFS_OK;
4608 return GNOME_VFS_ERROR_EOF;
4612 result = (* parent_method->read) (parent_method,
4621 static GnomeVFSResult
4622 do_write (GnomeVFSMethod *method,
4623 GnomeVFSMethodHandle *method_handle,
4624 gconstpointer buffer,
4625 GnomeVFSFileSize num_bytes,
4626 GnomeVFSFileSize *bytes_written,
4627 GnomeVFSContext *context)
4629 GnomeVFSResult result;
4630 FileHandle *handle = (FileHandle *)method_handle;
4632 if (method_handle == (GnomeVFSMethodHandle *)method)
4633 return GNOME_VFS_OK;
4635 result = (* parent_method->write) (parent_method,
4645 static GnomeVFSResult
4646 do_seek (GnomeVFSMethod *method,
4647 GnomeVFSMethodHandle *method_handle,
4648 GnomeVFSSeekPosition whence,
4649 GnomeVFSFileOffset offset,
4650 GnomeVFSContext *context)
4652 GnomeVFSResult result;
4653 FileHandle *handle = (FileHandle *)method_handle;
4655 if (method_handle == (GnomeVFSMethodHandle *)method)
4656 return GNOME_VFS_OK;
4658 result = (* parent_method->seek) (parent_method,
4666 static GnomeVFSResult
4667 do_tell (GnomeVFSMethod *method,
4668 GnomeVFSMethodHandle *method_handle,
4669 GnomeVFSFileOffset *offset_return)
4671 GnomeVFSResult result;
4672 FileHandle *handle = (FileHandle *)method_handle;
4674 result = (* parent_method->tell) (parent_method,
4682 static GnomeVFSResult
4683 do_truncate_handle (GnomeVFSMethod *method,
4684 GnomeVFSMethodHandle *method_handle,
4685 GnomeVFSFileSize where,
4686 GnomeVFSContext *context)
4688 GnomeVFSResult result;
4689 FileHandle *handle = (FileHandle *)method_handle;
4691 if (method_handle == (GnomeVFSMethodHandle *)method)
4692 return GNOME_VFS_OK;
4694 result = (* parent_method->truncate_handle) (parent_method,
4702 static GnomeVFSResult
4703 do_truncate (GnomeVFSMethod *method,
4705 GnomeVFSFileSize where,
4706 GnomeVFSContext *context)
4708 GnomeVFSURI *file_uri;
4709 GnomeVFSResult result = GNOME_VFS_OK;
4714 VFOLDER_URI_PARSE (uri, &vuri);
4716 /* These can't be very nice FILE names */
4717 if (vuri.file == NULL ||
4719 return GNOME_VFS_ERROR_INVALID_URI;
4721 if (vuri.is_all_scheme)
4722 return GNOME_VFS_ERROR_READ_ONLY;
4724 info = get_vfolder_info (vuri.scheme, &result, context);
4728 if (info->read_only)
4729 return GNOME_VFS_ERROR_READ_ONLY;
4731 G_LOCK (vfolder_lock);
4732 file_uri = desktop_uri_to_file_uri (info,
4735 NULL /* the_is_directory_file */,
4736 NULL /* the_folder */,
4737 TRUE /* privatize */,
4740 G_UNLOCK (vfolder_lock);
4742 if (file_uri == NULL)
4745 result = (* parent_method->truncate) (parent_method,
4750 gnome_vfs_uri_unref (file_uri);
4753 G_LOCK (vfolder_lock);
4754 vfolder_info_write_user (info);
4755 G_UNLOCK (vfolder_lock);
4758 if (entry->type == ENTRY_FILE) {
4759 EntryFile *efile = (EntryFile *)entry;
4761 G_LOCK (vfolder_lock);
4762 g_slist_free (efile->keywords);
4763 efile->keywords = NULL;
4764 G_UNLOCK (vfolder_lock);
4767 /* Perhaps a bit drastic, but oh well */
4768 invalidate_folder (info->root);
4773 typedef struct _DirHandle DirHandle;
4778 GnomeVFSFileInfoOptions options;
4780 /* List of Entries */
4785 static GnomeVFSResult
4786 do_open_directory (GnomeVFSMethod *method,
4787 GnomeVFSMethodHandle **method_handle,
4789 GnomeVFSFileInfoOptions options,
4790 GnomeVFSContext *context)
4792 GnomeVFSResult result = GNOME_VFS_OK;
4799 VFOLDER_URI_PARSE (uri, &vuri);
4801 info = get_vfolder_info (vuri.scheme, &result, context);
4805 /* In the all- scheme just list all filenames */
4806 if (vuri.is_all_scheme) {
4807 if (any_subdir (vuri.path))
4808 return GNOME_VFS_ERROR_NOT_FOUND;
4810 dh = g_new0 (DirHandle, 1);
4812 dh->options = options;
4815 G_LOCK (vfolder_lock);
4816 dh->list = g_slist_copy (info->entries);
4817 g_slist_foreach (dh->list, (GFunc)entry_ref, NULL);
4818 dh->current = dh->list;
4819 G_UNLOCK (vfolder_lock);
4821 *method_handle = (GnomeVFSMethodHandle*) dh;
4822 return GNOME_VFS_OK;
4825 folder = resolve_folder (info, vuri.path,
4826 FALSE /* ignore_basename */,
4831 /* Make sure we have the entries and sorted here */
4832 ensure_folder_sort (info, folder);
4834 dh = g_new0 (DirHandle, 1);
4836 dh->options = options;
4838 G_LOCK (vfolder_lock);
4839 dh->folder = (Folder *)entry_ref ((Entry *)folder);
4840 dh->list = g_slist_copy (folder->entries);
4841 g_slist_foreach (folder->entries, (GFunc)entry_ref, NULL);
4842 G_UNLOCK (vfolder_lock);
4844 desktop_file = get_directory_file (info, folder);
4845 if (desktop_file != NULL) {
4846 EntryFile *efile = file_new (".directory");
4847 dh->list = g_slist_prepend (dh->list, efile);
4848 g_free (desktop_file);
4851 dh->current = dh->list;
4853 *method_handle = (GnomeVFSMethodHandle*) dh;
4855 return GNOME_VFS_OK;
4858 static GnomeVFSResult
4859 do_close_directory (GnomeVFSMethod *method,
4860 GnomeVFSMethodHandle *method_handle,
4861 GnomeVFSContext *context)
4865 dh = (DirHandle*) method_handle;
4867 G_LOCK (vfolder_lock);
4869 g_slist_foreach (dh->list, (GFunc)entry_unref, NULL);
4870 g_slist_free (dh->list);
4875 if (dh->folder != NULL)
4876 entry_unref ((Entry *)dh->folder);
4883 G_UNLOCK (vfolder_lock);
4885 return GNOME_VFS_OK;
4888 static GnomeVFSResult
4889 do_read_directory (GnomeVFSMethod *method,
4890 GnomeVFSMethodHandle *method_handle,
4891 GnomeVFSFileInfo *file_info,
4892 GnomeVFSContext *context)
4896 GnomeVFSFileInfoOptions options;
4898 dh = (DirHandle*) method_handle;
4900 read_directory_again:
4902 if (dh->current == NULL) {
4903 return GNOME_VFS_ERROR_EOF;
4906 entry = dh->current->data;
4907 dh->current = dh->current->next;
4909 options = dh->options;
4911 if (entry->type == ENTRY_FILE &&
4912 ((EntryFile *)entry)->filename != NULL) {
4913 EntryFile *efile = (EntryFile *)entry;
4914 char *furi = gnome_vfs_get_uri_from_local_path (efile->filename);
4915 GnomeVFSURI *uri = gnome_vfs_uri_new (furi);
4917 /* we always get mime-type by forcing it below */
4918 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
4919 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
4921 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4923 /* Get the file info for this */
4924 (* parent_method->get_file_info) (parent_method,
4930 /* we ignore errors from this since the file_info just
4931 * won't be filled completely if there's an error, that's all */
4933 g_free (file_info->mime_type);
4934 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4935 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4937 /* Now we wipe those fields we don't support */
4938 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
4940 gnome_vfs_uri_unref (uri);
4942 } else if (entry->type == ENTRY_FILE) {
4943 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4945 file_info->name = g_strdup (entry->name);
4946 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4948 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
4949 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4951 /* FIXME: Is this correct? isn't there an xdg mime type? */
4952 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4953 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4955 /* FIXME: get some ctime/mtime */
4956 } else /* ENTRY_FOLDER */ {
4957 Folder *folder = (Folder *)entry;
4959 /* Skip empty folders if they have
4961 if (folder->dont_show_if_empty) {
4962 /* Make sure we have the entries */
4963 ensure_folder (dh->info, folder,
4964 FALSE /* subfolders */,
4966 FALSE /* ignore_unallocated */);
4968 if (folder->entries == NULL) {
4969 /* start this function over on the
4971 goto read_directory_again;
4975 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4977 file_info->name = g_strdup (entry->name);
4978 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4980 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
4981 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4983 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
4984 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4986 file_info->ctime = dh->info->modification_time;
4987 file_info->mtime = dh->info->modification_time;
4988 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
4989 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
4992 return GNOME_VFS_OK;
4995 static GnomeVFSResult
4996 do_get_file_info (GnomeVFSMethod *method,
4998 GnomeVFSFileInfo *file_info,
4999 GnomeVFSFileInfoOptions options,
5000 GnomeVFSContext *context)
5002 GnomeVFSURI *file_uri;
5003 GnomeVFSResult result = GNOME_VFS_OK;
5008 VFOLDER_URI_PARSE (uri, &vuri);
5010 info = get_vfolder_info (vuri.scheme, &result, context);
5014 G_LOCK (vfolder_lock);
5015 file_uri = desktop_uri_to_file_uri (info,
5017 NULL /* the_entry */,
5018 NULL /* the_is_directory_file */,
5020 FALSE /* privatize */,
5023 G_UNLOCK (vfolder_lock);
5025 if (file_uri == NULL &&
5026 result != GNOME_VFS_ERROR_IS_DIRECTORY)
5029 if (file_uri != NULL) {
5030 /* we always get mime-type by forcing it below */
5031 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5032 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5034 result = (* parent_method->get_file_info) (parent_method,
5040 g_free (file_info->mime_type);
5041 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5042 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5044 /* Now we wipe those fields we don't support */
5045 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5047 gnome_vfs_uri_unref (file_uri);
5050 } else if (folder != NULL) {
5051 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
5053 file_info->name = g_strdup (folder->entry.name);
5054 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
5056 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
5057 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
5059 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
5060 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5062 file_info->ctime = info->modification_time;
5063 file_info->mtime = info->modification_time;
5064 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
5065 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
5067 return GNOME_VFS_OK;
5069 return GNOME_VFS_ERROR_NOT_FOUND;
5073 static GnomeVFSResult
5074 do_get_file_info_from_handle (GnomeVFSMethod *method,
5075 GnomeVFSMethodHandle *method_handle,
5076 GnomeVFSFileInfo *file_info,
5077 GnomeVFSFileInfoOptions options,
5078 GnomeVFSContext *context)
5080 GnomeVFSResult result;
5081 FileHandle *handle = (FileHandle *)method_handle;
5083 if (method_handle == (GnomeVFSMethodHandle *)method) {
5084 g_free (file_info->mime_type);
5085 file_info->mime_type = g_strdup ("text/plain");
5086 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5087 return GNOME_VFS_OK;
5090 /* we always get mime-type by forcing it below */
5091 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5092 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5094 result = (* parent_method->get_file_info_from_handle) (parent_method,
5100 /* any file is of the .desktop type */
5101 g_free (file_info->mime_type);
5102 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5103 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5105 /* Now we wipe those fields we don't support */
5106 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5113 do_is_local (GnomeVFSMethod *method,
5114 const GnomeVFSURI *uri)
5120 try_free_folder_monitors_create_unlocked (VFolderInfo *info,
5125 list = g_slist_copy (info->free_folder_monitors);
5127 for (li = list; li != NULL; li = li->next) {
5128 FileMonitorHandle *handle = li->data;
5131 GnomeVFSResult result;
5133 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
5134 * have lots of free monitors */
5136 VFOLDER_URI_PARSE (handle->uri, &vuri);
5138 f = resolve_folder (info,
5140 FALSE /* ignore_basename */,
5147 info->free_folder_monitors =
5148 g_slist_remove (info->free_folder_monitors, handle);
5149 ((Entry *)folder)->monitors =
5150 g_slist_prepend (((Entry *)folder)->monitors, handle);
5152 handle->exists = TRUE;
5153 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
5155 GNOME_VFS_MONITOR_EVENT_CREATED);
5160 static GnomeVFSResult
5161 do_make_directory (GnomeVFSMethod *method,
5164 GnomeVFSContext *context)
5166 GnomeVFSResult result = GNOME_VFS_OK;
5168 Folder *parent, *folder;
5171 VFOLDER_URI_PARSE (uri, &vuri);
5173 if (vuri.is_all_scheme)
5174 return GNOME_VFS_ERROR_READ_ONLY;
5176 info = get_vfolder_info (vuri.scheme, &result, context);
5180 if (info->user_filename == NULL ||
5182 return GNOME_VFS_ERROR_READ_ONLY;
5184 parent = resolve_folder (info, vuri.path,
5185 TRUE /* ignore_basename */,
5189 else if (parent->read_only)
5190 return GNOME_VFS_ERROR_READ_ONLY;
5192 G_LOCK (vfolder_lock);
5194 folder = (Folder *)find_entry (parent->subfolders,
5196 if (folder != NULL) {
5197 G_UNLOCK (vfolder_lock);
5198 return GNOME_VFS_ERROR_FILE_EXISTS;
5201 folder = folder_new (vuri.file);
5202 parent->subfolders = g_slist_append (parent->subfolders, folder);
5203 folder->parent = parent;
5204 parent->up_to_date = FALSE;
5206 try_free_folder_monitors_create_unlocked (info, folder);
5208 /* parent changed */
5209 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5211 vfolder_info_write_user (info);
5212 G_UNLOCK (vfolder_lock);
5214 return GNOME_VFS_OK;
5217 static GnomeVFSResult
5218 do_remove_directory (GnomeVFSMethod *method,
5220 GnomeVFSContext *context)
5222 GnomeVFSResult result = GNOME_VFS_OK;
5227 VFOLDER_URI_PARSE (uri, &vuri);
5229 if (vuri.is_all_scheme)
5230 return GNOME_VFS_ERROR_READ_ONLY;
5232 info = get_vfolder_info (vuri.scheme, &result, context);
5236 if (info->user_filename == NULL ||
5238 return GNOME_VFS_ERROR_READ_ONLY;
5240 G_LOCK (vfolder_lock);
5242 folder = resolve_folder (info, vuri.path,
5243 FALSE /* ignore_basename */,
5245 if (folder == NULL) {
5246 G_UNLOCK (vfolder_lock);
5250 if (folder->read_only ||
5251 (folder->parent != NULL &&
5252 folder->parent->read_only)) {
5253 G_UNLOCK (vfolder_lock);
5254 return GNOME_VFS_ERROR_READ_ONLY;
5257 /* don't make removing directories easy */
5258 if (folder->desktop_file != NULL) {
5259 G_UNLOCK (vfolder_lock);
5260 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5263 /* Make sure we have the entries */
5264 ensure_folder_unlocked (info, folder,
5265 FALSE /* subfolders */,
5267 FALSE /* ignore_unallocated */);
5269 /* don't make removing directories easy */
5270 if (folder->entries != NULL) {
5271 G_UNLOCK (vfolder_lock);
5272 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5275 emit_and_delete_monitor (info, folder);
5277 if (folder->only_unallocated) {
5278 GSList *li = g_slist_find (info->unallocated_folders,
5281 info->unallocated_folders = g_slist_delete_link
5282 (info->unallocated_folders, li);
5283 entry_unref ((Entry *)folder);
5287 if (folder == info->root) {
5289 entry_unref ((Entry *)folder);
5290 info->root = folder_new ("Root");
5292 Folder *parent = folder->parent;
5294 g_assert (parent != NULL);
5296 parent->subfolders =
5297 g_slist_remove (parent->subfolders, folder);
5299 parent->up_to_date = FALSE;
5301 entry_unref ((Entry *)folder);
5303 /* parent changed */
5304 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5307 vfolder_info_write_user (info);
5309 G_UNLOCK (vfolder_lock);
5311 return GNOME_VFS_OK;
5314 /* a fairly evil function that does the whole move bit by copy and
5316 static GnomeVFSResult
5317 long_move (GnomeVFSMethod *method,
5318 VFolderURI *old_vuri,
5319 VFolderURI *new_vuri,
5320 gboolean force_replace,
5321 GnomeVFSContext *context)
5323 GnomeVFSResult result;
5324 GnomeVFSMethodHandle *handle;
5325 GnomeVFSURI *file_uri;
5332 info = get_vfolder_info (old_vuri->scheme, &result, context);
5336 G_LOCK (vfolder_lock);
5337 file_uri = desktop_uri_to_file_uri (info,
5339 NULL /* the_entry */,
5340 NULL /* the_is_directory_file */,
5341 NULL /* the_folder */,
5342 FALSE /* privatize */,
5345 G_UNLOCK (vfolder_lock);
5347 if (file_uri == NULL)
5350 path = gnome_vfs_uri_get_path (file_uri);
5352 gnome_vfs_uri_unref (file_uri);
5353 return GNOME_VFS_ERROR_INVALID_URI;
5356 fd = open (path, O_RDONLY);
5358 gnome_vfs_uri_unref (file_uri);
5359 return gnome_vfs_result_from_errno ();
5362 gnome_vfs_uri_unref (file_uri);
5364 info->inhibit_write++;
5366 result = method->create (method,
5369 GNOME_VFS_OPEN_WRITE,
5370 force_replace /* exclusive */,
5373 if (result != GNOME_VFS_OK) {
5375 info->inhibit_write--;
5379 while ((bytes = read (fd, buf, BUFSIZ)) > 0) {
5380 GnomeVFSFileSize bytes_written = 0;
5381 result = method->write (method,
5387 if (result == GNOME_VFS_OK &&
5388 bytes_written != bytes)
5389 result = GNOME_VFS_ERROR_NO_SPACE;
5390 if (result != GNOME_VFS_OK) {
5392 method->close (method, handle, context);
5393 /* FIXME: is this completely correct ? */
5394 method->unlink (method,
5397 G_LOCK (vfolder_lock);
5398 info->inhibit_write--;
5399 vfolder_info_write_user (info);
5400 G_UNLOCK (vfolder_lock);
5407 result = method->close (method, handle, context);
5408 if (result != GNOME_VFS_OK) {
5409 G_LOCK (vfolder_lock);
5410 info->inhibit_write--;
5411 vfolder_info_write_user (info);
5412 G_UNLOCK (vfolder_lock);
5416 result = method->unlink (method, old_vuri->uri, context);
5418 G_LOCK (vfolder_lock);
5419 info->inhibit_write--;
5420 vfolder_info_write_user (info);
5421 G_UNLOCK (vfolder_lock);
5426 static GnomeVFSResult
5427 move_directory_file (VFolderInfo *info,
5431 if (old_folder->desktop_file == NULL)
5432 return GNOME_VFS_ERROR_NOT_FOUND;
5434 /* "move" the desktop file */
5435 g_free (new_folder->desktop_file);
5436 new_folder->desktop_file = old_folder->desktop_file;
5437 old_folder->desktop_file = NULL;
5439 /* is this too drastic, it will requery the folder? */
5440 new_folder->up_to_date = FALSE;
5441 old_folder->up_to_date = FALSE;
5443 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5444 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5446 vfolder_info_write_user (info);
5448 return GNOME_VFS_OK;
5452 is_sub (Folder *master, Folder *sub)
5456 for (li = master->subfolders; li != NULL; li = li->next) {
5457 Folder *subfolder = li->data;
5459 if (subfolder == sub ||
5460 is_sub (subfolder, sub))
5467 static GnomeVFSResult
5468 move_folder (VFolderInfo *info,
5469 Folder *old_folder, Entry *old_entry,
5470 Folder *new_folder, Entry *new_entry)
5472 Folder *source = (Folder *)old_entry;
5475 if (new_entry != NULL &&
5476 new_entry->type != ENTRY_FOLDER)
5477 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
5479 if (new_entry != NULL) {
5480 target = (Folder *)new_entry;
5482 target = new_folder;
5485 /* move to where we are, yay, we're done :) */
5486 if (source->parent == target)
5487 return GNOME_VFS_OK;
5489 if (source == target ||
5490 is_sub (source, target))
5491 return GNOME_VFS_ERROR_LOOP;
5493 /* this will never happen, but we're paranoid */
5494 if (source->parent == NULL)
5495 return GNOME_VFS_ERROR_LOOP;
5497 source->parent->subfolders = g_slist_remove (source->parent->subfolders,
5499 target->subfolders = g_slist_append (target->subfolders,
5502 source->parent = target;
5504 source->up_to_date = FALSE;
5505 target->up_to_date = FALSE;
5507 emit_monitor (source, GNOME_VFS_MONITOR_EVENT_CHANGED);
5508 emit_monitor (target, GNOME_VFS_MONITOR_EVENT_CHANGED);
5510 vfolder_info_write_user (info);
5512 return GNOME_VFS_OK;
5515 static GnomeVFSResult
5516 do_move (GnomeVFSMethod *method,
5517 GnomeVFSURI *old_uri,
5518 GnomeVFSURI *new_uri,
5519 gboolean force_replace,
5520 GnomeVFSContext *context)
5522 GnomeVFSResult result = GNOME_VFS_OK;
5524 Folder *old_folder, *new_folder;
5525 Entry *old_entry, *new_entry;
5526 gboolean old_is_directory_file, new_is_directory_file;
5527 VFolderURI old_vuri, new_vuri;
5529 VFOLDER_URI_PARSE (old_uri, &old_vuri);
5530 VFOLDER_URI_PARSE (new_uri, &new_vuri);
5532 if (old_vuri.file == NULL)
5533 return GNOME_VFS_ERROR_INVALID_URI;
5535 if (old_vuri.is_all_scheme)
5536 return GNOME_VFS_ERROR_READ_ONLY;
5538 if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
5539 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
5541 info = get_vfolder_info (old_vuri.scheme, &result, context);
5545 if (info->read_only)
5546 return GNOME_VFS_ERROR_READ_ONLY;
5548 old_entry = get_entry (&old_vuri,
5550 &old_is_directory_file,
5553 if (old_entry == NULL)
5556 if (old_folder != NULL && old_folder->read_only)
5557 return GNOME_VFS_ERROR_READ_ONLY;
5559 new_entry = get_entry (&new_vuri,
5561 &new_is_directory_file,
5564 if (new_entry == NULL && new_folder == NULL)
5567 if (new_folder != NULL && new_folder->read_only)
5568 return GNOME_VFS_ERROR_READ_ONLY;
5570 if (new_is_directory_file != old_is_directory_file) {
5571 /* this will do another set of lookups
5572 * perhaps this can be done in a nicer way,
5573 * but is this the common case? I don't think so */
5574 return long_move (method, &old_vuri, &new_vuri,
5575 force_replace, context);
5578 if (new_is_directory_file) {
5579 g_assert (old_entry != NULL);
5580 g_assert (new_entry != NULL);
5581 G_LOCK (vfolder_lock);
5582 result = move_directory_file (info,
5583 (Folder *)old_entry,
5584 (Folder *)new_entry);
5585 G_UNLOCK (vfolder_lock);
5589 if (old_entry->type == ENTRY_FOLDER) {
5590 G_LOCK (vfolder_lock);
5591 result = move_folder (info,
5592 old_folder, old_entry,
5593 new_folder, new_entry);
5594 G_UNLOCK (vfolder_lock);
5598 /* move into self, just whack the old one */
5599 if (old_entry == new_entry) {
5601 if (new_folder == old_folder)
5602 return GNOME_VFS_OK;
5604 if ( ! force_replace)
5605 return GNOME_VFS_ERROR_FILE_EXISTS;
5607 G_LOCK (vfolder_lock);
5609 remove_file (old_folder, old_vuri.file);
5611 old_folder->entries = g_slist_remove (old_folder->entries,
5613 entry_unref (old_entry);
5615 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5617 vfolder_info_write_user (info);
5619 G_UNLOCK (vfolder_lock);
5621 return GNOME_VFS_OK;
5624 /* this is a simple move */
5625 if (new_entry == NULL ||
5626 new_entry->type == ENTRY_FOLDER) {
5627 if (new_entry != NULL) {
5628 new_folder = (Folder *)new_entry;
5630 /* a file and a totally different one */
5631 if (strcmp (new_vuri.file, old_entry->name) != 0) {
5632 /* yay, a long move */
5633 /* this will do another set of lookups
5634 * perhaps this can be done in a nicer way,
5635 * but is this the common case? I don't think
5637 return long_move (method, &old_vuri, &new_vuri,
5638 force_replace, context);
5643 if (new_folder == old_folder)
5644 return GNOME_VFS_OK;
5646 G_LOCK (vfolder_lock);
5648 remove_file (old_folder, old_entry->name);
5649 add_file (new_folder, old_entry->name);
5651 new_folder->entries = g_slist_prepend (new_folder->entries,
5653 entry_ref (old_entry);
5654 new_folder->sorted = FALSE;
5656 old_folder->entries = g_slist_remove (old_folder->entries,
5658 entry_unref (old_entry);
5660 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5661 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5663 vfolder_info_write_user (info);
5665 G_UNLOCK (vfolder_lock);
5667 return GNOME_VFS_OK;
5670 /* do we EVER get here? */
5672 /* this will do another set of lookups
5673 * perhaps this can be done in a nicer way,
5674 * but is this the common case? I don't think so */
5675 return long_move (method, &old_vuri, &new_vuri,
5676 force_replace, context);
5679 static GnomeVFSResult
5680 do_unlink (GnomeVFSMethod *method,
5682 GnomeVFSContext *context)
5684 GnomeVFSResult result = GNOME_VFS_OK;
5687 gboolean is_directory_file;
5692 VFOLDER_URI_PARSE (uri, &vuri);
5694 if (vuri.file == NULL)
5695 return GNOME_VFS_ERROR_INVALID_URI;
5697 if (vuri.is_all_scheme == TRUE)
5698 return GNOME_VFS_ERROR_READ_ONLY;
5700 info = get_vfolder_info (vuri.scheme, &result, context);
5703 else if (info->read_only)
5704 return GNOME_VFS_ERROR_READ_ONLY;
5706 entry = get_entry (&vuri,
5712 else if (the_folder != NULL &&
5713 the_folder->read_only)
5714 return GNOME_VFS_ERROR_READ_ONLY;
5716 if (entry->type == ENTRY_FOLDER &&
5717 is_directory_file) {
5718 Folder *folder = (Folder *)entry;
5720 if (folder->desktop_file == NULL)
5721 return GNOME_VFS_ERROR_NOT_FOUND;
5723 G_LOCK (vfolder_lock);
5725 g_free (folder->desktop_file);
5726 folder->desktop_file = NULL;
5728 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5730 vfolder_info_write_user (info);
5732 G_UNLOCK (vfolder_lock);
5734 return GNOME_VFS_OK;
5735 } else if (entry->type == ENTRY_FOLDER) {
5736 return GNOME_VFS_ERROR_IS_DIRECTORY;
5737 } else if (the_folder == NULL) {
5738 return GNOME_VFS_ERROR_NOT_FOUND;
5741 G_LOCK (vfolder_lock);
5743 the_folder->entries = g_slist_remove (the_folder->entries,
5745 entry_unref (entry);
5747 remove_file (the_folder, vuri.file);
5749 emit_monitor (the_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5751 /* evil, we must remove this from the unallocated folders as well
5752 * so that it magically doesn't appear there. But it's not so simple.
5753 * We only want to remove it if it isn't in that folder already. */
5754 for (li = info->unallocated_folders;
5757 Folder *folder = li->data;
5760 /* This is actually really evil since ensuring
5761 * an unallocated folder clears all other unallocated
5762 * folders in it's wake. I'm not sure it's worth
5763 * optimizing however */
5764 ensure_folder_unlocked (info, folder,
5765 FALSE /* subfolders */,
5767 FALSE /* ignore_unallocated */);
5768 l = g_slist_find (folder->entries, entry);
5770 remove_file (folder, vuri.file);
5774 emit_file_deleted_monitor (info, entry, the_folder);
5776 /* FIXME: if this was a user file and this is the only
5777 * reference to it, unlink it. */
5779 vfolder_info_write_user (info);
5781 G_UNLOCK (vfolder_lock);
5783 return GNOME_VFS_OK;
5786 static GnomeVFSResult
5787 do_check_same_fs (GnomeVFSMethod *method,
5788 GnomeVFSURI *source_uri,
5789 GnomeVFSURI *target_uri,
5790 gboolean *same_fs_return,
5791 GnomeVFSContext *context)
5793 VFolderURI source_vuri, target_vuri;
5795 *same_fs_return = FALSE;
5797 VFOLDER_URI_PARSE (source_uri, &source_vuri);
5798 VFOLDER_URI_PARSE (target_uri, &target_vuri);
5800 if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
5801 source_vuri.is_all_scheme != target_vuri.is_all_scheme)
5802 *same_fs_return = FALSE;
5804 *same_fs_return = TRUE;
5806 return GNOME_VFS_OK;
5809 static GnomeVFSResult
5810 do_set_file_info (GnomeVFSMethod *method,
5812 const GnomeVFSFileInfo *info,
5813 GnomeVFSSetFileInfoMask mask,
5814 GnomeVFSContext *context)
5818 VFOLDER_URI_PARSE (uri, &vuri);
5820 if (vuri.file == NULL)
5821 return GNOME_VFS_ERROR_INVALID_URI;
5823 if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
5824 GnomeVFSResult result = GNOME_VFS_OK;
5825 char *dirname = gnome_vfs_uri_extract_dirname (uri);
5826 GnomeVFSURI *new_uri = gnome_vfs_uri_dup (uri);
5828 G_LOCK (vfolder_lock);
5829 g_free (new_uri->text);
5830 new_uri->text = g_build_path ("/", dirname, info->name, NULL);
5831 G_UNLOCK (vfolder_lock);
5833 result = do_move (method,
5836 FALSE /* force_replace */,
5840 gnome_vfs_uri_unref (new_uri);
5843 /* We don't support setting any of this other permission,
5844 * times and all that voodoo */
5845 return GNOME_VFS_ERROR_NOT_SUPPORTED;
5849 static GnomeVFSResult
5850 do_monitor_add (GnomeVFSMethod *method,
5851 GnomeVFSMethodHandle **method_handle_return,
5853 GnomeVFSMonitorType monitor_type)
5857 GnomeVFSResult result;
5860 GnomeVFSURI *file_uri;
5861 FileMonitorHandle *handle;
5862 gboolean is_directory_file;
5864 VFOLDER_URI_PARSE (uri, &vuri);
5866 info = get_vfolder_info (vuri.scheme, &result, NULL);
5870 if (monitor_type == GNOME_VFS_MONITOR_DIRECTORY) {
5871 G_LOCK (vfolder_lock);
5873 folder = resolve_folder (info,
5875 FALSE /* ignore_basename */,
5879 handle = g_new0 (FileMonitorHandle, 1);
5880 handle->refcount = 2;
5881 handle->uri = gnome_vfs_uri_dup (uri);
5882 handle->dir_monitor = TRUE;
5883 handle->handle = NULL;
5884 handle->filename = NULL;
5886 if (folder == NULL) {
5887 handle->exists = FALSE;
5888 info->free_folder_monitors =
5889 g_slist_prepend (info->free_folder_monitors,
5892 handle->exists = TRUE;
5893 ((Entry *)folder)->monitors =
5894 g_slist_prepend (((Entry *)folder)->monitors,
5898 info->folder_monitors =
5899 g_slist_prepend (info->folder_monitors, handle);
5901 G_UNLOCK (vfolder_lock);
5903 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5905 return GNOME_VFS_OK;
5907 /* These can't be very nice FILE names */
5908 if (vuri.file == NULL ||
5910 return GNOME_VFS_ERROR_INVALID_URI;
5912 G_LOCK (vfolder_lock);
5913 file_uri = desktop_uri_to_file_uri (info,
5917 NULL /* the_folder */,
5922 handle = g_new0 (FileMonitorHandle, 1);
5923 handle->refcount = 2;
5924 handle->uri = gnome_vfs_uri_dup (uri);
5925 handle->dir_monitor = FALSE;
5926 handle->handle = NULL;
5927 handle->filename = g_strdup (vuri.file);
5928 handle->is_directory_file = is_directory_file;
5930 info->file_monitors =
5931 g_slist_prepend (info->file_monitors, handle);
5934 if (file_uri == NULL) {
5935 handle->exists = FALSE;
5936 info->free_file_monitors =
5937 g_slist_prepend (info->free_file_monitors,
5940 char *uri_string = gnome_vfs_uri_to_string (file_uri, 0);
5941 handle->exists = TRUE;
5942 gnome_vfs_monitor_add (&(handle->handle),
5944 GNOME_VFS_MONITOR_FILE,
5947 g_free (uri_string);
5949 entry->monitors = g_slist_prepend (entry->monitors,
5951 gnome_vfs_uri_unref (file_uri);
5954 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5956 G_UNLOCK (vfolder_lock);
5958 return GNOME_VFS_OK;
5962 static GnomeVFSResult
5963 do_monitor_cancel (GnomeVFSMethod *method,
5964 GnomeVFSMethodHandle *method_handle)
5966 FileMonitorHandle *handle;
5969 GnomeVFSResult result;
5973 handle = (FileMonitorHandle *)method_handle;
5975 /* FIXME: is this correct? */
5976 if (method_handle == NULL)
5977 return GNOME_VFS_OK;
5979 VFOLDER_URI_PARSE (handle->uri, &vuri);
5981 info = get_vfolder_info (vuri.scheme, &result, NULL);
5985 if (handle->dir_monitor) {
5986 G_LOCK (vfolder_lock);
5988 folder = resolve_folder (info,
5990 FALSE /* ignore_basename */,
5994 for (li = info->folder_monitors; li != NULL; li = li->next) {
5995 FileMonitorHandle *h = li->data;
5998 info->folder_monitors = g_slist_delete_link
5999 (info->folder_monitors, li);
6000 file_monitor_handle_unref_unlocked (h);
6004 if (folder == NULL) {
6005 for (li = info->free_folder_monitors;
6008 FileMonitorHandle *h = li->data;
6011 info->free_folder_monitors = g_slist_delete_link
6012 (info->free_folder_monitors, li);
6013 file_monitor_handle_unref_unlocked (h);
6017 for (li = ((Entry *)folder)->monitors;
6020 FileMonitorHandle *h = li->data;
6023 ((Entry *)folder)->monitors =
6025 (((Entry *)folder)->monitors, li);
6026 file_monitor_handle_unref_unlocked (h);
6031 G_UNLOCK (vfolder_lock);
6033 return GNOME_VFS_OK;
6035 G_LOCK (vfolder_lock);
6037 for (li = info->file_monitors; li != NULL; li = li->next) {
6038 FileMonitorHandle *h = li->data;
6041 info->file_monitors = g_slist_delete_link
6042 (info->file_monitors, li);
6043 file_monitor_handle_unref_unlocked (h);
6047 for (li = info->free_file_monitors;
6050 FileMonitorHandle *h = li->data;
6053 info->free_file_monitors = g_slist_delete_link
6054 (info->free_file_monitors, li);
6055 file_monitor_handle_unref_unlocked (h);
6059 for (li = info->entries; li != NULL; li = li->next) {
6060 Entry *e = li->data;
6061 GSList *link = g_slist_find (e->monitors, handle);
6066 e->monitors = g_slist_delete_link (e->monitors, link);
6068 file_monitor_handle_unref_unlocked (handle);
6072 G_UNLOCK (vfolder_lock);
6074 /* Note: last unref of our monitor will cancel the
6075 * underlying handle */
6077 return GNOME_VFS_OK;
6082 /* gnome-vfs bureaucracy */
6084 static GnomeVFSMethod method = {
6085 sizeof (GnomeVFSMethod),
6098 do_get_file_info_from_handle,
6101 do_remove_directory,
6107 NULL /* find_directory */,
6108 NULL /* create_symbolic_link */,
6114 vfs_module_init (const char *method_name,
6117 parent_method = gnome_vfs_method_get ("file");
6119 if (parent_method == NULL) {
6120 g_error ("Could not find 'file' method for gnome-vfs");
6128 vfs_module_shutdown (GnomeVFSMethod *method)
6133 g_hash_table_destroy (infos);