r4800: Updated Russian translation (Nikita E. Shalaev).
[rox-filer.git] / ROX-Filer / src / dir.c
blobfa820cfa0939670089e432061d2be149b5d2d3eb
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_DNOTIFY
73 /* Newer Linux kernels can tell us when the directories we are watching
74 * change, using the dnotify system.
76 static GHashTable *dnotify_fd_to_dir = NULL;
77 gboolean dnotify_wakeup_flag = FALSE;
78 static int dnotify_last_fd = -1;
79 #endif
81 /* For debugging. Can't detach when this is non-zero. */
82 static int in_callback = 0;
84 GFSCache *dir_cache = NULL;
86 /* Static prototypes */
87 static void update(Directory *dir, gchar *pathname, gpointer data);
88 static void set_idle_callback(Directory *dir);
89 static DirItem *insert_item(Directory *dir, const guchar *leafname);
90 static void remove_missing(Directory *dir, GPtrArray *keep);
91 static void dir_recheck(Directory *dir,
92 const guchar *path, const guchar *leafname);
93 static GPtrArray *hash_to_array(GHashTable *hash);
94 static void dir_force_update_item(Directory *dir, const gchar *leaf);
95 static Directory *dir_new(const char *pathname);
96 static void dir_rescan(Directory *dir);
97 #ifdef USE_DNOTIFY
98 static void dir_rescan_soon(Directory *dir);
99 static void dnotify_handler(int sig, siginfo_t *si, void *data);
100 #endif
102 /****************************************************************
103 * EXTERNAL INTERFACE *
104 ****************************************************************/
106 void dir_init(void)
108 dir_cache = g_fscache_new((GFSLoadFunc) dir_new,
109 (GFSUpdateFunc) update, NULL);
111 /* Check for dnotify support in the kernel */
112 #ifdef USE_DNOTIFY
114 struct sigaction act;
116 act.sa_sigaction = dnotify_handler;
117 sigemptyset(&act.sa_mask);
118 act.sa_flags = SA_SIGINFO;
119 sigaction(SIGRTMIN, &act, NULL);
121 /* Sometimes we get this instead of SIGRTMIN.
122 * Don't know why :-( but don't crash...
124 act.sa_handler = SIG_IGN;
125 sigemptyset(&act.sa_mask);
126 act.sa_flags = 0;
127 sigaction(SIGIO, &act, NULL);
129 dnotify_fd_to_dir = g_hash_table_new(NULL, NULL);
131 #endif
134 /* Periodically calls callback to notify about changes to the contents
135 * of the directory.
136 * Before this function returns, it calls the callback once to add all
137 * the items currently in the directory (unless the dir is empty).
138 * It then calls callback(DIR_QUEUE_INTERESTING) to find out which items the
139 * caller cares about.
140 * If we are not scanning, it also calls callback(DIR_END_SCAN).
142 void dir_attach(Directory *dir, DirCallback callback, gpointer data)
144 DirUser *user;
145 GPtrArray *items;
147 g_return_if_fail(dir != NULL);
148 g_return_if_fail(callback != NULL);
150 user = g_new(DirUser, 1);
151 user->callback = callback;
152 user->data = data;
154 #ifdef USE_DNOTIFY
155 if (!dir->users)
157 int fd;
159 if (dir->dnotify_fd != -1)
160 g_warning("dir_attach: dnotify error\n");
162 fd = open(dir->pathname, O_RDONLY);
163 g_return_if_fail(g_hash_table_lookup(dnotify_fd_to_dir,
164 GINT_TO_POINTER(fd)) == NULL);
165 if (fd != -1)
167 dir->dnotify_fd = fd;
168 g_hash_table_insert(dnotify_fd_to_dir,
169 GINT_TO_POINTER(fd), dir);
170 fcntl(fd, F_SETSIG, SIGRTMIN);
171 fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
172 DN_ATTRIB | DN_MULTISHOT);
175 #endif
177 dir->users = g_list_prepend(dir->users, user);
179 g_object_ref(dir);
181 items = hash_to_array(dir->known_items);
182 if (items->len)
183 callback(dir, DIR_ADD, items, data);
184 g_ptr_array_free(items, TRUE);
186 if (dir->needs_update && !dir->scanning)
187 dir_rescan(dir);
188 else
189 callback(dir, DIR_QUEUE_INTERESTING, NULL, data);
191 /* May start scanning if noone was watching before */
192 set_idle_callback(dir);
194 if (!dir->scanning)
195 callback(dir, DIR_END_SCAN, NULL, data);
198 /* Undo the effect of dir_attach */
199 void dir_detach(Directory *dir, DirCallback callback, gpointer data)
201 DirUser *user;
202 GList *list;
204 g_return_if_fail(dir != NULL);
205 g_return_if_fail(callback != NULL);
206 g_return_if_fail(in_callback == 0);
208 for (list = dir->users; list; list = list->next)
210 user = (DirUser *) list->data;
211 if (user->callback == callback && user->data == data)
213 g_free(user);
214 dir->users = g_list_remove(dir->users, user);
215 g_object_unref(dir);
217 /* May stop scanning if noone's watching */
218 set_idle_callback(dir);
220 #ifdef USE_DNOTIFY
221 if (!dir->users && dir->dnotify_fd != -1)
223 close(dir->dnotify_fd);
224 g_hash_table_remove(dnotify_fd_to_dir,
225 GINT_TO_POINTER(dir->dnotify_fd));
226 dir->dnotify_fd = -1;
228 #endif
229 return;
233 g_warning("dir_detach: Callback/data pair not attached!\n");
236 void dir_update(Directory *dir, gchar *pathname)
238 update(dir, pathname, NULL);
241 /* Rescan this directory */
242 void refresh_dirs(const char *path)
244 g_fscache_update(dir_cache, path);
247 /* When something has happened to a particular object, call this
248 * and all appropriate changes will be made.
250 void dir_check_this(const guchar *path)
252 guchar *real_path;
253 guchar *dir_path;
254 Directory *dir;
256 dir_path = g_path_get_dirname(path);
257 real_path = pathdup(dir_path);
258 g_free(dir_path);
260 dir = g_fscache_lookup_full(dir_cache, real_path,
261 FSCACHE_LOOKUP_PEEK, NULL);
262 if (dir)
264 dir_recheck(dir, real_path, g_basename(path));
265 g_object_unref(dir);
268 g_free(real_path);
271 #ifdef USE_DNOTIFY
272 static void drop_dnotify(gpointer key, gpointer value, gpointer data)
274 close(GPOINTER_TO_INT(key));
276 #endif
278 /* Used when we fork an action child, otherwise we can't delete or unmount
279 * any directory which we're watching!
281 void dir_drop_all_dnotifies(void)
283 #ifdef USE_DNOTIFY
284 g_hash_table_foreach(dnotify_fd_to_dir, drop_dnotify, NULL);
285 #endif
288 /* Tell watchers that this item has changed, but don't rescan.
289 * (used when thumbnail has been created for an item)
291 void dir_force_update_path(const gchar *path)
293 gchar *dir_path;
294 Directory *dir;
296 g_return_if_fail(path[0] == '/');
298 dir_path = g_path_get_dirname(path);
300 dir = g_fscache_lookup_full(dir_cache, dir_path, FSCACHE_LOOKUP_PEEK,
301 NULL);
302 if (dir)
304 dir_force_update_item(dir, g_basename(path));
305 g_object_unref(dir);
308 g_free(dir_path);
311 /* Ensure that 'leafname' is up-to-date. Returns the new/updated
312 * DirItem, or NULL if the file no longer exists.
314 DirItem *dir_update_item(Directory *dir, const gchar *leafname)
316 DirItem *item;
318 time(&diritem_recent_time);
319 item = insert_item(dir, leafname);
320 dir_merge_new(dir);
322 return item;
325 /* Add item to the recheck_list if it's marked as needing it.
326 * Item must have ITEM_FLAG_NEED_RESCAN_QUEUE.
327 * Items on the list will get checked later in an idle callback.
329 void dir_queue_recheck(Directory *dir, DirItem *item)
331 g_return_if_fail(dir != NULL);
332 g_return_if_fail(item != NULL);
333 g_return_if_fail(item->flags & ITEM_FLAG_NEED_RESCAN_QUEUE);
335 dir->recheck_list = g_list_prepend(dir->recheck_list,
336 g_strdup(item->leafname));
337 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
340 static void free_recheck_list(Directory *dir)
342 destroy_glist(&dir->recheck_list);
345 /* If scanning state has changed then notify all filer windows */
346 static void dir_set_scanning(Directory *dir, gboolean scanning)
348 GList *next;
350 if (scanning == dir->scanning)
351 return;
353 in_callback++;
355 dir->scanning = scanning;
357 for (next = dir->users; next; next = next->next)
359 DirUser *user = (DirUser *) next->data;
361 user->callback(dir,
362 scanning ? DIR_START_SCAN : DIR_END_SCAN,
363 NULL, user->data);
366 #if 0
367 /* Useful for profiling */
368 if (!scanning)
370 g_print("Done\n");
371 exit(0);
373 #endif
375 in_callback--;
378 /* Notify everyone that the error status of the directory has changed */
379 static void dir_error_changed(Directory *dir)
381 GList *next;
383 in_callback++;
385 for (next = dir->users; next; next = next->next)
387 DirUser *user = (DirUser *) next->data;
389 user->callback(dir, DIR_ERROR_CHANGED, NULL, user->data);
392 in_callback--;
395 /* This is called in the background when there are items on the
396 * dir->recheck_list to process.
398 static gboolean recheck_callback(gpointer data)
400 Directory *dir = (Directory *) data;
401 GList *next;
402 guchar *leaf;
404 g_return_val_if_fail(dir != NULL, FALSE);
405 g_return_val_if_fail(dir->recheck_list != NULL, FALSE);
407 /* Remove the first name from the list */
408 next = dir->recheck_list;
409 dir->recheck_list = g_list_remove_link(dir->recheck_list, next);
410 leaf = (guchar *) next->data;
411 g_list_free_1(next);
413 /* usleep(800); */
415 insert_item(dir, leaf);
417 g_free(leaf);
419 if (dir->recheck_list)
420 return TRUE; /* Call again */
422 /* The recheck_list list empty. Stop scanning, unless
423 * needs_update, in which case we start scanning again.
426 dir_merge_new(dir);
428 dir->have_scanned = TRUE;
429 dir_set_scanning(dir, FALSE);
430 g_source_remove(dir->idle_callback);
431 dir->idle_callback = 0;
433 if (dir->needs_update)
434 dir_rescan(dir);
436 return FALSE;
439 /* Add all the new items to the items array.
440 * Notify everyone who is watching us.
442 void dir_merge_new(Directory *dir)
444 GPtrArray *new = dir->new_items;
445 GPtrArray *up = dir->up_items;
446 GPtrArray *gone = dir->gone_items;
447 GList *list;
448 guint i;
450 in_callback++;
452 for (list = dir->users; list; list = list->next)
454 DirUser *user = (DirUser *) list->data;
456 if (new->len)
457 user->callback(dir, DIR_ADD, new, user->data);
458 if (up->len)
459 user->callback(dir, DIR_UPDATE, up, user->data);
460 if (gone->len)
461 user->callback(dir, DIR_REMOVE, gone, user->data);
464 in_callback--;
466 for (i = 0; i < new->len; i++)
468 DirItem *item = (DirItem *) new->pdata[i];
470 g_hash_table_insert(dir->known_items, item->leafname, item);
473 for (i = 0; i < gone->len; i++)
475 DirItem *item = (DirItem *) gone->pdata[i];
477 diritem_free(item);
480 g_ptr_array_set_size(gone, 0);
481 g_ptr_array_set_size(new, 0);
482 g_ptr_array_set_size(up, 0);
485 #ifdef USE_DNOTIFY
486 /* Called from the mainloop shortly after dnotify_handler */
487 void dnotify_wakeup(void)
489 Directory *dir;
491 dnotify_wakeup_flag = FALSE;
493 dir = g_hash_table_lookup(dnotify_fd_to_dir,
494 GINT_TO_POINTER(dnotify_last_fd));
496 if (dir)
497 dir_rescan_soon(dir);
499 #endif
501 /****************************************************************
502 * INTERNAL FUNCTIONS *
503 ****************************************************************/
505 #ifdef USE_DNOTIFY
506 static gint rescan_soon_timeout(gpointer data)
508 Directory *dir = (Directory *) data;
510 dir->rescan_timeout = -1;
511 if (dir->scanning)
512 dir->needs_update = TRUE;
513 else
514 dir_rescan(dir);
515 return FALSE;
518 /* Wait a fraction of a second and then rescan. If already waiting,
519 * this function does nothing.
521 static void dir_rescan_soon(Directory *dir)
523 if (dir->rescan_timeout != -1)
524 return;
525 dir->rescan_timeout = g_timeout_add(500, rescan_soon_timeout, dir);
527 #endif
529 static void free_items_array(GPtrArray *array)
531 guint i;
533 for (i = 0; i < array->len; i++)
535 DirItem *item = (DirItem *) array->pdata[i];
537 diritem_free(item);
540 g_ptr_array_free(array, TRUE);
543 /* Tell everyone watching that these items have gone */
544 static void notify_deleted(Directory *dir, GPtrArray *deleted)
546 GList *next;
548 if (!deleted->len)
549 return;
551 in_callback++;
553 for (next = dir->users; next; next = next->next)
555 DirUser *user = (DirUser *) next->data;
557 user->callback(dir, DIR_REMOVE, deleted, user->data);
560 in_callback--;
563 static void mark_unused(gpointer key, gpointer value, gpointer data)
565 DirItem *item = (DirItem *) value;
567 item->may_delete = TRUE;
570 static void keep_deleted(gpointer key, gpointer value, gpointer data)
572 DirItem *item = (DirItem *) value;
573 GPtrArray *deleted = (GPtrArray *) data;
575 if (item->may_delete)
576 g_ptr_array_add(deleted, item);
579 static gboolean check_unused(gpointer key, gpointer value, gpointer data)
581 DirItem *item = (DirItem *) value;
583 return item->may_delete;
586 /* Remove all the old items that have gone.
587 * Notify everyone who is watching us of the removed items.
589 static void remove_missing(Directory *dir, GPtrArray *keep)
591 GPtrArray *deleted;
592 guint i;
594 deleted = g_ptr_array_new();
596 /* Mark all current items as may_delete */
597 g_hash_table_foreach(dir->known_items, mark_unused, NULL);
599 /* Unmark all items also in 'keep' */
600 for (i = 0; i < keep->len; i++)
602 guchar *leaf = (guchar *) keep->pdata[i];
603 DirItem *item;
605 item = g_hash_table_lookup(dir->known_items, leaf);
607 if (item)
608 item->may_delete = FALSE;
611 /* Add each item still marked to 'deleted' */
612 g_hash_table_foreach(dir->known_items, keep_deleted, deleted);
614 /* Remove all items still marked */
615 g_hash_table_foreach_remove(dir->known_items, check_unused, NULL);
617 notify_deleted(dir, deleted);
619 free_items_array(deleted);
622 static gint notify_timeout(gpointer data)
624 Directory *dir = (Directory *) data;
626 g_return_val_if_fail(dir->notify_active == TRUE, FALSE);
628 dir_merge_new(dir);
630 dir->notify_active = FALSE;
631 g_object_unref(dir);
633 return FALSE;
636 /* Call dir_merge_new() after a while. */
637 static void delayed_notify(Directory *dir)
639 if (dir->notify_active)
640 return;
641 g_object_ref(dir);
642 g_timeout_add(1500, notify_timeout, dir);
643 dir->notify_active = TRUE;
646 /* Stat this item and add, update or remove it.
647 * Returns the new/updated item, if any.
648 * (leafname may be from the current DirItem item)
649 * Ensure diritem_recent_time is reasonably up-to-date before calling this.
651 static DirItem *insert_item(Directory *dir, const guchar *leafname)
653 const gchar *full_path;
654 DirItem *item;
655 DirItem old;
656 gboolean do_compare = FALSE; /* (old is filled in) */
658 if (leafname[0] == '.' && (leafname[1] == '\n' ||
659 (leafname[1] == '.' && leafname[2] == '\n')))
660 return NULL; /* Ignore '.' and '..' */
662 full_path = make_path(dir->pathname, leafname);
663 item = g_hash_table_lookup(dir->known_items, leafname);
665 if (item)
667 if (item->base_type != TYPE_UNKNOWN)
669 /* Preserve the old details so we can compare */
670 old = *item;
671 if (old._image)
672 g_object_ref(old._image);
673 do_compare = TRUE;
675 diritem_restat(full_path, item, &dir->stat_info);
677 else
679 /* Item isn't already here. This won't normally happen,
680 * because blank items are added when scanning, before
681 * we get here.
683 item = diritem_new(leafname);
684 diritem_restat(full_path, item, &dir->stat_info);
685 if (item->base_type == TYPE_ERROR &&
686 item->lstat_errno == ENOENT)
688 diritem_free(item);
689 return NULL;
691 g_ptr_array_add(dir->new_items, item);
695 /* No need to queue the item for scanning. If we got here because
696 * the item was queued, this flag will normally already be clear.
698 item->flags &= ~ITEM_FLAG_NEED_RESCAN_QUEUE;
700 if (item->base_type == TYPE_ERROR && item->lstat_errno == ENOENT)
702 /* Item has been deleted */
703 g_hash_table_remove(dir->known_items, item->leafname);
704 g_ptr_array_add(dir->gone_items, item);
705 if (do_compare && old._image)
706 g_object_unref(old._image);
707 delayed_notify(dir);
708 return NULL;
711 if (do_compare)
713 /* It's a bit inefficient that we force the image to be
714 * loaded here, if we had an old image.
716 if (item->lstat_errno == old.lstat_errno
717 && item->base_type == old.base_type
718 && item->flags == old.flags
719 && item->size == old.size
720 && item->mode == old.mode
721 && item->atime == old.atime
722 && item->ctime == old.ctime
723 && item->mtime == old.mtime
724 && item->uid == old.uid
725 && item->gid == old.gid
726 && item->mime_type == old.mime_type
727 && (old._image == NULL || di_image(item) == old._image))
729 if (old._image)
730 g_object_unref(old._image);
731 return item;
733 if (old._image)
734 g_object_unref(old._image);
737 g_ptr_array_add(dir->up_items, item);
738 delayed_notify(dir);
740 return item;
743 static void update(Directory *dir, gchar *pathname, gpointer data)
745 g_free(dir->pathname);
746 dir->pathname = pathdup(pathname);
748 if (dir->scanning)
749 dir->needs_update = TRUE;
750 else
751 dir_rescan(dir);
754 /* If there is work to do, set the idle callback.
755 * Otherwise, stop scanning and unset the idle callback.
757 static void set_idle_callback(Directory *dir)
759 if (dir->recheck_list && dir->users)
761 /* Work to do, and someone's watching */
762 dir_set_scanning(dir, TRUE);
763 if (dir->idle_callback)
764 return;
765 time(&diritem_recent_time);
766 dir->idle_callback = g_idle_add(recheck_callback, dir);
767 /* Do the first call now (will remove the callback itself) */
768 recheck_callback(dir);
770 else
772 dir_set_scanning(dir, FALSE);
773 if (dir->idle_callback)
775 g_source_remove(dir->idle_callback);
776 dir->idle_callback = 0;
781 /* See dir_force_update_path() */
782 static void dir_force_update_item(Directory *dir, const gchar *leaf)
784 GList *list;
785 GPtrArray *items;
786 DirItem *item;
788 items = g_ptr_array_new();
790 item = g_hash_table_lookup(dir->known_items, leaf);
791 if (!item)
792 goto out;
794 g_ptr_array_add(items, item);
796 in_callback++;
798 for (list = dir->users; list; list = list->next)
800 DirUser *user = (DirUser *) list->data;
802 user->callback(dir, DIR_UPDATE, items, user->data);
805 in_callback--;
807 out:
808 g_ptr_array_free(items, TRUE);
811 static void dir_recheck(Directory *dir,
812 const guchar *path, const guchar *leafname)
814 guchar *old = dir->pathname;
816 dir->pathname = g_strdup(path);
817 g_free(old);
819 time(&diritem_recent_time);
820 insert_item(dir, leafname);
823 static void to_array(gpointer key, gpointer value, gpointer data)
825 GPtrArray *array = (GPtrArray *) data;
827 g_ptr_array_add(array, value);
830 /* Convert a hash table to an unsorted GPtrArray.
831 * g_ptr_array_free() the result.
833 static GPtrArray *hash_to_array(GHashTable *hash)
835 GPtrArray *array;
837 array = g_ptr_array_new();
839 g_hash_table_foreach(hash, to_array, array);
841 return array;
844 static gpointer parent_class;
846 /* Note: dir_cache is never purged, so this shouldn't get called */
847 static void dir_finialize(GObject *object)
849 GPtrArray *items;
850 Directory *dir = (Directory *) object;
852 g_return_if_fail(dir->users == NULL);
854 g_print("[ dir finalize ]\n");
856 free_recheck_list(dir);
857 set_idle_callback(dir);
858 if (dir->rescan_timeout != -1)
859 g_source_remove(dir->rescan_timeout);
861 dir_merge_new(dir); /* Ensures new, up and gone are empty */
863 g_ptr_array_free(dir->up_items, TRUE);
864 g_ptr_array_free(dir->new_items, TRUE);
865 g_ptr_array_free(dir->gone_items, TRUE);
867 items = hash_to_array(dir->known_items);
868 free_items_array(items);
869 g_hash_table_destroy(dir->known_items);
871 g_free(dir->error);
872 g_free(dir->pathname);
874 G_OBJECT_CLASS(parent_class)->finalize(object);
877 static void directory_class_init(gpointer gclass, gpointer data)
879 GObjectClass *object = (GObjectClass *) gclass;
881 parent_class = g_type_class_peek_parent(gclass);
883 object->finalize = dir_finialize;
886 static void directory_init(GTypeInstance *object, gpointer gclass)
888 Directory *dir = (Directory *) object;
890 dir->known_items = g_hash_table_new(g_str_hash, g_str_equal);
891 dir->recheck_list = NULL;
892 dir->idle_callback = 0;
893 dir->scanning = FALSE;
894 dir->have_scanned = FALSE;
896 dir->users = NULL;
897 dir->needs_update = TRUE;
898 dir->notify_active = FALSE;
899 dir->pathname = NULL;
900 dir->error = NULL;
901 dir->rescan_timeout = -1;
902 #ifdef USE_DNOTIFY
903 dir->dnotify_fd = -1;
904 #endif
906 dir->new_items = g_ptr_array_new();
907 dir->up_items = g_ptr_array_new();
908 dir->gone_items = g_ptr_array_new();
911 static GType dir_get_type(void)
913 static GType type = 0;
915 if (!type)
917 static const GTypeInfo info =
919 sizeof (DirectoryClass),
920 NULL, /* base_init */
921 NULL, /* base_finalise */
922 directory_class_init,
923 NULL, /* class_finalise */
924 NULL, /* class_data */
925 sizeof(Directory),
926 0, /* n_preallocs */
927 directory_init
930 type = g_type_register_static(G_TYPE_OBJECT, "Directory",
931 &info, 0);
934 return type;
937 static Directory *dir_new(const char *pathname)
939 Directory *dir;
941 dir = g_object_new(dir_get_type(), NULL);
943 dir->pathname = g_strdup(pathname);
945 return dir;
948 /* Get the names of all files in the directory.
949 * Remove any DirItems that are no longer listed.
950 * Replace the recheck_list with the items found.
952 static void dir_rescan(Directory *dir)
954 GPtrArray *names;
955 DIR *d;
956 struct dirent *ent;
957 guint i;
958 const char *pathname;
959 GList *next;
961 g_return_if_fail(dir != NULL);
963 pathname = dir->pathname;
965 dir->needs_update = FALSE;
967 names = g_ptr_array_new();
969 read_globicons();
970 mount_update(FALSE);
971 if (dir->error)
973 null_g_free(&dir->error);
974 dir_error_changed(dir);
977 /* Saves statting the parent for each item... */
978 if (mc_stat(pathname, &dir->stat_info))
980 dir->error = g_strdup_printf(_("Can't stat directory: %s"),
981 g_strerror(errno));
982 dir_error_changed(dir);
983 return; /* Report on attach */
986 d = mc_opendir(pathname);
987 if (!d)
989 dir->error = g_strdup_printf(_("Can't open directory: %s"),
990 g_strerror(errno));
991 dir_error_changed(dir);
992 return; /* Report on attach */
995 dir_set_scanning(dir, TRUE);
996 dir_merge_new(dir);
997 gdk_flush();
999 /* Make a list of all the names in the directory */
1000 while ((ent = mc_readdir(d)))
1002 if (ent->d_name[0] == '.')
1004 if (ent->d_name[1] == '\0')
1005 continue; /* Ignore '.' */
1006 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
1007 continue; /* Ignore '..' */
1010 g_ptr_array_add(names, g_strdup(ent->d_name));
1012 mc_closedir(d);
1014 /* Compare the list with the current DirItems, removing
1015 * any that are missing.
1017 remove_missing(dir, names);
1019 free_recheck_list(dir);
1021 /* For each name found, mark it as needing to be put on the rescan
1022 * list at some point in the future.
1023 * If the item is new, put a blank place-holder item in the directory.
1025 for (i = 0; i < names->len; i++)
1027 DirItem *old;
1028 guchar *name = names->pdata[i];
1030 old = g_hash_table_lookup(dir->known_items, name);
1031 if (old)
1033 /* This flag is cleared when the item is added
1034 * to the rescan list.
1036 old->flags |= ITEM_FLAG_NEED_RESCAN_QUEUE;
1038 else
1040 DirItem *new;
1042 new = diritem_new(name);
1043 g_ptr_array_add(dir->new_items, new);
1048 dir_merge_new(dir);
1050 /* Ask everyone which items they need to display, and add them to
1051 * the recheck list. Typically, this means we don't waste time
1052 * scanning hidden items.
1054 in_callback++;
1055 for (next = dir->users; next; next = next->next)
1057 DirUser *user = (DirUser *) next->data;
1058 user->callback(dir,
1059 DIR_QUEUE_INTERESTING,
1060 NULL, user->data);
1062 in_callback--;
1064 g_ptr_array_free(names, TRUE);
1066 set_idle_callback(dir);
1067 dir_merge_new(dir);
1070 #ifdef USE_DNOTIFY
1071 /* Signal handler - don't do anything dangerous here */
1072 static void dnotify_handler(int sig, siginfo_t *si, void *data)
1074 /* Note: there is currently only one place to store the fd,
1075 * so we'll miss updates to several directories if they happen
1076 * close together.
1078 dnotify_last_fd = si->si_fd;
1079 dnotify_wakeup_flag = TRUE;
1080 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
1082 #endif