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) {
943 li = folder->subfolders;
944 for (li = folder->subfolders; li != NULL; li = li->next) {
945 Folder *f = li->data;
946 /* always dont_show_if_empty */
947 if (f->entries != NULL) {
948 entry_ref_alloc (&f->entry);
949 subfolders = g_slist_prepend (subfolders, f);
952 subfolders = g_slist_reverse (subfolders);
953 folder->entries = g_slist_concat (subfolders, folder->entries);
956 /* Exclude excludes */
957 if (folder->excludes != NULL) {
959 GSList *entries = folder->entries;
960 folder->entries = NULL;
961 for (li = entries; li != NULL; li = li->next) {
962 Entry *entry = li->data;
963 if (g_hash_table_lookup (folder->excludes, entry->name) == NULL)
964 folder->entries = g_slist_prepend (folder->entries, entry);
966 entry_unref_dealloc (entry);
969 g_slist_free (entries);
971 /* to preserve the Folders then everything else order */
972 folder->entries = g_slist_reverse (folder->entries);
975 folder->up_to_date = TRUE;
977 folder->sorted = FALSE;
981 ensure_folder (VFolderInfo *info,
985 gboolean ignore_unallocated)
987 G_LOCK (vfolder_lock);
988 ensure_folder_unlocked (info, folder, subfolders, except, ignore_unallocated);
989 G_UNLOCK (vfolder_lock);
993 get_directory_file_unlocked (VFolderInfo *info, Folder *folder)
997 /* FIXME: cache dir_files */
999 if (folder->desktop_file == NULL)
1002 if (folder->desktop_file[0] == G_DIR_SEPARATOR)
1003 return g_strdup (folder->desktop_file);
1005 /* Now try the user directory */
1006 if (info->user_desktop_dir != NULL) {
1007 filename = g_build_filename (info->user_desktop_dir,
1008 folder->desktop_file,
1010 if (access (filename, F_OK) == 0) {
1017 filename = g_build_filename (info->desktop_dir, folder->desktop_file, NULL);
1018 if (access (filename, F_OK) == 0) {
1027 get_directory_file (VFolderInfo *info, Folder *folder)
1031 G_LOCK (vfolder_lock);
1032 ret = get_directory_file_unlocked (info, folder);
1033 G_UNLOCK (vfolder_lock);
1039 get_sort_order (VFolderInfo *info, Folder *folder)
1047 filename = get_directory_file_unlocked (info, folder);
1048 if (filename == NULL)
1052 readitem_entry (filename,
1062 parsed = g_strsplit (order, ":", -1);
1067 for (i = 0; parsed[i] != NULL; i++) {
1068 char *word = parsed[i];
1072 if (word[0] == '\0') {
1076 list = g_slist_prepend (list, word);
1078 /* we've stolen all strings from it */
1081 return g_slist_reverse (list);
1084 /* get entries in folder */
1086 ensure_folder_sort (VFolderInfo *info, Folder *folder)
1088 GSList *li, *sort_order;
1090 GHashTable *entry_hash;
1092 ensure_folder (info, folder,
1093 TRUE /* subfolders */,
1095 FALSE /* ignore_unallocated */);
1099 G_LOCK (vfolder_lock);
1101 sort_order = get_sort_order (info, folder);
1102 if (sort_order == NULL) {
1103 folder->sorted = TRUE;
1104 G_UNLOCK (vfolder_lock);
1108 entries = folder->entries;
1109 folder->entries = NULL;
1111 entry_hash = g_hash_table_new (g_str_hash, g_str_equal);
1112 for (li = entries; li != NULL; li = li->next) {
1113 Entry *entry = li->data;
1114 g_hash_table_insert (entry_hash, entry->name, li);
1117 for (li = sort_order; li != NULL; li = li->next) {
1118 char *word = li->data;
1122 /* we kill the words here */
1125 entry_list = g_hash_table_lookup (entry_hash, word);
1128 if (entry_list == NULL)
1131 entry = entry_list->data;
1133 entries = g_slist_delete_link (entries, entry_list);
1135 folder->entries = g_slist_prepend (folder->entries,
1139 /* put on those that weren't mentioned in the sort */
1140 for (li = entries; li != NULL; li = li->next) {
1141 Entry *entry = li->data;
1143 folder->entries = g_slist_prepend (folder->entries,
1147 g_hash_table_destroy (entry_hash);
1148 g_slist_free (entries);
1149 g_slist_free (sort_order);
1151 folder->sorted = TRUE;
1153 G_UNLOCK (vfolder_lock);
1157 file_new (const char *name)
1159 EntryFile *efile = g_new0 (EntryFile, 1);
1161 efile->entry.type = ENTRY_FILE;
1162 efile->entry.name = g_strdup (name);
1163 efile->entry.refcount = 1;
1169 folder_new (const char *name)
1171 Folder *folder = g_new0 (Folder, 1);
1173 folder->entry.type = ENTRY_FOLDER;
1174 folder->entry.name = g_strdup (name);
1175 folder->entry.refcount = 1;
1176 folder->read_only = TRUE;
1177 folder->dont_show_if_empty = TRUE;
1183 query_new (int type)
1187 if (type == QUERY_KEYWORD)
1188 query = (Query *)g_new0 (QueryKeyword, 1);
1189 else if (type == QUERY_FILENAME)
1190 query = (Query *)g_new0 (QueryFilename, 1);
1192 query = g_new0 (Query, 1);
1200 query_destroy (Query *query)
1205 if (query->type == QUERY_FILENAME) {
1206 QueryFilename *qfile = (QueryFilename *)query;
1207 g_free (qfile->filename);
1208 qfile->filename = NULL;
1209 } else if (query->type == QUERY_OR ||
1210 query->type == QUERY_AND) {
1211 g_slist_foreach (query->queries, (GFunc)query_destroy, NULL);
1212 g_slist_free (query->queries);
1213 query->queries = NULL;
1220 add_folder_monitor_unlocked (VFolderInfo *info,
1221 FileMonitorHandle *handle)
1224 GnomeVFSResult result;
1227 VFOLDER_URI_PARSE (handle->uri, &vuri);
1229 file_monitor_handle_ref_unlocked (handle);
1231 info->folder_monitors =
1232 g_slist_prepend (info->folder_monitors, handle);
1234 folder = resolve_folder (info,
1236 FALSE /* ignore_basename */,
1240 if (folder == NULL) {
1241 file_monitor_handle_ref_unlocked (handle);
1243 info->free_folder_monitors =
1244 g_slist_prepend (info->free_folder_monitors, handle);
1246 if (handle->exists) {
1247 handle->exists = FALSE;
1248 gnome_vfs_monitor_callback
1249 ((GnomeVFSMethodHandle *)handle,
1251 GNOME_VFS_MONITOR_EVENT_DELETED);
1254 file_monitor_handle_ref_unlocked (handle);
1256 ((Entry *)folder)->monitors =
1257 g_slist_prepend (((Entry *)folder)->monitors, handle);
1259 if ( ! handle->exists) {
1260 handle->exists = TRUE;
1261 gnome_vfs_monitor_callback
1262 ((GnomeVFSMethodHandle *)handle,
1264 GNOME_VFS_MONITOR_EVENT_CREATED);
1271 invalidate_folder_T (Folder *folder)
1273 folder->up_to_date = FALSE;
1275 invalidate_folder_subfolders (folder, TRUE);
1279 invalidate_folder (Folder *folder)
1281 G_LOCK (vfolder_lock);
1282 folder->up_to_date = FALSE;
1283 G_UNLOCK (vfolder_lock);
1285 invalidate_folder_subfolders (folder, FALSE);
1289 invalidate_folder_subfolders (Folder *folder,
1290 gboolean lock_taken)
1294 for (li = folder->subfolders; li != NULL; li = li->next) {
1295 Folder *subfolder = li->data;
1298 invalidate_folder (subfolder);
1300 invalidate_folder_T (subfolder);
1303 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
1306 /* FIXME: this is UGLY!, we need to figure out when the file
1307 * got finished changing! */
1309 reread_timeout (gpointer data)
1311 VFolderInfo *info = data;
1312 gboolean force_read_items = info->file_monitors != NULL;
1313 vfolder_info_reload (info, NULL, NULL, force_read_items);
1318 queue_reread_in (VFolderInfo *info, int msec)
1320 G_LOCK (vfolder_lock);
1321 if (info->reread_queue != 0)
1322 g_source_remove (info->reread_queue);
1323 info->reread_queue = g_timeout_add (msec, reread_timeout, info);
1324 G_UNLOCK (vfolder_lock);
1328 vfolder_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1329 const gchar *monitor_uri,
1330 const gchar *info_uri,
1331 GnomeVFSMonitorEventType event_type,
1334 /* FIXME: implement */
1338 vfolder_user_desktop_dir_monitor (GnomeVFSMonitorHandle *handle,
1339 const gchar *monitor_uri,
1340 const gchar *info_uri,
1341 GnomeVFSMonitorEventType event_type,
1344 /* FIXME: implement */
1348 vfolder_filename_monitor (GnomeVFSMonitorHandle *handle,
1349 const gchar *monitor_uri,
1350 const gchar *info_uri,
1351 GnomeVFSMonitorEventType event_type,
1354 VFolderInfo *info = user_data;
1356 if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1357 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1358 ! info->user_file_active) {
1359 queue_reread_in (info, 200);
1360 } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1361 ! info->user_file_active) {
1362 /* FIXME: is this correct? I mean now
1363 * there probably isn't ANY vfolder file, so we
1364 * init to default values really. I have no clue what's
1366 vfolder_info_reload (info, NULL, NULL,
1367 TRUE /* force read items */);
1372 vfolder_user_filename_monitor (GnomeVFSMonitorHandle *handle,
1373 const gchar *monitor_uri,
1374 const gchar *info_uri,
1375 GnomeVFSMonitorEventType event_type,
1378 VFolderInfo *info = user_data;
1380 if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1381 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1382 info->user_file_active) {
1385 /* see if this was really our own change */
1386 if (info->user_filename_last_write == time (NULL))
1388 /* anal retentive */
1389 if (stat (info->user_filename, &s) == 0 &&
1390 info->user_filename_last_write == s.st_ctime)
1393 queue_reread_in (info, 200);
1394 } else if ((event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1395 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) &&
1396 ! info->user_file_active) {
1397 queue_reread_in (info, 200);
1398 } else if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED &&
1399 info->user_file_active) {
1400 gboolean force_read_items = info->file_monitors != NULL;
1401 vfolder_info_reload (info, NULL, NULL, force_read_items);
1406 item_dir_monitor (GnomeVFSMonitorHandle *handle,
1407 const gchar *monitor_uri,
1408 const gchar *info_uri,
1409 GnomeVFSMonitorEventType event_type,
1412 VFolderInfo *info = user_data;
1414 if (event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1415 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
1416 /* first invalidate all folders */
1417 invalidate_folder (info->root);
1418 /* second invalidate all entries */
1419 info->entries_valid = FALSE;
1421 if (info->file_monitors != NULL) {
1422 GnomeVFSResult result;
1425 /* Whack all monitors here! */
1426 for (li = info->file_monitors;
1429 FileMonitorHandle *h = li->data;
1430 if (h->handle != NULL)
1431 gnome_vfs_monitor_cancel (h->handle);
1435 if (vfolder_info_read_items (info, &result, NULL)) {
1436 info->entries_valid = TRUE;
1443 setup_dir_monitor (VFolderInfo *info, const char *dir, gboolean subdirs,
1444 GnomeVFSResult *result,
1445 GnomeVFSContext *context)
1447 GnomeVFSMonitorHandle *handle;
1452 uri = gnome_vfs_get_uri_from_local_path (dir);
1454 if (gnome_vfs_monitor_add (&handle,
1456 GNOME_VFS_MONITOR_DIRECTORY,
1458 info) != GNOME_VFS_OK) {
1459 StatLoc *sl = bake_statloc (dir, time (NULL));
1461 info->stat_dirs = g_slist_prepend (info->stat_dirs, sl);
1467 if (gnome_vfs_context_check_cancellation (context)) {
1468 gnome_vfs_monitor_cancel (handle);
1469 *result = GNOME_VFS_ERROR_CANCELLED;
1473 info->item_dir_monitors =
1474 g_slist_prepend (info->item_dir_monitors, handle);
1483 while ((de = readdir (dh)) != NULL) {
1486 if (gnome_vfs_context_check_cancellation (context)) {
1487 *result = GNOME_VFS_ERROR_CANCELLED;
1492 if (de->d_name[0] == '.')
1495 full_path = g_build_filename (dir, de->d_name, NULL);
1496 if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
1497 if ( ! setup_dir_monitor (info, full_path,
1513 monitor_setup (VFolderInfo *info,
1514 gboolean setup_filenames,
1515 gboolean setup_itemdirs,
1516 gboolean setup_desktop_dirs,
1517 GnomeVFSResult *result,
1518 GnomeVFSContext *context)
1523 if (setup_filenames) {
1524 uri = gnome_vfs_get_uri_from_local_path
1527 if (gnome_vfs_monitor_add (&info->filename_monitor,
1529 GNOME_VFS_MONITOR_FILE,
1530 vfolder_filename_monitor,
1531 info) != GNOME_VFS_OK) {
1532 info->filename_monitor = NULL;
1533 info->filename_statloc = bake_statloc (info->filename,
1538 if (setup_filenames &&
1539 info->user_filename != NULL) {
1540 uri = gnome_vfs_get_uri_from_local_path
1541 (info->user_filename);
1542 if (gnome_vfs_monitor_add (&info->user_filename_monitor,
1544 GNOME_VFS_MONITOR_FILE,
1545 vfolder_user_filename_monitor,
1546 info) != GNOME_VFS_OK) {
1547 info->user_filename_monitor = NULL;
1548 info->user_filename_statloc =
1549 bake_statloc (info->user_filename,
1556 if (gnome_vfs_context_check_cancellation (context)) {
1557 *result = GNOME_VFS_ERROR_CANCELLED;
1561 if (setup_itemdirs) {
1562 for (li = info->item_dirs; li != NULL; li = li->next) {
1563 const char *dir = li->data;
1564 if ( ! setup_dir_monitor (info, dir,
1565 FALSE /* subdirs */,
1569 if (info->user_item_dir != NULL) {
1570 if ( ! setup_dir_monitor (info, info->user_item_dir,
1571 FALSE /* subdirs */,
1575 for (li = info->merge_dirs; li != NULL; li = li->next) {
1576 const char *dir = li->data;
1577 if ( ! setup_dir_monitor (info, dir,
1584 if (setup_desktop_dirs) {
1585 uri = gnome_vfs_get_uri_from_local_path
1586 (info->desktop_dir);
1588 if (gnome_vfs_monitor_add (&info->desktop_dir_monitor,
1590 GNOME_VFS_MONITOR_FILE,
1591 vfolder_desktop_dir_monitor,
1592 info) != GNOME_VFS_OK) {
1593 info->desktop_dir_monitor = NULL;
1594 info->desktop_dir_statloc =
1595 bake_statloc (info->desktop_dir,
1600 if (setup_desktop_dirs &&
1601 info->user_desktop_dir != NULL) {
1602 uri = gnome_vfs_get_uri_from_local_path
1603 (info->user_desktop_dir);
1604 if (gnome_vfs_monitor_add (&info->user_desktop_dir_monitor,
1606 GNOME_VFS_MONITOR_DIRECTORY,
1607 vfolder_user_desktop_dir_monitor,
1608 info) != GNOME_VFS_OK) {
1609 info->user_desktop_dir_monitor = NULL;
1610 info->user_desktop_dir_statloc =
1611 bake_statloc (info->user_desktop_dir,
1622 vfolder_info_init (VFolderInfo *info, const char *scheme)
1627 info->scheme = g_strdup (scheme);
1629 info->filename = g_strconcat (SYSCONFDIR, "/X11/desktop-menus/",
1632 info->user_filename = g_strconcat (g_get_home_dir (),
1633 "/" DOT_GNOME "/vfolders/",
1634 scheme, ".vfolder-info",
1636 info->desktop_dir = g_strconcat (SYSCONFDIR,
1637 "/gnome-vfs-2.0/vfolders/",
1639 info->user_desktop_dir = g_strconcat (g_get_home_dir (),
1640 "/" DOT_GNOME "/vfolders/",
1643 /* Init the desktop paths */
1645 list = g_slist_prepend (list, g_strdup ("/usr/share/applications/"));
1646 if (strcmp ("/usr/share/applications/", DATADIR "/applications/") != 0)
1647 list = g_slist_prepend (list, g_strdup (DATADIR "/applications/"));
1648 path = g_getenv ("DESKTOP_FILE_PATH");
1651 char **ppath = g_strsplit (path, ":", -1);
1652 for (i = 0; ppath[i] != NULL; i++) {
1653 const char *dir = ppath[i];
1654 list = g_slist_prepend (list, g_strdup (dir));
1658 info->item_dirs = g_slist_reverse (list);
1660 info->user_item_dir = g_strconcat (g_get_home_dir (),
1661 "/" DOT_GNOME "/vfolders/",
1665 info->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1667 info->root = folder_new ("Root");
1669 info->modification_time = time (NULL);
1673 vfolder_info_free_internals_unlocked (VFolderInfo *info)
1678 if (info->filename_monitor != NULL) {
1679 gnome_vfs_monitor_cancel (info->filename_monitor);
1680 info->filename_monitor = NULL;
1683 if (info->user_filename_monitor != NULL) {
1684 gnome_vfs_monitor_cancel (info->user_filename_monitor);
1685 info->user_filename_monitor = NULL;
1688 g_free (info->filename_statloc);
1689 info->filename_statloc = NULL;
1691 g_free (info->user_filename_statloc);
1692 info->user_filename_statloc = NULL;
1695 if (info->desktop_dir_monitor != NULL) {
1696 gnome_vfs_monitor_cancel (info->desktop_dir_monitor);
1697 info->desktop_dir_monitor = NULL;
1700 if (info->user_desktop_dir_monitor != NULL) {
1701 gnome_vfs_monitor_cancel (info->user_desktop_dir_monitor);
1702 info->user_desktop_dir_monitor = NULL;
1705 g_free (info->desktop_dir_statloc);
1706 info->desktop_dir_statloc = NULL;
1708 g_free (info->user_desktop_dir_statloc);
1709 info->user_desktop_dir_statloc = NULL;
1712 g_slist_foreach (info->item_dir_monitors,
1713 (GFunc)gnome_vfs_monitor_cancel, NULL);
1714 g_slist_free (info->item_dir_monitors);
1715 info->item_dir_monitors = NULL;
1717 g_free (info->scheme);
1718 info->scheme = NULL;
1720 g_free (info->filename);
1721 info->filename = NULL;
1723 g_free (info->user_filename);
1724 info->user_filename = NULL;
1726 g_free (info->desktop_dir);
1727 info->desktop_dir = NULL;
1729 g_free (info->user_desktop_dir);
1730 info->user_desktop_dir = NULL;
1732 g_slist_foreach (info->item_dirs, (GFunc)g_free, NULL);
1733 g_slist_free (info->item_dirs);
1734 info->item_dirs = NULL;
1736 g_free (info->user_item_dir);
1737 info->user_item_dir = NULL;
1739 g_slist_foreach (info->merge_dirs, (GFunc)g_free, NULL);
1740 g_slist_free (info->merge_dirs);
1741 info->merge_dirs = NULL;
1743 g_slist_foreach (info->entries, (GFunc)entry_unref, NULL);
1744 g_slist_free (info->entries);
1745 info->entries = NULL;
1747 if (info->entries_ht != NULL)
1748 g_hash_table_destroy (info->entries_ht);
1749 info->entries_ht = NULL;
1751 g_slist_foreach (info->unallocated_folders,
1754 g_slist_free (info->unallocated_folders);
1755 info->unallocated_folders = NULL;
1757 entry_unref ((Entry *)info->root);
1760 g_slist_foreach (info->stat_dirs, (GFunc)g_free, NULL);
1761 g_slist_free (info->stat_dirs);
1762 info->stat_dirs = NULL;
1764 g_slist_foreach (info->folder_monitors,
1765 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1766 g_slist_free (info->folder_monitors);
1767 info->folder_monitors = NULL;
1769 g_slist_foreach (info->free_folder_monitors,
1770 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1771 g_slist_free (info->free_folder_monitors);
1772 info->free_folder_monitors = NULL;
1774 g_slist_foreach (info->file_monitors,
1775 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1776 g_slist_free (info->file_monitors);
1777 info->file_monitors = NULL;
1779 g_slist_foreach (info->free_file_monitors,
1780 (GFunc)file_monitor_handle_unref_unlocked, NULL);
1781 g_slist_free (info->free_file_monitors);
1782 info->free_file_monitors = NULL;
1784 if (info->reread_queue != 0)
1785 g_source_remove (info->reread_queue);
1786 info->reread_queue = 0;
1790 vfolder_info_free_internals (VFolderInfo *info)
1792 G_LOCK (vfolder_lock);
1793 vfolder_info_free_internals_unlocked (info);
1794 G_UNLOCK (vfolder_lock);
1798 vfolder_info_destroy (VFolderInfo *info)
1800 vfolder_info_free_internals (info);
1805 single_query_read (xmlNode *qnode)
1810 if (qnode->type != XML_ELEMENT_NODE ||
1811 qnode->name == NULL)
1816 if (g_ascii_strcasecmp (qnode->name, "Not") == 0 &&
1817 qnode->xmlChildrenNode != NULL) {
1820 for (iter = qnode->xmlChildrenNode;
1821 iter != NULL && query == NULL;
1823 query = single_query_read (iter);
1824 if (query != NULL) {
1825 query->not = ! query->not;
1828 } else if (g_ascii_strcasecmp (qnode->name, "Keyword") == 0) {
1829 xmlChar *word = xmlNodeGetContent (qnode);
1831 query = query_new (QUERY_KEYWORD);
1832 ((QueryKeyword *)query)->keyword =
1833 g_quark_from_string (word);
1838 } else if (g_ascii_strcasecmp (qnode->name, "Filename") == 0) {
1839 xmlChar *file = xmlNodeGetContent (qnode);
1841 query = query_new (QUERY_FILENAME);
1842 ((QueryFilename *)query)->filename =
1848 } else if (g_ascii_strcasecmp (qnode->name, "And") == 0) {
1849 query = query_new (QUERY_AND);
1850 } else if (g_ascii_strcasecmp (qnode->name, "Or") == 0) {
1851 query = query_new (QUERY_OR);
1853 /* We don't understand */
1857 /* This must be OR or AND */
1858 g_assert (query != NULL);
1860 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1861 Query *new_query = single_query_read (node);
1863 if (new_query != NULL)
1864 query->queries = g_slist_prepend
1865 (query->queries, new_query);
1868 query->queries = g_slist_reverse (query->queries);
1874 add_or_set_query (Query **query, Query *new_query)
1876 if (*query == NULL) {
1879 Query *old_query = *query;
1880 *query = query_new (QUERY_OR);
1882 g_slist_append ((*query)->queries, old_query);
1884 g_slist_append ((*query)->queries, new_query);
1889 query_read (xmlNode *qnode)
1896 for (node = qnode->xmlChildrenNode; node != NULL; node = node->next) {
1897 if (node->type != XML_ELEMENT_NODE ||
1901 if (g_ascii_strcasecmp (node->name, "Not") == 0 &&
1902 node->xmlChildrenNode != NULL) {
1904 Query *new_query = NULL;
1906 for (iter = node->xmlChildrenNode;
1907 iter != NULL && new_query == NULL;
1909 new_query = single_query_read (iter);
1910 if (new_query != NULL) {
1911 new_query->not = ! new_query->not;
1912 add_or_set_query (&query, new_query);
1915 Query *new_query = single_query_read (node);
1916 if (new_query != NULL)
1917 add_or_set_query (&query, new_query);
1925 folder_read (VFolderInfo *info, xmlNode *fnode)
1930 folder = folder_new (NULL);
1932 for (node = fnode->xmlChildrenNode; node != NULL; node = node->next) {
1933 if (node->type != XML_ELEMENT_NODE ||
1937 if (g_ascii_strcasecmp (node->name, "Name") == 0) {
1938 xmlChar *name = xmlNodeGetContent (node);
1940 g_free (folder->entry.name);
1941 folder->entry.name = g_strdup (name);
1944 } else if (g_ascii_strcasecmp (node->name, "Desktop") == 0) {
1945 xmlChar *desktop = xmlNodeGetContent (node);
1946 if (desktop != NULL) {
1947 g_free (folder->desktop_file);
1948 folder->desktop_file = g_strdup (desktop);
1951 } else if (g_ascii_strcasecmp (node->name, "Include") == 0) {
1952 xmlChar *file = xmlNodeGetContent (node);
1955 char *str = g_strdup (file);
1956 folder->includes = g_slist_prepend
1957 (folder->includes, str);
1958 if (folder->includes_ht == NULL) {
1959 folder->includes_ht =
1960 g_hash_table_new_full
1966 li = g_hash_table_lookup (folder->includes_ht,
1970 /* Note: this will NOT change folder->includes
1972 folder->includes = g_slist_delete_link
1973 (folder->includes, li);
1975 g_hash_table_replace (folder->includes_ht,
1976 file, folder->includes);
1979 } else if (g_ascii_strcasecmp (node->name, "Exclude") == 0) {
1980 xmlChar *file = xmlNodeGetContent (node);
1983 if (folder->excludes == NULL) {
1984 folder->excludes = g_hash_table_new_full
1987 (GDestroyNotify)g_free,
1990 s = g_strdup (file);
1991 g_hash_table_replace (folder->excludes, s, s);
1994 } else if (g_ascii_strcasecmp (node->name, "Query") == 0) {
1997 query = query_read (node);
1999 if (query != NULL) {
2000 if (folder->query != NULL)
2001 query_destroy (folder->query);
2002 folder->query = query;
2004 } else if (g_ascii_strcasecmp (node->name, "OnlyUnallocated") == 0) {
2005 info->unallocated_folders =
2006 g_slist_prepend (info->unallocated_folders,
2007 (Folder *)entry_ref ((Entry *)folder));
2008 folder->only_unallocated = TRUE;
2009 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
2010 Folder *new_folder = folder_read (info, node);
2011 if (new_folder != NULL) {
2012 folder->subfolders =
2013 g_slist_append (folder->subfolders,
2015 new_folder->parent = folder;
2017 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2018 folder->read_only = TRUE;
2019 } else if (g_ascii_strcasecmp (node->name,
2020 "DontShowIfEmpty") == 0) {
2021 folder->dont_show_if_empty = TRUE;
2025 /* Name is required */
2026 if (folder->entry.name == NULL) {
2027 entry_unref ((Entry *)folder);
2031 folder->includes = g_slist_reverse (folder->includes);
2037 subst_home (const char *dir)
2040 return g_strconcat (g_get_home_dir (),
2044 return g_strdup (dir);
2047 /* FORMAT looks like:
2049 * <!-- Merge dirs optional -->
2050 * <MergeDir>/etc/X11/applnk</MergeDir>
2051 * <!-- Only specify if it should override standard location -->
2052 * <ItemDir>/usr/share/applications</ItemDir>
2053 * <!-- This is where the .directories are -->
2054 * <DesktopDir>/etc/X11/gnome/vfolders</DesktopDir>
2055 * <!-- Root folder -->
2059 * <Include>important.desktop</Include>
2061 * <!-- Other folders -->
2063 * <Name>SomeFolder</Name>
2066 * <Name>Test_Folder</Name>
2067 * <!-- could also be absolute -->
2068 * <Desktop>Test_Folder.directory</Desktop>
2072 * <Keyword>Application</Keyword>
2073 * <Keyword>Game</Keyword>
2075 * <Keyword>Clock</Keyword>
2078 * <Include>somefile.desktop</Include>
2079 * <Include>someotherfile.desktop</Include>
2080 * <Exclude>yetanother.desktop</Exclude>
2087 vfolder_info_read_info (VFolderInfo *info,
2088 GnomeVFSResult *result,
2089 GnomeVFSContext *context)
2093 gboolean got_a_vfolder_dir = FALSE;
2096 if (info->user_filename != NULL &&
2097 access (info->user_filename, F_OK) == 0) {
2098 doc = xmlParseFile (info->user_filename);
2100 info->user_file_active = TRUE;
2103 access (info->filename, F_OK) == 0)
2104 doc = xmlParseFile (info->filename);
2106 if (gnome_vfs_context_check_cancellation (context)) {
2108 *result = GNOME_VFS_ERROR_CANCELLED;
2113 || doc->xmlRootNode == NULL
2114 || doc->xmlRootNode->name == NULL
2115 || g_ascii_strcasecmp (doc->xmlRootNode->name, "VFolderInfo") != 0) {
2117 return TRUE; /* FIXME: really, shouldn't we error out? */
2120 for (node = doc->xmlRootNode->xmlChildrenNode; node != NULL; node = node->next) {
2121 if (node->type != XML_ELEMENT_NODE ||
2125 if (gnome_vfs_context_check_cancellation (context)) {
2127 *result = GNOME_VFS_ERROR_CANCELLED;
2131 if (g_ascii_strcasecmp (node->name, "MergeDir") == 0) {
2132 xmlChar *dir = xmlNodeGetContent (node);
2134 info->merge_dirs = g_slist_append (info->merge_dirs,
2138 } else if (g_ascii_strcasecmp (node->name, "ItemDir") == 0) {
2139 xmlChar *dir = xmlNodeGetContent (node);
2141 if ( ! got_a_vfolder_dir) {
2142 g_slist_foreach (info->item_dirs,
2143 (GFunc)g_free, NULL);
2144 g_slist_free (info->item_dirs);
2145 info->item_dirs = NULL;
2147 got_a_vfolder_dir = TRUE;
2148 info->item_dirs = g_slist_append (info->item_dirs,
2152 } else if (g_ascii_strcasecmp (node->name, "UserItemDir") == 0) {
2153 xmlChar *dir = xmlNodeGetContent (node);
2155 g_free (info->user_item_dir);
2156 info->user_item_dir = subst_home (dir);
2159 } else if (g_ascii_strcasecmp (node->name, "DesktopDir") == 0) {
2160 xmlChar *dir = xmlNodeGetContent (node);
2162 g_free (info->desktop_dir);
2163 info->desktop_dir = g_strdup (dir);
2166 } else if (g_ascii_strcasecmp (node->name, "UserDesktopDir") == 0) {
2167 xmlChar *dir = xmlNodeGetContent (node);
2169 g_free (info->user_desktop_dir);
2170 info->user_desktop_dir = subst_home (dir);
2173 } else if (g_ascii_strcasecmp (node->name, "Folder") == 0) {
2174 Folder *folder = folder_read (info, node);
2175 if (folder != NULL) {
2176 if (info->root != NULL)
2177 entry_unref ((Entry *)info->root);
2178 info->root = folder;
2180 } else if (g_ascii_strcasecmp (node->name, "ReadOnly") == 0) {
2181 info->read_only = TRUE;
2191 add_xml_tree_from_query (xmlNode *parent, Query *query)
2193 xmlNode *real_parent;
2196 real_parent = xmlNewChild (parent /* parent */,
2199 NULL /* content */);
2201 real_parent = parent;
2203 if (query->type == QUERY_KEYWORD) {
2204 QueryKeyword *qkeyword = (QueryKeyword *)query;
2205 const char *string = g_quark_to_string (qkeyword->keyword);
2207 xmlNewChild (real_parent /* parent */,
2209 "Keyword" /* name */,
2210 string /* content */);
2211 } else if (query->type == QUERY_FILENAME) {
2212 QueryFilename *qfilename = (QueryFilename *)query;
2214 xmlNewChild (real_parent /* parent */,
2216 "Filename" /* name */,
2217 qfilename->filename /* content */);
2218 } else if (query->type == QUERY_OR ||
2219 query->type == QUERY_AND) {
2224 if (query->type == QUERY_OR)
2226 else /* QUERY_AND */
2229 node = xmlNewChild (real_parent /* parent */,
2232 NULL /* content */);
2234 for (li = query->queries; li != NULL; li = li->next) {
2235 Query *subquery = li->data;
2236 add_xml_tree_from_query (node, subquery);
2239 g_assert_not_reached ();
2244 add_excludes_to_xml (gpointer key, gpointer value, gpointer user_data)
2246 const char *filename = key;
2247 xmlNode *folder_node = user_data;
2249 xmlNewChild (folder_node /* parent */,
2251 "Exclude" /* name */,
2252 filename /* content */);
2256 add_xml_tree_from_folder (xmlNode *parent, Folder *folder)
2259 xmlNode *folder_node;
2262 folder_node = xmlNewChild (parent /* parent */,
2264 "Folder" /* name */,
2265 NULL /* content */);
2267 xmlNewChild (folder_node /* parent */,
2270 folder->entry.name /* content */);
2272 if (folder->desktop_file != NULL) {
2273 xmlNewChild (folder_node /* parent */,
2275 "Desktop" /* name */,
2276 folder->desktop_file /* content */);
2279 if (folder->read_only)
2280 xmlNewChild (folder_node /* parent */,
2282 "ReadOnly" /* name */,
2283 NULL /* content */);
2284 if (folder->dont_show_if_empty)
2285 xmlNewChild (folder_node /* parent */,
2287 "DontShowIfEmpty" /* name */,
2288 NULL /* content */);
2289 if (folder->only_unallocated)
2290 xmlNewChild (folder_node /* parent */,
2292 "OnlyUnallocated" /* name */,
2293 NULL /* content */);
2295 for (li = folder->subfolders; li != NULL; li = li->next) {
2296 Folder *subfolder = li->data;
2297 add_xml_tree_from_folder (folder_node, subfolder);
2300 for (li = folder->includes; li != NULL; li = li->next) {
2301 const char *include = li->data;
2302 xmlNewChild (folder_node /* parent */,
2304 "Include" /* name */,
2305 include /* content */);
2308 if (folder->excludes) {
2309 g_hash_table_foreach (folder->excludes,
2310 add_excludes_to_xml,
2314 if (folder->query != NULL) {
2315 xmlNode *query_node;
2316 query_node = xmlNewChild (folder_node /* parent */,
2319 NULL /* content */);
2321 add_xml_tree_from_query (query_node, folder->query);
2326 xml_tree_from_vfolder (VFolderInfo *info)
2332 doc = xmlNewDoc ("1.0");
2334 topnode = xmlNewDocNode (doc /* doc */,
2336 "VFolderInfo" /* name */,
2337 NULL /* content */);
2338 doc->xmlRootNode = topnode;
2340 for (li = info->merge_dirs; li != NULL; li = li->next) {
2341 const char *merge_dir = li->data;
2342 xmlNewChild (topnode /* parent */,
2344 "MergeDir" /* name */,
2345 merge_dir /* content */);
2348 for (li = info->item_dirs; li != NULL; li = li->next) {
2349 const char *item_dir = li->data;
2350 xmlNewChild (topnode /* parent */,
2352 "ItemDir" /* name */,
2353 item_dir /* content */);
2356 if (info->user_item_dir != NULL) {
2357 xmlNewChild (topnode /* parent */,
2359 "UserItemDir" /* name */,
2360 info->user_item_dir /* content */);
2363 if (info->desktop_dir != NULL) {
2364 xmlNewChild (topnode /* parent */,
2366 "DesktopDir" /* name */,
2367 info->desktop_dir /* content */);
2370 if (info->user_desktop_dir != NULL) {
2371 xmlNewChild (topnode /* parent */,
2373 "UserDesktopDir" /* name */,
2374 info->user_desktop_dir /* content */);
2377 if (info->root != NULL)
2378 add_xml_tree_from_folder (topnode, info->root);
2383 /* FIXME: what to do about errors */
2385 vfolder_info_write_user (VFolderInfo *info)
2389 if (info->inhibit_write > 0)
2392 if (info->user_filename == NULL)
2395 doc = xml_tree_from_vfolder (info);
2399 /* FIXME: errors, anyone? */
2400 ensure_dir (info->user_filename,
2401 TRUE /* ignore_basename */);
2403 xmlSaveFormatFile (info->user_filename, doc, TRUE /* format */);
2404 /* not as good as a stat, but cheaper ... hmmm what is
2405 * the likelyhood of this not being the same as ctime */
2406 info->user_filename_last_write = time (NULL);
2410 info->user_file_active = TRUE;
2411 info->dirty = FALSE;
2413 info->modification_time = time (NULL);
2416 /* An EVIL function for quick reading of .desktop files,
2417 * only reads in one or two keys, but that's ALL we need */
2419 readitem_entry (const char *filename,
2427 int keylen1, keylen2;
2430 if (result2 != NULL)
2433 fp = fopen (filename, "r");
2438 keylen1 = strlen (key1);
2440 keylen2 = strlen (key2);
2444 /* This is slightly wrong, it should only look
2445 * at the correct section */
2446 while (fgets (buf, sizeof (buf), fp) != NULL) {
2450 char **result = NULL;
2452 /* check if it's one of the keys */
2453 if (strncmp (buf, key1, keylen1) == 0) {
2456 } else if (keylen2 >= 0 &&
2457 strncmp (buf, key2, keylen2) == 0) {
2466 /* still not our key */
2467 if (!(*p == '=' || *p == ' ')) {
2472 while (*p == ' ' || *p == '=');
2474 /* get rid of trailing \n */
2476 if (p[len-1] == '\n' ||
2480 *result = g_strdup (p);
2482 if (*result1 != NULL &&
2483 (result2 == NULL || *result2 != NULL))
2491 vfolder_info_insert_entry (VFolderInfo *info, EntryFile *efile)
2495 entry_ref ((Entry *)efile);
2497 entry_list = g_hash_table_lookup (info->entries_ht, efile->entry.name);
2499 info->entries = g_slist_prepend (info->entries, efile);
2500 /* The hash table contains the GSList pointer */
2501 g_hash_table_replace (info->entries_ht, efile->entry.name,
2504 if (entry_list != NULL) {
2505 Entry *entry = entry_list->data;
2506 info->entries = g_slist_delete_link (info->entries,
2508 entry_unref (entry);
2513 set_keywords (EntryFile *efile, const char *keywords)
2515 if (keywords != NULL) {
2517 char **parsed = g_strsplit (keywords, ";", -1);
2518 for (i = 0; parsed[i] != NULL; i++) {
2520 const char *word = parsed[i];
2521 /* ignore empties (including end of list) */
2522 if (word[0] == '\0')
2524 quark = g_quark_from_string (word);
2525 efile->keywords = g_slist_prepend
2527 GINT_TO_POINTER (quark));
2529 g_strfreev (parsed);
2534 make_entry_file (const char *dir, const char *name)
2542 filename = g_build_filename (dir, name, NULL);
2544 readitem_entry (filename,
2550 if (only_show_in != NULL) {
2551 gboolean show = FALSE;
2552 char **parsed = g_strsplit (only_show_in, ";", -1);
2553 for (i = 0; parsed[i] != NULL; i++) {
2554 if (strcmp (parsed[i], "GNOME") == 0) {
2559 g_strfreev (parsed);
2562 g_free (only_show_in);
2563 g_free (categories);
2568 efile = file_new (name);
2569 efile->filename = filename;
2571 set_keywords (efile, categories);
2573 g_free (only_show_in);
2574 g_free (categories);
2580 vfolder_info_read_items_from (VFolderInfo *info,
2581 const char *item_dir,
2583 GnomeVFSResult *result,
2584 GnomeVFSContext *context)
2589 dir = opendir (item_dir);
2593 while ((de = readdir (dir)) != NULL) {
2596 if (gnome_vfs_context_check_cancellation (context)) {
2598 *result = GNOME_VFS_ERROR_CANCELLED;
2602 /* files MUST be called .desktop */
2603 if (de->d_name[0] == '.' ||
2604 ! check_ext (de->d_name, ".desktop"))
2607 efile = make_entry_file (item_dir, de->d_name);
2611 efile->per_user = per_user;
2613 vfolder_info_insert_entry (info, efile);
2614 entry_unref ((Entry *)efile);
2623 vfolder_info_read_items_merge (VFolderInfo *info,
2624 const char *merge_dir,
2626 GQuark inherited_keyword,
2627 GnomeVFSResult *result,
2628 GnomeVFSContext *context)
2632 GQuark extra_keyword;
2636 gboolean pass_down_extra_keyword = TRUE;
2638 dir = opendir (merge_dir);
2642 Application = g_quark_from_static_string ("Application");
2643 Merged = g_quark_from_static_string ("Merged");
2645 /* FIXME: this should be a hash or something */
2647 if (subdir == NULL) {
2648 extra_keyword = g_quark_from_static_string ("Core");
2649 pass_down_extra_keyword = FALSE;
2650 } else if (g_ascii_strcasecmp (subdir, "Development") == 0)
2651 extra_keyword = g_quark_from_static_string ("Development");
2652 else if (g_ascii_strcasecmp (subdir, "Editors") == 0)
2653 extra_keyword = g_quark_from_static_string ("TextEditor");
2654 else if (g_ascii_strcasecmp (subdir, "Games") == 0)
2655 extra_keyword = g_quark_from_static_string ("Game");
2656 else if (g_ascii_strcasecmp (subdir, "Graphics") == 0)
2657 extra_keyword = g_quark_from_static_string ("Graphics");
2658 else if (g_ascii_strcasecmp (subdir, "Internet") == 0)
2659 extra_keyword = g_quark_from_static_string ("Network");
2660 else if (g_ascii_strcasecmp (subdir, "Multimedia") == 0)
2661 extra_keyword = g_quark_from_static_string ("AudioVideo");
2662 else if (g_ascii_strcasecmp (subdir, "Office") == 0)
2663 extra_keyword = g_quark_from_static_string ("Office");
2664 else if (g_ascii_strcasecmp (subdir, "Settings") == 0)
2665 extra_keyword = g_quark_from_static_string ("Settings");
2666 else if (g_ascii_strcasecmp (subdir, "System") == 0)
2667 extra_keyword = g_quark_from_static_string ("System");
2668 else if (g_ascii_strcasecmp (subdir, "Utilities") == 0)
2669 extra_keyword = g_quark_from_static_string ("Utility");
2671 while ((de = readdir (dir)) != NULL) {
2674 if (gnome_vfs_context_check_cancellation (context)) {
2676 *result = GNOME_VFS_ERROR_CANCELLED;
2681 if (de->d_name[0] == '.')
2684 /* files MUST be called .desktop, so
2685 * treat all others as dirs. If we're wrong,
2686 * the open will fail, which is ok */
2687 if ( ! check_ext (de->d_name, ".desktop")) {
2688 /* if this is a directory recurse */
2689 char *fullname = g_build_filename (merge_dir, de->d_name, NULL);
2690 if ((pass_down_extra_keyword == TRUE) && (extra_keyword != 0)) {
2691 inheritance = extra_keyword;
2693 inheritance = inherited_keyword;
2696 if ( ! vfolder_info_read_items_merge (info,
2709 /* FIXME: add some keywords about some known apps
2710 * like gimp and whatnot, perhaps take these from the vfolder
2711 * file or some such */
2713 efile = make_entry_file (merge_dir, de->d_name);
2717 /* If no keywords set, then add the standard ones */
2718 if (efile->keywords == NULL) {
2719 efile->keywords = g_slist_prepend
2721 GINT_TO_POINTER (Application));
2723 efile->keywords = g_slist_prepend
2725 GINT_TO_POINTER (Merged));
2727 if (inherited_keyword != 0) {
2728 efile->keywords = g_slist_prepend
2730 GINT_TO_POINTER (inherited_keyword));
2733 if (extra_keyword != 0) {
2734 efile->keywords = g_slist_prepend
2736 GINT_TO_POINTER (extra_keyword));
2738 efile->implicit_keywords = TRUE;
2741 vfolder_info_insert_entry (info, efile);
2742 entry_unref ((Entry *)efile);
2751 find_entry (GSList *list, const char *name)
2755 for (li = list; li != NULL; li = li->next) {
2756 Entry *entry = li->data;
2757 if (strcmp (name, entry->name) == 0)
2764 file_monitor (GnomeVFSMonitorHandle *handle,
2765 const gchar *monitor_uri,
2766 const gchar *info_uri,
2767 GnomeVFSMonitorEventType event_type,
2770 FileMonitorHandle *h = user_data;
2772 /* proxy the event through if it is a changed event
2775 if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED &&
2777 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) h,
2778 h->uri, event_type);
2782 try_free_file_monitors_create_files_unlocked (VFolderInfo *info)
2786 list = g_slist_copy (info->free_file_monitors);
2788 for (li = list; li != NULL; li = li->next) {
2789 FileMonitorHandle *handle = li->data;
2791 GnomeVFSResult result;
2792 char *dirfile = NULL;
2794 if (handle->is_directory_file) {
2798 /* Evil! EVIL URI PARSING. this will eat a lot of
2799 * stack if we have lots of free monitors */
2801 VFOLDER_URI_PARSE (handle->uri, &vuri);
2803 folder = resolve_folder (info,
2805 TRUE /* ignore_basename */,
2812 dirfile = get_directory_file_unlocked (info, folder);
2813 if (dirfile == NULL)
2816 entry = (Entry *)folder;
2820 GnomeVFSResult result;
2824 /* Evil! EVIL URI PARSING. this will eat a lot of
2825 * stack if we have lots of monitors */
2827 VFOLDER_URI_PARSE (handle->uri, &vuri);
2829 f = resolve_folder (info,
2831 TRUE /* ignore_basename */,
2836 ensure_folder_unlocked (
2838 FALSE /* subfolders */,
2840 FALSE /* ignore_unallocated */);
2841 entry = find_entry (f->entries, vuri.file);
2848 info->free_file_monitors =
2849 g_slist_remove (info->free_file_monitors, handle);
2851 g_slist_prepend (entry->monitors, handle);
2853 handle->exists = TRUE;
2854 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
2856 GNOME_VFS_MONITOR_EVENT_CREATED);
2858 /* recreate a handle */
2859 if (handle->handle == NULL &&
2860 entry->type == ENTRY_FILE) {
2861 EntryFile *efile = (EntryFile *)entry;
2862 char *uri = gnome_vfs_get_uri_from_local_path
2865 gnome_vfs_monitor_add (&(handle->handle),
2867 GNOME_VFS_MONITOR_FILE,
2872 } else if (handle->handle == NULL &&
2874 char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
2876 gnome_vfs_monitor_add (&(handle->handle),
2878 GNOME_VFS_MONITOR_FILE,
2888 g_slist_free (list);
2891 static void /* unlocked */
2892 rescan_monitors (VFolderInfo *info)
2896 if (info->file_monitors == NULL)
2899 for (li = info->file_monitors; li != NULL; li = li->next) {
2900 FileMonitorHandle *h = li->data;
2901 GnomeVFSResult result;
2903 char *dirfile = NULL;
2905 /* these are handled below */
2909 if (h->is_directory_file) {
2913 /* Evil! EVIL URI PARSING. this will eat a lot of
2914 * stack if we have lots of monitors */
2916 VFOLDER_URI_PARSE (h->uri, &vuri);
2918 folder = resolve_folder (info,
2920 TRUE /* ignore_basename */,
2924 dirfile = get_directory_file_unlocked (info,
2927 if (dirfile == NULL) {
2929 gnome_vfs_monitor_callback
2930 ((GnomeVFSMethodHandle *)h,
2932 GNOME_VFS_MONITOR_EVENT_DELETED);
2933 info->free_file_monitors = g_slist_prepend
2934 (info->free_file_monitors, h);
2935 file_monitor_handle_ref_unlocked (h);
2936 /* it has been unreffed when the entry was
2941 entry = (Entry *)folder;
2945 GnomeVFSResult result;
2949 /* Evil! EVIL URI PARSING. this will eat a lot of
2950 * stack if we have lots of monitors */
2952 VFOLDER_URI_PARSE (h->uri, &vuri);
2954 f = resolve_folder (info,
2956 TRUE /* ignore_basename */,
2961 ensure_folder_unlocked (
2963 FALSE /* subfolders */,
2965 FALSE /* ignore_unallocated */);
2966 entry = find_entry (f->entries, vuri.file);
2969 if (entry == NULL) {
2971 gnome_vfs_monitor_callback
2972 ((GnomeVFSMethodHandle *)h,
2974 GNOME_VFS_MONITOR_EVENT_DELETED);
2975 info->free_file_monitors = g_slist_prepend
2976 (info->free_file_monitors, h);
2977 file_monitor_handle_ref_unlocked (h);
2978 /* it has been unreffed when the entry was
2984 /* recreate a handle */
2985 if (h->handle == NULL &&
2986 entry->type == ENTRY_FILE) {
2987 EntryFile *efile = (EntryFile *)entry;
2988 char *uri = gnome_vfs_get_uri_from_local_path
2991 gnome_vfs_monitor_add (&(h->handle),
2993 GNOME_VFS_MONITOR_FILE,
2998 } else if (h->handle == NULL &&
3000 char *uri = gnome_vfs_get_uri_from_local_path (dirfile);
3002 gnome_vfs_monitor_add (&(h->handle),
3004 GNOME_VFS_MONITOR_FILE,
3014 try_free_file_monitors_create_files_unlocked (info);
3017 static gboolean /* unlocked */
3018 vfolder_info_read_items (VFolderInfo *info,
3019 GnomeVFSResult *result,
3020 GnomeVFSContext *context)
3025 for (li = info->merge_dirs; li != NULL; li = li->next) {
3026 const char *merge_dir = li->data;
3028 if ( ! vfolder_info_read_items_merge (info, merge_dir, NULL, FALSE,
3033 /* Then read the real thing (later overrides) */
3034 for (li = info->item_dirs; li != NULL; li = li->next) {
3035 const char *item_dir = li->data;
3037 if ( ! vfolder_info_read_items_from (info, item_dir,
3038 FALSE /* per_user */,
3043 if (info->user_item_dir != NULL) {
3044 if ( ! vfolder_info_read_items_from (info,
3045 info->user_item_dir,
3046 TRUE /* per_user */,
3051 rescan_monitors (info);
3057 string_slist_equal (GSList *list1, GSList *list2)
3061 for (li1 = list1, li2 = list2;
3062 li1 != NULL && li2 != NULL;
3063 li1 = li1->next, li2 = li2->next) {
3064 const char *s1 = li1->data;
3065 const char *s2 = li2->data;
3066 if (strcmp (s1, s2) != 0)
3069 /* if both are not NULL, then lengths are
3077 safe_string_same (const char *string1, const char *string2)
3079 if (string1 == string2 &&
3083 if (string1 != NULL && string2 != NULL &&
3084 strcmp (string1, string2) == 0)
3091 vfolder_info_item_dirs_same (VFolderInfo *info1, VFolderInfo *info2)
3093 if ( ! string_slist_equal (info1->item_dirs,
3097 if ( ! string_slist_equal (info1->merge_dirs,
3101 if ( ! safe_string_same (info1->user_item_dir,
3102 info2->user_item_dir))
3109 vfolder_info_reload_unlocked (VFolderInfo *info,
3110 GnomeVFSResult *result,
3111 GnomeVFSContext *context,
3112 gboolean force_read_items)
3114 VFolderInfo *newinfo;
3115 gboolean setup_filenames;
3116 gboolean setup_itemdirs;
3119 /* FIXME: Hmmm, race, there is no locking YAIKES,
3120 * we need filename locking for changes. eek, eek, eek */
3125 newinfo = g_new0 (VFolderInfo, 1);
3126 vfolder_info_init (newinfo, info->scheme);
3128 g_free (newinfo->filename);
3129 g_free (newinfo->user_filename);
3130 newinfo->filename = g_strdup (info->filename);
3131 newinfo->user_filename = g_strdup (info->user_filename);
3133 if (gnome_vfs_context_check_cancellation (context)) {
3134 vfolder_info_destroy (newinfo);
3135 *result = GNOME_VFS_ERROR_CANCELLED;
3139 if ( ! vfolder_info_read_info (newinfo, result, context)) {
3140 vfolder_info_destroy (newinfo);
3144 /* FIXME: reload logic for 'desktop_dir' and
3145 * 'user_desktop_dir' */
3147 setup_itemdirs = TRUE;
3149 /* Validity of entries and item dirs and all that is unchanged */
3150 if (vfolder_info_item_dirs_same (info, newinfo)) {
3151 newinfo->entries = info->entries;
3152 info->entries = NULL;
3153 newinfo->entries_ht = info->entries_ht;
3154 info->entries_ht = NULL /* some places assume this
3155 non-null, but we're only
3156 going to destroy this */;
3157 newinfo->entries_valid = info->entries_valid;
3159 /* move over the monitors/statlocs since those are valid */
3160 newinfo->item_dir_monitors = info->item_dir_monitors;
3161 info->item_dir_monitors = NULL;
3162 newinfo->stat_dirs = info->stat_dirs;
3163 info->stat_dirs = NULL;
3165 /* No need to resetup dir monitors */
3166 setup_itemdirs = FALSE;
3168 /* No need to do anything with file monitors */
3170 /* Whack all monitors here! */
3171 for (li = info->file_monitors; li != NULL; li = li->next) {
3172 FileMonitorHandle *h = li->data;
3173 if (h->handle != NULL)
3174 gnome_vfs_monitor_cancel (h->handle);
3179 setup_filenames = TRUE;
3181 if (safe_string_same (info->filename, newinfo->filename) &&
3182 safe_string_same (info->user_filename, newinfo->user_filename)) {
3183 newinfo->user_filename_last_write =
3184 info->user_filename_last_write;
3186 /* move over the monitors/statlocs since those are valid */
3187 newinfo->filename_monitor = info->filename_monitor;
3188 info->filename_monitor = NULL;
3189 newinfo->user_filename_monitor = info->user_filename_monitor;
3190 info->user_filename_monitor = NULL;
3192 if (info->filename_statloc != NULL &&
3193 info->filename != NULL)
3194 newinfo->filename_statloc =
3195 bake_statloc (info->filename,
3197 if (info->user_filename_statloc != NULL &&
3198 info->user_filename != NULL)
3199 newinfo->user_filename_statloc =
3200 bake_statloc (info->user_filename,
3203 /* No need to resetup filename monitors */
3204 setup_filenames = FALSE;
3207 /* Note: not cancellable anymore, since we've
3208 * already started nibbling on the info structure,
3209 * so we'd need to back things out or some such,
3210 * too complex, so screw that */
3211 monitor_setup (info,
3214 /* FIXME: setup_desktop_dirs */ TRUE,
3217 for (li = info->folder_monitors;
3220 FileMonitorHandle *handle = li->data;
3223 add_folder_monitor_unlocked (newinfo, handle);
3225 file_monitor_handle_unref_unlocked (handle);
3227 g_slist_free (info->folder_monitors);
3228 info->folder_monitors = NULL;
3230 g_slist_foreach (info->free_folder_monitors,
3231 (GFunc)file_monitor_handle_unref_unlocked, NULL);
3232 g_slist_free (info->free_folder_monitors);
3233 info->folder_monitors = NULL;
3235 /* we can just copy these for now, they will be readded
3236 * and all the fun stuff will be done with them later */
3237 newinfo->file_monitors = info->file_monitors;
3238 info->file_monitors = NULL;
3239 newinfo->free_file_monitors = info->free_file_monitors;
3240 info->free_file_monitors = NULL;
3242 /* emit changed on all folders, a bit drastic, but oh well,
3243 * we also invalidate all folders at the same time, but that is
3244 * irrelevant since they should all just be invalid to begin with */
3245 invalidate_folder_T (info->root);
3247 /* FIXME: make sure if this was enough, I think it was */
3249 vfolder_info_free_internals_unlocked (info);
3250 memcpy (info, newinfo, sizeof (VFolderInfo));
3253 /* must rescan the monitors here */
3254 if (info->entries_valid) {
3255 rescan_monitors (info);
3258 if ( ! info->entries_valid &&
3261 /* FIXME: I bet cancelation plays havoc with monitors,
3262 * I'm not sure however */
3263 if (info->file_monitors != NULL) {
3264 vfolder_info_read_items (info, &res, NULL);
3266 if ( ! vfolder_info_read_items (info, result, context))
3269 info->entries_valid = TRUE;
3276 vfolder_info_reload (VFolderInfo *info,
3277 GnomeVFSResult *result,
3278 GnomeVFSContext *context,
3279 gboolean force_read_items)
3281 G_LOCK (vfolder_lock);
3282 if (vfolder_info_reload_unlocked (info, result, context,
3283 force_read_items)) {
3284 G_UNLOCK (vfolder_lock);
3287 G_UNLOCK (vfolder_lock);
3293 vfolder_info_recheck (VFolderInfo *info,
3294 GnomeVFSResult *result,
3295 GnomeVFSContext *context)
3298 time_t curtime = time (NULL);
3299 gboolean reread = FALSE;
3301 if (info->filename_statloc != NULL &&
3302 ! check_statloc (info->filename_statloc, curtime)) {
3303 if ( ! vfolder_info_reload_unlocked (info, result, context,
3304 FALSE /* force read items */)) {
3305 /* we have failed, make sure we fail
3307 info->filename_statloc->trigger_next = TRUE;
3313 info->user_filename_statloc != NULL &&
3314 ! check_statloc (info->user_filename_statloc, curtime)) {
3315 if ( ! vfolder_info_reload_unlocked (info, result, context,
3316 FALSE /* force read items */)) {
3317 /* we have failed, make sure we fail
3319 info->user_filename_statloc->trigger_next = TRUE;
3325 if (info->entries_valid) {
3326 for (li = info->stat_dirs; li != NULL; li = li->next) {
3327 StatLoc *sl = li->data;
3328 if ( ! check_statloc (sl, curtime)) {
3329 info->entries_valid = FALSE;
3337 static VFolderInfo *
3338 get_vfolder_info_unlocked (const char *scheme,
3339 GnomeVFSResult *result,
3340 GnomeVFSContext *context)
3344 if (infos != NULL &&
3345 (info = g_hash_table_lookup (infos, scheme)) != NULL) {
3346 if ( ! vfolder_info_recheck (info, result, context)) {
3349 if ( ! info->entries_valid) {
3350 g_slist_foreach (info->entries,
3351 (GFunc)entry_unref, NULL);
3352 g_slist_free (info->entries);
3353 info->entries = NULL;
3355 if (info->entries_ht != NULL)
3356 g_hash_table_destroy (info->entries_ht);
3357 info->entries_ht = g_hash_table_new (g_str_hash,
3360 if ( ! vfolder_info_read_items (info,
3362 info->entries_valid = FALSE;
3366 invalidate_folder_T (info->root);
3368 info->entries_valid = TRUE;
3370 /* Update modification time of all folders,
3371 * kind of evil, but it will make adding new items work
3372 * I hope. This is because rereading usually means
3373 * something changed */
3374 info->modification_time = time (NULL);
3379 if (gnome_vfs_context_check_cancellation (context)) {
3380 *result = GNOME_VFS_ERROR_CANCELLED;
3385 infos = g_hash_table_new_full
3386 (g_str_hash, g_str_equal,
3387 (GDestroyNotify)g_free,
3388 (GDestroyNotify)vfolder_info_destroy);
3390 info = g_new0 (VFolderInfo, 1);
3391 vfolder_info_init (info, scheme);
3393 if (gnome_vfs_context_check_cancellation (context)) {
3394 vfolder_info_destroy (info);
3395 *result = GNOME_VFS_ERROR_CANCELLED;
3399 if ( ! vfolder_info_read_info (info, result, context)) {
3400 vfolder_info_destroy (info);
3404 if ( ! monitor_setup (info,
3405 TRUE /* setup_filenames */,
3406 TRUE /* setup_itemdirs */,
3407 TRUE /* setup_desktop_dirs */,
3409 vfolder_info_destroy (info);
3413 g_hash_table_insert (infos, g_strdup (scheme), info);
3415 if ( ! vfolder_info_read_items (info, result, context)) {
3416 info->entries_valid = FALSE;
3419 info->entries_valid = TRUE;
3424 static VFolderInfo *
3425 get_vfolder_info (const char *scheme,
3426 GnomeVFSResult *result,
3427 GnomeVFSContext *context)
3430 G_LOCK (vfolder_lock);
3431 info = get_vfolder_info_unlocked (scheme, result, context);
3432 G_UNLOCK (vfolder_lock);
3438 keywords_to_string (GSList *keywords)
3441 GString *str = g_string_new (NULL);
3443 for (li = keywords; li != NULL; li = li->next) {
3444 GQuark word = GPOINTER_TO_INT (li->data);
3445 g_string_append (str, g_quark_to_string (word));
3446 g_string_append_c (str, ';');
3449 return g_string_free (str, FALSE);
3452 /* copy file and add keywords line */
3454 copy_file_with_keywords (const char *from, const char *to, GSList *keywords)
3460 char *keyword_string;
3462 if ( ! ensure_dir (to,
3463 TRUE /* ignore_basename */))
3466 wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3471 keyword_string = keywords_to_string (keywords);
3473 wfp = fdopen (wfd, "w");
3475 fp = fopen (from, "r");
3477 gboolean wrote_keywords = FALSE;
3478 while (fgets (buf, sizeof (buf), fp) != NULL) {
3479 fprintf (wfp, "%s", buf);
3480 if ( ! wrote_keywords &&
3481 (strncmp (buf, "[Desktop Entry]",
3482 strlen ("[Desktop Entry]")) == 0 ||
3483 strncmp (buf, "[KDE Desktop Entry]",
3484 strlen ("[KDE Desktop Entry]")) == 0)) {
3485 fprintf (wfp, "Categories=%s\n",
3487 wrote_keywords = TRUE;
3493 fprintf (wfp, "[Desktop Entry]\nCategories=%s\n",
3497 /* FIXME: does this close wfd???? */
3502 g_free (keyword_string);
3508 copy_file (const char *from, const char *to)
3513 if ( ! ensure_dir (to,
3514 TRUE /* ignore_basename */))
3517 wfd = open (to, O_CREAT | O_WRONLY | O_TRUNC, 0600);
3522 fd = open (from, O_RDONLY);
3527 while ((n = read (fd, buf, sizeof(buf))) > 0) {
3528 write (wfd, buf, n);
3540 make_file_private (VFolderInfo *info, EntryFile *efile)
3543 Entry *entry = (Entry *)efile;
3545 if (efile->per_user)
3548 /* this file already exists so whack its monitors */
3549 if (efile->filename != NULL) {
3552 for (li = entry->monitors; li != NULL; li = li->next) {
3553 FileMonitorHandle *h = li->data;
3554 if (h->handle != NULL)
3555 gnome_vfs_monitor_cancel (h->handle);
3560 newfname = g_build_filename (g_get_home_dir (),
3567 if (efile->implicit_keywords) {
3568 if (efile->filename != NULL &&
3569 ! copy_file_with_keywords (efile->filename,
3572 /* FIXME: what to do with monitors here, they
3573 * have already been whacked, a corner case
3579 if (efile->filename != NULL &&
3580 ! copy_file (efile->filename, newfname)) {
3581 /* FIXME: what to do with monitors here, they
3582 * have already been whacked, a corner case
3589 /* we didn't copy but ensure path anyway */
3590 if (efile->filename == NULL &&
3591 ! ensure_dir (newfname,
3592 TRUE /* ignore_basename */)) {
3597 /* this file already exists so re-add monitors at the new location */
3598 if (efile->filename != NULL) {
3600 char *uri = gnome_vfs_get_uri_from_local_path (newfname);
3602 for (li = entry->monitors; li != NULL; li = li->next) {
3603 FileMonitorHandle *h = li->data;
3605 gnome_vfs_monitor_add (&(h->handle),
3607 GNOME_VFS_MONITOR_FILE,
3615 g_free (efile->filename);
3616 efile->filename = newfname;
3617 efile->per_user = TRUE;
3623 try_free_file_monitors_create_dirfile_unlocked (VFolderInfo *info,
3628 list = g_slist_copy (info->free_file_monitors);
3630 for (li = list; li != NULL; li = li->next) {
3631 FileMonitorHandle *handle = li->data;
3634 GnomeVFSResult result;
3636 if ( ! handle->is_directory_file)
3639 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
3640 * have lots of free monitors */
3642 VFOLDER_URI_PARSE (handle->uri, &vuri);
3644 f = resolve_folder (info,
3646 TRUE /* ignore_basename */,
3653 info->free_file_monitors =
3654 g_slist_remove (info->free_file_monitors, handle);
3655 ((Entry *)folder)->monitors =
3656 g_slist_prepend (((Entry *)folder)->monitors, handle);
3658 handle->exists = TRUE;
3659 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
3661 GNOME_VFS_MONITOR_EVENT_CREATED);
3664 g_slist_free (list);
3668 make_new_dirfile (VFolderInfo *info, Folder *folder)
3670 char *name = g_strdup (folder->entry.name);
3676 for (p = name; *p != '\0'; p++) {
3677 if ( ! ( (*p >= 'a' && *p <= 'z') ||
3678 (*p >= 'A' && *p <= 'Z') ||
3679 (*p >= '0' && *p <= '9') ||
3693 fname = g_strdup_printf ("%s-%d.directory", name, i);
3695 fname = g_strdup_printf ("%s.directory", name);
3698 fullname = g_build_filename
3699 (info->user_desktop_dir, fname, NULL);
3700 fd = open (fullname, O_CREAT | O_WRONLY | O_EXCL, 0600);
3706 folder->desktop_file = fname;
3709 try_free_file_monitors_create_dirfile_unlocked (info, folder);
3713 make_dirfile_private (VFolderInfo *info, Folder *folder)
3721 if (info->user_desktop_dir == NULL)
3724 if ( ! ensure_dir (info->user_desktop_dir,
3725 FALSE /* ignore_basename */))
3729 if (folder->desktop_file == NULL) {
3730 make_new_dirfile (info, folder);
3734 /* FIXME: this is broken! What if the desktop file exists
3735 * in the local but there is a different (but with a same name)
3736 * .directory in the system. */
3737 fname = g_build_filename (info->user_desktop_dir,
3738 folder->desktop_file,
3741 if (access (fname, F_OK) == 0) {
3746 desktop_file = get_directory_file (info, folder);
3748 if (desktop_file == NULL) {
3749 int fd = open (fname, O_CREAT | O_EXCL | O_WRONLY, 0600);
3758 for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3759 FileMonitorHandle *h = li->data;
3760 if (h->is_directory_file) {
3761 if (h->handle != NULL)
3762 gnome_vfs_monitor_cancel (h->handle);
3769 if ( ! copy_file (desktop_file, fname)) {
3772 fname = desktop_file;
3773 desktop_file = NULL;
3776 uri = gnome_vfs_get_uri_from_local_path (fname);
3778 for (li = ((Entry *)folder)->monitors; li != NULL; li = li->next) {
3779 FileMonitorHandle *h = li->data;
3781 if (h->is_directory_file) {
3782 gnome_vfs_monitor_add (&(h->handle),
3784 GNOME_VFS_MONITOR_FILE,
3792 g_free (desktop_file);
3799 resolve_folder (VFolderInfo *info,
3801 gboolean ignore_basename,
3802 GnomeVFSResult *result,
3803 GnomeVFSContext *context)
3807 Folder *folder = info->root;
3809 ppath = g_strsplit (path, "/", -1);
3811 if (ppath == NULL ||
3814 *result = GNOME_VFS_ERROR_INVALID_URI;
3818 for (i = 0; ppath [i] != NULL; i++) {
3819 const char *segment = ppath[i];
3821 if (*segment == '\0')
3824 if (ignore_basename && ppath [i + 1] == NULL)
3827 folder = (Folder *) find_entry (folder->subfolders,
3835 if (gnome_vfs_context_check_cancellation (context)) {
3836 *result = GNOME_VFS_ERROR_CANCELLED;
3841 *result = GNOME_VFS_ERROR_NOT_FOUND;
3847 resolve_path (VFolderInfo *info,
3849 const char *basename,
3850 Folder **return_folder,
3851 GnomeVFSResult *result,
3852 GnomeVFSContext *context)
3857 if (strcmp (path, "/") == 0)
3858 return (Entry *)info->root;
3860 folder = resolve_folder (info, path,
3861 TRUE /* ignore_basename */,
3864 if (return_folder != NULL)
3865 *return_folder = folder;
3867 if (folder == NULL) {
3871 /* Make sure we have the entries here */
3872 ensure_folder_unlocked (info, folder,
3873 FALSE /* subfolders */,
3875 FALSE /* ignore_unallocated */);
3877 entry = find_entry (folder->entries, basename);
3880 *result = GNOME_VFS_ERROR_NOT_FOUND;
3886 get_entry_unlocked (VFolderURI *vuri,
3888 gboolean *is_directory_file,
3889 GnomeVFSResult *result,
3890 GnomeVFSContext *context)
3895 if (is_directory_file != NULL)
3896 *is_directory_file = FALSE;
3900 info = get_vfolder_info_unlocked (vuri->scheme, result, context);
3904 if (gnome_vfs_context_check_cancellation (context)) {
3905 *result = GNOME_VFS_ERROR_CANCELLED;
3909 if (vuri->is_all_scheme) {
3912 if (vuri->file == NULL) {
3913 entry = resolve_path (info,
3922 efile_list = g_hash_table_lookup (info->entries_ht, vuri->file);
3924 if (efile_list == NULL) {
3925 *result = GNOME_VFS_ERROR_NOT_FOUND;
3928 return efile_list->data;
3932 if (vuri->file != NULL &&
3933 check_ext (vuri->file, ".directory") == TRUE) {
3936 folder = resolve_folder (info, vuri->path,
3937 TRUE /* ignore_basename */,
3939 if (folder == NULL) {
3943 if (is_directory_file != NULL)
3944 *is_directory_file = TRUE;
3949 return (Entry *)folder;
3951 entry = resolve_path (info, vuri->path, vuri->file, parent,
3958 get_entry (VFolderURI *vuri,
3960 gboolean *is_directory_file,
3961 GnomeVFSResult *result,
3962 GnomeVFSContext *context)
3966 G_LOCK (vfolder_lock);
3967 entry = get_entry_unlocked (vuri,
3971 G_UNLOCK (vfolder_lock);
3976 /* only works for files and only those that exist */
3977 /* unlocked function */
3978 static GnomeVFSURI *
3979 desktop_uri_to_file_uri (VFolderInfo *info,
3980 VFolderURI *desktop_vuri,
3982 gboolean *the_is_directory_file,
3983 Folder **the_folder,
3985 GnomeVFSResult *result,
3986 GnomeVFSContext *context)
3988 gboolean is_directory_file;
3989 GnomeVFSURI *ret_uri;
3990 Folder *folder = NULL;
3993 entry = get_entry_unlocked (desktop_vuri,
4001 if (gnome_vfs_context_check_cancellation (context)) {
4002 *result = GNOME_VFS_ERROR_CANCELLED;
4006 if (the_folder != NULL)
4007 *the_folder = folder;
4009 if (the_entry != NULL)
4011 if (the_is_directory_file != NULL)
4012 *the_is_directory_file = is_directory_file;
4014 if (is_directory_file &&
4015 entry->type == ENTRY_FOLDER) {
4018 folder = (Folder *)entry;
4020 if (the_folder != NULL)
4021 *the_folder = folder;
4023 /* we'll be doing something write like */
4024 if (folder->read_only &&
4026 *result = GNOME_VFS_ERROR_READ_ONLY;
4033 if (gnome_vfs_context_check_cancellation (context)) {
4034 *result = GNOME_VFS_ERROR_CANCELLED;
4038 if ( ! make_dirfile_private (info, folder)) {
4039 *result = GNOME_VFS_ERROR_GENERIC;
4042 fname = g_build_filename (g_get_home_dir (),
4043 folder->desktop_file,
4045 ret_uri = gnome_vfs_uri_new (fname);
4050 desktop_file = get_directory_file_unlocked (info, folder);
4051 if (desktop_file != NULL) {
4052 char *s = gnome_vfs_get_uri_from_local_path
4055 g_free (desktop_file);
4057 ret_uri = gnome_vfs_uri_new (s);
4062 *result = GNOME_VFS_ERROR_NOT_FOUND;
4065 } else if (entry->type == ENTRY_FILE) {
4066 EntryFile *efile = (EntryFile *)entry;
4069 /* we'll be doing something write like */
4070 if (folder != NULL &&
4071 folder->read_only &&
4073 *result = GNOME_VFS_ERROR_READ_ONLY;
4077 if (gnome_vfs_context_check_cancellation (context)) {
4078 *result = GNOME_VFS_ERROR_CANCELLED;
4083 ! make_file_private (info, efile)) {
4084 *result = GNOME_VFS_ERROR_GENERIC;
4088 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4089 ret_uri = gnome_vfs_uri_new (s);
4094 if (the_folder != NULL)
4095 *the_folder = (Folder *)entry;
4096 *result = GNOME_VFS_ERROR_IS_DIRECTORY;
4102 remove_file (Folder *folder, const char *basename)
4107 if (folder->includes_ht != NULL) {
4108 li = g_hash_table_lookup (folder->includes_ht, basename);
4110 char *name = li->data;
4111 folder->includes = g_slist_delete_link
4112 (folder->includes, li);
4113 g_hash_table_remove (folder->includes_ht, basename);
4118 if (folder->excludes == NULL) {
4119 folder->excludes = g_hash_table_new_full
4120 (g_str_hash, g_str_equal,
4121 (GDestroyNotify)g_free,
4124 s = g_strdup (basename);
4125 g_hash_table_replace (folder->excludes, s, s);
4129 add_file (Folder *folder, const char *basename)
4133 if (folder->includes_ht != NULL) {
4134 li = g_hash_table_lookup (folder->includes_ht, basename);
4139 char *str = g_strdup (basename);
4141 g_slist_prepend (folder->includes, str);
4142 if (folder->includes_ht == NULL) {
4143 folder->includes_ht =
4144 g_hash_table_new_full (g_str_hash,
4149 g_hash_table_replace (folder->includes_ht,
4150 str, folder->includes);
4152 if (folder->excludes != NULL)
4153 g_hash_table_remove (folder->excludes, basename);
4156 typedef struct _FileHandle FileHandle;
4157 struct _FileHandle {
4159 GnomeVFSMethodHandle *handle;
4162 gboolean is_directory_file;
4166 make_handle (GnomeVFSMethodHandle **method_handle,
4167 GnomeVFSMethodHandle *file_handle,
4170 gboolean is_directory_file,
4173 if (file_handle != NULL) {
4174 FileHandle *handle = g_new0 (FileHandle, 1);
4176 handle->info = info;
4177 handle->handle = file_handle;
4178 handle->entry = entry_ref (entry);
4179 handle->is_directory_file = is_directory_file;
4180 handle->write = write;
4182 *method_handle = (GnomeVFSMethodHandle *) handle;
4184 *method_handle = NULL;
4189 whack_handle (FileHandle *handle)
4191 entry_unref (handle->entry);
4192 handle->entry = NULL;
4194 handle->handle = NULL;
4195 handle->info = NULL;
4200 static GnomeVFSResult
4201 do_open (GnomeVFSMethod *method,
4202 GnomeVFSMethodHandle **method_handle,
4204 GnomeVFSOpenMode mode,
4205 GnomeVFSContext *context)
4207 GnomeVFSURI *file_uri;
4208 GnomeVFSResult result = GNOME_VFS_OK;
4211 gboolean is_directory_file;
4212 GnomeVFSMethodHandle *file_handle = NULL;
4215 VFOLDER_URI_PARSE (uri, &vuri);
4217 /* These can't be very nice FILE names */
4218 if (vuri.file == NULL ||
4220 return GNOME_VFS_ERROR_INVALID_URI;
4222 info = get_vfolder_info (vuri.scheme, &result, context);
4226 if (mode & GNOME_VFS_OPEN_WRITE &&
4227 (info->read_only || vuri.is_all_scheme))
4228 return GNOME_VFS_ERROR_READ_ONLY;
4230 G_LOCK (vfolder_lock);
4231 file_uri = desktop_uri_to_file_uri (info,
4235 NULL /* the_folder */,
4236 mode & GNOME_VFS_OPEN_WRITE,
4240 if (file_uri == NULL) {
4241 G_UNLOCK (vfolder_lock);
4245 result = (* parent_method->open) (parent_method,
4251 if (result == GNOME_VFS_ERROR_CANCELLED) {
4252 G_UNLOCK (vfolder_lock);
4253 gnome_vfs_uri_unref (file_uri);
4257 make_handle (method_handle,
4262 mode & GNOME_VFS_OPEN_WRITE);
4264 gnome_vfs_uri_unref (file_uri);
4267 vfolder_info_write_user (info);
4270 G_UNLOCK (vfolder_lock);
4276 remove_from_all_except (Folder *root,
4282 if (root != except) {
4283 remove_file (root, name);
4284 if (root->up_to_date) {
4285 for (li = root->entries; li != NULL; li = li->next) {
4286 Entry *entry = li->data;
4287 if (strcmp (name, entry->name) == 0) {
4290 (root->entries, li);
4297 for (li = root->subfolders; li != NULL; li = li->next) {
4298 Folder *subfolder = li->data;
4300 remove_from_all_except (subfolder, name, except);
4304 static GnomeVFSResult
4305 do_create (GnomeVFSMethod *method,
4306 GnomeVFSMethodHandle **method_handle,
4308 GnomeVFSOpenMode mode,
4311 GnomeVFSContext *context)
4313 GnomeVFSResult result = GNOME_VFS_OK;
4314 GnomeVFSMethodHandle *file_handle;
4315 GnomeVFSURI *file_uri;
4324 VFOLDER_URI_PARSE (uri, &vuri);
4326 /* These can't be very nice FILE names */
4327 if (vuri.file == NULL ||
4329 return GNOME_VFS_ERROR_INVALID_URI;
4331 if ( ! check_ext (vuri.file, ".desktop") &&
4332 ! strcmp (vuri.file, ".directory") == 0) {
4333 return GNOME_VFS_ERROR_INVALID_URI;
4336 /* all scheme is read only */
4337 if (vuri.is_all_scheme)
4338 return GNOME_VFS_ERROR_READ_ONLY;
4340 info = get_vfolder_info (vuri.scheme, &result, context);
4344 if (info->user_filename == NULL ||
4346 return GNOME_VFS_ERROR_READ_ONLY;
4348 parent = resolve_folder (info, vuri.path,
4349 TRUE /* ignore_basename */,
4354 if (parent->read_only)
4355 return GNOME_VFS_ERROR_READ_ONLY;
4357 if (strcmp (vuri.file, ".directory") == 0) {
4360 G_LOCK (vfolder_lock);
4364 desktop_file = get_directory_file_unlocked (info, parent);
4365 if (desktop_file != NULL) {
4366 g_free (desktop_file);
4367 G_UNLOCK (vfolder_lock);
4368 return GNOME_VFS_ERROR_FILE_EXISTS;
4372 if ( ! make_dirfile_private (info, parent)) {
4373 G_UNLOCK (vfolder_lock);
4374 return GNOME_VFS_ERROR_GENERIC;
4376 fname = g_build_filename (g_get_home_dir (),
4377 parent->desktop_file,
4379 s = gnome_vfs_get_uri_from_local_path (fname);
4380 file_uri = gnome_vfs_uri_new (s);
4384 if (file_uri == NULL) {
4385 G_UNLOCK (vfolder_lock);
4386 return GNOME_VFS_ERROR_GENERIC;
4389 result = (* parent_method->create) (parent_method,
4396 gnome_vfs_uri_unref (file_uri);
4398 make_handle (method_handle,
4402 TRUE /* is_directory_file */,
4406 vfolder_info_write_user (info);
4408 G_UNLOCK (vfolder_lock);
4413 ensure_folder (info, parent,
4414 FALSE /* subfolders */,
4416 FALSE /* ignore_unallocated */);
4418 entry = find_entry (parent->entries, vuri.file);
4420 if (entry != NULL &&
4421 entry->type == ENTRY_FOLDER)
4422 return GNOME_VFS_ERROR_IS_DIRECTORY;
4424 efile = (EntryFile *)entry;
4426 if (efile != NULL) {
4428 return GNOME_VFS_ERROR_FILE_EXISTS;
4430 G_LOCK (vfolder_lock);
4431 if ( ! make_file_private (info, efile)) {
4432 G_UNLOCK (vfolder_lock);
4433 return GNOME_VFS_ERROR_GENERIC;
4436 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4437 file_uri = gnome_vfs_uri_new (s);
4440 if (file_uri == NULL) {
4441 G_UNLOCK (vfolder_lock);
4442 return GNOME_VFS_ERROR_GENERIC;
4445 result = (* parent_method->create) (parent_method,
4452 gnome_vfs_uri_unref (file_uri);
4454 make_handle (method_handle,
4458 FALSE /* is_directory_file */,
4461 G_UNLOCK (vfolder_lock);
4466 G_LOCK (vfolder_lock);
4468 li = g_hash_table_lookup (info->entries_ht, vuri.file);
4470 if (exclusive && li != NULL) {
4471 G_UNLOCK (vfolder_lock);
4472 return GNOME_VFS_ERROR_FILE_EXISTS;
4476 efile = file_new (vuri.file);
4477 vfolder_info_insert_entry (info, efile);
4478 entry_unref ((Entry *)efile);
4483 /* this will make a private name for this */
4484 if ( ! make_file_private (info, efile)) {
4485 G_UNLOCK (vfolder_lock);
4486 return GNOME_VFS_ERROR_GENERIC;
4489 add_file (parent, vuri.file);
4490 parent->sorted = FALSE;
4492 if (parent->up_to_date)
4493 parent->entries = g_slist_prepend (parent->entries, efile);
4495 /* if we created a brand new name, then we exclude it
4496 * from everywhere else to ensure overall sanity */
4498 remove_from_all_except (info->root, vuri.file, parent);
4500 s = gnome_vfs_get_uri_from_local_path (efile->filename);
4501 file_uri = gnome_vfs_uri_new (s);
4504 result = (* parent_method->create) (parent_method,
4511 gnome_vfs_uri_unref (file_uri);
4513 make_handle (method_handle,
4517 FALSE /* is_directory_file */,
4520 vfolder_info_write_user (info);
4522 G_UNLOCK (vfolder_lock);
4527 static GnomeVFSResult
4528 do_close (GnomeVFSMethod *method,
4529 GnomeVFSMethodHandle *method_handle,
4530 GnomeVFSContext *context)
4532 GnomeVFSResult result;
4533 FileHandle *handle = (FileHandle *)method_handle;
4534 if (method_handle == (GnomeVFSMethodHandle *)method)
4535 return GNOME_VFS_OK;
4537 G_LOCK (vfolder_lock);
4539 result = (* parent_method->close) (parent_method,
4542 handle->handle = NULL;
4544 /* we reread the Categories keyword */
4545 if (handle->write &&
4546 handle->entry != NULL &&
4547 handle->entry->type == ENTRY_FILE) {
4548 EntryFile *efile = (EntryFile *)handle->entry;
4550 readitem_entry (efile->filename,
4555 set_keywords (efile, categories);
4556 g_free (categories);
4557 /* FIXME: what about OnlyShowIn */
4559 /* FIXME: check if the keywords changed, if not, do
4562 /* Perhaps a bit drastic */
4563 /* also this emits the CHANGED monitor signal */
4564 invalidate_folder_T (handle->info->root);
4566 /* the file changed monitor will happen by itself
4567 * as the underlying file is changed */
4568 } else if (handle->write &&
4569 handle->entry != NULL &&
4570 handle->entry->type == ENTRY_FOLDER &&
4571 handle->is_directory_file) {
4572 /* if we're monitoring this directory, emit the CHANGED
4573 * monitor thing, it will also emit a changed on
4574 * the file itself. It is better to emit changed
4576 emit_monitor ((Folder *)(handle->entry),
4577 GNOME_VFS_MONITOR_EVENT_CHANGED);
4580 whack_handle (handle);
4582 G_UNLOCK (vfolder_lock);
4588 fill_buffer (gpointer buffer,
4589 GnomeVFSFileSize num_bytes,
4590 GnomeVFSFileSize *bytes_read)
4594 for (i = 0; i < num_bytes; i++) {
4595 if (rand () % 32 == 0 ||
4599 buf[i] = ((rand()>>4) % 94) + 32;
4601 if (bytes_read != 0)
4605 static GnomeVFSResult
4606 do_read (GnomeVFSMethod *method,
4607 GnomeVFSMethodHandle *method_handle,
4609 GnomeVFSFileSize num_bytes,
4610 GnomeVFSFileSize *bytes_read,
4611 GnomeVFSContext *context)
4613 GnomeVFSResult result;
4614 FileHandle *handle = (FileHandle *)method_handle;
4616 if (method_handle == (GnomeVFSMethodHandle *)method) {
4617 if ((rand () >> 4) & 0x3) {
4618 fill_buffer (buffer, num_bytes, bytes_read);
4619 return GNOME_VFS_OK;
4621 return GNOME_VFS_ERROR_EOF;
4625 result = (* parent_method->read) (parent_method,
4634 static GnomeVFSResult
4635 do_write (GnomeVFSMethod *method,
4636 GnomeVFSMethodHandle *method_handle,
4637 gconstpointer buffer,
4638 GnomeVFSFileSize num_bytes,
4639 GnomeVFSFileSize *bytes_written,
4640 GnomeVFSContext *context)
4642 GnomeVFSResult result;
4643 FileHandle *handle = (FileHandle *)method_handle;
4645 if (method_handle == (GnomeVFSMethodHandle *)method)
4646 return GNOME_VFS_OK;
4648 result = (* parent_method->write) (parent_method,
4658 static GnomeVFSResult
4659 do_seek (GnomeVFSMethod *method,
4660 GnomeVFSMethodHandle *method_handle,
4661 GnomeVFSSeekPosition whence,
4662 GnomeVFSFileOffset offset,
4663 GnomeVFSContext *context)
4665 GnomeVFSResult result;
4666 FileHandle *handle = (FileHandle *)method_handle;
4668 if (method_handle == (GnomeVFSMethodHandle *)method)
4669 return GNOME_VFS_OK;
4671 result = (* parent_method->seek) (parent_method,
4679 static GnomeVFSResult
4680 do_tell (GnomeVFSMethod *method,
4681 GnomeVFSMethodHandle *method_handle,
4682 GnomeVFSFileOffset *offset_return)
4684 GnomeVFSResult result;
4685 FileHandle *handle = (FileHandle *)method_handle;
4687 result = (* parent_method->tell) (parent_method,
4695 static GnomeVFSResult
4696 do_truncate_handle (GnomeVFSMethod *method,
4697 GnomeVFSMethodHandle *method_handle,
4698 GnomeVFSFileSize where,
4699 GnomeVFSContext *context)
4701 GnomeVFSResult result;
4702 FileHandle *handle = (FileHandle *)method_handle;
4704 if (method_handle == (GnomeVFSMethodHandle *)method)
4705 return GNOME_VFS_OK;
4707 result = (* parent_method->truncate_handle) (parent_method,
4715 static GnomeVFSResult
4716 do_truncate (GnomeVFSMethod *method,
4718 GnomeVFSFileSize where,
4719 GnomeVFSContext *context)
4721 GnomeVFSURI *file_uri;
4722 GnomeVFSResult result = GNOME_VFS_OK;
4727 VFOLDER_URI_PARSE (uri, &vuri);
4729 /* These can't be very nice FILE names */
4730 if (vuri.file == NULL ||
4732 return GNOME_VFS_ERROR_INVALID_URI;
4734 if (vuri.is_all_scheme)
4735 return GNOME_VFS_ERROR_READ_ONLY;
4737 info = get_vfolder_info (vuri.scheme, &result, context);
4741 if (info->read_only)
4742 return GNOME_VFS_ERROR_READ_ONLY;
4744 G_LOCK (vfolder_lock);
4745 file_uri = desktop_uri_to_file_uri (info,
4748 NULL /* the_is_directory_file */,
4749 NULL /* the_folder */,
4750 TRUE /* privatize */,
4753 G_UNLOCK (vfolder_lock);
4755 if (file_uri == NULL)
4758 result = (* parent_method->truncate) (parent_method,
4763 gnome_vfs_uri_unref (file_uri);
4766 G_LOCK (vfolder_lock);
4767 vfolder_info_write_user (info);
4768 G_UNLOCK (vfolder_lock);
4771 if (entry->type == ENTRY_FILE) {
4772 EntryFile *efile = (EntryFile *)entry;
4774 G_LOCK (vfolder_lock);
4775 g_slist_free (efile->keywords);
4776 efile->keywords = NULL;
4777 G_UNLOCK (vfolder_lock);
4780 /* Perhaps a bit drastic, but oh well */
4781 invalidate_folder (info->root);
4786 typedef struct _DirHandle DirHandle;
4791 GnomeVFSFileInfoOptions options;
4793 /* List of Entries */
4798 static GnomeVFSResult
4799 do_open_directory (GnomeVFSMethod *method,
4800 GnomeVFSMethodHandle **method_handle,
4802 GnomeVFSFileInfoOptions options,
4803 GnomeVFSContext *context)
4805 GnomeVFSResult result = GNOME_VFS_OK;
4812 VFOLDER_URI_PARSE (uri, &vuri);
4814 info = get_vfolder_info (vuri.scheme, &result, context);
4818 /* In the all- scheme just list all filenames */
4819 if (vuri.is_all_scheme) {
4820 if (any_subdir (vuri.path))
4821 return GNOME_VFS_ERROR_NOT_FOUND;
4823 dh = g_new0 (DirHandle, 1);
4825 dh->options = options;
4828 G_LOCK (vfolder_lock);
4829 dh->list = g_slist_copy (info->entries);
4830 g_slist_foreach (dh->list, (GFunc)entry_ref, NULL);
4831 dh->current = dh->list;
4832 G_UNLOCK (vfolder_lock);
4834 *method_handle = (GnomeVFSMethodHandle*) dh;
4835 return GNOME_VFS_OK;
4838 folder = resolve_folder (info, vuri.path,
4839 FALSE /* ignore_basename */,
4844 /* Make sure we have the entries and sorted here */
4845 ensure_folder_sort (info, folder);
4847 dh = g_new0 (DirHandle, 1);
4849 dh->options = options;
4851 G_LOCK (vfolder_lock);
4852 dh->folder = (Folder *)entry_ref ((Entry *)folder);
4853 dh->list = g_slist_copy (folder->entries);
4854 g_slist_foreach (folder->entries, (GFunc)entry_ref, NULL);
4855 G_UNLOCK (vfolder_lock);
4857 desktop_file = get_directory_file (info, folder);
4858 if (desktop_file != NULL) {
4859 EntryFile *efile = file_new (".directory");
4860 dh->list = g_slist_prepend (dh->list, efile);
4861 g_free (desktop_file);
4864 dh->current = dh->list;
4866 *method_handle = (GnomeVFSMethodHandle*) dh;
4868 return GNOME_VFS_OK;
4871 static GnomeVFSResult
4872 do_close_directory (GnomeVFSMethod *method,
4873 GnomeVFSMethodHandle *method_handle,
4874 GnomeVFSContext *context)
4878 dh = (DirHandle*) method_handle;
4880 G_LOCK (vfolder_lock);
4882 g_slist_foreach (dh->list, (GFunc)entry_unref, NULL);
4883 g_slist_free (dh->list);
4888 if (dh->folder != NULL)
4889 entry_unref ((Entry *)dh->folder);
4896 G_UNLOCK (vfolder_lock);
4898 return GNOME_VFS_OK;
4901 static GnomeVFSResult
4902 do_read_directory (GnomeVFSMethod *method,
4903 GnomeVFSMethodHandle *method_handle,
4904 GnomeVFSFileInfo *file_info,
4905 GnomeVFSContext *context)
4909 GnomeVFSFileInfoOptions options;
4911 dh = (DirHandle*) method_handle;
4913 read_directory_again:
4915 if (dh->current == NULL) {
4916 return GNOME_VFS_ERROR_EOF;
4919 entry = dh->current->data;
4920 dh->current = dh->current->next;
4922 options = dh->options;
4924 if (entry->type == ENTRY_FILE &&
4925 ((EntryFile *)entry)->filename != NULL) {
4926 EntryFile *efile = (EntryFile *)entry;
4927 char *furi = gnome_vfs_get_uri_from_local_path (efile->filename);
4928 GnomeVFSURI *uri = gnome_vfs_uri_new (furi);
4930 /* we always get mime-type by forcing it below */
4931 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
4932 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
4934 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4936 /* Get the file info for this */
4937 (* parent_method->get_file_info) (parent_method,
4943 /* we ignore errors from this since the file_info just
4944 * won't be filled completely if there's an error, that's all */
4946 g_free (file_info->mime_type);
4947 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4948 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4950 /* Now we wipe those fields we don't support */
4951 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
4953 gnome_vfs_uri_unref (uri);
4955 } else if (entry->type == ENTRY_FILE) {
4956 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4958 file_info->name = g_strdup (entry->name);
4959 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4961 file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
4962 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4964 /* FIXME: Is this correct? isn't there an xdg mime type? */
4965 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
4966 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4968 /* FIXME: get some ctime/mtime */
4969 } else /* ENTRY_FOLDER */ {
4970 Folder *folder = (Folder *)entry;
4972 /* Skip empty folders if they have
4974 if (folder->dont_show_if_empty) {
4975 /* Make sure we have the entries */
4976 ensure_folder (dh->info, folder,
4977 FALSE /* subfolders */,
4979 FALSE /* ignore_unallocated */);
4981 if (folder->entries == NULL) {
4982 /* start this function over on the
4984 goto read_directory_again;
4988 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
4990 file_info->name = g_strdup (entry->name);
4991 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
4993 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
4994 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
4996 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
4997 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
4999 file_info->ctime = dh->info->modification_time;
5000 file_info->mtime = dh->info->modification_time;
5001 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
5002 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
5005 return GNOME_VFS_OK;
5008 static GnomeVFSResult
5009 do_get_file_info (GnomeVFSMethod *method,
5011 GnomeVFSFileInfo *file_info,
5012 GnomeVFSFileInfoOptions options,
5013 GnomeVFSContext *context)
5015 GnomeVFSURI *file_uri;
5016 GnomeVFSResult result = GNOME_VFS_OK;
5021 VFOLDER_URI_PARSE (uri, &vuri);
5023 info = get_vfolder_info (vuri.scheme, &result, context);
5027 G_LOCK (vfolder_lock);
5028 file_uri = desktop_uri_to_file_uri (info,
5030 NULL /* the_entry */,
5031 NULL /* the_is_directory_file */,
5033 FALSE /* privatize */,
5036 G_UNLOCK (vfolder_lock);
5038 if (file_uri == NULL &&
5039 result != GNOME_VFS_ERROR_IS_DIRECTORY)
5042 if (file_uri != NULL) {
5043 /* we always get mime-type by forcing it below */
5044 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5045 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5047 result = (* parent_method->get_file_info) (parent_method,
5053 g_free (file_info->mime_type);
5054 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5055 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5057 /* Now we wipe those fields we don't support */
5058 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5060 gnome_vfs_uri_unref (file_uri);
5063 } else if (folder != NULL) {
5064 file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;
5066 file_info->name = g_strdup (folder->entry.name);
5067 GNOME_VFS_FILE_INFO_SET_LOCAL (file_info, TRUE);
5069 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
5070 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
5072 file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
5073 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5075 file_info->ctime = info->modification_time;
5076 file_info->mtime = info->modification_time;
5077 file_info->valid_fields |= (GNOME_VFS_FILE_INFO_FIELDS_CTIME |
5078 GNOME_VFS_FILE_INFO_FIELDS_MTIME);
5080 return GNOME_VFS_OK;
5082 return GNOME_VFS_ERROR_NOT_FOUND;
5086 static GnomeVFSResult
5087 do_get_file_info_from_handle (GnomeVFSMethod *method,
5088 GnomeVFSMethodHandle *method_handle,
5089 GnomeVFSFileInfo *file_info,
5090 GnomeVFSFileInfoOptions options,
5091 GnomeVFSContext *context)
5093 GnomeVFSResult result;
5094 FileHandle *handle = (FileHandle *)method_handle;
5096 if (method_handle == (GnomeVFSMethodHandle *)method) {
5097 g_free (file_info->mime_type);
5098 file_info->mime_type = g_strdup ("text/plain");
5099 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5100 return GNOME_VFS_OK;
5103 /* we always get mime-type by forcing it below */
5104 if (options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
5105 options &= ~GNOME_VFS_FILE_INFO_GET_MIME_TYPE;
5107 result = (* parent_method->get_file_info_from_handle) (parent_method,
5113 /* any file is of the .desktop type */
5114 g_free (file_info->mime_type);
5115 file_info->mime_type = g_strdup ("application/x-gnome-app-info");
5116 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
5118 /* Now we wipe those fields we don't support */
5119 file_info->valid_fields &= ~(UNSUPPORTED_INFO_FIELDS);
5126 do_is_local (GnomeVFSMethod *method,
5127 const GnomeVFSURI *uri)
5133 try_free_folder_monitors_create_unlocked (VFolderInfo *info,
5138 list = g_slist_copy (info->free_folder_monitors);
5140 for (li = list; li != NULL; li = li->next) {
5141 FileMonitorHandle *handle = li->data;
5144 GnomeVFSResult result;
5146 /* Evil! EVIL URI PARSING. this will eat a lot of stack if we
5147 * have lots of free monitors */
5149 VFOLDER_URI_PARSE (handle->uri, &vuri);
5151 f = resolve_folder (info,
5153 FALSE /* ignore_basename */,
5160 info->free_folder_monitors =
5161 g_slist_remove (info->free_folder_monitors, handle);
5162 ((Entry *)folder)->monitors =
5163 g_slist_prepend (((Entry *)folder)->monitors, handle);
5165 handle->exists = TRUE;
5166 gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *)handle,
5168 GNOME_VFS_MONITOR_EVENT_CREATED);
5173 static GnomeVFSResult
5174 do_make_directory (GnomeVFSMethod *method,
5177 GnomeVFSContext *context)
5179 GnomeVFSResult result = GNOME_VFS_OK;
5181 Folder *parent, *folder;
5184 VFOLDER_URI_PARSE (uri, &vuri);
5186 if (vuri.is_all_scheme)
5187 return GNOME_VFS_ERROR_READ_ONLY;
5189 info = get_vfolder_info (vuri.scheme, &result, context);
5193 if (info->user_filename == NULL ||
5195 return GNOME_VFS_ERROR_READ_ONLY;
5197 parent = resolve_folder (info, vuri.path,
5198 TRUE /* ignore_basename */,
5202 else if (parent->read_only)
5203 return GNOME_VFS_ERROR_READ_ONLY;
5205 G_LOCK (vfolder_lock);
5207 folder = (Folder *)find_entry (parent->subfolders,
5209 if (folder != NULL) {
5210 G_UNLOCK (vfolder_lock);
5211 return GNOME_VFS_ERROR_FILE_EXISTS;
5214 folder = folder_new (vuri.file);
5215 parent->subfolders = g_slist_append (parent->subfolders, folder);
5216 folder->parent = parent;
5217 parent->up_to_date = FALSE;
5219 try_free_folder_monitors_create_unlocked (info, folder);
5221 /* parent changed */
5222 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5224 vfolder_info_write_user (info);
5225 G_UNLOCK (vfolder_lock);
5227 return GNOME_VFS_OK;
5230 static GnomeVFSResult
5231 do_remove_directory (GnomeVFSMethod *method,
5233 GnomeVFSContext *context)
5235 GnomeVFSResult result = GNOME_VFS_OK;
5240 VFOLDER_URI_PARSE (uri, &vuri);
5242 if (vuri.is_all_scheme)
5243 return GNOME_VFS_ERROR_READ_ONLY;
5245 info = get_vfolder_info (vuri.scheme, &result, context);
5249 if (info->user_filename == NULL ||
5251 return GNOME_VFS_ERROR_READ_ONLY;
5253 G_LOCK (vfolder_lock);
5255 folder = resolve_folder (info, vuri.path,
5256 FALSE /* ignore_basename */,
5258 if (folder == NULL) {
5259 G_UNLOCK (vfolder_lock);
5263 if (folder->read_only ||
5264 (folder->parent != NULL &&
5265 folder->parent->read_only)) {
5266 G_UNLOCK (vfolder_lock);
5267 return GNOME_VFS_ERROR_READ_ONLY;
5270 /* don't make removing directories easy */
5271 if (folder->desktop_file != NULL) {
5272 G_UNLOCK (vfolder_lock);
5273 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5276 /* Make sure we have the entries */
5277 ensure_folder_unlocked (info, folder,
5278 FALSE /* subfolders */,
5280 FALSE /* ignore_unallocated */);
5282 /* don't make removing directories easy */
5283 if (folder->entries != NULL) {
5284 G_UNLOCK (vfolder_lock);
5285 return GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
5288 emit_and_delete_monitor (info, folder);
5290 if (folder->only_unallocated) {
5291 GSList *li = g_slist_find (info->unallocated_folders,
5294 info->unallocated_folders = g_slist_delete_link
5295 (info->unallocated_folders, li);
5296 entry_unref ((Entry *)folder);
5300 if (folder == info->root) {
5302 entry_unref ((Entry *)folder);
5303 info->root = folder_new ("Root");
5305 Folder *parent = folder->parent;
5307 g_assert (parent != NULL);
5309 parent->subfolders =
5310 g_slist_remove (parent->subfolders, folder);
5312 parent->up_to_date = FALSE;
5314 entry_unref ((Entry *)folder);
5316 /* parent changed */
5317 emit_monitor (parent, GNOME_VFS_MONITOR_EVENT_CHANGED);
5320 vfolder_info_write_user (info);
5322 G_UNLOCK (vfolder_lock);
5324 return GNOME_VFS_OK;
5327 /* a fairly evil function that does the whole move bit by copy and
5329 static GnomeVFSResult
5330 long_move (GnomeVFSMethod *method,
5331 VFolderURI *old_vuri,
5332 VFolderURI *new_vuri,
5333 gboolean force_replace,
5334 GnomeVFSContext *context)
5336 GnomeVFSResult result;
5337 GnomeVFSMethodHandle *handle;
5338 GnomeVFSURI *file_uri;
5345 info = get_vfolder_info (old_vuri->scheme, &result, context);
5349 G_LOCK (vfolder_lock);
5350 file_uri = desktop_uri_to_file_uri (info,
5352 NULL /* the_entry */,
5353 NULL /* the_is_directory_file */,
5354 NULL /* the_folder */,
5355 FALSE /* privatize */,
5358 G_UNLOCK (vfolder_lock);
5360 if (file_uri == NULL)
5363 path = gnome_vfs_uri_get_path (file_uri);
5365 gnome_vfs_uri_unref (file_uri);
5366 return GNOME_VFS_ERROR_INVALID_URI;
5369 fd = open (path, O_RDONLY);
5371 gnome_vfs_uri_unref (file_uri);
5372 return gnome_vfs_result_from_errno ();
5375 gnome_vfs_uri_unref (file_uri);
5377 info->inhibit_write++;
5379 result = method->create (method,
5382 GNOME_VFS_OPEN_WRITE,
5383 force_replace /* exclusive */,
5386 if (result != GNOME_VFS_OK) {
5388 info->inhibit_write--;
5392 while ((bytes = read (fd, buf, BUFSIZ)) > 0) {
5393 GnomeVFSFileSize bytes_written = 0;
5394 result = method->write (method,
5400 if (result == GNOME_VFS_OK &&
5401 bytes_written != bytes)
5402 result = GNOME_VFS_ERROR_NO_SPACE;
5403 if (result != GNOME_VFS_OK) {
5405 method->close (method, handle, context);
5406 /* FIXME: is this completely correct ? */
5407 method->unlink (method,
5410 G_LOCK (vfolder_lock);
5411 info->inhibit_write--;
5412 vfolder_info_write_user (info);
5413 G_UNLOCK (vfolder_lock);
5420 result = method->close (method, handle, context);
5421 if (result != GNOME_VFS_OK) {
5422 G_LOCK (vfolder_lock);
5423 info->inhibit_write--;
5424 vfolder_info_write_user (info);
5425 G_UNLOCK (vfolder_lock);
5429 result = method->unlink (method, old_vuri->uri, context);
5431 G_LOCK (vfolder_lock);
5432 info->inhibit_write--;
5433 vfolder_info_write_user (info);
5434 G_UNLOCK (vfolder_lock);
5439 static GnomeVFSResult
5440 move_directory_file (VFolderInfo *info,
5444 if (old_folder->desktop_file == NULL)
5445 return GNOME_VFS_ERROR_NOT_FOUND;
5447 /* "move" the desktop file */
5448 g_free (new_folder->desktop_file);
5449 new_folder->desktop_file = old_folder->desktop_file;
5450 old_folder->desktop_file = NULL;
5452 /* is this too drastic, it will requery the folder? */
5453 new_folder->up_to_date = FALSE;
5454 old_folder->up_to_date = FALSE;
5456 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5457 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5459 vfolder_info_write_user (info);
5461 return GNOME_VFS_OK;
5465 is_sub (Folder *master, Folder *sub)
5469 for (li = master->subfolders; li != NULL; li = li->next) {
5470 Folder *subfolder = li->data;
5472 if (subfolder == sub ||
5473 is_sub (subfolder, sub))
5480 static GnomeVFSResult
5481 move_folder (VFolderInfo *info,
5482 Folder *old_folder, Entry *old_entry,
5483 Folder *new_folder, Entry *new_entry)
5485 Folder *source = (Folder *)old_entry;
5488 if (new_entry != NULL &&
5489 new_entry->type != ENTRY_FOLDER)
5490 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
5492 if (new_entry != NULL) {
5493 target = (Folder *)new_entry;
5495 target = new_folder;
5498 /* move to where we are, yay, we're done :) */
5499 if (source->parent == target)
5500 return GNOME_VFS_OK;
5502 if (source == target ||
5503 is_sub (source, target))
5504 return GNOME_VFS_ERROR_LOOP;
5506 /* this will never happen, but we're paranoid */
5507 if (source->parent == NULL)
5508 return GNOME_VFS_ERROR_LOOP;
5510 source->parent->subfolders = g_slist_remove (source->parent->subfolders,
5512 target->subfolders = g_slist_append (target->subfolders,
5515 source->parent = target;
5517 source->up_to_date = FALSE;
5518 target->up_to_date = FALSE;
5520 emit_monitor (source, GNOME_VFS_MONITOR_EVENT_CHANGED);
5521 emit_monitor (target, GNOME_VFS_MONITOR_EVENT_CHANGED);
5523 vfolder_info_write_user (info);
5525 return GNOME_VFS_OK;
5528 static GnomeVFSResult
5529 do_move (GnomeVFSMethod *method,
5530 GnomeVFSURI *old_uri,
5531 GnomeVFSURI *new_uri,
5532 gboolean force_replace,
5533 GnomeVFSContext *context)
5535 GnomeVFSResult result = GNOME_VFS_OK;
5537 Folder *old_folder, *new_folder;
5538 Entry *old_entry, *new_entry;
5539 gboolean old_is_directory_file, new_is_directory_file;
5540 VFolderURI old_vuri, new_vuri;
5542 VFOLDER_URI_PARSE (old_uri, &old_vuri);
5543 VFOLDER_URI_PARSE (new_uri, &new_vuri);
5545 if (old_vuri.file == NULL)
5546 return GNOME_VFS_ERROR_INVALID_URI;
5548 if (old_vuri.is_all_scheme)
5549 return GNOME_VFS_ERROR_READ_ONLY;
5551 if (strcmp (old_vuri.scheme, new_vuri.scheme) != 0)
5552 return GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
5554 info = get_vfolder_info (old_vuri.scheme, &result, context);
5558 if (info->read_only)
5559 return GNOME_VFS_ERROR_READ_ONLY;
5561 old_entry = get_entry (&old_vuri,
5563 &old_is_directory_file,
5566 if (old_entry == NULL)
5569 if (old_folder != NULL && old_folder->read_only)
5570 return GNOME_VFS_ERROR_READ_ONLY;
5572 new_entry = get_entry (&new_vuri,
5574 &new_is_directory_file,
5577 if (new_entry == NULL && new_folder == NULL)
5580 if (new_folder != NULL && new_folder->read_only)
5581 return GNOME_VFS_ERROR_READ_ONLY;
5583 if (new_is_directory_file != old_is_directory_file) {
5584 /* this will do another set of lookups
5585 * perhaps this can be done in a nicer way,
5586 * but is this the common case? I don't think so */
5587 return long_move (method, &old_vuri, &new_vuri,
5588 force_replace, context);
5591 if (new_is_directory_file) {
5592 g_assert (old_entry != NULL);
5593 g_assert (new_entry != NULL);
5594 G_LOCK (vfolder_lock);
5595 result = move_directory_file (info,
5596 (Folder *)old_entry,
5597 (Folder *)new_entry);
5598 G_UNLOCK (vfolder_lock);
5602 if (old_entry->type == ENTRY_FOLDER) {
5603 G_LOCK (vfolder_lock);
5604 result = move_folder (info,
5605 old_folder, old_entry,
5606 new_folder, new_entry);
5607 G_UNLOCK (vfolder_lock);
5611 /* move into self, just whack the old one */
5612 if (old_entry == new_entry) {
5614 if (new_folder == old_folder)
5615 return GNOME_VFS_OK;
5617 if ( ! force_replace)
5618 return GNOME_VFS_ERROR_FILE_EXISTS;
5620 G_LOCK (vfolder_lock);
5622 remove_file (old_folder, old_vuri.file);
5624 old_folder->entries = g_slist_remove (old_folder->entries,
5626 entry_unref (old_entry);
5628 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5630 vfolder_info_write_user (info);
5632 G_UNLOCK (vfolder_lock);
5634 return GNOME_VFS_OK;
5637 /* this is a simple move */
5638 if (new_entry == NULL ||
5639 new_entry->type == ENTRY_FOLDER) {
5640 if (new_entry != NULL) {
5641 new_folder = (Folder *)new_entry;
5643 /* a file and a totally different one */
5644 if (strcmp (new_vuri.file, old_entry->name) != 0) {
5645 /* yay, a long move */
5646 /* this will do another set of lookups
5647 * perhaps this can be done in a nicer way,
5648 * but is this the common case? I don't think
5650 return long_move (method, &old_vuri, &new_vuri,
5651 force_replace, context);
5656 if (new_folder == old_folder)
5657 return GNOME_VFS_OK;
5659 G_LOCK (vfolder_lock);
5661 remove_file (old_folder, old_entry->name);
5662 add_file (new_folder, old_entry->name);
5664 new_folder->entries = g_slist_prepend (new_folder->entries,
5666 entry_ref (old_entry);
5667 new_folder->sorted = FALSE;
5669 old_folder->entries = g_slist_remove (old_folder->entries,
5671 entry_unref (old_entry);
5673 emit_monitor (new_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5674 emit_monitor (old_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5676 vfolder_info_write_user (info);
5678 G_UNLOCK (vfolder_lock);
5680 return GNOME_VFS_OK;
5683 /* do we EVER get here? */
5685 /* this will do another set of lookups
5686 * perhaps this can be done in a nicer way,
5687 * but is this the common case? I don't think so */
5688 return long_move (method, &old_vuri, &new_vuri,
5689 force_replace, context);
5692 static GnomeVFSResult
5693 do_unlink (GnomeVFSMethod *method,
5695 GnomeVFSContext *context)
5697 GnomeVFSResult result = GNOME_VFS_OK;
5700 gboolean is_directory_file;
5705 VFOLDER_URI_PARSE (uri, &vuri);
5707 if (vuri.file == NULL)
5708 return GNOME_VFS_ERROR_INVALID_URI;
5710 if (vuri.is_all_scheme == TRUE)
5711 return GNOME_VFS_ERROR_READ_ONLY;
5713 info = get_vfolder_info (vuri.scheme, &result, context);
5716 else if (info->read_only)
5717 return GNOME_VFS_ERROR_READ_ONLY;
5719 entry = get_entry (&vuri,
5725 else if (the_folder != NULL &&
5726 the_folder->read_only)
5727 return GNOME_VFS_ERROR_READ_ONLY;
5729 if (entry->type == ENTRY_FOLDER &&
5730 is_directory_file) {
5731 Folder *folder = (Folder *)entry;
5733 if (folder->desktop_file == NULL)
5734 return GNOME_VFS_ERROR_NOT_FOUND;
5736 G_LOCK (vfolder_lock);
5738 g_free (folder->desktop_file);
5739 folder->desktop_file = NULL;
5741 emit_monitor (folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5743 vfolder_info_write_user (info);
5745 G_UNLOCK (vfolder_lock);
5747 return GNOME_VFS_OK;
5748 } else if (entry->type == ENTRY_FOLDER) {
5749 return GNOME_VFS_ERROR_IS_DIRECTORY;
5750 } else if (the_folder == NULL) {
5751 return GNOME_VFS_ERROR_NOT_FOUND;
5754 G_LOCK (vfolder_lock);
5756 the_folder->entries = g_slist_remove (the_folder->entries,
5758 entry_unref (entry);
5760 remove_file (the_folder, vuri.file);
5762 emit_monitor (the_folder, GNOME_VFS_MONITOR_EVENT_CHANGED);
5764 /* evil, we must remove this from the unallocated folders as well
5765 * so that it magically doesn't appear there. But it's not so simple.
5766 * We only want to remove it if it isn't in that folder already. */
5767 for (li = info->unallocated_folders;
5770 Folder *folder = li->data;
5773 /* This is actually really evil since ensuring
5774 * an unallocated folder clears all other unallocated
5775 * folders in it's wake. I'm not sure it's worth
5776 * optimizing however */
5777 ensure_folder_unlocked (info, folder,
5778 FALSE /* subfolders */,
5780 FALSE /* ignore_unallocated */);
5781 l = g_slist_find (folder->entries, entry);
5783 remove_file (folder, vuri.file);
5787 emit_file_deleted_monitor (info, entry, the_folder);
5789 /* FIXME: if this was a user file and this is the only
5790 * reference to it, unlink it. */
5792 vfolder_info_write_user (info);
5794 G_UNLOCK (vfolder_lock);
5796 return GNOME_VFS_OK;
5799 static GnomeVFSResult
5800 do_check_same_fs (GnomeVFSMethod *method,
5801 GnomeVFSURI *source_uri,
5802 GnomeVFSURI *target_uri,
5803 gboolean *same_fs_return,
5804 GnomeVFSContext *context)
5806 VFolderURI source_vuri, target_vuri;
5808 *same_fs_return = FALSE;
5810 VFOLDER_URI_PARSE (source_uri, &source_vuri);
5811 VFOLDER_URI_PARSE (target_uri, &target_vuri);
5813 if (strcmp (source_vuri.scheme, target_vuri.scheme) != 0 ||
5814 source_vuri.is_all_scheme != target_vuri.is_all_scheme)
5815 *same_fs_return = FALSE;
5817 *same_fs_return = TRUE;
5819 return GNOME_VFS_OK;
5822 static GnomeVFSResult
5823 do_set_file_info (GnomeVFSMethod *method,
5825 const GnomeVFSFileInfo *info,
5826 GnomeVFSSetFileInfoMask mask,
5827 GnomeVFSContext *context)
5831 VFOLDER_URI_PARSE (uri, &vuri);
5833 if (vuri.file == NULL)
5834 return GNOME_VFS_ERROR_INVALID_URI;
5836 if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
5837 GnomeVFSResult result = GNOME_VFS_OK;
5838 char *dirname = gnome_vfs_uri_extract_dirname (uri);
5839 GnomeVFSURI *new_uri = gnome_vfs_uri_dup (uri);
5841 G_LOCK (vfolder_lock);
5842 g_free (new_uri->text);
5843 new_uri->text = g_build_path ("/", dirname, info->name, NULL);
5844 G_UNLOCK (vfolder_lock);
5846 result = do_move (method,
5849 FALSE /* force_replace */,
5853 gnome_vfs_uri_unref (new_uri);
5856 /* We don't support setting any of this other permission,
5857 * times and all that voodoo */
5858 return GNOME_VFS_ERROR_NOT_SUPPORTED;
5862 static GnomeVFSResult
5863 do_monitor_add (GnomeVFSMethod *method,
5864 GnomeVFSMethodHandle **method_handle_return,
5866 GnomeVFSMonitorType monitor_type)
5870 GnomeVFSResult result;
5873 GnomeVFSURI *file_uri;
5874 FileMonitorHandle *handle;
5875 gboolean is_directory_file;
5877 VFOLDER_URI_PARSE (uri, &vuri);
5879 info = get_vfolder_info (vuri.scheme, &result, NULL);
5883 if (monitor_type == GNOME_VFS_MONITOR_DIRECTORY) {
5884 G_LOCK (vfolder_lock);
5886 folder = resolve_folder (info,
5888 FALSE /* ignore_basename */,
5892 handle = g_new0 (FileMonitorHandle, 1);
5893 handle->refcount = 2;
5894 handle->uri = gnome_vfs_uri_dup (uri);
5895 handle->dir_monitor = TRUE;
5896 handle->handle = NULL;
5897 handle->filename = NULL;
5899 if (folder == NULL) {
5900 handle->exists = FALSE;
5901 info->free_folder_monitors =
5902 g_slist_prepend (info->free_folder_monitors,
5905 handle->exists = TRUE;
5906 ((Entry *)folder)->monitors =
5907 g_slist_prepend (((Entry *)folder)->monitors,
5911 info->folder_monitors =
5912 g_slist_prepend (info->folder_monitors, handle);
5914 G_UNLOCK (vfolder_lock);
5916 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5918 return GNOME_VFS_OK;
5920 /* These can't be very nice FILE names */
5921 if (vuri.file == NULL ||
5923 return GNOME_VFS_ERROR_INVALID_URI;
5925 G_LOCK (vfolder_lock);
5926 file_uri = desktop_uri_to_file_uri (info,
5930 NULL /* the_folder */,
5935 handle = g_new0 (FileMonitorHandle, 1);
5936 handle->refcount = 2;
5937 handle->uri = gnome_vfs_uri_dup (uri);
5938 handle->dir_monitor = FALSE;
5939 handle->handle = NULL;
5940 handle->filename = g_strdup (vuri.file);
5941 handle->is_directory_file = is_directory_file;
5943 info->file_monitors =
5944 g_slist_prepend (info->file_monitors, handle);
5947 if (file_uri == NULL) {
5948 handle->exists = FALSE;
5949 info->free_file_monitors =
5950 g_slist_prepend (info->free_file_monitors,
5953 char *uri_string = gnome_vfs_uri_to_string (file_uri, 0);
5954 handle->exists = TRUE;
5955 gnome_vfs_monitor_add (&(handle->handle),
5957 GNOME_VFS_MONITOR_FILE,
5960 g_free (uri_string);
5962 entry->monitors = g_slist_prepend (entry->monitors,
5964 gnome_vfs_uri_unref (file_uri);
5967 *method_handle_return = (GnomeVFSMethodHandle *) handle;
5969 G_UNLOCK (vfolder_lock);
5971 return GNOME_VFS_OK;
5975 static GnomeVFSResult
5976 do_monitor_cancel (GnomeVFSMethod *method,
5977 GnomeVFSMethodHandle *method_handle)
5979 FileMonitorHandle *handle;
5982 GnomeVFSResult result;
5986 handle = (FileMonitorHandle *)method_handle;
5988 /* FIXME: is this correct? */
5989 if (method_handle == NULL)
5990 return GNOME_VFS_OK;
5992 VFOLDER_URI_PARSE (handle->uri, &vuri);
5994 info = get_vfolder_info (vuri.scheme, &result, NULL);
5998 if (handle->dir_monitor) {
5999 G_LOCK (vfolder_lock);
6001 folder = resolve_folder (info,
6003 FALSE /* ignore_basename */,
6007 for (li = info->folder_monitors; li != NULL; li = li->next) {
6008 FileMonitorHandle *h = li->data;
6011 info->folder_monitors = g_slist_delete_link
6012 (info->folder_monitors, li);
6013 file_monitor_handle_unref_unlocked (h);
6017 if (folder == NULL) {
6018 for (li = info->free_folder_monitors;
6021 FileMonitorHandle *h = li->data;
6024 info->free_folder_monitors = g_slist_delete_link
6025 (info->free_folder_monitors, li);
6026 file_monitor_handle_unref_unlocked (h);
6030 for (li = ((Entry *)folder)->monitors;
6033 FileMonitorHandle *h = li->data;
6036 ((Entry *)folder)->monitors =
6038 (((Entry *)folder)->monitors, li);
6039 file_monitor_handle_unref_unlocked (h);
6044 G_UNLOCK (vfolder_lock);
6046 return GNOME_VFS_OK;
6048 G_LOCK (vfolder_lock);
6050 for (li = info->file_monitors; li != NULL; li = li->next) {
6051 FileMonitorHandle *h = li->data;
6054 info->file_monitors = g_slist_delete_link
6055 (info->file_monitors, li);
6056 file_monitor_handle_unref_unlocked (h);
6060 for (li = info->free_file_monitors;
6063 FileMonitorHandle *h = li->data;
6066 info->free_file_monitors = g_slist_delete_link
6067 (info->free_file_monitors, li);
6068 file_monitor_handle_unref_unlocked (h);
6072 for (li = info->entries; li != NULL; li = li->next) {
6073 Entry *e = li->data;
6074 GSList *link = g_slist_find (e->monitors, handle);
6079 e->monitors = g_slist_delete_link (e->monitors, link);
6081 file_monitor_handle_unref_unlocked (handle);
6085 G_UNLOCK (vfolder_lock);
6087 /* Note: last unref of our monitor will cancel the
6088 * underlying handle */
6090 return GNOME_VFS_OK;
6095 /* gnome-vfs bureaucracy */
6097 static GnomeVFSMethod method = {
6098 sizeof (GnomeVFSMethod),
6100 NULL, /* do_create, */
6103 NULL, /* do_write, */
6106 NULL, /* do_truncate_handle, */
6111 do_get_file_info_from_handle,
6113 NULL, /* do_make_directory, */
6114 NULL, /* do_remove_directory, */
6115 NULL, /* do_move, */
6116 NULL, /* do_unlink, */
6118 NULL, /* do_set_file_info, */
6119 NULL, /* do_truncate, */
6120 NULL /* find_directory */,
6121 NULL /* create_symbolic_link */,
6127 vfs_module_init (const char *method_name,
6130 parent_method = gnome_vfs_method_get ("file");
6132 if (parent_method == NULL) {
6133 g_error ("Could not find 'file' method for gnome-vfs");
6141 vfs_module_shutdown (GnomeVFSMethod *method)
6146 g_hash_table_destroy (infos);