r3941: Fixed error in tooltip in bulk rename box (Vincent Lef��vre).
[rox-filer/translations.git] / ROX-Filer / src / dir.c
blobed90a464efd0a4a7c9b55fd4242ebbb371596457
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 in_callback--;
371 /* Notify everyone that the error status of the directory has changed */
372 static void dir_error_changed(Directory *dir)
374 GList *next;
376 in_callback++;
378 for (next = dir->users; next; next = next->next)
380 DirUser *user = (DirUser *) next->data;
382 user->callback(dir, DIR_ERROR_CHANGED, NULL, user->data);
385 in_callback--;
388 /* This is called in the background when there are items on the
389 * dir->recheck_list to process.
391 static gboolean recheck_callback(gpointer data)
393 Directory *dir = (Directory *) data;
394 GList *next;
395 guchar *leaf;
397 g_return_val_if_fail(dir != NULL, FALSE);
398 g_return_val_if_fail(dir->recheck_list != NULL, FALSE);
400 /* Remove the first name from the list */
401 next = dir->recheck_list;
402 dir->recheck_list = g_list_remove_link(dir->recheck_list, next);
403 leaf = (guchar *) next->data;
404 g_list_free_1(next);
406 /* usleep(800); */
408 insert_item(dir, leaf);
410 g_free(leaf);
412 if (dir->recheck_list)
413 return TRUE; /* Call again */
415 /* The recheck_list list empty. Stop scanning, unless
416 * needs_update, in which case we start scanning again.
419 dir_merge_new(dir);
421 dir->have_scanned = TRUE;
422 dir_set_scanning(dir, FALSE);
423 g_source_remove(dir->idle_callback);
424 dir->idle_callback = 0;
426 if (dir->needs_update)
427 dir_rescan(dir);
429 return FALSE;
432 /* Add all the new items to the items array.
433 * Notify everyone who is watching us.
435 void dir_merge_new(Directory *dir)
437 GPtrArray *new = dir->new_items;
438 GPtrArray *up = dir->up_items;
439 GPtrArray *gone = dir->gone_items;
440 GList *list;
441 guint i;
443 in_callback++;
445 for (list = dir->users; list; list = list->next)
447 DirUser *user = (DirUser *) list->data;
449 if (new->len)
450 user->callback(dir, DIR_ADD, new, user->data);
451 if (up->len)
452 user->callback(dir, DIR_UPDATE, up, user->data);
453 if (gone->len)
454 user->callback(dir, DIR_REMOVE, gone, user->data);
457 in_callback--;
459 for (i = 0; i < new->len; i++)
461 DirItem *item = (DirItem *) new->pdata[i];
463 g_hash_table_insert(dir->known_items, item->leafname, item);
466 for (i = 0; i < gone->len; i++)
468 DirItem *item = (DirItem *) gone->pdata[i];
470 diritem_free(item);
473 g_ptr_array_set_size(gone, 0);
474 g_ptr_array_set_size(new, 0);
475 g_ptr_array_set_size(up, 0);
478 #ifdef USE_DNOTIFY
479 /* Called from the mainloop shortly after dnotify_handler */
480 void dnotify_wakeup(void)
482 Directory *dir;
484 dnotify_wakeup_flag = FALSE;
486 dir = g_hash_table_lookup(dnotify_fd_to_dir,
487 GINT_TO_POINTER(dnotify_last_fd));
489 if (dir)
490 dir_rescan_soon(dir);
492 #endif
494 /****************************************************************
495 * INTERNAL FUNCTIONS *
496 ****************************************************************/
498 #ifdef USE_DNOTIFY
499 static gint rescan_soon_timeout(gpointer data)
501 Directory *dir = (Directory *) data;
503 dir->rescan_timeout = -1;
504 if (dir->scanning)
505 dir->needs_update = TRUE;
506 else
507 dir_rescan(dir);
508 return FALSE;
511 /* Wait a fraction of a second and then rescan. If already waiting,
512 * this function does nothing.
514 static void dir_rescan_soon(Directory *dir)
516 if (dir->rescan_timeout != -1)
517 return;
518 dir->rescan_timeout = g_timeout_add(500, rescan_soon_timeout, dir);
520 #endif
522 static void free_items_array(GPtrArray *array)
524 guint i;
526 for (i = 0; i < array->len; i++)
528 DirItem *item = (DirItem *) array->pdata[i];
530 diritem_free(item);
533 g_ptr_array_free(array, TRUE);
536 /* Tell everyone watching that these items have gone */
537 static void notify_deleted(Directory *dir, GPtrArray *deleted)
539 GList *next;
541 if (!deleted->len)
542 return;
544 in_callback++;
546 for (next = dir->users; next; next = next->next)
548 DirUser *user = (DirUser *) next->data;
550 user->callback(dir, DIR_REMOVE, deleted, user->data);
553 in_callback--;
556 static void mark_unused(gpointer key, gpointer value, gpointer data)
558 DirItem *item = (DirItem *) value;
560 item->may_delete = TRUE;
563 static void keep_deleted(gpointer key, gpointer value, gpointer data)
565 DirItem *item = (DirItem *) value;
566 GPtrArray *deleted = (GPtrArray *) data;
568 if (item->may_delete)
569 g_ptr_array_add(deleted, item);
572 static gboolean check_unused(gpointer key, gpointer value, gpointer data)
574 DirItem *item = (DirItem *) value;
576 return item->may_delete;
579 /* Remove all the old items that have gone.
580 * Notify everyone who is watching us of the removed items.
582 static void remove_missing(Directory *dir, GPtrArray *keep)
584 GPtrArray *deleted;
585 guint i;
587 deleted = g_ptr_array_new();
589 /* Mark all current items as may_delete */
590 g_hash_table_foreach(dir->known_items, mark_unused, NULL);
592 /* Unmark all items also in 'keep' */
593 for (i = 0; i < keep->len; i++)
595 guchar *leaf = (guchar *) keep->pdata[i];
596 DirItem *item;
598 item = g_hash_table_lookup(dir->known_items, leaf);
600 if (item)
601 item->may_delete = FALSE;
604 /* Add each item still marked to 'deleted' */
605 g_hash_table_foreach(dir->known_items, keep_deleted, deleted);
607 /* Remove all items still marked */
608 g_hash_table_foreach_remove(dir->known_items, check_unused, NULL);
610 notify_deleted(dir, deleted);
612 free_items_array(deleted);
615 static gint notify_timeout(gpointer data)
617 Directory *dir = (Directory *) data;
619 g_return_val_if_fail(dir->notify_active == TRUE, FALSE);
621 dir_merge_new(dir);
623 dir->notify_active = FALSE;
624 g_object_unref(dir);
626 return FALSE;
629 /* Call dir_merge_new() after a while. */
630 static void delayed_notify(Directory *dir)
632 if (dir->notify_active)
633 return;
634 g_object_ref(dir);
635 g_timeout_add(1500, notify_timeout, dir);
636 dir->notify_active = TRUE;
639 /* Stat this item and add, update or remove it.
640 * Returns the new/updated item, if any.
641 * (leafname may be from the current DirItem item)
642 * Ensure diritem_recent_time is reasonably up-to-date before calling this.
644 static DirItem *insert_item(Directory *dir, const guchar *leafname)
646 const gchar *full_path;
647 DirItem *item;
648 DirItem old;
649 gboolean do_compare = FALSE; /* (old is filled in) */
651 if (leafname[0] == '.' && (leafname[1] == '\n' ||
652 (leafname[1] == '.' && leafname[2] == '\n')))
653 return NULL; /* Ignore '.' and '..' */
655 full_path = make_path(dir->pathname, leafname);
656 item = g_hash_table_lookup(dir->known_items, leafname);
658 if (item)
660 if (item->base_type != TYPE_UNKNOWN)
662 /* Preserve the old details so we can compare */
663 old = *item;
664 if (old._image)
665 g_object_ref(old._image);
666 do_compare = TRUE;
668 diritem_restat(full_path, item, &dir->stat_info);
670 else
672 /* Item isn't already here. This won't normally happen,
673 * because blank items are added when scanning, before
674 * we get here.
676 item = diritem_new(leafname);
677 diritem_restat(full_path, item, &dir->stat_info);
678 if (item->base_type == TYPE_ERROR &&
679 item->lstat_errno == ENOENT)
681 diritem_free(item);
682 return NULL;
684 g_ptr_array_add(dir->new_items, item);
688 /* No need to queue the item for scanning. If we got here because
689 * the item was queued, this flag will normally already be clear.
691 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
693 if (item->base_type == TYPE_ERROR && item->lstat_errno == ENOENT)
695 /* Item has been deleted */
696 g_hash_table_remove(dir->known_items, item->leafname);
697 g_ptr_array_add(dir->gone_items, item);
698 if (do_compare && old._image)
699 g_object_unref(old._image);
700 delayed_notify(dir);
701 return NULL;
704 if (do_compare)
706 /* It's a bit inefficient that we force the image to be
707 * loaded here, if we had an old image.
709 if (item->lstat_errno == old.lstat_errno
710 && item->base_type == old.base_type
711 && item->flags == old.flags
712 && item->size == old.size
713 && item->mode == old.mode
714 && item->atime == old.atime
715 && item->ctime == old.ctime
716 && item->mtime == old.mtime
717 && item->uid == old.uid
718 && item->gid == old.gid
719 && item->mime_type == old.mime_type
720 && (old._image == NULL || di_image(item) == old._image))
722 if (old._image)
723 g_object_unref(old._image);
724 return item;
726 if (old._image)
727 g_object_unref(old._image);
730 g_ptr_array_add(dir->up_items, item);
731 delayed_notify(dir);
733 return item;
736 static void update(Directory *dir, gchar *pathname, gpointer data)
738 g_free(dir->pathname);
739 dir->pathname = pathdup(pathname);
741 if (dir->scanning)
742 dir->needs_update = TRUE;
743 else
744 dir_rescan(dir);
747 /* If there is work to do, set the idle callback.
748 * Otherwise, stop scanning and unset the idle callback.
750 static void set_idle_callback(Directory *dir)
752 if (dir->recheck_list && dir->users)
754 /* Work to do, and someone's watching */
755 dir_set_scanning(dir, TRUE);
756 if (dir->idle_callback)
757 return;
758 time(&diritem_recent_time);
759 dir->idle_callback = g_idle_add(recheck_callback, dir);
760 /* Do the first call now (will remove the callback itself) */
761 recheck_callback(dir);
763 else
765 dir_set_scanning(dir, FALSE);
766 if (dir->idle_callback)
768 g_source_remove(dir->idle_callback);
769 dir->idle_callback = 0;
774 /* See dir_force_update_path() */
775 static void dir_force_update_item(Directory *dir, const gchar *leaf)
777 GList *list;
778 GPtrArray *items;
779 DirItem *item;
781 items = g_ptr_array_new();
783 item = g_hash_table_lookup(dir->known_items, leaf);
784 if (!item)
785 goto out;
787 g_ptr_array_add(items, item);
789 in_callback++;
791 for (list = dir->users; list; list = list->next)
793 DirUser *user = (DirUser *) list->data;
795 user->callback(dir, DIR_UPDATE, items, user->data);
798 in_callback--;
800 out:
801 g_ptr_array_free(items, TRUE);
804 static void dir_recheck(Directory *dir,
805 const guchar *path, const guchar *leafname)
807 guchar *old = dir->pathname;
809 dir->pathname = g_strdup(path);
810 g_free(old);
812 time(&diritem_recent_time);
813 insert_item(dir, leafname);
816 static void to_array(gpointer key, gpointer value, gpointer data)
818 GPtrArray *array = (GPtrArray *) data;
820 g_ptr_array_add(array, value);
823 /* Convert a hash table to an unsorted GPtrArray.
824 * g_ptr_array_free() the result.
826 static GPtrArray *hash_to_array(GHashTable *hash)
828 GPtrArray *array;
830 array = g_ptr_array_new();
832 g_hash_table_foreach(hash, to_array, array);
834 return array;
837 static gpointer parent_class;
839 /* Note: dir_cache is never purged, so this shouldn't get called */
840 static void dir_finialize(GObject *object)
842 GPtrArray *items;
843 Directory *dir = (Directory *) object;
845 g_return_if_fail(dir->users == NULL);
847 g_print("[ dir finalize ]\n");
849 free_recheck_list(dir);
850 set_idle_callback(dir);
851 if (dir->rescan_timeout != -1)
852 g_source_remove(dir->rescan_timeout);
854 dir_merge_new(dir); /* Ensures new, up and gone are empty */
856 g_ptr_array_free(dir->up_items, TRUE);
857 g_ptr_array_free(dir->new_items, TRUE);
858 g_ptr_array_free(dir->gone_items, TRUE);
860 items = hash_to_array(dir->known_items);
861 free_items_array(items);
862 g_hash_table_destroy(dir->known_items);
864 g_free(dir->error);
865 g_free(dir->pathname);
867 G_OBJECT_CLASS(parent_class)->finalize(object);
870 static void directory_class_init(gpointer gclass, gpointer data)
872 GObjectClass *object = (GObjectClass *) gclass;
874 parent_class = g_type_class_peek_parent(gclass);
876 object->finalize = dir_finialize;
879 static void directory_init(GTypeInstance *object, gpointer gclass)
881 Directory *dir = (Directory *) object;
883 dir->known_items = g_hash_table_new(g_str_hash, g_str_equal);
884 dir->recheck_list = NULL;
885 dir->idle_callback = 0;
886 dir->scanning = FALSE;
887 dir->have_scanned = FALSE;
889 dir->users = NULL;
890 dir->needs_update = TRUE;
891 dir->notify_active = FALSE;
892 dir->pathname = NULL;
893 dir->error = NULL;
894 dir->rescan_timeout = -1;
895 #ifdef USE_DNOTIFY
896 dir->dnotify_fd = -1;
897 #endif
899 dir->new_items = g_ptr_array_new();
900 dir->up_items = g_ptr_array_new();
901 dir->gone_items = g_ptr_array_new();
904 static GType dir_get_type(void)
906 static GType type = 0;
908 if (!type)
910 static const GTypeInfo info =
912 sizeof (DirectoryClass),
913 NULL, /* base_init */
914 NULL, /* base_finalise */
915 directory_class_init,
916 NULL, /* class_finalise */
917 NULL, /* class_data */
918 sizeof(Directory),
919 0, /* n_preallocs */
920 directory_init
923 type = g_type_register_static(G_TYPE_OBJECT, "Directory",
924 &info, 0);
927 return type;
930 static Directory *dir_new(const char *pathname)
932 Directory *dir;
934 dir = g_object_new(dir_get_type(), NULL);
936 dir->pathname = g_strdup(pathname);
938 return dir;
941 /* Get the names of all files in the directory.
942 * Remove any DirItems that are no longer listed.
943 * Replace the recheck_list with the items found.
945 static void dir_rescan(Directory *dir)
947 GPtrArray *names;
948 DIR *d;
949 struct dirent *ent;
950 guint i;
951 const char *pathname;
952 GList *next;
954 g_return_if_fail(dir != NULL);
956 pathname = dir->pathname;
958 dir->needs_update = FALSE;
960 names = g_ptr_array_new();
962 read_globicons();
963 mount_update(FALSE);
964 if (dir->error)
966 null_g_free(&dir->error);
967 dir_error_changed(dir);
970 /* Saves statting the parent for each item... */
971 if (mc_stat(pathname, &dir->stat_info))
973 dir->error = g_strdup_printf(_("Can't stat directory: %s"),
974 g_strerror(errno));
975 dir_error_changed(dir);
976 return; /* Report on attach */
979 d = mc_opendir(pathname);
980 if (!d)
982 dir->error = g_strdup_printf(_("Can't open directory: %s"),
983 g_strerror(errno));
984 dir_error_changed(dir);
985 return; /* Report on attach */
988 dir_set_scanning(dir, TRUE);
989 dir_merge_new(dir);
990 gdk_flush();
992 /* Make a list of all the names in the directory */
993 while ((ent = mc_readdir(d)))
995 if (ent->d_name[0] == '.')
997 if (ent->d_name[1] == '\0')
998 continue; /* Ignore '.' */
999 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
1000 continue; /* Ignore '..' */
1003 g_ptr_array_add(names, g_strdup(ent->d_name));
1005 mc_closedir(d);
1007 /* Compare the list with the current DirItems, removing
1008 * any that are missing.
1010 remove_missing(dir, names);
1012 free_recheck_list(dir);
1014 /* For each name found, mark it as needing to be put on the rescan
1015 * list at some point in the future.
1016 * If the item is new, put a blank place-holder item in the directory.
1018 for (i = 0; i < names->len; i++)
1020 DirItem *old;
1021 guchar *name = names->pdata[i];
1023 old = g_hash_table_lookup(dir->known_items, name);
1024 if (old)
1026 /* This flag is cleared when the item is added
1027 * to the rescan list.
1029 old->flags |= ITEM_FLAG_NEED_RESCAN_QUEUE;
1031 else
1033 DirItem *new;
1035 new = diritem_new(name);
1036 g_ptr_array_add(dir->new_items, new);
1041 dir_merge_new(dir);
1043 /* Ask everyone which items they need to display, and add them to
1044 * the recheck list. Typically, this means we don't waste time
1045 * scanning hidden items.
1047 in_callback++;
1048 for (next = dir->users; next; next = next->next)
1050 DirUser *user = (DirUser *) next->data;
1051 user->callback(dir,
1052 DIR_QUEUE_INTERESTING,
1053 NULL, user->data);
1055 in_callback--;
1057 g_ptr_array_free(names, TRUE);
1059 set_idle_callback(dir);
1060 dir_merge_new(dir);
1063 #ifdef USE_DNOTIFY
1064 /* Signal handler - don't do anything dangerous here */
1065 static void dnotify_handler(int sig, siginfo_t *si, void *data)
1067 /* Note: there is currently only one place to store the fd,
1068 * so we'll miss updates to several directories if they happen
1069 * close together.
1071 dnotify_last_fd = si->si_fd;
1072 dnotify_wakeup_flag = TRUE;
1073 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
1075 #endif