r4097: Removed GNOME-VFS from --version output; we never use it now (Thomas Leonard).
[rox-filer.git] / ROX-Filer / src / dir.c
blobdfea92f55bb16b82bd50a1ac14f5d70b68994eec
1 /*
2 * $Id$
4 * Copyright (C) 2005, the ROX-Filer team.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 * Place, Suite 330, Boston, MA 02111-1307 USA
21 /* dir.c - directory scanning and caching */
23 /* How it works:
25 * A Directory contains a list DirItems, each having a name and some details
26 * (size, image, owner, etc).
28 * There is a list of file names that need to be rechecked. While this
29 * list is non-empty, items are taken from the list in an idle callback
30 * and checked. Missing items are removed from the Directory, new items are
31 * added and existing items are updated if they've changed.
33 * When a whole directory is to be rescanned:
35 * - A list of all filenames in the directory is fetched, without any
36 * of the extra details.
37 * - This list is compared to the current DirItems, removing any that are now
38 * missing.
39 * - Each window onto the directory is asked which items it will actually
40 * display, and the union of these sets is the new recheck list.
42 * This system is designed to get the number of items and their names quickly,
43 * so that the auto-sizer can make a good guess. It also prevents checking
44 * hidden files if they're not going to be displayed.
46 * To get the Directory object, use dir_cache, which will automatically
47 * trigger a rescan if needed.
49 * To get notified when the Directory changes, use the dir_attach() and
50 * dir_detach() functions.
53 #include "config.h"
55 #include <gtk/gtk.h>
56 #include <errno.h>
57 #include <stdio.h>
58 #include <string.h>
60 #include "global.h"
62 #include "dir.h"
63 #include "diritem.h"
64 #include "support.h"
65 #include "gui_support.h"
66 #include "dir.h"
67 #include "fscache.h"
68 #include "mount.h"
69 #include "pixmaps.h"
70 #include "type.h"
71 #include "usericons.h"
72 #include "main.h"
74 #ifdef USE_DNOTIFY
75 /* Newer Linux kernels can tell us when the directories we are watching
76 * change, using the dnotify system.
78 static GHashTable *dnotify_fd_to_dir = NULL;
79 gboolean dnotify_wakeup_flag = FALSE;
80 static int dnotify_last_fd = -1;
81 #endif
83 /* For debugging. Can't detach when this is non-zero. */
84 static int in_callback = 0;
86 GFSCache *dir_cache = NULL;
88 /* Static prototypes */
89 static void update(Directory *dir, gchar *pathname, gpointer data);
90 static void set_idle_callback(Directory *dir);
91 static DirItem *insert_item(Directory *dir, const guchar *leafname);
92 static void remove_missing(Directory *dir, GPtrArray *keep);
93 static void dir_recheck(Directory *dir,
94 const guchar *path, const guchar *leafname);
95 static GPtrArray *hash_to_array(GHashTable *hash);
96 static void dir_force_update_item(Directory *dir, const gchar *leaf);
97 static Directory *dir_new(const char *pathname);
98 static void dir_rescan(Directory *dir);
99 #ifdef USE_DNOTIFY
100 static void dir_rescan_soon(Directory *dir);
101 static void dnotify_handler(int sig, siginfo_t *si, void *data);
102 #endif
104 /****************************************************************
105 * EXTERNAL INTERFACE *
106 ****************************************************************/
108 void dir_init(void)
110 dir_cache = g_fscache_new((GFSLoadFunc) dir_new,
111 (GFSUpdateFunc) update, NULL);
113 /* Check for dnotify support in the kernel */
114 #ifdef USE_DNOTIFY
116 struct sigaction act;
118 act.sa_sigaction = dnotify_handler;
119 sigemptyset(&act.sa_mask);
120 act.sa_flags = SA_SIGINFO;
121 sigaction(SIGRTMIN, &act, NULL);
123 /* Sometimes we get this instead of SIGRTMIN.
124 * Don't know why :-( but don't crash...
126 act.sa_handler = SIG_IGN;
127 sigemptyset(&act.sa_mask);
128 act.sa_flags = 0;
129 sigaction(SIGIO, &act, NULL);
131 dnotify_fd_to_dir = g_hash_table_new(NULL, NULL);
133 #endif
136 /* Periodically calls callback to notify about changes to the contents
137 * of the directory.
138 * Before this function returns, it calls the callback once to add all
139 * the items currently in the directory (unless the dir is empty).
140 * It then calls callback(DIR_QUEUE_INTERESTING) to find out which items the
141 * caller cares about.
142 * If we are not scanning, it also calls callback(DIR_END_SCAN).
144 void dir_attach(Directory *dir, DirCallback callback, gpointer data)
146 DirUser *user;
147 GPtrArray *items;
149 g_return_if_fail(dir != NULL);
150 g_return_if_fail(callback != NULL);
152 user = g_new(DirUser, 1);
153 user->callback = callback;
154 user->data = data;
156 #ifdef USE_DNOTIFY
157 if (!dir->users)
159 int fd;
161 if (dir->dnotify_fd != -1)
162 g_warning("dir_attach: dnotify error\n");
164 fd = open(dir->pathname, O_RDONLY);
165 g_return_if_fail(g_hash_table_lookup(dnotify_fd_to_dir,
166 GINT_TO_POINTER(fd)) == NULL);
167 if (fd != -1)
169 dir->dnotify_fd = fd;
170 g_hash_table_insert(dnotify_fd_to_dir,
171 GINT_TO_POINTER(fd), dir);
172 fcntl(fd, F_SETSIG, SIGRTMIN);
173 fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
174 DN_ATTRIB | DN_MULTISHOT);
177 #endif
179 dir->users = g_list_prepend(dir->users, user);
181 g_object_ref(dir);
183 items = hash_to_array(dir->known_items);
184 if (items->len)
185 callback(dir, DIR_ADD, items, data);
186 g_ptr_array_free(items, TRUE);
188 if (dir->needs_update && !dir->scanning)
189 dir_rescan(dir);
190 else
191 callback(dir, DIR_QUEUE_INTERESTING, NULL, data);
193 /* May start scanning if noone was watching before */
194 set_idle_callback(dir);
196 if (!dir->scanning)
197 callback(dir, DIR_END_SCAN, NULL, data);
200 /* Undo the effect of dir_attach */
201 void dir_detach(Directory *dir, DirCallback callback, gpointer data)
203 DirUser *user;
204 GList *list;
206 g_return_if_fail(dir != NULL);
207 g_return_if_fail(callback != NULL);
208 g_return_if_fail(in_callback == 0);
210 for (list = dir->users; list; list = list->next)
212 user = (DirUser *) list->data;
213 if (user->callback == callback && user->data == data)
215 g_free(user);
216 dir->users = g_list_remove(dir->users, user);
217 g_object_unref(dir);
219 /* May stop scanning if noone's watching */
220 set_idle_callback(dir);
222 #ifdef USE_DNOTIFY
223 if (!dir->users && dir->dnotify_fd != -1)
225 close(dir->dnotify_fd);
226 g_hash_table_remove(dnotify_fd_to_dir,
227 GINT_TO_POINTER(dir->dnotify_fd));
228 dir->dnotify_fd = -1;
230 #endif
231 return;
235 g_warning("dir_detach: Callback/data pair not attached!\n");
238 void dir_update(Directory *dir, gchar *pathname)
240 update(dir, pathname, NULL);
243 /* Rescan this directory */
244 void refresh_dirs(const char *path)
246 g_fscache_update(dir_cache, path);
249 /* When something has happened to a particular object, call this
250 * and all appropriate changes will be made.
252 void dir_check_this(const guchar *path)
254 guchar *real_path;
255 guchar *dir_path;
256 Directory *dir;
258 dir_path = g_path_get_dirname(path);
259 real_path = pathdup(dir_path);
260 g_free(dir_path);
262 dir = g_fscache_lookup_full(dir_cache, real_path,
263 FSCACHE_LOOKUP_PEEK, NULL);
264 if (dir)
266 dir_recheck(dir, real_path, g_basename(path));
267 g_object_unref(dir);
270 g_free(real_path);
273 #ifdef USE_DNOTIFY
274 static void drop_dnotify(gpointer key, gpointer value, gpointer data)
276 close(GPOINTER_TO_INT(key));
278 #endif
280 /* Used when we fork an action child, otherwise we can't delete or unmount
281 * any directory which we're watching!
283 void dir_drop_all_dnotifies(void)
285 #ifdef USE_DNOTIFY
286 g_hash_table_foreach(dnotify_fd_to_dir, drop_dnotify, NULL);
287 #endif
290 /* Tell watchers that this item has changed, but don't rescan.
291 * (used when thumbnail has been created for an item)
293 void dir_force_update_path(const gchar *path)
295 gchar *dir_path;
296 Directory *dir;
298 g_return_if_fail(path[0] == '/');
300 dir_path = g_path_get_dirname(path);
302 dir = g_fscache_lookup_full(dir_cache, dir_path, FSCACHE_LOOKUP_PEEK,
303 NULL);
304 if (dir)
306 dir_force_update_item(dir, g_basename(path));
307 g_object_unref(dir);
310 g_free(dir_path);
313 /* Ensure that 'leafname' is up-to-date. Returns the new/updated
314 * DirItem, or NULL if the file no longer exists.
316 DirItem *dir_update_item(Directory *dir, const gchar *leafname)
318 DirItem *item;
320 time(&diritem_recent_time);
321 item = insert_item(dir, leafname);
322 dir_merge_new(dir);
324 return item;
327 /* Add item to the recheck_list if it's marked as needing it.
328 * Item must have ITEM_FLAG_NEED_RESCAN_QUEUE.
329 * Items on the list will get checked later in an idle callback.
331 void dir_queue_recheck(Directory *dir, DirItem *item)
333 g_return_if_fail(dir != NULL);
334 g_return_if_fail(item != NULL);
335 g_return_if_fail(item->flags & ITEM_FLAG_NEED_RESCAN_QUEUE);
337 dir->recheck_list = g_list_prepend(dir->recheck_list,
338 g_strdup(item->leafname));
339 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
342 static void free_recheck_list(Directory *dir)
344 destroy_glist(&dir->recheck_list);
347 /* If scanning state has changed then notify all filer windows */
348 static void dir_set_scanning(Directory *dir, gboolean scanning)
350 GList *next;
352 if (scanning == dir->scanning)
353 return;
355 in_callback++;
357 dir->scanning = scanning;
359 for (next = dir->users; next; next = next->next)
361 DirUser *user = (DirUser *) next->data;
363 user->callback(dir,
364 scanning ? DIR_START_SCAN : DIR_END_SCAN,
365 NULL, user->data);
368 #if 0
369 /* Useful for profiling */
370 if (!scanning)
372 g_print("Done\n");
373 exit(0);
375 #endif
377 in_callback--;
380 /* Notify everyone that the error status of the directory has changed */
381 static void dir_error_changed(Directory *dir)
383 GList *next;
385 in_callback++;
387 for (next = dir->users; next; next = next->next)
389 DirUser *user = (DirUser *) next->data;
391 user->callback(dir, DIR_ERROR_CHANGED, NULL, user->data);
394 in_callback--;
397 /* This is called in the background when there are items on the
398 * dir->recheck_list to process.
400 static gboolean recheck_callback(gpointer data)
402 Directory *dir = (Directory *) data;
403 GList *next;
404 guchar *leaf;
406 g_return_val_if_fail(dir != NULL, FALSE);
407 g_return_val_if_fail(dir->recheck_list != NULL, FALSE);
409 /* Remove the first name from the list */
410 next = dir->recheck_list;
411 dir->recheck_list = g_list_remove_link(dir->recheck_list, next);
412 leaf = (guchar *) next->data;
413 g_list_free_1(next);
415 /* usleep(800); */
417 insert_item(dir, leaf);
419 g_free(leaf);
421 if (dir->recheck_list)
422 return TRUE; /* Call again */
424 /* The recheck_list list empty. Stop scanning, unless
425 * needs_update, in which case we start scanning again.
428 dir_merge_new(dir);
430 dir->have_scanned = TRUE;
431 dir_set_scanning(dir, FALSE);
432 g_source_remove(dir->idle_callback);
433 dir->idle_callback = 0;
435 if (dir->needs_update)
436 dir_rescan(dir);
438 return FALSE;
441 /* Add all the new items to the items array.
442 * Notify everyone who is watching us.
444 void dir_merge_new(Directory *dir)
446 GPtrArray *new = dir->new_items;
447 GPtrArray *up = dir->up_items;
448 GPtrArray *gone = dir->gone_items;
449 GList *list;
450 guint i;
452 in_callback++;
454 for (list = dir->users; list; list = list->next)
456 DirUser *user = (DirUser *) list->data;
458 if (new->len)
459 user->callback(dir, DIR_ADD, new, user->data);
460 if (up->len)
461 user->callback(dir, DIR_UPDATE, up, user->data);
462 if (gone->len)
463 user->callback(dir, DIR_REMOVE, gone, user->data);
466 in_callback--;
468 for (i = 0; i < new->len; i++)
470 DirItem *item = (DirItem *) new->pdata[i];
472 g_hash_table_insert(dir->known_items, item->leafname, item);
475 for (i = 0; i < gone->len; i++)
477 DirItem *item = (DirItem *) gone->pdata[i];
479 diritem_free(item);
482 g_ptr_array_set_size(gone, 0);
483 g_ptr_array_set_size(new, 0);
484 g_ptr_array_set_size(up, 0);
487 #ifdef USE_DNOTIFY
488 /* Called from the mainloop shortly after dnotify_handler */
489 void dnotify_wakeup(void)
491 Directory *dir;
493 dnotify_wakeup_flag = FALSE;
495 dir = g_hash_table_lookup(dnotify_fd_to_dir,
496 GINT_TO_POINTER(dnotify_last_fd));
498 if (dir)
499 dir_rescan_soon(dir);
501 #endif
503 /****************************************************************
504 * INTERNAL FUNCTIONS *
505 ****************************************************************/
507 #ifdef USE_DNOTIFY
508 static gint rescan_soon_timeout(gpointer data)
510 Directory *dir = (Directory *) data;
512 dir->rescan_timeout = -1;
513 if (dir->scanning)
514 dir->needs_update = TRUE;
515 else
516 dir_rescan(dir);
517 return FALSE;
520 /* Wait a fraction of a second and then rescan. If already waiting,
521 * this function does nothing.
523 static void dir_rescan_soon(Directory *dir)
525 if (dir->rescan_timeout != -1)
526 return;
527 dir->rescan_timeout = g_timeout_add(500, rescan_soon_timeout, dir);
529 #endif
531 static void free_items_array(GPtrArray *array)
533 guint i;
535 for (i = 0; i < array->len; i++)
537 DirItem *item = (DirItem *) array->pdata[i];
539 diritem_free(item);
542 g_ptr_array_free(array, TRUE);
545 /* Tell everyone watching that these items have gone */
546 static void notify_deleted(Directory *dir, GPtrArray *deleted)
548 GList *next;
550 if (!deleted->len)
551 return;
553 in_callback++;
555 for (next = dir->users; next; next = next->next)
557 DirUser *user = (DirUser *) next->data;
559 user->callback(dir, DIR_REMOVE, deleted, user->data);
562 in_callback--;
565 static void mark_unused(gpointer key, gpointer value, gpointer data)
567 DirItem *item = (DirItem *) value;
569 item->may_delete = TRUE;
572 static void keep_deleted(gpointer key, gpointer value, gpointer data)
574 DirItem *item = (DirItem *) value;
575 GPtrArray *deleted = (GPtrArray *) data;
577 if (item->may_delete)
578 g_ptr_array_add(deleted, item);
581 static gboolean check_unused(gpointer key, gpointer value, gpointer data)
583 DirItem *item = (DirItem *) value;
585 return item->may_delete;
588 /* Remove all the old items that have gone.
589 * Notify everyone who is watching us of the removed items.
591 static void remove_missing(Directory *dir, GPtrArray *keep)
593 GPtrArray *deleted;
594 guint i;
596 deleted = g_ptr_array_new();
598 /* Mark all current items as may_delete */
599 g_hash_table_foreach(dir->known_items, mark_unused, NULL);
601 /* Unmark all items also in 'keep' */
602 for (i = 0; i < keep->len; i++)
604 guchar *leaf = (guchar *) keep->pdata[i];
605 DirItem *item;
607 item = g_hash_table_lookup(dir->known_items, leaf);
609 if (item)
610 item->may_delete = FALSE;
613 /* Add each item still marked to 'deleted' */
614 g_hash_table_foreach(dir->known_items, keep_deleted, deleted);
616 /* Remove all items still marked */
617 g_hash_table_foreach_remove(dir->known_items, check_unused, NULL);
619 notify_deleted(dir, deleted);
621 free_items_array(deleted);
624 static gint notify_timeout(gpointer data)
626 Directory *dir = (Directory *) data;
628 g_return_val_if_fail(dir->notify_active == TRUE, FALSE);
630 dir_merge_new(dir);
632 dir->notify_active = FALSE;
633 g_object_unref(dir);
635 return FALSE;
638 /* Call dir_merge_new() after a while. */
639 static void delayed_notify(Directory *dir)
641 if (dir->notify_active)
642 return;
643 g_object_ref(dir);
644 g_timeout_add(1500, notify_timeout, dir);
645 dir->notify_active = TRUE;
648 /* Stat this item and add, update or remove it.
649 * Returns the new/updated item, if any.
650 * (leafname may be from the current DirItem item)
651 * Ensure diritem_recent_time is reasonably up-to-date before calling this.
653 static DirItem *insert_item(Directory *dir, const guchar *leafname)
655 const gchar *full_path;
656 DirItem *item;
657 DirItem old;
658 gboolean do_compare = FALSE; /* (old is filled in) */
660 if (leafname[0] == '.' && (leafname[1] == '\n' ||
661 (leafname[1] == '.' && leafname[2] == '\n')))
662 return NULL; /* Ignore '.' and '..' */
664 full_path = make_path(dir->pathname, leafname);
665 item = g_hash_table_lookup(dir->known_items, leafname);
667 if (item)
669 if (item->base_type != TYPE_UNKNOWN)
671 /* Preserve the old details so we can compare */
672 old = *item;
673 if (old._image)
674 g_object_ref(old._image);
675 do_compare = TRUE;
677 diritem_restat(full_path, item, &dir->stat_info);
679 else
681 /* Item isn't already here. This won't normally happen,
682 * because blank items are added when scanning, before
683 * we get here.
685 item = diritem_new(leafname);
686 diritem_restat(full_path, item, &dir->stat_info);
687 if (item->base_type == TYPE_ERROR &&
688 item->lstat_errno == ENOENT)
690 diritem_free(item);
691 return NULL;
693 g_ptr_array_add(dir->new_items, item);
697 /* No need to queue the item for scanning. If we got here because
698 * the item was queued, this flag will normally already be clear.
700 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
702 if (item->base_type == TYPE_ERROR && item->lstat_errno == ENOENT)
704 /* Item has been deleted */
705 g_hash_table_remove(dir->known_items, item->leafname);
706 g_ptr_array_add(dir->gone_items, item);
707 if (do_compare && old._image)
708 g_object_unref(old._image);
709 delayed_notify(dir);
710 return NULL;
713 if (do_compare)
715 /* It's a bit inefficient that we force the image to be
716 * loaded here, if we had an old image.
718 if (item->lstat_errno == old.lstat_errno
719 && item->base_type == old.base_type
720 && item->flags == old.flags
721 && item->size == old.size
722 && item->mode == old.mode
723 && item->atime == old.atime
724 && item->ctime == old.ctime
725 && item->mtime == old.mtime
726 && item->uid == old.uid
727 && item->gid == old.gid
728 && item->mime_type == old.mime_type
729 && (old._image == NULL || di_image(item) == old._image))
731 if (old._image)
732 g_object_unref(old._image);
733 return item;
735 if (old._image)
736 g_object_unref(old._image);
739 g_ptr_array_add(dir->up_items, item);
740 delayed_notify(dir);
742 return item;
745 static void update(Directory *dir, gchar *pathname, gpointer data)
747 g_free(dir->pathname);
748 dir->pathname = pathdup(pathname);
750 if (dir->scanning)
751 dir->needs_update = TRUE;
752 else
753 dir_rescan(dir);
756 /* If there is work to do, set the idle callback.
757 * Otherwise, stop scanning and unset the idle callback.
759 static void set_idle_callback(Directory *dir)
761 if (dir->recheck_list && dir->users)
763 /* Work to do, and someone's watching */
764 dir_set_scanning(dir, TRUE);
765 if (dir->idle_callback)
766 return;
767 time(&diritem_recent_time);
768 dir->idle_callback = g_idle_add(recheck_callback, dir);
769 /* Do the first call now (will remove the callback itself) */
770 recheck_callback(dir);
772 else
774 dir_set_scanning(dir, FALSE);
775 if (dir->idle_callback)
777 g_source_remove(dir->idle_callback);
778 dir->idle_callback = 0;
783 /* See dir_force_update_path() */
784 static void dir_force_update_item(Directory *dir, const gchar *leaf)
786 GList *list;
787 GPtrArray *items;
788 DirItem *item;
790 items = g_ptr_array_new();
792 item = g_hash_table_lookup(dir->known_items, leaf);
793 if (!item)
794 goto out;
796 g_ptr_array_add(items, item);
798 in_callback++;
800 for (list = dir->users; list; list = list->next)
802 DirUser *user = (DirUser *) list->data;
804 user->callback(dir, DIR_UPDATE, items, user->data);
807 in_callback--;
809 out:
810 g_ptr_array_free(items, TRUE);
813 static void dir_recheck(Directory *dir,
814 const guchar *path, const guchar *leafname)
816 guchar *old = dir->pathname;
818 dir->pathname = g_strdup(path);
819 g_free(old);
821 time(&diritem_recent_time);
822 insert_item(dir, leafname);
825 static void to_array(gpointer key, gpointer value, gpointer data)
827 GPtrArray *array = (GPtrArray *) data;
829 g_ptr_array_add(array, value);
832 /* Convert a hash table to an unsorted GPtrArray.
833 * g_ptr_array_free() the result.
835 static GPtrArray *hash_to_array(GHashTable *hash)
837 GPtrArray *array;
839 array = g_ptr_array_new();
841 g_hash_table_foreach(hash, to_array, array);
843 return array;
846 static gpointer parent_class;
848 /* Note: dir_cache is never purged, so this shouldn't get called */
849 static void dir_finialize(GObject *object)
851 GPtrArray *items;
852 Directory *dir = (Directory *) object;
854 g_return_if_fail(dir->users == NULL);
856 g_print("[ dir finalize ]\n");
858 free_recheck_list(dir);
859 set_idle_callback(dir);
860 if (dir->rescan_timeout != -1)
861 g_source_remove(dir->rescan_timeout);
863 dir_merge_new(dir); /* Ensures new, up and gone are empty */
865 g_ptr_array_free(dir->up_items, TRUE);
866 g_ptr_array_free(dir->new_items, TRUE);
867 g_ptr_array_free(dir->gone_items, TRUE);
869 items = hash_to_array(dir->known_items);
870 free_items_array(items);
871 g_hash_table_destroy(dir->known_items);
873 g_free(dir->error);
874 g_free(dir->pathname);
876 G_OBJECT_CLASS(parent_class)->finalize(object);
879 static void directory_class_init(gpointer gclass, gpointer data)
881 GObjectClass *object = (GObjectClass *) gclass;
883 parent_class = g_type_class_peek_parent(gclass);
885 object->finalize = dir_finialize;
888 static void directory_init(GTypeInstance *object, gpointer gclass)
890 Directory *dir = (Directory *) object;
892 dir->known_items = g_hash_table_new(g_str_hash, g_str_equal);
893 dir->recheck_list = NULL;
894 dir->idle_callback = 0;
895 dir->scanning = FALSE;
896 dir->have_scanned = FALSE;
898 dir->users = NULL;
899 dir->needs_update = TRUE;
900 dir->notify_active = FALSE;
901 dir->pathname = NULL;
902 dir->error = NULL;
903 dir->rescan_timeout = -1;
904 #ifdef USE_DNOTIFY
905 dir->dnotify_fd = -1;
906 #endif
908 dir->new_items = g_ptr_array_new();
909 dir->up_items = g_ptr_array_new();
910 dir->gone_items = g_ptr_array_new();
913 static GType dir_get_type(void)
915 static GType type = 0;
917 if (!type)
919 static const GTypeInfo info =
921 sizeof (DirectoryClass),
922 NULL, /* base_init */
923 NULL, /* base_finalise */
924 directory_class_init,
925 NULL, /* class_finalise */
926 NULL, /* class_data */
927 sizeof(Directory),
928 0, /* n_preallocs */
929 directory_init
932 type = g_type_register_static(G_TYPE_OBJECT, "Directory",
933 &info, 0);
936 return type;
939 static Directory *dir_new(const char *pathname)
941 Directory *dir;
943 dir = g_object_new(dir_get_type(), NULL);
945 dir->pathname = g_strdup(pathname);
947 return dir;
950 /* Get the names of all files in the directory.
951 * Remove any DirItems that are no longer listed.
952 * Replace the recheck_list with the items found.
954 static void dir_rescan(Directory *dir)
956 GPtrArray *names;
957 DIR *d;
958 struct dirent *ent;
959 guint i;
960 const char *pathname;
961 GList *next;
963 g_return_if_fail(dir != NULL);
965 pathname = dir->pathname;
967 dir->needs_update = FALSE;
969 names = g_ptr_array_new();
971 read_globicons();
972 mount_update(FALSE);
973 if (dir->error)
975 null_g_free(&dir->error);
976 dir_error_changed(dir);
979 /* Saves statting the parent for each item... */
980 if (mc_stat(pathname, &dir->stat_info))
982 dir->error = g_strdup_printf(_("Can't stat directory: %s"),
983 g_strerror(errno));
984 dir_error_changed(dir);
985 return; /* Report on attach */
988 d = mc_opendir(pathname);
989 if (!d)
991 dir->error = g_strdup_printf(_("Can't open directory: %s"),
992 g_strerror(errno));
993 dir_error_changed(dir);
994 return; /* Report on attach */
997 dir_set_scanning(dir, TRUE);
998 dir_merge_new(dir);
999 gdk_flush();
1001 /* Make a list of all the names in the directory */
1002 while ((ent = mc_readdir(d)))
1004 if (ent->d_name[0] == '.')
1006 if (ent->d_name[1] == '\0')
1007 continue; /* Ignore '.' */
1008 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
1009 continue; /* Ignore '..' */
1012 g_ptr_array_add(names, g_strdup(ent->d_name));
1014 mc_closedir(d);
1016 /* Compare the list with the current DirItems, removing
1017 * any that are missing.
1019 remove_missing(dir, names);
1021 free_recheck_list(dir);
1023 /* For each name found, mark it as needing to be put on the rescan
1024 * list at some point in the future.
1025 * If the item is new, put a blank place-holder item in the directory.
1027 for (i = 0; i < names->len; i++)
1029 DirItem *old;
1030 guchar *name = names->pdata[i];
1032 old = g_hash_table_lookup(dir->known_items, name);
1033 if (old)
1035 /* This flag is cleared when the item is added
1036 * to the rescan list.
1038 old->flags |= ITEM_FLAG_NEED_RESCAN_QUEUE;
1040 else
1042 DirItem *new;
1044 new = diritem_new(name);
1045 g_ptr_array_add(dir->new_items, new);
1050 dir_merge_new(dir);
1052 /* Ask everyone which items they need to display, and add them to
1053 * the recheck list. Typically, this means we don't waste time
1054 * scanning hidden items.
1056 in_callback++;
1057 for (next = dir->users; next; next = next->next)
1059 DirUser *user = (DirUser *) next->data;
1060 user->callback(dir,
1061 DIR_QUEUE_INTERESTING,
1062 NULL, user->data);
1064 in_callback--;
1066 g_ptr_array_free(names, TRUE);
1068 set_idle_callback(dir);
1069 dir_merge_new(dir);
1072 #ifdef USE_DNOTIFY
1073 /* Signal handler - don't do anything dangerous here */
1074 static void dnotify_handler(int sig, siginfo_t *si, void *data)
1076 /* Note: there is currently only one place to store the fd,
1077 * so we'll miss updates to several directories if they happen
1078 * close together.
1080 dnotify_last_fd = si->si_fd;
1081 dnotify_wakeup_flag = TRUE;
1082 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
1084 #endif