Updated libsm and libice dependencies to use pkg-config
[rox-filer.git] / ROX-Filer / src / dir.c
blobde97f485f56510b68eaf2a0c08207bc2d81facf3
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place, Suite 330, Boston, MA 02111-1307 USA
19 /* dir.c - directory scanning and caching */
21 /* How it works:
23 * A Directory contains a list DirItems, each having a name and some details
24 * (size, image, owner, etc).
26 * There is a list of file names that need to be rechecked. While this
27 * list is non-empty, items are taken from the list in an idle callback
28 * and checked. Missing items are removed from the Directory, new items are
29 * added and existing items are updated if they've changed.
31 * When a whole directory is to be rescanned:
33 * - A list of all filenames in the directory is fetched, without any
34 * of the extra details.
35 * - This list is compared to the current DirItems, removing any that are now
36 * missing.
37 * - Each window onto the directory is asked which items it will actually
38 * display, and the union of these sets is the new recheck list.
40 * This system is designed to get the number of items and their names quickly,
41 * so that the auto-sizer can make a good guess. It also prevents checking
42 * hidden files if they're not going to be displayed.
44 * To get the Directory object, use dir_cache, which will automatically
45 * trigger a rescan if needed.
47 * To get notified when the Directory changes, use the dir_attach() and
48 * dir_detach() functions.
51 #include "config.h"
53 #include <gtk/gtk.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <string.h>
58 #include "global.h"
60 #include "dir.h"
61 #include "diritem.h"
62 #include "support.h"
63 #include "gui_support.h"
64 #include "dir.h"
65 #include "fscache.h"
66 #include "mount.h"
67 #include "pixmaps.h"
68 #include "type.h"
69 #include "usericons.h"
70 #include "main.h"
72 #ifdef USE_NOTIFY
73 static GHashTable *notify_fd_to_dir = NULL;
74 #endif
75 #ifdef USE_INOTIFY
76 # include <sys/inotify.h>
77 GIOChannel *inotify_channel;
78 static int inotify_fd;
79 #endif
80 #ifdef USE_DNOTIFY
81 /* Newer Linux kernels can tell us when the directories we are watching
82 * change, using the dnotify system.
84 gboolean dnotify_wakeup_flag = FALSE;
85 static int dnotify_last_fd = -1;
86 #endif
88 /* For debugging. Can't detach when this is non-zero. */
89 static int in_callback = 0;
91 GFSCache *dir_cache = NULL;
93 /* Static prototypes */
94 static void update(Directory *dir, gchar *pathname, gpointer data);
95 static void set_idle_callback(Directory *dir);
96 static DirItem *insert_item(Directory *dir, const guchar *leafname);
97 static void remove_missing(Directory *dir, GPtrArray *keep);
98 static void dir_recheck(Directory *dir,
99 const guchar *path, const guchar *leafname);
100 static GPtrArray *hash_to_array(GHashTable *hash);
101 static void dir_force_update_item(Directory *dir, const gchar *leaf);
102 static Directory *dir_new(const char *pathname);
103 static void dir_rescan(Directory *dir);
104 #ifdef USE_NOTIFY
105 static void dir_rescan_soon(Directory *dir);
106 # ifdef USE_INOTIFY
107 static gboolean inotify_handler(GIOChannel *source, GIOCondition condition,
108 gpointer udata);
109 # else
110 static void dnotify_handler(int sig, siginfo_t *si, void *data);
111 # endif
112 #endif
114 /****************************************************************
115 * EXTERNAL INTERFACE *
116 ****************************************************************/
118 void dir_init(void)
120 dir_cache = g_fscache_new((GFSLoadFunc) dir_new,
121 (GFSUpdateFunc) update, NULL);
123 #ifdef USE_NOTIFY
124 notify_fd_to_dir = g_hash_table_new(NULL, NULL);
126 # ifdef USE_INOTIFY
127 inotify_fd = inotify_init();
128 inotify_channel = g_io_channel_unix_new(inotify_fd);
129 g_io_add_watch(inotify_channel, G_IO_IN, inotify_handler, NULL);
130 # endif
132 # ifdef USE_DNOTIFY
134 struct sigaction act;
136 act.sa_sigaction = dnotify_handler;
137 sigemptyset(&act.sa_mask);
138 act.sa_flags = SA_SIGINFO;
139 sigaction(SIGRTMIN, &act, NULL);
141 /* Sometimes we get this instead of SIGRTMIN.
142 * Don't know why :-( but don't crash...
144 act.sa_handler = SIG_IGN;
145 sigemptyset(&act.sa_mask);
146 act.sa_flags = 0;
147 sigaction(SIGIO, &act, NULL);
150 # endif
151 #endif
154 /* Periodically calls callback to notify about changes to the contents
155 * of the directory.
156 * Before this function returns, it calls the callback once to add all
157 * the items currently in the directory (unless the dir is empty).
158 * It then calls callback(DIR_QUEUE_INTERESTING) to find out which items the
159 * caller cares about.
160 * If we are not scanning, it also calls callback(DIR_END_SCAN).
162 void dir_attach(Directory *dir, DirCallback callback, gpointer data)
164 DirUser *user;
165 GPtrArray *items;
167 g_return_if_fail(dir != NULL);
168 g_return_if_fail(callback != NULL);
170 user = g_new(DirUser, 1);
171 user->callback = callback;
172 user->data = data;
174 #ifdef USE_INOTIFY
175 if (!dir->users)
177 int fd;
179 if (dir->notify_fd != -1)
180 g_warning("dir_attach: inotify error\n");
182 fd = inotify_add_watch( inotify_fd,
183 dir->pathname,
184 IN_CREATE | IN_DELETE | IN_MOVE |
185 IN_ATTRIB);
187 g_return_if_fail(g_hash_table_lookup(notify_fd_to_dir,
188 GINT_TO_POINTER(fd)) == NULL);
189 if (fd != -1)
192 dir->notify_fd = fd;
193 g_hash_table_insert(notify_fd_to_dir,
194 GINT_TO_POINTER(fd), dir);
197 #endif
198 #ifdef USE_DNOTIFY
199 if (!dir->users)
201 int fd;
203 if (dir->notify_fd != -1)
204 g_warning("dir_attach: dnotify error\n");
206 fd = open(dir->pathname, O_RDONLY);
207 g_return_if_fail(g_hash_table_lookup(notify_fd_to_dir,
208 GINT_TO_POINTER(fd)) == NULL);
209 if (fd != -1)
211 dir->notify_fd = fd;
212 g_hash_table_insert(notify_fd_to_dir,
213 GINT_TO_POINTER(fd), dir);
214 fcntl(fd, F_SETSIG, SIGRTMIN);
215 fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
216 DN_ATTRIB | DN_MULTISHOT);
219 #endif
221 dir->users = g_list_prepend(dir->users, user);
223 g_object_ref(dir);
225 items = hash_to_array(dir->known_items);
226 if (items->len)
227 callback(dir, DIR_ADD, items, data);
228 g_ptr_array_free(items, TRUE);
230 if (dir->needs_update && !dir->scanning)
231 dir_rescan(dir);
232 else
233 callback(dir, DIR_QUEUE_INTERESTING, NULL, data);
235 /* May start scanning if noone was watching before */
236 set_idle_callback(dir);
238 if (!dir->scanning)
239 callback(dir, DIR_END_SCAN, NULL, data);
242 /* Undo the effect of dir_attach */
243 void dir_detach(Directory *dir, DirCallback callback, gpointer data)
245 DirUser *user;
246 GList *list;
248 g_return_if_fail(dir != NULL);
249 g_return_if_fail(callback != NULL);
250 g_return_if_fail(in_callback == 0);
252 for (list = dir->users; list; list = list->next)
254 user = (DirUser *) list->data;
255 if (user->callback == callback && user->data == data)
257 g_free(user);
258 dir->users = g_list_remove(dir->users, user);
259 g_object_unref(dir);
261 /* May stop scanning if noone's watching */
262 set_idle_callback(dir);
264 #ifdef USE_NOTIFY
265 if (!dir->users && dir->notify_fd != -1)
267 # ifdef USE_DNOTIFY
268 close(dir->notify_fd);
269 # endif
270 g_hash_table_remove(notify_fd_to_dir,
271 GINT_TO_POINTER(dir->notify_fd));
272 dir->notify_fd = -1;
274 # ifdef USE_INOTIFY
275 if (dir->inotify_source) {
276 g_source_remove(dir->inotify_source);
277 dir->inotify_source = 0;
279 # endif
280 #endif
281 return;
285 g_warning("dir_detach: Callback/data pair not attached!\n");
288 void dir_update(Directory *dir, gchar *pathname)
290 update(dir, pathname, NULL);
293 /* Rescan this directory */
294 void refresh_dirs(const char *path)
296 g_fscache_update(dir_cache, path);
299 /* When something has happened to a particular object, call this
300 * and all appropriate changes will be made.
302 void dir_check_this(const guchar *path)
304 guchar *real_path;
305 guchar *dir_path;
306 Directory *dir;
308 dir_path = g_path_get_dirname(path);
309 real_path = pathdup(dir_path);
310 g_free(dir_path);
312 dir = g_fscache_lookup_full(dir_cache, real_path,
313 FSCACHE_LOOKUP_PEEK, NULL);
314 if (dir)
316 dir_recheck(dir, real_path, g_basename(path));
317 g_object_unref(dir);
320 g_free(real_path);
323 #ifdef USE_NOTIFY
324 static void drop_notify(gpointer key, gpointer value, gpointer data)
326 #ifdef USE_INOTIFY
327 inotify_rm_watch(inotify_fd, GPOINTER_TO_INT(key));
328 #endif
329 #ifdef USE_DNOTIFY
330 close(GPOINTER_TO_INT(key));
331 #endif
333 #endif
335 /* Used when we fork an action child, otherwise we can't delete or unmount
336 * any directory which we're watching via dnotify! inotify does not have
337 * this problem
339 void dir_drop_all_notifies(void)
341 #ifdef USE_DNOTIFY
342 g_hash_table_foreach(notify_fd_to_dir, drop_notify, NULL);
343 #endif
346 /* Tell watchers that this item has changed, but don't rescan.
347 * (used when thumbnail has been created for an item)
349 void dir_force_update_path(const gchar *path)
351 gchar *dir_path;
352 Directory *dir;
354 g_return_if_fail(path[0] == '/');
356 dir_path = g_path_get_dirname(path);
358 dir = g_fscache_lookup_full(dir_cache, dir_path, FSCACHE_LOOKUP_PEEK,
359 NULL);
360 if (dir)
362 dir_force_update_item(dir, g_basename(path));
363 g_object_unref(dir);
366 g_free(dir_path);
369 /* Ensure that 'leafname' is up-to-date. Returns the new/updated
370 * DirItem, or NULL if the file no longer exists.
372 DirItem *dir_update_item(Directory *dir, const gchar *leafname)
374 DirItem *item;
376 time(&diritem_recent_time);
377 item = insert_item(dir, leafname);
378 dir_merge_new(dir);
380 return item;
383 /* Add item to the recheck_list if it's marked as needing it.
384 * Item must have ITEM_FLAG_NEED_RESCAN_QUEUE.
385 * Items on the list will get checked later in an idle callback.
387 void dir_queue_recheck(Directory *dir, DirItem *item)
389 g_return_if_fail(dir != NULL);
390 g_return_if_fail(item != NULL);
391 g_return_if_fail(item->flags & ITEM_FLAG_NEED_RESCAN_QUEUE);
393 dir->recheck_list = g_list_prepend(dir->recheck_list,
394 g_strdup(item->leafname));
395 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
398 static void free_recheck_list(Directory *dir)
400 destroy_glist(&dir->recheck_list);
403 /* If scanning state has changed then notify all filer windows */
404 static void dir_set_scanning(Directory *dir, gboolean scanning)
406 GList *next;
408 if (scanning == dir->scanning)
409 return;
411 in_callback++;
413 dir->scanning = scanning;
415 for (next = dir->users; next; next = next->next)
417 DirUser *user = (DirUser *) next->data;
419 user->callback(dir,
420 scanning ? DIR_START_SCAN : DIR_END_SCAN,
421 NULL, user->data);
424 #if 0
425 /* Useful for profiling */
426 if (!scanning)
428 g_print("Done\n");
429 exit(0);
431 #endif
433 in_callback--;
436 /* Notify everyone that the error status of the directory has changed */
437 static void dir_error_changed(Directory *dir)
439 GList *next;
441 in_callback++;
443 for (next = dir->users; next; next = next->next)
445 DirUser *user = (DirUser *) next->data;
447 user->callback(dir, DIR_ERROR_CHANGED, NULL, user->data);
450 in_callback--;
453 /* This is called in the background when there are items on the
454 * dir->recheck_list to process.
456 static gboolean recheck_callback(gpointer data)
458 Directory *dir = (Directory *) data;
459 GList *next;
460 guchar *leaf;
462 g_return_val_if_fail(dir != NULL, FALSE);
463 g_return_val_if_fail(dir->recheck_list != NULL, FALSE);
465 /* Remove the first name from the list */
466 next = dir->recheck_list;
467 dir->recheck_list = g_list_remove_link(dir->recheck_list, next);
468 leaf = (guchar *) next->data;
469 g_list_free_1(next);
471 /* usleep(800); */
473 insert_item(dir, leaf);
475 g_free(leaf);
477 if (dir->recheck_list)
478 return TRUE; /* Call again */
480 /* The recheck_list list empty. Stop scanning, unless
481 * needs_update, in which case we start scanning again.
484 dir_merge_new(dir);
486 dir->have_scanned = TRUE;
487 dir_set_scanning(dir, FALSE);
488 g_source_remove(dir->idle_callback);
489 dir->idle_callback = 0;
491 if (dir->needs_update)
492 dir_rescan(dir);
494 return FALSE;
497 /* Add all the new items to the items array.
498 * Notify everyone who is watching us.
500 void dir_merge_new(Directory *dir)
502 GPtrArray *new = dir->new_items;
503 GPtrArray *up = dir->up_items;
504 GPtrArray *gone = dir->gone_items;
505 GList *list;
506 guint i;
508 in_callback++;
510 for (list = dir->users; list; list = list->next)
512 DirUser *user = (DirUser *) list->data;
514 if (new->len)
515 user->callback(dir, DIR_ADD, new, user->data);
516 if (up->len)
517 user->callback(dir, DIR_UPDATE, up, user->data);
518 if (gone->len)
519 user->callback(dir, DIR_REMOVE, gone, user->data);
522 in_callback--;
524 for (i = 0; i < new->len; i++)
526 DirItem *item = (DirItem *) new->pdata[i];
528 g_hash_table_insert(dir->known_items, item->leafname, item);
531 for (i = 0; i < gone->len; i++)
533 DirItem *item = (DirItem *) gone->pdata[i];
535 diritem_free(item);
538 g_ptr_array_set_size(gone, 0);
539 g_ptr_array_set_size(new, 0);
540 g_ptr_array_set_size(up, 0);
543 #ifdef USE_DNOTIFY
544 /* Called from the mainloop shortly after dnotify_handler */
545 void dnotify_wakeup(void)
547 Directory *dir;
549 dnotify_wakeup_flag = FALSE;
551 dir = g_hash_table_lookup(notify_fd_to_dir,
552 GINT_TO_POINTER(dnotify_last_fd));
554 if (dir)
555 dir_rescan_soon(dir);
557 #endif
559 /****************************************************************
560 * INTERNAL FUNCTIONS *
561 ****************************************************************/
563 #ifdef USE_NOTIFY
564 static gint rescan_soon_timeout(gpointer data)
566 Directory *dir = (Directory *) data;
568 dir->rescan_timeout = -1;
569 if (dir->scanning)
570 dir->needs_update = TRUE;
571 else
572 dir_rescan(dir);
573 return FALSE;
576 /* Wait a fraction of a second and then rescan. If already waiting,
577 * this function does nothing.
579 static void dir_rescan_soon(Directory *dir)
581 if (dir->rescan_timeout != -1)
582 return;
583 dir->rescan_timeout = g_timeout_add(500, rescan_soon_timeout, dir);
585 #endif
587 static void free_items_array(GPtrArray *array)
589 guint i;
591 for (i = 0; i < array->len; i++)
593 DirItem *item = (DirItem *) array->pdata[i];
595 diritem_free(item);
598 g_ptr_array_free(array, TRUE);
601 /* Tell everyone watching that these items have gone */
602 static void notify_deleted(Directory *dir, GPtrArray *deleted)
604 GList *next;
606 if (!deleted->len)
607 return;
609 in_callback++;
611 for (next = dir->users; next; next = next->next)
613 DirUser *user = (DirUser *) next->data;
615 user->callback(dir, DIR_REMOVE, deleted, user->data);
618 in_callback--;
621 static void mark_unused(gpointer key, gpointer value, gpointer data)
623 DirItem *item = (DirItem *) value;
625 item->may_delete = TRUE;
628 static void keep_deleted(gpointer key, gpointer value, gpointer data)
630 DirItem *item = (DirItem *) value;
631 GPtrArray *deleted = (GPtrArray *) data;
633 if (item->may_delete)
634 g_ptr_array_add(deleted, item);
637 static gboolean check_unused(gpointer key, gpointer value, gpointer data)
639 DirItem *item = (DirItem *) value;
641 return item->may_delete;
644 /* Remove all the old items that have gone.
645 * Notify everyone who is watching us of the removed items.
647 static void remove_missing(Directory *dir, GPtrArray *keep)
649 GPtrArray *deleted;
650 guint i;
652 deleted = g_ptr_array_new();
654 /* Mark all current items as may_delete */
655 g_hash_table_foreach(dir->known_items, mark_unused, NULL);
657 /* Unmark all items also in 'keep' */
658 for (i = 0; i < keep->len; i++)
660 guchar *leaf = (guchar *) keep->pdata[i];
661 DirItem *item;
663 item = g_hash_table_lookup(dir->known_items, leaf);
665 if (item)
666 item->may_delete = FALSE;
669 /* Add each item still marked to 'deleted' */
670 g_hash_table_foreach(dir->known_items, keep_deleted, deleted);
672 /* Remove all items still marked */
673 g_hash_table_foreach_remove(dir->known_items, check_unused, NULL);
675 notify_deleted(dir, deleted);
677 free_items_array(deleted);
680 static gint notify_timeout(gpointer data)
682 Directory *dir = (Directory *) data;
684 g_return_val_if_fail(dir->notify_active == TRUE, FALSE);
686 dir_merge_new(dir);
688 dir->notify_active = FALSE;
689 g_object_unref(dir);
691 return FALSE;
694 /* Call dir_merge_new() after a while. */
695 static void delayed_notify(Directory *dir)
697 if (dir->notify_active)
698 return;
699 g_object_ref(dir);
700 g_timeout_add(1500, notify_timeout, dir);
701 dir->notify_active = TRUE;
704 /* Stat this item and add, update or remove it.
705 * Returns the new/updated item, if any.
706 * (leafname may be from the current DirItem item)
707 * Ensure diritem_recent_time is reasonably up-to-date before calling this.
709 static DirItem *insert_item(Directory *dir, const guchar *leafname)
711 const gchar *full_path;
712 DirItem *item;
713 DirItem old;
714 gboolean do_compare = FALSE; /* (old is filled in) */
716 if (leafname[0] == '.' && (leafname[1] == '\n' ||
717 (leafname[1] == '.' && leafname[2] == '\n')))
718 return NULL; /* Ignore '.' and '..' */
720 full_path = make_path(dir->pathname, leafname);
721 item = g_hash_table_lookup(dir->known_items, leafname);
723 if (item)
725 if (item->base_type != TYPE_UNKNOWN)
727 /* Preserve the old details so we can compare */
728 old = *item;
729 if (old._image)
730 g_object_ref(old._image);
731 do_compare = TRUE;
733 diritem_restat(full_path, item, &dir->stat_info);
735 else
737 /* Item isn't already here. This won't normally happen,
738 * because blank items are added when scanning, before
739 * we get here.
741 item = diritem_new(leafname);
742 diritem_restat(full_path, item, &dir->stat_info);
743 if (item->base_type == TYPE_ERROR &&
744 item->lstat_errno == ENOENT)
746 diritem_free(item);
747 return NULL;
749 g_ptr_array_add(dir->new_items, item);
753 /* No need to queue the item for scanning. If we got here because
754 * the item was queued, this flag will normally already be clear.
756 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
758 if (item->base_type == TYPE_ERROR && item->lstat_errno == ENOENT)
760 /* Item has been deleted */
761 g_hash_table_remove(dir->known_items, item->leafname);
762 g_ptr_array_add(dir->gone_items, item);
763 if (do_compare && old._image)
764 g_object_unref(old._image);
765 delayed_notify(dir);
766 return NULL;
769 if (do_compare)
771 /* It's a bit inefficient that we force the image to be
772 * loaded here, if we had an old image.
774 if (item->lstat_errno == old.lstat_errno
775 && item->base_type == old.base_type
776 && item->flags == old.flags
777 && item->size == old.size
778 && item->mode == old.mode
779 && item->atime == old.atime
780 && item->ctime == old.ctime
781 && item->mtime == old.mtime
782 && item->uid == old.uid
783 && item->gid == old.gid
784 && item->mime_type == old.mime_type
785 && (old._image == NULL || di_image(item) == old._image))
787 if (old._image)
788 g_object_unref(old._image);
789 return item;
791 if (old._image)
792 g_object_unref(old._image);
795 g_ptr_array_add(dir->up_items, item);
796 delayed_notify(dir);
798 return item;
801 static void update(Directory *dir, gchar *pathname, gpointer data)
803 g_free(dir->pathname);
804 dir->pathname = pathdup(pathname);
806 if (dir->scanning)
807 dir->needs_update = TRUE;
808 else
809 dir_rescan(dir);
812 /* If there is work to do, set the idle callback.
813 * Otherwise, stop scanning and unset the idle callback.
815 static void set_idle_callback(Directory *dir)
817 if (dir->recheck_list && dir->users)
819 /* Work to do, and someone's watching */
820 dir_set_scanning(dir, TRUE);
821 if (dir->idle_callback)
822 return;
823 time(&diritem_recent_time);
824 dir->idle_callback = g_idle_add(recheck_callback, dir);
825 /* Do the first call now (will remove the callback itself) */
826 recheck_callback(dir);
828 else
830 dir_set_scanning(dir, FALSE);
831 if (dir->idle_callback)
833 g_source_remove(dir->idle_callback);
834 dir->idle_callback = 0;
839 /* See dir_force_update_path() */
840 static void dir_force_update_item(Directory *dir, const gchar *leaf)
842 GList *list;
843 GPtrArray *items;
844 DirItem *item;
846 items = g_ptr_array_new();
848 item = g_hash_table_lookup(dir->known_items, leaf);
849 if (!item)
850 goto out;
852 g_ptr_array_add(items, item);
854 in_callback++;
856 for (list = dir->users; list; list = list->next)
858 DirUser *user = (DirUser *) list->data;
860 user->callback(dir, DIR_UPDATE, items, user->data);
863 in_callback--;
865 out:
866 g_ptr_array_free(items, TRUE);
869 static void dir_recheck(Directory *dir,
870 const guchar *path, const guchar *leafname)
872 guchar *old = dir->pathname;
874 dir->pathname = g_strdup(path);
875 g_free(old);
877 time(&diritem_recent_time);
878 insert_item(dir, leafname);
881 static void to_array(gpointer key, gpointer value, gpointer data)
883 GPtrArray *array = (GPtrArray *) data;
885 g_ptr_array_add(array, value);
888 /* Convert a hash table to an unsorted GPtrArray.
889 * g_ptr_array_free() the result.
891 static GPtrArray *hash_to_array(GHashTable *hash)
893 GPtrArray *array;
895 array = g_ptr_array_new();
897 g_hash_table_foreach(hash, to_array, array);
899 return array;
902 static gpointer parent_class;
904 /* Note: dir_cache is never purged, so this shouldn't get called */
905 static void dir_finialize(GObject *object)
907 GPtrArray *items;
908 Directory *dir = (Directory *) object;
910 g_return_if_fail(dir->users == NULL);
912 g_print("[ dir finalize ]\n");
914 free_recheck_list(dir);
915 set_idle_callback(dir);
916 if (dir->rescan_timeout != -1)
917 g_source_remove(dir->rescan_timeout);
919 dir_merge_new(dir); /* Ensures new, up and gone are empty */
921 g_ptr_array_free(dir->up_items, TRUE);
922 g_ptr_array_free(dir->new_items, TRUE);
923 g_ptr_array_free(dir->gone_items, TRUE);
925 items = hash_to_array(dir->known_items);
926 free_items_array(items);
927 g_hash_table_destroy(dir->known_items);
929 g_free(dir->error);
930 g_free(dir->pathname);
932 G_OBJECT_CLASS(parent_class)->finalize(object);
935 static void directory_class_init(gpointer gclass, gpointer data)
937 GObjectClass *object = (GObjectClass *) gclass;
939 parent_class = g_type_class_peek_parent(gclass);
941 object->finalize = dir_finialize;
944 static void directory_init(GTypeInstance *object, gpointer gclass)
946 Directory *dir = (Directory *) object;
948 dir->known_items = g_hash_table_new(g_str_hash, g_str_equal);
949 dir->recheck_list = NULL;
950 dir->idle_callback = 0;
951 dir->scanning = FALSE;
952 dir->have_scanned = FALSE;
954 dir->users = NULL;
955 dir->needs_update = TRUE;
956 dir->notify_active = FALSE;
957 dir->pathname = NULL;
958 dir->error = NULL;
959 dir->rescan_timeout = -1;
960 #ifdef USE_NOTIFY
961 dir->notify_fd = -1;
962 #endif
963 #ifdef USE_INOTIFY
964 dir->inotify_source = 0;
965 #endif
967 dir->new_items = g_ptr_array_new();
968 dir->up_items = g_ptr_array_new();
969 dir->gone_items = g_ptr_array_new();
972 static GType dir_get_type(void)
974 static GType type = 0;
976 if (!type)
978 static const GTypeInfo info =
980 sizeof (DirectoryClass),
981 NULL, /* base_init */
982 NULL, /* base_finalise */
983 directory_class_init,
984 NULL, /* class_finalise */
985 NULL, /* class_data */
986 sizeof(Directory),
987 0, /* n_preallocs */
988 directory_init
991 type = g_type_register_static(G_TYPE_OBJECT, "Directory",
992 &info, 0);
995 return type;
998 static Directory *dir_new(const char *pathname)
1000 Directory *dir;
1002 dir = g_object_new(dir_get_type(), NULL);
1004 dir->pathname = g_strdup(pathname);
1006 return dir;
1009 /* Get the names of all files in the directory.
1010 * Remove any DirItems that are no longer listed.
1011 * Replace the recheck_list with the items found.
1013 static void dir_rescan(Directory *dir)
1015 GPtrArray *names;
1016 DIR *d;
1017 struct dirent *ent;
1018 guint i;
1019 const char *pathname;
1020 GList *next;
1022 g_return_if_fail(dir != NULL);
1024 pathname = dir->pathname;
1026 dir->needs_update = FALSE;
1028 names = g_ptr_array_new();
1030 read_globicons();
1031 mount_update(FALSE);
1032 if (dir->error)
1034 null_g_free(&dir->error);
1035 dir_error_changed(dir);
1038 /* Saves statting the parent for each item... */
1039 if (mc_stat(pathname, &dir->stat_info))
1041 dir->error = g_strdup_printf(_("Can't stat directory: %s"),
1042 g_strerror(errno));
1043 dir_error_changed(dir);
1044 return; /* Report on attach */
1047 d = mc_opendir(pathname);
1048 if (!d)
1050 dir->error = g_strdup_printf(_("Can't open directory: %s"),
1051 g_strerror(errno));
1052 dir_error_changed(dir);
1053 return; /* Report on attach */
1056 dir_set_scanning(dir, TRUE);
1057 dir_merge_new(dir);
1058 gdk_flush();
1060 /* Make a list of all the names in the directory */
1061 while ((ent = mc_readdir(d)))
1063 if (ent->d_name[0] == '.')
1065 if (ent->d_name[1] == '\0')
1066 continue; /* Ignore '.' */
1067 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
1068 continue; /* Ignore '..' */
1071 g_ptr_array_add(names, g_strdup(ent->d_name));
1073 mc_closedir(d);
1075 /* Compare the list with the current DirItems, removing
1076 * any that are missing.
1078 remove_missing(dir, names);
1080 free_recheck_list(dir);
1082 /* For each name found, mark it as needing to be put on the rescan
1083 * list at some point in the future.
1084 * If the item is new, put a blank place-holder item in the directory.
1086 for (i = 0; i < names->len; i++)
1088 DirItem *old;
1089 guchar *name = names->pdata[i];
1091 old = g_hash_table_lookup(dir->known_items, name);
1092 if (old)
1094 /* This flag is cleared when the item is added
1095 * to the rescan list.
1097 old->flags |= ITEM_FLAG_NEED_RESCAN_QUEUE;
1099 else
1101 DirItem *new;
1103 new = diritem_new(name);
1104 g_ptr_array_add(dir->new_items, new);
1109 dir_merge_new(dir);
1111 /* Ask everyone which items they need to display, and add them to
1112 * the recheck list. Typically, this means we don't waste time
1113 * scanning hidden items.
1115 in_callback++;
1116 for (next = dir->users; next; next = next->next)
1118 DirUser *user = (DirUser *) next->data;
1119 user->callback(dir,
1120 DIR_QUEUE_INTERESTING,
1121 NULL, user->data);
1123 in_callback--;
1125 g_ptr_array_free(names, TRUE);
1127 set_idle_callback(dir);
1128 dir_merge_new(dir);
1131 #ifdef USE_DNOTIFY
1132 /* Signal handler - don't do anything dangerous here */
1133 static void dnotify_handler(int sig, siginfo_t *si, void *data)
1135 /* Note: there is currently only one place to store the fd,
1136 * so we'll miss updates to several directories if they happen
1137 * close together.
1139 dnotify_last_fd = si->si_fd;
1140 dnotify_wakeup_flag = TRUE;
1141 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
1143 #endif
1145 #ifdef USE_INOTIFY
1146 static gboolean inotify_handler(GIOChannel *source, GIOCondition condition,
1147 gpointer udata)
1149 int fd = g_io_channel_unix_get_fd(source);
1150 Directory *dir;
1151 char buf[sizeof(struct inotify_event)+1024];
1152 int len, i = 0;
1154 len = read(fd, buf, sizeof(buf));
1155 if (len<0)
1157 if (errno != EINTR)
1158 perror("read");
1159 return TRUE;
1161 else if (!len)
1162 return TRUE;
1164 while (i<len)
1166 struct inotify_event *event=(struct inotify_event *) (buf+i);
1168 dir = g_hash_table_lookup(notify_fd_to_dir,
1169 GINT_TO_POINTER(event->wd));
1170 if (dir)
1171 dir_rescan_soon(dir);
1173 i += sizeof(*event)+event->len;
1177 return TRUE;
1179 #endif