ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / vfolder / vfolder-common.c.vfolder-hacks
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* 
3  * vfolder-common.c - Implementation of abstract Folder, Entry, and Query 
4  *                    interfaces.
5  *
6  * Copyright (C) 2002 Ximian, Inc.
7  *
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.
12  *
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.
17  *
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.
22  *
23  * Author: Alex Graveley <alex@ximian.com>
24  *         Based on original code by George Lebl <jirka@5z.com>.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <string.h>
32 #include <sys/time.h>
33
34 #include <glib.h>
35 #include <libgnomevfs/gnome-vfs-directory.h>
36 #include <libgnomevfs/gnome-vfs-ops.h>
37 #include <libgnomevfs/gnome-vfs-xfer.h>
38
39 #include "vfolder-common.h"
40
41 \f
42 /* 
43  * Entry Implementation
44  */
45 Entry *
46 entry_new (VFolderInfo *info, 
47            const gchar *filename, 
48            const gchar *displayname, 
49            gboolean     user_private,
50            gushort      weight)
51 {
52         Entry *entry;
53
54         entry = g_new0 (Entry, 1);
55         entry->refcnt = 1;
56         entry->allocs = 0;
57         entry->info = info;
58         entry->filename = g_strdup (filename);
59         entry->displayname = g_strdup (displayname);
60         entry->user_private = user_private;
61         entry->weight = weight;
62
63         entry->dirty = TRUE;
64
65         /* 
66          * Lame-O special case .directory handling, as we don't want them
67          * showing up for all-applications:///.
68          */
69         if (strcmp (displayname, ".directory") != 0)
70                 vfolder_info_add_entry (info, entry);
71
72         return entry;
73 }
74
75 void 
76 entry_ref (Entry *entry)
77 {
78         entry->refcnt++;
79 }
80
81 void 
82 entry_unref (Entry *entry)
83 {
84         entry->refcnt--;
85
86         if (entry->refcnt == 0) {
87                 D (g_print ("-- KILLING ENTRY: (%p) %s---\n",
88                             entry,
89                             entry->displayname));
90
91                 vfolder_info_remove_entry (entry->info, entry);
92
93                 g_free (entry->filename);
94                 g_free (entry->displayname);
95                 g_slist_free (entry->keywords);
96                 g_slist_free (entry->implicit_keywords);
97                 g_free (entry);
98         }
99 }
100
101 void
102 entry_alloc (Entry *entry)
103 {
104         entry->allocs++;
105 }
106
107 void
108 entry_dealloc (Entry *entry)
109 {
110         entry->allocs--;
111 }
112
113 gboolean 
114 entry_is_allocated (Entry *entry)
115 {
116         return entry->allocs > 0;
117 }
118
119 gboolean
120 entry_make_user_private (Entry *entry, Folder *folder)
121 {
122         GnomeVFSURI *src_uri, *dest_uri;
123         GnomeVFSResult result;
124         gchar *uniqname, *filename;
125
126         if (entry->user_private)
127                 return TRUE;
128
129         /* Don't write privately if folder is link */
130         if (folder->is_link)
131                 return TRUE;
132
133         /* Need a writedir, otherwise just modify the original */
134         if (!entry->info->write_dir)
135                 return TRUE;
136
137         /* Need a filename to progress further */
138         if (!entry_get_filename (entry))
139                 return FALSE;
140
141         /* Make sure the destination directory exists */
142         result = vfolder_make_directory_and_parents (entry->info->write_dir, 
143                                                      FALSE, 
144                                                      0700);
145         if (result != GNOME_VFS_OK)
146                 return FALSE;
147
148         /* 
149          * Add a timestamp to the filename since we don't want conflicts between
150          * files in different logical folders with the same filename.
151          */
152         uniqname = vfolder_timestamp_file_name (entry_get_displayname (entry));
153         filename = vfolder_build_uri (entry->info->write_dir, uniqname, NULL);
154         g_free (uniqname);
155
156         src_uri = entry_get_real_uri (entry);
157         dest_uri = gnome_vfs_uri_new (filename);
158
159         result = gnome_vfs_xfer_uri (src_uri, 
160                                      dest_uri, 
161                                      GNOME_VFS_XFER_USE_UNIQUE_NAMES, 
162                                      GNOME_VFS_XFER_ERROR_MODE_ABORT, 
163                                      GNOME_VFS_XFER_OVERWRITE_MODE_ABORT, 
164                                      NULL, 
165                                      NULL);
166
167         gnome_vfs_uri_unref (src_uri);
168         gnome_vfs_uri_unref (dest_uri);
169
170         if (result == GNOME_VFS_OK) {
171                 if (!strcmp (entry_get_displayname (entry), ".directory")) {
172                         folder_set_desktop_file (folder, filename);
173                 } else {
174                         /* Exclude current displayname. */
175                         folder_add_exclude (folder, 
176                                             entry_get_displayname (entry));
177                         /* Remove include for current filename. */
178                         folder_remove_include (folder, 
179                                                entry_get_filename (entry));
180                         /* Add include for new private filename. */
181                         folder_add_include (folder, filename);
182                 }
183
184                 entry_set_filename (entry, filename);
185                 entry_set_weight (entry, 1000);
186                 entry->user_private = TRUE;
187         }
188
189         g_free (filename);
190         
191         return result == GNOME_VFS_OK;
192 }
193
194 gboolean
195 entry_is_user_private (Entry *entry)
196 {
197         return entry->user_private;
198 }
199
200 static void
201 entry_reload_if_needed (Entry *entry)
202 {
203         gboolean changed = FALSE;
204         gchar *keywords, *deprecates;
205         int i;
206
207         if (!entry->dirty)
208                 return;
209
210         entry_quick_read_keys (entry, 
211                                "Categories",
212                                &keywords,
213                                "Deprecates",
214                                &deprecates);
215
216         /* 
217          * Clear keywords from file, leaving only ones added from 
218          * the directory.
219          */
220         g_slist_free (entry->keywords);
221         entry->keywords = g_slist_copy (entry->implicit_keywords);
222
223         if (keywords) {
224                 char **parsed = g_strsplit (keywords, ";", -1);
225                 GSList *keylist = entry->keywords;
226
227                 for (i = 0; parsed[i] != NULL; i++) {
228                         GQuark quark;
229                         const char *word = parsed[i];
230
231                         /* ignore empties (including end of list) */
232                         if (word[0] == '\0')
233                                 continue;
234
235                         quark = g_quark_from_string (word);
236                         if (g_slist_find (keylist, GINT_TO_POINTER (quark)))
237                                 continue;
238
239                         D (g_print ("ADDING KEYWORD: %s, %s\n", 
240                                     entry_get_displayname (entry),
241                                     word));
242
243                         entry->keywords = 
244                                 g_slist_prepend (entry->keywords, 
245                                                  GINT_TO_POINTER (quark));
246                         changed = TRUE;
247                 }
248                 g_strfreev (parsed);
249         }
250
251         /* FIXME: Support this */
252         if (deprecates) {
253                 char **parsed = g_strsplit (keywords, ";", -1);
254                 Entry *dep;
255
256                 for (i = 0; parsed[i] != NULL; i++) {
257                         dep = vfolder_info_lookup_entry (entry->info, 
258                                                          parsed[i]);
259                         if (dep) {
260                                 vfolder_info_remove_entry (entry->info, dep);
261 #if 0 /* vfolder_monitor_emit is not defined */
262                                 vfolder_monitor_emit (
263                                         entry_get_filename (dep),
264                                         GNOME_VFS_MONITOR_EVENT_DELETED);
265 #endif
266                                 entry_unref (dep);
267                         }
268                 }
269                 g_strfreev (parsed);
270         }
271
272         g_free (keywords);
273         g_free (deprecates);
274
275         entry->dirty = FALSE;
276 }
277
278 gushort 
279 entry_get_weight (Entry *entry)
280 {
281         return entry->weight;
282 }
283
284 void
285 entry_set_weight (Entry *entry, gushort weight)
286 {
287         entry->weight = weight;
288 }
289
290 void
291 entry_set_dirty (Entry *entry)
292 {
293         entry->dirty = TRUE;
294 }
295
296 void          
297 entry_set_filename (Entry *entry, const gchar *name)
298 {
299         g_free (entry->filename);
300         entry->filename = g_strdup (name);
301
302         if (entry->uri) {
303                 gnome_vfs_uri_unref (entry->uri);
304                 entry->uri = NULL;
305         }
306
307         entry_set_dirty (entry);
308 }
309
310 const gchar *
311 entry_get_filename (Entry *entry)
312 {
313         return entry->filename;
314 }
315
316 void
317 entry_set_displayname (Entry *entry, const gchar *name)
318 {
319         g_free (entry->displayname);
320         entry->displayname = g_strdup (name);
321 }
322
323 const gchar *
324 entry_get_displayname (Entry *entry)
325 {
326         return entry->displayname;
327 }
328
329 GnomeVFSURI *
330 entry_get_real_uri (Entry *entry)
331 {
332         if (!entry->filename)
333                 return NULL; 
334
335         if (!entry->uri)
336                 entry->uri = gnome_vfs_uri_new (entry->filename);
337
338         gnome_vfs_uri_ref (entry->uri);
339         return entry->uri;
340 }
341
342 const GSList *
343 entry_get_keywords (Entry *entry)
344 {
345         entry_reload_if_needed (entry);
346         return entry->keywords;
347 }
348
349 void 
350 entry_add_implicit_keyword (Entry *entry, GQuark keyword)
351 {
352         entry->keywords = g_slist_prepend (entry->keywords, 
353                                            GINT_TO_POINTER (keyword));
354         entry->implicit_keywords = g_slist_prepend (entry->implicit_keywords, 
355                                                     GINT_TO_POINTER (keyword));
356 }
357
358 static void
359 entry_key_val_from_string (gchar *src, const gchar *key, gchar **result)
360 {
361         gchar *start;
362         gint keylen = strlen (key), end;
363
364         *result = NULL;
365
366         start = strstr (src, key);
367         if (start && 
368             (start == src || (*(start-1) == '\r') || (*(start-1) == '\n')) &&
369             ((*(start+keylen) == ' ') || (*(start+keylen) == '='))) {
370                 start += keylen;
371                 start += strspn (start, "= ");
372                 end = strcspn (start, "\r\n");
373                 if (end > 0)
374                         *result = g_strndup (start, end);
375         }
376 }
377
378 void 
379 entry_quick_read_keys (Entry  *entry,
380                        const gchar  *key1,
381                        gchar       **result1,
382                        const gchar  *key2,
383                        gchar       **result2)
384 {
385         GnomeVFSHandle *handle;
386         GnomeVFSFileSize readlen;
387         GString *fullbuf;
388         char buf[2048];
389
390         *result1 = NULL;
391         if (key2)
392           *result2 = NULL;
393
394         if (gnome_vfs_open (&handle, 
395                             entry_get_filename (entry), 
396                             GNOME_VFS_OPEN_READ) != GNOME_VFS_OK)
397                 return;
398
399         fullbuf = g_string_new (NULL);
400         while (gnome_vfs_read (handle, 
401                                buf, 
402                                sizeof (buf), 
403                                &readlen) == GNOME_VFS_OK) {
404                 g_string_append_len (fullbuf, buf, readlen);
405         }
406
407         gnome_vfs_close (handle);
408
409         if (!fullbuf->len) {
410                 g_string_free (fullbuf, TRUE);
411                 return;
412         }
413
414         entry_key_val_from_string (fullbuf->str, key1, result1);
415
416         if (key2)
417                 entry_key_val_from_string (fullbuf->str, key2, result2);
418
419         g_string_free (fullbuf, TRUE);
420 }
421
422 void
423 entry_dump (Entry *entry, int indent)
424 {
425         gchar *space = g_strnfill (indent, ' ');
426         GSList *keywords = entry->keywords, *iter;
427
428         D (g_print ("%s%s\n%s  Filename: %s\n%s  Keywords: ",
429                     space,
430                     entry_get_displayname (entry),
431                     space,
432                     entry_get_filename (entry),
433                     space));
434
435         for (iter = keywords; iter; iter = iter->next) {
436                 G_GNUC_UNUSED GQuark quark = GPOINTER_TO_INT (iter->data);
437                 D (g_print (g_quark_to_string (quark)));
438         }
439
440         D (g_print ("\n"));
441
442         g_free (space);
443 }
444
445
446 \f
447 /* 
448  * Folder Implementation
449  */
450 Folder *
451 folder_new (VFolderInfo *info, const gchar *name, gboolean user_private)
452 {
453         Folder *folder = g_new0 (Folder, 1);
454
455         folder->name         = g_strdup (name);
456         folder->user_private = user_private;
457         folder->info         = info;
458         folder->refcnt       = 1;
459
460         folder->dirty = TRUE;
461
462         return folder;
463 }
464
465 void 
466 folder_ref (Folder *folder)
467 {
468         folder->refcnt++;
469 }
470
471 static void
472 unalloc_exclude (gpointer key, gpointer val, gpointer user_data)
473 {
474         gchar *filename = key;
475         VFolderInfo *info = user_data;
476         Entry *entry;
477
478         /* Skip excludes which probably from the parent URI */
479         if (strchr (filename, '/'))
480                 return;
481
482         entry = vfolder_info_lookup_entry (info, filename);
483         if (entry)
484                 entry_dealloc (entry);
485 }
486
487 static void
488 folder_reset_entries (Folder *folder)
489 {
490         /* entries */
491         g_slist_foreach (folder->entries, (GFunc) entry_dealloc, NULL);
492         g_slist_foreach (folder->entries, (GFunc) entry_unref, NULL);
493         g_slist_free (folder->entries);
494         folder->entries = NULL;
495
496         if (folder->entries_ht) {
497                 g_hash_table_destroy (folder->entries_ht);
498                 folder->entries_ht = NULL;
499         }
500 }
501
502 void
503 folder_unref (Folder *folder)
504 {
505         folder->refcnt--;
506
507         if (folder->refcnt == 0) {
508                 D (g_print ("DESTORYING FOLDER: %p, %s\n", 
509                             folder, 
510                             folder->name));
511
512                 g_free (folder->name);
513                 g_free (folder->extend_uri);
514                 g_free (folder->desktop_file);
515
516                 if (folder->extend_monitor)
517                         vfolder_monitor_cancel (folder->extend_monitor);
518
519                 query_free (folder->query);
520
521                 if (folder->excludes) {
522                         g_hash_table_foreach (folder->excludes, 
523                                               (GHFunc) unalloc_exclude,
524                                               folder->info);                    
525                         g_hash_table_destroy (folder->excludes);
526                 }
527
528                 g_slist_foreach (folder->includes, (GFunc) g_free, NULL);
529                 g_slist_free (folder->includes);
530
531                 /* subfolders */
532                 g_slist_foreach (folder->subfolders, 
533                                  (GFunc) folder_unref, 
534                                  NULL);
535                 g_slist_free (folder->subfolders);
536
537                 if (folder->subfolders_ht)
538                         g_hash_table_destroy (folder->subfolders_ht);
539
540                 folder_reset_entries (folder);
541
542                 g_free (folder);
543         }
544 }
545
546 static gboolean read_one_extended_entry (Folder           *folder, 
547                                          const gchar      *file_uri, 
548                                          GnomeVFSFileInfo *file_info);
549
550 static void
551 folder_extend_monitor_cb (GnomeVFSMonitorHandle    *handle,
552                           const gchar              *monitor_uri,
553                           const gchar              *info_uri,
554                           GnomeVFSMonitorEventType  event_type,
555                           gpointer                  user_data)
556 {
557         Folder *folder = user_data;
558         FolderChild child;
559         GnomeVFSFileInfo *file_info;
560         GnomeVFSResult result;
561         GnomeVFSURI *uri, *entry_uri;
562         gchar *filename;
563
564         /* Operating on the whole directory, ignore */
565         if (!strcmp (monitor_uri, info_uri))
566                 return;
567
568         D (g_print ("*** Exdended folder %s ('%s') monitor %s%s%s called! ***\n",
569                     folder->name,
570                     info_uri,
571                     event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED":"",
572                     event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED":"",
573                     event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED":""));
574
575         uri = gnome_vfs_uri_new (info_uri);
576         filename = gnome_vfs_uri_extract_short_name (uri);
577
578         VFOLDER_INFO_WRITE_LOCK (folder->info);
579
580         switch (event_type) {
581         case GNOME_VFS_MONITOR_EVENT_CHANGED:
582                 /* 
583                  * We only care about entries here, as the extend_monitor_cb on
584                  * the subfolders themselves should take care of emitting
585                  * changes.
586                  */
587                 child.entry = folder_get_entry (folder, filename);
588                 if (child.entry) {
589                         entry_uri = entry_get_real_uri (child.entry);
590
591                         if (gnome_vfs_uri_equal (entry_uri, uri)) {
592                                 entry_set_dirty (child.entry);
593                                 folder_emit_changed (
594                                         folder, 
595                                         entry_get_displayname (child.entry),
596                                         GNOME_VFS_MONITOR_EVENT_CHANGED);
597                         }
598
599                         gnome_vfs_uri_unref (entry_uri);
600                 }
601                 break;
602         case GNOME_VFS_MONITOR_EVENT_DELETED:
603                 folder_get_child (folder, filename, &child);
604
605                 /* 
606                  * FIXME: should look for replacement in info's entry
607                  * pool here, before sending event 
608                  */
609
610                 if (child.type == DESKTOP_FILE) {
611                         entry_uri = entry_get_real_uri (child.entry);
612
613                         if (gnome_vfs_uri_equal (uri, entry_uri)) {
614                                 folder_remove_entry (folder, child.entry);
615                                 folder_emit_changed (
616                                         folder, 
617                                         filename,
618                                         GNOME_VFS_MONITOR_EVENT_DELETED);
619                         }
620
621                         gnome_vfs_uri_unref (entry_uri);
622                 } 
623                 else if (child.type == FOLDER) {
624                         if (folder_is_user_private (child.folder)) {
625                                 folder_set_dirty (child.folder);
626                         } else {
627                                 folder_remove_subfolder (folder, child.folder);
628                                 folder_emit_changed (
629                                         folder, 
630                                         filename,
631                                         GNOME_VFS_MONITOR_EVENT_DELETED);
632                         }
633                 }
634                 break;
635         case GNOME_VFS_MONITOR_EVENT_CREATED:
636                 file_info = gnome_vfs_file_info_new ();
637                 result = 
638                         gnome_vfs_get_file_info_uri (
639                                 uri,
640                                 file_info,
641                                 GNOME_VFS_FILE_INFO_DEFAULT);
642
643                 if (result == GNOME_VFS_OK &&
644                     read_one_extended_entry (folder, info_uri, file_info))
645                         folder_emit_changed (folder, 
646                                              file_info->name,
647                                              GNOME_VFS_MONITOR_EVENT_CREATED);
648
649                 gnome_vfs_file_info_unref (file_info);
650                 break;
651         default:
652                 break;
653         }
654
655         folder->info->modification_time = time (NULL);
656
657         VFOLDER_INFO_WRITE_UNLOCK (folder->info);
658
659         gnome_vfs_uri_unref (uri);
660         g_free (filename);
661 }
662
663 gboolean
664 folder_make_user_private (Folder *folder)
665 {       
666         if (folder->user_private)
667                 return TRUE;
668
669         if (folder->parent) {
670                 if (folder->parent->read_only ||
671                     !folder_make_user_private (folder->parent))
672                         return FALSE;
673
674                 if (!folder->parent->has_user_private_subfolders) {
675                         Folder *iter;
676
677                         for (iter = folder->parent; iter; iter = iter->parent)
678                                 iter->has_user_private_subfolders = TRUE;
679                 }
680         }
681
682         folder->user_private = TRUE;
683
684         vfolder_info_set_dirty (folder->info);
685
686         return TRUE;
687 }
688
689 gboolean
690 folder_is_user_private (Folder *folder)
691 {
692         return folder->user_private;
693 }
694
695 static gboolean
696 create_dot_directory_entry (Folder *folder)
697 {
698         Entry *entry = NULL, *existing;
699         const gchar *dot_directory = folder_get_desktop_file (folder);
700
701         /* Only replace if existing isn't user-private */
702         existing = folder_get_entry (folder, ".directory");
703         if (existing && entry_get_weight (existing) == 1000)
704                 return FALSE;
705
706         if (strchr (dot_directory, '/')) {
707                 /* Assume full path or URI */
708                 entry = entry_new (folder->info, 
709                                    dot_directory, 
710                                    ".directory", 
711                                    TRUE /*user_private*/,
712                                    950  /*weight*/);
713         } else {
714                 gchar *dirpath = NULL;
715                 gchar *full_path;
716
717                 if (folder->info->desktop_dir)
718                         dirpath = folder->info->desktop_dir;
719                 else if (folder->info->write_dir)
720                         dirpath = folder->info->write_dir;
721                 else
722                         return FALSE;
723
724                 if (dirpath) {
725                         full_path = vfolder_build_uri (dirpath,
726                                                        dot_directory, 
727                                                        NULL);
728                         entry = entry_new (folder->info,
729                                            full_path,
730                                            ".directory",
731                                            TRUE /*user_private*/,
732                                            950  /*weight*/);
733                         g_free (full_path);
734                 }
735         }
736
737         if (entry) {
738                 folder_add_entry (folder, entry);
739                 entry_unref (entry);
740         }
741
742         return entry != NULL;
743 }
744
745 static gboolean
746 read_one_include (Folder *folder, const gchar *file_uri)
747 {
748         Entry *entry = NULL, *existing;
749         GnomeVFSURI *uri;
750         gchar *basename, *basename_ts;
751
752         if (!strchr (file_uri, '/')) {
753                 entry = vfolder_info_lookup_entry (folder->info, file_uri);
754                 if (entry && entry != folder_get_entry (folder, file_uri)) {
755                         folder_add_entry (folder, entry);
756                         return TRUE;
757                 }
758                 return FALSE;
759         }
760         else {
761                 uri = gnome_vfs_uri_new (file_uri);
762                 if (!uri || !gnome_vfs_uri_exists (uri))
763                         return FALSE;
764
765                 basename = gnome_vfs_uri_extract_short_name (uri);
766
767                 /* If including something from the WriteDir, untimestamp it. */
768                 if (folder->info->write_dir &&
769                     strstr (file_uri, folder->info->write_dir)) {
770                         basename_ts = basename;
771                         basename = vfolder_untimestamp_file_name (basename_ts);
772                         g_free (basename_ts);
773                 }
774
775                 /* Only replace if existing is not user-private */
776                 existing = folder_get_entry (folder, basename);
777                 if (existing && entry_get_weight (existing) == 1000) {
778                         gnome_vfs_uri_unref (uri);
779                         g_free (basename);
780                         return FALSE;
781                 }
782
783                 entry = entry_new (folder->info, 
784                                    file_uri,
785                                    basename, 
786                                    TRUE,
787                                    1000 /*weight*/);
788                 folder_add_entry (folder, entry);
789
790                 entry_unref (entry);
791                 gnome_vfs_uri_unref (uri);
792                 g_free (basename);
793
794                 return TRUE;
795         }
796 }
797
798 static gboolean 
799 read_includes (Folder *folder)
800 {
801         GSList *iter;
802         gboolean changed = FALSE;
803
804         for (iter = folder->includes; iter; iter = iter->next) {
805                 gchar *include = iter->data;
806
807                 changed |= read_one_include (folder, include);
808         }
809
810         return changed;
811 }
812
813 static gboolean
814 is_excluded (Folder *folder, const gchar *filename, const gchar *displayname)
815 {
816         if (!folder->excludes)
817                 return FALSE;
818
819         if (displayname && g_hash_table_lookup (folder->excludes, displayname))
820                 return TRUE;
821
822         if (filename && g_hash_table_lookup (folder->excludes, filename))
823                 return TRUE;
824
825         return FALSE;
826 }
827
828 static gboolean
829 read_one_extended_entry (Folder           *folder, 
830                          const gchar      *file_uri, 
831                          GnomeVFSFileInfo *file_info)
832 {
833         Query *query = folder_get_query (folder);
834
835         if (is_excluded (folder, file_uri, file_info->name))
836                 return FALSE;
837
838         if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
839                 Folder *sub;
840
841                 if (folder_get_subfolder (folder, file_info->name))
842                         return FALSE;
843
844                 sub = folder_new (folder->info, file_info->name, FALSE);
845
846                 folder_set_extend_uri (sub, file_uri);
847                 sub->is_link = folder->is_link;
848
849                 folder_add_subfolder (folder, sub);
850                 folder_unref (sub);
851
852                 return TRUE;
853         } else {
854                 Entry *entry, *existing;
855                 gboolean retval = FALSE;
856
857                 /* Only replace if entry is more important than existing */
858                 existing = folder_get_entry (folder, file_info->name);
859                 if (existing && entry_get_weight (existing) >= 900)
860                         return FALSE;
861
862                 entry = entry_new (folder->info, 
863                                    file_uri,
864                                    file_info->name, 
865                                    FALSE /*user_private*/,
866                                    900   /*weight*/);
867
868                 /* Include unless specifically excluded by query */
869                 if (!query || query_try_match (query, folder, entry)) {
870                         D (g_print ("ADDING EXTENDED ENTRY: "
871                                     "%s, %s, #%d!\n",
872                                     folder_get_name (folder),
873                                     entry_get_displayname (entry),
874                                     g_slist_length ((GSList*)
875                                             folder_list_entries (folder))));
876
877                         folder_add_entry (folder, entry);
878                         retval = TRUE;
879                 }
880
881                 entry_unref (entry);
882                 return retval;
883         }
884 }
885
886 static gboolean
887 read_extended_entries (Folder *folder)
888 {
889         GnomeVFSResult result;
890         GnomeVFSDirectoryHandle *handle;
891         GnomeVFSFileInfo *file_info;
892         const gchar *extend_uri;
893         gboolean changed = FALSE;
894
895         extend_uri = folder_get_extend_uri (folder);
896
897         result = gnome_vfs_directory_open (&handle,
898                                            extend_uri,
899                                            GNOME_VFS_FILE_INFO_DEFAULT);
900         if (result != GNOME_VFS_OK)
901                 return FALSE;
902
903         file_info = gnome_vfs_file_info_new ();
904
905         while (TRUE) {
906                 gchar *file_uri;
907
908                 result = gnome_vfs_directory_read_next (handle, file_info);
909                 if (result != GNOME_VFS_OK)
910                         break;
911
912                 if (!strcmp (file_info->name, ".") ||
913                     !strcmp (file_info->name, ".."))
914                         continue;
915
916                 file_uri = vfolder_build_uri (extend_uri, 
917                                               file_info->name, 
918                                               NULL);
919
920                 changed |= read_one_extended_entry (folder, 
921                                                     file_uri, 
922                                                     file_info);
923
924                 g_free (file_uri);
925         }
926
927         gnome_vfs_file_info_unref (file_info);
928         gnome_vfs_directory_close (handle);
929
930         return changed;
931 }
932
933 static gboolean
934 read_one_info_entry_pool (Folder *folder, Entry *entry)
935 {
936         Query *query = folder_get_query (folder);
937         Entry *existing;
938
939         if (is_excluded (folder, 
940                          entry_get_filename (entry), 
941                          entry_get_displayname (entry))) {
942                 /* 
943                  * Being excluded counts as a ref because we don't want
944                  * them showing up in the Others menu.
945                  */
946                 entry_alloc (entry);
947                 return FALSE;
948         }
949
950         /* Only replace if entry is more important than existing */
951         existing = folder_get_entry (folder, entry_get_displayname (entry));
952         if (existing && entry_get_weight (existing) >= entry_get_weight (entry))
953                 return FALSE;
954
955         /* Only include if matches a mandatory query. */
956         if (query && query_try_match (query, folder, entry)) {
957                 D (g_print ("ADDING POOL ENTRY: %s, %s, #%d!!!!\n",
958                             folder_get_name (folder),
959                             entry_get_displayname (entry),
960                             g_slist_length (
961                                     (GSList*) folder_list_entries (folder))));
962
963                 folder_add_entry (folder, entry);
964
965                 return TRUE;
966         } else
967                 return FALSE;
968 }
969
970 static gboolean
971 read_info_entry_pool (Folder *folder)
972 {
973         const GSList *all_entries, *iter;
974         Query *query;
975         gboolean changed = FALSE;
976
977         if (folder->only_unallocated)
978                 return FALSE;
979
980         query = folder_get_query (folder);
981         all_entries = vfolder_info_list_all_entries (folder->info);
982
983         for (iter = all_entries; iter; iter = iter->next) {
984                 Entry *entry = iter->data;
985
986                 changed |= read_one_info_entry_pool (folder, entry);
987         }
988
989         return changed;
990 }
991
992 void
993 folder_emit_changed (Folder                   *folder,
994                      const gchar              *child,
995                      GnomeVFSMonitorEventType  event_type)
996 {
997         Folder *iter;
998         GString *buf;
999
1000         buf = g_string_new (NULL);
1001
1002         if (child) {
1003                 g_string_prepend (buf, child);
1004                 g_string_prepend_c (buf, '/');
1005         }
1006
1007         for (iter = folder; 
1008              iter != NULL && iter != folder->info->root; 
1009              iter = iter->parent) {
1010                 g_string_prepend (buf, folder_get_name (iter));
1011                 g_string_prepend_c (buf, '/');
1012         }
1013         
1014         vfolder_info_emit_change (folder->info, 
1015                                   buf->len ? buf->str : "/", 
1016                                   event_type);
1017
1018         g_string_free (buf, TRUE);
1019 }
1020
1021 static void
1022 remove_extended_subfolders (Folder *folder)
1023 {
1024         GSList *iter, *copy;
1025         Folder *sub;
1026
1027         copy = g_slist_copy ((GSList *) folder_list_subfolders (folder));
1028         for (iter = copy; iter; iter = iter->next) {
1029                 sub = iter->data;
1030                 if (!folder_is_user_private (sub))
1031                         folder_remove_subfolder (folder, sub);
1032         }
1033         g_slist_free (copy);
1034 }
1035
1036 static void
1037 folder_reload_if_needed (Folder *folder)
1038 {
1039         gboolean changed = FALSE;
1040
1041         if (!folder->dirty || folder->loading)
1042                 return;
1043
1044         D (g_print ("----- RELOADING FOLDER: %s -----\n",
1045                     folder->name));
1046
1047         folder->loading = TRUE;
1048         folder->info->loading = TRUE;
1049
1050         folder_reset_entries (folder);
1051         remove_extended_subfolders (folder);
1052
1053         if (folder_get_desktop_file (folder))
1054                 changed |= create_dot_directory_entry (folder);
1055
1056         if (folder->includes)
1057                 changed |= read_includes (folder);
1058
1059         if (folder_get_extend_uri (folder)) {
1060                 changed |= read_extended_entries (folder);
1061
1062                 /* Start monitoring here, to cut down on unneeded events */
1063                 if (!folder->extend_monitor)
1064                         folder->extend_monitor = 
1065                                 vfolder_monitor_dir_new (
1066                                         folder_get_extend_uri (folder),
1067                                         folder_extend_monitor_cb,
1068                                         folder);
1069         }
1070
1071         if (folder_get_query (folder))
1072                 changed |= read_info_entry_pool (folder);
1073
1074         if (changed)
1075                 folder_emit_changed (folder, 
1076                                      NULL,
1077                                      GNOME_VFS_MONITOR_EVENT_CHANGED);  
1078
1079         folder->info->loading = FALSE;
1080         folder->loading = FALSE;
1081         folder->dirty = FALSE;
1082 }
1083
1084 void
1085 folder_set_dirty (Folder *folder)
1086 {
1087         folder->dirty = TRUE;
1088 }
1089
1090 void 
1091 folder_set_name (Folder *folder, const gchar *name)
1092 {
1093         g_free (folder->name);
1094         folder->name = g_strdup (name);
1095
1096         vfolder_info_set_dirty (folder->info);
1097 }
1098
1099 const gchar *
1100 folder_get_name (Folder *folder)
1101 {
1102         return folder->name;
1103 }
1104
1105 void
1106 folder_set_query (Folder *folder, Query *query)
1107 {
1108         if (folder->query)
1109                 query_free (folder->query);
1110
1111         folder->query = query;
1112
1113         folder_set_dirty (folder);
1114         vfolder_info_set_dirty (folder->info);
1115 }
1116
1117 Query *
1118 folder_get_query (Folder *folder)
1119 {
1120         return folder->query;
1121 }
1122
1123 void
1124 folder_set_extend_uri (Folder *folder, const gchar *uri)
1125 {
1126         g_free (folder->extend_uri);
1127         folder->extend_uri = g_strdup (uri);
1128
1129         if (folder->extend_monitor) {
1130                 vfolder_monitor_cancel (folder->extend_monitor);
1131                 folder->extend_monitor = NULL;
1132         }
1133
1134         folder_set_dirty (folder);
1135         vfolder_info_set_dirty (folder->info);
1136 }
1137
1138 const gchar *
1139 folder_get_extend_uri (Folder *folder)
1140 {
1141         return folder->extend_uri;
1142 }
1143
1144 void 
1145 folder_set_desktop_file (Folder *folder, const gchar *filename)
1146 {
1147         g_free (folder->desktop_file);
1148         folder->desktop_file = g_strdup (filename);
1149
1150         vfolder_info_set_dirty (folder->info);
1151 }
1152
1153 const gchar *
1154 folder_get_desktop_file (Folder *folder)
1155 {
1156         return folder->desktop_file;
1157 }
1158
1159 gboolean 
1160 folder_get_child  (Folder *folder, const gchar *name, FolderChild *child)
1161 {
1162         Folder *subdir;
1163         Entry *file;
1164
1165         memset (child, 0, sizeof (FolderChild));
1166
1167         if (name)
1168                 subdir = folder_get_subfolder (folder, name);
1169         else
1170                 /* No name, just return the parent folder */
1171                 subdir = folder;
1172
1173         if (subdir) {
1174                 child->type = FOLDER;
1175                 child->folder = subdir;
1176                 return TRUE;
1177         }
1178
1179         file = folder_get_entry (folder, name);
1180         if (file) {
1181                 child->type = DESKTOP_FILE;
1182                 child->entry = file;
1183                 return TRUE;
1184         }
1185
1186         return FALSE;
1187 }
1188
1189 static void
1190 child_list_foreach_prepend (gpointer key, 
1191                             gpointer val, 
1192                             gpointer user_data)
1193 {
1194         gchar *name = key;
1195         GSList **list = user_data;
1196
1197         *list = g_slist_prepend (*list, g_strdup (name));
1198 }
1199
1200 static GSList * 
1201 child_list_prepend_sorted (gchar      *sortorder, 
1202                            GHashTable *name_hash)
1203 {
1204         GSList *ret = NULL;
1205         gchar **split_ord;
1206         int i;
1207
1208         if (!sortorder)
1209                 return NULL;
1210
1211         split_ord = g_strsplit (sortorder, ":", -1);
1212         if (split_ord && split_ord [0]) {
1213                 for (i = 0; split_ord [i]; i++) {
1214                         gchar *name = split_ord [i];
1215
1216                         if (g_hash_table_lookup (name_hash, name)) {
1217                                 g_hash_table_remove (name_hash, name);
1218                                 ret = g_slist_prepend (ret, g_strdup (name));
1219                         }
1220                 }
1221         }
1222
1223         return ret;
1224 }
1225
1226 GSList *
1227 folder_list_children (Folder *folder)
1228 {       
1229         Entry *dot_directory;
1230         GHashTable *name_hash;
1231         const GSList *iter;
1232         GSList *list = NULL;
1233
1234         /* FIXME: handle duplicate names here, by not using a hashtable */
1235
1236         name_hash = g_hash_table_new (g_str_hash, g_str_equal);
1237
1238         for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1239                 Folder *child = iter->data;
1240                 g_hash_table_insert (name_hash, 
1241                                      (gchar *) folder_get_name (child),
1242                                      NULL);
1243         }
1244
1245         for (iter = folder_list_entries (folder); iter; iter = iter->next) {
1246                 Entry *entry = iter->data;
1247                 g_hash_table_insert (name_hash, 
1248                                      (gchar *) entry_get_displayname (entry),
1249                                      NULL);
1250         }
1251
1252         if (folder->only_unallocated) {
1253                 Query *query = folder_get_query (folder);
1254
1255                 iter = vfolder_info_list_all_entries (folder->info);
1256                 for (; iter; iter = iter->next) {
1257                         Entry *entry = iter->data;
1258
1259                         if (entry_is_allocated (entry))
1260                                 continue;
1261
1262                         if (query && !query_try_match (query, folder, entry))
1263                                 continue;
1264
1265                         g_hash_table_insert (
1266                                 name_hash, 
1267                                 (gchar *) entry_get_displayname (entry),
1268                                 NULL);
1269                 }
1270         }
1271
1272         dot_directory = folder_get_entry (folder, ".directory");
1273         if (dot_directory) {
1274                 gchar *sortorder;
1275                 entry_quick_read_keys (dot_directory,
1276                                        "SortOrder",
1277                                        &sortorder,
1278                                        NULL, 
1279                                        NULL);
1280                 if (sortorder) {
1281                         list = child_list_prepend_sorted (sortorder,
1282                                                           name_hash);
1283                         g_free (sortorder);
1284                 }
1285         }
1286
1287         g_hash_table_foreach (name_hash, 
1288                               (GHFunc) child_list_foreach_prepend,
1289                               &list);
1290         g_hash_table_destroy (name_hash);
1291
1292         list = g_slist_reverse (list);
1293
1294         return list;
1295 }
1296
1297 Entry *
1298 folder_get_entry (Folder *folder, const gchar *filename)
1299 {
1300         Entry *retval = NULL;
1301
1302         folder_reload_if_needed (folder);
1303
1304         if (folder->entries_ht)
1305                 retval = g_hash_table_lookup (folder->entries_ht, filename);
1306
1307         if (!retval && folder->only_unallocated)
1308                 retval = vfolder_info_lookup_entry (folder->info, filename);
1309
1310         return retval;
1311 }
1312
1313 const GSList *
1314 folder_list_entries (Folder *folder)
1315 {
1316         folder_reload_if_needed (folder);
1317
1318         return folder->entries;
1319 }
1320
1321 /* 
1322  * This doesn't set the folder dirty. 
1323  * Use the include/exclude functions for that.
1324  */
1325 void 
1326 folder_remove_entry (Folder *folder, Entry *entry)
1327 {
1328         const gchar *name;
1329         Entry *existing;
1330
1331         if (!folder->entries_ht)
1332                 return;
1333
1334         name = entry_get_displayname (entry);
1335         existing = g_hash_table_lookup (folder->entries_ht, name);
1336         if (existing) {
1337                 g_hash_table_remove (folder->entries_ht, name);
1338                 folder->entries = g_slist_remove (folder->entries, existing);
1339
1340                 entry_dealloc (existing);
1341                 entry_unref (existing);
1342         }
1343 }
1344
1345 /* 
1346  * This doesn't set the folder dirty. 
1347  * Use the include/exclude functions for that.
1348  */
1349 void 
1350 folder_add_entry (Folder *folder, Entry *entry)
1351 {
1352         entry_alloc (entry);
1353         entry_ref (entry);
1354
1355         folder_remove_entry (folder, entry);
1356
1357         if (!folder->entries_ht) 
1358                 folder->entries_ht = g_hash_table_new (g_str_hash, g_str_equal);
1359
1360         g_hash_table_insert (folder->entries_ht, 
1361                              (gchar *) entry_get_displayname (entry),
1362                              entry);
1363         folder->entries = g_slist_append (folder->entries, entry);
1364 }
1365
1366 void
1367 folder_add_include (Folder *folder, const gchar *include)
1368 {
1369         folder_remove_exclude (folder, include);
1370         
1371         folder->includes = g_slist_prepend (folder->includes, 
1372                                             g_strdup (include));
1373
1374         vfolder_info_set_dirty (folder->info);
1375 }
1376
1377 void 
1378 folder_remove_include (Folder *folder, const gchar *file)
1379 {
1380         GSList *li;
1381
1382         if (!folder->includes)
1383                 return;
1384
1385         li = g_slist_find_custom (folder->includes, 
1386                                   file, 
1387                                   (GCompareFunc) strcmp);
1388         if (li) {
1389                 folder->includes = g_slist_delete_link (folder->includes, li);
1390                 vfolder_info_set_dirty (folder->info);
1391         }
1392 }
1393
1394 void
1395 folder_add_exclude (Folder *parent, const gchar *exclude)
1396 {
1397         char *s;
1398
1399         folder_remove_include (parent, exclude);
1400
1401         if (!parent->excludes)
1402                 parent->excludes = 
1403                         g_hash_table_new_full (g_str_hash,
1404                                                g_str_equal,
1405                                                (GDestroyNotify) g_free,
1406                                                NULL);
1407
1408         s = g_strdup (exclude);
1409         g_hash_table_replace (parent->excludes, s, s);
1410
1411         vfolder_info_set_dirty (parent->info);
1412 }
1413
1414 void 
1415 folder_remove_exclude (Folder *folder, const gchar *file)
1416 {
1417         if (!folder->excludes)
1418                 return;
1419
1420         g_hash_table_remove (folder->excludes, file);
1421
1422         vfolder_info_set_dirty (folder->info);
1423 }
1424
1425 Folder *
1426 folder_get_subfolder (Folder *folder, const gchar *name)
1427 {
1428         folder_reload_if_needed (folder);
1429
1430         if (!folder->subfolders_ht)
1431                 return NULL;
1432
1433         return g_hash_table_lookup (folder->subfolders_ht, name);
1434 }
1435
1436 const GSList * 
1437 folder_list_subfolders (Folder *parent)
1438 {
1439         folder_reload_if_needed (parent);
1440
1441         return parent->subfolders;
1442 }
1443
1444 void
1445 folder_remove_subfolder (Folder *parent, Folder *child)
1446 {
1447         const gchar *name;
1448         Folder *existing;
1449
1450         if (!parent->subfolders_ht)
1451                 return;
1452
1453         name = folder_get_name (child);
1454         existing = g_hash_table_lookup (parent->subfolders_ht, name);
1455         if (existing) {
1456                 g_hash_table_remove (parent->subfolders_ht, name);
1457                 parent->subfolders = g_slist_remove (parent->subfolders, 
1458                                                      existing);
1459                 existing->parent = NULL;
1460                 folder_unref (existing);
1461                 vfolder_info_set_dirty (parent->info);
1462         }
1463 }
1464
1465 void
1466 folder_add_subfolder (Folder *parent, Folder *child)
1467 {
1468         if (child->user_private && !parent->has_user_private_subfolders) {
1469                 Folder *iter;
1470                 for (iter = parent; iter != NULL; iter = iter->parent)
1471                         iter->has_user_private_subfolders = TRUE;
1472         }
1473
1474         folder_ref (child);
1475         child->parent = parent;
1476
1477         if (!parent->subfolders_ht)
1478                 parent->subfolders_ht = g_hash_table_new (g_str_hash, 
1479                                                           g_str_equal);
1480         else
1481                 folder_remove_subfolder (parent, child);
1482
1483         g_hash_table_insert (parent->subfolders_ht, 
1484                              (gchar *) folder_get_name (child),
1485                              child);
1486         parent->subfolders = g_slist_append (parent->subfolders, child);
1487
1488         vfolder_info_set_dirty (parent->info);
1489 }
1490
1491 void
1492 folder_dump_tree (Folder *folder, int indent)
1493 {
1494         const GSList *iter;
1495         gchar *space = g_strnfill (indent, ' ');
1496
1497         D (g_print ("%s(%p): %s\n",
1498                     space,
1499                     folder,
1500                     folder ? folder_get_name (folder) : NULL));
1501
1502         g_free (space);
1503
1504         for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1505                 Folder *child = iter->data;
1506
1507                 folder_dump_tree (child, indent + 2);
1508         }
1509 }
1510
1511 /* This is a pretty lame hack */
1512 gboolean
1513 folder_is_hidden (Folder *folder)
1514 {
1515         const GSList *iter, *ents;
1516
1517         if (folder->dont_show_if_empty == FALSE)
1518                 return FALSE;
1519
1520         if (folder->only_unallocated) {
1521                 Query *query = folder_get_query (folder);
1522
1523                 iter = vfolder_info_list_all_entries (folder->info);
1524                 for (; iter; iter = iter->next) {
1525                         Entry *entry = iter->data;
1526
1527                         if (entry_is_allocated (entry))
1528                                 continue;
1529
1530                         if (query && !query_try_match (query, folder, entry))
1531                                 continue;
1532
1533                         return FALSE;
1534                 }
1535         }
1536
1537         ents = folder_list_entries (folder);
1538         if (ents) {
1539                 /* If there is only one entry, check it is not .directory */
1540                 if (!ents->next) {
1541                         Entry *dot_directory = ents->data;
1542                         const gchar *name;
1543
1544                         name = entry_get_displayname (dot_directory);
1545                         if (strcmp (".directory", name) != 0)
1546                                 return FALSE;
1547                 } else
1548                         return FALSE;
1549         }
1550
1551         for (iter = folder_list_subfolders (folder); iter; iter = iter->next) {
1552                 Folder *child = iter->data;
1553
1554                 if (!folder_is_hidden (child))
1555                         return FALSE;
1556         }
1557
1558         return TRUE;
1559 }
1560
1561
1562 \f
1563 /* 
1564  * Query Implementation
1565  */
1566 Query *
1567 query_new (int type)
1568 {
1569         Query *query;
1570
1571         query = g_new0 (Query, 1);
1572         query->type = type;
1573
1574         return query;
1575 }
1576
1577 void
1578 query_free (Query *query)
1579 {
1580         if (query == NULL)
1581                 return;
1582
1583         if (query->type == QUERY_OR || query->type == QUERY_AND) {
1584                 g_slist_foreach (query->val.queries, 
1585                                  (GFunc) query_free, 
1586                                  NULL);
1587                 g_slist_free (query->val.queries);
1588         }
1589         else if (query->type == QUERY_FILENAME)
1590                 g_free (query->val.filename);
1591
1592         g_free (query);
1593 }
1594
1595 #define INVERT_IF_NEEDED(val) (query->not ? !(val) : (val))
1596
1597 gboolean
1598 query_try_match (Query  *query,
1599                  Folder *folder,
1600                  Entry  *efile)
1601 {
1602         GSList *li;
1603
1604         if (query == NULL)
1605                 return TRUE;
1606
1607         switch (query->type) {
1608         case QUERY_OR:
1609                 for (li = query->val.queries; li != NULL; li = li->next) {
1610                         Query *subquery = li->data;
1611
1612                         if (query_try_match (subquery, folder, efile))
1613                                 return INVERT_IF_NEEDED (TRUE);
1614                 }
1615                 return INVERT_IF_NEEDED (FALSE);
1616         case QUERY_AND:
1617                 for (li = query->val.queries; li != NULL; li = li->next) {
1618                         Query *subquery = li->data;
1619
1620                         if (!query_try_match (subquery, folder, efile))
1621                                 return INVERT_IF_NEEDED (FALSE);
1622                 }
1623                 return INVERT_IF_NEEDED (TRUE);
1624         case QUERY_PARENT:
1625                 {
1626                         const gchar *extend_uri;
1627                         
1628                         /*
1629                          * Check that entry's path starts with that of the
1630                          * folder's extend_uri, so that we know that it matches
1631                          * the parent query. 
1632                          */
1633                         extend_uri = folder_get_extend_uri (folder);
1634                         if (extend_uri &&
1635                             strncmp (entry_get_filename (efile), 
1636                                      extend_uri,
1637                                      strlen (extend_uri)) == 0) 
1638                                 return INVERT_IF_NEEDED (TRUE);
1639                         else
1640                                 return INVERT_IF_NEEDED (FALSE);
1641                 }
1642         case QUERY_KEYWORD:
1643                 { 
1644                         const GSList *keywords;
1645                         GQuark keyword;
1646
1647                         keywords = entry_get_keywords (efile);
1648                         for (; keywords; keywords = keywords->next) {
1649                                 keyword = GPOINTER_TO_INT (keywords->data);
1650                                 if (keyword == query->val.keyword)
1651                                         return INVERT_IF_NEEDED (TRUE);
1652                         }
1653                 }
1654                 return INVERT_IF_NEEDED (FALSE);
1655         case QUERY_FILENAME:
1656                 if (strchr (query->val.filename, '/') &&
1657                     !strcmp (query->val.filename, entry_get_filename (efile)))
1658                         return INVERT_IF_NEEDED (TRUE);
1659                 else if (!strcmp (query->val.filename, 
1660                                   entry_get_displayname (efile)))
1661                         return INVERT_IF_NEEDED (TRUE);
1662                 else
1663                         return INVERT_IF_NEEDED (FALSE);
1664         }
1665
1666         g_assert_not_reached ();
1667         return FALSE;
1668 }